patch 9.1.0973: too many strlen() calls in fileio.c

Problem:  too many strlen() calls in fileio.c
Solution: refactor fileio.c and remove calls to STRLEN(),
          check for out-of-memory condition in buf_check_timestamp()
          (John Marriott)

closes: #16306

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 41e5b5d..2f57104 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -55,17 +55,23 @@
 {
     int		msg_scroll_save;
     int		prev_msg_col = msg_col;
+    size_t	len;
 
     if (msg_silent != 0)
 	return;
     msg_add_fname(buf, name);	    // put file name in IObuff with quotes
 
     // If it's extremely long, truncate it.
-    if (STRLEN(IObuff) > IOSIZE - 100)
-	IObuff[IOSIZE - 100] = NUL;
+    len = STRLEN(IObuff);
+    if (len > IOSIZE - 100)
+    {
+	len = IOSIZE - 100;
+	IObuff[len] = NUL;
+    }
 
     // Avoid an over-long translation to cause trouble.
-    STRNCAT(IObuff, s, 99);
+    if (*s != NUL)
+	STRNCPY(IObuff + len, s, 99);
 
     /*
      * For the first message may have to start a new line.
@@ -224,6 +230,7 @@
 #ifdef FEAT_SODIUM
     int		may_need_lseek = FALSE;
 #endif
+    size_t	fnamelen = 0;
 
     curbuf->b_au_did_filetype = FALSE; // reset before triggering any autocommands
 
@@ -336,10 +343,10 @@
 
     if (fname != NULL && *fname != NUL)
     {
-	size_t namelen = STRLEN(fname);
+	fnamelen = STRLEN(fname);
 
 	// If the name is too long we might crash further on, quit here.
-	if (namelen >= MAXPATHL)
+	if (fnamelen >= MAXPATHL)
 	{
 	    filemess(curbuf, fname, (char_u *)_("Illegal file name"), 0);
 	    msg_end();
@@ -350,7 +357,7 @@
 	// If the name ends in a path separator, we can't open it.  Check here,
 	// because reading the file may actually work, but then creating the
 	// swap file may destroy it!  Reported on MS-DOS and Win 95.
-	if (after_pathsep(fname, fname + namelen))
+	if (after_pathsep(fname, fname + fnamelen))
 	{
 	    filemess(curbuf, fname, (char_u *)_(msg_is_a_directory), 0);
 	    msg_end();
@@ -776,11 +783,13 @@
 		// Also write a message in the GUI window, if there is one.
 		if (gui.in_use && !gui.dying && !gui.starting)
 		{
+		    size_t  plen = STRLEN(_("Reading from stdin..."));
+
 		    // make a copy, gui_write() may try to change it
-		    p = vim_strsave((char_u *)_("Reading from stdin..."));
+		    p = vim_strnsave((char_u *)_("Reading from stdin..."), plen);
 		    if (p != NULL)
 		    {
-			gui_write(p, (int)STRLEN(p));
+			gui_write(p, (int)plen);
 			vim_free(p);
 		    }
 		}
@@ -838,7 +847,7 @@
 	c = enc_utf8;
 	if (!c && !read_stdin)
 	{
-	    fc = fname[STRLEN(fname) - 1];
+	    fc = fname[fnamelen - 1];
 	    if (TOLOWER_ASC(fc) == 'x')
 	    {
 		// Read the first line (and a bit more).  Immediately rewind to
@@ -1001,7 +1010,6 @@
     converted = need_conversion(fenc);
     if (converted)
     {
-
 	// "ucs-bom" means we need to check the first bytes of the file
 	// for a BOM.
 	if (STRCMP(fenc, ENC_UCSBOM) == 0)
@@ -2481,31 +2489,38 @@
 
 	if (!filtering && !(flags & READ_DUMMY))
 	{
+	    int	buflen;
+
 	    msg_add_fname(curbuf, sfname);   // fname in IObuff with quotes
 	    c = FALSE;
 
+	    buflen = (int)STRLEN(IObuff);
 #ifdef UNIX
 	    if (S_ISFIFO(perm))			    // fifo
 	    {
-		STRCAT(IObuff, _("[fifo]"));
+		buflen += vim_snprintf((char *)IObuff + buflen, IOSIZE - buflen,
+			_("[fifo]"));
 		c = TRUE;
 	    }
 	    if (S_ISSOCK(perm))			    // or socket
 	    {
-		STRCAT(IObuff, _("[socket]"));
+		buflen += vim_snprintf((char *)IObuff + buflen, IOSIZE - buflen,
+			_("[socket]"));
 		c = TRUE;
 	    }
 # ifdef OPEN_CHR_FILES
 	    if (S_ISCHR(perm))			    // or character special
 	    {
-		STRCAT(IObuff, _("[character special]"));
+		buflen += vim_snprintf((char *)IObuff + buflen, IOSIZE - buflen,
+			_("[character special]"));
 		c = TRUE;
 	    }
 # endif
 #endif
 	    if (curbuf->b_p_ro)
 	    {
-		STRCAT(IObuff, shortmess(SHM_RO) ? _("[RO]") : _("[readonly]"));
+		buflen += vim_snprintf((char *)IObuff + buflen, IOSIZE - buflen,
+			"%s", shortmess(SHM_RO) ? _("[RO]") : _("[readonly]"));
 		c = TRUE;
 	    }
 	    if (read_no_eol_lnum)
@@ -2515,22 +2530,26 @@
 	    }
 	    if (ff_error == EOL_DOS)
 	    {
-		STRCAT(IObuff, _("[CR missing]"));
+		buflen += vim_snprintf((char *)IObuff + buflen, IOSIZE - buflen,
+			_("CR missing"));
 		c = TRUE;
 	    }
 	    if (split)
 	    {
-		STRCAT(IObuff, _("[long lines split]"));
+		buflen += vim_snprintf((char *)IObuff + buflen, IOSIZE - buflen,
+			_("[long lines split]"));
 		c = TRUE;
 	    }
 	    if (notconverted)
 	    {
-		STRCAT(IObuff, _("[NOT converted]"));
+		buflen += vim_snprintf((char *)IObuff + buflen, IOSIZE - buflen,
+			_("[NOT converted]"));
 		c = TRUE;
 	    }
 	    else if (converted)
 	    {
-		STRCAT(IObuff, _("[converted]"));
+		buflen += vim_snprintf((char *)IObuff + buflen, IOSIZE - buflen,
+			_("[converted]"));
 		c = TRUE;
 	    }
 #ifdef FEAT_CRYPT
@@ -2542,19 +2561,20 @@
 #endif
 	    if (conv_error != 0)
 	    {
-		sprintf((char *)IObuff + STRLEN(IObuff),
-		       _("[CONVERSION ERROR in line %ld]"), (long)conv_error);
+		vim_snprintf((char *)IObuff + buflen, IOSIZE - buflen,
+			_("[CONVERSION ERROR in line %ld]"), (long)conv_error);
 		c = TRUE;
 	    }
 	    else if (illegal_byte > 0)
 	    {
-		sprintf((char *)IObuff + STRLEN(IObuff),
-			 _("[ILLEGAL BYTE in line %ld]"), (long)illegal_byte);
+		vim_snprintf((char *)IObuff + buflen, IOSIZE - buflen,
+			_("[ILLEGAL BYTE in line %ld]"), (long)illegal_byte);
 		c = TRUE;
 	    }
 	    else if (error)
 	    {
-		STRCAT(IObuff, _("[READ ERRORS]"));
+		vim_snprintf((char *)IObuff + buflen, IOSIZE - buflen,
+			_("[READ ERRORS]"));
 		c = TRUE;
 	    }
 	    if (msg_add_fileformat(fileformat))
@@ -3162,22 +3182,17 @@
     long    lnum,
     off_T   nchars)
 {
-    char_u  *p;
+    int  len = (int)STRLEN(IObuff);
 
-    p = IObuff + STRLEN(IObuff);
-
-    if (insert_space)
-	*p++ = ' ';
     if (shortmess(SHM_LINES))
-	vim_snprintf((char *)p, IOSIZE - (p - IObuff),
-		"%ldL, %lldB", lnum, (varnumber_T)nchars);
+	vim_snprintf((char *)IObuff + len, IOSIZE - (size_t)len,
+		"%s%ldL, %lldB", insert_space ? " " : "", lnum, (varnumber_T)nchars);
     else
     {
-	sprintf((char *)p, NGETTEXT("%ld line, ", "%ld lines, ", lnum), lnum);
-	p += STRLEN(p);
-	vim_snprintf((char *)p, IOSIZE - (p - IObuff),
-		NGETTEXT("%lld byte", "%lld bytes", nchars),
-		(varnumber_T)nchars);
+	len += vim_snprintf((char *)IObuff + len, IOSIZE - (size_t)len,
+		NGETTEXT("%s%ld line, ", "%s%ld lines, ", lnum), insert_space ? " " : "", lnum);
+	vim_snprintf((char *)IObuff + len, IOSIZE - (size_t)len,
+		NGETTEXT("%lld byte", "%lld bytes", nchars), (varnumber_T)nchars);
     }
 }
 
@@ -3585,6 +3600,7 @@
     char_u	*s;
     char_u	*e;
     char_u	*ptr;
+    size_t	ptrlen;
     int		fnamelen, extlen;
 
     extlen = (int)STRLEN(ext);
@@ -3642,10 +3658,14 @@
     }
 
     // the file name has at most BASENAMELEN characters.
-    if (STRLEN(ptr) > (unsigned)BASENAMELEN)
-	ptr[BASENAMELEN] = '\0';
+    ptrlen = (size_t)(fnamelen - (ptr - retval));
+    if (ptrlen > (unsigned)BASENAMELEN)
+    {
+	ptrlen = BASENAMELEN;
+	ptr[ptrlen] = NUL;
+    }
 
-    s = ptr + STRLEN(ptr);
+    s = ptr + ptrlen;
 
     /*
      * For 8.3 file names we may have to reduce the length.
@@ -3685,7 +3705,7 @@
 	 * If the extension doesn't start with '.', and there already is an
 	 * extension, it may need to be truncated
 	 */
-	else if ((int)STRLEN(e) + extlen > 4)
+	else if ((int)(ptrlen - (e - retval)) + extlen > 4)
 	    s = e + 4 - extlen;
     }
 #ifdef MSWIN
