patch 9.1.1467: too many strlen() calls

Problem:  too many strlen() calls
Solution: Change expand_env() to return string length
          (John Marriott)

This commit does the following changes:
- In expand_env_esc():
  - return the length of the returned dst string.
  - refactor to remove some calls to STRLEN() and STRCAT()
  - add check for out-of-memory condition.
- Change call sites in various source files to use the return value

closes: #17561

Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/fileio.c b/src/fileio.c
index 2c6c40e..c87ae4d 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -5351,14 +5351,13 @@
 	    long	nr;
 	    long	off;
 # endif
+	    size_t	itmplen;
 
 	    // Expand $TMP, leave room for "/v1100000/999999999".
 	    // Skip the directory check if the expansion fails.
-	    expand_env((char_u *)tempdirs[i], itmp, TEMPNAMELEN - 20);
+	    itmplen = expand_env((char_u *)tempdirs[i], itmp, TEMPNAMELEN - 20);
 	    if (itmp[0] != '$' && mch_isdir(itmp))
 	    {
-		size_t	itmplen = STRLEN(itmp);
-
 		// directory exists
 		if (!after_pathsep(itmp, itmp + itmplen))
 		{
diff --git a/src/findfile.c b/src/findfile.c
index 49def2f..0f5f2dc 100644
--- a/src/findfile.c
+++ b/src/findfile.c
@@ -1820,11 +1820,10 @@
 	// copy file name into NameBuff, expanding environment variables
 	save_char = ptr[len];
 	ptr[len] = NUL;
-	expand_env_esc(ptr, NameBuff, MAXPATHL, FALSE, TRUE, NULL);
+	file_to_findlen = expand_env_esc(ptr, NameBuff, MAXPATHL, FALSE, TRUE, NULL);
 	ptr[len] = save_char;
 
 	vim_free(*file_to_find);
-	file_to_findlen = STRLEN(NameBuff);
 	*file_to_find = vim_strnsave(NameBuff, file_to_findlen);
 	if (*file_to_find == NULL)	// out of memory
 	{
diff --git a/src/if_cscope.c b/src/if_cscope.c
index 6b0f920..86bfbd8 100644
--- a/src/if_cscope.c
+++ b/src/if_cscope.c
@@ -548,8 +548,7 @@
     if ((fname = alloc(MAXPATHL + 1)) == NULL)
 	goto add_err;
 
-    expand_env((char_u *)arg1, (char_u *)fname, MAXPATHL);
-    len = STRLEN(fname);
+    len = expand_env((char_u *)arg1, (char_u *)fname, MAXPATHL);
     fbuf = (char_u *)fname;
     (void)modify_fname((char_u *)":p", FALSE, &usedlen,
 					      (char_u **)&fname, &fbuf, &len);
@@ -829,6 +828,7 @@
     int		cmdlen;
     int		len;
     char	*prog, *cmd, *ppath = NULL;
+    size_t	proglen;
 #ifdef MSWIN
     int		fd;
     SECURITY_ATTRIBUTES sa;
@@ -916,10 +916,10 @@
 	    goto err_closing;
 #endif
 	}
-	expand_env(p_csprg, (char_u *)prog, MAXPATHL);
+	proglen = expand_env(p_csprg, (char_u *)prog, MAXPATHL);
 
 	// alloc space to hold the cscope command
-	cmdlen = (int)(strlen(prog) + strlen(csinfo[i].fname) + 32);
+	cmdlen = (int)(proglen + strlen(csinfo[i].fname) + 32);
 	if (csinfo[i].ppath)
 	{
 	    // expand the prepend path for env var's
@@ -933,9 +933,7 @@
 		goto err_closing;
 #endif
 	    }
-	    expand_env((char_u *)csinfo[i].ppath, (char_u *)ppath, MAXPATHL);
-
-	    cmdlen += (int)strlen(ppath);
+	    cmdlen += (int)expand_env((char_u *)csinfo[i].ppath, (char_u *)ppath, MAXPATHL);
 	}
 
 	if (csinfo[i].flags)
diff --git a/src/mark.c b/src/mark.c
index 2b03919..9bab352 100644
--- a/src/mark.c
+++ b/src/mark.c
@@ -540,10 +540,9 @@
 #endif
 		))
     {
-	int len;
+	size_t len;
 
-	expand_env((char_u *)"~/", NameBuff, MAXPATHL);
-	len = (int)STRLEN(NameBuff);
+	len = expand_env((char_u *)"~/", NameBuff, MAXPATHL);
 	vim_strncpy(NameBuff + len, fm->fname + 2, MAXPATHL - len - 1);
     }
     else
diff --git a/src/misc1.c b/src/misc1.c
index 142a616..4571ff2 100644
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -1401,16 +1401,16 @@
  * Skips over "\ ", "\~" and "\$" (not for Win32 though).
  * If anything fails no expansion is done and dst equals src.
  */
-    void
+    size_t
 expand_env(
     char_u	*src,		// input string e.g. "$HOME/vim.hlp"
     char_u	*dst,		// where to put the result
     int		dstlen)		// maximum length of the result
 {
-    expand_env_esc(src, dst, dstlen, FALSE, FALSE, NULL);
+    return expand_env_esc(src, dst, dstlen, FALSE, FALSE, NULL);
 }
 
