1: /************************************************************************* 2: * This program is copyright (C) 1985, 1986 by Jonathan Payne. It is * 3: * provided to you without charge for use only on a licensed Unix * 4: * system. You may copy JOVE provided that this notice is included with * 5: * the copy. You may not sell copies of this program or versions * 6: * modified for use on microcomputer systems, unless the copies are * 7: * included with a Unix system distribution and the source is provided. * 8: *************************************************************************/ 9: 10: #include "jove.h" 11: #include "io.h" 12: #include "termcap.h" 13: 14: #include <signal.h> 15: 16: private char 17: *errfmt = "^\\{\",\\}\\([^:\"( \t]*\\)\\{\"\\, line ,:,(\\} *\\([0-9][0-9]*\\)[:)]"; 18: 19: struct error { 20: Buffer *er_buf; /* Buffer error is in */ 21: Line *er_mess, /* Actual error message */ 22: *er_text; /* Actual error */ 23: int er_char; /* char pos of error */ 24: struct error *er_prev, /* List of errors */ 25: *er_next; 26: }; 27: 28: struct error *cur_error = 0, 29: *errorlist = 0; 30: Buffer *perr_buf = 0; /* Buffer with error messages */ 31: 32: int WtOnMk = 1; /* Write the modified files when we make */ 33: 34: /* Add an error to the end of the list of errors. This is used for 35: parse-{C,LINT}-errors and for the spell-buffer command */ 36: 37: private struct error * 38: AddError(laste, errline, buf, line, charpos) 39: struct error *laste; 40: Line *errline, 41: *line; 42: Buffer *buf; 43: { 44: struct error *new = (struct error *) emalloc(sizeof *new); 45: 46: new->er_prev = laste; 47: if (laste) 48: laste->er_next = new; 49: else { 50: if (errorlist) /* Free up old errors */ 51: ErrFree(); 52: cur_error = errorlist = new; 53: } 54: laste = new; 55: new->er_next = 0; 56: new->er_buf = buf; 57: new->er_text = line; 58: new->er_char = charpos; 59: new->er_mess = errline; 60: 61: return new; 62: } 63: 64: ParseAll() 65: { 66: ErrParse(errfmt); 67: } 68: 69: XParse() 70: { 71: char *sstr; 72: 73: sstr = ask(errfmt, ProcFmt); 74: ErrParse(sstr); 75: } 76: 77: /* Parse for {C,LINT} errors (or anything that matches fmtstr) in the 78: current buffer. Set up for the next-error command. This is neat 79: because this will work for any kind of output that prints a file 80: name and a line number on the same line. */ 81: 82: ErrParse(fmtstr) 83: char *fmtstr; 84: { 85: Bufpos *bp; 86: char fname[FILESIZE], 87: lineno[10], 88: REbuf[128], 89: *REalts[10]; 90: int lnum, 91: last_lnum = -1; 92: struct error *ep = 0; 93: Buffer *buf, 94: *lastb = 0; 95: Line *err_line; 96: 97: ErrFree(); /* This is important! */ 98: ToFirst(); 99: perr_buf = curbuf; 100: REcompile(fmtstr, 1, REbuf, REalts); 101: /* Find a line with a number on it. */ 102: while (bp = docompiled(FORWARD, REbuf, REalts)) { 103: SetDot(bp); 104: putmatch(1, fname, sizeof fname); 105: putmatch(2, lineno, sizeof lineno); 106: buf = do_find((Window *) 0, fname, 1); 107: if (buf != lastb) { 108: lastb = buf; 109: last_lnum = -1; /* signals new file */ 110: err_line = buf->b_first; 111: } 112: lnum = chr_to_int(lineno, 10, 0); 113: if (lnum == last_lnum) /* one error per line is nicer */ 114: continue; 115: if (last_lnum == -1) 116: last_lnum = 1; /* that's where we really are */ 117: err_line = next_line(err_line, lnum - last_lnum); 118: ep = AddError(ep, curline, buf, err_line, 0); 119: last_lnum = lnum; 120: } 121: if (cur_error != 0) 122: ShowErr(); 123: exp = 1; 124: } 125: 126: /* Free up all the errors */ 127: 128: ErrFree() 129: { 130: register struct error *ep; 131: 132: for (ep = errorlist; ep != 0; ep = ep->er_next) 133: free((char *) ep); 134: errorlist = cur_error = 0; 135: } 136: 137: /* Internal next error sets cur_error to the next error, taking the 138: argument count, supplied by the user, into consideration. */ 139: 140: private char errbounds[] = "You're at the %s error.", 141: noerrs[] = "No errors!"; 142: 143: private 144: toerror(forward) 145: { 146: register int i; 147: register struct error *e = cur_error; 148: 149: if (e == 0) 150: complain(noerrs); 151: if ((forward && (e->er_next == 0)) || 152: (!forward && (e->er_prev == 0))) 153: complain(errbounds, forward ? "last" : "first"); 154: 155: for (i = 0; i < exp; i++) { 156: if ((e = forward ? e->er_next : e->er_prev) == 0) 157: break; 158: cur_error = e; 159: } 160: } 161: 162: NextError() 163: { 164: ToError(1); 165: } 166: 167: PrevError() 168: { 169: ToError(0); 170: } 171: 172: private 173: okay_error() 174: { 175: return ((inlist(perr_buf->b_first, cur_error->er_mess)) && 176: (inlist(cur_error->er_buf->b_first, cur_error->er_text))); 177: } 178: 179: /* Go the the next error, if there is one. Put the error buffer in 180: one window and the buffer with the error in another window. 181: It checks to make sure that the error actually exists. */ 182: 183: ToError(forward) 184: { 185: do { 186: toerror(forward); 187: exp = 1; 188: } while (!okay_error()); 189: ShowErr(); 190: } 191: 192: int EWSize = 20; /* percentage of screen the error window 193: should be */ 194: 195: /* Show the current error, i.e. put the line containing the error message 196: in one window, and the buffer containing the actual error in another 197: window. */ 198: 199: ShowErr() 200: { 201: Window *err_wind, 202: *buf_wind; 203: int w_size; /* size of window */ 204: 205: if (cur_error == 0) 206: complain(noerrs); 207: if (!okay_error()) { 208: rbell(); 209: return; 210: } 211: err_wind = windbp(perr_buf); 212: buf_wind = windbp(cur_error->er_buf); 213: 214: if (err_wind && !buf_wind) { 215: SetWind(err_wind); 216: pop_wind(cur_error->er_buf->b_name, 0, -1); 217: buf_wind = curwind; 218: } else if (!err_wind && buf_wind) { 219: SetWind(buf_wind); 220: pop_wind(perr_buf->b_name, 0, -1); 221: err_wind = curwind; 222: } else if (!err_wind && !buf_wind) { 223: pop_wind(perr_buf->b_name, 0, -1); 224: err_wind = curwind; 225: pop_wind(cur_error->er_buf->b_name, 0, -1); 226: buf_wind = curwind; 227: } 228: 229: /* Put the current error message at the top of its Window */ 230: SetWind(err_wind); 231: SetLine(cur_error->er_mess); 232: SetTop(curwind, (curwind->w_line = cur_error->er_mess)); 233: w_size = (ILI * EWSize) / 100; 234: if (w_size >= 1) 235: WindSize(curwind, w_size - (curwind->w_height - 1)); 236: 237: /* now go to the the line with the error in the other window */ 238: SetWind(buf_wind); 239: DotTo(cur_error->er_text, cur_error->er_char); 240: } 241: 242: char ShcomBuf[128] = {0}; 243: 244: /* Make a buffer name given the command `command', i.e. "fgrep -n foo *.c" 245: will return the buffer name "fgrep". */ 246: 247: char * 248: MakeName(command) 249: char *command; 250: { 251: static char bufname[50]; 252: register char *cp = bufname, 253: c; 254: 255: while ((c = *command++) && (c == ' ' || c == '\t')) 256: ; 257: do 258: *cp++ = c; 259: while ((c = *command++) && (c != ' ' && c != '\t')); 260: *cp = 0; 261: strcpy(bufname, basename(bufname)); 262: 263: return bufname; 264: } 265: 266: /* Run make, first writing all the modified buffers (if the WtOnMk flag is 267: non-zero), parse the errors, and go the first error. */ 268: 269: char make_cmd[128] = "make"; 270: 271: MakeErrors() 272: { 273: Window *old = curwind; 274: int status, 275: compilation; 276: 277: if (WtOnMk) 278: put_bufs(0); 279: /* When we're not doing make or cc (i.e., the last command 280: was probably a grep or something) and the user just types 281: C-X C-E, he probably (possibly, hopefully, usually (in my 282: case)) doesn't want to do the grep again but rather wants 283: to do a make again; so we ring the bell and insert the 284: default command and let the person decide. */ 285: 286: compilation = (sindex("make", make_cmd) || sindex("cc", make_cmd)); 287: if (exp_p || !compilation) { 288: if (!compilation) { 289: rbell(); 290: Inputp = make_cmd; /* insert the default for the 291: user */ 292: } 293: null_ncpy(make_cmd, ask(make_cmd, "Compilation command: "), 294: sizeof (make_cmd) - 1); 295: } 296: status = UnixToBuf(MakeName(make_cmd), YES, EWSize, YES, Shell, basename(Shell), ShFlags, make_cmd, 0); 297: com_finish(status, make_cmd); 298: 299: ErrParse(errfmt); 300: 301: if (!cur_error) 302: SetWind(old); 303: } 304: 305: #ifdef SPELL 306: 307: SpelBuffer() 308: { 309: char *Spell = "Spell", 310: com[100]; 311: Window *savewp = curwind; 312: 313: put_bufs(0); 314: sprintf(com, "spell %s", curbuf->b_fname); 315: (void) UnixToBuf(Spell, YES, EWSize, YES, Shell, basename(Shell), ShFlags, com, 0); 316: message("[Delete the irrelevant words and then type C-X C-C]"); 317: Recur(); 318: SetWind(savewp); 319: SpelParse(Spell); 320: } 321: 322: SpelWords() 323: { 324: char *buftospel; 325: Buffer *wordsb = curbuf; 326: 327: if ((buftospel = ask_buf((Buffer *) 0)) == 0) 328: return; 329: SetBuf(do_select(curwind, buftospel)); 330: SpelParse(wordsb->b_name); 331: } 332: 333: SpelParse(bname) 334: char *bname; 335: { 336: Buffer *buftospel, 337: *wordsb; 338: char wordspel[100]; 339: Bufpos *bp; 340: struct error *ep = 0; 341: 342: ErrFree(); /* This is important! */ 343: 344: buftospel = curbuf; 345: wordsb = buf_exists(bname); 346: perr_buf = wordsb; /* This is important (buffer containing 347: error messages) */ 348: SetBuf(wordsb); 349: ToFirst(); 350: f_mess("Finding misspelled words ... "); 351: while (!lastp(curline)) { 352: sprintf(wordspel, "\\<%s\\>", linebuf); 353: SetBuf(buftospel); 354: ToFirst(); 355: while (bp = dosearch(wordspel, 1, 1)) { 356: SetDot(bp); 357: ep = AddError(ep, wordsb->b_dot, buftospel, 358: curline, curchar); 359: } 360: SetBuf(wordsb); 361: line_move(FORWARD, NO); 362: } 363: add_mess("Done."); 364: SetBuf(buftospel); 365: ShowErr(); 366: } 367: 368: #endif SPELL 369: 370: ShToBuf() 371: { 372: char bufname[100]; 373: 374: strcpy(bufname, ask((char *) 0, "Buffer: ")); 375: DoShell(bufname, ask(ShcomBuf, "Command: ")); 376: } 377: 378: ShellCom() 379: { 380: null_ncpy(ShcomBuf, ask(ShcomBuf, ProcFmt), (sizeof ShcomBuf) - 1); 381: DoShell(MakeName(ShcomBuf), ShcomBuf); 382: } 383: 384: /* Run the shell command into `bufname'. Empty the buffer except when we 385: give a numeric argument, in which case it inserts the output at the 386: current position in the buffer. */ 387: 388: private 389: DoShell(bufname, command) 390: char *bufname, 391: *command; 392: { 393: Window *savewp = curwind; 394: int status; 395: 396: exp = 1; 397: status = UnixToBuf(bufname, YES, 0, !exp_p, Shell, basename(Shell), 398: ShFlags, command, 0); 399: com_finish(status, command); 400: SetWind(savewp); 401: } 402: 403: private 404: com_finish(status, com) 405: char *com; 406: { 407: s_mess("\"%s\" completed %ssuccessfully.", com, status ? "un" : NullStr); 408: } 409: 410: dowait(pid, status) 411: int pid, 412: *status; 413: { 414: #ifndef IPROCS 415: 416: int rpid; 417: 418: while ((rpid = wait(status)) != pid) 419: ; 420: #else 421: 422: #ifdef BSD4_2 423: # include <sys/wait.h> 424: #else 425: # include <wait.h> 426: #endif 427: 428: union wait w; 429: int rpid; 430: 431: for (;;) { 432: #ifndef VMUNIX 433: rpid = wait2(&w.w_status, 0); 434: #else 435: rpid = wait3(&w, 0, (struct rusage *) 0); 436: #endif 437: if (rpid == pid) { 438: if (status) 439: *status = w.w_status; 440: break; 441: } else 442: kill_off(rpid, w); 443: } 444: #endif IPROCS 445: } 446: 447: /* Run the command to bufname, erase the buffer if clobber is non-zero, 448: and redisplay if disp is non-zero. Leaves current buffer in `bufname' 449: and leaves any windows it creates lying around. It's up to the caller 450: to fix everything up after we're done. (Usually there's nothing to 451: fix up.) */ 452: 453: /* VARARGS3 */ 454: 455: UnixToBuf(bufname, disp, wsize, clobber, cmd, args) 456: char *bufname, 457: *cmd; 458: { 459: int p[2], 460: pid; 461: extern int ninbuf; 462: Buffer *bp; 463: 464: if (clobber && (bp = buf_exists(bufname)) != 0 && 465: bp->b_type != B_PROCESS && bp->b_type != B_IPROCESS) 466: complain("Command would over-write buffer %s.", bufname); 467: if (disp) { 468: message("Starting up..."); 469: pop_wind(bufname, clobber, clobber ? B_PROCESS : B_FILE); 470: wsize = (LI * wsize) / 100; 471: if (wsize >= 1 && !one_windp()) 472: WindSize(curwind, wsize - (curwind->w_height - 1)); 473: redisplay(); 474: } 475: exp = 1; 476: dopipe(p); 477: pid = fork(); 478: if (pid == -1) { 479: pclose(p); 480: complain("[Fork failed]"); 481: } 482: if (pid == 0) { 483: (void) close(0); 484: (void) open("/dev/null", 0); 485: (void) close(1); 486: (void) close(2); 487: (void) dup(p[1]); 488: (void) dup(p[1]); 489: pclose(p); 490: execv(cmd, (char **) &args); 491: (void) write(1, "Execl failed.\n", 14); 492: _exit(1); 493: } else { 494: int status; 495: int (*oldint)() = signal(SIGINT, SIG_IGN); 496: char *mess; 497: File *fp; 498: 499: #ifdef IPROCS 500: sighold(SIGCHLD); 501: #endif 502: 503: (void) close(p[1]); 504: fp = fd_open(cmd, F_READ, p[0], iobuff, LBSIZE); 505: while (inIOread = 1, f_gets(fp, genbuf, LBSIZE) != EOF) { 506: inIOread = 0; 507: ins_str(genbuf, YES); 508: LineInsert(); 509: if (disp != 0 && fp->f_cnt <= 0) { 510: #ifdef LOAD_AV 511: { 512: double theavg; 513: 514: get_la(&theavg); 515: if (theavg < 2.0) 516: mess = "Screaming along..."; 517: else if (theavg < 5.0) 518: mess = "Chugging along..."; 519: else 520: mess = "Crawling along..."; 521: } 522: #else 523: mess = "Chugging along..."; 524: #endif LOAD_AV 525: message(mess); 526: redisplay(); 527: } 528: } 529: if (disp) 530: DrawMesg(NO); 531: close_file(fp); 532: (void) signal(SIGINT, oldint); 533: dowait(pid, &status); 534: #ifdef IPROCS 535: sigrelse(SIGCHLD); 536: #endif 537: return status; 538: } 539: return 0; 540: } 541: 542: #ifdef BSD4_2 543: 544: private int SigMask = 0; 545: 546: sighold(sig) 547: { 548: (void) sigblock(SigMask |= (1 << (sig - 1))); 549: } 550: 551: sigrelse(sig) 552: { 553: (void) sigsetmask(SigMask &= ~(1 << (sig - 1))); 554: } 555: 556: #endif 557: 558: FilterRegion() 559: { 560: char *cmd = ask((char *) 0, ": %f (through command) ", ProcFmt); 561: 562: RegToUnix(curbuf, cmd); 563: } 564: 565: /* Send the current region to CMD and insert the output from the 566: command into OUT_BUF. */ 567: 568: RegToUnix(outbuf, cmd) 569: Buffer *outbuf; 570: char *cmd; 571: { 572: Mark *m = CurMark(); 573: char *tname = mktemp("/tmp/jfilterXXXXXX"), 574: combuf[130]; 575: Window *save_wind = curwind; 576: int status; 577: File *fp; 578: 579: CATCH 580: fp = open_file(tname, iobuff, F_WRITE, COMPLAIN, QUIET); 581: putreg(fp, m->m_line, m->m_char, curline, curchar, YES); 582: DelReg(); 583: sprintf(combuf, "%s < %s", cmd, tname); 584: status = UnixToBuf(outbuf->b_name, NO, 0, outbuf->b_type == B_SCRATCH, 585: Shell, basename(Shell), ShFlags, combuf, 0); 586: ONERROR 587: ; /* Do nothing ... but fall through and delete the tmp 588: file. */ 589: ENDCATCH 590: f_close(fp); 591: (void) unlink(tname); 592: SetWind(save_wind); 593: com_finish(status, combuf); 594: }