@@ -3703,13 +3723,12 @@
      * ext can start with '.' and cannot exceed 3 more characters.
      */
     STRCPY(s, ext);
-
     /*
      * Prepend the dot.
      */
     if (prepend_dot && !shortname && *(e = gettail(retval)) != '.')
     {
-	STRMOVE(e + 1, e);
+	mch_memmove(e + 1, e, (size_t)(((fnamelen + extlen) - (e - retval)) + 1));	// +1 for NUL
 	*e = '.';
     }
 
@@ -4111,7 +4130,7 @@
     curbuf = tobuf;
     for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; ++lnum)
     {
-	p = vim_strsave(ml_get_buf(frombuf, lnum, FALSE));
+	p = vim_strnsave(ml_get_buf(frombuf, lnum, FALSE), ml_get_buf_len(frombuf, lnum));
 	if (p == NULL || ml_append(lnum - 1, p, 0, FALSE) == FAIL)
 	{
 	    vim_free(p);
@@ -4154,8 +4173,6 @@
     stat_T	st;
     int		stat_res;
     int		retval = 0;
-    char_u	*path;
-    char	*tbuf;
     char	*mesg = NULL;
     char	*mesg2 = "";
     int		helpmesg = FALSE;
@@ -4164,7 +4181,6 @@
 	RELOAD_NORMAL,
 	RELOAD_DETECT
     }		reload = RELOAD_NONE;
-    char	*reason;
 #if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG)
     int		can_reload = FALSE;
 #endif
@@ -4175,9 +4191,6 @@
 #endif
     static int	busy = FALSE;
     int		n;
-#ifdef FEAT_EVAL
-    char_u	*s;
-#endif
     bufref_T	bufref;
 
     set_bufref(&bufref, buf);
@@ -4242,21 +4255,51 @@
 	    reload = RELOAD_NORMAL;
 	else
 	{
+	    char    *reason;
+#ifdef FEAT_EVAL
+	    size_t  reasonlen;
+#endif
+
 	    if (stat_res < 0)
+	    {
 		reason = "deleted";
+#ifdef FEAT_EVAL
+		reasonlen = STRLEN_LITERAL("deleted");
+#endif
+	    }
 	    else if (bufIsChanged(buf))
+	    {
 		reason = "conflict";
+#ifdef FEAT_EVAL
+		reasonlen = STRLEN_LITERAL("conflict");
+#endif
+	    }
 	    /*
 	     * Check if the file contents really changed to avoid giving a
 	     * warning when only the timestamp was set (e.g., checked out of
 	     * CVS).  Always warn when the buffer was changed.
 	     */
 	    else if (orig_size != buf->b_orig_size || buf_contents_changed(buf))
+	    {
 		reason = "changed";
+#ifdef FEAT_EVAL
+		reasonlen = STRLEN_LITERAL("changed");
+#endif
+	    }
 	    else if (orig_mode != buf->b_orig_mode)
+	    {
 		reason = "mode";
+#ifdef FEAT_EVAL
+		reasonlen = STRLEN_LITERAL("mode");
+#endif
+	    }
 	    else
+	    {
 		reason = "time";
+#ifdef FEAT_EVAL
+		reasonlen = STRLEN_LITERAL("time");
+#endif
+	    }
 
 	    /*
 	     * Only give the warning if there are no FileChangedShell
@@ -4265,8 +4308,8 @@
 	     */
 	    busy = TRUE;
 #ifdef FEAT_EVAL
-	    set_vim_var_string(VV_FCS_REASON, (char_u *)reason, -1);
-	    set_vim_var_string(VV_FCS_CHOICE, (char_u *)"", -1);
+	    set_vim_var_string(VV_FCS_REASON, (char_u *)reason, reasonlen);
+	    set_vim_var_string(VV_FCS_CHOICE, (char_u *)"", 0);
 #endif
 	    ++allbuf_lock;
 	    n = apply_autocmds(EVENT_FILECHANGEDSHELL,
@@ -4278,7 +4321,7 @@
 		if (!bufref_valid(&bufref))
 		    emsg(_(e_filechangedshell_autocommand_deleted_buffer));
 #ifdef FEAT_EVAL
-		s = get_vim_var_str(VV_FCS_CHOICE);
+		char_u	*s = get_vim_var_str(VV_FCS_CHOICE);
 		if (STRCMP(s, "reload") == 0 && *reason != 'd')
 		    reload = RELOAD_NORMAL;
 		else if (STRCMP(s, "edit") == 0)
@@ -4343,80 +4386,86 @@
 
     if (mesg != NULL)
     {
+	char_u *path;
+
 	path = home_replace_save(buf, buf->b_fname);
 	if (path != NULL)
 	{
+	    size_t  tbufsize;
+	    char    *tbuf;
+
 	    if (!helpmesg)
 		mesg2 = "";
-	    tbuf = alloc(STRLEN(path) + STRLEN(mesg) + STRLEN(mesg2) + 2);
-	    sprintf(tbuf, mesg, path);
+	    tbufsize = STRLEN(mesg) + STRLEN(path) + 2 + STRLEN(mesg2) + 1; // +2 for either '\n' or "; "
+									    // and +1 for NUL
+	    tbuf = alloc(tbufsize);
+	    if (tbuf != NULL)
+	    {
+		int tbuflen;
+
+		tbuflen = vim_snprintf(tbuf, tbufsize, mesg, path);
 #ifdef FEAT_EVAL
-	    // Set warningmsg here, before the unimportant and output-specific
-	    // mesg2 has been appended.
-	    set_vim_var_string(VV_WARNINGMSG, (char_u *)tbuf, -1);
+		// Set warningmsg here, before the unimportant and output-specific
+		// mesg2 has been appended.
+		set_vim_var_string(VV_WARNINGMSG, (char_u *)tbuf, tbuflen);
 #endif
 #if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG)
-	    if (can_reload)
-	    {
-		if (*mesg2 != NUL)
+		if (can_reload)
 		{
-		    STRCAT(tbuf, "\n");
-		    STRCAT(tbuf, mesg2);
-		}
-		switch (do_dialog(VIM_WARNING, (char_u *)_("Warning"),
-			(char_u *)tbuf,
-			(char_u *)_("&OK\n&Load File\nLoad File &and Options"),
-			1, NULL, TRUE))
-		{
-		    case 2:
-			reload = RELOAD_NORMAL;
-			break;
-		    case 3:
-			reload = RELOAD_DETECT;
-			break;
-		}
-	    }
-	    else
-#endif
-	    if (State > MODE_NORMAL_BUSY || (State & MODE_CMDLINE)
-							     || already_warned)
-	    {
-		if (*mesg2 != NUL)
-		{
-		    STRCAT(tbuf, "; ");
-		    STRCAT(tbuf, mesg2);
-		}
-		emsg(tbuf);
-		retval = 2;
-	    }
-	    else
-	    {
-		if (!autocmd_busy)
-		{
-		    msg_start();
-		    msg_puts_attr(tbuf, HL_ATTR(HLF_E) + MSG_HIST);
 		    if (*mesg2 != NUL)
-			msg_puts_attr(mesg2, HL_ATTR(HLF_W) + MSG_HIST);
-		    msg_clr_eos();
-		    (void)msg_end();
-		    if (emsg_silent == 0 && !in_assert_fails)
+			vim_snprintf(tbuf + tbuflen, tbufsize - tbuflen, "\n%s", mesg2);
+		    switch (do_dialog(VIM_WARNING, (char_u *)_("Warning"),
+			    (char_u *)tbuf,
+			    (char_u *)_("&OK\n&Load File\nLoad File &and Options"),
+			    1, NULL, TRUE))
 		    {
-			out_flush();
-#ifdef FEAT_GUI
-			if (!focus)
-#endif
-			    // give the user some time to think about it
-			    ui_delay(1004L, TRUE);
-
-			// don't redraw and erase the message
-			redraw_cmdline = FALSE;
+			case 2:
+			    reload = RELOAD_NORMAL;
+			    break;
+			case 3:
+			    reload = RELOAD_DETECT;
+			    break;
 		    }
 		}
-		already_warned = TRUE;
+		else
+#endif
+		if (State > MODE_NORMAL_BUSY || (State & MODE_CMDLINE)
+								 || already_warned)
+		{
+		    if (*mesg2 != NUL)
+			vim_snprintf(tbuf + tbuflen, tbufsize - tbuflen, "; %s", mesg2);
+		    emsg(tbuf);
+		    retval = 2;
+		}
+		else
+		{
+		    if (!autocmd_busy)
+		    {
+			msg_start();
+			msg_puts_attr(tbuf, HL_ATTR(HLF_E) + MSG_HIST);
+			if (*mesg2 != NUL)
+			    msg_puts_attr(mesg2, HL_ATTR(HLF_W) + MSG_HIST);
+			msg_clr_eos();
+			(void)msg_end();
+			if (emsg_silent == 0 && !in_assert_fails)
+			{
+			    out_flush();
+    #ifdef FEAT_GUI
+			    if (!focus)
+    #endif
+				// give the user some time to think about it
+				ui_delay(1004L, TRUE);
+
+			    // don't redraw and erase the message
+			    redraw_cmdline = FALSE;
+			}
+		    }
+		    already_warned = TRUE;
+		}
 	    }
 
-	    vim_free(path);
 	    vim_free(tbuf);
+	    vim_free(path);
 	}
     }
 
@@ -4696,19 +4745,19 @@
     {
 	if (tag == IO_REPARSE_TAG_MOUNT_POINT)
 	    return (char_u*)"junction";
-	else if (tag == IO_REPARSE_TAG_SYMLINK)
+	if (tag == IO_REPARSE_TAG_SYMLINK)
 	{
 	    if (flag & FILE_ATTRIBUTE_DIRECTORY)
 		return (char_u*)"linkd";
-	    else
-		return (char_u*)"link";
+
+	    return (char_u*)"link";
 	}
 	return (char_u*)"reparse";	// unknown reparse point type
     }
     if (flag & FILE_ATTRIBUTE_DIRECTORY)
 	return (char_u*)"dir";
-    else
-	return (char_u*)"file";
+
+    return (char_u*)"file";
 }
 
     static dict_T *
@@ -4793,7 +4842,6 @@
 	ret = mch_stat(p, &st);
 	if (ret < 0)
 	    q = (char_u*)"link";
-
     }
     vim_free(p);
 
@@ -4876,10 +4924,10 @@
     name2 = dict_get_string(*(dict_T**)p2, "name", FALSE);
     if (readdirex_sort == READDIR_SORT_BYTE)
 	return STRCMP(name1, name2);
-    else if (readdirex_sort == READDIR_SORT_IC)
+    if (readdirex_sort == READDIR_SORT_IC)
 	return STRICMP(name1, name2);
-    else
-	return STRCOLL(name1, name2);
+
+    return STRCOLL(name1, name2);
 }
 
     static int
@@ -4887,10 +4935,10 @@
 {
     if (readdirex_sort == READDIR_SORT_BYTE)
 	return STRCMP(*(char **)s1, *(char **)s2);
-    else if (readdirex_sort == READDIR_SORT_IC)
+    if (readdirex_sort == READDIR_SORT_IC)
 	return STRICMP(*(char **)s1, *(char **)s2);
-    else
-	return STRCOLL(*(char **)s1, *(char **)s2);
+
+    return STRCOLL(*(char **)s1, *(char **)s2);
 }
 #endif
 
@@ -4919,6 +4967,7 @@
     HANDLE		hFind = INVALID_HANDLE_VALUE;
     WIN32_FIND_DATAW    wfd;
     WCHAR		*wn = NULL;	// UTF-16 name, NULL when not used.
+    char_u		*p_end;
 # else
     DIR			*dirp;
     struct dirent	*dp;
@@ -4944,11 +4993,11 @@
     if (buf == NULL)
 	return FAIL;
     STRNCPY(buf, path, MAXPATHL-5);
-    p = buf + STRLEN(buf);
+    p = p_end = buf + STRLEN(buf);
     MB_PTR_BACK(buf, p);
     if (*p == '\\' || *p == '/')
-	*p = NUL;
-    STRCAT(p, "\\*");
+	p_end = p;
+    STRCPY(p_end, "\\*");
 
     wn = enc_to_utf16(buf, NULL);
     if (wn != NULL)
@@ -5118,9 +5167,6 @@
 delete_recursive(char_u *name)
 {
     int result = 0;
-    int		i;
-    char_u	*exp;
-    garray_T	ga;
 
     // A symbolic link to a directory itself is deleted, not the directory it
     // points to.
@@ -5132,15 +5178,19 @@
 # endif
 	    )
     {
-	exp = vim_strsave(name);
+	char_u	    *exp = vim_strsave(name);
+	garray_T    ga;
+
 	if (exp == NULL)
 	    return -1;
 	if (readdir_core(&ga, exp, FALSE, NULL, NULL, READDIR_SORT_NONE) == OK)
 	{
+	    int	len = vim_snprintf((char *)NameBuff, MAXPATHL, "%s/", exp);
+	    int	i;
+
 	    for (i = 0; i < ga.ga_len; ++i)
 	    {
-		vim_snprintf((char *)NameBuff, MAXPATHL, "%s/%s", exp,
-					    ((char_u **)ga.ga_data)[i]);
+		vim_snprintf((char *)NameBuff + len, MAXPATHL - len, "%s", ((char_u **)ga.ga_data)[i]);
 		if (delete_recursive(NameBuff) != 0)
 		    // Remember the failure but continue deleting any further
 		    // entries.
@@ -5226,6 +5276,7 @@
 vim_settempdir(char_u *tempdir)
 {
     char_u	*buf;
+    size_t	buflen;
 
     buf = alloc(MAXPATHL + 2);
     if (buf == NULL)
@@ -5233,8 +5284,13 @@
 
     if (vim_FullName(tempdir, buf, MAXPATHL, FALSE) == FAIL)
 	STRCPY(buf, tempdir);
-    add_pathsep(buf);
-    vim_tempdir = vim_strsave(buf);
+    buflen = STRLEN(buf);
+    if (!after_pathsep(buf, buf + buflen))
+    {
+	STRCPY(buf + buflen, PATHSEPSTR);
+	buflen += STRLEN_LITERAL(PATHSEPSTR);
+    }
+    vim_tempdir = vim_strnsave(buf, buflen);
 # if defined(UNIX) && defined(HAVE_FLOCK) && defined(HAVE_DIRFD)
     vim_opentempdir();
 # endif
@@ -5286,7 +5342,6 @@
 	for (i = 0; i < (int)ARRAY_LENGTH(tempdirs); ++i)
 	{
 # ifndef HAVE_MKDTEMP
-	    size_t	itmplen;
 	    long	nr;
 	    long	off;
 # endif
@@ -5296,8 +5351,14 @@
 	    expand_env((char_u *)tempdirs[i], itmp, TEMPNAMELEN - 20);
 	    if (itmp[0] != '$' && mch_isdir(itmp))
 	    {
+		size_t	itmplen = STRLEN(itmp);
+
 		// directory exists
-		add_pathsep(itmp);
+		if (!after_pathsep(itmp, itmp + itmplen))
+		{
+		    STRCPY(itmp + itmplen, PATHSEPSTR);
+		    itmplen += STRLEN_LITERAL(PATHSEPSTR);
+		}
 
 # ifdef HAVE_MKDTEMP
 		{
@@ -5307,7 +5368,8 @@
 		    mode_t	umask_save = umask(077);
 #  endif
 		    // Leave room for filename
-		    STRCAT(itmp, "vXXXXXX");
+		    STRCPY(itmp + itmplen, "vXXXXXX");
+		    itmplen += STRLEN_LITERAL("vXXXXXX");
 		    if (mkdtemp((char *)itmp) != NULL)
 			vim_settempdir(itmp);
 #  if defined(UNIX) || defined(VMS)
@@ -5320,7 +5382,6 @@
 		// otherwise it doesn't matter.  The use of mkdir() avoids any
 		// security problems because of the predictable number.
 		nr = (mch_get_pid() + (long)time(NULL)) % 1000000L;
-		itmplen = STRLEN(itmp);
 
 		// Try up to 10000 different values until we find a name that
 		// doesn't exist.
@@ -5331,7 +5392,7 @@
 		    mode_t	umask_save;
 #  endif
 
-		    sprintf((char *)itmp + itmplen, "v%ld", nr + off);
+		    vim_snprintf((char *)itmp + itmplen, sizeof(itmp) - itmplen, "v%ld", nr + off);
 #  ifndef EEXIST
 		    // If mkdir() does not set errno to EEXIST, check for
 		    // existing file here.  There is a race condition then,
@@ -5372,20 +5433,20 @@
     {
 	// There is no need to check if the file exists, because we own the
 	// directory and nobody else creates a file in it.
-	sprintf((char *)itmp, "%s%ld", vim_tempdir, temp_count++);
-	return vim_strsave(itmp);
+	int itmplen = vim_snprintf((char *)itmp, sizeof(itmp), "%s%ld", vim_tempdir, temp_count++);
+	return vim_strnsave(itmp, (size_t)itmplen);
     }
 
     return NULL;
 
 #else // TEMPDIRNAMES
+    char_u	*p;
 
 # ifdef MSWIN
     WCHAR	wszTempFile[_MAX_PATH + 1];
     WCHAR	buf4[4];
     WCHAR	*chartab = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
     char_u	*retval;
-    char_u	*p;
     char_u	*shname;
     long	i;
 
@@ -5405,7 +5466,7 @@
     if (GetTempFileNameW(wszTempFile, buf4, 0, itmp) == 0)
 	return NULL;
     if (!keep)
-	// GetTempFileName() will create the file, we don't want that
+	// GetTempFileNameW() will create the file, we don't want that
 	(void)DeleteFileW(itmp);
 
     // Backslashes in a temp file name cause problems when filtering with
@@ -5422,34 +5483,32 @@
     return retval;
 
 # else // MSWIN
+    int		itmplen;
 
 #  ifdef USE_TMPNAM
-    char_u	*p;
-
     // tmpnam() will make its own name
     p = tmpnam((char *)itmp);
     if (p == NULL || *p == NUL)
 	return NULL;
 #  else
-    char_u	*p;
 
 #   ifdef VMS_TEMPNAM
     // mktemp() is not working on VMS.  It seems to be
     // a do-nothing function. Therefore we use tempnam().
-    sprintf((char *)itmp, "VIM%c", extra_char);
+    vim_snprintf((char *)itmp, sizeof(itmp), "VIM%c", extra_char);
     p = (char_u *)tempnam("tmp:", (char *)itmp);
     if (p != NULL)
     {
 	// VMS will use '.LIS' if we don't explicitly specify an extension,
 	// and VIM will then be unable to find the file later
-	STRCPY(itmp, p);
-	STRCAT(itmp, ".txt");
+	itmplen = vim_snprintf((char *)itmp, sizeof(itmp), "%s.txt", p);
 	free(p);
     }
     else
 	return NULL;
 #   else
     STRCPY(itmp, TEMPNAME);
+    itmplen = STRLEN_LITERAL(TEMPNAME);
     if ((p = vim_strchr(itmp, '?')) != NULL)
 	*p = extra_char;
     if (mktemp((char *)itmp) == NULL)
@@ -5457,7 +5516,7 @@
 #   endif
 #  endif
 
-    return vim_strsave(itmp);
+    return vim_strnsave(itmp, (size_t)itmplen);
 # endif // MSWIN
 #endif // TEMPDIRNAMES
 }