/*************************************************************************** * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne. JOVE * * is provided to you without charge, and with no warranty. You may give * * away copies of JOVE, including sources, provided that this notice is * * included in all the files. * ***************************************************************************/ #include "jove.h" #include "io.h" #include "termcap.h" #include #include #ifdef MSDOS # include # include #endif #ifdef LINT_ARGS private void DoShell(char *, char *), com_finish(int, char *), toerror(int, int), closepipe(void); private int okay_error(void), openforpipe(void), reopenforpipe(void); private struct error * AddError(struct error *, Line *, Buffer *, Line *, int); #else private void DoShell(), com_finish(), toerror(), closepipe(); private int openforpipe(), okay_error(), reopenforpipe(); private struct error * AddError(); #endif long SigMask = 0; /* This disgusting RE search string parses output from the GREP family, from the pdp11 compiler, pcc, and lint. Jay (HACK) Fenlasen changed this to work for the lint errors. */ char ErrFmtStr[256] = "^\\{\",\\}\\([^:\"( \t]*\\)\\{\"\\, line ,:,(\\} *\\([0-9][0-9]*\\)[:)]\ \\|:: *\\([^(]*\\)(\\([0-9]*\\))$\ \\|( \\([^(]*\\)(\\([0-9]*\\)) ),"; struct error { Buffer *er_buf; /* Buffer error is in */ Line *er_mess, /* Actual error message */ *er_text; /* Actual error */ int er_char; /* char pos of error */ struct error *er_prev, /* List of errors */ *er_next; }; struct error *cur_error = 0, *errorlist = 0; Buffer *perr_buf = 0; /* Buffer with error messages */ int WtOnMk = 1; /* Write the modified files when we make */ /* Add an error to the end of the list of errors. This is used for parse-{C,LINT}-errors and for the spell-buffer command */ private struct error * AddError(laste, errline, buf, line, charpos) struct error *laste; Line *errline, *line; Buffer *buf; { struct error *new = (struct error *) emalloc(sizeof *new); new->er_prev = laste; if (laste) laste->er_next = new; else { if (errorlist) /* Free up old errors */ ErrFree(); cur_error = errorlist = new; } laste = new; new->er_next = 0; new->er_buf = buf; new->er_text = line; new->er_char = charpos; new->er_mess = errline; return new; } /* Parse errors of the form specified in ErrFmtStr in the current buffer. Do a show error of the first error. This is neat because this will work for any kind of output that prints a file name and a line number on the same line. */ void ErrParse() { Bufpos *bp; char fname[FILESIZE], lineno[10], REbuf[256], *REalts[10]; int lnum, last_lnum = -1; struct error *ep = 0; Buffer *buf, *lastb = 0; Line *err_line; ErrFree(); /* This is important! */ ToFirst(); perr_buf = curbuf; REcompile(ErrFmtStr, 1, REbuf, REalts); /* Find a line with a number on it. */ while (bp = docompiled(FORWARD, REbuf, REalts)) { SetDot(bp); putmatch(1, fname, sizeof fname); putmatch(2, lineno, sizeof lineno); buf = do_find((Window *) 0, fname, 1); if (buf != lastb) { lastb = buf; last_lnum = -1; /* signals new file */ err_line = buf->b_first; } (void) chr_to_int(lineno, 10, NO, &lnum); if (lnum == last_lnum) /* one error per line is nicer */ continue; if (last_lnum == -1) last_lnum = 1; /* that's where we really are */ err_line = next_line(err_line, lnum - last_lnum); ep = AddError(ep, curline, buf, err_line, 0); last_lnum = lnum; } if (cur_error != 0) ShowErr(); } /* Free up all the errors */ void ErrFree() { register struct error *ep; for (ep = errorlist; ep != 0; ep = ep->er_next) free((char *) ep); errorlist = cur_error = 0; } /* Internal next error sets cur_error to the next error, taking the argument count, supplied by the user, into consideration. */ private char errbounds[] = "You're at the %s error.", noerrs[] = "No errors!"; private void toerror(forward, num) { register struct error *e = cur_error; if (e == 0) complain(noerrs); if ((forward && (e->er_next == 0)) || (!forward && (e->er_prev == 0))) complain(errbounds, forward ? "last" : "first"); while (--num >= 0) { if ((e = forward ? e->er_next : e->er_prev) == 0) break; cur_error = e; } } void NextError() { ToError(1); } void PrevError() { ToError(0); } private int okay_error() { return ((inlist(perr_buf->b_first, cur_error->er_mess)) && (inlist(cur_error->er_buf->b_first, cur_error->er_text))); } /* Go the the next error, if there is one. Put the error buffer in one window and the buffer with the error in another window. It checks to make sure that the error actually exists. */ void ToError(forward) { do { toerror(forward, arg_value()); } while (!okay_error()); ShowErr(); } int EWSize = 20; /* percentage of screen the error window should be */ void set_wsize(wsize) int wsize; { wsize = (LI * wsize) / 100; if (wsize >= 1 && !one_windp()) WindSize(curwind, wsize - (curwind->w_height - 1)); } /* Show the current error, i.e. put the line containing the error message in one window, and the buffer containing the actual error in another window. */ void ShowErr() { Window *err_wind, *buf_wind; if (cur_error == 0) complain(noerrs); if (!okay_error()) { rbell(); return; } err_wind = windbp(perr_buf); buf_wind = windbp(cur_error->er_buf); if (err_wind && !buf_wind) { SetWind(err_wind); pop_wind(cur_error->er_buf->b_name, NO, -1); buf_wind = curwind; } else if (!err_wind && buf_wind) { SetWind(buf_wind); pop_wind(perr_buf->b_name, NO, -1); err_wind = curwind; } else if (!err_wind && !buf_wind) { pop_wind(perr_buf->b_name, NO, -1); err_wind = curwind; pop_wind(cur_error->er_buf->b_name, NO, -1); buf_wind = curwind; } /* Put the current error message at the top of its Window */ SetWind(err_wind); SetLine(cur_error->er_mess); SetTop(curwind, (curwind->w_line = cur_error->er_mess)); set_wsize(EWSize); /* now go to the the line with the error in the other window */ SetWind(buf_wind); DotTo(cur_error->er_text, cur_error->er_char); } char ShcomBuf[128] = {0}; /* Make a buffer name given the command `command', i.e. "fgrep -n foo *.c" will return the buffer name "fgrep". */ char * MakeName(command) char *command; { static char bufname[50]; register char *cp = bufname, c; while ((c = *command++) && (c == ' ' || c == '\t')) ; do *cp++ = c; while ((c = *command++) && (c != ' ' && c != '\t')); *cp = 0; strcpy(bufname, basename(bufname)); return bufname; } /* Run make, first writing all the modified buffers (if the WtOnMk flag is non-zero), parse the errors, and go the first error. */ char make_cmd[128] = "make"; void MakeErrors() { Window *old = curwind; int status, compilation; if (WtOnMk) put_bufs(0); /* When we're not doing make or cc (i.e., the last command was probably a grep or something) and the user just types C-X C-E, he probably (possibly, hopefully, usually (in my case)) doesn't want to do the grep again but rather wants to do a make again; so we ring the bell and insert the default command and let the person decide. */ compilation = (sindex("make", make_cmd) || sindex("cc", make_cmd)); if (is_an_arg() || !compilation) { if (!compilation) { rbell(); Inputp = make_cmd; /* insert the default for the user */ } null_ncpy(make_cmd, ask(make_cmd, "Compilation command: "), sizeof (make_cmd) - 1); } status = UnixToBuf(MakeName(make_cmd), YES, EWSize, YES, Shell, ShFlags, make_cmd, (char *) 0); com_finish(status, make_cmd); ErrParse(); if (!cur_error) SetWind(old); } #ifdef SPELL void SpelBuffer() { char *Spell = "Spell", com[100]; Window *savewp = curwind; put_bufs(0); sprintf(com, "spell %s", curbuf->b_fname); (void) UnixToBuf(Spell, YES, EWSize, YES, Shell, ShFlags, com, (char *) 0); message("[Delete the irrelevant words and then type C-X C-C]"); ToFirst(); Recur(); SetWind(savewp); SpelParse(Spell); } void SpelWords() { char *buftospel; Buffer *wordsb = curbuf; if ((buftospel = ask_buf((Buffer *) 0)) == 0) return; SetBuf(do_select(curwind, buftospel)); SpelParse(wordsb->b_name); } void SpelParse(bname) char *bname; { Buffer *buftospel, *wordsb; char wordspel[100]; Bufpos *bp; struct error *ep = 0; ErrFree(); /* This is important! */ buftospel = curbuf; wordsb = buf_exists(bname); perr_buf = wordsb; /* This is important (buffer containing error messages) */ SetBuf(wordsb); ToFirst(); f_mess("Finding misspelled words ... "); while (!lastp(curline)) { sprintf(wordspel, "\\<%s\\>", linebuf); SetBuf(buftospel); ToFirst(); while (bp = dosearch(wordspel, 1, 1)) { SetDot(bp); ep = AddError(ep, wordsb->b_dot, buftospel, curline, curchar); } SetBuf(wordsb); line_move(FORWARD, 1, NO); } add_mess("Done."); SetBuf(buftospel); ShowErr(); } #endif /* SPELL */ void ShToBuf() { char bufname[128], cmd[128]; strcpy(bufname, ask((char *) 0, "Buffer: ")); strcpy(cmd, ask(ShcomBuf, "Command: ")); DoShell(bufname, cmd); } void ShellCom() { null_ncpy(ShcomBuf, ask(ShcomBuf, ProcFmt), (sizeof ShcomBuf) - 1); DoShell(MakeName(ShcomBuf), ShcomBuf); } ShNoBuf() { int status; null_ncpy(ShcomBuf, ask(ShcomBuf, ProcFmt), (sizeof ShcomBuf) - 1); status = UnixToBuf((char *) 0, NO, 0, NO, Shell, ShFlags, ShcomBuf, (char *) 0); com_finish(status, ShcomBuf); } Shtypeout() { int status; null_ncpy(ShcomBuf, ask(ShcomBuf, ProcFmt), (sizeof ShcomBuf) - 1); status = UnixToBuf((char *) 0, YES, 0, NO, Shell, ShFlags, ShcomBuf, (char *) 0); if (status == 0) Typeout("[%s: completed successfully]", ShcomBuf); else Typeout("[%s: exited (%d)]", ShcomBuf, status); TOstop(); } /* Run the shell command into `bufname'. Empty the buffer except when we give a numeric argument, in which case it inserts the output at the current position in the buffer. */ private void DoShell(bufname, command) char *bufname, *command; { Window *savewp = curwind; int status; status = UnixToBuf(bufname, YES, 0, !is_an_arg(), Shell, ShFlags, command, (char *) 0); com_finish(status, command); SetWind(savewp); } private void com_finish(status, cmd) int status; char *cmd; { s_mess("[%s: ", cmd); if (status == 0) add_mess("completed successfully"); else add_mess("exited (%d)", status); add_mess("]"); } #ifndef MSDOS void dowait(pid, status) int pid, *status; { # ifndef IPROCS int rpid; while ((rpid = wait(status)) != pid) ; # else # ifdef BSD4_2 # include # else # include # endif union wait w; int rpid; for (;;) { # ifndef BSD4_2 rpid = wait2(&w.w_status, 0); # else rpid = wait3(&w, 0, (struct rusage *) 0); # endif if (rpid == pid) { if (status) *status = w.w_status; break; } else kill_off(rpid, w); } # endif /* IPROCS */ } #endif /* MSDOS */ /* Run the command to bufname, erase the buffer if clobber is non-zero, and redisplay if disp is non-zero. Leaves current buffer in `bufname' and leaves any windows it creates lying around. It's up to the caller to fix everything up after we're done. (Usually there's nothing to fix up.) */ /* VARARGS5 */ int UnixToBuf(bufname, disp, wsize, clobber, va_alist) char *bufname; va_dcl { #ifndef MSDOS int p[2], pid, status, #else /* MSDOS */ int p0, oldo, olde, retcode, #endif /* MSDOS */ eof; va_list ap; char *argv[32], *mess; File *fp; int (*old_int)(); va_start(ap); make_argv(argv, ap); va_end(ap); if (bufname != 0 && clobber == YES) isprocbuf(bufname); if (disp) { if (bufname != 0) message("Starting up..."); else { TOstart(argv[0], TRUE); Typeout("Starting up..."); } if (bufname != 0) { pop_wind(bufname, clobber, clobber ? B_PROCESS : B_FILE); set_wsize(wsize); redisplay(); } } /* Now I will attempt to describe how I deal with signals during the execution of the shell command. My desire was to be able to interrupt the shell command AS SOON AS the window pops up. So, if we have JOB_CONTROL (i.e., the new signal mechanism) I hold SIGINT, meaning if we interrupt now, we will eventually see the interrupt, but not before we are ready for it. We fork, the child releases the interrupt, it then sees the interrupt, and so exits. Meanwhile the parent ignores the signal, so if there was a pending one, it's now lost. With no JOB_CONTROL, the best behavior you can expect is, when you type ^] too very quickly after the window pops up, it may be ignored. The behavior BEFORE was that it would interrupt JOVE and then you would have to continue JOVE and wait a little while longer before trying again. Now that is fixed, in that you just have to type it twice. */ #ifndef MSDOS # ifdef IPROCS sighold(SIGCHLD); # endif # ifdef JOB_CONTROL sighold(SIGINT); # else old_int = signal(SIGINT, SIG_IGN), # endif dopipe(p); pid = vfork(); if (pid == -1) { pclose(p); complain("[Fork failed]"); } if (pid == 0) { # ifdef BSD4_2 /* * We want to release SIGCHLD and SIGINT in the child, but * we can't use sigrelse because that would change Jove's * copy of the SigMask variable (because we're in a * vfork). So we simply set set the mask directly. There * are several other forks in Jove, but this is the only * one we execute often enough to make it worth using a * vfork. */ (void) signal(SIGINT, SIG_DFL); (void) sigsetmask(SigMask & ~(sigmask(SIGCHLD)|sigmask(SIGINT))); # else /* BSD4_2 */ # ifdef IPROCS sigrelse(SIGCHLD); /* don't know if this matters */ # endif /* IPROCS */ (void) signal(SIGINT, SIG_DFL); # ifdef JOB_CONTROL sigrelse(SIGINT); # endif # endif /* BSD4_2 */ (void) close(0); (void) open("/dev/null", 0); (void) close(1); (void) close(2); (void) dup(p[1]); (void) dup(p[1]); pclose(p); execv(argv[0], &argv[1]); (void) write(1, "Execl failed.\n", 14); _exit(1); } # ifdef JOB_CONTROL old_int = signal(SIGINT, SIG_IGN); # endif (void) close(p[1]); fp = fd_open(argv[1], F_READ, p[0], iobuff, LBSIZE); #else /* MSDOS */ if ((p0 = openforpipe()) < 0) complain("cannot make pipe for filter"); oldo = dup(1); olde = dup(2); close(1); close(2); dup(p0); dup(1); close(p0); retcode = spawnv(0, argv[0], &argv[1]); p0 = reopenforpipe(); close(1); close(2); dup(oldo); dup(olde); close(oldo); close(olde); if (retcode < 0) complain("[Spawn failed]"); fp = fd_open(argv[1], F_READ, p0, iobuff, LBSIZE); #endif /* MSDOS */ do { #ifndef MSDOS inIOread = 1; #endif eof = f_gets(fp, genbuf, LBSIZE); #ifndef MSDOS inIOread = 0; #endif if (bufname != 0) { ins_str(genbuf, YES); if (!eof) LineInsert(1); } else if (disp == YES) Typeout("%s", genbuf); if (bufname != 0 && disp != 0 && fp->f_cnt <= 0) { #ifdef LOAD_AV { double theavg; get_la(&theavg); if (theavg < 2.0) mess = "Screaming along..."; else if (theavg < 5.0) mess = "Chugging along..."; else mess = "Crawling along..."; } #else mess = "Chugging along..."; #endif /* LOAD_AV */ if (bufname != 0) { message(mess); redisplay(); } } } while (!eof); if (disp) DrawMesg(NO); close_file(fp); #ifndef MSDOS dowait(pid, &status); # ifdef JOB_CONTROL (void) sigrelse(SIGINT); # endif #else /* MSDOS */ closepipe(); #endif /* MSDOS */ (void) signal(SIGINT, old_int); #ifndef MSDOS # ifdef IPROCS sigrelse(SIGCHLD); # endif return status; #else /* MSDOS */ # ifdef CHDIR getCWD(); # endif return retcode; #endif /* MSDOS */ } void FilterRegion() { char *cmd = ask((char *) 0, ": %f (through command) ", ProcFmt); RegToUnix(curbuf, cmd); } /* Send the current region to CMD and insert the output from the command into OUT_BUF. */ void RegToUnix(outbuf, cmd) Buffer *outbuf; char *cmd; { Mark *m = CurMark(); #ifndef MSDOS char *tname = mktemp("/tmp/jfilterXXXXXX"), combuf[128]; #endif /* MSDOS */ Window *save_wind = curwind; int status, error = NO; #ifdef MSDOS int p0, oldi; #endif /* MSDOS */ File *fp; #ifndef MSDOS fp = open_file(tname, iobuff, F_WRITE, COMPLAIN, QUIET); #else /* MSDOS */ p0 = openforpipe(); #endif /* MSDOS */ CATCH #ifdef MSDOS fp = fd_open(cmd, F_WRITE, p0, iobuff, LBSIZE); #endif /* MSDOS */ putreg(fp, m->m_line, m->m_char, curline, curchar, YES); DelReg(); #ifndef MSDOS sprintf(combuf, "%s < %s", cmd, tname); #else /* MSDOS */ f_close(fp); p0 = reopenforpipe(); oldi = dup(0); close(0); dup(p0); close(p0); #endif /* MSDOS */ status = UnixToBuf(outbuf->b_name, NO, 0, outbuf->b_type == B_SCRATCH, #ifndef MSDOS Shell, ShFlags, combuf, (char *) 0); #else /* MSDOS */ Shell, ShFlags, cmd, (char *) 0); #endif /* MSDOS */ ONERROR error = YES; ENDCATCH #ifndef MSDOS f_close(fp); (void) unlink(tname); #else /* MSDOS */ close(0); open("con", 0); /* dup(oldi); */ close(oldi); closepipe(); #endif /* MSDOS */ SetWind(save_wind); if (error == NO) #ifndef MSDOS com_finish(status, combuf); #else com_finish(status, cmd); #endif } void isprocbuf(bufname) char *bufname; { Buffer *bp; if ((bp = buf_exists(bufname)) != 0 && bp->b_type != B_PROCESS) confirm("Over-write buffer %s?", bufname); } #ifdef MSDOS /* msdos specific hacks to allow for pipes */ #include #include #include #include static char pipeiname[64]; static char pipeoname[64]; static int pipehandle; extern char TmpFilePath[]; private int openforpipe() { sprintf(pipeiname, "%s/%s", TmpFilePath, "Jove-I"); sprintf(pipeoname, "%s/%s", TmpFilePath, "Jove-O"); return(pipehandle = creat(pipeoname, S_IWRITE|S_IREAD)); } private int reopenforpipe() { close(pipehandle); unlink(pipeiname); rename(pipeoname, pipeiname); if ((pipehandle = open(pipeiname, 0)) >= 0) return(pipehandle); closepipe(); return(-1); } private void closepipe() { unlink(pipeoname); unlink(pipeiname); } char switchar() { union REGS regs; regs.h.ah = 0x37; regs.h.al = 0; intdos(®s, ®s); return(regs.h.dl); } #endif /* MSDOS */