Annotation of src/usr.bin/vim/quickfix.c, Revision 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: }