Annotation of src/usr.bin/vim/quickfix.c, Revision 1.2
1.2 ! downsj 1: /* $OpenBSD: quickfix.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
1.1 downsj 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
1.2 ! downsj 377: qf_jump(dir, errornr, forceit)
1.1 downsj 378: int dir;
379: int errornr;
1.2 ! downsj 380: int forceit;
1.1 downsj 381: {
382: struct qf_line *old_qf_ptr;
383: int old_qf_index;
384: static char_u *e_no_more_errors = (char_u *)"No more errors";
385: char_u *err = e_no_more_errors;
386: linenr_t i;
387:
388: if (qf_count == 0)
389: {
390: emsg(e_quickfix);
391: return;
392: }
393:
394: old_qf_ptr = qf_ptr;
395: old_qf_index = qf_index;
396: if (dir == FORWARD) /* next valid entry */
397: {
398: while (errornr--)
399: {
400: old_qf_ptr = qf_ptr;
401: old_qf_index = qf_index;
402: do
403: {
404: if (qf_index == qf_count || qf_ptr->qf_next == NULL)
405: {
406: qf_ptr = old_qf_ptr;
407: qf_index = old_qf_index;
408: if (err != NULL)
409: {
410: emsg(err);
411: return;
412: }
413: errornr = 0;
414: break;
415: }
416: ++qf_index;
417: qf_ptr = qf_ptr->qf_next;
418: } while (!qf_nonevalid && !qf_ptr->qf_valid);
419: err = NULL;
420: }
421: }
422: else if (dir == BACKWARD) /* previous valid entry */
423: {
424: while (errornr--)
425: {
426: old_qf_ptr = qf_ptr;
427: old_qf_index = qf_index;
428: do
429: {
430: if (qf_index == 1 || qf_ptr->qf_prev == NULL)
431: {
432: qf_ptr = old_qf_ptr;
433: qf_index = old_qf_index;
434: if (err != NULL)
435: {
436: emsg(err);
437: return;
438: }
439: errornr = 0;
440: break;
441: }
442: --qf_index;
443: qf_ptr = qf_ptr->qf_prev;
444: } while (!qf_nonevalid && !qf_ptr->qf_valid);
445: err = NULL;
446: }
447: }
448: else if (errornr != 0) /* go to specified number */
449: {
450: while (errornr < qf_index && qf_index > 1 && qf_ptr->qf_prev != NULL)
451: {
452: --qf_index;
453: qf_ptr = qf_ptr->qf_prev;
454: }
455: while (errornr > qf_index && qf_index < qf_count && qf_ptr->qf_next != NULL)
456: {
457: ++qf_index;
458: qf_ptr = qf_ptr->qf_next;
459: }
460: }
461:
462: /*
463: * If there is a file name,
464: * read the wanted file if needed, and check autowrite etc.
465: */
466: if (qf_ptr->qf_fnum == 0 || buflist_getfile(qf_ptr->qf_fnum,
1.2 ! downsj 467: (linenr_t)1, GETF_SETMARK, forceit) == OK)
1.1 downsj 468: {
469: /*
470: * Go to line with error, unless qf_lnum is 0.
471: */
472: i = qf_ptr->qf_lnum;
473: if (i > 0)
474: {
475: if (i > curbuf->b_ml.ml_line_count)
476: i = curbuf->b_ml.ml_line_count;
477: curwin->w_cursor.lnum = i;
478: }
479: if (qf_ptr->qf_col > 0)
480: {
481: curwin->w_cursor.col = qf_ptr->qf_col - 1;
482: adjust_cursor();
483: }
484: else
485: beginline(TRUE);
486: cursupdate();
487: smsg((char_u *)"(%d of %d)%s%s: %s", qf_index, qf_count,
488: qf_ptr->qf_cleared ? (char_u *)" (line deleted)" : (char_u *)"",
489: qf_types(qf_ptr->qf_type, qf_ptr->qf_nr), qf_ptr->qf_text);
490: /*
491: * if the message is short, redisplay after redrawing the screen
492: */
493: if (linetabsize(IObuff) < ((int)p_ch - 1) * Columns + sc_col)
494: keep_msg = IObuff;
495: }
496: else if (qf_ptr->qf_fnum != 0)
497: {
498: /*
499: * Couldn't open file, so put index back where it was. This could
500: * happen if the file was readonly and we changed something - webb
501: */
502: qf_ptr = old_qf_ptr;
503: qf_index = old_qf_index;
504: }
505: }
506:
507: /*
508: * list all errors
509: */
510: void
511: qf_list(all)
512: int all; /* If not :cl!, only show recognised errors */
513: {
514: BUF *buf;
515: char_u *fname;
516: struct qf_line *qfp;
517: int i;
518:
519: if (qf_count == 0)
520: {
521: emsg(e_quickfix);
522: return;
523: }
524:
525: if (qf_nonevalid)
526: all = TRUE;
527: qfp = qf_start;
528: set_highlight('d'); /* Same as for directories */
529: for (i = 1; !got_int && i <= qf_count; ++i)
530: {
531: if (qfp->qf_valid || all)
532: {
533: msg_outchar('\n');
534: start_highlight();
535: fname = NULL;
536: if (qfp->qf_fnum != 0 &&
537: (buf = buflist_findnr(qfp->qf_fnum)) != NULL)
538: fname = buf->b_xfilename;
539: if (fname == NULL)
540: sprintf((char *)IObuff, "%2d", i);
541: else
542: sprintf((char *)IObuff, "%2d %s", i, fname);
543: msg_outtrans(IObuff);
544: stop_highlight();
545: if (qfp->qf_lnum == 0)
546: IObuff[0] = NUL;
547: else if (qfp->qf_col == 0)
548: sprintf((char *)IObuff, ":%ld", qfp->qf_lnum);
549: else
550: sprintf((char *)IObuff, ":%ld, col %d",
551: qfp->qf_lnum, qfp->qf_col);
552: sprintf((char *)IObuff + STRLEN(IObuff), "%s: ",
553: qf_types(qfp->qf_type, qfp->qf_nr));
554: msg_outstr(IObuff);
555: msg_prt_line(qfp->qf_text);
556: flushbuf(); /* show one line at a time */
557: }
558: qfp = qfp->qf_next;
559: mch_breakcheck();
560: }
561: }
562:
563: /*
564: * free the error list
565: */
566: static void
567: qf_free()
568: {
569: struct qf_line *qfp;
570:
571: while (qf_count)
572: {
573: qfp = qf_start->qf_next;
574: vim_free(qf_start->qf_text);
575: vim_free(qf_start);
576: qf_start = qfp;
577: --qf_count;
578: }
579: }
580:
581: /*
582: * qf_mark_adjust: adjust marks
583: */
584: void
585: qf_mark_adjust(line1, line2, amount, amount_after)
586: linenr_t line1;
587: linenr_t line2;
588: long amount;
589: long amount_after;
590: {
591: register int i;
592: struct qf_line *qfp;
593:
594: if (qf_count)
595: for (i = 0, qfp = qf_start; i < qf_count; ++i, qfp = qfp->qf_next)
596: if (qfp->qf_fnum == curbuf->b_fnum)
597: {
598: if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2)
599: {
600: if (amount == MAXLNUM)
601: qfp->qf_cleared = TRUE;
602: else
603: qfp->qf_lnum += amount;
604: }
605: if (amount_after && qfp->qf_lnum > line2)
606: qfp->qf_lnum += amount_after;
607: }
608: }
609:
610: /*
611: * Make a nice message out of the error character and the error number:
612: * char number message
613: * e or E 0 " error"
614: * w or W 0 " warning"
615: * other 0 ""
616: * w or W n " warning n"
617: * other n " error n"
618: */
619: static char_u *
620: qf_types(c, nr)
621: int c, nr;
622: {
623: static char_u buf[20];
624: char_u *p1;
625:
626: p1 = (char_u *)" error";
627: if (c == 'W' || c == 'w')
628: p1 = (char_u *)" warning";
629: else if (nr <= 0 && c != 'E' && c != 'e')
630: p1 = (char_u *)"";
631:
632: if (nr <= 0)
633: return p1;
634:
635: sprintf((char *)buf, "%s %3d", p1, nr);
636: return buf;
637: }