patch 9.2.0368: too many strlen() calls when adding strings to dicts

Commit: 
https://github.com/vim/vim/commit/c13232699db413e735f30b5649c78a7f38a9a069
Author: John Marriott <[email protected]>
Date:   Sun Apr 19 20:58:33 2026 +0000

    patch 9.2.0368: too many strlen() calls when adding strings to dicts
    
    Problem:  too many strlen() calls when adding strings to dicts
    Solution: Refactor code to use string_T, use dict_add_string_len()
              instead of dict_add_string() (John Marriott)
    
    Additionally:
    - In textprop.c, in function prop_fill_dict() use a string_T to store
      local variable text_align.
    - In popupwin.c, use a string_T to store struct member pp_name in struct
      poppos_entry_T.
    - In mark.c, refactor function add_mark() to pass in the length of
      argument mname.
    - In insexpand.c:
      ->Use a string_T to store the elements of static array
        ctrl_x_mode_names.
      ->Refactor function trigger_complete_done_event():
      ->->change type of argument char_u *word to string_T *word.
      ->->make one access of array ctrl_x_mode_names instead of two.
      ->Refactor function ins_compl_mode() to accept a string_T to return the
        resulting string.
    - In fileio.c:
      ->Refactor function getftypewfd() to accept a string_T to return the
        resulting string.
      ->In function create_readdirex_item() use a string_T to store local
        variable q.
    - In cmdexpand.c, store global cmdline_orig as a string_T.
    - In autocmd.c, in function f_autocmd_get() use a string_T to store local
      variables event_name and group_name. Measure their lengths once when
      they are assigned so they are not remeasured on each call to
      dict_add_string() in the subsequent for loop.
    - In channel.c, in function channel_part_info() drop local variable status
      and use s instead. Make s a string_T.
    
    closes: #19999
    
    Signed-off-by: John Marriott <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/src/autocmd.c b/src/autocmd.c
index b27297f79..99d6b3c92 100644
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -3343,7 +3343,7 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv)
     AutoCmd    *ac;
     list_T     *event_list;
     dict_T     *event_dict;
-    char_u     *event_name = NULL;
+    string_T   event_name = {NULL, 0};
     char_u     *pat = NULL;
     char_u     *name = NULL;
     int                group = AUGROUP_ALL;
@@ -3425,12 +3425,13 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv)
        if (event_arg != NUM_EVENTS && event != event_arg)
            continue;
 
-       event_name = event_nr2name(event);
+       event_name.string = event_nr2name(event);
+       event_name.length = STRLEN(event_name.string);
 
        // iterate through all the patterns for this autocmd event
        FOR_ALL_AUTOCMD_PATTERNS(event, ap)
        {
-           char_u      *group_name;
+           string_T    group_name;
 
            if (ap->pat == NULL)                // pattern has been removed
                continue;
@@ -3441,7 +3442,14 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv)
            if (pat != NULL && STRCMP(pat, ap->pat) != 0)
                continue;
 
-           group_name = get_augroup_name(NULL, ap->group);
+           group_name.string = get_augroup_name(NULL, ap->group);
+           if (group_name.string == NULL)
+           {
+               group_name.string = (char_u *)"";
+               group_name.length = 0;
+           }
+           else
+               group_name.length = STRLEN(group_name.string);
 
            // iterate through all the commands for this pattern and add one
            // item for each cmd.
@@ -3455,10 +3463,10 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv)
                    return;
                }
 
-               if (dict_add_string(event_dict, "event", event_name) == FAIL
-                       || dict_add_string(event_dict, "group",
-                                       group_name == NULL ? (char_u *)""
-                                                         : group_name) == FAIL
+               if (dict_add_string_len(event_dict, "event",
+                       event_name.string, (int)event_name.length) == FAIL
+                       || dict_add_string_len(event_dict, "group",
+                           group_name.string, (int)group_name.length) == FAIL
                        || (ap->buflocal_nr != 0
                                && (dict_add_number(event_dict, "bufnr",
                                                    ap->buflocal_nr) == FAIL))
diff --git a/src/channel.c b/src/channel.c
index d58c69708..56878458e 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -3668,46 +3668,98 @@ channel_part_info(channel_T *channel, dict_T *dict, 
char *name, ch_part_T part)
     chanpart_T *chanpart = &channel->ch_part[part];
     char       namebuf[20];  // longest is "sock_timeout"
     size_t     tail;
-    char       *status;
-    char       *s = "";
+    string_T   s;
 
     vim_strncpy((char_u *)namebuf, (char_u *)name, 4);
-    STRCAT(namebuf, "_");
     tail = STRLEN(namebuf);
+    STRCPY(namebuf + tail, "_");
+    tail += STRLEN_LITERAL("_");
 
     STRCPY(namebuf + tail, "status");
     if (chanpart->ch_fd != INVALID_FD)
-       status = "open";
+    {
+       s.string = (char_u *)"open";
+       s.length = STRLEN_LITERAL("open");
+    }
     else if (channel_has_readahead(channel, part))
-       status = "buffered";
+    {
+       s.string = (char_u *)"buffered";
+       s.length = STRLEN_LITERAL("buffered");
+    }
     else
-       status = "closed";
-    dict_add_string(dict, namebuf, (char_u *)status);
+    {
+       s.string = (char_u *)"closed";
+       s.length = STRLEN_LITERAL("closed");
+    }
+    dict_add_string_len(dict, namebuf, s.string, (int)s.length);
 
     STRCPY(namebuf + tail, "mode");
     switch (chanpart->ch_mode)
     {
-       case CH_MODE_NL: s = "NL"; break;
-       case CH_MODE_RAW: s = "RAW"; break;
-       case CH_MODE_JSON: s = "JSON"; break;
-       case CH_MODE_JS: s = "JS"; break;
-       case CH_MODE_LSP: s = "LSP"; break;
-       case CH_MODE_DAP: s = "DAP"; break;
+       case CH_MODE_NL:
+           s.string = (char_u *)"NL";
+           s.length = STRLEN_LITERAL("NL");
+           break;
+       case CH_MODE_RAW:
+           s.string = (char_u *)"RAW";
+           s.length = STRLEN_LITERAL("RAW");
+           break;
+       case CH_MODE_JSON:
+           s.string = (char_u *)"JSON";
+           s.length = STRLEN_LITERAL("JSON");
+           break;
+       case CH_MODE_JS:
+           s.string = (char_u *)"JS";
+           s.length = STRLEN_LITERAL("JS");
+           break;
+       case CH_MODE_LSP:
+           s.string = (char_u *)"LSP";
+           s.length = STRLEN_LITERAL("LSP");
+           break;
+       case CH_MODE_DAP:
+           s.string = (char_u *)"DAP";
+           s.length = STRLEN_LITERAL("DAP");
+           break;
+       default:
+           s.string = (char_u *)"";
+           s.length = 0;
+           break;
     }
-    dict_add_string(dict, namebuf, (char_u *)s);
+    dict_add_string_len(dict, namebuf, s.string, (int)s.length);
 
     STRCPY(namebuf + tail, "io");
     if (part == PART_SOCK)
-       s = "socket";
+    {
+       s.string = (char_u *)"socket";
+       s.length = STRLEN_LITERAL("socket");
+    }
     else switch (chanpart->ch_io)
     {
-       case JIO_NULL: s = "null"; break;
-       case JIO_PIPE: s = "pipe"; break;
-       case JIO_FILE: s = "file"; break;
-       case JIO_BUFFER: s = "buffer"; break;
-       case JIO_OUT: s = "out"; break;
+       case JIO_NULL:
+           s.string = (char_u *)"null";
+           s.length = STRLEN_LITERAL("null");
+           break;
+       case JIO_PIPE:
+           s.string = (char_u *)"pipe";
+           s.length = STRLEN_LITERAL("pipe");
+           break;
+       case JIO_FILE:
+           s.string = (char_u *)"file";
+           s.length = STRLEN_LITERAL("file");
+           break;
+       case JIO_BUFFER:
+           s.string = (char_u *)"buffer";
+           s.length = STRLEN_LITERAL("buffer");
+           break;
+       case JIO_OUT:
+           s.string = (char_u *)"out";
+           s.length = STRLEN_LITERAL("out");
+           break;
+       default:
+           s.string = (char_u *)"";
+           s.length = 0;
     }
-    dict_add_string(dict, namebuf, (char_u *)s);
+    dict_add_string_len(dict, namebuf, s.string, (int)s.length);
 
     STRCPY(namebuf + tail, "timeout");
     dict_add_number(dict, namebuf, chanpart->ch_timeout);
@@ -5066,7 +5118,7 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int 
eval)
                id = di->di_tv.vval.v_number;
        }
        if (ch_mode == CH_MODE_LSP && !dict_has_key(d, "jsonrpc"))
