patch 9.1.1063: too many strlen() calls in userfunc.c

Problem:  too many strlen() calls in userfunc.c
Solution: refactor userfunc.c and remove calls to strlen(),
          drop set_ufunc_name() and roll it into alloc_ufunc(),
          check for out-of-memory condition in trans_function_name_ext()
          (John Marriott)

closes: #16537

Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/userfunc.c b/src/userfunc.c
index 91c971e..81f1f28 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -171,9 +171,13 @@
 	if (!skip)
 	{
 	    if (type == NULL && types_optional)
+	    {
 		// lambda arguments default to "any" type
-		type = vim_strsave((char_u *)
-					    (is_vararg ? "list<any>" : "any"));
+		if (is_vararg)
+		    type = vim_strnsave((char_u *)"list<any>", 9);
+		else
+		    type = vim_strnsave((char_u *)"any", 3);
+	    }
 	    ((char_u **)argtypes->ga_data)[argtypes->ga_len++] = type;
 	    ((int8_T *)arg_objm->ga_data)[arg_objm->ga_len++] = FALSE;
 	}
@@ -346,7 +350,7 @@
 		if (ga_grow(default_args, 1) == FAIL)
 		    goto err_ret;
 
-		char_u *expr = vim_strsave((char_u *)"v:none");
+		char_u *expr = vim_strnsave((char_u *)"v:none", 6);
 		if (expr == NULL)
 		    goto err_ret;
 		((char_u **)(default_args->ga_data))
@@ -373,7 +377,7 @@
 		{
 		    // TODO: use the actual type
 		    ((char_u **)argtypes->ga_data)[argtypes->ga_len++] =
-						  vim_strsave((char_u *)"any");
+						  vim_strnsave((char_u *)"any", 3);
 		    ((int8_T *)arg_objm->ga_data)[arg_objm->ga_len++] = TRUE;
 
 		    // Add a line to the function body for the assignment.
@@ -656,24 +660,6 @@
     return OK;
 }
 
-    static void
-set_ufunc_name(ufunc_T *fp, char_u *name)
-{
-    // Add a type cast to avoid a warning for an overflow, the uf_name[] array
-    // actually extends beyond the struct.
-    STRCPY((void *)fp->uf_name, name);
-
-    if (name[0] == K_SPECIAL)
-    {
-	fp->uf_name_exp = alloc(STRLEN(name) + 3);
-	if (fp->uf_name_exp != NULL)
-	{
-	    STRCPY(fp->uf_name_exp, "<SNR>");
-	    STRCAT(fp->uf_name_exp, fp->uf_name + 3);
-	}
-    }
-}
-
 /*
  * If "name" starts with K_SPECIAL and "buf[bufsize]" is big enough
  * return "buf" filled with a readable function name.
@@ -699,14 +685,34 @@
 /*
  * Get a name for a lambda.  Returned in static memory.
  */
+static char_u	lambda_name[8 + NUMBUFLEN];
+static size_t	lambda_namelen = 0;
+
     char_u *
 get_lambda_name(void)
 {
-    static char_u   name[30];
-    static int	    lambda_no = 0;
+    static int	lambda_no = 0;
+    int		n;
 
-    sprintf((char*)name, "<lambda>%d", ++lambda_no);
-    return name;
+    n = vim_snprintf((char *)lambda_name, sizeof(lambda_name), "<lambda>%d", ++lambda_no);
+    if (n < 1)
+	lambda_namelen = 0;
+    else
+    if (n >= (int)sizeof(lambda_name))
+	lambda_namelen = sizeof(lambda_name) - 1;
+    else
+	lambda_namelen = (size_t)n;
+
+    return lambda_name;
+}
+
+/*
+ * Get the length of the last lambda name.
+ */
+    size_t
+get_lambda_name_len(void)
+{
+    return lambda_namelen;
 }
 
 /*
@@ -714,12 +720,32 @@
  * Makes sure the size is right.
  */
     static ufunc_T *
