aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/clint.py10
-rw-r--r--src/nvim/eval.c80
-rw-r--r--src/nvim/ex_cmds.c61
-rw-r--r--src/nvim/file_search.c1
-rw-r--r--src/nvim/fileio.c9
-rw-r--r--src/nvim/globals.h1
-rw-r--r--src/nvim/hardcopy.c4
-rw-r--r--src/nvim/memory.c27
-rw-r--r--src/nvim/message.c19
-rw-r--r--src/nvim/misc1.c5
-rw-r--r--src/nvim/normal.c30
-rw-r--r--src/nvim/ops.c120
-rw-r--r--src/nvim/os/env.c25
-rw-r--r--src/nvim/os/os_defs.h2
-rw-r--r--src/nvim/path.c21
-rw-r--r--src/nvim/quickfix.c2
-rw-r--r--src/nvim/shada.c2
-rw-r--r--src/nvim/strings.c18
-rw-r--r--src/nvim/syntax.c4
-rw-r--r--src/nvim/tui/tui.c18
-rw-r--r--src/nvim/version.c4
-rw-r--r--src/nvim/vim.h1
22 files changed, 277 insertions, 187 deletions
diff --git a/src/clint.py b/src/clint.py
index 0c9f55c71e..df71282362 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -3166,11 +3166,15 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
# Check if some verboten C functions are being used.
if Search(r'\bsprintf\b', line):
error(filename, linenum, 'runtime/printf', 5,
- 'Never use sprintf. Use snprintf instead.')
- match = Search(r'\b(strcpy|strcat)\b', line)
+ 'Use snprintf instead of sprintf.')
+ match = Search(r'\b(STRCPY|strcpy)\b', line)
if match:
error(filename, linenum, 'runtime/printf', 4,
- 'Almost always, snprintf is better than %s' % match.group(1))
+ 'Use xstrlcpy or snprintf instead of %s' % match.group(1))
+ match = Search(r'\b(STRNCAT|strncat)\b', line)
+ if match:
+ error(filename, linenum, 'runtime/printf', 4,
+ 'Use xstrlcat instead of %s' % match.group(1))
# Check for suspicious usage of "if" like
# } if (a == b) {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 6b14d21da7..dffbbe9df9 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -9748,60 +9748,54 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL) {
- partial_T *pt = (partial_T *)xcalloc(1, sizeof(partial_T));
+ partial_T *const pt = xcalloc(1, sizeof(*pt));
// result is a VAR_PARTIAL
- if (pt != NULL) {
- if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) {
- listitem_T *li;
+ if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) {
+ const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc);
+ const int lv_len = (list == NULL ? 0 : list->lv_len);
+
+ pt->pt_argc = arg_len + lv_len;
+ pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * pt->pt_argc);
+ if (pt->pt_argv == NULL) {
+ xfree(pt);
+ xfree(name);
+ return;
+ } else {
int i = 0;
- int arg_len = 0;
- int lv_len = 0;
-
- if (arg_pt != NULL) {
- arg_len = arg_pt->pt_argc;
+ for (; i < arg_len; i++) {
+ copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
}
- if (list != NULL) {
- lv_len = list->lv_len;
- }
- pt->pt_argc = arg_len + lv_len;
- pt->pt_argv = (typval_T *)xmalloc(sizeof(typval_T) * pt->pt_argc);
- if (pt->pt_argv == NULL) {
- xfree(pt);
- xfree(name);
- return;
- } else {
- for (i = 0; i < arg_len; i++) {
- copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
- }
- if (lv_len > 0) {
- for (li = list->lv_first; li != NULL; li = li->li_next) {
- copy_tv(&li->li_tv, &pt->pt_argv[i++]);
- }
+ if (lv_len > 0) {
+ for (listitem_T *li = list->lv_first;
+ li != NULL;
+ li = li->li_next) {
+ copy_tv(&li->li_tv, &pt->pt_argv[i++]);
}
}
}
+ }
- // For "function(dict.func, [], dict)" and "func" is a partial
- // use "dict". That is backwards compatible.
- if (dict_idx > 0) {
- // The dict is bound explicitly, pt_auto is false
- pt->pt_dict = argvars[dict_idx].vval.v_dict;
+ // For "function(dict.func, [], dict)" and "func" is a partial
+ // use "dict". That is backwards compatible.
+ if (dict_idx > 0) {
+ // The dict is bound explicitly, pt_auto is false
+ pt->pt_dict = argvars[dict_idx].vval.v_dict;
+ (pt->pt_dict->dv_refcount)++;
+ } else if (arg_pt != NULL) {
+ // If the dict was bound automatically the result is also
+ // bound automatically.
+ pt->pt_dict = arg_pt->pt_dict;
+ pt->pt_auto = arg_pt->pt_auto;
+ if (pt->pt_dict != NULL) {
(pt->pt_dict->dv_refcount)++;
- } else if (arg_pt != NULL) {
- // If the dict was bound automatically the result is also
- // bound automatically.
- pt->pt_dict = arg_pt->pt_dict;
- pt->pt_auto = arg_pt->pt_auto;
- if (pt->pt_dict != NULL) {
- (pt->pt_dict->dv_refcount)++;
- }
}
-
- pt->pt_refcount = 1;
- pt->pt_name = name;
- func_ref(pt->pt_name);
}
+
+ pt->pt_refcount = 1;
+ pt->pt_name = name;
+ func_ref(pt->pt_name);
+
rettv->v_type = VAR_PARTIAL;
rettv->vval.v_partial = pt;
} else {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 69eed33736..56919db024 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1408,32 +1408,30 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
}
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);
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);
}
}
}
@@ -4814,9 +4812,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.
@@ -4979,8 +4981,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.
@@ -4993,13 +4999,16 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname,
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);
@@ -5173,8 +5182,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.
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index 03cb504f17..73faac0a43 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -193,7 +193,6 @@ typedef struct ff_search_ctx_T {
static char_u e_pathtoolong[] = N_("E854: path too long for completion");
-
/*
* Initialization routine for vim_findfile().
*
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 13329d771b..d433afab3e 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -1882,6 +1882,7 @@ failed:
xfree(keep_msg);
keep_msg = NULL;
+ p = NULL;
msg_scrolled_ign = TRUE;
if (!read_stdin && !read_buffer) {
@@ -4981,8 +4982,8 @@ buf_check_timestamp (
set_vim_var_string(VV_WARNINGMSG, tbuf, -1);
if (can_reload) {
if (*mesg2 != NUL) {
- strncat(tbuf, "\n", tbuf_len);
- strncat(tbuf, mesg2, tbuf_len);
+ xstrlcat(tbuf, "\n", tbuf_len - 1);
+ xstrlcat(tbuf, mesg2, tbuf_len - 1);
}
if (do_dialog(VIM_WARNING, (char_u *) _("Warning"), (char_u *) tbuf,
(char_u *) _("&OK\n&Load File"), 1, NULL, true) == 2) {
@@ -4990,8 +4991,8 @@ buf_check_timestamp (
}
} else if (State > NORMAL_BUSY || (State & CMDLINE) || already_warned) {
if (*mesg2 != NUL) {
- strncat(tbuf, "; ", tbuf_len);
- strncat(tbuf, mesg2, tbuf_len);
+ xstrlcat(tbuf, "; ", tbuf_len - 1);
+ xstrlcat(tbuf, mesg2, tbuf_len - 1);
}
EMSG(tbuf);
retval = 2;
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index e3c84cb852..baa85c01f8 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -1215,6 +1215,7 @@ EXTERN char_u e_invalidreg[] INIT(= N_("E850: Invalid register name"));
EXTERN char_u e_dirnotf[] INIT(= N_(
"E919: Directory not found in '%s': \"%s\""));
EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
+EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long"));
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index cb0415a486..c2dc6231f1 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -1544,8 +1544,8 @@ static int prt_find_resource(char *name, struct prt_ps_resource_S *resource)
/* Look for named resource file in runtimepath */
STRCPY(buffer, "print");
add_pathsep((char *)buffer);
- vim_strcat(buffer, (char_u *)name, MAXPATHL);
- vim_strcat(buffer, (char_u *)".ps", MAXPATHL);
+ xstrlcat((char *)buffer, name, MAXPATHL);
+ xstrlcat((char *)buffer, ".ps", MAXPATHL);
resource->filename[0] = NUL;
retval = (do_in_runtimepath(buffer, 0, prt_resource_name, resource->filename)
&& resource->filename[0] != NUL);
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 92ead873ae..6408ac1664 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -389,6 +389,33 @@ size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t size)
return ret;
}
+/// xstrlcat - Appends string src to the end of dst.
+///
+/// Compatible with *BSD strlcat: Appends at most (dstsize - strlen(dst) - 1)
+/// characters. dst will be NUL-terminated.
+///
+/// Note: Replaces `vim_strcat`.
+///
+/// @param dst Where to copy the string to
+/// @param src Where to copy the string from
+/// @param dstsize Size of destination buffer, must be greater than 0
+/// @return strlen(src) + MIN(dstsize, strlen(initial dst)).
+/// If retval >= dstsize, truncation occurs.
+size_t xstrlcat(char *restrict dst, const char *restrict src, size_t dstsize)
+ FUNC_ATTR_NONNULL_ALL
+{
+ assert(dstsize > 0);
+ size_t srclen = strlen(src);
+ size_t dstlen = strlen(dst);
+ size_t ret = srclen + dstlen; // Total string length (excludes NUL)
+ if (srclen) {
+ size_t len = (ret >= dstsize) ? dstsize - 1 : ret;
+ memcpy(dst + dstlen, src, len - dstlen);
+ dst[len] = '\0';
+ }
+ return ret; // Does not include NUL.
+}
+
/// strdup() wrapper
///
/// @see {xmalloc}
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 749fa8a706..91dd042777 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -381,20 +381,17 @@ static int other_sourcing_name(void)
return FALSE;
}
-/*
- * Get the message about the source, as used for an error message.
- * Returns an allocated string with room for one more character.
- * Returns NULL when no message is to be given.
- */
+/// Get the message about the source, as used for an error message.
+/// Returns an allocated string with room for one more character.
+/// Returns NULL when no message is to be given.
static char_u *get_emsg_source(void)
{
- char_u *Buf, *p;
-
if (sourcing_name != NULL && other_sourcing_name()) {
- p = (char_u *)_("Error detected while processing %s:");
- Buf = xmalloc(STRLEN(sourcing_name) + STRLEN(p));
- sprintf((char *)Buf, (char *)p, sourcing_name);
- return Buf;
+ char_u *p = (char_u *)_("Error detected while processing %s:");
+ size_t len = STRLEN(sourcing_name) + STRLEN(p) + 1;
+ char_u *buf = xmalloc(len);
+ snprintf((char *)buf, len, (char *)p, sourcing_name);
+ return buf;
}
return NULL;
}
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index 71aa6e83e5..ba26381e23 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -2507,8 +2507,9 @@ void msgmore(long n)
vim_snprintf((char *)msg_buf, MSG_BUF_LEN,
_("%" PRId64 " fewer lines"), (int64_t)pn);
}
- if (got_int)
- vim_strcat(msg_buf, (char_u *)_(" (Interrupted)"), MSG_BUF_LEN);
+ if (got_int) {
+ xstrlcat((char *)msg_buf, _(" (Interrupted)"), MSG_BUF_LEN);
+ }
if (msg(msg_buf)) {
set_keep_msg(msg_buf, 0);
keep_msg_more = TRUE;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 227bfbe779..b17b4c584e 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1130,6 +1130,7 @@ static int normal_execute(VimState *state, int key)
start_selection();
unshift_special(&s->ca);
s->idx = find_command(s->ca.cmdchar);
+ assert(s->idx >= 0);
} else if ((nv_cmds[s->idx].cmd_flags & NV_SSS)
&& (mod_mask & MOD_MASK_SHIFT)) {
start_selection();
@@ -1517,10 +1518,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
coladvance(curwin->w_curswant);
}
cap->count0 = redo_VIsual_count;
- if (redo_VIsual_count != 0)
- cap->count1 = redo_VIsual_count;
- else
- cap->count1 = 1;
+ cap->count1 = (cap->count0 == 0 ? 1 : cap->count0);
} else if (VIsual_active) {
if (!gui_yank) {
/* Save the current VIsual area for '< and '> marks, and "gv" */
@@ -7727,16 +7725,22 @@ static void nv_put(cmdarg_T *cap)
savereg = copy_register(regname);
}
- /* Now delete the selected text. */
- cap->cmdchar = 'd';
- cap->nchar = NUL;
- cap->oap->regname = NUL;
- nv_operator(cap);
- do_pending_operator(cap, 0, false);
- empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
+ // To place the cursor correctly after a blockwise put, and to leave the
+ // text in the correct position when putting over a selection with
+ // 'virtualedit' and past the end of the line, we use the 'c' operator in
+ // do_put(), which requires the visual selection to still be active.
+ if (!VIsual_active || VIsual_mode == 'V' || regname != '.') {
+ // Now delete the selected text.
+ cap->cmdchar = 'd';
+ cap->nchar = NUL;
+ cap->oap->regname = NUL;
+ nv_operator(cap);
+ do_pending_operator(cap, 0, false);
+ empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
- /* delete PUT_LINE_BACKWARD; */
- cap->oap->regname = regname;
+ // delete PUT_LINE_BACKWARD;
+ cap->oap->regname = regname;
+ }
/* When deleted a linewise Visual area, put the register as
* lines to avoid it joined with the next line. When deletion was
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 10d6be85f8..9a01891483 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -2637,12 +2637,79 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
* special characters (newlines, etc.).
*/
if (regname == '.') {
- (void)stuff_inserted((dir == FORWARD ? (count == -1 ? 'o' : 'a') :
- (count == -1 ? 'O' : 'i')), count, FALSE);
- /* Putting the text is done later, so can't really move the cursor to
- * the next character. Use "l" to simulate it. */
- if ((flags & PUT_CURSEND) && gchar_cursor() != NUL)
- stuffcharReadbuff('l');
+ bool non_linewise_vis = (VIsual_active && VIsual_mode != 'V');
+
+ // PUT_LINE has special handling below which means we use 'i' to start.
+ char command_start_char = non_linewise_vis ? 'c' :
+ (flags & PUT_LINE ? 'i' : (dir == FORWARD ? 'a' : 'i'));
+
+ // To avoid 'autoindent' on linewise puts, create a new line with `:put _`.
+ if (flags & PUT_LINE) {
+ do_put('_', NULL, dir, 1, PUT_LINE);
+ }
+
+ // If given a count when putting linewise, we stuff the readbuf with the
+ // dot register 'count' times split by newlines.
+ if (flags & PUT_LINE) {
+ stuffcharReadbuff(command_start_char);
+ for (; count > 0; count--) {
+ (void)stuff_inserted(NUL, 1, count != 1);
+ if (count != 1) {
+ // To avoid 'autoindent' affecting the text, use Ctrl_U to remove any
+ // whitespace. Can't just insert Ctrl_U into readbuf1, this would go
+ // back to the previous line in the case of 'noautoindent' and
+ // 'backspace' includes "eol". So we insert a dummy space for Ctrl_U
+ // to consume.
+ stuffReadbuff((char_u *)"\n ");
+ stuffcharReadbuff(Ctrl_U);
+ }
+ }
+ } else {
+ (void)stuff_inserted(command_start_char, count, false);
+ }
+
+ // Putting the text is done later, so can't move the cursor to the next
+ // character. Simulate it with motion commands after the insert.
+ if (flags & PUT_CURSEND) {
+ if (flags & PUT_LINE) {
+ stuffReadbuff((char_u *)"j0");
+ } else {
+ // Avoid ringing the bell from attempting to move into the space after
+ // the current line. We can stuff the readbuffer with "l" if:
+ // 1) 'virtualedit' is "all" or "onemore"
+ // 2) We are not at the end of the line
+ // 3) We are not (one past the end of the line && on the last line)
+ // This allows a visual put over a selection one past the end of the
+ // line joining the current line with the one below.
+
+ // curwin->w_cursor.col marks the byte position of the cursor in the
+ // currunt line. It increases up to a max of
+ // STRLEN(ml_get(curwin->w_cursor.lnum)). With 'virtualedit' and the
+ // cursor past the end of the line, curwin->w_cursor.coladd is
+ // incremented instead of curwin->w_cursor.col.
+ char_u *cursor_pos = get_cursor_pos_ptr();
+ bool one_past_line = (*cursor_pos == NUL);
+ bool eol = false;
+ if (!one_past_line) {
+ eol = (*(cursor_pos + mb_ptr2len(cursor_pos)) == NUL);
+ }
+
+ bool ve_allows = (ve_flags == VE_ALL || ve_flags == VE_ONEMORE);
+ bool eof = curbuf->b_ml.ml_line_count == curwin->w_cursor.lnum
+ && one_past_line;
+ if (ve_allows || !(eol || eof)) {
+ stuffcharReadbuff('l');
+ }
+ }
+ } else if (flags & PUT_LINE) {
+ stuffReadbuff((char_u *)"g'[");
+ }
+
+ // So the 'u' command restores cursor position after ".p, save the cursor
+ // position now (though not saving any text).
+ if (command_start_char == 'a') {
+ u_save(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1);
+ }
return;
}
@@ -2831,14 +2898,12 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
else
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
- if (has_mbyte)
- /* move to start of next multi-byte character */
- curwin->w_cursor.col += (*mb_ptr2len)(get_cursor_pos_ptr());
- else if (c != TAB || ve_flags != VE_ALL)
- ++curwin->w_cursor.col;
- ++col;
- } else
+ // move to start of next multi-byte character
+ curwin->w_cursor.col += (*mb_ptr2len)(get_cursor_pos_ptr());
+ col++;
+ } else {
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
+ }
col += curwin->w_cursor.coladd;
if (ve_flags == VE_ALL
@@ -2892,8 +2957,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
bd.startspaces = incr - bd.endspaces;
--bd.textcol;
delcount = 1;
- if (has_mbyte)
- bd.textcol -= (*mb_head_off)(oldp, oldp + bd.textcol);
+ bd.textcol -= (*mb_head_off)(oldp, oldp + bd.textcol);
if (oldp[bd.textcol] != TAB) {
/* Only a Tab can be split into spaces. Other
* characters will have to be moved to after the
@@ -2975,21 +3039,13 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// if type is kMTCharWise, FORWARD is the same as BACKWARD on the next
// char
if (dir == FORWARD && gchar_cursor() != NUL) {
- if (has_mbyte) {
- int bytelen = (*mb_ptr2len)(get_cursor_pos_ptr());
-
- /* put it on the next of the multi-byte character. */
- col += bytelen;
- if (yanklen) {
- curwin->w_cursor.col += bytelen;
- curbuf->b_op_end.col += bytelen;
- }
- } else {
- ++col;
- if (yanklen) {
- ++curwin->w_cursor.col;
- ++curbuf->b_op_end.col;
- }
+ int bytelen = (*mb_ptr2len)(get_cursor_pos_ptr());
+
+ // put it on the next of the multi-byte character.
+ col += bytelen;
+ if (yanklen) {
+ curwin->w_cursor.col += bytelen;
+ curbuf->b_op_end.col += bytelen;
}
}
curbuf->b_op_start = curwin->w_cursor;
@@ -3027,7 +3083,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
if (VIsual_active)
lnum++;
- } while (VIsual_active && lnum <= curbuf->b_visual.vi_end.lnum);
+ } while (VIsual_active
+ && (lnum <= curbuf->b_visual.vi_end.lnum
+ || lnum <= curbuf->b_visual.vi_start.lnum));
if (VIsual_active) { /* reset lnum to the last visual line */
lnum--;
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 47d541c6a7..109b5c7175 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -226,25 +226,24 @@ char_u *expand_env_save_opt(char_u *src, bool one)
/// "~/" is also expanded, using $HOME. For Unix "~user/" is expanded.
/// Skips over "\ ", "\~" and "\$" (not for Win32 though).
/// If anything fails no expansion is done and dst equals src.
-/// @param src Input string e.g. "$HOME/vim.hlp"
-/// @param dst Where to put the result
-/// @param dstlen Maximum length of the result
+///
+/// @param src Input string e.g. "$HOME/vim.hlp"
+/// @param dst[out] Where to put the result
+/// @param dstlen Maximum length of the result
void expand_env(char_u *src, char_u *dst, int dstlen)
{
expand_env_esc(src, dst, dstlen, false, false, NULL);
}
/// Expand environment variable with path name and escaping.
-/// "~/" is also expanded, using $HOME. For Unix "~user/" is expanded.
-/// Skips over "\ ", "\~" and "\$" (not for Win32 though).
-/// If anything fails no expansion is done and dst equals src.
-/// prefix recognize the start of a new name, for '~' expansion.
-/// @param srcp Input string e.g. "$HOME/vim.hlp"
-/// @param dst Where to put the result
-/// @param dstlen Maximum length of the result
-/// @param esc Should we escape spaces in expanded variables?
-/// @param one Should we expand more than one '~'?
-/// @param prefix Common prefix for paths, can be NULL
+/// @see expand_env
+///
+/// @param srcp Input string e.g. "$HOME/vim.hlp"
+/// @param dst[out] Where to put the result
+/// @param dstlen Maximum length of the result
+/// @param esc Escape spaces in expanded variables
+/// @param one `srcp` is a single filename
+/// @param prefix Start again after this (can be NULL)
void expand_env_esc(char_u *restrict srcp,
char_u *restrict dst,
int dstlen,
diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h
index 5e164b54a5..14c210c69c 100644
--- a/src/nvim/os/os_defs.h
+++ b/src/nvim/os/os_defs.h
@@ -16,7 +16,7 @@
#define BASENAMELEN (NAME_MAX - 5)
// Use the system path length if it makes sense.
-#if defined(PATH_MAX) && (PATH_MAX > 1000)
+#if defined(PATH_MAX) && (PATH_MAX > 1024)
# define MAXPATHL PATH_MAX
#else
# define MAXPATHL 1024
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 3d1def8dd4..7e1183d5db 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -391,15 +391,22 @@ char *concat_fnames_realloc(char *fname1, const char *fname2, bool sep)
fname2, len2, sep);
}
-/*
- * Add a path separator to a file name, unless it already ends in a path
- * separator.
- */
-void add_pathsep(char *p)
+/// Adds a path separator to a filename, unless it already ends in one.
+///
+/// @return `true` if the path separator was added or already existed.
+/// `false` if the filename is too long.
+bool add_pathsep(char *p)
FUNC_ATTR_NONNULL_ALL
{
- if (*p != NUL && !after_pathsep(p, p + strlen(p)))
- strcat(p, PATHSEPSTR);
+ const size_t len = strlen(p);
+ if (*p != NUL && !after_pathsep(p, p + len)) {
+ const size_t pathsep_len = sizeof(PATHSEPSTR);
+ if (len > MAXPATHL - pathsep_len) {
+ return false;
+ }
+ memcpy(p + len, PATHSEPSTR, pathsep_len);
+ }
+ return true;
}
/// Get an allocated copy of the full path to a file.
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 19287ecffb..cebff5e8b7 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -2126,7 +2126,7 @@ static void qf_msg(qf_info_T *qi, int which, char *lead)
memset(buf + len, ' ', 34 - len);
buf[34] = NUL;
}
- vim_strcat(buf, (char_u *)title, IOSIZE);
+ xstrlcat((char *)buf, title, IOSIZE);
}
trunc_string(buf, buf, (int)Columns - 1, IOSIZE);
msg(buf);
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 8cf5976e8b..197b029591 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1986,7 +1986,7 @@ static ShaDaWriteResult shada_pack_encoded_entry(msgpack_packer *const packer,
entry.data.data.reg.contents =
xmemdup(entry.data.data.reg.contents,
(entry.data.data.reg.contents_size
- * sizeof(entry.data.data.reg.contents)));
+ * sizeof(entry.data.data.reg.contents[0])));
for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) {
if (i >= first_non_ascii) {
entry.data.data.reg.contents[i] = get_converted_string(
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index c1800a0639..5b4f23f30e 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -344,24 +344,6 @@ void del_trailing_spaces(char_u *ptr)
*q = NUL;
}
-/*
- * Like strcat(), but make sure the result fits in "tosize" bytes and is
- * always NUL terminated.
- */
-void vim_strcat(char_u *restrict to, const char_u *restrict from,
- size_t tosize)
- FUNC_ATTR_NONNULL_ALL
-{
- size_t tolen = STRLEN(to);
- size_t fromlen = STRLEN(from);
-
- if (tolen + fromlen + 1 > tosize) {
- memcpy(to + tolen, from, tosize - tolen - 1);
- to[tosize - 1] = NUL;
- } else
- STRCPY(to + tolen, from);
-}
-
#if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP))
/*
* Compare two strings, ignoring case, using current locale.
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index aded08faee..5bfc655645 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -6902,8 +6902,8 @@ static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg
for (i = 0; hl_attr_table[i] != 0; ++i) {
if (iarg & hl_attr_table[i]) {
if (buf[0] != NUL)
- vim_strcat(buf, (char_u *)",", 100);
- vim_strcat(buf, (char_u *)hl_name_table[i], 100);
+ xstrlcat((char *)buf, ",", 100);
+ xstrlcat((char *)buf, hl_name_table[i], 100);
iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
}
}
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 74187e07c0..342c68818d 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -844,7 +844,7 @@ static void fix_terminfo(TUIData *data)
}
if (STARTS_WITH(term, "xterm") || STARTS_WITH(term, "rxvt")) {
- unibi_set_if_empty(ut, unibi_cursor_normal, "\x1b[?12l\x1b[?25h");
+ unibi_set_if_empty(ut, unibi_cursor_normal, "\x1b[?25h");
unibi_set_if_empty(ut, unibi_cursor_invisible, "\x1b[?25l");
unibi_set_if_empty(ut, unibi_flash_screen, "\x1b[?5h$<100/>\x1b[?5l");
unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b(B\x1b[m");
@@ -877,9 +877,11 @@ static void fix_terminfo(TUIData *data)
unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB);
}
- if (os_getenv("NVIM_TUI_ENABLE_CURSOR_SHAPE") == NULL) {
+ const char * env_cusr_shape = os_getenv("NVIM_TUI_ENABLE_CURSOR_SHAPE");
+ if (env_cusr_shape && strncmp(env_cusr_shape, "0", 1) == 0) {
goto end;
}
+ bool cusr_blink = env_cusr_shape && strncmp(env_cusr_shape, "2", 1) == 0;
#define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq)
// Support changing cursor shape on some popular terminals.
@@ -891,22 +893,22 @@ static void fix_terminfo(TUIData *data)
// Konsole uses a proprietary escape code to set the cursor shape
// and does not support DECSCUSR.
data->unibi_ext.set_cursor_shape_bar = (int)unibi_add_ext_str(ut, NULL,
- TMUX_WRAP("\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07"));
+ TMUX_WRAP("\x1b]50;CursorShape=1\x07"));
data->unibi_ext.set_cursor_shape_ul = (int)unibi_add_ext_str(ut, NULL,
- TMUX_WRAP("\x1b]50;CursorShape=2;BlinkingCursorEnabled=1\x07"));
+ TMUX_WRAP("\x1b]50;CursorShape=2\x07"));
data->unibi_ext.set_cursor_shape_block = (int)unibi_add_ext_str(ut, NULL,
- TMUX_WRAP("\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07"));
+ TMUX_WRAP("\x1b]50;CursorShape=0\x07"));
} else if (!vte_version || atoi(vte_version) >= 3900) {
// Assume that the terminal supports DECSCUSR unless it is an
// old VTE based terminal. This should not get wrapped for tmux,
// which will handle it via its Ss/Se terminfo extension - usually
// according to its terminal-overrides.
data->unibi_ext.set_cursor_shape_bar =
- (int)unibi_add_ext_str(ut, NULL, "\x1b[5 q");
+ (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[5 q" : "\x1b[6 q");
data->unibi_ext.set_cursor_shape_ul =
- (int)unibi_add_ext_str(ut, NULL, "\x1b[3 q");
+ (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[3 q" : "\x1b[4 q");
data->unibi_ext.set_cursor_shape_block =
- (int)unibi_add_ext_str(ut, NULL, "\x1b[2 q");
+ (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[1 q" : "\x1b[2 q");
}
end:
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 3d81788a7e..9cf509ca23 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -366,7 +366,7 @@ static int included_patches[] = {
2077,
// 2076,
2075,
- // 2074,
+ 2074,
// 2073 NA
// 2072,
2071,
@@ -526,7 +526,7 @@ static int included_patches[] = {
// 1917 NA
// 1916 NA
// 1915 NA
- // 1914,
+ // 1914 NA
1913,
1912,
// 1911 NA
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 8271abda8d..458d23fcad 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -269,6 +269,7 @@ enum {
#define STRCAT(d, s) strcat((char *)(d), (char *)(s))
#define STRNCAT(d, s, n) strncat((char *)(d), (char *)(s), (size_t)(n))
+#define STRLCAT(d, s, n) xstrlcat((char *)(d), (char *)(s), (size_t)(n))
# define vim_strpbrk(s, cs) (char_u *)strpbrk((char *)(s), (char *)(cs))