-           dict_add_string(d, "jsonrpc", (char_u *)"2.0");
+           dict_add_string_len(d, "jsonrpc", (char_u *)"2.0", 
STRLEN_LITERAL("2.0"));
        text = json_encode_lsp_msg(&argvars[1]);
     }
     else
diff --git a/src/cmdexpand.c b/src/cmdexpand.c
index 63757179a..befc18c79 100644
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -36,7 +36,7 @@ static int compl_match_arraysize;
 static int compl_startcol;
 static int compl_selected;
 // cmdline before expansion
-static char_u *cmdline_orig = NULL;
+static string_T cmdline_orig = {NULL, 0};
 
 #define SHOW_MATCH(m) (showtail ? showmatches_gettail(matches[m]) : matches[m])
 
@@ -338,8 +338,12 @@ nextwild(
     // Save cmdline before inserting selected item
     if (!wild_navigate && ccline->cmdbuff != NULL)
     {
-       vim_free(cmdline_orig);
-       cmdline_orig = vim_strnsave(ccline->cmdbuff, ccline->cmdlen);
+       vim_free(cmdline_orig.string);
+       cmdline_orig.string = vim_strnsave(ccline->cmdbuff, ccline->cmdlen);
+       if (cmdline_orig.string == NULL)
+           cmdline_orig.length = 0;
+       else
+           cmdline_orig.length = ccline->cmdlen;
     }
 
     if (p != NULL && !got_int && !(options & WILD_NOSELECT))
@@ -1188,7 +1192,7 @@ ExpandCleanup(expand_T *xp)
     void
 clear_cmdline_orig(void)
 {
-    VIM_CLEAR(cmdline_orig);
+    VIM_CLEAR_STRING(cmdline_orig);
 }
 
 /*
@@ -4715,7 +4719,8 @@ f_cmdcomplete_info(typval_T *argvars UNUSED, typval_T 
*rettv)
            || ccline->xpc == NULL || ccline->xpc->xp_files == NULL)
        return;
     retdict = rettv->vval.v_dict;
-    ret = dict_add_string(retdict, "cmdline_orig", cmdline_orig);
+    ret = dict_add_string_len(retdict, "cmdline_orig",
+       cmdline_orig.string, (int)cmdline_orig.length);
     if (ret == OK)
        ret = dict_add_number(retdict, "pum_visible", pum_visible());
     if (ret == OK)
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 04774128d..e03dc9b20 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -10416,6 +10416,7 @@ f_getreginfo(typval_T *argvars, typval_T *rettv)
 {
     int                regname;
     char_u     buf[NUMBUFLEN + 2];
+    size_t     buflen;
     long       reglen = 0;
     dict_T     *dict;
     list_T     *list;
@@ -10439,23 +10440,34 @@ f_getreginfo(typval_T *argvars, typval_T *rettv)
        return;
     (void)dict_add_list(dict, "regcontents", list);
 
-    buf[0] = NUL;
-    buf[1] = NUL;
     switch (get_reg_type(regname, &reglen))
     {
-       case MLINE: buf[0] = 'V'; break;
-       case MCHAR: buf[0] = 'v'; break;
+       case MLINE:
+           buf[0] = 'V';
+           buf[1] = NUL;
+           buflen = 1;
+           break;
+       case MCHAR:
+           buf[0] = 'v';
+           buf[1] = NUL;
+           buflen = 1;
+           break;
        case MBLOCK:
-                   vim_snprintf((char *)buf, sizeof(buf), "%c%ld", Ctrl_V,
-                           reglen + 1);
-                   break;
+           buflen = vim_snprintf_safelen((char *)buf, sizeof(buf),
+               "%c%ld", Ctrl_V, reglen + 1);
+           break;
+       default:
+           buf[0] = NUL;
+           buflen = 0;
+           break;
     }
-    (void)dict_add_string(dict, (char *)"regtype", buf);
+    (void)dict_add_string_len(dict, (char *)"regtype", buf, (int)buflen);
 
     buf[0] = get_register_name(get_unname_register());
     buf[1] = NUL;
+    buflen = (buf[0] == NUL) ? 0 : 1;
     if (regname == '"')
-       (void)dict_add_string(dict, (char *)"points_to", buf);
+       (void)dict_add_string_len(dict, (char *)"points_to", buf, (int)buflen);
     else
     {
        dictitem_T      *item = dictitem_alloc((char_u *)"isunnamed");
@@ -10473,11 +10485,11 @@ f_getreginfo(typval_T *argvars, typval_T *rettv)
     static void
 return_register(int regname, typval_T *rettv)
 {
-    char_u buf[2] = {0, 0};
+    char_u buf[2] = {NUL, NUL};
 
     buf[0] = (char_u)regname;
     rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = vim_strsave(buf);
+    rettv->vval.v_string = vim_strnsave(buf, (buf[0] == NUL) ? 0 : 1);
 }
 
 /*
diff --git a/src/fileio.c b/src/fileio.c
index 1906a4316..b023e5a0d 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -4811,8 +4811,8 @@ getfpermwfd(WIN32_FIND_DATAW *wfd, char_u *perm)
     return getfpermst(&st, perm);
 }
 
-    static char_u *
-getftypewfd(WIN32_FIND_DATAW *wfd)
+    static void
+getftypewfd(WIN32_FIND_DATAW *wfd, string_T *ret)
 {
     DWORD flag = wfd->dwFileAttributes;
     DWORD tag = wfd->dwReserved0;
@@ -4820,20 +4820,40 @@ getftypewfd(WIN32_FIND_DATAW *wfd)
     if (flag & FILE_ATTRIBUTE_REPARSE_POINT)
     {
        if (tag == IO_REPARSE_TAG_MOUNT_POINT)
-           return (char_u*)"junction";
+       {
+           ret->string = (char_u *)"junction";
+           ret->length = STRLEN_LITERAL("junction");
+           return;
+       }
        if (tag == IO_REPARSE_TAG_SYMLINK)
        {
            if (flag & FILE_ATTRIBUTE_DIRECTORY)
-               return (char_u*)"linkd";
+           {
+               ret->string = (char_u *)"linkd";
+               ret->length = STRLEN_LITERAL("linkd");
+               return;
+           }
 
-           return (char_u*)"link";
+           ret->string = (char_u *)"link";
+           ret->length = STRLEN_LITERAL("link");
+           return;
        }
-       return (char_u*)"reparse";      // unknown reparse point type
+
+       // unknown reparse point type
+       ret->string = (char_u *)"reparse";
+       ret->length = STRLEN_LITERAL("reparse");
+       return;
     }
+
     if (flag & FILE_ATTRIBUTE_DIRECTORY)
-       return (char_u*)"dir";
+    {
+       ret->string = (char_u *)"dir";
+       ret->length = STRLEN_LITERAL("dir");
+       return;
+    }
 
-    return (char_u*)"file";
+    ret->string = (char_u *)"file";
+    ret->length = STRLEN_LITERAL("file");
 }
 
     static dict_T *
@@ -4843,6 +4863,7 @@ create_readdirex_item(WIN32_FIND_DATAW *wfd)
     char_u     *p;
     varnumber_T        size, time;
     char_u     permbuf[] = "---------";
+    string_T   s;
 
     item = dict_alloc();
     if (item == NULL)
@@ -4870,14 +4891,15 @@ create_readdirex_item(WIN32_FIND_DATAW *wfd)
     if (dict_add_number(item, "time", time) == FAIL)
        goto theend;
 
-    if (dict_add_string(item, "type", getftypewfd(wfd)) == FAIL)
+    getftypewfd(wfd, &s);
+    if (dict_add_string_len(item, "type", s.string, (int)s.length) == FAIL)
        goto theend;
     if (dict_add_string(item, "perm", getfpermwfd(wfd, permbuf)) == FAIL)
        goto theend;
 
-    if (dict_add_string(item, "user", (char_u*)"") == FAIL)
+    if (dict_add_string_len(item, "user", (char_u *)"", 0) == FAIL)
        goto theend;
-    if (dict_add_string(item, "group", (char_u*)"") == FAIL)
+    if (dict_add_string_len(item, "group", (char_u *)"", 0) == FAIL)
        goto theend;
 
     return item;
@@ -4893,11 +4915,12 @@ create_readdirex_item(char_u *path, char_u *name)
     dict_T     *item;
     char       *p;
     size_t     pathlen, len;
+    size_t     namelen;
     stat_T     st;
     int                ret, link = FALSE;
     varnumber_T        size;
     char_u     permbuf[] = "---------";
-    char_u     *q = NULL;
+    string_T   q = {NULL, 0};
     struct passwd *pw;
     struct group  *gr;
 
@@ -4907,7 +4930,8 @@ create_readdirex_item(char_u *path, char_u *name)
     item->dv_refcount++;
 
     pathlen = STRLEN(path);
-    len = pathlen + 1 + STRLEN(name) + 1;
+    namelen = STRLEN(name);
+    len = pathlen + 1 + namelen + 1;
     p = alloc(len);
     if (p == NULL)
        goto theend;
@@ -4921,11 +4945,14 @@ create_readdirex_item(char_u *path, char_u *name)
        link = TRUE;
        ret = mch_stat(p, &st);
        if (ret < 0)
-           q = (char_u*)"link";
+       {
+           q.string = (char_u *)"link";
+           q.length = STRLEN_LITERAL("link");
+       }
     }
     vim_free(p);
 
-    if (dict_add_string(item, "name", name) == FAIL)
+    if (dict_add_string_len(item, "name", name, (int)namelen) == FAIL)
        goto theend;
 
     if (ret >= 0)
@@ -4944,32 +4971,53 @@ create_readdirex_item(char_u *path, char_u *name)
        if (link)
        {
            if (S_ISDIR(st.st_mode))
-               q = (char_u*)"linkd";
+           {
+               q.string = (char_u *)"linkd";
+               q.length = STRLEN_LITERAL("linkd");
+           }
            else
-               q = (char_u*)"link";
+           {
+               q.string = (char_u *)"link";
+               q.length = STRLEN_LITERAL("link");
+           }
        }
        else
-           q = getftypest(&st);
-       if (dict_add_string(item, "type", q) == FAIL)
+       {
+           q.string = getftypest(&st);
+           q.length = STRLEN(q.string);
+       }
+       if (dict_add_string_len(item, "type", q.string, (int)q.length) == FAIL)
            goto theend;
        if (dict_add_string(item, "perm", getfpermst(&st, permbuf)) == FAIL)
            goto theend;
 
        pw = getpwuid(st.st_uid);
        if (pw == NULL)
-           q = (char_u*)"";
+       {
+           q.string = (char_u *)"";
+           q.length = 0;
+       }
        else
-           q = (char_u*)pw->pw_name;
-       if (dict_add_string(item, "user", q) == FAIL)
+       {
+           q.string = (char_u *)pw->pw_name;
+           q.length = STRLEN(q.string);
+       }
+       if (dict_add_string_len(item, "user", q.string, (int)q.length) == FAIL)
            goto theend;
 #  if !defined(VMS) || (defined(VMS) && defined(HAVE_XOS_R_H))
        gr = getgrgid(st.st_gid);
        if (gr == NULL)
-           q = (char_u*)"";
+       {
+           q.string = (char_u *)"";
+           q.length = 0;
+       }
        else
-           q = (char_u*)gr->gr_name;
+       {
+           q.string = (char_u *)gr->gr_name;
+           q.length = STRLEN(q.string);
+       }
 #  endif
-       if (dict_add_string(item, "group", q) == FAIL)
+       if (dict_add_string_len(item, "group", q.string, (int)q.length) == FAIL)
            goto theend;
     }
     else
@@ -4978,13 +5026,21 @@ create_readdirex_item(char_u *path, char_u *name)
            goto theend;
        if (dict_add_number(item, "time", -1) == FAIL)
            goto theend;
-       if (dict_add_string(item, "type", q == NULL ? (char_u*)"" : q) == FAIL)
-           goto theend;
-       if (dict_add_string(item, "perm", (char_u*)"") == FAIL)
+       if (q.string == NULL)
+       {
+           if (dict_add_string_len(item, "type", (char_u *)"", 0) == FAIL)
+               goto theend;
+       }
+       else
+       {
+           if (dict_add_string_len(item, "type", q.string, (int)q.length) == 
FAIL)
+               goto theend;
+       }
+       if (dict_add_string_len(item, "perm", (char_u *)"", 0) == FAIL)
            goto theend;
-       if (dict_add_string(item, "user", (char_u*)"") == FAIL)
+       if (dict_add_string_len(item, "user", (char_u *)"", 0) == FAIL)
            goto theend;
-       if (dict_add_string(item, "group", (char_u*)"") == FAIL)
+       if (dict_add_string_len(item, "group", (char_u *)"", 0) == FAIL)
            goto theend;
     }
     return item;
diff --git a/src/insexpand.c b/src/insexpand.c
index eb9420a19..72c323777 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -67,27 +67,30 @@ static char *ctrl_x_msgs[] =
 };
 
 #if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
-static char *ctrl_x_mode_names[] = {
-    "keyword",
-    "ctrl_x",
-    "scroll",
-    "whole_line",
-    "files",
-    "tags",
-    "path_patterns",
-    "path_defines",
-    "unknown",             // CTRL_X_FINISHED
-    "dictionary",
-    "thesaurus",
-    "cmdline",
-    "function",
-    "omni",
-    "spell",
-    NULL,                  // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs"
-    "eval",
-    "cmdline",
-    "register",
+# define STRING_INIT(s) \
+    {(char_u *)(s), STRLEN_LITERAL(s)}
+static string_T ctrl_x_mode_names[] = {
+    STRING_INIT("keyword"),
+    STRING_INIT("ctrl_x"),
+    STRING_INIT("scroll"),
+    STRING_INIT("whole_line"),
+    STRING_INIT("files"),
+    STRING_INIT("tags"),
+    STRING_INIT("path_patterns"),
+    STRING_INIT("path_defines"),
+    STRING_INIT("unknown"),                // CTRL_X_FINISHED
+    STRING_INIT("dictionary"),
+    STRING_INIT("thesaurus"),
+    STRING_INIT("cmdline"),
+    STRING_INIT("function"),
+    STRING_INIT("omni"),
+    STRING_INIT("spell"),
+    {NULL, 0},             // CTRL_X_LOCAL_MSG is only used in "ctrl_x_msgs"
+    STRING_INIT("eval"),
+    STRING_INIT("cmdline"),
+    STRING_INIT("register"),
 };
+# undef STRING_INIT
 #endif
 
 /*
@@ -1386,13 +1389,13 @@ ins_compl_dict_alloc(compl_T *match)
     if (dict == NULL)
        return NULL;
 
-    dict_add_string(dict, "word", match->cp_str.string);
+    dict_add_string_len(dict, "word", match->cp_str.string, 
(int)match->cp_str.length);
     dict_add_string(dict, "abbr", match->cp_text[CPT_ABBR]);
     dict_add_string(dict, "menu", match->cp_text[CPT_MENU]);
     dict_add_string(dict, "kind", match->cp_text[CPT_KIND]);
     dict_add_string(dict, "info", match->cp_text[CPT_INFO]);
     if (match->cp_user_data.v_type == VAR_UNKNOWN)
-       dict_add_string(dict, "user_data", (char_u *)"");
+       dict_add_string_len(dict, "user_data", (char_u *)"", 0);
     else
        dict_add_tv(dict, "user_data", &match->cp_user_data);
 
@@ -2888,21 +2891,21 @@ set_ctrl_x_mode(int c)
  * Trigger CompleteDone event and adds relevant information to v:event
  */
     static void
-trigger_complete_done_event(int mode UNUSED, char_u *word UNUSED)
+trigger_complete_done_event(int mode UNUSED, string_T *word UNUSED)
 {
 #if defined(FEAT_EVAL)
     save_v_event_T     save_v_event;
     dict_T             *v_event = get_v_event(&save_v_event);
-    char_u             *mode_str = NULL;
+    string_T           *mode_str;
 
-    mode = mode & ~CTRL_X_WANT_IDENT;
-    if (ctrl_x_mode_names[mode])
-       mode_str = (char_u *)ctrl_x_mode_names[mode];
+    if (word == NULL || word->string == NULL)
+       (void)dict_add_string_len(v_event, "complete_word", (char_u *)"", 0);
+    else
+       (void)dict_add_string_len(v_event, "complete_word", word->string, 
(int)word->length);
 
-    (void)dict_add_string(v_event, "complete_word",
-                               word == NULL ? (char_u *)"" : word);
-    (void)dict_add_string(v_event, "complete_type",
-                               mode_str != NULL ? mode_str : (char_u *)"");
+    mode_str = &ctrl_x_mode_names[mode & ~CTRL_X_WANT_IDENT];
+    (void)dict_add_string_len(v_event, "complete_type",
+       (mode_str->string == NULL) ? (char_u *)"" : mode_str->string, 
(int)mode_str->length);
 
     dict_set_items_ro(v_event);
 #endif
@@ -2920,7 +2923,7 @@ trigger_complete_done_event(int mode UNUSED, char_u *word 
UNUSED)
 ins_compl_stop(int c, int prev_mode, int retval)
 {
     int                want_cindent;
-    char_u     *word = NULL;
+    string_T   word = {NULL, 0};
 
     // Remove pre-inserted text when present.
     if (ins_compl_preinsert_effect() && ins_compl_win_active(curwin))
@@ -2979,7 +2982,12 @@ ins_compl_stop(int c, int prev_mode, int retval)
                    && (c == CAR || c == K_KENTER || c == NL)))
            && pum_visible())
     {
-       word = vim_strsave(compl_shown_match->cp_str.string);
+       word.string = vim_strnsave(compl_shown_match->cp_str.string,
+           compl_shown_match->cp_str.length);
+       if (word.string == NULL)
+           word.length = 0;
+       else
+           word.length = compl_shown_match->cp_str.length;
        retval = TRUE;
     }
 
