Annotation of src/usr.bin/vim/quickfix.c, Revision 1.1.1.1
1.1 downsj 1: /* $OpenBSD$ */
2: /* vi:set ts=4 sw=4:
3: *
4: * VIM - Vi IMproved by Bram Moolenaar
5: *
6: * Do ":help uganda" in Vim to read copying and usage conditions.
7: * Do ":help credits" in Vim to see a list of people who contributed.
8: */
9:
10: /*
11: * quickfix.c: functions for quickfix mode, using a file with error messages
12: */
13:
14: #include "vim.h"
15: #include "globals.h"
16: #include "proto.h"
17: #include "option.h"
18:
19: static void qf_free __ARGS((void));
20: static char_u *qf_types __ARGS((int, int));
21:
22: /*
23: * for each error the next struct is allocated and linked in a list
24: */
25: struct qf_line
26: {
27: struct qf_line *qf_next; /* pointer to next error in the list */
28: struct qf_line *qf_prev; /* pointer to previous error in the list */
29: linenr_t qf_lnum; /* line number where the error occurred */
30: int qf_fnum; /* file number for the line */
31: int qf_col; /* column where the error occurred */
32: int qf_nr; /* error number */
33: char_u *qf_text; /* description of the error */
34: char_u qf_cleared;/* set to TRUE if line has been deleted */
35: char_u qf_type; /* type of the error (mostly 'E') */
36: char_u qf_valid; /* valid error message detected */
37: };
38:
39: static struct qf_line *qf_start; /* pointer to the first error */
40: static struct qf_line *qf_ptr; /* pointer to the current error */
41:
42: static int qf_count = 0; /* number of errors (0 means no error list) */
43: static int qf_index; /* current index in the error list */
44: static int qf_nonevalid; /* set to TRUE if not a single valid entry found */
45:
46: #define MAX_ADDR 7 /* maximum number of % recognized, also adjust
47: sscanf() below */
48:
49: /*
50: * Structure used to hold the info of one part of 'errorformat'
51: */
52: struct eformat
53: {
54: char_u *fmtstr; /* pre-formatted part of 'errorformat' */
55: #ifdef UTS2
56: char_u *(adr[MAX_ADDR]); /* addresses used */
57: #else
58: void *(adr[MAX_ADDR]);
59: #endif
60: int adr_cnt; /* number of addresses used */
61: struct eformat *next; /* pointer to next (NULL if last) */
62: };
63:
64: /*
65: * Read the errorfile into memory, line by line, building the error list.
66: * Return FAIL for error, OK for success.
67: */
68: int
69: qf_init()
70: {
71: char_u *namebuf;
72: char_u *errmsg;
73: int col;
74: int type;
75: int valid;
76: long lnum;
77: int enr;
78: FILE *fd;
79: struct qf_line *qfp = NULL;
80: struct qf_line *qfprev = NULL; /* init to make SASC shut up */
81: char_u *efmp;
82: struct eformat *fmt_first = NULL;
83: struct eformat *fmt_last = NULL;
84: struct eformat *fmt_ptr;
85: char_u *efm;
86: int maxlen;
87: int len;
88: int i, j;
89: int retval = FAIL;
90:
91: if (*p_ef == NUL)
92: {
93: emsg(e_errorf);
94: return FAIL;
95: }
96:
97: namebuf = alloc(CMDBUFFSIZE + 1);
98: errmsg = alloc(CMDBUFFSIZE + 1);
99: if (namebuf == NULL || errmsg == NULL)
100: goto qf_init_end;
101:
102: if ((fd = fopen((char *)p_ef, "r")) == NULL)
103: {
104: emsg2(e_openerrf, p_ef);
105: goto qf_init_end;
106: }
107: qf_free();
108: qf_index = 0;
109:
110: /*
111: * Each part of the format string is copied and modified from p_efm to fmtstr.
112: * Only a few % characters are allowed.
113: */
114: efm = p_efm;
115: while (efm[0])
116: {
117: /*
118: * Allocate a new eformat structure and put it at the end of the list
119: */
120: fmt_ptr = (struct eformat *)alloc((unsigned)sizeof(struct eformat));
121: if (fmt_ptr == NULL)
122: goto error2;
123: if (fmt_first == NULL) /* first one */
124: fmt_first = fmt_ptr;
125: else
126: fmt_last->next = fmt_ptr;
127: fmt_last = fmt_ptr;
128: fmt_ptr->next = NULL;
129: fmt_ptr->adr_cnt = 0;
130:
131: /*
132: * Isolate one part in the 'errorformat' option
133: */
134: for (len = 0; efm[len] != NUL && efm[len] != ','; ++len)
135: if (efm[len] == '\\' && efm[len + 1] != NUL)
136: ++len;
137:
138: /*
139: * Get some space to modify the format string into.
140: * Must be able to do the largest expansion (x3) MAX_ADDR times.
141: */
142: maxlen = len + MAX_ADDR * 3 + 4;
143: if ((fmt_ptr->fmtstr = alloc(maxlen)) == NULL)
144: goto error2;
145:
146: for (i = 0; i < MAX_ADDR; ++i)
147: fmt_ptr->adr[i] = NULL;
148:
149: for (efmp = efm, i = 0; efmp < efm + len; ++efmp, ++i)
150: {
151: if (efmp[0] != '%') /* copy normal character */
152: {
153: if (efmp[0] == '\\' && efmp + 1 < efm + len)
154: ++efmp;
155: fmt_ptr->fmtstr[i] = efmp[0];
156: }
157: else
158: {
159: fmt_ptr->fmtstr[i++] = '%';
160: switch (efmp[1])
161: {
162: case 'f': /* filename */
163: fmt_ptr->adr[fmt_ptr->adr_cnt++] = namebuf;
164: /* FALLTHROUGH */
165:
166: case 'm': /* message */
167: if (efmp[1] == 'm')
168: fmt_ptr->adr[fmt_ptr->adr_cnt++] = errmsg;
169: fmt_ptr->fmtstr[i++] = '[';
170: fmt_ptr->fmtstr[i++] = '^';
171: #ifdef __EMX__
172: /* don't allow spaces in filename. This fixes
173: * the broken sscanf() where an empty message
174: * is accepted as a valid conversion.
175: */
176: if (efmp[1] == 'f')
177: fmt_ptr->fmtstr[i++] = ' ';
178: #endif
179: if (efmp[2] == '\\') /* could be "%m\," */
180: j = 3;
181: else
182: j = 2;
183: if (efmp + j < efm + len)
184: fmt_ptr->fmtstr[i++] = efmp[j];
185: else
186: {
187: /*
188: * The %f or %m is the last one in the format,
189: * stop at the CR of NL at the end of the line.
190: */
191: #ifdef USE_CRNL
192: fmt_ptr->fmtstr[i++] = '\r';
193: #endif
194: fmt_ptr->fmtstr[i++] = '\n';
195: }
196: fmt_ptr->fmtstr[i] = ']';
197: break;
198: case 'c': /* column */
199: fmt_ptr->adr[fmt_ptr->adr_cnt++] = &col;
200: fmt_ptr->fmtstr[i] = 'd';
201: break;
202: case 'l': /* line */
203: fmt_ptr->adr[fmt_ptr->adr_cnt++] = &lnum;
204: fmt_ptr->fmtstr[i++] = 'l';
205: fmt_ptr->fmtstr[i] = 'd';
206: break;
207: case 'n': /* error number */
208: fmt_ptr->adr[fmt_ptr->adr_cnt++] = &enr;
209: fmt_ptr->fmtstr[i] = 'd';
210: break;
211: case 't': /* error type */
212: fmt_ptr->adr[fmt_ptr->adr_cnt++] = &type;
213: fmt_ptr->fmtstr[i] = 'c';
214: break;
215: case '%': /* %% */
216: case '*': /* %*: no assignment */
217: fmt_ptr->fmtstr[i] = efmp[1];
218: break;
219: default:
220: EMSG("invalid % in format string");
221: goto error2;
222: }
223: if (fmt_ptr->adr_cnt == MAX_ADDR)
224: {
225: EMSG("too many % in format string");
226: goto error2;
227: }
228: ++efmp;
229: }
230: if (i >= maxlen - 6)
231: {
232: EMSG("invalid format string");
233: goto error2;
234: }
235: }
236: fmt_ptr->fmtstr[i] = NUL;
237:
238: /*
239: * Advance to next part
240: */
241: efm = skip_to_option_part(efm + len); /* skip comma and spaces */
242: }
243: if (fmt_first == NULL) /* nothing found */
244: {
245: EMSG("'errorformat' contains no pattern");
246: goto error2;
247: }
248:
249: /*
250: * Read the lines in the error file one by one.
251: * Try to recognize one of the error formats in each line.
252: */
253: while (fgets((char *)IObuff, CMDBUFFSIZE, fd) != NULL && !got_int)
254: {
255: if ((qfp = (struct qf_line *)alloc((unsigned)sizeof(struct qf_line)))
256: == NULL)
257: goto error2;
258:
259: IObuff[CMDBUFFSIZE] = NUL; /* for very long lines */
260:
261: /*
262: * Try to match each part of 'errorformat' until we find a complete
263: * match or none matches.
264: */
265: valid = TRUE;
266: for (fmt_ptr = fmt_first; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next)
267: {
268: namebuf[0] = NUL;
269: errmsg[0] = NUL;
270: lnum = 0;
271: col = 0;
272: enr = -1;
273: type = 0;
274:
275: /*
276: * If first char of the format and message don't match, there is
277: * no need to try sscanf() on it... Somehow I believe there are
278: * very slow implementations of sscanf().
279: * -- Paul Slootman
280: */
281: if (fmt_ptr->fmtstr[0] != '%' && fmt_ptr->fmtstr[0] != IObuff[0])
282: continue;
283:
284: if (sscanf((char *)IObuff, (char *)fmt_ptr->fmtstr,
285: fmt_ptr->adr[0], fmt_ptr->adr[1], fmt_ptr->adr[2],
286: fmt_ptr->adr[3], fmt_ptr->adr[4], fmt_ptr->adr[5],
287: fmt_ptr->adr[6]) == fmt_ptr->adr_cnt)
288: break;
289: }
290: if (fmt_ptr == NULL)
291: {
292: namebuf[0] = NUL; /* no match found, remove file name */
293: lnum = 0; /* don't jump to this line */
294: valid = FALSE;
295: STRCPY(errmsg, IObuff); /* copy whole line to error message */
296: if ((efmp = vim_strrchr(errmsg, '\n')) != NULL)
297: *efmp = NUL;
298: #ifdef USE_CRNL
299: if ((efmp = vim_strrchr(errmsg, '\r')) != NULL)
300: *efmp = NUL;
301: #endif
302: }
303:
304: if (namebuf[0] == NUL) /* no file name */
305: qfp->qf_fnum = 0;
306: else
307: qfp->qf_fnum = buflist_add(namebuf);
308: if ((qfp->qf_text = strsave(errmsg)) == NULL)
309: goto error1;
310: qfp->qf_lnum = lnum;
311: qfp->qf_col = col;
312: qfp->qf_nr = enr;
313: qfp->qf_type = type;
314: qfp->qf_valid = valid;
315:
316: if (qf_count == 0) /* first element in the list */
317: {
318: qf_start = qfp;
319: qfp->qf_prev = qfp; /* first element points to itself */
320: }
321: else
322: {
323: qfp->qf_prev = qfprev;
324: qfprev->qf_next = qfp;
325: }
326: qfp->qf_next = qfp; /* last element points to itself */
327: qfp->qf_cleared = FALSE;
328: qfprev = qfp;
329: ++qf_count;
330: if (qf_index == 0 && qfp->qf_valid) /* first valid entry */
331: {
332: qf_index = qf_count;
333: qf_ptr = qfp;
334: }
335: line_breakcheck();
336: }
337: if (!ferror(fd))
338: {
339: if (qf_index == 0) /* no valid entry found */
340: {
341: qf_ptr = qf_start;
342: qf_index = 1;
343: qf_nonevalid = TRUE;
344: }
345: else
346: qf_nonevalid = FALSE;
347: retval = OK;
348: goto qf_init_ok;
349: }
350: emsg(e_readerrf);
351: error1:
352: vim_free(qfp);
353: error2:
354: qf_free();
355: qf_init_ok:
356: fclose(fd);
357: for (fmt_ptr = fmt_first; fmt_ptr != NULL; fmt_ptr = fmt_first)
358: {
359: fmt_first = fmt_ptr->next;
360: vim_free(fmt_ptr->fmtstr);
361: vim_free(fmt_ptr);
362: }
363: qf_init_end:
364: vim_free(namebuf);
365: vim_free(errmsg);
366: return retval;
367: }
368:
369: /*
370: * jump to a quickfix line
371: * if dir == FORWARD go "errornr" valid entries forward
372: * if dir == BACKWARD go "errornr" valid entries backward
373: * else if "errornr" is zero, redisplay the same line
374: * else go to entry "errornr"
375: */
376: void
377: qf_jump(dir, errornr)
378: int dir;
379: int errornr;
380: {
381: struct qf_line *old_qf_ptr;
382: int old_qf_index;
383: static char_u *e_no_more_errors = (char_u *)"No more errors";
384: char_u *err = e_no_more_errors;
385: linenr_t i;
386:
387: if (qf_count == 0)
388: {
389: emsg(e_quickfix);
390: return;
391: }
392:
393: old_qf_ptr = qf_ptr;
394: old_qf_index = qf_index;
395: if (dir == FORWARD) /* next valid entry */
396: {
397: while (errornr--)
398: {
399: old_qf_ptr = qf_ptr;
400: old_qf_index = qf_index;
401: do
402: {
403: if (qf_index == qf_count || qf_ptr->qf_next == NULL)
404: {
405: qf_ptr = old_qf_ptr;
406: qf_index = old_qf_index;
407: if (err != NULL)
408: {
409: emsg(err);
410: return;
411: }
412: errornr = 0;
413: break;
414: }
415: ++qf_index;
416: qf_ptr = qf_ptr->qf_next;
417: } while (!qf_nonevalid && !qf_ptr->qf_valid);
418: err = NULL;
419: }
420: }
421: else if (dir == BACKWARD) /* previous valid entry */
422: {
423: while (errornr--)
424: {
425: old_qf_ptr = qf_ptr;
426: old_qf_index = qf_index;
427: do
428: {
429: if (qf_index == 1 || qf_ptr->qf_prev == NULL)
430: {
431: qf_ptr = old_qf_ptr;
432: qf_index = old_qf_index;
433: if (err != NULL)
434: {
435: emsg(err);
436: return;
437: }
438: errornr = 0;
439: break;
440: }
441: --qf_index;
442: qf_ptr = qf_ptr->qf_prev;
443: } while (!qf_nonevalid && !qf_ptr->qf_valid);
444: err = NULL;
445: }
446: }
447: else if (errornr != 0) /* go to specified number */
448: {
449: while (errornr < qf_index && qf_index > 1 && qf_ptr->qf_prev != NULL)
450: {
451: --qf_index;
452: qf_ptr = qf_ptr->qf_prev;
453: }
454: while (errornr > qf_index && qf_index < qf_count && qf_ptr->qf_next != NULL)
455: {
456: ++qf_index;
457: qf_ptr = qf_ptr->qf_next;
458: }
459: }
460:
461: /*
462: * If there is a file name,
463: * read the wanted file if needed, and check autowrite etc.
464: */
465: if (qf_ptr->qf_fnum == 0 || buflist_getfile(qf_ptr->qf_fnum,
466: (linenr_t)1, GETF_SETMARK) == OK)
467: {
468: /*
469: * Go to line with error, unless qf_lnum is 0.
470: */
471: i = qf_ptr->qf_lnum;
472: if (i > 0)
473: {
474: if (i > curbuf->b_ml.ml_line_count)
475: i = curbuf->b_ml.ml_line_count;
476: curwin->w_cursor.lnum = i;
477: }
478: if (qf_ptr->qf_col > 0)
479: {
480: curwin->w_cursor.col = qf_ptr->qf_col - 1;
481: adjust_cursor();
482: }
483: else
484: beginline(TRUE);
485: cursupdate();
486: smsg((char_u *)"(%d of %d)%s%s: %s", qf_index, qf_count,
487: qf_ptr->qf_cleared ? (char_u *)" (line deleted)" : (char_u *)"",
488: qf_types(qf_ptr->qf_type, qf_ptr->qf_nr), qf_ptr->qf_text);
489: /*
490: * if the message is short, redisplay after redrawing the screen
491: */
492: if (linetabsize(IObuff) < ((int)p_ch - 1) * Columns + sc_col)
493: keep_msg = IObuff;
494: }
495: else if (qf_ptr->qf_fnum != 0)
496: {
497: /*
498: * Couldn't open file, so put index back where it was. This could
499: * happen if the file was readonly and we changed something - webb
500: */
501: qf_ptr = old_qf_ptr;
502: qf_index = old_qf_index;
503: }
504: }
505:
506: /*
507: * list all errors
508: */
509: void
510: qf_list(all)
511: int all; /* If not :cl!, only show recognised errors */
512: {
513: BUF *buf;
514: char_u *fname;
515: struct qf_line *qfp;
516: int i;
517:
518: if (qf_count == 0)
519: {
520: emsg(e_quickfix);
521: return;
522: }
523:
524: if (qf_nonevalid)
525: all = TRUE;
526: qfp = qf_start;
527: set_highlight('d'); /* Same as for directories */
528: for (i = 1; !got_int && i <= qf_count; ++i)
529: {
530: if (qfp->qf_valid || all)
531: {
532: msg_outchar('\n');
533: start_highlight();
534: fname = NULL;
535: if (qfp->qf_fnum != 0 &&
536: (buf = buflist_findnr(qfp->qf_fnum)) != NULL)
537: fname = buf->b_xfilename;
538: if (fname == NULL)
539: sprintf((char *)IObuff, "%2d", i);
540: else
541: sprintf((char *)IObuff, "%2d %s", i, fname);
542: msg_outtrans(IObuff);
543: stop_highlight();
544: if (qfp->qf_lnum == 0)
545: IObuff[0] = NUL;
546: else if (qfp->qf_col == 0)
547: sprintf((char *)IObuff, ":%ld", qfp->qf_lnum);
548: else
549: sprintf((char *)IObuff, ":%ld, col %d",
550: qfp->qf_lnum, qfp->qf_col);
551: sprintf((char *)IObuff + STRLEN(IObuff), "%s: ",
552: qf_types(qfp->qf_type, qfp->qf_nr));
553: msg_outstr(IObuff);
554: msg_prt_line(qfp->qf_text);
555: flushbuf(); /* show one line at a time */
556: }
557: qfp = qfp->qf_next;
558: mch_breakcheck();
559: }
560: }
561:
562: /*
563: * free the error list
564: */
565: static void
566: qf_free()
567: {
568: struct qf_line *qfp;
569:
570: while (qf_count)
571: {
572: qfp = qf_start->qf_next;
573: vim_free(qf_start->qf_text);
574: vim_free(qf_start);
575: qf_start = qfp;
576: --qf_count;
577: }
578: }
579:
580: /*
581: * qf_mark_adjust: adjust marks
582: */
583: void
584: qf_mark_adjust(line1, line2, amount, amount_after)
585: linenr_t line1;
586: linenr_t line2;
587: long amount;
588: long amount_after;
589: {
590: register int i;
591: struct qf_line *qfp;
592:
593: if (qf_count)
594: for (i = 0, qfp = qf_start; i < qf_count; ++i, qfp = qfp->qf_next)
595: if (qfp->qf_fnum == curbuf->b_fnum)
596: {
597: if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2)
598: {
599: if (amount == MAXLNUM)
600: qfp->qf_cleared = TRUE;
601: else
602: qfp->qf_lnum += amount;
603: }
604: if (amount_after && qfp->qf_lnum > line2)
605: qfp->qf_lnum += amount_after;
606: }
607: }
608:
609: /*
610: * Make a nice message out of the error character and the error number:
611: * char number message
612: * e or E 0 " error"
613: * w or W 0 " warning"
614: * other 0 ""
615: * w or W n " warning n"
616: * other n " error n"
617: */
618: static char_u *
619: qf_types(c, nr)
620: int c, nr;
621: {
622: static char_u buf[20];
623: char_u *p1;
624:
625: p1 = (char_u *)" error";
626: if (c == 'W' || c == 'w')
627: p1 = (char_u *)" warning";
628: else if (nr <= 0 && c != 'E' && c != 'e')
629: p1 = (char_u *)"";
630:
631: if (nr <= 0)
632: return p1;
633:
634: sprintf((char *)buf, "%s %3d", p1, nr);
635: return buf;
636: }