diff options
Diffstat (limited to 'src/nvim/ex_cmds.c')
-rw-r--r-- | src/nvim/ex_cmds.c | 519 |
1 files changed, 332 insertions, 187 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index b1a17e8c44..32dbf4cc69 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1,3 +1,6 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + /* * ex_cmds.c: some functions for command line commands */ @@ -315,14 +318,12 @@ static int sort_abort; ///< flag to indicate if sorting has been interrupted /// Struct to store info to be sorted. typedef struct { linenr_T lnum; ///< line number - long start_col_nr; ///< starting column number or number - long end_col_nr; ///< ending column number union { struct { - long start_col_nr; ///< starting column number - long end_col_nr; ///< ending column number + varnumber_T start_col_nr; ///< starting column number + varnumber_T end_col_nr; ///< ending column number } line; - long value; ///< value if sorting by integer + varnumber_T value; ///< value if sorting by integer float_T value_flt; ///< value if sorting by float } st_u; } sorti_T; @@ -357,12 +358,12 @@ static int sort_compare(const void *s1, const void *s2) // We need to copy one line into "sortbuf1", because there is no // guarantee that the first pointer becomes invalid when obtaining the // second one. - STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.st_u.line.start_col_nr, - l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1); - sortbuf1[l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr] = 0; - STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.st_u.line.start_col_nr, - l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1); - sortbuf2[l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr] = 0; + memcpy(sortbuf1, ml_get(l1.lnum) + l1.st_u.line.start_col_nr, + l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1); + sortbuf1[l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr] = NUL; + memcpy(sortbuf2, ml_get(l2.lnum) + l2.st_u.line.start_col_nr, + l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1); + sortbuf2[l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr] = NUL; result = sort_ic ? STRICMP(sortbuf1, sortbuf2) : STRCMP(sortbuf1, sortbuf2); @@ -596,9 +597,10 @@ void ex_sort(exarg_T *eap) // Adjust marks for deleted (or added) lines and prepare for displaying. deleted = (long)(count - (lnum - eap->line2)); if (deleted > 0) { - mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted); + mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted, + false); } else if (deleted < 0) { - mark_adjust(eap->line2, MAXLNUM, -deleted, 0L); + mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, false); } changed_lines(eap->line1, 0, eap->line2 + 1, -deleted); @@ -756,14 +758,6 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) linenr_T num_lines; // Num lines moved linenr_T last_line; // Last line in file after adding new text - // Moving lines seems to corrupt the folds, delete folding info now - // and recreate it when finished. Don't do this for manual folding, it - // would delete all folds. - bool isFolded = hasAnyFolding(curwin) && !foldmethodIsManual(curwin); - if (isFolded) { - deleteFoldRecurse(&curwin->w_folds); - } - if (dest >= line1 && dest < line2) { EMSG(_("E134: Move lines into themselves")); return FAIL; @@ -801,21 +795,29 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) * their final destination at the new text position -- webb */ last_line = curbuf->b_ml.ml_line_count; - mark_adjust(line1, line2, last_line - line2, 0L); - changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines); + mark_adjust_nofold(line1, line2, last_line - line2, 0L, true); if (dest >= line2) { - mark_adjust(line2 + 1, dest, -num_lines, 0L); + mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L, false); + FOR_ALL_TAB_WINDOWS(tab, win) { + if (win->w_buffer == curbuf) { + foldMoveRange(&win->w_folds, line1, line2, dest); + } + } curbuf->b_op_start.lnum = dest - num_lines + 1; curbuf->b_op_end.lnum = dest; } else { - mark_adjust(dest + 1, line1 - 1, num_lines, 0L); + mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L, false); + FOR_ALL_TAB_WINDOWS(tab, win) { + if (win->w_buffer == curbuf) { + foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2); + } + } curbuf->b_op_start.lnum = dest + 1; curbuf->b_op_end.lnum = dest + num_lines; } curbuf->b_op_start.col = curbuf->b_op_end.col = 0; - mark_adjust(last_line - num_lines + 1, last_line, - -(last_line - dest - extra), 0L); - changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra); + mark_adjust_nofold(last_line - num_lines + 1, last_line, + -(last_line - dest - extra), 0L, true); /* * Now we delete the original text -- webb @@ -851,11 +853,6 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) changed_lines(dest + 1, 0, line1 + num_lines, 0L); } - // recreate folds - if (isFolded) { - foldUpdateAll(curwin); - } - return OK; } @@ -1013,8 +1010,8 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) AppendToRedobuffLit(cmd, -1); xfree(cmd); - AppendToRedobuff((char_u *)"\n"); - bangredo = FALSE; + AppendToRedobuff("\n"); + bangredo = false; } /* * Add quotes around the command, for shells that need them. @@ -1131,11 +1128,12 @@ static void do_filter( */ ++no_wait_return; /* don't call wait_return() while busy */ if (itmp != NULL && buf_write(curbuf, itmp, NULL, line1, line2, eap, - FALSE, FALSE, FALSE, TRUE) == FAIL) { - msg_putchar('\n'); /* keep message from buf_write() */ - --no_wait_return; - if (!aborting()) - (void)EMSG2(_(e_notcreate), itmp); /* will call wait_return */ + false, false, false, true) == FAIL) { + msg_putchar('\n'); // Keep message from buf_write(). + no_wait_return--; + if (!aborting()) { + EMSG2(_("E482: Can't create file %s"), itmp); // Will call wait_return. + } goto filterend; } if (curbuf != old_curbuf) @@ -1193,8 +1191,8 @@ static void do_filter( if (do_out) { if (otmp != NULL) { - if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM, - eap, READ_FILTER) == FAIL) { + if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, + READ_FILTER) != OK) { if (!aborting()) { msg_putchar('\n'); EMSG2(_(e_notread), otmp); @@ -1215,15 +1213,14 @@ static void do_filter( if (do_in) { if (cmdmod.keepmarks || vim_strchr(p_cpo, CPO_REMMARK) == NULL) { - if (read_linecount >= linecount) - /* move all marks from old lines to new lines */ - mark_adjust(line1, line2, linecount, 0L); - else { - /* move marks from old lines to new lines, delete marks - * that are in deleted lines */ - mark_adjust(line1, line1 + read_linecount - 1, - linecount, 0L); - mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L); + if (read_linecount >= linecount) { + // move all marks from old lines to new lines + mark_adjust(line1, line2, linecount, 0L, false); + } else { + // move marks from old lines to new lines, delete marks + // that are in deleted lines + mark_adjust(line1, line1 + read_linecount - 1, linecount, 0L, false); + mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L, false); } } @@ -1404,36 +1401,34 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) : "(%s)"; vim_snprintf(buf, len, fmt, (char *)cmd); } else { - strncpy(buf, (char *) cmd, len); + xstrlcpy(buf, (char *)cmd, len); } if (itmp != NULL) { - strncat(buf, " < ", len); - strncat(buf, (char *) itmp, len); + xstrlcat(buf, " < ", len - 1); + xstrlcat(buf, (const char *)itmp, len - 1); } #else // For shells that don't understand braces around commands, at least allow // the use of commands in a pipe. - strncpy(buf, cmd, len); + xstrlcpy(buf, cmd, len); if (itmp != NULL) { - char_u *p; - // If there is a pipe, we have to put the '<' in front of it. // Don't do this when 'shellquote' is not empty, otherwise the // redirection would be inside the quotes. if (*p_shq == NUL) { - p = strchr(buf, '|'); + char *const p = strchr(buf, '|'); if (p != NULL) { *p = NUL; } } - strncat(buf, " < ", len); - strncat(buf, (char *) itmp, len); + xstrlcat(buf, " < ", len); + xstrlcat(buf, (const char *)itmp, len); if (*p_shq == NUL) { - p = strchr(cmd, '|'); + const char *const p = strchr((const char *)cmd, '|'); if (p != NULL) { - strncat(buf, " ", len); // Insert a space before the '|' for DOS - strncat(buf, p, len); + xstrlcat(buf, " ", len - 1); // Insert a space before the '|' for DOS + xstrlcat(buf, p, len - 1); } } } @@ -1476,12 +1471,12 @@ void append_redir(char *const buf, const size_t buflen, void print_line_no_prefix(linenr_T lnum, int use_number, int list) { - char_u numbuf[30]; + char numbuf[30]; if (curwin->w_p_nu || use_number) { - vim_snprintf((char *)numbuf, sizeof(numbuf), - "%*ld ", number_width(curwin), (long)lnum); - msg_puts_attr(numbuf, hl_attr(HLF_N)); /* Highlight line nrs */ + vim_snprintf(numbuf, sizeof(numbuf), "%*" PRIdLINENR " ", + number_width(curwin), lnum); + msg_puts_attr(numbuf, hl_attr(HLF_N)); // Highlight line nrs. } msg_prt_line(ml_get(lnum), list); } @@ -1493,6 +1488,11 @@ void print_line(linenr_T lnum, int use_number, int list) { int save_silent = silent_mode; + // apply :filter /pat/ + if (message_filtered(ml_get(lnum))) { + return; + } + msg_start(); silent_mode = FALSE; info_message = TRUE; /* use mch_msg(), not mch_errmsg() */ @@ -1906,11 +1906,15 @@ void do_wqall(exarg_T *eap) FALSE) == FAIL) { ++error; } else { - if (buf_write_all(buf, eap->forceit) == FAIL) - ++error; - /* an autocommand may have deleted the buffer */ - if (!buf_valid(buf)) + bufref_T bufref; + set_bufref(&bufref, buf); + if (buf_write_all(buf, eap->forceit) == FAIL) { + error++; + } + // An autocommand may have deleted the buffer. + if (!bufref_valid(&bufref)) { buf = firstbuf; + } } eap->forceit = save_forceit; /* check_overwrite() may set it */ } @@ -2083,7 +2087,8 @@ int do_ecmd( char_u *new_name = NULL; int did_set_swapcommand = FALSE; buf_T *buf; - buf_T *old_curbuf = curbuf; + bufref_T bufref; + bufref_T old_curbuf; char_u *free_fname = NULL; int retval = FAIL; long n; @@ -2100,6 +2105,8 @@ int do_ecmd( if (eap != NULL) command = eap->do_ecmd_cmd; + set_bufref(&old_curbuf, curbuf); + if (fnum != 0) { if (fnum == curbuf->b_fnum) /* file is already being edited */ return OK; /* nothing to do */ @@ -2211,23 +2218,27 @@ int do_ecmd( if (oldwin != NULL) { oldwin = curwin; } - old_curbuf = curbuf; + set_bufref(&old_curbuf, curbuf); } if (buf == NULL) goto theend; - if (buf->b_ml.ml_mfp == NULL) { /* no memfile yet */ - oldbuf = FALSE; - } else { /* existing memfile */ - oldbuf = TRUE; - (void)buf_check_timestamp(buf, FALSE); - /* Check if autocommands made buffer invalid or changed the current - * buffer. */ - if (!buf_valid(buf) - || curbuf != old_curbuf - ) + if (buf->b_ml.ml_mfp == NULL) { + // No memfile yet. + oldbuf = false; + } else { + // Existing memfile. + oldbuf = true; + set_bufref(&bufref, buf); + (void)buf_check_timestamp(buf, false); + // Check if autocommands made buffer invalid or changed the current + // buffer. + if (!bufref_valid(&bufref) || curbuf != old_curbuf.br_buf) { goto theend; - if (aborting()) /* autocmds may abort script processing */ + } + if (aborting()) { + // Autocmds may abort script processing. goto theend; + } } /* May jump to last used line number for a loaded buffer or when asked @@ -2255,12 +2266,14 @@ int do_ecmd( * - If we ended up in the new buffer already, need to skip a few * things, set auto_buf. */ - if (buf->b_fname != NULL) + if (buf->b_fname != NULL) { new_name = vim_strsave(buf->b_fname); - au_new_curbuf = buf; - apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); - if (!buf_valid(buf)) { /* new buffer has been deleted */ - delbuf_msg(new_name); /* frees new_name */ + } + set_bufref(&au_new_curbuf, buf); + apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf); + if (!bufref_valid(&au_new_curbuf)) { + // New buffer has been deleted. + delbuf_msg(new_name); // Frees new_name. goto theend; } if (aborting()) { /* autocmds may abort script processing */ @@ -2272,29 +2285,34 @@ int do_ecmd( } else { win_T *the_curwin = curwin; - // Set the w_closing flag to avoid that autocommands close the window. + // Set w_closing to avoid that autocommands close the window. + // Set b_locked for the same reason. the_curwin->w_closing = true; - if (curbuf == old_curbuf) { + buf->b_locked++; + + if (curbuf == old_curbuf.br_buf) { buf_copy_options(buf, BCO_ENTER); } // Close the link to the current buffer. This will set - // curwin->w_buffer to NULL. + // oldwin->w_buffer to NULL. u_sync(false); close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, false); the_curwin->w_closing = false; + buf->b_locked--; // autocmds may abort script processing if (aborting() && curwin->w_buffer != NULL) { xfree(new_name); goto theend; } - /* Be careful again, like above. */ - if (!buf_valid(buf)) { /* new buffer has been deleted */ - delbuf_msg(new_name); /* frees new_name */ + // Be careful again, like above. + if (!bufref_valid(&au_new_curbuf)) { + // New buffer has been deleted. + delbuf_msg(new_name); // Frees new_name. goto theend; } if (buf == curbuf) { // already in new buffer @@ -2327,7 +2345,8 @@ int do_ecmd( } xfree(new_name); - au_new_curbuf = NULL; + au_new_curbuf.br_buf = NULL; + au_new_curbuf.br_buf_free_count = 0; } curwin->w_pcmark.lnum = 1; @@ -2380,10 +2399,12 @@ int do_ecmd( solcol = curwin->w_cursor.col; } buf = curbuf; - if (buf->b_fname != NULL) + if (buf->b_fname != NULL) { new_name = vim_strsave(buf->b_fname); - else + } else { new_name = NULL; + } + set_bufref(&bufref, buf); if (p_ur < 0 || curbuf->b_ml.ml_line_count <= p_ur) { /* Save all the text, so that the reload can be undone. * Sync first so that this is a separate undo-able action. */ @@ -2396,14 +2417,15 @@ int do_ecmd( u_unchanged(curbuf); buf_freeall(curbuf, BFA_KEEP_UNDO); - /* tell readfile() not to clear or reload undo info */ + // Tell readfile() not to clear or reload undo info. readfile_flags = READ_KEEP_UNDO; - } else - buf_freeall(curbuf, 0); /* free all things for buffer */ - /* If autocommands deleted the buffer we were going to re-edit, give - * up and jump to the end. */ - if (!buf_valid(buf)) { - delbuf_msg(new_name); /* frees new_name */ + } else { + buf_freeall(curbuf, 0); // Free all things for buffer. + } + // If autocommands deleted the buffer we were going to re-edit, give + // up and jump to the end. + if (!bufref_valid(&bufref)) { + delbuf_msg(new_name); // Frees new_name. goto theend; } xfree(new_name); @@ -2428,11 +2450,6 @@ int do_ecmd( retval = OK; /* - * Reset cursor position, could be used by autocommands. - */ - check_cursor(); - - /* * Check if we are editing the w_arg_idx file in the argument list. */ check_arg_idx(curwin); @@ -2474,7 +2491,7 @@ int do_ecmd( if (swap_exists_action == SEA_QUIT) retval = FAIL; - handle_swap_exists(old_curbuf); + handle_swap_exists(&old_curbuf); } else { /* Read the modelines, but only to set window-local options. Any * buffer-local options have already been set and may have been @@ -2609,7 +2626,8 @@ static void delbuf_msg(char_u *name) EMSG2(_("E143: Autocommands unexpectedly deleted new buffer %s"), name == NULL ? (char_u *)"" : name); xfree(name); - au_new_curbuf = NULL; + au_new_curbuf.br_buf = NULL; + au_new_curbuf.br_buf_free_count = 0; } static int append_indent = 0; /* autoindent for first line */ @@ -2784,16 +2802,18 @@ void ex_z(exarg_T *eap) int j; linenr_T lnum = eap->line2; - /* Vi compatible: ":z!" uses display height, without a count uses - * 'scroll' */ - if (eap->forceit) + // Vi compatible: ":z!" uses display height, without a count uses + // 'scroll' + if (eap->forceit) { bigness = curwin->w_height; - else if (firstwin == lastwin) + } else if (ONE_WINDOW) { bigness = curwin->w_p_scr * 2; - else + } else { bigness = curwin->w_height - 3; - if (bigness < 1) + } + if (bigness < 1) { bigness = 1; + } x = eap->arg; kind = x; @@ -2862,8 +2882,11 @@ void ex_z(exarg_T *eap) if (end > curbuf->b_ml.ml_line_count) end = curbuf->b_ml.ml_line_count; - if (curs > curbuf->b_ml.ml_line_count) + if (curs > curbuf->b_ml.ml_line_count) { curs = curbuf->b_ml.ml_line_count; + } else if (curs < 1) { + curs = 1; + } for (i = start; i <= end; i++) { if (minus && i == lnum) { @@ -2883,8 +2906,11 @@ void ex_z(exarg_T *eap) } } - curwin->w_cursor.lnum = curs; - ex_no_reprint = TRUE; + if (curwin->w_cursor.lnum != curs) { + curwin->w_cursor.lnum = curs; + curwin->w_cursor.col = 0; + } + ex_no_reprint = true; } /* @@ -2946,7 +2972,7 @@ void sub_set_replacement(SubReplacementString sub) { xfree(old_sub.sub); if (sub.additional_elements != old_sub.additional_elements) { - list_unref(old_sub.additional_elements); + tv_list_unref(old_sub.additional_elements); } old_sub = sub; } @@ -3308,7 +3334,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) // Check for a match on each line. linenr_T line2 = eap->line2; for (linenr_T lnum = eap->line1; - lnum <= line2 && !(got_quit || aborting()); + lnum <= line2 && !(got_quit || aborting()) + && (!preview || matched_lines.size <= (size_t)p_cwh); lnum++) { long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, (colnr_T)0, NULL); @@ -3473,6 +3500,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) setmouse(); /* disable mouse in xterm */ curwin->w_cursor.col = regmatch.startpos[0].col; + if (curwin->w_p_crb) { + do_check_cursorbind(); + } + /* When 'cpoptions' contains "u" don't sync undo when * asking for confirmation. */ if (vim_strchr(p_cpo, CPO_UNDO) != NULL) @@ -3565,11 +3596,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) ui_cursor_goto(msg_row, msg_col); RedrawingDisabled = temp; - ++no_mapping; /* don't map this key */ - ++allow_keys; /* allow special keys */ + no_mapping++; // don't map this key typed = plain_vgetc(); - --allow_keys; - --no_mapping; + no_mapping--; /* clear the question */ msg_didout = FALSE; /* don't scroll up */ @@ -3736,7 +3765,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) *p1 = NUL; // truncate up to the CR ml_append(lnum - 1, new_start, (colnr_T)(p1 - new_start + 1), false); - mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L); + mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, false); if (subflags.do_ask) { appended_lines(lnum - 1, 1L); } else { @@ -3825,7 +3854,7 @@ skip: for (i = 0; i < nmatch_tl; ++i) ml_delete(lnum, (int)FALSE); mark_adjust(lnum, lnum + nmatch_tl - 1, - (long)MAXLNUM, -nmatch_tl); + (long)MAXLNUM, -nmatch_tl, false); if (subflags.do_ask) { deleted_lines(lnum, nmatch_tl); } @@ -4345,19 +4374,22 @@ void ex_help(exarg_T *eap) if (!curwin->w_buffer->b_help || cmdmod.tab != 0 ) { - if (cmdmod.tab != 0) + if (cmdmod.tab != 0) { wp = NULL; - else - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_buffer != NULL && wp->w_buffer->b_help) + } else { + wp = NULL; + FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) { + if (wp2->w_buffer != NULL && wp2->w_buffer->b_help) { + wp = wp2; break; - if (wp != NULL && wp->w_buffer->b_nwindows > 0) + } + } + } + if (wp != NULL && wp->w_buffer->b_nwindows > 0) { win_enter(wp, true); - else { - /* - * There is no help window yet. - * Try to open the file specified by the "helpfile" option. - */ + } else { + // There is no help window yet. + // Try to open the file specified by the "helpfile" option. if ((helpfd = mch_fopen((char *)p_hf, READBIN)) == NULL) { smsg(_("Sorry, help file \"%s\" not found"), p_hf); goto erret; @@ -4751,8 +4783,8 @@ void fix_help_buffer(void) char_u *p; char_u *rt; - /* set filetype to "help". */ - set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL); + // Set filetype to "help". + set_option_value("ft", 0L, "help", OPT_LOCAL); if (!syntax_present(curwin)) { for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { @@ -4814,9 +4846,13 @@ void fix_help_buffer(void) vimconv_T vc; char_u *cp; - /* Find all "doc/ *.txt" files in this directory. */ - add_pathsep((char *)NameBuff); - STRCAT(NameBuff, "doc/*.??[tx]"); + // Find all "doc/ *.txt" files in this directory. + if (!add_pathsep((char *)NameBuff) + || STRLCAT(NameBuff, "doc/*.??[tx]", + sizeof(NameBuff)) >= MAXPATHL) { + EMSG(_(e_fnametoolong)); + continue; + } // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. @@ -4847,8 +4883,9 @@ void fix_help_buffer(void) continue; e1 = vim_strrchr(t1, '.'); e2 = vim_strrchr(path_tail(f2), '.'); - if (e1 == NUL || e2 == NUL) + if (e1 == NULL || e2 == NULL) { continue; + } if (fnamecmp(e1, ".txt") != 0 && fnamecmp(e1, fname + 4) != 0) { /* Not .txt and not .abx, remove it. */ @@ -4979,8 +5016,12 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, // Find all *.txt files. size_t dirlen = STRLCPY(NameBuff, dir, sizeof(NameBuff)); - STRCAT(NameBuff, "/**/*"); // NOLINT - STRCAT(NameBuff, ext); + if (dirlen >= MAXPATHL + || STRLCAT(NameBuff, "/**/*", sizeof(NameBuff)) >= MAXPATHL // NOLINT + || STRLCAT(NameBuff, ext, sizeof(NameBuff)) >= MAXPATHL) { + EMSG(_(e_fnametoolong)); + return; + } // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. @@ -4988,18 +5029,22 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, if (gen_expand_wildcards(1, buff_list, &filecount, &files, EW_FILE|EW_SILENT) == FAIL || filecount == 0) { - if (!got_int) - EMSG2("E151: No match: %s", NameBuff); + if (!got_int) { + EMSG2(_("E151: No match: %s"), NameBuff); + } return; } - /* - * Open the tags file for writing. - * Do this before scanning through all the files. - */ - STRLCPY(NameBuff, dir, sizeof(NameBuff)); - add_pathsep((char *)NameBuff); - STRNCAT(NameBuff, tagfname, sizeof(NameBuff) - dirlen - 2); + // + // Open the tags file for writing. + // Do this before scanning through all the files. + // + memcpy(NameBuff, dir, dirlen + 1); + if (!add_pathsep((char *)NameBuff) + || STRLCAT(NameBuff, tagfname, sizeof(NameBuff)) >= MAXPATHL) { + EMSG(_(e_fnametoolong)); + return; + } fd_tags = mch_fopen((char *)NameBuff, "w"); if (fd_tags == NULL) { EMSG2(_("E152: Cannot open %s for writing"), NameBuff); @@ -5063,14 +5108,13 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, } p1 = vim_strchr(IObuff, '*'); /* find first '*' */ while (p1 != NULL) { - /* Use vim_strbyte() instead of vim_strchr() so that when - * 'encoding' is dbcs it still works, don't find '*' in the - * second byte. */ - p2 = vim_strbyte(p1 + 1, '*'); /* find second '*' */ - if (p2 != NULL && p2 > p1 + 1) { /* skip "*" and "**" */ - for (s = p1 + 1; s < p2; ++s) - if (*s == ' ' || *s == '\t' || *s == '|') + p2 = (char_u *)strchr((const char *)p1 + 1, '*'); // Find second '*'. + if (p2 != NULL && p2 > p1 + 1) { // Skip "*" and "**". + for (s = p1 + 1; s < p2; s++) { + if (*s == ' ' || *s == '\t' || *s == '|') { break; + } + } /* * Only accept a *tag* when it consists of valid @@ -5173,8 +5217,12 @@ static void do_helptags(char_u *dirname, bool add_help_tags) // Get a list of all files in the help directory and in subdirectories. STRLCPY(NameBuff, dirname, sizeof(NameBuff)); - add_pathsep((char *)NameBuff); - STRCAT(NameBuff, "**"); + if (!add_pathsep((char *)NameBuff) + || STRLCAT(NameBuff, "**", sizeof(NameBuff)) >= MAXPATHL) { + EMSG(_(e_fnametoolong)); + xfree(dirname); + return; + } // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. @@ -5182,7 +5230,7 @@ static void do_helptags(char_u *dirname, bool add_help_tags) if (gen_expand_wildcards(1, buff_list, &filecount, &files, EW_FILE|EW_SILENT) == FAIL || filecount == 0) { - EMSG2("E151: No match: %s", NameBuff); + EMSG2(_("E151: No match: %s"), NameBuff); xfree(dirname); return; } @@ -5688,33 +5736,33 @@ void ex_sign(exarg_T *eap) */ static void sign_list_defined(sign_T *sp) { - char_u *p; - smsg("sign %s", sp->sn_name); if (sp->sn_icon != NULL) { - MSG_PUTS(" icon="); + msg_puts(" icon="); msg_outtrans(sp->sn_icon); - MSG_PUTS(_(" (not supported)")); + msg_puts(_(" (not supported)")); } if (sp->sn_text != NULL) { - MSG_PUTS(" text="); + msg_puts(" text="); msg_outtrans(sp->sn_text); } if (sp->sn_line_hl > 0) { - MSG_PUTS(" linehl="); - p = get_highlight_name(NULL, sp->sn_line_hl - 1); - if (p == NULL) - MSG_PUTS("NONE"); - else + msg_puts(" linehl="); + const char *const p = get_highlight_name(NULL, sp->sn_line_hl - 1); + if (p == NULL) { + msg_puts("NONE"); + } else { msg_puts(p); + } } if (sp->sn_text_hl > 0) { - MSG_PUTS(" texthl="); - p = get_highlight_name(NULL, sp->sn_text_hl - 1); - if (p == NULL) - MSG_PUTS("NONE"); - else + msg_puts(" texthl="); + const char *const p = get_highlight_name(NULL, sp->sn_text_hl - 1); + if (p == NULL) { + msg_puts("NONE"); + } else { msg_puts(p); + } } } @@ -5908,9 +5956,8 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) // :sign define {name} {args}... {last}= // | | // last p - if (p == NUL) - { - /* Expand last argument name (before equal sign). */ + if (p == NULL) { + // Expand last argument name (before equal sign). xp->xp_pattern = last; switch (cmd_idx) { @@ -6093,7 +6140,6 @@ void ex_substitute(exarg_T *eap) int save_w_p_cul = curwin->w_p_cul; int save_w_p_cuc = curwin->w_p_cuc; - emsg_off++; // No error messages during command preview. curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes curwin->w_p_cul = false; // Disable 'cursorline' curwin->w_p_cuc = false; // Disable 'cursorcolumn' @@ -6107,7 +6153,7 @@ void ex_substitute(exarg_T *eap) // restore it so that undotree() is identical before/after the preview. curbuf->b_u_newhead = save_b_u_newhead; curbuf->b_u_time_cur = save_b_u_time_cur; - curbuf->b_changedtick = save_changedtick; + buf_set_changedtick(curbuf, save_changedtick); } if (buf_valid(preview_buf)) { // XXX: Must do this *after* u_undo_and_forget(), why? @@ -6120,6 +6166,105 @@ void ex_substitute(exarg_T *eap) restore_search_patterns(); win_size_restore(&save_view); ga_clear(&save_view); - emsg_off--; unblock_autocmds(); } + +/// Skip over the pattern argument of ":vimgrep /pat/[g][j]". +/// Put the start of the pattern in "*s", unless "s" is NULL. +/// If "flags" is not NULL put the flags in it: VGR_GLOBAL, VGR_NOJUMP. +/// If "s" is not NULL terminate the pattern with a NUL. +/// Return a pointer to the char just past the pattern plus flags. +char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags) +{ + int c; + + if (vim_isIDc(*p)) { + // ":vimgrep pattern fname" + if (s != NULL) { + *s = p; + } + p = skiptowhite(p); + if (s != NULL && *p != NUL) { + *p++ = NUL; + } + } else { + // ":vimgrep /pattern/[g][j] fname" + if (s != NULL) { + *s = p + 1; + } + c = *p; + p = skip_regexp(p + 1, c, true, NULL); + if (*p != c) { + return NULL; + } + + // Truncate the pattern. + if (s != NULL) { + *p = NUL; + } + p++; + + // Find the flags + while (*p == 'g' || *p == 'j') { + if (flags != NULL) { + if (*p == 'g') { + *flags |= VGR_GLOBAL; + } else { + *flags |= VGR_NOJUMP; + } + } + p++; + } + } + return p; +} + +/// List v:oldfiles in a nice way. +void ex_oldfiles(exarg_T *eap) +{ + list_T *l = get_vim_var_list(VV_OLDFILES); + listitem_T *li; + long nr = 0; + + if (l == NULL) { + msg((char_u *)_("No old files")); + } else { + msg_start(); + msg_scroll = true; + for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) { + nr++; + const char *fname = tv_get_string(&li->li_tv); + if (!message_filtered((char_u *)fname)) { + msg_outnum(nr); + MSG_PUTS(": "); + msg_outtrans((char_u *)tv_get_string(&li->li_tv)); + msg_clr_eos(); + msg_putchar('\n'); + ui_flush(); // output one line at a time + os_breakcheck(); + } + } + + // Assume "got_int" was set to truncate the listing. + got_int = false; + + // File selection prompt on ":browse oldfiles" + if (cmdmod.browse) { + quit_more = false; + nr = prompt_for_number(false); + msg_starthere(); + if (nr > 0 && nr <= l->lv_len) { + const char *const p = tv_list_find_str(l, nr - 1); + if (p == NULL) { + return; + } + char *const s = (char *)expand_env_save((char_u *)p); + eap->arg = (char_u *)s; + eap->cmdidx = CMD_edit; + cmdmod.browse = false; + do_exedit(eap, NULL); + xfree(s); + } + } + } +} |