-    void
+    size_t
 expand_env_esc(
     char_u	*srcp,		// input string e.g. "$HOME/vim.hlp"
     char_u	*dst,		// where to put the result
@@ -1427,6 +1427,7 @@
     int		mustfree;	// var was allocated, need to free it later
     int		at_start = TRUE; // at start of a name
     int		startstr_len = 0;
+    char_u	*dst_start = dst;
 
     if (startstr != NULL)
 	startstr_len = (int)STRLEN(startstr);
@@ -1577,6 +1578,7 @@
 		 */
 		{
 		    char_u	test[MAXPATHL], paths[MAXPATHL];
+		    size_t	testlen;
 		    char_u	*path, *next_path, *ptr;
 		    stat_T	st;
 
@@ -1588,14 +1590,20 @@
 				next_path++);
 			if (*next_path)
 			    *next_path++ = NUL;
-			STRCPY(test, path);
-			STRCAT(test, "/");
-			STRCAT(test, dst + 1);
+			testlen = vim_snprintf_safelen(
+			    (char *)test,
+			    sizeof(test),
+			    "%s/%s",
+			    path,
+			    dst + 1);
 			if (mch_stat(test, &st) == 0)
 			{
-			    var = alloc(STRLEN(test) + 1);
-			    STRCPY(var, test);
-			    mustfree = TRUE;
+			    var = alloc(testlen + 1);
+			    if (var != NULL)
+			    {
+				STRCPY(var, test);
+				mustfree = TRUE;
+			    }
 			    break;
 			}
 		    }
@@ -1641,23 +1649,26 @@
 		}
 	    }
 
-	    if (var != NULL && *var != NUL
-		    && (STRLEN(var) + STRLEN(tail) + 1 < (unsigned)dstlen))
+	    if (var != NULL && *var != NUL)
 	    {
-		STRCPY(dst, var);
-		dstlen -= (int)STRLEN(var);
 		c = (int)STRLEN(var);
-		// if var[] ends in a path separator and tail[] starts
-		// with it, skip a character
-		if (after_pathsep(dst, dst + c)
+
+		if (c + STRLEN(tail) + 1 < (unsigned)dstlen)
+		{
+		    STRCPY(dst, var);
+		    dstlen -= c;
+		    // if var[] ends in a path separator and tail[] starts
+		    // with it, skip a character
+		    if (after_pathsep(dst, dst + c)
 #if defined(BACKSLASH_IN_FILENAME) || defined(AMIGA)
-			&& dst[c - 1] != ':'
+			    && dst[c - 1] != ':'
 #endif
-			&& vim_ispathsep(*tail))
-		    ++tail;
-		dst += c;
-		src = tail;
-		copy_char = FALSE;
+			    && vim_ispathsep(*tail))
+			++tail;
+		    dst += c;
+		    src = tail;
+		    copy_char = FALSE;
+		}
 	    }
 	    if (mustfree)
 		vim_free(var);
@@ -1692,6 +1703,8 @@
 
     }
     *dst = NUL;
+
+    return (size_t)(dst - dst_start);
 }
 
 /*
diff --git a/src/proto/misc1.pro b/src/proto/misc1.pro
index 1a053e3..d7c187a 100644
--- a/src/proto/misc1.pro
+++ b/src/proto/misc1.pro
@@ -29,8 +29,8 @@
 void init_vimdir(void);
 char_u *expand_env_save(char_u *src);
 char_u *expand_env_save_opt(char_u *src, int one);
-void expand_env(char_u *src, char_u *dst, int dstlen);
-void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, int esc, int one, char_u *startstr);
+size_t expand_env(char_u *src, char_u *dst, int dstlen);
+size_t expand_env_esc(char_u *srcp, char_u *dst, int dstlen, int esc, int one, char_u *startstr);
 char_u *vim_getenv(char_u *name, int *mustfree);
 void vim_unsetenv(char_u *var);
 void vim_unsetenv_ext(char_u *var);
diff --git a/src/version.c b/src/version.c
index 0b6a3b0..168ff4e 100644
--- a/src/version.c
+++ b/src/version.c
@@ -710,6 +710,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1467,
+/**/
     1466,
 /**/
     1465,
diff --git a/src/viminfo.c b/src/viminfo.c
index 5e4caf8..489cb2d 100644
--- a/src/viminfo.c
+++ b/src/viminfo.c
@@ -99,6 +99,8 @@
 {
     if (file == NULL || *file == NUL)
     {
+	size_t  len;
+
 	if (*p_viminfofile != NUL)
 	    file = p_viminfofile;
 	else if ((file = find_viminfo_parameter('n')) == NULL || *file == NUL)
@@ -127,9 +129,12 @@
 #endif
 		file = (char_u *)VIMINFO_FILE;
 	}
-	expand_env(file, NameBuff, MAXPATHL);
+	len = expand_env(file, NameBuff, MAXPATHL);
 	file = NameBuff;
+
+	return vim_strnsave(file, len);
     }
+
     return vim_strsave(file);
 }