/************************************************************************ * This program is Copyright (C) 1986 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. * ************************************************************************/ /* * [May 89 TRH] for this to work non-unix systems, You have to supply * the routine ShellToBuf(bufname, disp, wsize, clobber, cmd) * [Jan 91 TRH] add I-Process parsing. * [Jun 91 TRH] revision: now the routine * UnixToBuf(bufname, disp, wsize, clobber, infile, command ... ) * is the interface (and a misnomer for non-unix systems...life is wonderful!). */ #define NO_PROCDECL /* kludge for teensy pdp11 compiler */ #include "jove.h" RCS("$Id: proc.c,v 14.32.0.12 1994/06/15 12:02:29 tom Exp tom $") #include "ctype.h" #include "io.h" #define Extern public /* force definition of any variables in "process.h" */ #include "process.h" #include "re.h" #if (HIGHLIGHT) # include "termcap.h" #endif /* 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. [TRH] added the (Atari-ST) Turbo C errors, the {GEM,MS}DOS absolute pathnames, and the Turbo/GNU C in-line position guess. {{This is about as complex as RE strings can get with current RE_SIZE; especially [...]s take a relatively large amount of space}} */ DEF_STR( "error-format-string", ErrFmt, 200, V_REGEXP ) _IF(def PRIVATE) = #if !vms "\ ^\\{Error ,cc: \",\"?\\}\\(\\{.:\\\\,.:/,\\}[^:\" (\t]+\\)\ \\{ ,\"\\, line ,:,(\\}\t* *\\([0-9]+\\)\\{:,)\\}\ \\{.*[`'\"]\\([^'\"]*\\)\\{',\"\\},\\}\ \\|\ \\{:: *,( \\}\\([^(]*\\)(\\(.+\\))\\{ )\\,,$\\}\ "; #else "\ ^\\{cc: \",\"?\\}\\([^\" (\t]+\\)\ \\{ ,\"\\, line ,:,(\\}\t* *\\([0-9]+\\)\\{:,)\\}\ \\{.*[`'\"]\\([^'\"]*\\)\\{',\"\\},\\}\ \\|\ ^\t\tAt line number \\([0-9]+\\) in \\(.*\\);[0-9]*\\.$\ "; #endif struct error { Buffer *er_buf; /* Buffer error is in */ Line *er_mess; /* Actual error message */ Mark er_mark; /* Pointer to actual error */ /* Note that this is an embedded mark, NOT a pointer to mark. Old names are retained for backward compatibility. */ #define er_text er_mark.m_line /* Actual error */ #define er_char er_mark.m_char /* char pos of error */ struct error *er_prev, /* List of errors */ *er_next; }; private struct error *cur_error ZERO, *errorlist ZERO; Buffer *perr_buf ZERO; /* Buffer with error messages */ DEF_INT( "write-files-on-make", WtOnMk, V_BOOL ) = YES; _IF(def PRIVATE) /* Write the modified files when we make */ DEF_INT( "error-window-size", EWSize, V_BASE10|MAX(100) ) = 20; /* percentage of screen the error window should be */ #ifdef COLOR # include "screen.h" /* for color definitions. */ DEF_INT( "error-buffer-color", ErrColor, V_COLOR ) _IF(def COLOR)_IF(def PRIVATE) ZERO; /* give current error buffer this color. */ private int saved_color; # define set_errbuf(errbuf) (saved_color = (perr_buf = (errbuf))->b_color) #else # define set_errbuf(errbuf) (perr_buf = errbuf) #endif #ifdef IPARSE /* Support for incremental error parsing. */ private void (*err_more)__(( void )) ZERO; /* Parse more errors. */ #endif void set_wsize(fraction) { extern int LI; register Window *w = curwind; register int wsize; if (one_windp(w) || (wsize = fraction) <= 0) return; if ((wsize = (LI * wsize) / 100) == 0) wsize = 1; WindSize(w, wsize - SIZE(w)); } /* 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 */ /* Free up all the errors */ void ErrFree() { { register struct error *ep, *prev; for (ep = errorlist; (prev = ep) != NULL; ) { ep = ep->er_next; if (bufno(prev->er_buf)) /* delete mark only if buffer still exists. */ del_mark(&prev->er_mark, prev->er_buf); free((void_*) prev); } } errorlist = cur_error = NULL; #if (HIGHLIGHT) { register Window *ew; if (ew = windbp(perr_buf)) WUnHighLight(ew); } #endif #ifdef COLOR if (perr_buf) perr_buf->b_color = saved_color; #endif #ifdef IPARSE err_more = NULL; #endif } private struct error *AddError __(( struct error *_(laste), Line *_(errline), Buffer *_(buf), Line *_(line), int _(charpos) )); private struct error * AddError(laste, errline, buf, line, charpos) register struct error *laste; Line *errline, *line; register Buffer *buf; { register struct error *new = (struct error *) emalloc(sizeof *new); if (new->er_prev = laste) laste->er_next = new; else { #if NO if (errorlist) /* Free up old errors */ ErrFree(); #endif /* This is now done explicitly before a new list is built. */ cur_error = errorlist = new; } new->er_next = NULL; new->er_buf = buf; init_mark(&new->er_mark, buf, line, charpos, MarksShouldFloat); new->er_mess = errline; return new; } private int okay_error __(( void )); private int okay_error() { register struct error *e = cur_error; return (inlist(perr_buf->b_first, e->er_mess) && bufno(e->er_buf) && inlist(e->er_buf->b_first, e->er_text)); } /* 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. */ private const char noerrs[] = "No errors!"; #ifdef IPARSE private const char noerrs_yet[] = "No errors! (yet)"; # define NOERRS (pbufalivep(perr_buf) ? noerrs_yet : noerrs) #else # define NOERRS noerrs #endif DEF_CMD( "current-error", ShowErr, NO ) { register Window *err_wind, *buf_wind; register struct error *ce; if ((ce = cur_error) == NULL #ifdef IPARSE && !(err_more && ((*err_more)(), ce = cur_error)) #endif ) { message(NOERRS); rbell(); return; } if (!okay_error()) { rbell(); return; } err_wind = windbp(perr_buf); buf_wind = windbp(ce->er_buf); if (!buf_wind || !err_wind) { if (err_wind) SetWind(err_wind); if (!buf_wind) { pop_wind(ce->er_buf->b_name, NO, NO); buf_wind = curwind; } else SetWind(buf_wind); if (!err_wind) { pop_wind(perr_buf->b_name, NO, NO); err_wind = curwind; } } /* Put the current error message at the top of its Window */ SetWind(err_wind); SetLine(ce->er_mess); set_wsize(EWSize); SetTop(err_wind, (err_wind->w_line = ce->er_mess)); #ifdef COLOR err_wind->w_bufp->b_color = ErrColor; #endif #if (HIGHLIGHT) WHighLight(err_wind, ce->er_mess); WHighLight(buf_wind, ce->er_text); if (BO && True(HighLight)) CentWind(err_wind); /* I [TRH] think this is nicer. */ #endif /* now go to the the line with the error in the other window */ SetWind(buf_wind); DotTo(ce->er_text, ce->er_char); } /* Get file name and line number from match; these can be in any order. You lose if you have the order and filename is a valid number, but that doesn't happen all too often I hope. Returns the line number, and deposits file name in `buf' */ int get_FL_info(buf) register char buf[FILESIZE]; { char tmp[FILESIZE]; register int lnum; /* first assume ; if this fails we have filename already in the right buffer, so we use a temp buffer to store match for lineno. */ putmatch(2, buf, FILESIZE); if ((lnum = chr_to_int(buf, 10, YES)) < 0) buf = tmp; putmatch(1, buf, FILESIZE); if (lnum <= 0 && (lnum = chr_to_int(buf, 10, YES)) > 0) buf[0] = '\0'; /* Invalidate this as a file name in case `buf' still points to the filename buffer. (so that the caller knows it should use a default filename.) */ return lnum; } /* Parse for {C,LINT} errors (or anything that matches ErrFmt) in the current buffer. Set up for the current-error command. 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. Use default format string if no numeric argument is present, otherwise ask for a format. */ #ifdef IPARSE private char *err_fmt ZERO; /* Actual error format string. */ private void ParseMore __(( void )); #endif DEF_CMD( "parse-errors", ParseAll, NO ) { /* [TRH Jul-90] we can use compbuf since LookingAt now uses a local RE buffer. (LookingAt may be called by do_find via DoAutoExec) */ if (exp_p) #ifdef IPARSE set_str(&err_fmt, #endif re_ask(ErrFmt, YES, "With error format: ") #ifdef IPARSE ) #endif ; else #ifdef IPARSE if (err_fmt) { free(err_fmt); err_fmt = NULL; } #else REcompile(ErrFmt, YES, compbuf); #endif ErrFree(); set_errbuf(curbuf); #ifdef IPARSE err_more = ParseMore; /* Setup for incremental error parse. */ ShowErr(); /* Invokes incremental error parse. */ } private void ParseMore() { Buffer *orgbuf = curbuf; if (perr_buf == NULL) return; REcompile(err_fmt ? err_fmt : ErrFmt, YES, compbuf); SetBuf(perr_buf); #endif /* IPARSE */ /* {This is still a part of ParseAll() if not IPARSE.} */ { char fnamebuf[FILESIZE]; register struct error *e = NULL; register Buffer *b = NULL; register int num; register Bufpos *bp = NULL; ToFirst(); #ifdef IPARSE if (e = cur_error) { /* * Start parsing from where we left off. * {NOTE: assumes cur_error points to last error entry.} */ b = e->er_buf; DotTo(e->er_mess, length(e->er_mess)); } #endif /* Find a line with a number on it. */ while (bp = docompiled(FORWARD, compbuf, bp, (Line *) 0)) { register const char *fname = fnamebuf; num = get_FL_info((char *)fname); /* (optional match to pinpoint error more precisely) */ putmatch(3, genbuf, LBSIZE); /* [TRH 07-Dec-91] implicit filename for error formats without in each error message. */ if (fnamebuf[0] == '\0') { if (b == NULL) /* No filename found yet. */ continue; fname = b->b_fname; } b = do_find((Window *) 0, fname, num); if (num <= 0) continue; /* [15-Jan-91] `do_find' now positions to the right line... */ # define errline (b->b_dot) /* one error per line is nicer */ if (e && e->er_text == errline) continue; num = 0; if (genbuf[0] && (num = sindex(genbuf, lcontents(errline)))) num--; e = AddError(e, bp->p_line, b, errline, num); # undef errline } } #ifdef IPARSE SetBuf(orgbuf); #else ShowErr(); #endif } /* NextError sets cur_error to the next error, taking the argument count, supplied by the user, into consideration. 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. */ DEF_CMD( "previous-error", NextError, NEGATE ); DEF_CMD( "next-error", NextError, NO ) { register int num = exp; register struct error *e; register char *bad = NULL; if ((e = cur_error) == NULL) #ifdef IPARSE /* See if any new errors have arrived. If there are, adjust `num' so we get the FIRST error as the `next' one. */ if (err_more && ((*err_more)(), e = cur_error)) { if (num > 0) --num; } else #endif complain(NOERRS); do { if (num < 0) { /* previous error */ bad = "first"; do { if ((e = e->er_prev) == NULL) { break; } cur_error = e; bad = NULL; } while (++num); --num; /* num = -1; */ } else if (num > 0) { /* next error */ bad = "last"; do { if ((e = e->er_next) == NULL) { #ifdef IPARSE if (!(err_more && ((*err_more)(), e = cur_error->er_next))) #endif break; } cur_error = e; bad = NULL; } while (--num); ++num; /* num = 1; */ } else /* (num == 0) */ { /* current error */ ++num; /* num = 1; */ } if (bad) complain("You're at the %s error.", bad); } while (!okay_error()); ShowErr(); } #ifndef NOPROCS char *ShcomBuf ZERO; /* Make a buffer name given the command `command', i.e. "fgrep -n foo *.c" will return the buffer name "[fgrep]". NOTE: the result is passed in Minibuf, so be careful. */ char * MakeName(command) register const char *command; { register char *cp = genbuf; register char c; while (c = *command++) { if (isspace(c)) if (cp != genbuf) break; else continue; *cp++ = c; } *cp = '\0'; cp = Minibuf; sprintf(cp, "[%s]", basename(genbuf)); return cp; } /* I-process parsing. */ #define RunAndParse(bname, command, parser, prompt) \ { \ MAYBE_IPARSE_ELSE(bname, command, parser, prompt) { \ ShellToBuf(bname, YES, EWSize, YES, (char*)0, command); \ parser(); \ } \ } #ifndef IPARSE # define MAYBE_IPARSE_ELSE(bname, command, parser, prompt) #else # define MAYBE_IPARSE_ELSE(bname, command, parser, prompt) \ if (True(IParse)) { \ static Command _parser = { FUNCTION, prompt, parser }; \ IprocWTerm(bname, command, (data_obj *) &_parser); \ } else DEF_INT( "i-process-parse", IParse, V_BOOL ) ZERO; _IF(def IPARSE)_IF(def PRIVATE) #endif /* Run make, first writing all the modified buffers (if the WtOnMk flag is non-zero), parse the errors, and go the first error. */ DEF_STR( "compile-command", make_cmd, 128, V_STRING) _IF(ndef NOPROCS)_IF(def PRIVATE) = "make -k"; DEF_CMD( "compile-it", MakeErrors, NO ) _IF(ndef NOPROCS) { register Window *old = curwind; register char *cmd = make_cmd; if (True(WtOnMk)) put_bufs(NO, NO); /* 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. */ if (exp_p || (!sindex("make", cmd) && !sindex("cc", cmd) && (rbell(), TRUE))) { Inputp = cmd; /* insert the default for the user */ exp_p = NO; cmd = set_str(&make_cmd, ask(cmd, "Compilation command: ")); } #ifdef IPARSE if (True(IParse)) { IprocWTerm(MakeName(cmd), cmd, NULL); } else #endif ShellToBuf(MakeName(cmd), YES, EWSize, YES, (char*)0, cmd); ParseAll(); if (!cur_error) SetWind(old); } #endif /* NOPROCS */ #ifdef SPELL private void SpelParse __(( const char *_(bname) )); private void SpelParse(bname) const char *bname; { register Buffer *buftospel, *wordsb; register Bufpos *bp; register struct error *ep = NULL; ErrFree(); /* This is important! */ buftospel = curbuf; wordsb = buf_exists(bname); set_errbuf(wordsb); /* This is important (buffer containing error messages) */ wordsb->b_dot = wordsb->b_first; f_mess("Finding misspelled words ... "); while (!lastp(wordsb, wordsb->b_dot)) { REcompile(sprint("\\<%s\\>", lcontents(wordsb->b_dot)), YES, compbuf); ToFirst(); bp = NULL; while (bp = docompiled(FORWARD, compbuf, bp, NULL)) { ep = AddError(ep, wordsb->b_dot, buftospel, bp->p_line, bp->p_char); } wordsb->b_dot = wordsb->b_dot->l_next; } add_mess("Done."); ShowErr(); } #ifndef NOPROCS private Buffer *buftospell; private void DoSpelBuf __(( void )); DEF_CMD( "spell-buffer", SpelBuffer, NO ) _IF(ndef NOPROCS)_IF(def SPELL) { SaveFile(); buftospell = curbuf; RunAndParse("Spell", sprint("spell %s", curbuf->b_fname), DoSpelBuf, "Parse spelling errors"); #define SPELL_CMD_FILE(cmd) ((cmd) + 6) /* kludge */ } private void DoSpelBuf() { register Buffer *wordsb = curbuf; if (b_empty(wordsb)) return; message("[Delete the irrelevant words and then type C-X C-C]"); Recur(); #ifdef IPARSE { register Process *p; if (p = wordsb->b_process) buftospell = do_find((Window *)0, SPELL_CMD_FILE(p->p_name), NO); } #endif SetBuf(buftospell); SpelParse(wordsb->b_name); } #endif /* NOPROCS */ DEF_CMD( "parse-spelling-errors-in-buffer", SpelWords, NO ) _IF(def SPELL) { register const char *buftospel; register Buffer *wordsb = curbuf; if ((buftospel = ask_buf((Buffer *) 0)) == NULL) return; SetBuf(do_select(curwind, buftospel)); SpelParse(wordsb->b_name); } #endif /* SPELL */ #ifndef NOPROCS /* 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. If argument is zero, ignore the output altogether. If only a sign is entered (like ESC - ), the current buffer is used for output. #ifdef PROC_TYPEOUT If the argument is negative, the output is displayed using Typeout. #endif */ private void DoShell __(( const char *_(bufname), const char *_(cmd) )); private void DoShell(bufname, cmd) const char *bufname; const char *cmd; { register int disp, clobber = NO; register Window *savewp = curwind; if ((disp = exp) == 0) bufname = DevNull; /* don't insert anything */ else if (exp_p == YES_NODIGIT) disp = 0; /* insert at point */ else if (disp == 1) clobber++; ShellToBuf(bufname, disp, 0, clobber, (char *)0, cmd); SetWind(savewp); } DEF_CMD( "shell-command-to-buffer", ShToBuf, NO ) _IF(ndef NOPROCS) { char buf[BNAMESIZE]; register char *bufname = buf; strcpy(bufname, ask_buf((Buffer *) 0)); DoShell(bufname, ask(ShcomBuf, "%N: %f %s Command: ", bufname)); } DEF_CMD( "shell-command", ShellCom, NO ) _IF(ndef NOPROCS) { register char *cmd; register const char *prompt = ProcFmt; #ifndef TINY if (exp == 0) prompt = ": %f-no-buffer "; else if (exp_p == YES_NODIGIT) prompt = ": %f-at-point "; else if (exp < 0) prompt = ": %f-with-typeout "; #endif cmd = set_str(&ShcomBuf, ask(ShcomBuf, prompt)); DoShell(MakeName(cmd), cmd); } #if unix int dowait(pid) { WAIT_T w; register int rpid; while ((rpid = wait2(&w, 0)) != pid) { #ifndef TINY if (rpid < 0 && errno != EINTR) return -errno; #endif #ifdef IPROCS kill_off(rpid, w); #endif } return WEXITSTATUS(w); } #endif /* unix */ /* 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.) Note that buffer is selected ONLY if disp is also set. (I guess that's a bug, but I can live with it) Display scheme has been changed, as follows: o disp > 0 - insert output in `bufname'; o disp = 0 - if bufname = DevNull, ignore output, else insert output at point in current buffer; #ifdef PROC_TYPEOUT o disp < 0 - Typeout output. #endif */ #ifdef PROC_TYPEOUT private void (*mfun)__(( const char *_(fmt), ... )); #endif int ShellToBuf(bufname, disp, wsize, clobber, infile, cmd) const char *bufname, *infile; register const char *cmd; { register int status; SyncRec(); /* in case system hangs in program exec. */ #ifdef PROC_TYPEOUT mfun = s_mess; /* eventually set to Typeout in read_pipe */ # define s_mess (*mfun) # endif status = UnixToBuf(bufname, disp, wsize, clobber, infile, Shell, ShFlags, cmd, (char *) 0); s_mess((status == EXIT_SUCCESS) ? "[%s: completed successfully]" : (status == -ENOEXEC) ? "[%s: exec failed!]" : "[%s: exited (%d)]", cmd, status); #ifdef PROC_TYPEOUT # undef s_mess /* {dumb cast for dumb compilers (AIX!!! IBM, which figures }-> */ if ((void_*) mfun == (void_*) Typeout) { mfun = NULL; TOstop(); } #endif return status; } #if unix /* VARARGS4 */ int DEFVARG(UnixToBuf, (const char *bufname, int disp, int wsize, int clobber, const char *infile, ...), (bufname, disp, wsize, clobber, infile, va_alist) const char *bufname; int disp; int clobber; const char *infile;) { int p[2]; register int c, d; register int pid = 0; sig_tp (*old_int)__(( int _(sig) )); int old_state; if (c = clobber) isprocbuf(bufname); # define clobber c /* 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. */ #ifdef HOLD_SIGS # ifdef IPROCS # ifndef PIPEPROCS sighold(SIGCHLD); # endif # endif sighold(SIGINT); #else old_int = signal(SIGINT, SIG_IGN); old_state = ttsigset(OFF); #endif pid = 0; CATCH /* So that we can restore signal status on errors. If an error occurs, pid has value <= 0 after the catch. */ message("Starting up..."); #ifdef PROC_TYPEOUT if ((d = disp) > 0) #else if ((d = disp)) #endif # define disp d { pop_wind(bufname, clobber, clobber ? B_PROCESS : B_FILE), set_wsize(wsize); redisplay(); } pipeopen(p); if ((pid = vfork()) <= 0) { va_list ap; char *argv[32]; if (pid < 0) { pipeclose(p); complain("[Fork failed]"); } va_begin(ap, infile); make_argv(argv, ap); va_end(ap); signal(SIGINT, SIG_DFL); #ifdef HOLD_SIGS # ifdef BSD_SIGS /* we can't use sigrelse here since that would change JOVEs copy of SigMask (because we're in a vfork). [TRH] {although it does not hurt because the first thing we'll do with them in JOVE is release them.} */ sigsetmask(SigMask & ~(sigmask(SIGCHLD)|sigmask(SIGINT))); # else # ifdef IPROCS # ifndef PIPEPROCS sigrelse(SIGCHLD); /* don't know if this matters */ # endif # endif sigrelse(SIGINT); # endif #endif /* BSD_SIGS */ close(0); if (infile == NULL || open(infile, O_RDONLY) != 0) open(DevNull, O_RDONLY); close(1); if (bufname == DevNull) /* ignore output */ open(bufname, O_WRONLY); else dup(p[WRITE]); dup2(1, 2); pipeclose(p); execv(argv[0], &argv[1]); exec_fail((char *)0); } ENDCATCH #ifdef HOLD_SIGS /* was JOB_CONTROL */ old_int = signal(SIGINT, SIG_IGN); #endif if (pid > 0) { close(p[WRITE]); read_pipe(bufname, p[READ], disp); # define status d status = dowait(pid); } #ifdef HOLD_SIGS /* was JOB_CONTROL */ sigrelse(SIGINT); # ifdef IPROCS # ifndef PIPEPROCS sigrelse(SIGCHLD); # endif # endif #else ttsigset(old_state); #endif signal(SIGINT, old_int); if (pid <= 0) complain((char *)0); /* propagate the error */ return status; #undef status #undef clobber #undef disp } #endif /* unix */ #ifdef PROC_WRAP /* {{Process line wrap kludge to keep Jonathan happy; it would be infinitely better (and ditto more complex) to solve this in the display engine.}} */ DEF_INT( "wrap-process-lines", WrapProcLines, V_BOOL ) _IF(def PROC_WRAP)_IF(def PRIVATE) ZERO; /* chop line in parts of at most the screen width if "wrap-process-lines" is on, else pass line as is. First call with non-NULL `line' to load the line; subsequently calls have `line' == NULL until line is exhausted, i.e., until chop() returns NULL. `start' is the start column; start = -1 means calculate position of point and use that as start position. */ char * chop(line, start) char *line; { if (True(WrapProcLines)) { static char *savelp ZERO; static char savechar; register char *lp; register int pos; register int c; if ((lp = line) == NULL) { if ((line = lp = savelp) == NULL) return line; *lp = savechar; } savelp = NULL; if ((pos = start) < 0) pos = calc_pos(linebuf, curchar); while (c = *lp++) { # ifdef IPROCS if (c == '\n') { pos = 0; continue; } # endif if (UpdVisPos(pos, c) >= CO) { savechar = *--lp; savelp = lp; *lp = '\0'; break; } } } return line; } #endif /* PROC_WRAP */ #ifndef TINY DEF_INT( "shell-command-leaves-point-at-end", ShPointAtEnd, V_BOOL ) _IF(ndef TINY)_IF(def PRIVATE) ZERO; #endif /* read pipe input into current buffer. Allow redisplay while reading. Handles all nasties of interrupted read. */ void read_pipe(name, fd, disp) const char *name; { static const char Chugging[] = "Chugging along..."; register File *fp; int eof; register int try = 0; int save_freq; Bufpos savedot; int saveIOread = inIOread; #if F_BINARY # define open_mode (True(BinRdFile) ? F_READ|F_BINARY : F_READ) #else # define open_mode F_READ|F_TEXT #endif #ifdef DOS # define UPD_RATE 1 #endif #ifndef UPD_RATE # define UPD_RATE 4 #endif if ((save_freq = UpdFreq) > UPD_RATE) /* do it more frequently */ #ifdef PROC_TYPEOUT if (disp >= 0) #endif alarm(UpdFreq = UPD_RATE); if (disp > 0) inIOread++; fp = fd_open(name, open_mode, fd, iobuff, BLKSIZ); /* iobuff can be used here since we SyncRec() only within getch() */ DOTsave(&savedot); for (;;) { UpdModLine = YES; /* to communicate with alarm handler */ eof = f_gets(fp, genbuf, LBSIZE); eof |= f_eof(fp); /* in case of partial line */ #ifdef PROC_WRAP { char *line = chop(genbuf, # ifdef PROC_TYPEOUT (disp < 0 && False(UseBuffers)) ? i_col : # endif -1); do { #else # define line genbuf #endif #ifdef PROC_TYPEOUT if (disp >= 0) { #endif ins_str(line, -YES); if ( #ifdef PROC_WRAP (line = chop((char *)0, 0)) || #endif !eof) LineInsert(1); #if !(unix || vms) /* for systems without a real SIGALRM signal handling, this presumably checks for and handles the "alarm" */ inIOread = saveIOread; charp(); if (disp > 0) inIOread++; #endif if (UpdModLine != YES) { /* if this has changed, the alarm handler or f_gets() reaching EOF are the only ones that could have done it, so we know it's time to show we're still alive. (but not if invoked from alarm handler.) */ #ifdef LOAD_AV register const char *fmt = Chugging; int theavg = get_la(); if (theavg < 2*100) fmt = "Screaming along."; else if (5*100 < theavg) fmt = "Crawling along.."; message(fmt); #else message(Chugging); #endif /* LOAD_AV */ /* add dots to show we're still alive */ { register char *cp = &mesgbuf[16], *ep = &cp[try++ & 63]; do *cp++ = '.'; while (cp <= ep); *cp = '\0'; } DrawMesg(NO); } #ifdef PROC_TYPEOUT } else { if ((void_*) mfun != (void_*) Typeout) { mfun = Typeout; TOstart(name, FALSE); } Typeout("%s", line); if ( #ifdef PROC_WRAP (line = chop((char *)0, 0)) || #endif !eof) { Typeout((char *)0); continue; } } #endif #ifdef PROC_WRAP } while(line); } #else # undef line #endif if (!(fp->f_flags & (F_ERR|F_EOF))) continue; /* long line */ #ifdef EINTR if ((fp->f_flags & F_ERR) && (errno == EINTR)) { fp->f_flags &= ~(F_ERR|F_EOF); continue; /* interrupt; retry */ } #endif break; } UpdFreq = save_freq; inIOread = saveIOread; f_close(fp); { register Buffer *cb = curbuf; if (cb->b_dot != savedot.p_line || cb->b_char != savedot.p_char) { set_mark(); /* make it a region. */ SetDot(&savedot); #ifndef TINY if (True(ShPointAtEnd)) PtToMark(); #endif } } #undef open_mode } #ifdef GET_PROC_LINE /* Get first line of output from external command `process'. */ char * get_proc_line(process) const char *process; { register const char *bufname = "*JUNK*"; register Buffer *cb = do_select((Window *)0, bufname); register char *save = Minibuf; SetBuf(cb); cb->b_type = B_PROCESS; cb->b_modified++; /* frutz... */ /* Save message buffer since UnixToBuf() munges all over it. */ strcpy(save, mesgbuf); /* prutz... */ UnixToBuf(bufname, NO, 0, NO, (char *)0, process, (char *)0); strcpy(mesgbuf, save); /* klotz... */ strcpy(save, linebuf); kill_buf(cb); /* this 'll restore original curbuf */ return save; } #endif /* GET_PROC_LINE */ /* Send the current region to a command as input, and replace it by the output from the command. */ DEF_CMD( "filter-region", FilterRegion, EDIT ) _IF(ndef NOPROCS) { static char *FComBuf ZERO; register Mark *m = CurMark(); register Buffer *cb = curbuf; register File *fp; Window *save_wind = curwind; #if unix static char tmpfile[] = "/tmp/jfXXXXXX"; register char *tname = mktemp(tmpfile); #else char tmpfile[FILESIZE]; register char *tname = mktmpe(tmpfile, "jfXXXXXX"); #endif set_str(&FComBuf, ask(FComBuf, "%N: %f (through command) ")); fp = open_file(tname, iobuff, F_WRITE|F_TEXT|F_COMPLAIN|F_QUIET); putreg(fp, m->m_line, m->m_char, cb->b_dot, cb->b_char, YES); close_file(fp); DelReg(); CATCH ShellToBuf(cb->b_name, NO, 0, NO, tname, FComBuf); ONERROR Yank(); /* Restore the region on error */ ENDCATCH unlink(tname); SetWind(save_wind); } void isprocbuf(bufname) const char *bufname; { register Buffer *bp; if ((bp = buf_exists(bufname)) && #ifdef IPROCS bp->b_type != B_IPROCESS && #endif bp->b_type != B_PROCESS) confirm("Over-write buffer %b? ", bp); } #endif /* NOPROCS */ /*====================================================================== * $Log: proc.c,v $ * Revision 14.32.0.12 1994/06/15 12:02:29 tom * (ErrFmt): avoid adjacent space and tab in character sets. * * Revision 14.32.0.10 1994/04/07 02:34:44 tom * (read_pipe): allow CRLF translation on input pipe. * * Revision 14.32.0.1 1993/07/07 12:12:56 tom * (F_TEXT): new option for f_open et al. * * Revision 14.32 1993/06/09 01:02:20 tom * (read_pipe): eliminate F_IGN_0 on fd_open(), use ins_str(..., -1) instead. * * Revision 14.31 1993/02/16 22:39:33 tom * remove implicit size limit in MakeName(); fix nested inIOread botch in * read_pipe(); get_proc_line() moved in from io.c; remove (void) casts; * lotsa random optimizations. * * Revision 14.30 1993/02/09 17:44:07 tom * cleanup whitespace; some random optimizations. * * Revision 14.29 1992/12/28 00:38:07 tom * implement incremental error parsing to make "i-process-parse" more * convenient. * * Revision 14.28 1992/10/24 01:24:21 tom * convert to "port{ansi,defs}.h" conventions; remove compile-time * initialization to "error-buffer-color" so it gets expected bgr. color. * * Revision 14.26 1992/08/26 23:56:57 tom * add RCS directives. * */