/* vi:set ts=8 sts=4 sw=4: * * VIM - Vi IMproved by Bram Moolenaar * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ /* * quickfix.c: functions for quickfix mode, using a file with error messages */ #include "vim.h" #if defined(FEAT_QUICKFIX) || defined(PROTO) struct dir_stack_T{ struct dir_stack_T *next; char_u *dirname;}; static struct dir_stack_T *dir_stack = NULL; /* * For each error the next struct is allocated and linked in a list. */typedef struct qfline_S qfline_T;struct qfline_S{ qfline_T *qf_next; /* pointer to next error in the list */ qfline_T *qf_prev; /* pointer to previous error in the list */ linenr_T qf_lnum; /* line number where the error occurred */ int qf_fnum; /* file number for the line */ int qf_col; /* column where the error occurred */ int qf_nr; /* error number */ char_u *qf_pattern; /* search pattern for the error */ char_u *qf_text; /* description of the error */ char_u qf_viscol; /* set to TRUE if qf_col is screen column */ char_u qf_cleared; /* set to TRUE if line has been deleted */ char_u qf_type; /* type of the error (mostly 'E'); 1 for :helpgrep */ char_u qf_valid; /* valid error message detected */}; /* * There is a stack of error lists. */#define LISTCOUNT 10 typedef struct qf_list_S{ qfline_T *qf_start; /* pointer to the first error */ qfline_T *qf_ptr; /* pointer to the current error */ int qf_count; /* number of errors (0 means no error list) */ int qf_index; /* current index in the error list */ int qf_nonevalid; /* TRUE if not a single valid entry found */} qf_list_T; struct qf_info_S{ /* * Count of references to this list. Used only for location lists. * When a location list window reference this list, qf_refcount * will be 2. Otherwise, qf_refcount will be 1. When qf_refcount * reaches 0, the list is freed. */ int qf_refcount; int qf_listcount; /* current number of lists */ int qf_curlist; /* current error list */ qf_list_T qf_lists[LISTCOUNT];}; static qf_info_T ql_info; /* global quickfix list */ #define FMT_PATTERNS 10 /* maximum number of % recognized */ /* * Structure used to hold the info of one part of 'errorformat' */typedef struct efm_S efm_T;struct efm_S{ regprog_T *prog; /* pre-formatted part of 'errorformat' */ efm_T *next; /* pointer to next (NULL if last) */ char_u addr[FMT_PATTERNS]; /* indices of used % patterns */ char_u prefix; /* prefix of this format line: */ /* 'D' enter directory */ /* 'X' leave directory */ /* 'A' start of multi-line message */ /* 'E' error message */ /* 'W' warning message */ /* 'I' informational message */ /* 'C' continuation line */ /* 'Z' end of multi-line message */ /* 'G' general, unspecific message */ /* 'P' push file (partial) message */ /* 'Q' pop/quit file (partial) message */ /* 'O' overread (partial) message */ char_u flags; /* additional flags given in prefix */ /* '-' do not include this line */ /* '+' include whole line in message */ int conthere; /* %> used */}; static int qf_init_ext __ARGS((qf_info_T *qi, char_u *efile, buf_T *buf, typval_T *tv, char_u *errorformat, int newlist, linenr_T lnumfirst, linenr_T lnumlast));static void qf_new_list __ARGS((qf_info_T *qi));static int qf_add_entry __ARGS((qf_info_T *qi, qfline_T **prevp, char_u *dir, char_u *fname, int bufnum, char_u *mesg, long lnum, int col, int vis_col, char_u *pattern, int nr, int type, int valid));static void qf_msg __ARGS((qf_info_T *qi));static void qf_free __ARGS((qf_info_T *qi, int idx));static char_u *qf_types __ARGS((int, int));static int qf_get_fnum __ARGS((char_u *, char_u *));static char_u *qf_push_dir __ARGS((char_u *, struct dir_stack_T **));static char_u *qf_pop_dir __ARGS((struct dir_stack_T **));static char_u *qf_guess_filepath __ARGS((char_u *));static void qf_fmt_text __ARGS((char_u *text, char_u *buf, int bufsize));static void qf_clean_dir_stack __ARGS((struct dir_stack_T **));#ifdef FEAT_WINDOWSstatic int qf_win_pos_update __ARGS((qf_info_T *qi, int old_qf_index));static int is_qf_win __ARGS((win_T *win, qf_info_T *qi));static win_T *qf_find_win __ARGS((qf_info_T *qi));static buf_T *qf_find_buf __ARGS((qf_info_T *qi));static void qf_update_buffer __ARGS((qf_info_T *qi));static void qf_fill_buffer __ARGS((qf_info_T *qi));#endifstatic char_u *get_mef_name __ARGS((void));static buf_T *load_dummy_buffer __ARGS((char_u *fname));static void wipe_dummy_buffer __ARGS((buf_T *buf));static void unload_dummy_buffer __ARGS((buf_T *buf));static qf_info_T *ll_get_or_alloc_list __ARGS((win_T *)); /* Quickfix window check helper macro */#define IS_QF_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref == NULL)/* Location list window check helper macro */#define IS_LL_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)/* * Return location list for window 'wp' * For location list window, return the referenced location list */#define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? wp->w_llist_ref : wp->w_llist) /* * Read the errorfile "efile" into memory, line by line, building the error * list. * Return -1 for error, number of errors for success. */ intqf_init(wp, efile, errorformat, newlist) win_T *wp; char_u *efile; char_u *errorformat; int newlist; /* TRUE: start a new error list */{ qf_info_T *qi = &ql_info; if (efile == NULL) return FAIL; if (wp != NULL) { qi = ll_get_or_alloc_list(wp); if (qi == NULL) return FAIL; } return qf_init_ext(qi, efile, curbuf, NULL, errorformat, newlist, (linenr_T)0, (linenr_T)0);} /* * Read the errorfile "efile" into memory, line by line, building the error * list. * Alternative: when "efile" is null read errors from buffer "buf". * Always use 'errorformat' from "buf" if there is a local value. * Then lnumfirst and lnumlast specify the range of lines to use. * Return -1 for error, number of errors for success. */ static intqf_init_ext(qi, efile, buf, tv, errorformat, newlist, lnumfirst, lnumlast) qf_info_T *qi; char_u *efile; buf_T *buf; typval_T *tv; char_u *errorformat; int newlist; /* TRUE: start a new error list */ linenr_T lnumfirst; /* first line number to use */ linenr_T lnumlast; /* last line number to use */{ char_u *namebuf; char_u *errmsg; char_u *pattern; char_u *fmtstr = NULL; int col = 0; char_u use_viscol = FALSE; int type = 0; int valid; linenr_T buflnum = lnumfirst; long lnum = 0L; int enr = 0; FILE *fd = NULL; qfline_T *qfprev = NULL; /* init to make SASC shut up */ char_u *efmp; efm_T *fmt_first = NULL; efm_T *fmt_last = NULL; efm_T *fmt_ptr; efm_T *fmt_start = NULL; char_u *efm; char_u *ptr; char_u *srcptr; int len; int i; int round; int idx = 0; int multiline = FALSE; int multiignore = FALSE; int multiscan = FALSE; int retval = -1; /* default: return error flag */ char_u *directory = NULL; char_u *currfile = NULL; char_u *tail = NULL; char_u *p_str = NULL; listitem_T *p_li = NULL; struct dir_stack_T *file_stack = NULL; regmatch_T regmatch; static struct fmtpattern { char_u convchar; char *pattern; } fmt_pat[FMT_PATTERNS] = { {'f', ".\\+"}, /* only used when at end */ {'n', "\\d\\+"}, {'l', "\\d\\+"}, {'c', "\\d\\+"}, {'t', "."}, {'m', ".\\+"}, {'r', ".*"}, {'p', "[- .]*"}, {'v', "\\d\\+"}, {'s', ".\\+"} }; namebuf = alloc(CMDBUFFSIZE + 1); errmsg = alloc(CMDBUFFSIZE + 1); pattern = alloc(CMDBUFFSIZE + 1); if (namebuf == NULL || errmsg == NULL || pattern == NULL) goto qf_init_end; if (efile != NULL && (fd = mch_fopen((char *)efile, "r")) == NULL) { EMSG2(_(e_openerrf), efile); goto qf_init_end; } if (newlist || qi->qf_curlist == qi->qf_listcount) /* make place for a new list */ qf_new_list(qi); else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) /* Adding to existing list, find last entry. */ for (qfprev = qi->qf_lists[qi->qf_curlist].qf_start; qfprev->qf_next != qfprev; qfprev = qfprev->qf_next) ; /* * Each part of the format string is copied and modified from errorformat to * regex prog. Only a few % characters are allowed. */ /* Use the local value of 'errorformat' if it's set. */ if (errorformat == p_efm && tv == NULL && *buf->b_p_efm != NUL) efm = buf->b_p_efm; else efm = errorformat; /* * Get some space to modify the format string into. */ i = (FMT_PATTERNS * 3) + ((int)STRLEN(efm) << 2); for (round = FMT_PATTERNS; round > 0; ) i += (int)STRLEN(fmt_pat[--round].pattern);#ifdef COLON_IN_FILENAME i += 12; /* "%f" can become twelve chars longer */#else i += 2; /* "%f" can become two chars longer */#endif if ((fmtstr = alloc(i)) == NULL) goto error2; while (efm[0] != NUL) { /* * Allocate a new eformat structure and put it at the end of the list */ fmt_ptr = (efm_T *)alloc_clear((unsigned)sizeof(efm_T)); if (fmt_ptr == NULL) goto error2; if (fmt_first == NULL) /* first one */ fmt_first = fmt_ptr; else fmt_last->next = fmt_ptr; fmt_last = fmt_ptr; /* * Isolate one part in the 'errorformat' option */ for (len = 0; efm[len] != NUL && efm[len] != ','; ++len) if (efm[len] == '\\' && efm[len + 1] != NUL) ++len; /* * Build regexp pattern from current 'errorformat' option */ ptr = fmtstr; *ptr++ = '^'; round = 0; for (efmp = efm; efmp < efm + len; ++efmp) { if (*efmp == '%') { ++efmp; for (idx = 0; idx < FMT_PATTERNS; ++idx) if (fmt_pat[idx].convchar == *efmp) break; if (idx < FMT_PATTERNS) { if (fmt_ptr->addr[idx]) { sprintf((char *)errmsg, _("E372: Too many %%%c in format string"), *efmp); EMSG(errmsg); goto error2; } if ((idx && idx < 6 && vim_strchr((char_u *)"DXOPQ", fmt_ptr->prefix) != NULL) || (idx == 6 && vim_strchr((char_u *)"OPQ", fmt_ptr->prefix) == NULL)) { sprintf((char *)errmsg, _("E373: Unexpected %%%c in format string"), *efmp); EMSG(errmsg); goto error2; } fmt_ptr->addr[idx] = (char_u)++round; *ptr++ = '\\'; *ptr++ = '(';#ifdef BACKSLASH_IN_FILENAME if (*efmp == 'f') { /* Also match "c:" in the file name, even when * checking for a colon next: "%f:". * "\%(\a:\)\=" */ STRCPY(ptr, "\\%(\\a:\\)\\="); ptr += 10; }#endif if (*efmp == 'f' && efmp[1] != NUL) { if (efmp[1] != '\\' && efmp[1] != '%') { /* A file name may contain spaces, but this isn't * in "\f". For "%f:%l:%m" there may be a ":" in * the file name. Use ".\{-1,}x" instead (x is * the next character), the requirement that :999: * follows should work. */ STRCPY(ptr, ".\\{-1,}"); ptr += 7; } else { /* File name followed by '\\' or '%': include as * many file name chars as possible. */ STRCPY(ptr, "\\f\\+"); ptr += 4; } } else { srcptr = (char_u *)fmt_pat[idx].pattern; while ((*ptr = *srcptr++) != NUL) ++ptr; } *ptr++ = '\\'; *ptr++ = ')'; } else if (*efmp == '*') { if (*++efmp == '[' || *efmp == '\\') { if ((*ptr++ = *efmp) == '[') /* %*[^a-z0-9] etc. */ { if (efmp[1] == '^') *ptr++ = *++efmp; if (efmp < efm + len) { *ptr++ = *++efmp; /* could be ']' */ while (efmp < efm + len && (*ptr++ = *++efmp) != ']') /* skip */; if (efmp == efm + len) { EMSG(_("E374: Missing ] in format string")); goto error2; } } } else if (efmp < efm + len) /* %*\D, %*\s etc. */ *ptr++ = *++efmp; *ptr++ = '\\'; *ptr++ = '+'; } else { /* TODO: scanf()-like: %*ud, %*3c, %*f, ... ? */ sprintf((char *)errmsg, _("E375: Unsupported %%%c in format string"), *efmp); EMSG(errmsg); goto error2; } } else if (vim_strchr((char_u *)"%\\.^$~[", *efmp) != NULL) *ptr++ = *efmp; /* regexp magic characters */ else if (*efmp == '#') *ptr++ = '*'; else if (*efmp == '>') fmt_ptr->conthere = TRUE; else if (efmp == efm + 1) /* analyse prefix */ { if (vim_strchr((char_u *)"+-", *efmp) != NULL) fmt_ptr->flags = *efmp++; if (vim_strchr((char_u *)"DXAEWICZGOPQ", *efmp) != NULL) fmt_ptr->prefix = *efmp; else { sprintf((char *)errmsg, _("E376: Invalid %%%c in format string prefix"), *efmp); EMSG(errmsg); goto error2; } } else { sprintf((char *)errmsg, _("E377: Invalid %%%c in format string"), *efmp); EMSG(errmsg); goto error2; } } else /* copy normal character */ { if (*efmp == '\\' && efmp + 1 < efm + len) ++efmp; else if (vim_strchr((char_u *)".*^$~[", *efmp) != NULL) *ptr++ = '\\'; /* escape regexp atoms */ if (*efmp) *ptr++ = *efmp; } } *ptr++ = '$'; *ptr = NUL; if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL) goto error2; /* * Advance to next part */ efm = skip_to_option_part(efm + len); /* skip comma and spaces */ } if (fmt_first == NULL) /* nothing found */ { EMSG(_("E378: 'errorformat' contains no pattern")); goto error2; } /* * got_int is reset here, because it was probably set when killing the * ":make" command, but we still want to read the errorfile then. */ got_int = FALSE; /* Always ignore case when looking for a matching error. */ regmatch.rm_ic = TRUE; if (tv != NULL) { if (tv->v_type == VAR_STRING) p_str = tv->vval.v_string; else if (tv->v_type == VAR_LIST) p_li = tv->vval.v_list->lv_first; } /* * Read the lines in the error file one by one. * Try to recognize one of the error formats in each line. */ while (!got_int) { /* Get the next line. */ if (fd == NULL) {Lines 1–500 of 3849. Load more
/* vi:set ts=8 sts=4 sw=4: * * VIM - Vi IMproved by Bram Moolenaar * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ /* * quickfix.c: functions for quickfix mode, using a file with error messages */ #include "vim.h" #if defined(FEAT_QUICKFIX) || defined(PROTO) struct dir_stack_T { struct dir_stack_T *next; char_u *dirname; }; static struct dir_stack_T *dir_stack = NULL; /* * For each error the next struct is allocated and linked in a list. */ typedef struct qfline_S qfline_T; struct qfline_S { qfline_T *qf_next; /* pointer to next error in the list */ qfline_T *qf_prev; /* pointer to previous error in the list */ linenr_T qf_lnum; /* line number where the error occurred */ int qf_fnum; /* file number for the line */ int qf_col; /* column where the error occurred */ int qf_nr; /* error number */ char_u *qf_pattern; /* search pattern for the error */ char_u *qf_text; /* description of the error */ char_u qf_viscol; /* set to TRUE if qf_col is screen column */ char_u qf_cleared; /* set to TRUE if line has been deleted */ char_u qf_type; /* type of the error (mostly 'E'); 1 for :helpgrep */ char_u qf_valid; /* valid error message detected */ }; /* * There is a stack of error lists. */ #define LISTCOUNT 10 typedef struct qf_list_S { qfline_T *qf_start; /* pointer to the first error */ qfline_T *qf_ptr; /* pointer to the current error */ int qf_count; /* number of errors (0 means no error list) */ int qf_index; /* current index in the error list */ int qf_nonevalid; /* TRUE if not a single valid entry found */ } qf_list_T; struct qf_info_S { /* * Count of references to this list. Used only for location lists. * When a location list window reference this list, qf_refcount * will be 2. Otherwise, qf_refcount will be 1. When qf_refcount * reaches 0, the list is freed. */ int qf_refcount; int qf_listcount; /* current number of lists */ int qf_curlist; /* current error list */ qf_list_T qf_lists[LISTCOUNT]; }; static qf_info_T ql_info; /* global quickfix list */ #define FMT_PATTERNS 10 /* maximum number of % recognized */ /* * Structure used to hold the info of one part of 'errorformat' */ typedef struct efm_S efm_T; struct efm_S { regprog_T *prog; /* pre-formatted part of 'errorformat' */ efm_T *next; /* pointer to next (NULL if last) */ char_u addr[FMT_PATTERNS]; /* indices of used % patterns */ char_u prefix; /* prefix of this format line: */ /* 'D' enter directory */ /* 'X' leave directory */ /* 'A' start of multi-line message */ /* 'E' error message */ /* 'W' warning message */ /* 'I' informational message */ /* 'C' continuation line */ /* 'Z' end of multi-line message */ /* 'G' general, unspecific message */ /* 'P' push file (partial) message */ /* 'Q' pop/quit file (partial) message */ /* 'O' overread (partial) message */ char_u flags; /* additional flags given in prefix */ /* '-' do not include this line */ /* '+' include whole line in message */ int conthere; /* %> used */ }; static int qf_init_ext __ARGS((qf_info_T *qi, char_u *efile, buf_T *buf, typval_T *tv, char_u *errorformat, int newlist, linenr_T lnumfirst, linenr_T lnumlast)); static void qf_new_list __ARGS((qf_info_T *qi)); static int qf_add_entry __ARGS((qf_info_T *qi, qfline_T **prevp, char_u *dir, char_u *fname, int bufnum, char_u *mesg, long lnum, int col, int vis_col, char_u *pattern, int nr, int type, int valid)); static void qf_msg __ARGS((qf_info_T *qi)); static void qf_free __ARGS((qf_info_T *qi, int idx)); static char_u *qf_types __ARGS((int, int)); static int qf_get_fnum __ARGS((char_u *, char_u *)); static char_u *qf_push_dir __ARGS((char_u *, struct dir_stack_T **)); static char_u *qf_pop_dir __ARGS((struct dir_stack_T **)); static char_u *qf_guess_filepath __ARGS((char_u *)); static void qf_fmt_text __ARGS((char_u *text, char_u *buf, int bufsize)); static void qf_clean_dir_stack __ARGS((struct dir_stack_T **)); #ifdef FEAT_WINDOWS static int qf_win_pos_update __ARGS((qf_info_T *qi, int old_qf_index)); static int is_qf_win __ARGS((win_T *win, qf_info_T *qi)); static win_T *qf_find_win __ARGS((qf_info_T *qi)); static buf_T *qf_find_buf __ARGS((qf_info_T *qi)); static void qf_update_buffer __ARGS((qf_info_T *qi)); static void qf_fill_buffer __ARGS((qf_info_T *qi)); #endif static char_u *get_mef_name __ARGS((void)); static buf_T *load_dummy_buffer __ARGS((char_u *fname)); static void wipe_dummy_buffer __ARGS((buf_T *buf)); static void unload_dummy_buffer __ARGS((buf_T *buf)); static qf_info_T *ll_get_or_alloc_list __ARGS((win_T *)); /* Quickfix window check helper macro */ #define IS_QF_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref == NULL) /* Location list window check helper macro */ #define IS_LL_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL) /* * Return location list for window 'wp' * For location list window, return the referenced location list */ #define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? wp->w_llist_ref : wp->w_llist) /* * Read the errorfile "efile" into memory, line by line, building the error * list. * Return -1 for error, number of errors for success. */ int qf_init(wp, efile, errorformat, newlist) win_T *wp; char_u *efile; char_u *errorformat; int newlist; /* TRUE: start a new error list */ { qf_info_T *qi = &ql_info; if (efile == NULL) return FAIL; if (wp != NULL) { qi = ll_get_or_alloc_list(wp); if (qi == NULL) return FAIL; } return qf_init_ext(qi, efile, curbuf, NULL, errorformat, newlist, (linenr_T)0, (linenr_T)0); } /* * Read the errorfile "efile" into memory, line by line, building the error * list. * Alternative: when "efile" is null read errors from buffer "buf". * Always use 'errorformat' from "buf" if there is a local value. * Then lnumfirst and lnumlast specify the range of lines to use. * Return -1 for error, number of errors for success. */ static int qf_init_ext(qi, efile, buf, tv, errorformat, newlist, lnumfirst, lnumlast) qf_info_T *qi; char_u *efile; buf_T *buf; typval_T *tv; char_u *errorformat; int newlist; /* TRUE: start a new error list */ linenr_T lnumfirst; /* first line number to use */ linenr_T lnumlast; /* last line number to use */ { char_u *namebuf; char_u *errmsg; char_u *pattern; char_u *fmtstr = NULL; int col = 0; char_u use_viscol = FALSE; int type = 0; int valid; linenr_T buflnum = lnumfirst; long lnum = 0L; int enr = 0; FILE *fd = NULL; qfline_T *qfprev = NULL; /* init to make SASC shut up */ char_u *efmp; efm_T *fmt_first = NULL; efm_T *fmt_last = NULL; efm_T *fmt_ptr; efm_T *fmt_start = NULL; char_u *efm; char_u *ptr; char_u *srcptr; int len; int i; int round; int idx = 0; int multiline = FALSE; int multiignore = FALSE; int multiscan = FALSE; int retval = -1; /* default: return error flag */ char_u *directory = NULL; char_u *currfile = NULL; char_u *tail = NULL; char_u *p_str = NULL; listitem_T *p_li = NULL; struct dir_stack_T *file_stack = NULL; regmatch_T regmatch; static struct fmtpattern { char_u convchar; char *pattern; } fmt_pat[FMT_PATTERNS] = { {'f', ".\\+"}, /* only used when at end */ {'n', "\\d\\+"}, {'l', "\\d\\+"}, {'c', "\\d\\+"}, {'t', "."}, {'m', ".\\+"}, {'r', ".*"}, {'p', "[- .]*"}, {'v', "\\d\\+"}, {'s', ".\\+"} }; namebuf = alloc(CMDBUFFSIZE + 1); errmsg = alloc(CMDBUFFSIZE + 1); pattern = alloc(CMDBUFFSIZE + 1); if (namebuf == NULL || errmsg == NULL || pattern == NULL) goto qf_init_end; if (efile != NULL && (fd = mch_fopen((char *)efile, "r")) == NULL) { EMSG2(_(e_openerrf), efile); goto qf_init_end; } if (newlist || qi->qf_curlist == qi->qf_listcount) /* make place for a new list */ qf_new_list(qi); else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) /* Adding to existing list, find last entry. */ for (qfprev = qi->qf_lists[qi->qf_curlist].qf_start; qfprev->qf_next != qfprev; qfprev = qfprev->qf_next) ; /* * Each part of the format string is copied and modified from errorformat to * regex prog. Only a few % characters are allowed. */ /* Use the local value of 'errorformat' if it's set. */ if (errorformat == p_efm && tv == NULL && *buf->b_p_efm != NUL) efm = buf->b_p_efm; else efm = errorformat; /* * Get some space to modify the format string into. */ i = (FMT_PATTERNS * 3) + ((int)STRLEN(efm) << 2); for (round = FMT_PATTERNS; round > 0; ) i += (int)STRLEN(fmt_pat[--round].pattern); #ifdef COLON_IN_FILENAME i += 12; /* "%f" can become twelve chars longer */ #else i += 2; /* "%f" can become two chars longer */ #endif if ((fmtstr = alloc(i)) == NULL) goto error2; while (efm[0] != NUL) { /* * Allocate a new eformat structure and put it at the end of the list */ fmt_ptr = (efm_T *)alloc_clear((unsigned)sizeof(efm_T)); if (fmt_ptr == NULL) goto error2; if (fmt_first == NULL) /* first one */ fmt_first = fmt_ptr; else fmt_last->next = fmt_ptr; fmt_last = fmt_ptr; /* * Isolate one part in the 'errorformat' option */ for (len = 0; efm[len] != NUL && efm[len] != ','; ++len) if (efm[len] == '\\' && efm[len + 1] != NUL) ++len; /* * Build regexp pattern from current 'errorformat' option */ ptr = fmtstr; *ptr++ = '^'; round = 0; for (efmp = efm; efmp < efm + len; ++efmp) { if (*efmp == '%') { ++efmp; for (idx = 0; idx < FMT_PATTERNS; ++idx) if (fmt_pat[idx].convchar == *efmp) break; if (idx < FMT_PATTERNS) { if (fmt_ptr->addr[idx]) { sprintf((char *)errmsg, _("E372: Too many %%%c in format string"), *efmp); EMSG(errmsg); goto error2; } if ((idx && idx < 6 && vim_strchr((char_u *)"DXOPQ", fmt_ptr->prefix) != NULL) || (idx == 6 && vim_strchr((char_u *)"OPQ", fmt_ptr->prefix) == NULL)) { sprintf((char *)errmsg, _("E373: Unexpected %%%c in format string"), *efmp); EMSG(errmsg); goto error2; } fmt_ptr->addr[idx] = (char_u)++round; *ptr++ = '\\'; *ptr++ = '('; #ifdef BACKSLASH_IN_FILENAME if (*efmp == 'f') { /* Also match "c:" in the file name, even when * checking for a colon next: "%f:". * "\%(\a:\)\=" */ STRCPY(ptr, "\\%(\\a:\\)\\="); ptr += 10; } #endif if (*efmp == 'f' && efmp[1] != NUL) { if (efmp[1] != '\\' && efmp[1] != '%') { /* A file name may contain spaces, but this isn't * in "\f". For "%f:%l:%m" there may be a ":" in * the file name. Use ".\{-1,}x" instead (x is * the next character), the requirement that :999: * follows should work. */ STRCPY(ptr, ".\\{-1,}"); ptr += 7; } else { /* File name followed by '\\' or '%': include as * many file name chars as possible. */ STRCPY(ptr, "\\f\\+"); ptr += 4; } } else { srcptr = (char_u *)fmt_pat[idx].pattern; while ((*ptr = *srcptr++) != NUL) ++ptr; } *ptr++ = '\\'; *ptr++ = ')'; } else if (*efmp == '*') { if (*++efmp == '[' || *efmp == '\\') { if ((*ptr++ = *efmp) == '[') /* %*[^a-z0-9] etc. */ { if (efmp[1] == '^') *ptr++ = *++efmp; if (efmp < efm + len) { *ptr++ = *++efmp; /* could be ']' */ while (efmp < efm + len && (*ptr++ = *++efmp) != ']') /* skip */; if (efmp == efm + len) { EMSG(_("E374: Missing ] in format string")); goto error2; } } } else if (efmp < efm + len) /* %*\D, %*\s etc. */ *ptr++ = *++efmp; *ptr++ = '\\'; *ptr++ = '+'; } else { /* TODO: scanf()-like: %*ud, %*3c, %*f, ... ? */ sprintf((char *)errmsg, _("E375: Unsupported %%%c in format string"), *efmp); EMSG(errmsg); goto error2; } } else if (vim_strchr((char_u *)"%\\.^$~[", *efmp) != NULL) *ptr++ = *efmp; /* regexp magic characters */ else if (*efmp == '#') *ptr++ = '*'; else if (*efmp == '>') fmt_ptr->conthere = TRUE; else if (efmp == efm + 1) /* analyse prefix */ { if (vim_strchr((char_u *)"+-", *efmp) != NULL) fmt_ptr->flags = *efmp++; if (vim_strchr((char_u *)"DXAEWICZGOPQ", *efmp) != NULL) fmt_ptr->prefix = *efmp; else { sprintf((char *)errmsg, _("E376: Invalid %%%c in format string prefix"), *efmp); EMSG(errmsg); goto error2; } } else { sprintf((char *)errmsg, _("E377: Invalid %%%c in format string"), *efmp); EMSG(errmsg); goto error2; } } else /* copy normal character */ { if (*efmp == '\\' && efmp + 1 < efm + len) ++efmp; else if (vim_strchr((char_u *)".*^$~[", *efmp) != NULL) *ptr++ = '\\'; /* escape regexp atoms */ if (*efmp) *ptr++ = *efmp; } } *ptr++ = '$'; *ptr = NUL; if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL) goto error2; /* * Advance to next part */ efm = skip_to_option_part(efm + len); /* skip comma and spaces */ } if (fmt_first == NULL) /* nothing found */ { EMSG(_("E378: 'errorformat' contains no pattern")); goto error2; } /* * got_int is reset here, because it was probably set when killing the * ":make" command, but we still want to read the errorfile then. */ got_int = FALSE; /* Always ignore case when looking for a matching error. */ regmatch.rm_ic = TRUE; if (tv != NULL) { if (tv->v_type == VAR_STRING) p_str = tv->vval.v_string; else if (tv->v_type == VAR_LIST) p_li = tv->vval.v_list->lv_first; } /* * Read the lines in the error file one by one. * Try to recognize one of the error formats in each line. */ while (!got_int) { /* Get the next line. */ if (fd == NULL) {