home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume8 / jove / part06 / proc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-02-02  |  14.1 KB  |  654 lines

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