diff options
-rw-r--r-- | src/nvim/quickfix.c | 430 | ||||
-rw-r--r-- | src/nvim/version.c | 2 |
2 files changed, 240 insertions, 192 deletions
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 16d336c41f..1499c3e12f 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -188,6 +188,243 @@ static char_u *qf_grow_linebuf(char_u **growbuf, size_t *growbufsiz, return *growbuf; } +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', ".\\+"} +}; + +// Converts a 'errorformat' string to regular expression pattern +static int efm_to_regpat(char_u *efm, int len, efm_T *fmt_ptr, + char_u *regpat, char_u *errmsg) +{ + // Build regexp pattern from current 'errorformat' option + char_u *ptr = regpat; + *ptr++ = '^'; + int round = 0; + for (char_u *efmp = efm; efmp < efm + len; efmp++) { + if (*efmp == '%') { + efmp++; + int idx; + 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); + return -1; + } + 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); + return -1; + } + round++; + 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 { + char_u *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) { + efmp++; + *ptr++ = *efmp; // could be ']' + while (efmp < efm + len) { + efmp++; + if ((*ptr++ = *efmp) == ']') { + break; + } + } + if (efmp == efm + len) { + EMSG(_("E374: Missing ] in format string")); + return -1; + } + } + } else if (efmp < efm + len) { // %*\D, %*\s etc. + efmp++; + *ptr++ = *efmp; + } + *ptr++ = '\\'; + *ptr++ = '+'; + } else { + // TODO: scanf()-like: %*ud, %*3c, %*f, ... ? + sprintf((char *)errmsg, + _("E375: Unsupported %%%c in format string"), *efmp); + EMSG(errmsg); + return -1; + } + } 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); + return -1; + } + } else { + sprintf((char *)errmsg, + _("E377: Invalid %%%c in format string"), *efmp); + EMSG(errmsg); + return -1; + } + } 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; + + return 0; +} + +static void free_efm_list(efm_T **efm_first) +{ + for (efm_T *efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first) { + *efm_first = efm_ptr->next; + vim_regfree(efm_ptr->prog); + xfree(efm_ptr); + } +} + +// Parse 'errorformat' option +static efm_T * parse_efm_option(char_u *efm) +{ + efm_T *fmt_ptr = NULL; + efm_T *fmt_first = NULL; + efm_T *fmt_last = NULL; + int len; + + size_t errmsglen = CMDBUFFSIZE + 1; + char_u *errmsg = xmalloc(errmsglen); + + // Get some space to modify the format string into. + size_t i = (FMT_PATTERNS * 3) + (STRLEN(efm) << 2); + for (int round = FMT_PATTERNS - 1; round >= 0; ) { + i += 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 + char_u *fmtstr = xmalloc(i); + + while (efm[0] != NUL) { + // Allocate a new eformat structure and put it at the end of the list + fmt_ptr = (efm_T *)xcalloc(1, sizeof(efm_T)); + 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++; + } + } + + if (efm_to_regpat(efm, len, fmt_ptr, fmtstr, errmsg) == -1) { + goto parse_efm_error; + } + if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL) { + goto parse_efm_error; + } + // 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 parse_efm_end; + +parse_efm_error: + free_efm_list(&fmt_first); + +parse_efm_end: + xfree(fmtstr); + xfree(errmsg); + + return fmt_first; +} + // Read the errorfile "efile" into memory, line by line, building the error // list. // Alternative: when "efile" is NULL read errors from buffer "buf". @@ -213,7 +450,6 @@ qf_init_ext ( char_u *errmsg; size_t errmsglen; char_u *pattern; - char_u *fmtstr = NULL; char_u *growbuf = NULL; size_t growbuflen; size_t growbufsiz = 0; @@ -228,17 +464,13 @@ qf_init_ext ( int enr = 0; FILE *fd = NULL; qfline_T *old_last = NULL; - 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; size_t len; int i; - int round; int idx = 0; bool multiline = false; bool multiignore = false; @@ -252,22 +484,6 @@ qf_init_ext ( 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 = xmalloc(CMDBUFFSIZE + 1); errmsglen = CMDBUFFSIZE + 1; @@ -296,172 +512,9 @@ qf_init_ext ( efm = buf->b_p_efm; else efm = errorformat; - /* - * Get some space to modify the format string into. - */ - size_t fmtstr_size = 3 * FMT_PATTERNS + 4 * STRLEN(efm); - for (round = FMT_PATTERNS; round > 0; ) { - fmtstr_size += STRLEN(fmt_pat[--round].pattern); - } -#ifdef COLON_IN_FILENAME - fmtstr_size += 12; // "%f" can become twelve chars longer -#else - fmtstr_size += 2; // "%f" can become two chars longer -#endif - fmtstr = xmalloc(fmtstr_size); - while (efm[0] != NUL) { - /* - * Allocate a new eformat structure and put it at the end of the list - */ - fmt_ptr = xcalloc(1, sizeof(efm_T)); - 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 */ - } + fmt_first = parse_efm_option(efm); if (fmt_first == NULL) { /* nothing found */ - EMSG(_("E378: 'errorformat' contains no pattern")); goto error2; } @@ -899,18 +952,13 @@ error2: qf_init_ok: if (fd != NULL) fclose(fd); - for (fmt_ptr = fmt_first; fmt_ptr != NULL; fmt_ptr = fmt_first) { - fmt_first = fmt_ptr->next; - vim_regfree(fmt_ptr->prog); - xfree(fmt_ptr); - } + free_efm_list(&fmt_first); qf_clean_dir_stack(&dir_stack); qf_clean_dir_stack(&file_stack); qf_init_end: xfree(namebuf); xfree(errmsg); xfree(pattern); - xfree(fmtstr); xfree(growbuf); qf_update_buffer(qi, old_last); diff --git a/src/nvim/version.c b/src/nvim/version.c index c4fdcd61f9..3efc161b05 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -476,7 +476,7 @@ static int included_patches[] = { 1967, // 1966, // 1965 NA - // 1964, + 1964, // 1963 NA // 1962, 1961, |