To: vim_dev@googlegroups.com Subject: Patch 8.1.0252 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.0252 Problem: Quickfix functions are too long. Solution: Refactor. (Yegappan Lakshmanan, closes #2950) Files: src/quickfix.c *** ../vim-8.1.0251/src/quickfix.c 2018-08-07 19:47:46.746434541 +0200 --- src/quickfix.c 2018-08-07 21:52:51.654369210 +0200 *************** *** 201,306 **** /* * Convert an errorformat pattern to a regular expression pattern. ! * See fmt_pat definition above for the list of supported patterns. */ static char_u * ! fmtpat_to_regpat( ! char_u *efmp, ! efm_T *fmt_ptr, int idx, int round, - char_u *ptr, char_u *errmsg) { char_u *srcptr; ! if (fmt_ptr->addr[idx]) { /* Each errorformat pattern can occur only once */ sprintf((char *)errmsg, ! _("E372: Too many %%%c in format string"), *efmp); EMSG(errmsg); return NULL; } 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 NULL; } ! 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++ = ')'; ! return ptr; } /* * Convert a scanf like format in 'errorformat' to a regular expression. */ static char_u * scanf_fmt_to_regpat( char_u *efm, int len, ! char_u **pefmp, ! char_u *ptr, char_u *errmsg) { char_u *efmp = *pefmp; ! 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) { --- 201,309 ---- /* * Convert an errorformat pattern to a regular expression pattern. ! * See fmt_pat definition above for the list of supported patterns. The ! * pattern specifier is supplied in "efmpat". The converted pattern is stored ! * in "regpat". Returns a pointer to the location after the pattern. */ static char_u * ! efmpat_to_regpat( ! char_u *efmpat, ! char_u *regpat, ! efm_T *efminfo, int idx, int round, char_u *errmsg) { char_u *srcptr; ! if (efminfo->addr[idx]) { /* Each errorformat pattern can occur only once */ sprintf((char *)errmsg, ! _("E372: Too many %%%c in format string"), *efmpat); EMSG(errmsg); return NULL; } if ((idx && idx < 6 ! && vim_strchr((char_u *)"DXOPQ", efminfo->prefix) != NULL) || (idx == 6 ! && vim_strchr((char_u *)"OPQ", efminfo->prefix) == NULL)) { sprintf((char *)errmsg, ! _("E373: Unexpected %%%c in format string"), *efmpat); EMSG(errmsg); return NULL; } ! efminfo->addr[idx] = (char_u)++round; ! *regpat++ = '\\'; ! *regpat++ = '('; #ifdef BACKSLASH_IN_FILENAME ! if (*efmpat == 'f') { /* Also match "c:" in the file name, even when * checking for a colon next: "%f:". * "\%(\a:\)\=" */ ! STRCPY(regpat, "\\%(\\a:\\)\\="); ! regpat += 10; } #endif ! if (*efmpat == 'f' && efmpat[1] != NUL) { ! if (efmpat[1] != '\\' && efmpat[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(regpat, ".\\{-1,}"); ! regpat += 7; } else { /* File name followed by '\\' or '%': include as * many file name chars as possible. */ ! STRCPY(regpat, "\\f\\+"); ! regpat += 4; } } else { srcptr = (char_u *)fmt_pat[idx].pattern; ! while ((*regpat = *srcptr++) != NUL) ! ++regpat; } ! *regpat++ = '\\'; ! *regpat++ = ')'; ! return regpat; } /* * Convert a scanf like format in 'errorformat' to a regular expression. + * Returns a pointer to the location after the pattern. */ static char_u * scanf_fmt_to_regpat( + char_u **pefmp, char_u *efm, int len, ! char_u *regpat, char_u *errmsg) { char_u *efmp = *pefmp; ! if (*efmp == '[' || *efmp == '\\') { ! if ((*regpat++ = *efmp) == '[') /* %*[^a-z0-9] etc. */ { if (efmp[1] == '^') ! *regpat++ = *++efmp; if (efmp < efm + len) { ! *regpat++ = *++efmp; /* could be ']' */ while (efmp < efm + len ! && (*regpat++ = *++efmp) != ']') /* skip */; if (efmp == efm + len) { *************** *** 310,318 **** } } else if (efmp < efm + len) /* %*\D, %*\s etc. */ ! *ptr++ = *++efmp; ! *ptr++ = '\\'; ! *ptr++ = '+'; } else { --- 313,321 ---- } } else if (efmp < efm + len) /* %*\D, %*\s etc. */ ! *regpat++ = *++efmp; ! *regpat++ = '\\'; ! *regpat++ = '+'; } else { *************** *** 325,360 **** *pefmp = efmp; ! return ptr; } /* * Analyze/parse an errorformat prefix. */ ! static int ! efm_analyze_prefix(char_u **pefmp, efm_T *fmt_ptr, char_u *errmsg) { - char_u *efmp = *pefmp; - 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 FAIL; } ! *pefmp = efmp; ! ! return OK; } /* ! * Converts a 'errorformat' string to regular expression pattern */ static int efm_to_regpat( --- 328,362 ---- *pefmp = efmp; ! return regpat; } /* * Analyze/parse an errorformat prefix. */ ! static char_u * ! efm_analyze_prefix(char_u *efmp, efm_T *efminfo, char_u *errmsg) { if (vim_strchr((char_u *)"+-", *efmp) != NULL) ! efminfo->flags = *efmp++; if (vim_strchr((char_u *)"DXAEWICZGOPQ", *efmp) != NULL) ! efminfo->prefix = *efmp; else { sprintf((char *)errmsg, _("E376: Invalid %%%c in format string prefix"), *efmp); EMSG(errmsg); ! return NULL; } ! return efmp; } /* ! * Converts a 'errorformat' string part in 'efm' to a regular expression ! * pattern. The resulting regex pattern is returned in "regpat". Additional ! * information about the 'erroformat' pattern is returned in "fmt_ptr". ! * Returns OK or FAIL. */ static int efm_to_regpat( *************** *** 370,376 **** int idx = 0; /* ! * Build regexp pattern from current 'errorformat' option */ ptr = regpat; *ptr++ = '^'; --- 372,378 ---- int idx = 0; /* ! * Build a regexp pattern for a 'errorformat' option part */ ptr = regpat; *ptr++ = '^'; *************** *** 385,401 **** break; if (idx < FMT_PATTERNS) { ! ptr = fmtpat_to_regpat(efmp, fmt_ptr, idx, round, ptr, errmsg); if (ptr == NULL) ! return -1; round++; } else if (*efmp == '*') { ! ptr = scanf_fmt_to_regpat(efm, len, &efmp, ptr, errmsg); if (ptr == NULL) ! return -1; } else if (vim_strchr((char_u *)"%\\.^$~[", *efmp) != NULL) *ptr++ = *efmp; /* regexp magic characters */ --- 387,404 ---- break; if (idx < FMT_PATTERNS) { ! ptr = efmpat_to_regpat(efmp, ptr, fmt_ptr, idx, round, errmsg); if (ptr == NULL) ! return FAIL; round++; } else if (*efmp == '*') { ! ++efmp; ! ptr = scanf_fmt_to_regpat(&efmp, efm, len, ptr, errmsg); if (ptr == NULL) ! return FAIL; } else if (vim_strchr((char_u *)"%\\.^$~[", *efmp) != NULL) *ptr++ = *efmp; /* regexp magic characters */ *************** *** 405,419 **** fmt_ptr->conthere = TRUE; else if (efmp == efm + 1) /* analyse prefix */ { ! if (efm_analyze_prefix(&efmp, fmt_ptr, errmsg) == FAIL) ! return -1; } else { sprintf((char *)errmsg, _("E377: Invalid %%%c in format string"), *efmp); EMSG(errmsg); ! return -1; } } else /* copy normal character */ --- 408,427 ---- fmt_ptr->conthere = TRUE; else if (efmp == efm + 1) /* analyse prefix */ { ! /* ! * prefix is allowed only at the beginning of the errorformat ! * option part ! */ ! efmp = efm_analyze_prefix(efmp, fmt_ptr, errmsg); ! if (efmp == NULL) ! return FAIL; } else { sprintf((char *)errmsg, _("E377: Invalid %%%c in format string"), *efmp); EMSG(errmsg); ! return FAIL; } } else /* copy normal character */ *************** *** 429,437 **** *ptr++ = '$'; *ptr = NUL; ! return 0; } static void free_efm_list(efm_T **efm_first) { --- 437,448 ---- *ptr++ = '$'; *ptr = NUL; ! return OK; } + /* + * Free the 'errorformat' information list + */ static void free_efm_list(efm_T **efm_first) { *************** *** 446,452 **** fmt_start = NULL; } ! /* Parse 'errorformat' option */ static efm_T * parse_efm_option(char_u *efm) { --- 457,504 ---- fmt_start = NULL; } ! /* ! * Compute the size of the buffer used to convert a 'errorformat' pattern into ! * a regular expression pattern. ! */ ! static int ! efm_regpat_bufsz(char_u *efm) ! { ! int sz; ! int i; ! ! sz = (FMT_PATTERNS * 3) + ((int)STRLEN(efm) << 2); ! for (i = FMT_PATTERNS; i > 0; ) ! sz += (int)STRLEN(fmt_pat[--i].pattern); ! #ifdef BACKSLASH_IN_FILENAME ! sz += 12; /* "%f" can become twelve chars longer (see efm_to_regpat) */ ! #else ! sz += 2; /* "%f" can become two chars longer */ ! #endif ! ! return sz; ! } ! ! /* ! * Return the length of a 'errorformat' option part (separated by ","). ! */ ! static int ! efm_option_part_len(char_u *efm) ! { ! int len; ! ! for (len = 0; efm[len] != NUL && efm[len] != ','; ++len) ! if (efm[len] == '\\' && efm[len + 1] != NUL) ! ++len; ! ! return len; ! } ! ! /* ! * Parse the 'errorformat' option. Multiple parts in the 'errorformat' option ! * are parsed and converted to regular expressions. Returns information about ! * the parsed 'errorformat' option. ! */ static efm_T * parse_efm_option(char_u *efm) { *************** *** 457,464 **** efm_T *fmt_last = NULL; char_u *fmtstr = NULL; int len; ! int i; ! int round; errmsglen = CMDBUFFSIZE + 1; errmsg = alloc_id(errmsglen, aid_qf_errmsg); --- 509,515 ---- efm_T *fmt_last = NULL; char_u *fmtstr = NULL; int len; ! int sz; errmsglen = CMDBUFFSIZE + 1; errmsg = alloc_id(errmsglen, aid_qf_errmsg); *************** *** 473,487 **** /* * 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 BACKSLASH_IN_FILENAME ! i += 12; /* "%f" can become twelve chars longer (see efm_to_regpat) */ ! #else ! i += 2; /* "%f" can become two chars longer */ ! #endif ! if ((fmtstr = alloc(i)) == NULL) goto parse_efm_error; while (efm[0] != NUL) --- 524,531 ---- /* * Get some space to modify the format string into. */ ! sz = efm_regpat_bufsz(efm); ! if ((fmtstr = alloc(sz)) == NULL) goto parse_efm_error; while (efm[0] != NUL) *************** *** 501,511 **** /* * 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; --- 545,553 ---- /* * Isolate one part in the 'errorformat' option */ ! len = efm_option_part_len(efm); ! if (efm_to_regpat(efm, len, fmt_ptr, fmtstr, errmsg) == FAIL) goto parse_efm_error; if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL) goto parse_efm_error; *************** *** 539,544 **** --- 581,590 ---- QF_MULTISCAN = 5, }; + /* + * State information used to parse lines and add entries to a quickfix/location + * list. + */ typedef struct { char_u *linebuf; int linelen; *************** *** 554,559 **** --- 600,608 ---- vimconv_T vc; } qfstate_T; + /* + * Allocate more memory for the line buffer used for parsing lines. + */ static char_u * qf_grow_linebuf(qfstate_T *state, int newsz) { *************** *** 861,870 **** } qffields_T; /* ! * Parse the error format matches in 'regmatch' and set the values in 'fields'. ! * fmt_ptr contains the 'efm' format specifiers/prefixes that have a match. ! * Returns QF_OK if all the matches are successfully parsed. On failure, ! * returns QF_FAIL or QF_NOMEM. */ static int qf_parse_match( --- 910,1160 ---- } qffields_T; /* ! * Parse the match for filename ('%f') pattern in regmatch. ! * Return the matched value in "fields->namebuf". ! */ ! static int ! qf_parse_fmt_f(regmatch_T *rmp, int midx, qffields_T *fields, int prefix) ! { ! int c; ! ! if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) ! return QF_FAIL; ! ! /* Expand ~/file and $HOME/file to full path. */ ! c = *rmp->endp[midx]; ! *rmp->endp[midx] = NUL; ! expand_env(rmp->startp[midx], fields->namebuf, CMDBUFFSIZE); ! *rmp->endp[midx] = c; ! ! /* ! * For separate filename patterns (%O, %P and %Q), the specified file ! * should exist. ! */ ! if (vim_strchr((char_u *)"OPQ", prefix) != NULL ! && mch_getperm(fields->namebuf) == -1) ! return QF_FAIL; ! ! return QF_OK; ! } ! ! /* ! * Parse the match for error number ('%n') pattern in regmatch. ! * Return the matched value in "fields->enr". ! */ ! static int ! qf_parse_fmt_n(regmatch_T *rmp, int midx, qffields_T *fields) ! { ! if (rmp->startp[midx] == NULL) ! return QF_FAIL; ! fields->enr = (int)atol((char *)rmp->startp[midx]); ! return QF_OK; ! } ! ! /* ! * Parse the match for line number (%l') pattern in regmatch. ! * Return the matched value in "fields->lnum". ! */ ! static int ! qf_parse_fmt_l(regmatch_T *rmp, int midx, qffields_T *fields) ! { ! if (rmp->startp[midx] == NULL) ! return QF_FAIL; ! fields->lnum = atol((char *)rmp->startp[midx]); ! return QF_OK; ! } ! ! /* ! * Parse the match for column number ('%c') pattern in regmatch. ! * Return the matched value in "fields->col". ! */ ! static int ! qf_parse_fmt_c(regmatch_T *rmp, int midx, qffields_T *fields) ! { ! if (rmp->startp[midx] == NULL) ! return QF_FAIL; ! fields->col = (int)atol((char *)rmp->startp[midx]); ! return QF_OK; ! } ! ! /* ! * Parse the match for error type ('%t') pattern in regmatch. ! * Return the matched value in "fields->type". ! */ ! static int ! qf_parse_fmt_t(regmatch_T *rmp, int midx, qffields_T *fields) ! { ! if (rmp->startp[midx] == NULL) ! return QF_FAIL; ! fields->type = *rmp->startp[midx]; ! return QF_OK; ! } ! ! /* ! * Parse the match for '%+' format pattern. The whole matching line is included ! * in the error string. Return the matched line in "fields->errmsg". ! */ ! static int ! qf_parse_fmt_plus(char_u *linebuf, int linelen, qffields_T *fields) ! { ! char_u *p; ! ! if (linelen >= fields->errmsglen) ! { ! /* linelen + null terminator */ ! if ((p = vim_realloc(fields->errmsg, linelen + 1)) == NULL) ! return QF_NOMEM; ! fields->errmsg = p; ! fields->errmsglen = linelen + 1; ! } ! vim_strncpy(fields->errmsg, linebuf, linelen); ! return QF_OK; ! } ! ! /* ! * Parse the match for error message ('%m') pattern in regmatch. ! * Return the matched value in "fields->errmsg". ! */ ! static int ! qf_parse_fmt_m(regmatch_T *rmp, int midx, qffields_T *fields) ! { ! char_u *p; ! int len; ! ! if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) ! return QF_FAIL; ! len = (int)(rmp->endp[midx] - rmp->startp[midx]); ! if (len >= fields->errmsglen) ! { ! /* len + null terminator */ ! if ((p = vim_realloc(fields->errmsg, len + 1)) == NULL) ! return QF_NOMEM; ! fields->errmsg = p; ! fields->errmsglen = len + 1; ! } ! vim_strncpy(fields->errmsg, rmp->startp[midx], len); ! return QF_OK; ! } ! ! /* ! * Parse the match for rest of a single-line file message ('%r') pattern. ! * Return the matched value in "tail". ! */ ! static int ! qf_parse_fmt_r(regmatch_T *rmp, int midx, char_u **tail) ! { ! if (rmp->startp[midx] == NULL) ! return QF_FAIL; ! *tail = rmp->startp[midx]; ! return QF_OK; ! } ! ! /* ! * Parse the match for the pointer line ('%p') pattern in regmatch. ! * Return the matched value in "fields->col". ! */ ! static int ! qf_parse_fmt_p(regmatch_T *rmp, int midx, qffields_T *fields) ! { ! char_u *match_ptr; ! ! if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) ! return QF_FAIL; ! fields->col = 0; ! for (match_ptr = rmp->startp[midx]; match_ptr != rmp->endp[midx]; ! ++match_ptr) ! { ! ++fields->col; ! if (*match_ptr == TAB) ! { ! fields->col += 7; ! fields->col -= fields->col % 8; ! } ! } ! ++fields->col; ! fields->use_viscol = TRUE; ! return QF_OK; ! } ! ! /* ! * Parse the match for the virtual column number ('%v') pattern in regmatch. ! * Return the matched value in "fields->col". ! */ ! static int ! qf_parse_fmt_v(regmatch_T *rmp, int midx, qffields_T *fields) ! { ! if (rmp->startp[midx] == NULL) ! return QF_FAIL; ! fields->col = (int)atol((char *)rmp->startp[midx]); ! fields->use_viscol = TRUE; ! return QF_OK; ! } ! ! /* ! * Parse the match for the search text ('%s') pattern in regmatch. ! * Return the matched value in "fields->pattern". ! */ ! static int ! qf_parse_fmt_s(regmatch_T *rmp, int midx, qffields_T *fields) ! { ! int len; ! ! if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) ! return QF_FAIL; ! len = (int)(rmp->endp[midx] - rmp->startp[midx]); ! if (len > CMDBUFFSIZE - 5) ! len = CMDBUFFSIZE - 5; ! STRCPY(fields->pattern, "^\\V"); ! STRNCAT(fields->pattern, rmp->startp[midx], len); ! fields->pattern[len + 3] = '\\'; ! fields->pattern[len + 4] = '$'; ! fields->pattern[len + 5] = NUL; ! return QF_OK; ! } ! ! /* ! * Parse the match for the module ('%o') pattern in regmatch. ! * Return the matched value in "fields->module". ! */ ! static int ! qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields) ! { ! int len; ! ! if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) ! return QF_FAIL; ! len = (int)(rmp->endp[midx] - rmp->startp[midx]); ! if (len > CMDBUFFSIZE) ! len = CMDBUFFSIZE; ! STRNCAT(fields->module, rmp->startp[midx], len); ! return QF_OK; ! } ! ! /* ! * 'errorformat' format pattern parser functions. ! * The '%f' and '%r' formats are parsed differently from other formats. ! * See qf_parse_match() for details. ! */ ! static int (*qf_parse_fmt[FMT_PATTERNS])(regmatch_T *, int, qffields_T *) = ! { ! NULL, ! qf_parse_fmt_n, ! qf_parse_fmt_l, ! qf_parse_fmt_c, ! qf_parse_fmt_t, ! qf_parse_fmt_m, ! NULL, ! qf_parse_fmt_p, ! qf_parse_fmt_v, ! qf_parse_fmt_s, ! qf_parse_fmt_o ! }; ! ! /* ! * Parse the error format pattern matches in "regmatch" and set the values in ! * "fields". fmt_ptr contains the 'efm' format specifiers/prefixes that have a ! * match. Returns QF_OK if all the matches are successfully parsed. On ! * failure, returns QF_FAIL or QF_NOMEM. */ static int qf_parse_match( *************** *** 877,886 **** int qf_multiscan, char_u **tail) { - char_u *p; int idx = fmt_ptr->prefix; int i; ! int len; if ((idx == 'C' || idx == 'Z') && !qf_multiline) return QF_FAIL; --- 1167,1176 ---- int qf_multiscan, char_u **tail) { int idx = fmt_ptr->prefix; int i; ! int midx; ! int status; if ((idx == 'C' || idx == 'Z') && !qf_multiline) return QF_FAIL; *************** *** 893,1020 **** * We check for an actual submatch, because "\[" and "\]" in * the 'errorformat' may cause the wrong submatch to be used. */ ! if ((i = (int)fmt_ptr->addr[0]) > 0) /* %f */ { ! int c; ! if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) ! return QF_FAIL; ! ! /* Expand ~/file and $HOME/file to full path. */ ! c = *regmatch->endp[i]; ! *regmatch->endp[i] = NUL; ! expand_env(regmatch->startp[i], fields->namebuf, CMDBUFFSIZE); ! *regmatch->endp[i] = c; ! ! if (vim_strchr((char_u *)"OPQ", idx) != NULL ! && mch_getperm(fields->namebuf) == -1) ! return QF_FAIL; ! } ! if ((i = (int)fmt_ptr->addr[1]) > 0) /* %n */ ! { ! if (regmatch->startp[i] == NULL) ! return QF_FAIL; ! fields->enr = (int)atol((char *)regmatch->startp[i]); ! } ! if ((i = (int)fmt_ptr->addr[2]) > 0) /* %l */ ! { ! if (regmatch->startp[i] == NULL) ! return QF_FAIL; ! fields->lnum = atol((char *)regmatch->startp[i]); ! } ! if ((i = (int)fmt_ptr->addr[3]) > 0) /* %c */ ! { ! if (regmatch->startp[i] == NULL) ! return QF_FAIL; ! fields->col = (int)atol((char *)regmatch->startp[i]); ! } ! if ((i = (int)fmt_ptr->addr[4]) > 0) /* %t */ ! { ! if (regmatch->startp[i] == NULL) ! return QF_FAIL; ! fields->type = *regmatch->startp[i]; ! } ! if (fmt_ptr->flags == '+' && !qf_multiscan) /* %+ */ ! { ! if (linelen >= fields->errmsglen) ! { ! /* linelen + null terminator */ ! if ((p = vim_realloc(fields->errmsg, linelen + 1)) == NULL) ! return QF_NOMEM; ! fields->errmsg = p; ! fields->errmsglen = linelen + 1; ! } ! vim_strncpy(fields->errmsg, linebuf, linelen); ! } ! else if ((i = (int)fmt_ptr->addr[5]) > 0) /* %m */ ! { ! if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) ! return QF_FAIL; ! len = (int)(regmatch->endp[i] - regmatch->startp[i]); ! if (len >= fields->errmsglen) ! { ! /* len + null terminator */ ! if ((p = vim_realloc(fields->errmsg, len + 1)) == NULL) ! return QF_NOMEM; ! fields->errmsg = p; ! fields->errmsglen = len + 1; ! } ! vim_strncpy(fields->errmsg, regmatch->startp[i], len); ! } ! if ((i = (int)fmt_ptr->addr[6]) > 0) /* %r */ ! { ! if (regmatch->startp[i] == NULL) ! return QF_FAIL; ! *tail = regmatch->startp[i]; ! } ! if ((i = (int)fmt_ptr->addr[7]) > 0) /* %p */ ! { ! char_u *match_ptr; ! ! if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) ! return QF_FAIL; ! fields->col = 0; ! for (match_ptr = regmatch->startp[i]; ! match_ptr != regmatch->endp[i]; ++match_ptr) ! { ! ++fields->col; ! if (*match_ptr == TAB) ! { ! fields->col += 7; ! fields->col -= fields->col % 8; ! } ! } ! ++fields->col; ! fields->use_viscol = TRUE; ! } ! if ((i = (int)fmt_ptr->addr[8]) > 0) /* %v */ ! { ! if (regmatch->startp[i] == NULL) ! return QF_FAIL; ! fields->col = (int)atol((char *)regmatch->startp[i]); ! fields->use_viscol = TRUE; ! } ! if ((i = (int)fmt_ptr->addr[9]) > 0) /* %s */ ! { ! if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) ! return QF_FAIL; ! len = (int)(regmatch->endp[i] - regmatch->startp[i]); ! if (len > CMDBUFFSIZE - 5) ! len = CMDBUFFSIZE - 5; ! STRCPY(fields->pattern, "^\\V"); ! STRNCAT(fields->pattern, regmatch->startp[i], len); ! fields->pattern[len + 3] = '\\'; ! fields->pattern[len + 4] = '$'; ! fields->pattern[len + 5] = NUL; ! } ! if ((i = (int)fmt_ptr->addr[10]) > 0) /* %o */ ! { ! if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) ! return QF_FAIL; ! len = (int)(regmatch->endp[i] - regmatch->startp[i]); ! if (len > CMDBUFFSIZE) ! len = CMDBUFFSIZE; ! STRNCAT(fields->module, regmatch->startp[i], len); } return QF_OK; --- 1183,1208 ---- * We check for an actual submatch, because "\[" and "\]" in * the 'errorformat' may cause the wrong submatch to be used. */ ! for (i = 0; i < FMT_PATTERNS; i++) { ! status = QF_OK; ! midx = (int)fmt_ptr->addr[i]; ! if (i == 0 && midx > 0) /* %f */ ! status = qf_parse_fmt_f(regmatch, midx, fields, idx); ! else if (i == 5) ! { ! if (fmt_ptr->flags == '+' && !qf_multiscan) /* %+ */ ! status = qf_parse_fmt_plus(linebuf, linelen, fields); ! else if (midx > 0) /* %m */ ! status = qf_parse_fmt_m(regmatch, midx, fields); ! } ! else if (i == 6 && midx > 0) /* %r */ ! status = qf_parse_fmt_r(regmatch, midx, tail); ! else if (midx > 0) /* others */ ! status = (qf_parse_fmt[i])(regmatch, midx, fields); ! if (status != QF_OK) ! return status; } return QF_OK; *************** *** 1308,1313 **** --- 1496,1513 ---- } /* + * Returns TRUE if the specified quickfix/location list is empty. + */ + static int + qf_list_empty(qf_info_T *qi, int qf_idx) + { + if (qi == NULL || qf_idx < 0 || qf_idx >= LISTCOUNT) + return TRUE; + return qi->qf_lists[qf_idx].qf_count <= 0; + } + + + /* * Allocate the fields used for parsing lines and populating a quickfix list. */ static int *************** *** 1450,1456 **** { /* Adding to existing list, use last entry. */ adding = TRUE; ! if (qi->qf_lists[qf_idx].qf_count > 0) old_last = qi->qf_lists[qf_idx].qf_last; } --- 1650,1656 ---- { /* Adding to existing list, use last entry. */ adding = TRUE; ! if (!qf_list_empty(qi, qf_idx)) old_last = qi->qf_lists[qf_idx].qf_last; } *************** *** 1777,1784 **** qfp->qf_valid = valid; lastp = &qi->qf_lists[qf_idx].qf_last; ! if (qi->qf_lists[qf_idx].qf_count == 0) ! /* first element in the list */ { qi->qf_lists[qf_idx].qf_start = qfp; qi->qf_lists[qf_idx].qf_ptr = qfp; --- 1977,1983 ---- qfp->qf_valid = valid; lastp = &qi->qf_lists[qf_idx].qf_last; ! if (qf_list_empty(qi, qf_idx)) /* first element in the list */ { qi->qf_lists[qf_idx].qf_start = qfp; qi->qf_lists[qf_idx].qf_ptr = qfp; *************** *** 1875,1881 **** to->w_llist->qf_listcount = qi->qf_listcount; /* Copy the location lists one at a time */ ! for (idx = 0; idx < qi->qf_listcount; idx++) { qf_list_T *from_qfl; qf_list_T *to_qfl; --- 2074,2080 ---- to->w_llist->qf_listcount = qi->qf_listcount; /* Copy the location lists one at a time */ ! for (idx = 0; idx < qi->qf_listcount; ++idx) { qf_list_T *from_qfl; qf_list_T *to_qfl; *************** *** 2907,2913 **** qi = &ql_info; if (qi->qf_curlist >= qi->qf_listcount ! || qi->qf_lists[qi->qf_curlist].qf_count == 0) { EMSG(_(e_quickfix)); return; --- 3106,3112 ---- qi = &ql_info; if (qi->qf_curlist >= qi->qf_listcount ! || qf_list_empty(qi, qi->qf_curlist)) { EMSG(_(e_quickfix)); return; *************** *** 3033,3056 **** } /* * ":clist": list all errors * ":llist": list all locations */ void qf_list(exarg_T *eap) { - buf_T *buf; - char_u *fname; qfline_T *qfp; int i; int idx1 = 1; int idx2 = -1; char_u *arg = eap->arg; int plus = FALSE; - int qfFileAttr; - int qfSepAttr; - int qfLineAttr; - int filter_entry; int all = eap->forceit; /* if not :cl!, only show recognised errors */ qf_info_T *qi = &ql_info; --- 3232,3336 ---- } /* + * Highlight attributes used for displaying entries from the quickfix list. + */ + static int qfFileAttr; + static int qfSepAttr; + static int qfLineAttr; + + /* + * Display information about a single entry from the quickfix/location list. + * Used by ":clist/:llist" commands. + */ + static void + qf_list_entry(qf_info_T *qi, qfline_T *qfp, int qf_idx) + { + char_u *fname; + buf_T *buf; + int filter_entry; + + fname = NULL; + if (qfp->qf_module != NULL && *qfp->qf_module != NUL) + vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", qf_idx, + (char *)qfp->qf_module); + else { + if (qfp->qf_fnum != 0 + && (buf = buflist_findnr(qfp->qf_fnum)) != NULL) + { + fname = buf->b_fname; + if (qfp->qf_type == 1) /* :helpgrep */ + fname = gettail(fname); + } + if (fname == NULL) + sprintf((char *)IObuff, "%2d", qf_idx); + else + vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", + qf_idx, (char *)fname); + } + + // Support for filtering entries using :filter /pat/ clist + // Match against the module name, file name, search pattern and + // text of the entry. + filter_entry = TRUE; + if (qfp->qf_module != NULL && *qfp->qf_module != NUL) + filter_entry &= message_filtered(qfp->qf_module); + if (filter_entry && fname != NULL) + filter_entry &= message_filtered(fname); + if (filter_entry && qfp->qf_pattern != NULL) + filter_entry &= message_filtered(qfp->qf_pattern); + if (filter_entry) + filter_entry &= message_filtered(qfp->qf_text); + if (filter_entry) + return; + + msg_putchar('\n'); + msg_outtrans_attr(IObuff, qf_idx == qi->qf_lists[qi->qf_curlist].qf_index + ? HL_ATTR(HLF_QFL) : qfFileAttr); + + if (qfp->qf_lnum != 0) + msg_puts_attr((char_u *)":", qfSepAttr); + if (qfp->qf_lnum == 0) + IObuff[0] = NUL; + else if (qfp->qf_col == 0) + sprintf((char *)IObuff, "%ld", qfp->qf_lnum); + else + sprintf((char *)IObuff, "%ld col %d", + qfp->qf_lnum, qfp->qf_col); + sprintf((char *)IObuff + STRLEN(IObuff), "%s", + (char *)qf_types(qfp->qf_type, qfp->qf_nr)); + msg_puts_attr(IObuff, qfLineAttr); + msg_puts_attr((char_u *)":", qfSepAttr); + if (qfp->qf_pattern != NULL) + { + qf_fmt_text(qfp->qf_pattern, IObuff, IOSIZE); + msg_puts(IObuff); + msg_puts_attr((char_u *)":", qfSepAttr); + } + msg_puts((char_u *)" "); + + /* Remove newlines and leading whitespace from the text. For an + * unrecognized line keep the indent, the compiler may mark a word + * with ^^^^. */ + qf_fmt_text((fname != NULL || qfp->qf_lnum != 0) + ? skipwhite(qfp->qf_text) : qfp->qf_text, + IObuff, IOSIZE); + msg_prt_line(IObuff, FALSE); + out_flush(); /* show one line at a time */ + } + + /* * ":clist": list all errors * ":llist": list all locations */ void qf_list(exarg_T *eap) { qfline_T *qfp; int i; int idx1 = 1; int idx2 = -1; char_u *arg = eap->arg; int plus = FALSE; int all = eap->forceit; /* if not :cl!, only show recognised errors */ qf_info_T *qi = &ql_info; *************** *** 3066,3072 **** } if (qi->qf_curlist >= qi->qf_listcount ! || qi->qf_lists[qi->qf_curlist].qf_count == 0) { EMSG(_(e_quickfix)); return; --- 3346,3352 ---- } if (qi->qf_curlist >= qi->qf_listcount ! || qf_list_empty(qi, qi->qf_curlist)) { EMSG(_(e_quickfix)); return; *************** *** 3123,3197 **** if (got_int) break; ! fname = NULL; ! if (qfp->qf_module != NULL && *qfp->qf_module != NUL) ! vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", i, (char *)qfp->qf_module); ! else { ! if (qfp->qf_fnum != 0 ! && (buf = buflist_findnr(qfp->qf_fnum)) != NULL) ! { ! fname = buf->b_fname; ! if (qfp->qf_type == 1) /* :helpgrep */ ! fname = gettail(fname); ! } ! if (fname == NULL) ! sprintf((char *)IObuff, "%2d", i); ! else ! vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", ! i, (char *)fname); ! } ! ! // Support for filtering entries using :filter /pat/ clist ! // Match against the module name, file name, search pattern and ! // text of the entry. ! filter_entry = TRUE; ! if (qfp->qf_module != NULL && *qfp->qf_module != NUL) ! filter_entry &= message_filtered(qfp->qf_module); ! if (filter_entry && fname != NULL) ! filter_entry &= message_filtered(fname); ! if (filter_entry && qfp->qf_pattern != NULL) ! filter_entry &= message_filtered(qfp->qf_pattern); ! if (filter_entry) ! filter_entry &= message_filtered(qfp->qf_text); ! if (filter_entry) ! goto next_entry; ! ! msg_putchar('\n'); ! msg_outtrans_attr(IObuff, i == qi->qf_lists[qi->qf_curlist].qf_index ! ? HL_ATTR(HLF_QFL) : qfFileAttr); ! ! if (qfp->qf_lnum != 0) ! msg_puts_attr((char_u *)":", qfSepAttr); ! if (qfp->qf_lnum == 0) ! IObuff[0] = NUL; ! else if (qfp->qf_col == 0) ! sprintf((char *)IObuff, "%ld", qfp->qf_lnum); ! else ! sprintf((char *)IObuff, "%ld col %d", ! qfp->qf_lnum, qfp->qf_col); ! sprintf((char *)IObuff + STRLEN(IObuff), "%s", ! (char *)qf_types(qfp->qf_type, qfp->qf_nr)); ! msg_puts_attr(IObuff, qfLineAttr); ! msg_puts_attr((char_u *)":", qfSepAttr); ! if (qfp->qf_pattern != NULL) ! { ! qf_fmt_text(qfp->qf_pattern, IObuff, IOSIZE); ! msg_puts(IObuff); ! msg_puts_attr((char_u *)":", qfSepAttr); ! } ! msg_puts((char_u *)" "); ! ! /* Remove newlines and leading whitespace from the text. For an ! * unrecognized line keep the indent, the compiler may mark a word ! * with ^^^^. */ ! qf_fmt_text((fname != NULL || qfp->qf_lnum != 0) ! ? skipwhite(qfp->qf_text) : qfp->qf_text, ! IObuff, IOSIZE); ! msg_prt_line(IObuff, FALSE); ! out_flush(); /* show one line at a time */ } - next_entry: qfp = qfp->qf_next; if (qfp == NULL) break; --- 3403,3411 ---- if (got_int) break; ! qf_list_entry(qi, qfp, i); } qfp = qfp->qf_next; if (qfp == NULL) break; *************** *** 3320,3326 **** if (eap->cmdidx == CMD_lhistory) qi = GET_LOC_LIST(curwin); if (qi == NULL || (qi->qf_listcount == 0 ! && qi->qf_lists[qi->qf_curlist].qf_count == 0)) MSG(_("No entries")); else for (i = 0; i < qi->qf_listcount; ++i) --- 3534,3540 ---- if (eap->cmdidx == CMD_lhistory) qi = GET_LOC_LIST(curwin); if (qi == NULL || (qi->qf_listcount == 0 ! && qf_list_empty(qi, qi->qf_curlist))) MSG(_("No entries")); else for (i = 0; i < qi->qf_listcount; ++i) *************** *** 3421,3427 **** } for (idx = 0; idx < qi->qf_listcount; ++idx) ! if (qi->qf_lists[idx].qf_count) for (i = 0, qfp = qi->qf_lists[idx].qf_start; i < qi->qf_lists[idx].qf_count && qfp != NULL; ++i, qfp = qfp->qf_next) --- 3635,3641 ---- } for (idx = 0; idx < qi->qf_listcount; ++idx) ! if (!qf_list_empty(qi, idx)) for (i = 0, qfp = qi->qf_lists[idx].qf_start; i < qi->qf_lists[idx].qf_count && qfp != NULL; ++i, qfp = qfp->qf_next) *************** *** 3552,3558 **** * it if we have errors; otherwise, leave it closed. */ if (qi->qf_lists[qi->qf_curlist].qf_nonevalid ! || qi->qf_lists[qi->qf_curlist].qf_count == 0 || qi->qf_curlist >= qi->qf_listcount) { if (win != NULL) --- 3766,3772 ---- * it if we have errors; otherwise, leave it closed. */ if (qi->qf_lists[qi->qf_curlist].qf_nonevalid ! || qf_list_empty(qi, qi->qf_curlist) || qi->qf_curlist >= qi->qf_listcount) { if (win != NULL) *************** *** 5154,5160 **** qi->qf_curlist = qf_id2nr(qi, save_qfid); /* Jump to first match. */ ! if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { if ((flags & VGR_NOJUMP) == 0) vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, --- 5368,5374 ---- qi->qf_curlist = qf_id2nr(qi, save_qfid); /* Jump to first match. */ ! if (!qf_list_empty(qi, qi->qf_curlist)) { if ((flags & VGR_NOJUMP) == 0) vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, *************** *** 5387,5394 **** if (qf_idx == INVALID_QFIDX) qf_idx = qi->qf_curlist; ! if (qf_idx >= qi->qf_listcount ! || qi->qf_lists[qf_idx].qf_count == 0) return FAIL; qfp = qi->qf_lists[qf_idx].qf_start; --- 5601,5607 ---- if (qf_idx == INVALID_QFIDX) qf_idx = qi->qf_curlist; ! if (qf_idx >= qi->qf_listcount || qf_list_empty(qi, qf_idx)) return FAIL; qfp = qi->qf_lists[qf_idx].qf_start; *************** *** 5709,5715 **** qf_getprop_idx(qf_info_T *qi, int qf_idx, dict_T *retdict) { int idx = qi->qf_lists[qf_idx].qf_index; ! if (qi->qf_lists[qf_idx].qf_count == 0) /* For empty lists, qf_index is set to 1 */ idx = 0; return dict_add_number(retdict, "idx", idx); --- 5922,5928 ---- qf_getprop_idx(qf_info_T *qi, int qf_idx, dict_T *retdict) { int idx = qi->qf_lists[qf_idx].qf_index; ! if (qf_list_empty(qi, qf_idx)) /* For empty lists, qf_index is set to 1 */ idx = 0; return dict_add_number(retdict, "idx", idx); *************** *** 5798,5804 **** qf_new_list(qi, title); qf_idx = qi->qf_curlist; } ! else if (action == 'a' && qi->qf_lists[qf_idx].qf_count > 0) /* Adding to existing list, use last entry. */ old_last = qi->qf_lists[qf_idx].qf_last; else if (action == 'r') --- 6011,6017 ---- qf_new_list(qi, title); qf_idx = qi->qf_curlist; } ! else if (action == 'a' && !qf_list_empty(qi, qf_idx)) /* Adding to existing list, use last entry. */ old_last = qi->qf_lists[qf_idx].qf_last; else if (action == 'r') *************** *** 5887,5893 **** { qi->qf_lists[qf_idx].qf_ptr = qi->qf_lists[qf_idx].qf_start; ! if (qi->qf_lists[qf_idx].qf_count > 0) qi->qf_lists[qf_idx].qf_index = 1; } --- 6100,6106 ---- { qi->qf_lists[qf_idx].qf_ptr = qi->qf_lists[qf_idx].qf_start; ! if (!qf_list_empty(qi, qf_idx)) qi->qf_lists[qf_idx].qf_index = 1; } *************** *** 6746,6752 **** } /* Jump to first match. */ ! if (qi->qf_lists[qi->qf_curlist].qf_count > 0) qf_jump(qi, 0, 0, FALSE); else EMSG2(_(e_nomatch2), eap->arg); --- 6959,6965 ---- } /* Jump to first match. */ ! if (!qf_list_empty(qi, qi->qf_curlist)) qf_jump(qi, 0, 0, FALSE); else EMSG2(_(e_nomatch2), eap->arg); *** ../vim-8.1.0251/src/version.c 2018-08-07 21:39:09.251060096 +0200 --- src/version.c 2018-08-07 21:43:08.981711510 +0200 *************** *** 796,797 **** --- 796,799 ---- { /* Add new patch number below this line */ + /**/ + 252, /**/ -- Q: How do you tell the difference between a female cat and a male cat? A: You ask it a question and if HE answers, it's a male but, if SHE answers, it's a female. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///