-alloc_ufunc(char_u *name)
+alloc_ufunc(char_u *name, size_t namelen)
 {
+    size_t  len;
+    ufunc_T *fp;
+
     // When the name is short we need to make sure we allocate enough bytes for
     // the whole struct, including any padding.
-    size_t len = offsetof(ufunc_T, uf_name) + STRLEN(name) + 1;
-    return alloc_clear(len < sizeof(ufunc_T) ? sizeof(ufunc_T) : len);
+    len = offsetof(ufunc_T, uf_name) + namelen + 1;
+    fp = alloc_clear(len < sizeof(ufunc_T) ? sizeof(ufunc_T) : len);
+    if (fp != NULL)
+    {
+	// Add a type cast to avoid a warning for an overflow, the uf_name[] array
+	// can actually extend beyond the struct.
+	STRCPY((void *)fp->uf_name, name);
+	fp->uf_namelen = namelen;
+
+	if (name[0] == K_SPECIAL)
+	{
+	    len = namelen + 3;		    // including +1 for NUL
+	    fp->uf_name_exp = alloc(len);
+	    if (fp->uf_name_exp != NULL)
+		vim_snprintf((char *)fp->uf_name_exp, len, "<SNR>%s", fp->uf_name + 3);
+	}
+    }
+
+    return fp;
 }
 
 #if defined(FEAT_LUA) || defined(PROTO)
@@ -731,9 +757,10 @@
 register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state)
 {
     char_u	*name = get_lambda_name();
+    size_t	namelen = get_lambda_name_len();
     ufunc_T	*fp;
 
-    fp = alloc_ufunc(name);
+    fp = alloc_ufunc(name, namelen);
     if (fp == NULL)
 	return NULL;
 
@@ -747,7 +774,6 @@
     fp->uf_cb_free = cb_free;
     fp->uf_cb_state = state;
 
-    set_ufunc_name(fp, name);
     hash_add(&func_hashtab, UF2HIKEY(fp), "add C function");
 
     return name;
@@ -976,6 +1002,7 @@
     int		heredoc_concat_len = 0;
     garray_T	heredoc_ga;
     char_u	*heredoc_trimmed = NULL;
+    size_t	heredoc_trimmedlen = 0;
 
     ga_init2(&heredoc_ga, 1, 500);
 
@@ -1059,19 +1086,20 @@
 	    if (heredoc_trimmed == NULL
 		    || (is_heredoc && skipwhite(theline) == theline)
 		    || STRNCMP(theline, heredoc_trimmed,
-						 STRLEN(heredoc_trimmed)) == 0)
+						 heredoc_trimmedlen) == 0)
 	    {
 		if (heredoc_trimmed == NULL)
 		    p = theline;
 		else if (is_heredoc)
 		    p = skipwhite(theline) == theline
-				 ? theline : theline + STRLEN(heredoc_trimmed);
+				 ? theline : theline + heredoc_trimmedlen;
 		else
-		    p = theline + STRLEN(heredoc_trimmed);
+		    p = theline + heredoc_trimmedlen;
 		if (STRCMP(p, skip_until) == 0)
 		{
 		    VIM_CLEAR(skip_until);
 		    VIM_CLEAR(heredoc_trimmed);
+		    heredoc_trimmedlen = 0;
 		    getline_options = vim9_function
 				? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
 		    is_heredoc = FALSE;
@@ -1271,7 +1299,7 @@
 				    || (p[2] == 's'
 					&& (!ASCII_ISALPHA(p[3])
 						|| p[3] == 'e'))))))))
-		skip_until = vim_strsave((char_u *)".");
+		skip_until = vim_strnsave((char_u *)".", 1);
 
 	    // Check for ":python <<EOF", ":tcl <<EOF", etc.
 	    arg = skipwhite(skiptowhite(p));
