patch 9.1.1515: Coverity complains about potential unterminated strings

Problem:    memcpy() in ExpandRTDir_int() may create unterminated strings
Solution:   Use vim_snprintf() to construct complete paths safely
            (glepnir).

related: neovim/neovim#34795
closes: #17669

Signed-off-by: glepnir <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/scriptfile.c b/src/scriptfile.c
index 856d00f..9535cef 100644
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -1128,43 +1128,51 @@
 {
     for (int i = 0; dirnames[i] != NULL; ++i)
     {
-	size_t		buf_len = STRLEN(dirnames[i]) + pat_len + 22;
+	const size_t	buf_len = STRLEN(dirnames[i]) + pat_len + 64;
 	char		*buf = alloc(buf_len);
 	if (buf == NULL)
 	{
 	    ga_clear_strings(gap);
 	    return;
 	}
-	char		*tail = buf + 15;
-	size_t		tail_buflen = buf_len - 15;
 	int		glob_flags = 0;
 	int		expand_dirs = FALSE;
 
-	if (*(dirnames[i]) == NUL)  // empty dir used for :runtime
-	    vim_snprintf(tail, tail_buflen, "%s*.vim", pat);
-	else
-	    vim_snprintf(tail, tail_buflen, "%s/%s*.vim", dirnames[i], pat);
+	// Build base pattern
+	vim_snprintf(buf, buf_len, "%s%s%s%s",
+		     *dirnames[i] ? dirnames[i] : "", *dirnames[i] ? "/" : "",
+		     pat, "*.vim");
 
 expand:
 	if ((flags & DIP_NORTP) == 0)
-	    globpath(p_rtp, (char_u *)tail, gap, glob_flags, expand_dirs);
+	    globpath(p_rtp, (char_u *)buf, gap, glob_flags, expand_dirs);
 
 	if (flags & DIP_START)
 	{
-	    memcpy(tail - 15, "pack/*/start/*/", 15);
-	    globpath(p_pp, (char_u *)tail - 15, gap, glob_flags, expand_dirs);
+	    // Build complete search path: pack/*/start/*/dirnames[i]/pat*.vim
+	    vim_snprintf(buf, buf_len, "pack/*/start/*/%s%s%s%s",
+			 *dirnames[i] ? dirnames[i] : "",
+			 *dirnames[i] ? "/" : "",
+			 pat,
+			 expand_dirs ? "*" : "*.vim");
+	    globpath(p_pp, (char_u *)buf, gap, glob_flags, expand_dirs);
 	}
 
 	if (flags & DIP_OPT)
 	{
-	    memcpy(tail - 13, "pack/*/opt/*/", 13);
-	    globpath(p_pp, (char_u *)tail - 13, gap, glob_flags, expand_dirs);
+	    // Build complete search path: pack/*/opt/*/dirnames[i]/pat*.vim
+	    vim_snprintf(buf, buf_len, "pack/*/opt/*/%s%s%s%s",
+			 *dirnames[i] ? dirnames[i] : "",
+			 *dirnames[i] ? "/" : "", pat,
+			 expand_dirs ? "*" : "*.vim");
+	    globpath(p_pp, (char_u *)buf, gap, glob_flags, expand_dirs);
 	}
 
-	if (*(dirnames[i]) == NUL && !expand_dirs)
+	// Second round for directories
+	if (*dirnames[i] == NUL && !expand_dirs)
 	{
 	    // expand dir names in another round
-	    vim_snprintf(tail, tail_buflen, "%s*", pat);
+	    vim_snprintf(buf, buf_len, "%s*", pat);
 	    glob_flags = WILD_ADD_SLASH;
 	    expand_dirs = TRUE;
 	    goto expand;
@@ -1175,8 +1183,10 @@
 
     int pat_pathsep_cnt = 0;
     for (size_t i = 0; i < pat_len; ++i)
+    {
 	if (vim_ispathsep(pat[i]))
 	    ++pat_pathsep_cnt;
+    }
 
     for (int i = 0; i < gap->ga_len; ++i)
     {
@@ -1191,9 +1201,11 @@
 
 	int match_pathsep_cnt = (e > s && e[-1] == '/') ? -1 : 0;
 	for (s = e; s > match; MB_PTR_BACK(match, s))
+	{
 	    if (s < match || (vim_ispathsep(*s)
 				     && ++match_pathsep_cnt > pat_pathsep_cnt))
 		break;
+	}
 	++s;
 	if (s != match)
 	    mch_memmove(match, s, e - s + 1);