home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / finderr.c < prev    next >
C/C++ Source or Header  |  1998-07-10  |  14KB  |  595 lines

  1. /* Find the next error in mentioned in the shell output window.
  2.  * written for vile: Copyright (c) 1990, 1995 by Paul Fox
  3.  * rewritten to use regular expressions, 1995 by T.Dickey (dickey@clark.net)
  4.  *
  5.  * $Header: /usr/build/vile/vile/RCS/finderr.c,v 1.71 1998/07/10 10:32:06 tom Exp $
  6.  *
  7.  */
  8.  
  9. #include "estruct.h"
  10. #include "edef.h"
  11.  
  12. #if OPT_FINDERR
  13.  
  14. #define W_VERB  0
  15. #define W_FILE  1
  16. #define W_LINE  2
  17. #define W_COLM  3
  18. #define W_TEXT  4
  19.  
  20. typedef    struct    {
  21.     regexp *exp_comp;
  22.     int    words[5];
  23.     } ERR_PATTERN;
  24.  
  25. static    LINE *    getdot (BUFFER *bp);
  26. static    void    putdotback (BUFFER *bp, LINE *dotp);
  27.  
  28. static    char febuff[NBUFN];    /* name of buffer to find errors in */
  29. static    int    newfebuff = TRUE; /* is the name new since last time? */
  30.  
  31. static    TBUFF *    fe_verb;
  32. static    TBUFF *    fe_file;
  33. static    TBUFF *    fe_text;
  34. static    int    fe_colm;
  35. static    int    fe_line;
  36.  
  37.     /*
  38.      * This is the list of predefined regular expressions for the error
  39.      * finder.  The user can substitute a new list at runtime by loading
  40.      * the buffer [Error Expressions].  Basically, they're normal regular
  41.      * expressions, with embedded stuff that the error finder can parse to
  42.      * find the verb, file, line and text fields that the regular
  43.      * expression may contain.  These fields may be in any order, and all
  44.      * except the file are optional.
  45.      *
  46.      *    %V - verb, for tracking gmake-style Entering/Leaving messages
  47.      *    %F - range of characters to match filename.
  48.      *    %B - range of characters to match scratch-buffer.
  49.      *    %L - line number (this has to be an integer)
  50.      *    %T - text to display in the message line. If no field is given,
  51.      *        the error finder will display the entire line from
  52.      *        the error-buffer.
  53.      *
  54.      * The %V, %F, %T fields may be given in alternate form, using ranges.
  55.      * The default field is a blank-delimited token, which is enough for
  56.      * %V, marginal for %F and useless for %T.  Vile takes each %-marked
  57.      * field and replaces it by a subexpression, to use the subexpression
  58.      * number to obtain the actual field mapping.
  59.      *
  60.      * FIXME:  some lint programs put the file, line-number and text on
  61.      * separate lines.  Maybe we should add another control code that
  62.      * specifies sequences of regular expressions.
  63.      *
  64.      * FIXME:  it might be useful to autoconf for the existing lint
  65.      * program, to select the bsd/sys5 lint regular expressions.
  66.      */
  67. static const
  68. char *const predefined[] = {
  69.     "^\"%[^\" \t]\", line %L:%T",        /* various C compilers */
  70.     "^%[^: \t]:\\s*%L:\\s*%T",        /* "grep -n" */
  71. #if OPT_MSDOS_PATH
  72.     "^%F:\\s*%L:\\s*%T",            /* "grep -n", handles */
  73.                                             /* dos drive letter   */
  74. #endif
  75. #if SYS_APOLLO
  76.     " Line %L of \"%[^\" \t]\"",        /* C compiler */
  77. #endif
  78. #if SYS_SUNOS && SYSTEM_HAS_LINT_PROG
  79.     "%[^:( \t](%L):%T",                      /* bsd lint) */
  80.     "  ::  %[^( \t](%L)",                    /* bsd lint) */
  81.     "used[ \t]*([ \t]%[^(](%L)[ \t]*)",            /* bsd lint) */
  82. #endif
  83.     /*     ultrix, sgi, osf1 (alpha only?)  use:            */
  84.     /*     compiler-name: Error: filename, line line-number ...    */
  85.     "[^ ]\\+ [^ ]\\+ \"%[^, \t\"]\", line %L",
  86.     "[^ ]\\+ [^ ]\\+ %[^, \t], line %L",
  87.     "[^ ]\\+ \"%[^\"]\", line %L",            /* HP/UX C compiler */
  88. #if defined(_AIX)
  89.     "^\"%[^\" \t]\", line %L\\.[0-9]\\+:%T",    /* AIX C compilers */
  90. #endif
  91. #if defined(clipper) || defined(__clipper__)
  92.     "^\"%[^\" \t]\", line %L (col. [0-9]\\+):%T",    /* CLIX C compiler */
  93. #endif
  94.     "^%[^(](%L)[ \t]\\+:%T",
  95.  
  96. #if SYS_UNIX && SYSTEM_HAS_LINT_PROG
  97.     "^    [^ \t]\\+[ \t]\\+%[^(](%L)$",            /* sys5 lint) */
  98.     "^    [^(].*( arg %L ) \t%[^( \t](%L) :: [^(]\\+(%L))",    /* sys5 lint))*/
  99.     "^    .* :: %[^(](%L)",                    /* sys5 lint) */
  100. #endif
  101. #if CC_CSETPP
  102.     "^%[^(](%L:%C) : %T",
  103. #endif
  104. #if CC_TURBO
  105.     "^Error %[^ ] %L:",
  106.     "^Warning %[^ ] %L:",
  107. #endif
  108. #if CC_WATCOM
  109.     "^%[^(](%L): %T",
  110. #endif
  111.     "^%B:%L:%T",                    /* "pp" in scratch buf*/
  112.     "^[^:]\\+: %V directory `%[^']'",        /* GNU make */
  113.     "%T at %F line %L.*",        /* perl 5 */
  114.     "%F\\[%L\\]:%T",        /* hgrep */
  115.     };
  116.  
  117. static    ERR_PATTERN *    exp_table = 0;
  118. static    ALLOC_T        exp_count = 0;
  119.  
  120. void
  121. set_febuff(const char *name)
  122. {
  123.     (void)strncpy0(febuff, name, NBUFN);
  124.     newfebuff = TRUE;
  125. }
  126.  
  127. /*
  128.  * Convert a given error-pattern to regular expression
  129.  */
  130.  
  131. #define    APP_S(S) if (pass == 1) want += sizeof(S); else dst = lsprintf(dst, "%s", S)
  132. #define APP_C    if (pass != 1) *dst++ = *src
  133.  
  134. static void
  135. convert_pattern(ERR_PATTERN *errp, LINE *lp)
  136. {
  137.     static    const    char    before[] = "\\(";
  138.     static    const    char    after [] = "\\+\\)";
  139.     static    const    char    number[] = "\\([0-9]\\+\\)";
  140.     static    const    char    normal[] = "\\([^ \t]\\+\\)";
  141.     static    const    char    remain[] = "\\(.\\+\\)";
  142.  
  143.     char    *temp = 0, *src, *dst = 0;
  144.     regexp    *exp = 0;
  145.     int    pass;
  146.     int    word;
  147.     int    mark;
  148.     int    range;
  149.     size_t    want  = llength(lp);
  150.     char *    first = lp->l_text;
  151.     char *    last  = first + want;
  152.  
  153.     (void) memset(errp, 0, sizeof(*errp));
  154.  
  155.     /* In the first pass, find the number of fields we'll substitute.
  156.      * Then allocate a new string that's a genuine regular expression
  157.      */
  158.     for (pass = 1; pass <= 2; pass++) {
  159.         for (src = first, word = 0, range = FALSE; src < last; src++) {
  160.             if (*src == '\\') {
  161.                 APP_C;
  162.                 if (++src == last)
  163.                     break;
  164.                 APP_C;
  165.             } else if (*src == '%') {
  166.                 mark = -1;
  167.                 switch(*++src) {
  168.                 case 'V':    mark = W_VERB;    break;
  169.                 case 'F':    mark = W_FILE;    break;
  170.                 case 'B':
  171.                     APP_S("\\(\\[[^:]\\+]\\)");
  172.                     errp->words[W_FILE] = ++word;
  173.                     break;
  174.                 case 'T':
  175.                     APP_S(remain);
  176.                     errp->words[W_TEXT] = ++word;
  177.                     break;
  178.                 case 'C':
  179.                     APP_S(number);
  180.                     errp->words[W_COLM] = ++word;
  181.                     break;
  182.                 case 'L':
  183.                     APP_S(number);
  184.                     errp->words[W_LINE] = ++word;
  185.                     break;
  186.                 case LBRACK:
  187.                     range = TRUE;
  188.                     APP_S(before);
  189.                     APP_C;
  190.                     if (src[1] == '^') {
  191.                         src++;
  192.                         APP_C;
  193.                     }
  194.                     if (src[1] == RBRACK) {
  195.                         src++;
  196.                         APP_C;
  197.                     }
  198.                     break;
  199.                 default:
  200.                     src--;
  201.                     break;
  202.                 }
  203.                 if (mark >= 0) {
  204.                     APP_S(normal);
  205.                     errp->words[mark] = ++word;
  206.                 }
  207.             } else if ((*src == RBRACK) && range) {
  208.                 APP_C;
  209.                 APP_S(after);
  210.                 range = FALSE;
  211.                 if (src+1 < last) {
  212.                     switch(*++src) {
  213.                     case 'V':    mark = W_VERB;    break;
  214.                     default:    src--;
  215.                             /* FALLTHRU */
  216.                     case 'F':    mark = W_FILE;    break;
  217.                     case 'T':    mark = W_TEXT;    break;
  218.                     }
  219.                 } else {
  220.                     mark = W_FILE;
  221.                 }
  222.                 errp->words[mark] = ++word;
  223.             } else {
  224.                 APP_C;
  225.             }
  226.         }
  227.         if (pass == 1) {
  228.             dst = temp = typeallocn(char, want);
  229.             if (dst == 0)
  230.                 break;
  231.         } else
  232.             *dst = EOS;
  233.     }
  234.     if (temp != 0) {
  235.         exp = regcomp(temp, TRUE);
  236.         free(temp);
  237.     }
  238.     errp->exp_comp = exp;
  239. }
  240.  
  241. /*
  242.  * Free the storage currently used in this module
  243.  */
  244. static void
  245. free_patterns(void)
  246. {
  247.     if (exp_table != 0) {
  248.         while (exp_count != 0)
  249.             free((char *)(exp_table[--exp_count].exp_comp));
  250.         free((char *)exp_table);
  251.     }
  252. }
  253.  
  254. #if OPT_UPBUFF
  255. /*ARGSUSED*/
  256. static int
  257. update_patterns(BUFFER *bp GCC_UNUSED)
  258. {
  259.     free_patterns();
  260.     return TRUE;
  261. }
  262. #endif
  263.  
  264. /*
  265.  * Initialize this module.  If the expressions buffer doesn't exist, load it
  266.  * from the internal table. If our cached regexp list doesn't match, recompute
  267.  * that as well.
  268.  */
  269. static int
  270. load_patterns(void)
  271. {
  272.     BUFFER    *bp;
  273.     LINE    *lp;
  274.     SIZE_T    n;
  275.  
  276.     /* find the error-expressions buffer */
  277.     if ((bp = find_b_name(ERRORS_BufName)) == 0) {
  278.         if ((bp = bfind(ERRORS_BufName, BFINVS)) == NULL)
  279.             return FALSE;
  280.  
  281.         for (n = 0; n < TABLESIZE(predefined); n++)
  282.             addline(bp, predefined[n], -1);
  283.         set_rdonly(bp, bp->b_fname, MDVIEW);
  284.         free_patterns();
  285.     } else if (b_is_changed(bp) || ((L_NUM)exp_count != bp->b_linecount)) {
  286.         free_patterns();
  287.     }
  288.     bsizes(bp);
  289.     if (bp->b_linecount == 0)
  290.         return FALSE;
  291.  
  292.     /* any change makes the patterns obsolete */
  293. #if OPT_UPBUFF
  294.     update_scratch(ERRORS_BufName, update_patterns);
  295.     bp->b_rmbuff = update_patterns;
  296. #endif
  297.  
  298.     if (exp_count == 0) {
  299.         exp_count = bp->b_linecount;
  300.         exp_table = typeallocn(ERR_PATTERN, exp_count);
  301.  
  302.         n = 0;
  303.         for_each_line(lp,bp)
  304.             convert_pattern(&exp_table[n++], lp);
  305.     }
  306.  
  307.     return TRUE;
  308. }
  309.  
  310. /*
  311.  * Initialize this module by converting the error-patterns to regular
  312.  * expressions.  Return the count'th item in the error-patterns list, or null
  313.  * if count is out of range.
  314.  */
  315. static ERR_PATTERN *
  316. next_pattern(ALLOC_T count)
  317. {
  318.     ERR_PATTERN *result = 0;
  319.  
  320.     if (count < exp_count)
  321.         result = &exp_table[count];
  322.     return (result);
  323. }
  324.  
  325. /*
  326.  * Decode the matched ERR_PATTERN
  327.  */
  328. static void
  329. decode_exp (ERR_PATTERN *exp)
  330. {
  331.     regexp *p = exp->exp_comp;
  332.     int    n;
  333.     TBUFF    *temp;
  334.  
  335.     tb_free(&fe_verb);
  336.     tb_free(&fe_file);
  337.     tb_free(&fe_text);
  338.     fe_colm = 1;
  339.     fe_line = 0;
  340.  
  341.     n = 0;
  342.     for (n = 1; (n < NSUBEXP) && p->startp[n] && p->endp[n]; n++) {
  343.         temp = 0;
  344.         if (tb_bappend(&temp,
  345.             p->startp[n],
  346.             (ALLOC_T)(p->endp[n] - p->startp[n])) == 0
  347.          || tb_append(&temp, EOS) == 0)
  348.             return;
  349.  
  350.         if (n == exp->words[W_VERB]) {
  351.             fe_verb = temp;
  352.         } else if (n == exp->words[W_FILE]) {
  353.             fe_file = temp;
  354.         } else if (n == exp->words[W_TEXT]) {
  355.             fe_text = temp;
  356.         } else {
  357.             if (n == exp->words[W_LINE])
  358.                 fe_line = atoi(tb_values(temp));
  359.             else if (n == exp->words[W_COLM])
  360.                 fe_colm = atoi(tb_values(temp));
  361.             tb_free(&temp);
  362.         }
  363.     }
  364. }
  365.  
  366. /* edits the file and goes to the line pointed at by the next compiler
  367.         error in the "[output]" window.  It unfortunately doesn't mark
  368.         the lines for you, so adding lines to the file throws off the
  369.         later numbering.  Solutions to this seem messy at the moment */
  370.  
  371. /* ARGSUSED */
  372. int
  373. finderr(int f GCC_UNUSED, int n GCC_UNUSED)
  374. {
  375.     register BUFFER *sbp;
  376.     register int s;
  377.     LINE *dotp;
  378.     int moveddot = FALSE;
  379.     ERR_PATTERN *exp;
  380.  
  381.     char *errverb;
  382.     char *errfile;
  383.     char *errtext;
  384.     char ferrfile[NFILEN];
  385.     ALLOC_T len;
  386.  
  387.     static int oerrline = -1;
  388.     static TBUFF *oerrfile;
  389.     static TBUFF *oerrtext;
  390.  
  391. #define DIRLEVELS 20
  392.     static int l = 0;
  393.     static char *dirs[DIRLEVELS];
  394.  
  395.     if (!comp_err_exps(FALSE,1))
  396.         return(FALSE);
  397.  
  398.     /* look up the right buffer */
  399.     if ((sbp = find_b_name(febuff)) == NULL) {
  400.         mlforce("[No buffer to search for errors.]");
  401.         return(FALSE);
  402.     }
  403.  
  404.     if (newfebuff) {
  405.         oerrline = -1;
  406.         oerrfile = tb_init(&oerrfile, EOS);
  407.         oerrtext = tb_init(&oerrtext, EOS);
  408.         while (l)
  409.             free(dirs[l--]);
  410.     }
  411.     newfebuff = FALSE;
  412.  
  413.     dotp = getdot(sbp);
  414.  
  415.     for_ever {
  416.         /* To use this line, we need both the filename and the line
  417.          * number in the expected places, and a different line than
  418.          * last time.
  419.          */
  420.         if (lisreal(dotp)) {
  421.             ALLOC_T    count = 0;
  422.  
  423.             while ((exp = next_pattern(count++)) != 0
  424.               && !lregexec(exp->exp_comp, dotp, 0, llength(dotp)))
  425.                 ;
  426.  
  427.             if (exp != 0) {
  428.                 decode_exp(exp);
  429.  
  430.                 errverb = tb_values(fe_verb);
  431.                 errfile = tb_values(fe_file);
  432.                 errtext = tb_values(fe_text);
  433.  
  434.                 if (errfile != 0
  435.                  && fe_line > 0) {
  436.                     if (oerrline != fe_line
  437.                      || strcmp(tb_values(oerrfile),errfile))
  438.                         break;
  439.                     if (oerrline == fe_line
  440.                      && errtext != 0
  441.                      && strcmp(tb_values(oerrtext),errtext))
  442.                          break;
  443.                 } else if (errverb != 0
  444.                     &&     errfile != 0) {
  445.                     if (!strcmp("Entering", errverb)) {
  446.                         if (l < DIRLEVELS) {
  447.                             dirs[++l] = strmalloc(errfile);
  448.                         }
  449.                     } else if (!strcmp("Leaving", errverb)) {
  450.                         if (l > 0)
  451.                             free(dirs[l--]);
  452.                     }
  453.                 }
  454.             }
  455.         }
  456.  
  457.         if (lforw(dotp) == buf_head(sbp)) {
  458.             mlwarn("[No more errors in %s buffer]", febuff);
  459.             /* start over at the top of file */
  460.             putdotback(sbp, lforw(buf_head(sbp)));
  461.             while (l)
  462.                 free(dirs[l--]);
  463.             return FALSE;
  464.         }
  465.         dotp = lforw(dotp);
  466.         moveddot = TRUE;
  467.     }
  468.     /* put the new dot back, before possible changes to contents
  469.                 of current window from getfile() */
  470.     if (moveddot)
  471.         putdotback(sbp,dotp);
  472.  
  473.     (void)pathcat(ferrfile, dirs[l], errfile);
  474.  
  475.     if (!eql_bname(curbp, ferrfile) &&
  476.         strcmp(ferrfile,curbp->b_fname)) {
  477.         /* if we must change windows */
  478.         WINDOW *wp;
  479.         for_each_visible_window(wp) {
  480.             if (eql_bname(wp->w_bufp, ferrfile)
  481.              || !strcmp(wp->w_bufp->b_fname,ferrfile))
  482.                 break;
  483.         }
  484.         if (wp) {
  485.             curwp = wp;
  486.             make_current(curwp->w_bufp);
  487.             upmode();
  488.         } else {
  489.             s = getfile(ferrfile,TRUE);
  490.             if (s != TRUE)
  491.                 return s;
  492.         }
  493.     }
  494.  
  495.     if (errtext) {
  496.         mlforce("%s", errtext);
  497.         len = strlen(errtext);
  498.     } else {
  499.         mlforce("Error: %*S", dotp->l_used, dotp->l_text);
  500.         errtext = dotp->l_text;
  501.         len = dotp->l_used;
  502.     }
  503.     if ((oerrtext = tb_init(&oerrtext, EOS)) != 0) {
  504.         tb_bappend(&oerrtext, errtext, len);
  505.         tb_append(&oerrtext, EOS);
  506.     }
  507.  
  508.     /* it's an absolute move */
  509.     curwp->w_lastdot = DOT;
  510.     s = gotoline(TRUE, -(curbp->b_lines_on_disk - fe_line + 1));
  511.     DOT.o = fe_colm ? fe_colm - 1 : 0;
  512.  
  513.     oerrline = fe_line;
  514.     (void)tb_scopy(&oerrfile, errfile);
  515.  
  516.     return s;
  517. }
  518.  
  519. static LINE *
  520. getdot(BUFFER *bp)
  521. {
  522.     register WINDOW *wp;
  523.     if (bp->b_nwnd) {
  524.         /* scan for windows holding that buffer,
  525.                     pull dot from the first */
  526.         for_each_visible_window(wp) {
  527.             if (wp->w_bufp == bp) {
  528.                 return wp->w_dot.l;
  529.             }
  530.         }
  531.     }
  532.     return bp->b_dot.l;
  533. }
  534.  
  535. static void
  536. putdotback(BUFFER *bp, LINE *dotp)
  537. {
  538.     register WINDOW *wp;
  539.  
  540.     if (bp->b_nwnd) {
  541.         for_each_visible_window(wp) {
  542.             if (wp->w_bufp == bp) {
  543.                 wp->w_dot.l = dotp;
  544.                 wp->w_dot.o = 0;
  545.                 wp->w_flag |= WFMOVE;
  546.             }
  547.         }
  548.         return;
  549.     }
  550.     /* then the buffer isn't displayed */
  551.     bp->b_dot.l = dotp;
  552.     bp->b_dot.o = 0;
  553. }
  554.  
  555. /*
  556.  * Ask for a new finderr buffer name
  557.  */
  558. /* ARGSUSED */
  559. int
  560. finderrbuf(int f GCC_UNUSED, int n GCC_UNUSED)
  561. {
  562.     register int    s;
  563.     char name[NFILEN+1];
  564.     BUFFER *bp;
  565.  
  566.     (void)strcpy(name, febuff);
  567.     if ((s = mlreply("Buffer to scan for \"errors\": ", name, sizeof(name))) == ABORT)
  568.         return s;
  569.     if (s == FALSE) {
  570.         set_febuff(OUTPUT_BufName);
  571.     } else {
  572.         if ((bp = find_any_buffer(name)) == 0)
  573.             return FALSE;
  574.         set_febuff(bp->b_bname);
  575.     }
  576.     return TRUE;
  577. }
  578.  
  579. /*
  580.  * (Re)compile the error-expressions buffer.  This is needed as an entrypoint
  581.  * so that macros can manipulate the set of expressions (including reading it
  582.  * from a file).
  583.  */
  584. /*ARGSUSED*/
  585. int
  586. comp_err_exps(int f GCC_UNUSED, int n GCC_UNUSED)
  587. {
  588.     if (!load_patterns()) {
  589.         mlforce("[No error-expressions are defined.]");
  590.         return(FALSE);
  591.     }
  592.     return TRUE;
  593. }
  594. #endif
  595.