@@ -3045,8 +3053,8 @@ ins_compl_stop(int c, int prev_mode, int retval)
        do_c_expr_indent();
     // Trigger the CompleteDone event to give scripts a chance to act
     // upon the end of completion.
-    trigger_complete_done_event(prev_mode, word);
-    vim_free(word);
+    trigger_complete_done_event(prev_mode, &word);
+    vim_free(word.string);
 
     return retval;
 }
@@ -3956,13 +3964,22 @@ f_complete_check(typval_T *argvars UNUSED, typval_T 
*rettv)
 /*
  * Return Insert completion mode name string
  */
-    static char_u *
-ins_compl_mode(void)
+    static void
+ins_compl_mode(string_T *ret)
 {
     if (ctrl_x_mode_not_defined_yet() || ctrl_x_mode_scroll() || compl_started)
-       return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT];
+    {
+       string_T    *mode;
 
-    return (char_u *)"";
+       mode = &ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT];
+       ret->string = (mode->string == NULL) ? (char_u *)"" : mode->string;
+       ret->length = mode->length;
+    }
+    else
+    {
+       ret->string = (char_u *)"";
+       ret->length = 0;
+    }
 }
 
 /*
@@ -4025,7 +4042,7 @@ ins_compl_update_sequence_numbers(void)
     static void
 fill_complete_info_dict(dict_T *di, compl_T *match, int add_match)
 {
-    dict_add_string(di, "word", match->cp_str.string);
+    dict_add_string_len(di, "word", match->cp_str.string, 
(int)match->cp_str.length);
     dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]);
     dict_add_string(di, "menu", match->cp_text[CPT_MENU]);
     dict_add_string(di, "kind", match->cp_text[CPT_KIND]);
@@ -4034,7 +4051,7 @@ fill_complete_info_dict(dict_T *di, compl_T *match, int 
add_match)
        dict_add_bool(di, "match", match->cp_in_match_array);
     if (match->cp_user_data.v_type == VAR_UNKNOWN)
        // Add an empty string for backwards compatibility
-       dict_add_string(di, "user_data", (char_u *)"");
+       dict_add_string_len(di, "user_data", (char_u *)"", 0);
     else
        dict_add_tv(di, "user_data", &match->cp_user_data);
 }
@@ -4085,7 +4102,12 @@ get_complete_info(list_T *what_list, dict_T *retdict)
     }
 
     if (ret == OK && (what_flag & CI_WHAT_MODE))
-       ret = dict_add_string(retdict, "mode", ins_compl_mode());
+    {
+       string_T    mode;
+
+       ins_compl_mode(&mode);
+       ret = dict_add_string_len(retdict, "mode", mode.string, 
(int)mode.length);
+    }
 
     if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE))
        ret = dict_add_number(retdict, "pum_visible", pum_visible());
diff --git a/src/mark.c b/src/mark.c
index 7cd4f31bc..eb77d8af9 100644
--- a/src/mark.c
+++ b/src/mark.c
@@ -1454,7 +1454,7 @@ get_namedfm(void)
  * Add information about mark 'mname' to list 'l'
  */
     static int