@@ -1298,11 +1326,13 @@
 		{
 		    // Ignore leading white space.
 		    p = skipwhite(p + 4);
-		    heredoc_trimmed = vim_strnsave(theline,
-						 skipwhite(theline) - theline);
+		    heredoc_trimmedlen = skipwhite(theline) - theline;
+		    heredoc_trimmed = vim_strnsave(theline, heredoc_trimmedlen);
+		    if (heredoc_trimmed == NULL)
+			heredoc_trimmedlen = 0;
 		}
 		if (*p == NUL)
-		    skip_until = vim_strsave((char_u *)".");
+		    skip_until = vim_strnsave((char_u *)".", 1);
 		else
 		    skip_until = vim_strnsave(p, skiptowhite(p) - p);
 		getline_options = GETLINE_NONE;
@@ -1344,8 +1374,10 @@
 			    {
 				// Ignore leading white space.
 				p = skipwhite(p + 4);
-				heredoc_trimmed = vim_strnsave(theline,
-					skipwhite(theline) - theline);
+				heredoc_trimmedlen = skipwhite(theline) - theline;
+				heredoc_trimmed = vim_strnsave(theline, heredoc_trimmedlen);
+				if (heredoc_trimmed == NULL)
+				    heredoc_trimmedlen = 0;
 				continue;
 			    }
 			    if (STRNCMP(p, "eval", 4) == 0)
@@ -1374,7 +1406,7 @@
 	    // to be used for the instruction later.
 	    ga_concat(&heredoc_ga, theline);
 	    ga_concat(&heredoc_ga, (char_u *)"\n");
-	    p = vim_strsave((char_u *)"");
+	    p = vim_strnsave((char_u *)"", 0);
 	}
 	else
 	{
@@ -1437,6 +1469,7 @@
     int		ret = FAIL;
     partial_T	*pt;
     char_u	*name;
+    size_t	namelen;
     int		lnum_save = -1;
     linenr_T	sourcing_lnum_top = SOURCING_LNUM;
     char_u	*line_arg = NULL;
@@ -1498,8 +1531,12 @@
 	    // Insert NL characters at the start of each line, the string will
 	    // be split again later in .get_lambda_tv().
 	    if (*p == NUL || vim9_comment_start(p))
+	    {
 		p = (char_u *)"";
-	    plen = STRLEN(p);
+		plen = 0;
+	    }
+	    else
+		plen = STRLEN(p);
 	    pnl = vim_strnsave((char_u *)"\n", plen + 1);
 	    if (pnl != NULL)
 		mch_memmove(pnl + 1, p, plen + 1);
@@ -1509,12 +1546,17 @@
 	if (ga_grow(gap, 1) == FAIL || ga_grow(freegap, 1) == FAIL)
 	    goto erret;
 	if (eap.nextcmd != NULL)
+	{
 	    // more is following after the "}", which was skipped
 	    last = cmdline;
+	    plen = STRLEN(last);
+	}
 	else
+	{
 	    // nothing is following the "}"
 	    last = (char_u *)"}";
-	plen = STRLEN(last);
+	    plen = 1;
+	}
 	pnl = vim_strnsave((char_u *)"\n", plen + 1);
 	if (pnl != NULL)
 	    mch_memmove(pnl + 1, last, plen + 1);
@@ -1546,10 +1588,10 @@
     }
 
     name = get_lambda_name();
-    ufunc = alloc_ufunc(name);
+    namelen = get_lambda_name_len();
+    ufunc = alloc_ufunc(name, namelen);
     if (ufunc == NULL)
 	goto erret;
-    set_ufunc_name(ufunc, name);
     if (hash_add(&func_hashtab, UF2HIKEY(ufunc), "add function") == FAIL)
 	goto erret;
     ufunc->uf_flags = FC_LAMBDA;
@@ -1755,8 +1797,9 @@
 	char_u	    *p;
 	char_u	    *line_end;
 	char_u	    *name = get_lambda_name();
+	size_t	    namelen = get_lambda_name_len();
 
