home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / vim-2.0.lha / Vim-2.0 / src / quickfix.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-15  |  9.6 KB  |  468 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony 
  8.  *                            G. R. (Fred) Walter        watmath!watcgl!grwalter 
  9.  */
  10.  
  11. /*
  12.  * quickfix.c: functions for quickfix mode, using a file with error messages
  13.  */
  14.  
  15. #include "vim.h"
  16. #include "globals.h"
  17. #include "proto.h"
  18. #include "param.h"
  19.  
  20. static void qf_free __ARGS((void));
  21. static char *qf_types __ARGS((int, int));
  22.  
  23. /*
  24.  * for each error the next struct is allocated and linked in a list
  25.  */
  26. struct qf_line
  27. {
  28.     struct qf_line    *qf_next;    /* pointer to next error in the list */
  29.     struct qf_line    *qf_prev;    /* pointer to previous error in the list */
  30.     linenr_t         qf_lnum;    /* line number where the error occurred */
  31.     char            *qf_mark;    /* pointer to that line (if != NULL) */
  32.     int                 qf_col;    /* column where the error occurred */
  33.     int                 qf_nr;        /* error number */
  34.     char            *qf_fname;    /* file name where the error occurred */
  35.     char            *qf_text;    /* description of the error */
  36.     char             qf_cleared;/* set to TRUE if qf_mark has been cleared */
  37.     char             qf_type;    /* type of the error (mostly 'E') */
  38.     char             qf_valid;    /* valid error message detected */
  39. };
  40.  
  41. static struct qf_line *qf_start;        /* pointer to the first error */
  42. static struct qf_line *qf_ptr;            /* pointer to the current error */
  43.  
  44. static int    qf_count = 0;        /* number of errors (0 means no error list) */
  45. static int    qf_index;            /* current index in the error list */
  46. static int    qf_nonevalid;        /* set to TRUE if not a single valid entry found */
  47. static int    qf_marksset;        /* set to 1 when qf_mark-s have been set */
  48.  
  49. /*
  50.  * Read the errorfile into memory, line by line, building the error list.
  51.  * Return 1 for error, 0 for success.
  52.  */
  53.     int
  54. qf_init()
  55. {
  56.     char             namebuf[CMDBUFFSIZE + 1];
  57.     char            errmsg[CMDBUFFSIZE + 1];
  58.     int                col;
  59.     char            type;
  60.     char            valid;
  61.     long            lnum;
  62.     int                enr;
  63.     FILE            *fd;
  64.     struct qf_line    *qfp = NULL;
  65.     struct qf_line    *qfprev = NULL;        /* init to make SASC shut up */
  66.     char            *pfmt, *fmtstr;
  67. #ifdef UTS2
  68.     char            *(adr[7]);
  69. #else
  70.     void            *(adr[7]);
  71. #endif
  72.     int                adr_cnt = 0;
  73.     int                maxlen;
  74.     int                i;
  75.  
  76.     if (p_ef == NULL || *p_ef == NUL)
  77.     {
  78.         emsg(e_errorf);
  79.         return 1;
  80.     }
  81.     if ((fd = fopen(p_ef, "r")) == NULL)
  82.     {
  83.         emsg(e_openerrf);
  84.         return 1;
  85.     }
  86.     qf_free();
  87.     qf_index = 0;
  88.     for (i = 0; i < 7; ++i)
  89.         adr[i] = NULL;
  90.  
  91. /*
  92.  * The format string is copied and modified from p_efm to fmtstr.
  93.  * Only a few % characters are allowed.
  94.  */
  95.         /* get some space to modify the format string into */
  96.         /* must be able to do the largest expansion 7 times (7 x 3) */
  97.     maxlen = strlen(p_efm) + 25;
  98.     fmtstr = (char *)alloc(maxlen);
  99.     if (fmtstr == NULL)
  100.         goto error2;
  101.     for (pfmt = p_efm, i = 0; *pfmt; ++pfmt, ++i)
  102.     {
  103.         if (pfmt[0] != '%')                /* copy normal character */
  104.             fmtstr[i] = pfmt[0];
  105.         else
  106.         {
  107.             fmtstr[i++] = '%';
  108.             switch (pfmt[1])
  109.             {
  110.             case 'f':        /* filename */
  111.                     adr[adr_cnt++] = namebuf;
  112.  
  113.             case 'm':        /* message */
  114.                     if (pfmt[1] == 'm')
  115.                         adr[adr_cnt++] = errmsg;
  116.                     fmtstr[i++] = '[';
  117.                     fmtstr[i++] = '^';
  118.                     if (pfmt[2])
  119.                         fmtstr[i++] = pfmt[2];
  120.                     else
  121. #ifdef MSDOS
  122.                         fmtstr[i++] = '\r';
  123. #else
  124.                         fmtstr[i++] = '\n';
  125. #endif
  126.                     fmtstr[i] = ']';
  127.                     break;
  128.             case 'c':        /* column */
  129.                     adr[adr_cnt++] = &col;
  130.                     fmtstr[i] = 'd';
  131.                     break;
  132.             case 'l':        /* line */
  133.                     adr[adr_cnt++] = &lnum;
  134.                     fmtstr[i++] = 'l';
  135.                     fmtstr[i] = 'd';
  136.                     break;
  137.             case 'n':        /* error number */
  138.                     adr[adr_cnt++] = &enr;
  139.                     fmtstr[i] = 'd';
  140.                     break;
  141.             case 't':        /* error type */
  142.                     adr[adr_cnt++] = &type;
  143.                     fmtstr[i] = 'c';
  144.                     break;
  145.             case '%':        /* %% */
  146.             case '*':        /* %*: no assignment */
  147.                     fmtstr[i] = pfmt[1];
  148.                     break;
  149.             default:
  150.                     emsg("invalid % in format string");
  151.                     goto error2;
  152.             }
  153.             if (adr_cnt == 7)
  154.             {
  155.                 emsg("too many % in format string");
  156.                 goto error2;
  157.             }
  158.             ++pfmt;
  159.         }
  160.         if (i >= maxlen - 6)
  161.         {
  162.             emsg("invalid format string");
  163.             goto error2;
  164.         }
  165.     }
  166.     fmtstr[i] = NUL;
  167.  
  168.     while (fgets(IObuff, CMDBUFFSIZE, fd) != NULL)
  169.     {
  170.         if ((qfp = (struct qf_line *)alloc((unsigned)sizeof(struct qf_line))) == NULL)
  171.             goto error2;
  172.  
  173.         IObuff[CMDBUFFSIZE] = NUL;    /* for very long lines */
  174.         namebuf[0] = NUL;
  175.         errmsg[0] = NUL;
  176.         lnum = 0;
  177.         col = 0;
  178.         enr = -1;
  179.         type = 0;
  180.         valid = TRUE;
  181.  
  182.         if (sscanf(IObuff, fmtstr, adr[0], adr[1], adr[2], adr[3],
  183.                                                 adr[4], adr[5]) != adr_cnt)
  184.         {
  185.             namebuf[0] = NUL;            /* something failed, remove file name */
  186.             valid = FALSE;
  187.             strcpy(errmsg, IObuff);        /* copy whole line to error message */
  188.             if ((pfmt = strrchr(errmsg, '\n')) != NULL)
  189.                 *pfmt = NUL;
  190. #ifdef MSDOS
  191.             if ((pfmt = strrchr(errmsg, '\r')) != NULL)
  192.                 *pfmt = NUL;
  193. #endif
  194.         }
  195.  
  196.         if ((qfp->qf_fname = strsave(namebuf)) == NULL)
  197.             goto error1;
  198.         if ((qfp->qf_text = strsave(errmsg)) == NULL)
  199.         {
  200.             free(qfp->qf_fname);
  201.             goto error1;
  202.         }
  203.         qfp->qf_lnum = lnum;
  204.         qfp->qf_col = col;
  205.         qfp->qf_nr = enr;
  206.         qfp->qf_type = type;
  207.         qfp->qf_valid = valid;
  208.  
  209.         if (qf_count == 0)        /* first element in the list */
  210.         {
  211.             qf_start = qfp;
  212.             qfp->qf_prev = qfp;    /* first element points to itself */
  213.         }
  214.         else
  215.         {
  216.             qfp->qf_prev = qfprev;
  217.             qfprev->qf_next = qfp;
  218.         }
  219.         qfp->qf_next = qfp;        /* last element points to itself */
  220.         qfp->qf_mark = NULL;
  221.         qfp->qf_cleared = FALSE;
  222.         qfprev = qfp;
  223.         ++qf_count;
  224.         if (qf_index == 0 && qfp->qf_valid)        /* first valid entry */
  225.         {
  226.             qf_index = qf_count;
  227.             qf_ptr = qfp;
  228.         }
  229.     }
  230.     free(fmtstr);
  231.     if (!ferror(fd))
  232.     {
  233.         if (qf_index == 0)        /* no valid entry found */
  234.         {
  235.             qf_ptr = qf_start;
  236.             qf_index = 1;
  237.             qf_nonevalid = TRUE;
  238.         }
  239.         else
  240.             qf_nonevalid = FALSE;
  241.         fclose(fd);
  242.         qf_jump(0);                /* display first error */
  243.         return 0;
  244.     }
  245.     emsg(e_readerrf);
  246. error1:
  247.     free(qfp);
  248. error2:
  249.     fclose(fd);
  250.     qf_free();
  251.     return 1;
  252. }
  253.  
  254. /*
  255.  * jump to quickfix line "errornr"; if "errornr" is zero, redisplay the same line
  256.  */
  257.     void
  258. qf_jump(errornr)
  259.     int errornr;
  260. {
  261.     struct qf_line *qfp;
  262.     linenr_t        i;
  263.     char            *msgp;
  264.  
  265.     if (qf_count == 0)
  266.     {
  267.         emsg(e_quickfix);
  268.         return;
  269.     }
  270.  
  271.     if (errornr == -1)        /* next valid entry */
  272.     {
  273.         do
  274.         {
  275.             if (qf_index == qf_count)
  276.                 break;
  277.             ++qf_index;
  278.             qf_ptr = qf_ptr->qf_next;
  279.         } while (!qf_nonevalid && !qf_ptr->qf_valid);
  280.     }
  281.     else if (errornr == -2)        /* previous valid entry */
  282.     {
  283.         do
  284.         {
  285.             if (qf_index == 1)
  286.                 break;
  287.             --qf_index;
  288.             qf_ptr = qf_ptr->qf_prev;
  289.         } while (!qf_nonevalid && !qf_ptr->qf_valid);
  290.     }
  291.     else if (errornr != 0)        /* go to specified number */
  292.     {
  293.         while (errornr < qf_index && qf_index > 1)
  294.         {
  295.             --qf_index;
  296.             qf_ptr = qf_ptr->qf_prev;
  297.         }
  298.         while (errornr > qf_index && qf_index < qf_count)
  299.         {
  300.             ++qf_index;
  301.             qf_ptr = qf_ptr->qf_next;
  302.         }
  303.     }
  304.  
  305.     /*
  306.      * If there is a file name, 
  307.      * read the wanted file if needed, and check autowrite etc.
  308.      */
  309.     if (qf_ptr->qf_fname[0] == NUL || getfile(qf_ptr->qf_fname, NULL, TRUE) <= 0)
  310.     {
  311.         /*
  312.          * Use mark if possible, because the line number may be invalid
  313.          * after line inserts / deletes.
  314.          * If qf_lnum is 0, stay on the same line.
  315.          */
  316.         i = 0;
  317.         msgp = "";
  318.         if ((qf_ptr->qf_mark != NULL && (i = ptr2nr(qf_ptr->qf_mark, (linenr_t)0)) == 0) || qf_ptr->qf_cleared)
  319.             msgp = "(line changed) ";
  320.         if (i == 0)
  321.             i = qf_ptr->qf_lnum;
  322.         if (i > line_count)
  323.             i = line_count;
  324.         if (i > 0)
  325.             Curpos.lnum = i;
  326.         Curpos.col = qf_ptr->qf_col;
  327.         adjustCurpos();
  328.         cursupdate();
  329.         smsg("(%d of %d) %s%s: %s", qf_index, qf_count, msgp,
  330.                     qf_types(qf_ptr->qf_type, qf_ptr->qf_nr), qf_ptr->qf_text);
  331.  
  332.         if (!qf_marksset)        /* marks not set yet: try to find them for
  333.                                     the errors in the curren file */
  334.         {
  335.             for (i = 0, qfp = qf_start; i < qf_count; ++i, qfp = qfp->qf_next)
  336.                 if (qfp->qf_fname != NUL && strcmp(qfp->qf_fname, qf_ptr->qf_fname) == 0 && qfp->qf_lnum <= line_count && qfp->qf_lnum > 0)
  337.                     qfp->qf_mark = nr2ptr(qfp->qf_lnum);
  338.             qf_marksset = 1;
  339.         }
  340.     }
  341. }
  342.  
  343. /*
  344.  * list all errors
  345.  */
  346.     void
  347. qf_list()
  348. {
  349.     struct qf_line *qfp;
  350.     int i;
  351.  
  352.     if (qf_count == 0)
  353.     {
  354.         emsg(e_quickfix);
  355.         return;
  356.     }
  357.     qfp = qf_start;
  358.     gotocmdline(TRUE, NUL);
  359. #ifdef AMIGA
  360.     settmode(0);        /* set cooked mode so output can be halted */
  361. #endif
  362.     for (i = 1; i <= qf_count; ++i)
  363.     {
  364.         sprintf(IObuff, "%2d line %3ld col %2d %s: %s",
  365.             i,
  366.             (long)qfp->qf_lnum,
  367.             qfp->qf_col,
  368.             qf_types(qfp->qf_type, qfp->qf_nr),
  369.             qfp->qf_text);
  370.         outstr(IObuff);
  371.         outchar('\n');
  372.         qfp = qfp->qf_next;
  373.         flushbuf();
  374.     }
  375. #ifdef AMIGA
  376.     settmode(1);
  377. #endif
  378.     wait_return(TRUE);
  379. }
  380.  
  381. /*
  382.  * free the error list
  383.  */
  384.     static void
  385. qf_free()
  386. {
  387.     struct qf_line *qfp;
  388.  
  389.     while (qf_count)
  390.     {
  391.         qfp = qf_start->qf_next;
  392.         free(qf_start->qf_fname);
  393.         free(qf_start->qf_text);
  394.         free(qf_start);
  395.         qf_start = qfp;
  396.         --qf_count;
  397.     }
  398.     qf_marksset = 0;
  399. }
  400.  
  401. /*
  402.  * qf_clrallmarks() - clear all marks
  403.  *
  404.  * Used mainly when trashing the entire buffer during ":e" type commands
  405.  */
  406.     void
  407. qf_clrallmarks()
  408. {
  409.     int             i;
  410.     struct qf_line *qfp;
  411.  
  412.     if (qf_count)
  413.         for (i = 0, qfp = qf_start; i < qf_count; i++, qfp = qfp->qf_next)
  414.             qfp->qf_mark = NULL;
  415.     qf_marksset = 0;
  416. }
  417.  
  418. /*
  419.  * qf_adjustmark: set new ptr for a mark
  420.  */
  421.    void
  422. qf_adjustmark(old, new)
  423.     char        *old, *new;
  424. {
  425.     register int i;
  426.     struct qf_line *qfp;
  427.  
  428.     if (qf_count)
  429.     {
  430.         for (i = 0, qfp = qf_start; i < qf_count; ++i, qfp = qfp->qf_next)
  431.             if (qfp->qf_mark == old)
  432.             {
  433.                 qfp->qf_mark = new;
  434.                 if (new == NULL)
  435.                     qfp->qf_cleared = TRUE;
  436.             }
  437.     }
  438. }
  439.  
  440. /*
  441.  * Make a nice message out of the error character and the error number:
  442.  *    char    number        message
  443.  *  e or E    0            "  Error"
  444.  *  w or W    0            "Warning"
  445.  *  other     0             ""
  446.  *  w or W    n            "Warning n"
  447.  *  other     n            "  Error n"
  448.  */
  449.     static char *
  450. qf_types(c, nr)
  451.     int c, nr;
  452. {
  453.     static char    buf[20];
  454.     char        *p1;
  455.  
  456.     p1 = "  Error";
  457.     if (c == 'W' || c == 'w')
  458.         p1 =  "Warning";
  459.     else if (nr <= 0 && c != 'E' && c != 'e')
  460.         p1 = "";
  461.  
  462.     if (nr <= 0)
  463.         return p1;
  464.  
  465.     sprintf(buf, "%s %3d", p1, nr);
  466.     return buf;
  467. }
  468.