-add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, char_u *fname)
+add_mark(list_T *l, char_u *mname, size_t mnamelen, pos_T *pos, int bufnr, 
char_u *fname)
 {
     dict_T     *d;
     list_T     *lpos;
@@ -1481,7 +1481,7 @@ add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, 
char_u *fname)
     list_append_number(lpos, pos->col < MAXCOL ? pos->col + 1 : MAXCOL);
     list_append_number(lpos, pos->coladd);
 
-    if (dict_add_string(d, "mark", mname) == FAIL
+    if (dict_add_string_len(d, "mark", mname, (int)mnamelen) == FAIL
            || dict_add_list(d, "pos", lpos) == FAIL
            || (fname != NULL && dict_add_string(d, "file", fname) == FAIL))
     {
@@ -1500,25 +1500,26 @@ add_mark(list_T *l, char_u *mname, pos_T *pos, int 
bufnr, char_u *fname)
 get_buf_local_marks(buf_T *buf, list_T *l)
 {
     char_u     mname[3] = "' ";
+    size_t     mnamelen = STRLEN_LITERAL("' ");
     int                i;
 
     // Marks 'a' to 'z'
     for (i = 0; i < NMARKS; ++i)
     {
        mname[1] = 'a' + i;
-       add_mark(l, mname, &buf->b_namedm[i], buf->b_fnum, NULL);
+       add_mark(l, mname, mnamelen, &buf->b_namedm[i], buf->b_fnum, NULL);
     }
 
     // Mark '' is a window local mark and not a buffer local mark
-    add_mark(l, (char_u *)"''", &curwin->w_pcmark, curbuf->b_fnum, NULL);
-
-    add_mark(l, (char_u *)"'\"", &buf->b_last_cursor, buf->b_fnum, NULL);
-    add_mark(l, (char_u *)"'[", &buf->b_op_start, buf->b_fnum, NULL);
-    add_mark(l, (char_u *)"']", &buf->b_op_end, buf->b_fnum, NULL);
-    add_mark(l, (char_u *)"'^", &buf->b_last_insert, buf->b_fnum, NULL);
-    add_mark(l, (char_u *)"'.", &buf->b_last_change, buf->b_fnum, NULL);
-    add_mark(l, (char_u *)"'<", &buf->b_visual.vi_start, buf->b_fnum, NULL);
-    add_mark(l, (char_u *)"'>", &buf->b_visual.vi_end, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"''", STRLEN_LITERAL("''"), &curwin->w_pcmark, 
curbuf->b_fnum, NULL);
+
+    add_mark(l, (char_u *)"'\"", STRLEN_LITERAL("'\""), &buf->b_last_cursor, 
buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'[", STRLEN_LITERAL("'["), &buf->b_op_start, 
buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"']", STRLEN_LITERAL("']"), &buf->b_op_end, 
buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'^", STRLEN_LITERAL("'^"), &buf->b_last_insert, 
buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'.", STRLEN_LITERAL("'."), &buf->b_last_change, 
buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'<", STRLEN_LITERAL("'<"), &buf->b_visual.vi_start, 
buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'>", STRLEN_LITERAL("'>"), &buf->b_visual.vi_end, 
buf->b_fnum, NULL);
 }
 
 /*
@@ -1528,6 +1529,7 @@ get_buf_local_marks(buf_T *buf, list_T *l)
 get_global_marks(list_T *l)
 {
     char_u     mname[3] = "' ";
+    size_t     mnamelen = STRLEN_LITERAL("' ");
     int                i;
     char_u     *name;
 
@@ -1541,7 +1543,7 @@ get_global_marks(list_T *l)
        if (name != NULL)
        {
            mname[1] = i >= NMARKS ? i - NMARKS + '0' : i + 'A';
-           add_mark(l, mname, &namedfm[i].fmark.mark,
+           add_mark(l, mname, mnamelen, &namedfm[i].fmark.mark,
                    namedfm[i].fmark.fnum, name);
            if (namedfm[i].fmark.fnum != 0)
                vim_free(name);
diff --git a/src/match.c b/src/match.c
index 912abfd74..709ac3049 100644
--- a/src/match.c
+++ b/src/match.c
@@ -1048,10 +1048,12 @@ f_getmatches(typval_T *argvars UNUSED, typval_T *rettv 
UNUSED)
 #  if defined(FEAT_CONCEAL)
        if (cur->mit_conceal_char)
        {
-           char_u buf[MB_MAXBYTES + 1];
+           char_u  buf[MB_MAXBYTES + 1];
+           int     buflen;
 
-           buf[(*mb_char2bytes)(cur->mit_conceal_char, buf)] = NUL;
-           dict_add_string(dict, "conceal", (char_u *)&buf);
+           buflen = (*mb_char2bytes)(cur->mit_conceal_char, buf);
+           buf[buflen] = NUL;
+           dict_add_string_len(dict, "conceal", (char_u *)&buf, buflen);
        }
 #  endif
        list_append_dict(rettv->vval.v_list, dict);
diff --git a/src/memline.c b/src/memline.c
index 8152a03a5..b374971e2 100644
--- a/src/memline.c
+++ b/src/memline.c
@@ -2225,9 +2225,11 @@ get_b0_dict(char_u *fname, dict_T *d)
        if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
        {
            if (ml_check_b0_id(&b0) == FAIL)
-               dict_add_string(d, "error", (char_u *)"Not a swap file");
+               dict_add_string_len(d, "error",
+                   (char_u *)"Not a swap file", STRLEN_LITERAL("Not a swap 
file"));
            else if (b0_magic_wrong(&b0))
-               dict_add_string(d, "error", (char_u *)"Magic number mismatch");
+               dict_add_string_len(d, "error",
+                   (char_u *)"Magic number mismatch", STRLEN_LITERAL("Magic 
number mismatch"));
            else
            {
                // we have swap information
@@ -2245,11 +2247,11 @@ get_b0_dict(char_u *fname, dict_T *d)
            }
        }
        else
-           dict_add_string(d, "error", (char_u *)"Cannot read file");
+           dict_add_string_len(d, "error", (char_u *)"Cannot read file", 
STRLEN_LITERAL("Cannot read file"));
        close(fd);
     }
     else
-       dict_add_string(d, "error", (char_u *)"Cannot open file");
+       dict_add_string_len(d, "error", (char_u *)"Cannot open file", 
STRLEN_LITERAL("Cannot open file"));
 }
 #endif
 
diff --git a/src/menu.c b/src/menu.c
index 3c0dfca67..861d78711 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -2892,15 +2892,17 @@ menuitem_getinfo(char_u *menu_name, vimmenu_T *menu, 
int modes, dict_T *dict)
     if (status == OK)
     {
        char_u  buf[NUMBUFLEN];
+       int     buflen;
 
        if (has_mbyte)
-           buf[utf_char2bytes(menu->mnemonic, buf)] = NUL;
+           buflen = utf_char2bytes(menu->mnemonic, buf);
        else
        {
            buf[0] = (char_u)menu->mnemonic;
-           buf[1] = NUL;
+           buflen = (buf[0] == NUL) ? 0 : 1;
        }
-       status = dict_add_string(dict, "shortcut", buf);
+       buf[buflen] = NUL;
+       status = dict_add_string_len(dict, "shortcut", buf, buflen);
     }
     if (status == OK && menu->children == NULL)
     {
@@ -2913,14 +2915,17 @@ menuitem_getinfo(char_u *menu_name, vimmenu_T *menu, 
int modes, dict_T *dict)
        {
            if (menu->strings[bit] != NULL)
            {
-               char_u *tofree = NULL;
-
-               status = dict_add_string(dict, "rhs",
-                       *menu->strings[bit] == NUL
-                               ? (char_u *)"<Nop>"
-                               : (tofree = str2special_save(
-                                       menu->strings[bit], FALSE, FALSE)));
-               vim_free(tofree);
+               if (*menu->strings[bit] == NUL)
+                   status = dict_add_string_len(dict, "rhs",
+                       (char_u *)"<Nop>", STRLEN_LITERAL("<Nop>"));
+               else
+               {
+                   char_u *tofree = NULL;
+
+                   status = dict_add_string(dict, "rhs",
+                       tofree = str2special_save(menu->strings[bit], FALSE, 
FALSE));
+                   vim_free(tofree);
+               }
            }
            if (status == OK)
                status = dict_add_bool(dict, "noremenu",
diff --git a/src/mouse.c b/src/mouse.c
index 45ffd4b51..04a420e4b 100644
--- a/src/mouse.c
+++ b/src/mouse.c
@@ -1731,7 +1731,7 @@ stl_click_handler_regions(
     else
        button_str[0] = 'm';
     button_str[1] = NUL;
-    dict_add_string(info, "button", button_str);
+    dict_add_string_len(info, "button", button_str, 1);
 
     // Modifiers.
     if (mods & MOD_MASK_SHIFT)
@@ -1741,7 +1741,7 @@ stl_click_handler_regions(
     if (mods & MOD_MASK_ALT)
        mods_str[mi++] = 'a';
     mods_str[mi] = NUL;
-    dict_add_string(info, "mods", mods_str);
+    dict_add_string_len(info, "mods", mods_str, mi);
 
     dict_add_number(info, "winid", winid);
 
diff --git a/src/popupwin.c b/src/popupwin.c
index 20d7708c6..35bcd4c80 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -16,17 +16,20 @@
 #if defined(FEAT_PROP_POPUP)
 
 typedef struct {
-    char       *pp_name;
+    string_T   pp_name;
     poppos_T   pp_val;
 } poppos_entry_T;
 
+#define STRING_INIT(s) \
+    {(char_u *)(s), STRLEN_LITERAL(s)}
 static poppos_entry_T poppos_entries[] = {
-    {"botleft", POPPOS_BOTLEFT},
-    {"topleft", POPPOS_TOPLEFT},
-    {"botright", POPPOS_BOTRIGHT},
-    {"topright", POPPOS_TOPRIGHT},
-    {"center", POPPOS_CENTER}
+    {STRING_INIT("botleft"), POPPOS_BOTLEFT},
+    {STRING_INIT("topleft"), POPPOS_TOPLEFT},
+    {STRING_INIT("botright"), POPPOS_BOTRIGHT},
+    {STRING_INIT("topright"), POPPOS_TOPRIGHT},
+    {STRING_INIT("center"), POPPOS_CENTER}
 };
+#undef STRING_INIT
 
 #ifdef HAS_MESSAGE_WINDOW
 // Window used for ":echowindow"
@@ -474,7 +477,7 @@ get_pos_entry(dict_T *d, int give_error)
        return POPPOS_NONE;
 
     for (nr = 0; nr < (int)ARRAY_LENGTH(poppos_entries); ++nr)
-       if (STRCMP(str, poppos_entries[nr].pp_name) == 0)
+       if (STRCMP(str, poppos_entries[nr].pp_name.string) == 0)
            return poppos_entries[nr].pp_val;
 
     if (give_error)
@@ -3887,14 +3890,18 @@ f_popup_getoptions(typval_T *argvars, typval_T *rettv)
     for (i = 0; i < (int)ARRAY_LENGTH(poppos_entries); ++i)
        if (wp->w_popup_pos == poppos_entries[i].pp_val)
        {
-           dict_add_string(dict, "pos",
-                   (char_u *)poppos_entries[i].pp_name);
+           dict_add_string_len(dict, "pos",
+               poppos_entries[i].pp_name.string,
+               (int)poppos_entries[i].pp_name.length);
            break;
        }
 
-    dict_add_string(dict, "close", (char_u *)(
-               wp->w_popup_close == POPCLOSE_BUTTON ? "button"
-               : wp->w_popup_close == POPCLOSE_CLICK ? "click" : "none"));
+    if (wp->w_popup_close == POPCLOSE_BUTTON)
+       dict_add_string_len(dict, "close", (char_u *)"button", 
STRLEN_LITERAL("button"));
+    else if (wp->w_popup_close == POPCLOSE_CLICK)
+       dict_add_string_len(dict, "close", (char_u *)"click", 
STRLEN_LITERAL("click"));
+    else
+       dict_add_string_len(dict, "close", (char_u *)"none", 
STRLEN_LITERAL("none"));
 
 #if defined(FEAT_TIMERS)
     dict_add_number(dict, "time", wp->w_popup_timer != NULL
diff --git a/src/quickfix.c b/src/quickfix.c
index 46665e8ca..28f3655e1 100644
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -7176,6 +7176,7 @@ get_qfline_items(qfline_T *qfp, list_T *list)
     int                bufnum;
     dict_T     *dict;
     char_u     buf[2];
+    size_t     buflen;
 
     // Handle entries with a non-existing buffer number.
     bufnum = qfp->qf_fnum;
@@ -7189,6 +7190,7 @@ get_qfline_items(qfline_T *qfp, list_T *list)
 
     buf[0] = qfp->qf_type;
     buf[1] = NUL;
+    buflen = (buf[0] == NUL) ? 0 : 1;
     if (dict_add_number(dict, "bufnr", (long)bufnum) == FAIL
            || dict_add_number(dict, "lnum",     (long)qfp->qf_lnum) == FAIL
            || dict_add_number(dict, "end_lnum", (long)qfp->qf_end_lnum) == FAIL
@@ -7199,7 +7201,7 @@ get_qfline_items(qfline_T *qfp, list_T *list)
            || dict_add_string(dict, "module", qfp->qf_module) == FAIL
            || dict_add_string(dict, "pattern", qfp->qf_pattern) == FAIL
            || dict_add_string(dict, "text", qfp->qf_text) == FAIL
-           || dict_add_string(dict, "type", buf) == FAIL
+           || dict_add_string_len(dict, "type", buf, (int)buflen) == FAIL
            || (qfp->qf_user_data.v_type != VAR_UNKNOWN
                && dict_add_tv(dict, "user_data", &qfp->qf_user_data) == FAIL )
            || dict_add_number(dict, "valid", (long)qfp->qf_valid) == FAIL)
@@ -7484,7 +7486,7 @@ qf_getprop_defaults(qf_info_T *qi, int flags, int 
locstack, dict_T *retdict)
     int                status = OK;
 
     if (flags & QF_GETLIST_TITLE)
-       status = dict_add_string(retdict, "title", (char_u *)"");
+       status = dict_add_string_len(retdict, "title", (char_u *)"", 0);
     if ((status == OK) && (flags & QF_GETLIST_ITEMS))
     {
        list_T  *l = list_alloc();
@@ -7498,7 +7500,7 @@ qf_getprop_defaults(qf_info_T *qi, int flags, int 
locstack, dict_T *retdict)
     if ((status == OK) && (flags & QF_GETLIST_WINID))
        status = dict_add_number(retdict, "winid", qf_winid(qi));
     if ((status == OK) && (flags & QF_GETLIST_CONTEXT))
-       status = dict_add_string(retdict, "context", (char_u *)"");
+       status = dict_add_string_len(retdict, "context", (char_u *)"", 0);
     if ((status == OK) && (flags & QF_GETLIST_ID))
        status = dict_add_number(retdict, "id", 0);
     if ((status == OK) && (flags & QF_GETLIST_IDX))
@@ -7512,7 +7514,7 @@ qf_getprop_defaults(qf_info_T *qi, int flags, int 
locstack, dict_T *retdict)
     if ((status == OK) && (flags & QF_GETLIST_QFBUFNR))
        status = qf_getprop_qfbufnr(qi, retdict);
     if ((status == OK) && (flags & QF_GETLIST_QFTF))
-       status = dict_add_string(retdict, "quickfixtextfunc", (char_u *)"");
+       status = dict_add_string_len(retdict, "quickfixtextfunc", (char_u *)"", 
0);
 
     return status;
 }
@@ -7589,7 +7591,7 @@ qf_getprop_ctx(qf_list_T *qfl, dict_T *retdict)
            status = FAIL;
     }
     else
-       status = dict_add_string(retdict, "context", (char_u *)"");
+       status = dict_add_string_len(retdict, "context", (char_u *)"", 0);
 
     return status;
 }
@@ -7628,7 +7630,7 @@ qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict)
        clear_tv(&tv);
     }
     else
-       status = dict_add_string(retdict, "quickfixtextfunc", (char_u *)"");
+       status = dict_add_string_len(retdict, "quickfixtextfunc", (char_u *)"", 
0);
 
     return status;
 }
diff --git a/src/register.c b/src/register.c
index 0215f0153..f7a02e8d9 100644
--- a/src/register.c
+++ b/src/register.c
@@ -1093,6 +1093,7 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
     list_T         *list;
     int                    n;
     char_u         buf[NUMBUFLEN + 2];
+    size_t         buflen;
     long           reglen = 0;
     save_v_event_T  save_v_event;
 
@@ -1114,7 +1115,8 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
     // register name or empty string for unnamed operation
     buf[0] = (char_u)oap->regname;
     buf[1] = NUL;
-    (void)dict_add_string(v_event, "regname", buf);
+    buflen = (buf[0] == NUL) ? 0 : 1;
+    (void)dict_add_string_len(v_event, "regname", buf, (int)buflen);
 
     // motion type: inclusive or exclusive
     (void)dict_add_bool(v_event, "inclusive", oap->inclusive);
@@ -1123,21 +1125,32 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
     buf[0] = get_op_char(oap->op_type);
     buf[1] = get_extra_op_char(oap->op_type);
     buf[2] = NUL;
-    (void)dict_add_string(v_event, "operator", buf);
+    buflen = (buf[0] == NUL) ? 0 : (buf[1] == NUL) ? 1 : 2;
+    (void)dict_add_string_len(v_event, "operator", buf, (int)buflen);
 
     // register type
-    buf[0] = NUL;
-    buf[1] = NUL;
     switch (get_reg_type(oap->regname, &reglen))
     {
-       case MLINE: buf[0] = 'V'; break;
-       case MCHAR: buf[0] = 'v'; break;
+       case MLINE:
+           buf[0] = 'V';
+           buf[1] = NUL;
+           buflen = 1;
+           break;
+       case MCHAR:
+           buf[0] = 'v';
+           buf[1] = NUL;
+           buflen = 1;
+           break;
        case MBLOCK:
-               vim_snprintf((char *)buf, sizeof(buf), "%c%ld", Ctrl_V,
-                            reglen + 1);
-               break;
+           buflen = vim_snprintf_safelen((char *)buf, sizeof(buf),
+               "%c%ld", Ctrl_V, reglen + 1);
+           break;
+       default:
+           buf[0] = NUL;
+           buflen = 0;
+           break;
     }
-    (void)dict_add_string(v_event, "regtype", buf);
+    (void)dict_add_string_len(v_event, "regtype", buf, (int)buflen);
 
     // selection type - visual or not
     (void)dict_add_bool(v_event, "visual", oap->is_VIsual);
diff --git a/src/sign.c b/src/sign.c
index 6477af307..a8dfd05ee 100644
--- a/src/sign.c
+++ b/src/sign.c
@@ -322,9 +322,10 @@ sign_get_info(sign_entry_T *sign)
         return NULL;
 
     dict_add_number(d, "id", sign->se_id);
-    dict_add_string(d, "group",
-                    (sign->se_group == NULL) ? (char_u *)""
-                                             : sign->se_group->sg_name);
+    if (sign->se_group == NULL)
+        dict_add_string_len(d, "group", (char_u *)"", 0);
+    else
+        dict_add_string(d, "group", sign->se_group->sg_name);
     dict_add_number(d, "lnum", sign->se_lnum);
     dict_add_string(d, "name", sign_typenr2name(sign->se_typenr));
     dict_add_number(d, "priority", sign->se_priority);
@@ -1777,32 +1778,40 @@ sign_getinfo(sign_T *sp, dict_T *retdict)
     {
         char_u *p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
         if (p == NULL)
-            p = (char_u *)"NONE";
-        dict_add_string(retdict, "linehl", p);
+            dict_add_string_len(retdict, "linehl",
+                (char_u *)"NONE", STRLEN_LITERAL("NONE"));
+        else
+            dict_add_string(retdict, "linehl", p);
     }
 
     if (sp->sn_text_hl > 0)
     {
         char_u *p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
         if (p == NULL)
-            p = (char_u *)"NONE";
-        dict_add_string(retdict, "texthl", p);
+            dict_add_string_len(retdict, "texthl",
+                (char_u *)"NONE", STRLEN_LITERAL("NONE"));
+        else
+            dict_add_string(retdict, "texthl", p);
     }
 
     if (sp->sn_cul_hl > 0)
     {
         char_u *p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
         if (p == NULL)
-            p = (char_u *)"NONE";
-        dict_add_string(retdict, "culhl", p);
+            dict_add_string_len(retdict, "culhl",
+                (char_u *)"NONE", STRLEN_LITERAL("NONE"));
+        else
+            dict_add_string(retdict, "culhl", p);
     }
 
     if (sp->sn_num_hl > 0)
     {
         char_u *p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE);
         if (p == NULL)
-            p = (char_u *)"NONE";
-        dict_add_string(retdict, "numhl", p);
+            dict_add_string_len(retdict, "numhl",
+                (char_u *)"NONE", STRLEN_LITERAL("NONE"));
+        else
+            dict_add_string(retdict, "numhl", p);
     }
 }
 
diff --git a/src/term.c b/src/term.c
index 468a7eb48..882df6655 100644
--- a/src/term.c
+++ b/src/term.c
@@ -1587,7 +1587,8 @@ f_terminalprops(typval_T *argvars UNUSED, typval_T *rettv)
 
        value[0] = term_props[i].tpr_status;
        value[1] = NUL;
-       dict_add_string(rettv->vval.v_dict, term_props[i].tpr_name, value);
+       dict_add_string_len(rettv->vval.v_dict, term_props[i].tpr_name,
+           value, (value[0] == NUL) ? 0 : 1);
     }
 # endif
 }
diff --git a/src/terminal.c b/src/terminal.c
index 92c889fc2..7e812fc97 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -6784,14 +6784,14 @@ f_term_scrape(typval_T *argvars, typval_T *rettv)
        VTermScreenCellAttrs attrs;
        VTermColor      fg, bg;
        char_u          rgb[8];
+       size_t          rgblen;
        char_u          mbs[MB_MAXBYTES * VTERM_MAX_CHARS_PER_CELL + 1];
-       int             off = 0;
+       size_t          mbslen;
        int             i;
 
        if (screen == NULL)
        {
            cellattr_T  *cellattr;
-           int         len;
 
            // vterm has finished, get the cell from scrollback
            if (pos.col >= line->sb_cols)
@@ -6801,10 +6801,10 @@ f_term_scrape(typval_T *argvars, typval_T *rettv)
            attrs = cellattr->attrs;
            fg = cellattr->fg;
            bg = cellattr->bg;
-           len = mb_ptr2len(p);
-           mch_memmove(mbs, p, len);
-           mbs[len] = NUL;
-           p += len;
+           mbslen = mb_ptr2len(p);
+           mch_memmove(mbs, p, mbslen);
+           mbs[mbslen] = NUL;
+           p += mbslen;
        }
        else
        {
@@ -6812,13 +6812,15 @@ f_term_scrape(typval_T *argvars, typval_T *rettv)
 
            if (vterm_screen_get_cell(screen, pos, &cell) == 0)
                break;
+
+           mbslen = 0;
            for (i = 0; i < VTERM_MAX_CHARS_PER_CELL; ++i)
            {
                if (cell.chars[i] == 0)
                    break;
-               off += (*utf_char2bytes)((int)cell.chars[i], mbs + off);
+               mbslen += (*utf_char2bytes)((int)cell.chars[i], mbs + mbslen);
            }
-           mbs[off] = NUL;
+           mbs[mbslen] = NUL;
            width = cell.width;
            attrs = cell.attrs;
            fg = cell.fg;
@@ -6829,14 +6831,14 @@ f_term_scrape(typval_T *argvars, typval_T *rettv)
            break;
        list_append_dict(l, dcell);
 
-       dict_add_string(dcell, "chars", mbs);
+       dict_add_string_len(dcell, "chars", mbs, (int)mbslen);
 
-       vim_snprintf((char *)rgb, 8, "#%02x%02x%02x",
-                                    fg.red, fg.green, fg.blue);
-       dict_add_string(dcell, "fg", rgb);
-       vim_snprintf((char *)rgb, 8, "#%02x%02x%02x",
-                                    bg.red, bg.green, bg.blue);
-       dict_add_string(dcell, "bg", rgb);
+       rgblen = vim_snprintf_safelen((char *)rgb, sizeof(rgb),
+           "#%02x%02x%02x", fg.red, fg.green, fg.blue);
+       dict_add_string_len(dcell, "fg", rgb, (int)rgblen);
+       rgblen = vim_snprintf_safelen((char *)rgb, sizeof(rgb),
+           "#%02x%02x%02x", bg.red, bg.green, bg.blue);
+       dict_add_string_len(dcell, "bg", rgb, (int)rgblen);
 
        dict_add_number(dcell, "attr",
                                      cell2attr(term, NULL, &attrs, &fg, &bg));
diff --git a/src/textprop.c b/src/textprop.c
index 78e9276e5..782701aff 100644
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -1727,23 +1727,35 @@ prop_fill_dict(dict_T *dict, textprop_T *prop, buf_T 
*buf)
        dict_add_number(dict, "type_bufnr", 0);
     if (virtualtext_prop)
     {
+       string_T    text_align = {NULL, 0};
+
        // virtual text property - u.tp_text must be set by caller
        dict_add_string(dict, "text", prop->u.tp_text);
 
        // text_align
-       char_u      *text_align = NULL;
        if (prop->tp_flags & TP_FLAG_ALIGN_RIGHT)
-           text_align = (char_u *)"right";
+       {
+           text_align.string = (char_u *)"right";
+           text_align.length = STRLEN_LITERAL("right");
+       }
        else if (prop->tp_flags & TP_FLAG_ALIGN_ABOVE)
-           text_align = (char_u *)"above";
+       {
+           text_align.string = (char_u *)"above";
+           text_align.length = STRLEN_LITERAL("above");
+       }
        else if (prop->tp_flags & TP_FLAG_ALIGN_BELOW)
-           text_align = (char_u *)"below";
-       if (text_align != NULL)
-           dict_add_string(dict, "text_align", text_align);
+       {
+           text_align.string = (char_u *)"below";
+           text_align.length = STRLEN_LITERAL("below");
+       }
+       if (text_align.string != NULL)
+           dict_add_string_len(dict, "text_align",
+               text_align.string, (int)text_align.length);
 
        // text_wrap
        if (prop->tp_flags & TP_FLAG_WRAP)
-           dict_add_string(dict, "text_wrap", (char_u *)"wrap");
+           dict_add_string_len(dict, "text_wrap",
+               (char_u *)"wrap", STRLEN_LITERAL("wrap"));
        if (prop->tp_padleft != 0)
            dict_add_number(dict, "text_padding_left", prop->tp_padleft);
     }
diff --git a/src/version.c b/src/version.c
index 20e593ac8..9c634f7da 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    368,
 /**/
     367,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1wEZTs-00B6Pv-NJ%40256bit.org.

Raspunde prin e-mail lui