-	fp = alloc_ufunc(name);
+	fp = alloc_ufunc(name, namelen);
 	if (fp == NULL)
 	    goto errret;
 	fp->uf_def_status = UF_NOT_COMPILED;
@@ -1804,7 +1847,6 @@
 	    flags |= FC_NOARGS;
 
 	fp->uf_refcount = 1;
-	set_ufunc_name(fp, name);
 	fp->uf_args = newargs;
 	ga_init(&fp->uf_def_args);
 	if (types_optional)
@@ -1875,10 +1917,13 @@
     {
 	ga_clear_strings(&argtypes);
 	ga_clear(&arg_objm);
-	if (fp != NULL)
-	    vim_free(fp->uf_arg_types);
     }
-    vim_free(fp);
+    if (fp != NULL)
+    {
+	vim_free(fp->uf_arg_types);
+	vim_free(fp->uf_name_exp);
+	vim_free(fp);
+    }
     vim_free(pt);
     vim_free(tofree2);
     eval_lavars_used = old_eval_lavars;
@@ -2184,44 +2229,48 @@
 	char_u	    **tofree,
 	funcerror_T *error)
 {
-    int		llen;
+    char_u	*script_name;
     char_u	*fname;
-    int		i;
+    size_t	fnamelen;
+    size_t	fname_buflen;
 
-    llen = eval_fname_script(name);
-    if (llen == 0)
+    script_name = name + eval_fname_script(name);
+    if (script_name == name)
 	return name;  // no prefix
 
     fname_buf[0] = K_SPECIAL;
     fname_buf[1] = KS_EXTRA;
     fname_buf[2] = (int)KE_SNR;
-    i = 3;
-    if (eval_fname_sid(name))	// "<SID>" or "s:"
+    fname_buflen = 3;
+    if (!eval_fname_sid(name))	// "<SID>" or "s:"
+	fname_buf[fname_buflen] = NUL;
+    else
     {
 	if (current_sctx.sc_sid <= 0)
 	    *error = FCERR_SCRIPT;
 	else
 	{
-	    sprintf((char *)fname_buf + 3, "%ld_",
+	    fname_buflen += vim_snprintf((char *)fname_buf + 3,
+						FLEN_FIXED - 3,
+						"%ld_",
 						(long)current_sctx.sc_sid);
-	    i = (int)STRLEN(fname_buf);
 	}
     }
-    if (i + STRLEN(name + llen) < FLEN_FIXED)
+    fnamelen = fname_buflen + STRLEN(script_name);
+    if (fnamelen < FLEN_FIXED)
     {
-	STRCPY(fname_buf + i, name + llen);
+	STRCPY(fname_buf + fname_buflen, script_name);
 	fname = fname_buf;
     }
     else
     {
-	fname = alloc(i + STRLEN(name + llen) + 1);
+	fname = alloc(fnamelen + 1);
 	if (fname == NULL)
 	    *error = FCERR_OTHER;
 	else
 	{
 	    *tofree = fname;
-	    mch_memmove(fname, fname_buf, (size_t)i);
-	    STRCPY(fname + i, name + llen);
+	    vim_snprintf((char *)fname, fnamelen + 1, "%s%s", fname_buf, script_name);
 	}
     }
     return fname;
@@ -2391,7 +2440,7 @@
     int
 func_requires_g_prefix(ufunc_T *ufunc)
 {
-    return ufunc->uf_name[0] != K_SPECIAL
+    return func_is_global(ufunc)
 	    && (ufunc->uf_flags & FC_LAMBDA) == 0
 	    && vim_strchr(ufunc->uf_name, AUTOLOAD_CHAR) == NULL
 	    && !SAFE_isdigit(ufunc->uf_name[0]);
@@ -2402,16 +2451,17 @@
  * "buf" must be able to hold the function name plus three bytes.
  * Takes care of script-local function names.
  */
-    static void
-cat_func_name(char_u *buf, ufunc_T *fp)
+    static int
+cat_func_name(char_u *buf, size_t bufsize, ufunc_T *fp)
 {
+    int	len;
+
     if (!func_is_global(fp))
-    {
-	STRCPY(buf, "<SNR>");
-	STRCAT(buf, fp->uf_name + 3);
-    }
+	len = vim_snprintf((char *)buf, bufsize, "<SNR>%s", fp->uf_name + 3);
     else
-	STRCPY(buf, fp->uf_name);
+	len = vim_snprintf((char *)buf, bufsize, "%s", fp->uf_name);
+
+    return (len >= (int)bufsize) ? (int)bufsize - 1 : len;
 }
 
 /*
@@ -2773,7 +2823,7 @@
 	return FAIL;
     }
 
-    fp = alloc_ufunc(global);
+    fp = alloc_ufunc(global, STRLEN(global));
     if (fp == NULL)
 	return FAIL;
 
@@ -2805,9 +2855,6 @@
 
     fp->uf_refcount = 1;
 
-    fp->uf_name_exp = NULL;
-    set_ufunc_name(fp, global);
-
     hash_add(&func_hashtab, UF2HIKEY(fp), "copy lambda");
 
     // the referenced dfunc_T is now used one more time
@@ -2943,6 +2990,7 @@
     int		islambda = FALSE;
     char_u	numbuf[NUMBUFLEN];
     char_u	*name;
+    size_t	namelen;
     typval_T	*tv_to_free[MAX_FUNC_ARGS];
     int		tv_to_free_len = 0;
 #ifdef FEAT_PROFILE
@@ -3099,6 +3147,8 @@
 		    break;
 		}
 	    }
+
+	    namelen = STRLEN(name);
 	}
 	else
 	{
@@ -3107,10 +3157,10 @@
 		break;
 
 	    // "..." argument a:1, a:2, etc.
-	    sprintf((char *)numbuf, "%d", ai + 1);
+	    namelen = vim_snprintf((char *)numbuf, sizeof(numbuf), "%d", ai + 1);
 	    name = numbuf;
 	}
-	if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN)
+	if (fixvar_idx < FIXVAR_CNT && namelen <= VAR_SHORT_LEN)
 	{
 	    v = &fc->fc_fixvar[fixvar_idx++].var;
 	    v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
@@ -3491,8 +3541,7 @@
     buf[0] = K_SPECIAL;
     buf[1] = KS_EXTRA;
     buf[2] = (int)KE_SNR;
-    sprintf((char *)buf + 3, "%d_", sid);
-    len = STRLEN(buf);
+    len = 3 + vim_snprintf((char *)buf + 3, sizeof(buf) - 3, "%d_", sid);
 
     while (todo > 0)
     {
@@ -4388,7 +4437,7 @@
     {
 	if (ufunc != NULL)
 	    *ufunc = lv.ll_ufunc;
-	name = vim_strsave(lv.ll_ufunc->uf_name);
+	name = vim_strnsave(lv.ll_ufunc->uf_name, lv.ll_ufunc->uf_namelen);
 	*pp = end;
 	goto theend;
     }
@@ -4485,7 +4534,7 @@
     {
 	name = vim_strsave(name);
 	*pp = end;
-	if (STRNCMP(name, "<SNR>", 5) == 0)
+	if (name != NULL && STRNCMP(name, "<SNR>", 5) == 0)
 	{
 	    // Change "<SNR>" to the byte sequence.
 	    name[0] = K_SPECIAL;
@@ -4567,17 +4616,19 @@
 					     && eval_fname_sid(lv.ll_exp_name))
 						       || eval_fname_sid(*pp))
 	{
+	    size_t  sid_buflen;
+
 	    // It's script-local, "s:" or "<SID>"
 	    if (current_sctx.sc_sid <= 0)
 	    {
 		emsg(_(e_using_sid_not_in_script_context));
 		goto theend;
 	    }
-	    sprintf((char *)sid_buf, "%ld_", (long)current_sctx.sc_sid);
+	    sid_buflen = vim_snprintf((char *)sid_buf, sizeof(sid_buf), "%ld_", (long)current_sctx.sc_sid);
 	    if (vim9_local)
-		extra = 3 + (int)STRLEN(sid_buf);
+		extra = 3 + (int)sid_buflen;
 	    else
-		lead += (int)STRLEN(sid_buf);
+		lead += (int)sid_buflen;
 	}
     }
     // The function name must start with an upper case letter (unless it is a
@@ -4664,8 +4715,10 @@
 get_scriptlocal_funcname(char_u *funcname)
 {
     char	sid_buf[25];
+    size_t	sid_buflen;
     int		off;
     char_u	*newname;
+    size_t	newnamesize;
     char_u	*p = funcname;
 
     if (funcname == NULL)
@@ -4696,13 +4749,13 @@
 	return NULL;
     }
     // Expand s: prefix into <SNR>nr_<name>
-    vim_snprintf(sid_buf, sizeof(sid_buf), "<SNR>%ld_",
+    sid_buflen = vim_snprintf(sid_buf, sizeof(sid_buf), "<SNR>%ld_",
 	    (long)current_sctx.sc_sid);
-    newname = alloc(STRLEN(sid_buf) + STRLEN(p + off) + 1);
+    newnamesize = sid_buflen + STRLEN(p + off) + 1;
+    newname = alloc(newnamesize);
     if (newname == NULL)
 	return NULL;
-    STRCPY(newname, sid_buf);
-    STRCAT(newname, p + off);
+    vim_snprintf((char *)newname, newnamesize, "%s%s", sid_buf, p + off);
 
     return newname;
 }
@@ -4811,6 +4864,7 @@
     int		c;
     int		saved_did_emsg = FALSE;
     char_u	*name = name_arg;
+    size_t	namelen = 0;
     int		is_global = FALSE;
     char_u	*p;
     char_u	*arg;
@@ -5107,7 +5161,7 @@
 	    // In Vim9 script a function cannot have the same name as a
 	    // variable.
 	    if (vim9script && *arg == K_SPECIAL
-		&& eval_variable(name_base, (int)STRLEN(name_base), 0, NULL,
+		&& eval_variable(name_base, i, 0, NULL,
 		    NULL, EVAL_VAR_NOAUTOLOAD + EVAL_VAR_IMPORT
 						     + EVAL_VAR_NO_FUNC) == OK)
 	    {
@@ -5252,7 +5306,7 @@
      */
     if (fudi.fd_dict != NULL)
     {
-	char	numbuf[20];
+	char	numbuf[NUMBUFLEN];
 
 	fp = NULL;
 	if (fudi.fd_newkey == NULL && !eap->forceit)
@@ -5273,8 +5327,8 @@
 	// Give the function a sequential number.  Can only be used with a
 	// Funcref!
 	vim_free(name);
-	sprintf(numbuf, "%d", ++func_nr);
-	name = vim_strsave((char_u *)numbuf);
+	namelen = vim_snprintf(numbuf, sizeof(numbuf), "%d", ++func_nr);
+	name = vim_strnsave((char_u *)numbuf, namelen);
 	if (name == NULL)
 	    goto erret;
     }
@@ -5373,6 +5427,7 @@
 
 		// redefine existing function, keep the expanded name
 		VIM_CLEAR(name);
+		namelen = 0;
 		fp->uf_name_exp = NULL;
 		func_clear_items(fp);
 		fp->uf_name_exp = exp_name;
@@ -5421,7 +5476,9 @@
 	    }
 	}
 
-	fp = alloc_ufunc(name);
+	if (namelen == 0)
+	    namelen = STRLEN(name);
+	fp = alloc_ufunc(name, namelen);
 	if (fp == NULL)
 	    goto erret;
 	fp_allocated = TRUE;
@@ -5448,7 +5505,7 @@
 		// overwrite existing dict entry
 		clear_tv(&fudi.fd_di->di_tv);
 	    fudi.fd_di->di_tv.v_type = VAR_FUNC;
-	    fudi.fd_di->di_tv.vval.v_string = vim_strsave(name);
+	    fudi.fd_di->di_tv.vval.v_string = vim_strnsave(name, namelen);
 
 	    // behave like "dict" was used
 	    flags |= FC_DICT;
@@ -5506,7 +5563,6 @@
     if (fp_allocated)
     {
 	// insert the new function in the function list
-	set_ufunc_name(fp, name);
 	if (overwrite)
 	{
 	    hi = hash_find(&func_hashtab, name);
@@ -5581,6 +5637,7 @@
     {
 	VIM_CLEAR(fp->uf_arg_types);
 	VIM_CLEAR(fp->uf_va_name);
+	VIM_CLEAR(fp->uf_name_exp);
 	clear_func_type_list(&fp->uf_type_list, &fp->uf_func_type);
     }
     if (free_fp)
@@ -5918,6 +5975,8 @@
     }
     if (changed == func_hashtab.ht_changed && done < func_hashtab.ht_used)
     {
+	int len;
+
 	if (done++ > 0)
 	    ++hi;
 	while (HASHITEM_EMPTY(hi))
@@ -5929,16 +5988,19 @@
 				|| STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
 	    return (char_u *)"";
 
-	if (STRLEN(fp->uf_name) + 4 >= IOSIZE)
+	if (fp->uf_namelen + 4 >= IOSIZE)
 	    return fp->uf_name;	// prevents overflow
 
-	cat_func_name(IObuff, fp);
+	len = cat_func_name(IObuff, IOSIZE, fp);
 	if (xp->xp_context != EXPAND_USER_FUNC
 				       && xp->xp_context != EXPAND_DISASSEMBLE)
 	{
-	    STRCAT(IObuff, "(");
+	    STRCPY(IObuff + len, "(");
 	    if (!has_varargs(fp) && fp->uf_args.ga_len == 0)
-		STRCAT(IObuff, ")");
+	    {
+		++len;
+		STRCPY(IObuff + len, ")");
+	    }
 	}
 	return IObuff;
     }
@@ -5955,7 +6017,7 @@
     ufunc_T *
 copy_function(ufunc_T *fp)
 {
-    ufunc_T *ufunc = alloc_ufunc(fp->uf_name);
+    ufunc_T *ufunc = alloc_ufunc(fp->uf_name, fp->uf_namelen);
     if (ufunc == NULL)
 	return NULL;
 
@@ -6004,8 +6066,6 @@
     ga_copy_strings(&fp->uf_lines, &ufunc->uf_lines);
 
     ufunc->uf_refcount = 1;
-    ufunc->uf_name_exp = NULL;
-    STRCPY(ufunc->uf_name, fp->uf_name);
 
     return ufunc;
 }
@@ -6760,20 +6820,31 @@
 get_return_cmd(void *rettv)
 {
     char_u	*s = NULL;
-    char_u	*tofree = NULL;
-    char_u	numbuf[NUMBUFLEN];
+    size_t	slen = 0;
+    size_t	IObufflen;
 
     if (rettv != NULL)
+    {
+	char_u	*tofree = NULL;
+	char_u	numbuf[NUMBUFLEN];
+
 	s = echo_string((typval_T *)rettv, &tofree, numbuf, 0);
+	vim_free(tofree);
+    }
     if (s == NULL)
 	s = (char_u *)"";
+    else
+	slen = STRLEN(s);
 
     STRCPY(IObuff, ":return ");
     STRNCPY(IObuff + 8, s, IOSIZE - 8);
-    if (STRLEN(s) + 8 >= IOSIZE)
+    IObufflen = 8 + slen;
+    if (slen + 8 >= IOSIZE)
+    {
 	STRCPY(IObuff + IOSIZE - 4, "...");
-    vim_free(tofree);
-    return vim_strsave(IObuff);
+	IObufflen += 3;
+    }
+    return vim_strnsave(IObuff, IObufflen);
 }
 
 /*