patch 9.1.1122: too many strlen() calls in findfile.c

Problem:  too many strlen() calls in findfile.c
Solution: refactor findfile.c and remove calls to strlen()
          (John Marriott)

closes: #16595

Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/findfile.c b/src/findfile.c
index b6ee092..ccb3ef8 100644
--- a/src/findfile.c
+++ b/src/findfile.c
@@ -66,8 +66,8 @@
 
     // the fix part (no wildcards) and the part containing the wildcards
     // of the search path
-    char_u		*ffs_fix_path;
-    char_u		*ffs_wc_path;
+    string_T		ffs_fix_path;
+    string_T		ffs_wc_path;
 
     // files/dirs found in the above directory, matched by the first wildcard
     // of wc_part
@@ -167,31 +167,31 @@
      ff_visited_list_hdr_T	*ffsc_dir_visited_list;
      ff_visited_list_hdr_T	*ffsc_visited_lists_list;
      ff_visited_list_hdr_T	*ffsc_dir_visited_lists_list;
-     char_u			*ffsc_file_to_search;
-     char_u			*ffsc_start_dir;
-     char_u			*ffsc_fix_path;
-     char_u			*ffsc_wc_path;
+     string_T			ffsc_file_to_search;
+     string_T			ffsc_start_dir;
+     string_T			ffsc_fix_path;
+     string_T			ffsc_wc_path;
      int			ffsc_level;
-     char_u			**ffsc_stopdirs_v;
+     string_T			*ffsc_stopdirs_v;
      int			ffsc_find_what;
      int			ffsc_tagfile;
 } ff_search_ctx_T;
 
 // locally needed functions
-static int ff_check_visited(ff_visited_T **, char_u *, char_u *);
+static int ff_check_visited(ff_visited_T **, char_u *, size_t, char_u *, size_t);
 static void vim_findfile_free_visited(void *search_ctx_arg);
 static void vim_findfile_free_visited_list(ff_visited_list_hdr_T **list_headp);
 static void ff_free_visited_list(ff_visited_T *vl);
-static ff_visited_list_hdr_T* ff_get_visited_list(char_u *, ff_visited_list_hdr_T **list_headp);
+static ff_visited_list_hdr_T* ff_get_visited_list(char_u *, size_t, ff_visited_list_hdr_T **list_headp);
 
 static void ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr);
 static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx);
 static void ff_clear(ff_search_ctx_T *search_ctx);
 static void ff_free_stack_element(ff_stack_T *stack_ptr);
-static ff_stack_T *ff_create_stack_element(char_u *, char_u *, int, int);
-static int ff_path_in_stoplist(char_u *, int, char_u **);
+static ff_stack_T *ff_create_stack_element(char_u *, size_t, char_u *, size_t, int, int);
+static int ff_path_in_stoplist(char_u *, int, string_T *);
 
-static char_u	*ff_expand_buffer = NULL; // used for expanding filenames
+static string_T ff_expand_buffer = {NULL, 0};	    // used for expanding filenames
 
 #if 0
 /*
@@ -283,6 +283,7 @@
 vim_findfile_init(
     char_u	*path,
     char_u	*filename,
+    size_t	filenamelen,
     char_u	*stopdirs UNUSED,
     int		level,
     int		free_visited,
@@ -294,6 +295,7 @@
     char_u		*wc_part;
     ff_stack_T		*sptr;
     ff_search_ctx_T	*search_ctx;
+    int			add_sep;
 
     // If a search context is given by the caller, reuse it, else allocate a
     // new one.
@@ -320,19 +322,20 @@
 	// filename. If no list for the current filename exists, creates a new
 	// one.
 	search_ctx->ffsc_visited_list = ff_get_visited_list(filename,
-					&search_ctx->ffsc_visited_lists_list);
+					filenamelen, &search_ctx->ffsc_visited_lists_list);
 	if (search_ctx->ffsc_visited_list == NULL)
 	    goto error_return;
 	search_ctx->ffsc_dir_visited_list = ff_get_visited_list(filename,
-				    &search_ctx->ffsc_dir_visited_lists_list);
+				    filenamelen, &search_ctx->ffsc_dir_visited_lists_list);
 	if (search_ctx->ffsc_dir_visited_list == NULL)
 	    goto error_return;
     }
 
-    if (ff_expand_buffer == NULL)
+    if (ff_expand_buffer.string == NULL)
     {
-	ff_expand_buffer = alloc(MAXPATHL);
-	if (ff_expand_buffer == NULL)
+	ff_expand_buffer.length = 0;
+	ff_expand_buffer.string = alloc(MAXPATHL);
+	if (ff_expand_buffer.string == NULL)
 	    goto error_return;
     }
 
@@ -348,13 +351,23 @@
 	if (!vim_isAbsName(rel_fname) && len + 1 < MAXPATHL)
 	{
 	    // Make the start dir an absolute path name.
-	    vim_strncpy(ff_expand_buffer, rel_fname, len);
-	    search_ctx->ffsc_start_dir = FullName_save(ff_expand_buffer, FALSE);
+	    vim_strncpy(ff_expand_buffer.string, rel_fname, len);
+	    ff_expand_buffer.length = len;
+
+	    search_ctx->ffsc_start_dir.string = FullName_save(ff_expand_buffer.string, FALSE);
+	    if (search_ctx->ffsc_start_dir.string == NULL)
+		goto error_return;
+	    search_ctx->ffsc_start_dir.length = STRLEN(search_ctx->ffsc_start_dir.string);
 	}
 	else
-	    search_ctx->ffsc_start_dir = vim_strnsave(rel_fname, len);
-	if (search_ctx->ffsc_start_dir == NULL)
-	    goto error_return;
+	{
+	    search_ctx->ffsc_start_dir.length = len;
+	    search_ctx->ffsc_start_dir.string = vim_strnsave(rel_fname,
+		search_ctx->ffsc_start_dir.length);
+	    if (search_ctx->ffsc_start_dir.string == NULL)
+		goto error_return;
+	}
+
 	if (*++path != NUL)
 	    ++path;
     }
@@ -369,17 +382,21 @@
 	    drive[0] = path[0];
 	    drive[1] = ':';
 	    drive[2] = NUL;
-	    if (vim_FullName(drive, ff_expand_buffer, MAXPATHL, TRUE) == FAIL)
+	    if (vim_FullName(drive, ff_expand_buffer.string, MAXPATHL, TRUE) == FAIL)
 		goto error_return;
 	    path += 2;
 	}
 	else
 #endif
-	if (mch_dirname(ff_expand_buffer, MAXPATHL) == FAIL)
+	if (mch_dirname(ff_expand_buffer.string, MAXPATHL) == FAIL)
 	    goto error_return;
 
-	search_ctx->ffsc_start_dir = vim_strsave(ff_expand_buffer);
-	if (search_ctx->ffsc_start_dir == NULL)
+	ff_expand_buffer.length = STRLEN(ff_expand_buffer.string);
+
+	search_ctx->ffsc_start_dir.length = ff_expand_buffer.length;
+	search_ctx->ffsc_start_dir.string = vim_strnsave(ff_expand_buffer.string,
+	    search_ctx->ffsc_start_dir.length);
+	if (search_ctx->ffsc_start_dir.string == NULL)
 	    goto error_return;
 
 #ifdef BACKSLASH_IN_FILENAME
@@ -387,8 +404,11 @@
 	// directory (but not for "//machine/dir").  Only use the drive name.
 	if ((*path == '/' || *path == '\\')
 		&& path[1] != path[0]
-		&& search_ctx->ffsc_start_dir[1] == ':')
-	    search_ctx->ffsc_start_dir[2] = NUL;
+		&& search_ctx->ffsc_start_dir.string[1] == ':')
+	{
+	    search_ctx->ffsc_start_dir.string[2] = NUL;
+	    search_ctx->ffsc_start_dir.length = 2;
+	}
 #endif
     }
 
@@ -410,10 +430,12 @@
 	    walker++;
 
 	dircount = 1;
-	search_ctx->ffsc_stopdirs_v = ALLOC_ONE(char_u *);
+	search_ctx->ffsc_stopdirs_v = ALLOC_ONE(string_T);
 
 	if (search_ctx->ffsc_stopdirs_v != NULL)
 	{
+	    string_T	*tmp;			// for convenience
+
 	    do
 	    {
 		char_u	*helper;
@@ -422,7 +444,7 @@
 
 		helper = walker;
 		ptr = vim_realloc(search_ctx->ffsc_stopdirs_v,
-					   (dircount + 1) * sizeof(char_u *));
+					   (dircount + 1) * sizeof(string_T));
 		if (ptr)
 		    search_ctx->ffsc_stopdirs_v = ptr;
 		else
@@ -431,23 +453,36 @@
 		walker = vim_strchr(walker, ';');
 		len = walker ? (size_t)(walker - helper) : STRLEN(helper);
 		// "" means ascent till top of directory tree.
+
 		if (*helper != NUL && !vim_isAbsName(helper)
 							 && len + 1 < MAXPATHL)
 		{
 		    // Make the stop dir an absolute path name.
-		    vim_strncpy(ff_expand_buffer, helper, len);
-		    search_ctx->ffsc_stopdirs_v[dircount-1] =
-					FullName_save(ff_expand_buffer, FALSE);
+		    vim_strncpy(ff_expand_buffer.string, helper, len);
+		    ff_expand_buffer.length = len;
+
+		    tmp = &search_ctx->ffsc_stopdirs_v[dircount - 1];
+		    tmp->string = FullName_save(ff_expand_buffer.string, FALSE);
+		    if (tmp->string != NULL)
+			tmp->length = STRLEN(tmp->string);
 		}
 		else
-		    search_ctx->ffsc_stopdirs_v[dircount-1] =
-						     vim_strnsave(helper, len);
+		{
+		    tmp = &search_ctx->ffsc_stopdirs_v[dircount - 1];
+		    tmp->length = len;
+		    tmp->string = vim_strnsave(helper, tmp->length);
+		    if (tmp->string == NULL)
+			tmp->length = 0;
+		}
 		if (walker)
 		    walker++;
 		dircount++;
 
 	    } while (walker != NULL);
-	    search_ctx->ffsc_stopdirs_v[dircount-1] = NULL;
+
+	    tmp = &search_ctx->ffsc_stopdirs_v[dircount - 1];
+	    tmp->string = NULL;
+	    tmp->length = 0;
 	}
     }
 
@@ -462,11 +497,14 @@
     if (wc_part != NULL)
     {
 	int	llevel;
-	int	len;
 	char	*errpt;
 
 	// save the fix part of the path
-	search_ctx->ffsc_fix_path = vim_strnsave(path, wc_part - path);
+	search_ctx->ffsc_fix_path.length = (size_t)(wc_part - path);
+	search_ctx->ffsc_fix_path.string = vim_strnsave(path,
+	    search_ctx->ffsc_fix_path.length);
+	if (search_ctx->ffsc_fix_path.string == NULL)
+	    goto error_return;
 
 	/*
 	 * copy wc_path and add restricts to the '**' wildcard.
@@ -477,27 +515,27 @@
 	 * Due to this technique the path looks awful if you print it as a
 	 * string.
 	 */
-	len = 0;
+	ff_expand_buffer.length = 0;
 	while (*wc_part != NUL)
 	{
-	    if (len + 5 >= MAXPATHL)
+	    if (ff_expand_buffer.length + 5 >= MAXPATHL)
 	    {
 		emsg(_(e_path_too_long_for_completion));
 		break;
 	    }
 	    if (STRNCMP(wc_part, "**", 2) == 0)
 	    {
-		ff_expand_buffer[len++] = *wc_part++;
-		ff_expand_buffer[len++] = *wc_part++;
+		ff_expand_buffer.string[ff_expand_buffer.length++] = *wc_part++;
+		ff_expand_buffer.string[ff_expand_buffer.length++] = *wc_part++;
 
 		llevel = strtol((char *)wc_part, &errpt, 10);
 		if ((char_u *)errpt != wc_part && llevel > 0 && llevel < 255)
-		    ff_expand_buffer[len++] = llevel;
+		    ff_expand_buffer.string[ff_expand_buffer.length++] = llevel;
 		else if ((char_u *)errpt != wc_part && llevel == 0)
 		    // restrict is 0 -> remove already added '**'
-		    len -= 2;
+		    ff_expand_buffer.length -= 2;
 		else
-		    ff_expand_buffer[len++] = FF_MAX_STAR_STAR_EXPAND;
+		    ff_expand_buffer.string[ff_expand_buffer.length++] = FF_MAX_STAR_STAR_EXPAND;
 		wc_part = (char_u *)errpt;
 		if (*wc_part != NUL && !vim_ispathsep(*wc_part))
 		{
@@ -506,107 +544,153 @@
 		}
 	    }
 	    else
-		ff_expand_buffer[len++] = *wc_part++;
+		ff_expand_buffer.string[ff_expand_buffer.length++] = *wc_part++;
 	}
-	ff_expand_buffer[len] = NUL;
-	search_ctx->ffsc_wc_path = vim_strsave(ff_expand_buffer);
+	ff_expand_buffer.string[ff_expand_buffer.length] = NUL;
 
-	if (search_ctx->ffsc_wc_path == NULL)
+	search_ctx->ffsc_wc_path.length = ff_expand_buffer.length;
+	search_ctx->ffsc_wc_path.string = vim_strnsave(ff_expand_buffer.string,
+	    search_ctx->ffsc_wc_path.length);
+	if (search_ctx->ffsc_wc_path.string == NULL)
 	    goto error_return;
     }
     else
-	search_ctx->ffsc_fix_path = vim_strsave(path);
+    {
+	search_ctx->ffsc_fix_path.length = STRLEN(path);
+	search_ctx->ffsc_fix_path.string = vim_strnsave(path,
+	    search_ctx->ffsc_fix_path.length);
+	if (search_ctx->ffsc_fix_path.string == NULL)
+	    goto error_return;
+    }
 
-    if (search_ctx->ffsc_start_dir == NULL)
+    if (search_ctx->ffsc_start_dir.string == NULL)
     {
 	// store the fix part as startdir.
 	// This is needed if the parameter path is fully qualified.
-	search_ctx->ffsc_start_dir = vim_strsave(search_ctx->ffsc_fix_path);
-	if (search_ctx->ffsc_start_dir == NULL)
+	search_ctx->ffsc_start_dir.length = search_ctx->ffsc_fix_path.length;
+	search_ctx->ffsc_start_dir.string = vim_strnsave(search_ctx->ffsc_fix_path.string,
+	    search_ctx->ffsc_start_dir.length);
+	if (search_ctx->ffsc_start_dir.string == NULL)
 	    goto error_return;
-	search_ctx->ffsc_fix_path[0] = NUL;
+	search_ctx->ffsc_fix_path.string[0] = NUL;
+	search_ctx->ffsc_fix_path.length = 0;
     }
 
     // create an absolute path
-    if (STRLEN(search_ctx->ffsc_start_dir)
-			  + STRLEN(search_ctx->ffsc_fix_path) + 3 >= MAXPATHL)
+    if (search_ctx->ffsc_start_dir.length
+	    + search_ctx->ffsc_fix_path.length + 3 >= MAXPATHL)
     {
 	emsg(_(e_path_too_long_for_completion));
 	goto error_return;
     }
-    STRCPY(ff_expand_buffer, search_ctx->ffsc_start_dir);
-    add_pathsep(ff_expand_buffer);
-    {
-	int    eb_len = (int)STRLEN(ff_expand_buffer);
-	char_u *buf = alloc(eb_len
-				+ (int)STRLEN(search_ctx->ffsc_fix_path) + 1);
 
-	STRCPY(buf, ff_expand_buffer);
-	STRCPY(buf + eb_len, search_ctx->ffsc_fix_path);
+    add_sep = !after_pathsep(search_ctx->ffsc_start_dir.string,
+	search_ctx->ffsc_start_dir.string + search_ctx->ffsc_start_dir.length);
+    ff_expand_buffer.length = vim_snprintf(
+	    (char *)ff_expand_buffer.string,
+	    MAXPATHL,
+	    "%s%s",
+	    search_ctx->ffsc_start_dir.string,
+	    add_sep ? PATHSEPSTR : "");
+
+    {
+	size_t	bufsize = ff_expand_buffer.length + search_ctx->ffsc_fix_path.length + 1;
+	char_u	*buf = alloc(bufsize);
+
+	if (buf == NULL)
+	    goto error_return;
+
+	vim_snprintf(
+		(char *)buf,
+		bufsize,
+		"%s%s",
+		ff_expand_buffer.string,
+		search_ctx->ffsc_fix_path.string);
 	if (mch_isdir(buf))
 	{
-	    STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path);
-	    add_pathsep(ff_expand_buffer);
+	    if (search_ctx->ffsc_fix_path.length > 0)
+	    {
+		add_sep = !after_pathsep(search_ctx->ffsc_fix_path.string,
+		    search_ctx->ffsc_fix_path.string + search_ctx->ffsc_fix_path.length);
+		ff_expand_buffer.length += vim_snprintf(
+			(char *)ff_expand_buffer.string + ff_expand_buffer.length,
+			MAXPATHL - ff_expand_buffer.length,
+			"%s%s",
+			search_ctx->ffsc_fix_path.string,
+			add_sep ? PATHSEPSTR : "");
+	    }
 	}
 	else
 	{
-	    char_u *p =  gettail(search_ctx->ffsc_fix_path);
-	    char_u *wc_path = NULL;
-	    char_u *temp = NULL;
-	    int    len = 0;
+	    char_u *p = gettail(search_ctx->ffsc_fix_path.string);
+	    int    len = (int)search_ctx->ffsc_fix_path.length;
 
-	    if (p > search_ctx->ffsc_fix_path)
+	    if (p > search_ctx->ffsc_fix_path.string)
 	    {
 		// do not add '..' to the path and start upwards searching
-		len = (int)(p - search_ctx->ffsc_fix_path) - 1;
+		len = (int)(p - search_ctx->ffsc_fix_path.string) - 1;
 		if ((len >= 2
-			&& STRNCMP(search_ctx->ffsc_fix_path, "..", 2) == 0)
-			&& (len == 2
-				   || search_ctx->ffsc_fix_path[2] == PATHSEP))
+			&& STRNCMP(search_ctx->ffsc_fix_path.string, "..", 2) == 0)
+			&& (len == 2 || search_ctx->ffsc_fix_path.string[2] == PATHSEP))
 		{
 		    vim_free(buf);
 		    goto error_return;
 		}
-		STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len);
-		add_pathsep(ff_expand_buffer);
-	    }
-	    else
-		len = (int)STRLEN(search_ctx->ffsc_fix_path);
 
-	    if (search_ctx->ffsc_wc_path != NULL)
+		add_sep = !after_pathsep(search_ctx->ffsc_fix_path.string,
+		    search_ctx->ffsc_fix_path.string + search_ctx->ffsc_fix_path.length);
+		ff_expand_buffer.length += vim_snprintf(
+			(char *)ff_expand_buffer.string + ff_expand_buffer.length,
+			MAXPATHL - ff_expand_buffer.length,
+			"%.*s%s",
+			len,
+			search_ctx->ffsc_fix_path.string,
+			add_sep ? PATHSEPSTR : "");
+	    }
+
+	    if (search_ctx->ffsc_wc_path.string != NULL)
 	    {
-		wc_path = vim_strsave(search_ctx->ffsc_wc_path);
-		temp = alloc(STRLEN(search_ctx->ffsc_wc_path)
-				 + STRLEN(search_ctx->ffsc_fix_path + len)
-				 + 1);
-		if (temp == NULL || wc_path == NULL)
+		size_t	tempsize = (search_ctx->ffsc_fix_path.length - len)
+				+ search_ctx->ffsc_wc_path.length
+				+ 1;
+		char_u	*temp = alloc(tempsize);
+
+		if (temp == NULL)
 		{
 		    vim_free(buf);
 		    vim_free(temp);
-		    vim_free(wc_path);
 		    goto error_return;
 		}
 
-		STRCPY(temp, search_ctx->ffsc_fix_path + len);
-		STRCAT(temp, search_ctx->ffsc_wc_path);
-		vim_free(search_ctx->ffsc_wc_path);
-		vim_free(wc_path);
-		search_ctx->ffsc_wc_path = temp;
+		search_ctx->ffsc_wc_path.length = vim_snprintf(
+			    (char *)temp,
+			    tempsize,
+			    "%s%s",
+			    search_ctx->ffsc_fix_path.string + len,
+			    search_ctx->ffsc_wc_path.string);
+		vim_free(search_ctx->ffsc_wc_path.string);
+		search_ctx->ffsc_wc_path.string = temp;
 	    }
 	}
 	vim_free(buf);
     }
 
-    sptr = ff_create_stack_element(ff_expand_buffer,
-					   search_ctx->ffsc_wc_path, level, 0);
+    sptr = ff_create_stack_element(ff_expand_buffer.string,
+			ff_expand_buffer.length,
+			search_ctx->ffsc_wc_path.string,
+			search_ctx->ffsc_wc_path.length,
+			level,
+			0);
 
     if (sptr == NULL)
 	goto error_return;
 
     ff_push(search_ctx, sptr);
 
-    search_ctx->ffsc_file_to_search = vim_strsave(filename);
-    if (search_ctx->ffsc_file_to_search == NULL)
+    search_ctx->ffsc_file_to_search.length = filenamelen;
+    search_ctx->ffsc_file_to_search.string = vim_strnsave(filename,
+	search_ctx->ffsc_file_to_search.length);
+    if (search_ctx->ffsc_file_to_search.string == NULL)
 	goto error_return;
 
     return search_ctx;
@@ -628,6 +712,7 @@
 vim_findfile_stopdir(char_u *buf)
 {
     char_u	*r_ptr = buf;
+    char_u	*r_ptr_end = NULL;	    // points to NUL at end of string "r_ptr"
 
     while (*r_ptr != NUL && *r_ptr != ';')
     {
@@ -635,14 +720,18 @@
 	{
 	    // Overwrite the escape char,
 	    // use STRLEN(r_ptr) to move the trailing '\0'.
-	    STRMOVE(r_ptr, r_ptr + 1);
+	    if (r_ptr_end == NULL)
+		r_ptr_end = r_ptr + STRLEN(r_ptr);
+	    mch_memmove(r_ptr, r_ptr + 1,
+		(size_t)(r_ptr_end - (r_ptr + 1)) + 1);	// +1 for NUL
 	    r_ptr++;
+	    --r_ptr_end;
 	}
 	r_ptr++;
     }
     if (*r_ptr == ';')
     {
-	*r_ptr = 0;
+	*r_ptr = NUL;
 	r_ptr++;
     }
     else if (*r_ptr == NUL)
@@ -679,14 +768,10 @@
     char_u *
 vim_findfile(void *search_ctx_arg)
 {
-    char_u	*file_path;
-    char_u	*rest_of_wildcards;
+    string_T	file_path;
+    string_T	rest_of_wildcards;
     char_u	*path_end = NULL;
     ff_stack_T	*stackp;
-    int		len;
-    int		i;
-    char_u	*p;
-    char_u	*suf;
     ff_search_ctx_T *search_ctx;
 
     if (search_ctx_arg == NULL)
@@ -698,13 +783,13 @@
      * filepath is used as buffer for various actions and as the storage to
      * return a found filename.
      */
-    if ((file_path = alloc(MAXPATHL)) == NULL)
+    if ((file_path.string = alloc(MAXPATHL)) == NULL)
 	return NULL;
 
     // store the end of the start dir -- needed for upward search
-    if (search_ctx->ffsc_start_dir != NULL)
-	path_end = &search_ctx->ffsc_start_dir[
-					  STRLEN(search_ctx->ffsc_start_dir)];
+    if (search_ctx->ffsc_start_dir.string != NULL)
+	path_end = &search_ctx->ffsc_start_dir.string[
+					    search_ctx->ffsc_start_dir.length];
 
     // upward search loop
     for (;;)
@@ -744,14 +829,17 @@
 	    if (stackp->ffs_filearray == NULL
 		    && ff_check_visited(&search_ctx->ffsc_dir_visited_list
 							  ->ffvl_visited_list,
-			    stackp->ffs_fix_path, stackp->ffs_wc_path) == FAIL)
+			    stackp->ffs_fix_path.string,
+			    stackp->ffs_fix_path.length,
+			    stackp->ffs_wc_path.string,
+			    stackp->ffs_wc_path.length) == FAIL)
 	    {
 #ifdef FF_VERBOSE
 		if (p_verbose >= 5)
 		{
 		    verbose_enter_scroll();
 		    smsg("Already Searched: %s (%s)",
-				   stackp->ffs_fix_path, stackp->ffs_wc_path);
+				   stackp->ffs_fix_path.string, stackp->ffs_wc_path.string);
 		    // don't overwrite this either
 		    msg_puts("\n");
 		    verbose_leave_scroll();
@@ -765,7 +853,7 @@
 	    {
 		verbose_enter_scroll();
 		smsg("Searching: %s (%s)",
-				   stackp->ffs_fix_path, stackp->ffs_wc_path);
+				   stackp->ffs_fix_path.string, stackp->ffs_wc_path.string);
 		// don't overwrite this either
 		msg_puts("\n");
 		verbose_leave_scroll();
@@ -779,7 +867,8 @@
 		continue;
 	    }
 
-	    file_path[0] = NUL;
+	    file_path.string[0] = NUL;
+	    file_path.length = 0;
 
 	    /*
 	     * If no filearray till now expand wildcards
@@ -793,17 +882,23 @@
 
 		// we use filepath to build the path expand_wildcards() should
 		// expand.
-		dirptrs[0] = file_path;
+		dirptrs[0] = file_path.string;
 		dirptrs[1] = NULL;
 
 		// if we have a start dir copy it in
-		if (!vim_isAbsName(stackp->ffs_fix_path)
-						&& search_ctx->ffsc_start_dir)
+		if (!vim_isAbsName(stackp->ffs_fix_path.string)
+						&& search_ctx->ffsc_start_dir.string)
 		{
-		    if (STRLEN(search_ctx->ffsc_start_dir) + 1 < MAXPATHL)
+		    if (search_ctx->ffsc_start_dir.length + 1 < MAXPATHL)
 		    {
-			STRCPY(file_path, search_ctx->ffsc_start_dir);
-			add_pathsep(file_path);
+			int add_sep = !after_pathsep(search_ctx->ffsc_start_dir.string,
+			    search_ctx->ffsc_start_dir.string + search_ctx->ffsc_start_dir.length);
+			file_path.length = vim_snprintf(
+			    (char *)file_path.string,
+			    MAXPATHL,
+			    "%s%s",
+			    search_ctx->ffsc_start_dir.string,
+			    add_sep ? PATHSEPSTR : "");
 		    }
 		    else
 		    {
@@ -813,11 +908,16 @@
 		}
 
 		// append the fix part of the search path
-		if (STRLEN(file_path) + STRLEN(stackp->ffs_fix_path) + 1
-								    < MAXPATHL)
+		if (file_path.length + stackp->ffs_fix_path.length + 1 < MAXPATHL)
 		{
-		    STRCAT(file_path, stackp->ffs_fix_path);
-		    add_pathsep(file_path);
+		    int add_sep = !after_pathsep(stackp->ffs_fix_path.string,
+			stackp->ffs_fix_path.string + stackp->ffs_fix_path.length);
+		    file_path.length += vim_snprintf(
+			(char *)file_path.string + file_path.length,
+			MAXPATHL - file_path.length,
+			"%s%s",
+			stackp->ffs_fix_path.string,
+			add_sep ? PATHSEPSTR : "");
 		}
 		else
 		{
@@ -825,21 +925,23 @@
 		    goto fail;
 		}
 
-		rest_of_wildcards = stackp->ffs_wc_path;
-		if (*rest_of_wildcards != NUL)
+		rest_of_wildcards.string = stackp->ffs_wc_path.string;
+		rest_of_wildcards.length = stackp->ffs_wc_path.length;
+		if (*rest_of_wildcards.string != NUL)
 		{
-		    len = (int)STRLEN(file_path);
-		    if (STRNCMP(rest_of_wildcards, "**", 2) == 0)
+		    if (STRNCMP(rest_of_wildcards.string, "**", 2) == 0)
 		    {
+			char_u	*p;
+
 			// pointer to the restrict byte
 			// The restrict byte is not a character!
-			p = rest_of_wildcards + 2;
+			p = rest_of_wildcards.string + 2;
 
 			if (*p > 0)
 			{
 			    (*p)--;
-			    if (len + 1 < MAXPATHL)
-				file_path[len++] = '*';
+			    if (file_path.length + 1 < MAXPATHL)
+				file_path.string[file_path.length++] = '*';
 			    else
 			    {
 				ff_free_stack_element(stackp);
@@ -850,16 +952,23 @@
 			if (*p == 0)
 			{
 			    // remove '**<numb> from wildcards
-			    STRMOVE(rest_of_wildcards, rest_of_wildcards + 3);
+			    mch_memmove(rest_of_wildcards.string,
+					rest_of_wildcards.string + 3,
+					(size_t)(rest_of_wildcards.length - 3) + 1);    // +1 for NUL
+			    rest_of_wildcards.length -= 3;
+			    stackp->ffs_wc_path.length = rest_of_wildcards.length;
 			}
 			else
-			    rest_of_wildcards += 3;
+			{
+			    rest_of_wildcards.string += 3;
+			    rest_of_wildcards.length -= 3;
+			}
 
 			if (stackp->ffs_star_star_empty == 0)
 			{
 			    // if not done before, expand '**' to empty
 			    stackp->ffs_star_star_empty = 1;
-			    dirptrs[1] = stackp->ffs_fix_path;
+			    dirptrs[1] = stackp->ffs_fix_path.string;
 			}
 		    }
 
@@ -870,19 +979,27 @@
 		     * pushing every directory returned from expand_wildcards()
 		     * on the stack again for further search.
 		     */
-		    while (*rest_of_wildcards
-			    && !vim_ispathsep(*rest_of_wildcards))
-			if (len + 1 < MAXPATHL)
-			    file_path[len++] = *rest_of_wildcards++;
+		    while (*rest_of_wildcards.string
+			    && !vim_ispathsep(*rest_of_wildcards.string))
+		    {
+			if (file_path.length + 1 < MAXPATHL)
+			{
+			    file_path.string[file_path.length++] = *rest_of_wildcards.string++;
+			    --rest_of_wildcards.length;
+			}
 			else
 			{
 			    ff_free_stack_element(stackp);
 			    goto fail;
 			}
+		    }
 
-		    file_path[len] = NUL;
-		    if (vim_ispathsep(*rest_of_wildcards))
-			rest_of_wildcards++;
+		    file_path.string[file_path.length] = NUL;
+		    if (vim_ispathsep(*rest_of_wildcards.string))
+		    {
+			rest_of_wildcards.string++;
+			rest_of_wildcards.length--;
+		    }
 		}
 
 		/*
@@ -894,7 +1011,7 @@
 		    stackp->ffs_filearray = ALLOC_ONE(char_u *);
 		    if (stackp->ffs_filearray != NULL
 			    && (stackp->ffs_filearray[0]
-				= vim_strsave(dirptrs[0])) != NULL)
+				= vim_strnsave(dirptrs[0], file_path.length)) != NULL)
 			stackp->ffs_filearray_size = 1;
 		    else
 			stackp->ffs_filearray_size = 0;
@@ -912,14 +1029,22 @@
 		stackp->ffs_stage = 0;
 	    }
 	    else
-		rest_of_wildcards = &stackp->ffs_wc_path[
-						 STRLEN(stackp->ffs_wc_path)];
+	    {
+		rest_of_wildcards.string = &stackp->ffs_wc_path.string[
+							stackp->ffs_wc_path.length];
+		rest_of_wildcards.length = 0;
+	    }
 
 	    if (stackp->ffs_stage == 0)
 	    {
+		int i;
+
 		// this is the first time we work on this directory
-		if (*rest_of_wildcards == NUL)
+		if (*rest_of_wildcards.string == NUL)
 		{
+		    size_t  len;
+		    char_u  *suf;
+
 		    /*
 		     * We don't have further wildcards to expand, so we have to
 		     * check for the final file now.
@@ -933,13 +1058,19 @@
 
 			// prepare the filename to be checked for existence
 			// below
-			if (STRLEN(stackp->ffs_filearray[i]) + 1
-				+ STRLEN(search_ctx->ffsc_file_to_search)
-								    < MAXPATHL)
+			len = STRLEN(stackp->ffs_filearray[i]);
+			if (len + 1 + search_ctx->ffsc_file_to_search.length
+									< MAXPATHL)
 			{
-			    STRCPY(file_path, stackp->ffs_filearray[i]);
-			    add_pathsep(file_path);
-			    STRCAT(file_path, search_ctx->ffsc_file_to_search);
+			    int	add_sep = !after_pathsep(stackp->ffs_filearray[i],
+				    stackp->ffs_filearray[i] + len);
+			    file_path.length = vim_snprintf(
+				    (char *)file_path.string,
+				    MAXPATHL,
+				    "%s%s%s",
+				    stackp->ffs_filearray[i],
+				    add_sep ? PATHSEPSTR : "",
+				    search_ctx->ffsc_file_to_search.string);
 			}
 			else
 			{
@@ -951,7 +1082,6 @@
 			 * Try without extra suffix and then with suffixes
 			 * from 'suffixesadd'.
 			 */
-			len = (int)STRLEN(file_path);
 			if (search_ctx->ffsc_tagfile)
 			    suf = (char_u *)"";
 			else
@@ -959,18 +1089,20 @@
 			for (;;)
 			{
 			    // if file exists and we didn't already find it
-			    if ((path_with_url(file_path)
-				  || (mch_getperm(file_path) >= 0
+			    if ((path_with_url(file_path.string)
+				  || (mch_getperm(file_path.string) >= 0
 				      && (search_ctx->ffsc_find_what
 							      == FINDFILE_BOTH
 					  || ((search_ctx->ffsc_find_what
 							      == FINDFILE_DIR)
-						   == mch_isdir(file_path)))))
+						   == mch_isdir(file_path.string)))))
 #ifndef FF_VERBOSE
 				    && (ff_check_visited(
 					    &search_ctx->ffsc_visited_list
 							   ->ffvl_visited_list,
-					    file_path, (char_u *)"") == OK)
+					    file_path.string,
+					    file_path.length,
+					    (char_u *)"", 0) == OK)
 #endif
 			       )
 			    {
@@ -978,13 +1110,14 @@
 				if (ff_check_visited(
 					    &search_ctx->ffsc_visited_list
 							   ->ffvl_visited_list,
-					      file_path, (char_u *)"") == FAIL)
+					      file_path.string,
+					      file_path.length,
+					      (char_u *)"", 0) == FAIL)
 				{
 				    if (p_verbose >= 5)
 				    {
 					verbose_enter_scroll();
-					smsg("Already: %s",
-								   file_path);
+					smsg("Already: %s", file_path.string);
 					// don't overwrite this either
 					msg_puts("\n");
 					verbose_leave_scroll();
@@ -997,34 +1130,42 @@
 				stackp->ffs_filearray_cur = i + 1;
 				ff_push(search_ctx, stackp);
 
-				if (!path_with_url(file_path))
-				    simplify_filename(file_path);
-				if (mch_dirname(ff_expand_buffer, MAXPATHL)
+				if (!path_with_url(file_path.string))
+				    file_path.length = simplify_filename(file_path.string);
+
+				if (mch_dirname(ff_expand_buffer.string, MAXPATHL)
 									== OK)
 				{
-				    p = shorten_fname(file_path,
-							    ff_expand_buffer);
+				    char_u  *p;
+
+				    ff_expand_buffer.length = STRLEN(ff_expand_buffer.string);
+				    p = shorten_fname(file_path.string,
+							    ff_expand_buffer.string);
 				    if (p != NULL)
-					STRMOVE(file_path, p);
+				    {
+					mch_memmove(file_path.string, p,
+					    (size_t)((file_path.string + file_path.length) - p) + 1);  // +1 for NUL
+					file_path.length -= (p - file_path.string);
+				    }
 				}
 #ifdef FF_VERBOSE
 				if (p_verbose >= 5)
 				{
 				    verbose_enter_scroll();
-				    smsg("HIT: %s", file_path);
+				    smsg("HIT: %s", file_path.string);
 				    // don't overwrite this either
 				    msg_puts("\n");
 				    verbose_leave_scroll();
 				}
 #endif
-				return file_path;
+				return file_path.string;
 			    }
 
 			    // Not found or found already, try next suffix.
 			    if (*suf == NUL)
 				break;
-			    copy_option_part(&suf, file_path + len,
-							 MAXPATHL - len, ",");
+			    file_path.length += copy_option_part(&suf, file_path.string + file_path.length,
+							 MAXPATHL - file_path.length, ",");
 			}
 		    }
 		}
@@ -1043,7 +1184,9 @@
 			ff_push(search_ctx,
 				ff_create_stack_element(
 						     stackp->ffs_filearray[i],
-						     rest_of_wildcards,
+						     STRLEN(stackp->ffs_filearray[i]),
+						     rest_of_wildcards.string,
+						     rest_of_wildcards.length,
 						     stackp->ffs_level - 1, 0));
 		    }
 		}
@@ -1055,68 +1198,83 @@
 	     * if wildcards contains '**' we have to descent till we reach the
 	     * leaves of the directory tree.
 	     */
-	    if (STRNCMP(stackp->ffs_wc_path, "**", 2) == 0)
+	    if (STRNCMP(stackp->ffs_wc_path.string, "**", 2) == 0)
 	    {
+		int i;
+
 		for (i = stackp->ffs_filearray_cur;
 					  i < stackp->ffs_filearray_size; ++i)
 		{
 		    if (fnamecmp(stackp->ffs_filearray[i],
-						   stackp->ffs_fix_path) == 0)
+						   stackp->ffs_fix_path.string) == 0)
 			continue; // don't repush same directory
 		    if (!mch_isdir(stackp->ffs_filearray[i]))
 			continue;   // not a directory
 		    ff_push(search_ctx,
-			    ff_create_stack_element(stackp->ffs_filearray[i],
-				stackp->ffs_wc_path, stackp->ffs_level - 1, 1));
+			    ff_create_stack_element(
+				stackp->ffs_filearray[i],
+				STRLEN(stackp->ffs_filearray[i]),
+				stackp->ffs_wc_path.string,
+				stackp->ffs_wc_path.length,
+				stackp->ffs_level - 1, 1));
 		}
 	    }
 
 	    // we are done with the current directory
 	    ff_free_stack_element(stackp);
-
 	}
 
 	// If we reached this, we didn't find anything downwards.
 	// Let's check if we should do an upward search.
-	if (search_ctx->ffsc_start_dir
+	if (search_ctx->ffsc_start_dir.string
 		&& search_ctx->ffsc_stopdirs_v != NULL && !got_int)
 	{
 	    ff_stack_T  *sptr;
 	    // path_end may point to the NUL or the previous path separator
-	    int plen = (path_end - search_ctx->ffsc_start_dir)
+	    int plen = (path_end - search_ctx->ffsc_start_dir.string)
 							  + (*path_end != NUL);
 
 	    // is the last starting directory in the stop list?
-	    if (ff_path_in_stoplist(search_ctx->ffsc_start_dir,
+	    if (ff_path_in_stoplist(search_ctx->ffsc_start_dir.string,
 				    plen, search_ctx->ffsc_stopdirs_v) == TRUE)
 		break;
 
 	    // cut of last dir
-	    while (path_end > search_ctx->ffsc_start_dir
+	    while (path_end > search_ctx->ffsc_start_dir.string
 						  && vim_ispathsep(*path_end))
 		path_end--;
-	    while (path_end > search_ctx->ffsc_start_dir
+	    while (path_end > search_ctx->ffsc_start_dir.string
 					      && !vim_ispathsep(path_end[-1]))
 		path_end--;
-	    *path_end = 0;
+	    *path_end = NUL;
+
+	    // we may have shortened search_ctx->ffsc_start_dir, so update it's length
+	    search_ctx->ffsc_start_dir.length = (size_t)(path_end - search_ctx->ffsc_start_dir.string);
 	    path_end--;
 
-	    if (*search_ctx->ffsc_start_dir == 0)
+	    if (*search_ctx->ffsc_start_dir.string == NUL)
 		break;
 
-	    if (STRLEN(search_ctx->ffsc_start_dir) + 1
-		    + STRLEN(search_ctx->ffsc_fix_path) < MAXPATHL)
+	    if (search_ctx->ffsc_start_dir.length + 1
+		    + search_ctx->ffsc_fix_path.length < MAXPATHL)
 	    {
-		STRCPY(file_path, search_ctx->ffsc_start_dir);
-		add_pathsep(file_path);
-		STRCAT(file_path, search_ctx->ffsc_fix_path);
+		int add_sep = !after_pathsep(search_ctx->ffsc_start_dir.string,
+			    search_ctx->ffsc_start_dir.string + search_ctx->ffsc_start_dir.length);
+		file_path.length = vim_snprintf(
+			(char *)file_path.string,
+			MAXPATHL,
+			"%s%s%s",
+			search_ctx->ffsc_start_dir.string,
+			add_sep ? PATHSEPSTR : "",
+			search_ctx->ffsc_fix_path.string);
 	    }
 	    else
 		goto fail;
 
 	    // create a new stack entry
-	    sptr = ff_create_stack_element(file_path,
-		    search_ctx->ffsc_wc_path, search_ctx->ffsc_level, 0);
+	    sptr = ff_create_stack_element(file_path.string, file_path.length,
+		    search_ctx->ffsc_wc_path.string, search_ctx->ffsc_wc_path.length,
+		    search_ctx->ffsc_level, 0);
 	    if (sptr == NULL)
 		break;
 	    ff_push(search_ctx, sptr);
@@ -1126,7 +1284,7 @@
     }
 
 fail:
-    vim_free(file_path);
+    vim_free(file_path.string);
     return NULL;
 }
 
@@ -1186,6 +1344,7 @@
     static ff_visited_list_hdr_T*
 ff_get_visited_list(
     char_u			*filename,
+    size_t			filenamelen,
     ff_visited_list_hdr_T	**list_headp)
 {
     ff_visited_list_hdr_T  *retptr = NULL;
@@ -1234,7 +1393,7 @@
 	return NULL;
 
     retptr->ffvl_visited_list = NULL;
-    retptr->ffvl_filename = vim_strsave(filename);
+    retptr->ffvl_filename = vim_strnsave(filename, filenamelen);
     if (retptr->ffvl_filename == NULL)
     {
 	vim_free(retptr);
@@ -1300,7 +1459,9 @@
 ff_check_visited(
     ff_visited_T	**visited_list,
     char_u		*fname,
-    char_u		*wc_path)
+    size_t		fnamelen,
+    char_u		*wc_path,
+    size_t		wc_pathlen)
 {
     ff_visited_T	*vp;
 #ifdef UNIX
@@ -1312,20 +1473,24 @@
     // device/inode (unix) or the full path name (not Unix).
     if (path_with_url(fname))
     {
-	vim_strncpy(ff_expand_buffer, fname, MAXPATHL - 1);
+	vim_strncpy(ff_expand_buffer.string, fname, fnamelen);
+	ff_expand_buffer.length = fnamelen;
 #ifdef UNIX
 	url = TRUE;
 #endif
     }
     else
     {
-	ff_expand_buffer[0] = NUL;
+	ff_expand_buffer.string[0] = NUL;
+	ff_expand_buffer.length = 0;
 #ifdef UNIX
 	if (mch_stat((char *)fname, &st) < 0)
-#else
-	if (vim_FullName(fname, ff_expand_buffer, MAXPATHL, TRUE) == FAIL)
-#endif
 	    return FAIL;
+#else
+	if (vim_FullName(fname, ff_expand_buffer.string, MAXPATHL, TRUE) == FAIL)
+	    return FAIL;
+	ff_expand_buffer.length = STRLEN(ff_expand_buffer.string);
+#endif
     }
 
     // check against list of already visited files
@@ -1337,7 +1502,7 @@
 						  && vp->ffv_ino == st.st_ino)
 		     :
 #endif
-		fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0
+		fnamecmp(vp->ffv_fname, ff_expand_buffer.string) == 0
 	   )
 	{
 	    // are the wildcard parts equal
@@ -1351,7 +1516,7 @@
      * New file/dir.  Add it to the list of visited files/dirs.
      */
     vp = alloc(
-	     offsetof(ff_visited_T, ffv_fname) + STRLEN(ff_expand_buffer) + 1);
+	     offsetof(ff_visited_T, ffv_fname) + ff_expand_buffer.length + 1);
     if (vp == NULL)
 	return OK;
 
@@ -1359,20 +1524,20 @@
     if (!url)
     {
 	vp->ffv_dev_valid = TRUE;
-	vp->ffv_ino = st.st_ino;
 	vp->ffv_dev = st.st_dev;
+	vp->ffv_ino = st.st_ino;
 	vp->ffv_fname[0] = NUL;
     }
     else
     {
 	vp->ffv_dev_valid = FALSE;
 #endif
-	STRCPY(vp->ffv_fname, ff_expand_buffer);
+	STRCPY(vp->ffv_fname, ff_expand_buffer.string);
 #ifdef UNIX
     }
 #endif
     if (wc_path != NULL)
-	vp->ffv_wc_path = vim_strsave(wc_path);
+	vp->ffv_wc_path = vim_strnsave(wc_path, wc_pathlen);
     else
 	vp->ffv_wc_path = NULL;
 
@@ -1388,7 +1553,9 @@
     static ff_stack_T *
 ff_create_stack_element(
     char_u	*fix_part,
+    size_t	fix_partlen,
     char_u	*wc_part,
+    size_t	wc_partlen,
     int		level,
     int		star_star_empty)
 {
@@ -1408,14 +1575,22 @@
 
     // the following saves NULL pointer checks in vim_findfile
     if (fix_part == NULL)
+    {
 	fix_part = (char_u *)"";
-    new->ffs_fix_path = vim_strsave(fix_part);
+	fix_partlen = 0;
+    }
+    new->ffs_fix_path.string = vim_strnsave(fix_part, fix_partlen);
+    new->ffs_fix_path.length = fix_partlen;
 
     if (wc_part == NULL)
-	wc_part  = (char_u *)"";
-    new->ffs_wc_path = vim_strsave(wc_part);
+    {
+	wc_part = (char_u *)"";
+	wc_partlen = 0;
+    }
+    new->ffs_wc_path.string = vim_strnsave(wc_part, wc_partlen);
+    new->ffs_wc_path.length = wc_partlen;
 
-    if (new->ffs_fix_path == NULL || new->ffs_wc_path == NULL)
+    if (new->ffs_fix_path.string == NULL || new->ffs_wc_path.string == NULL)
     {
 	ff_free_stack_element(new);
 	new = NULL;
@@ -1461,9 +1636,9 @@
     static void
 ff_free_stack_element(ff_stack_T *stack_ptr)
 {
-    // vim_free handles possible NULL pointers
-    vim_free(stack_ptr->ffs_fix_path);
-    vim_free(stack_ptr->ffs_wc_path);
+    // VIM_CLEAR_STRING handles possible NULL pointers
+    VIM_CLEAR_STRING(stack_ptr->ffs_fix_path);
+    VIM_CLEAR_STRING(stack_ptr->ffs_wc_path);
 
     if (stack_ptr->ffs_filearray != NULL)
 	FreeWild(stack_ptr->ffs_filearray_size, stack_ptr->ffs_filearray);
@@ -1483,29 +1658,23 @@
     while ((sptr = ff_pop(search_ctx)) != NULL)
 	ff_free_stack_element(sptr);
 
-    vim_free(search_ctx->ffsc_file_to_search);
-    vim_free(search_ctx->ffsc_start_dir);
-    vim_free(search_ctx->ffsc_fix_path);
-    vim_free(search_ctx->ffsc_wc_path);
-
     if (search_ctx->ffsc_stopdirs_v != NULL)
     {
 	int  i = 0;
 
-	while (search_ctx->ffsc_stopdirs_v[i] != NULL)
+	while (search_ctx->ffsc_stopdirs_v[i].string != NULL)
 	{
-	    vim_free(search_ctx->ffsc_stopdirs_v[i]);
+	    vim_free(search_ctx->ffsc_stopdirs_v[i].string);
 	    i++;
 	}
-	vim_free(search_ctx->ffsc_stopdirs_v);
+	VIM_CLEAR(search_ctx->ffsc_stopdirs_v);
     }
-    search_ctx->ffsc_stopdirs_v = NULL;
 
     // reset everything
-    search_ctx->ffsc_file_to_search = NULL;
-    search_ctx->ffsc_start_dir = NULL;
-    search_ctx->ffsc_fix_path = NULL;
-    search_ctx->ffsc_wc_path = NULL;
+    VIM_CLEAR_STRING(search_ctx->ffsc_file_to_search);
+    VIM_CLEAR_STRING(search_ctx->ffsc_start_dir);
+    VIM_CLEAR_STRING(search_ctx->ffsc_fix_path);
+    VIM_CLEAR_STRING(search_ctx->ffsc_wc_path);
     search_ctx->ffsc_level = 0;
 }
 
@@ -1514,7 +1683,7 @@
  * returns TRUE if yes else FALSE
  */
     static int
-ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
+ff_path_in_stoplist(char_u *path, int path_len, string_T *stopdirs_v)
 {
     int		i = 0;
 
@@ -1526,13 +1695,13 @@
     if (path_len == 0)
 	return TRUE;
 
-    for (i = 0; stopdirs_v[i] != NULL; i++)
+    for (i = 0; stopdirs_v[i].string != NULL; i++)
 	// match for parent directory. So '/home' also matches
 	// '/home/rks'. Check for PATHSEP in stopdirs_v[i], else
 	// '/home/r' would also match '/home/rks'
-	if (fnamencmp(stopdirs_v[i], path, path_len) == 0
-		&& ((int)STRLEN(stopdirs_v[i]) <= path_len
-		    || vim_ispathsep(stopdirs_v[i][path_len])))
+	if (fnamencmp(stopdirs_v[i].string, path, path_len) == 0
+		&& ((int)stopdirs_v[i].length <= path_len
+		    || vim_ispathsep(stopdirs_v[i].string[path_len])))
 	    return TRUE;
 
     return FALSE;
@@ -1583,7 +1752,7 @@
     void
 free_findfile(void)
 {
-    VIM_CLEAR(ff_expand_buffer);
+    VIM_CLEAR_STRING(ff_expand_buffer);
 }
 # endif
 
@@ -1628,9 +1797,7 @@
     ff_search_ctx_T	**search_ctx = (ff_search_ctx_T **)search_ctx_arg;
     static char_u	*dir;
     static int		did_findfile_init = FALSE;
-    char_u		save_char;
     char_u		*file_name = NULL;
-    char_u		*buf = NULL;
     int			rel_to_curdir;
 # ifdef AMIGA
     struct Process	*proc = (struct Process *)FindTask(0L);
@@ -1639,9 +1806,12 @@
     // Avoid a requester here for a volume that doesn't exist.
     proc->pr_WindowPtr = (APTR)-1L;
 # endif
+    static size_t	file_to_findlen = 0;
 
     if (first == TRUE)
     {
+	char_u	save_char;
+
 	if (len == 0)
 	    return NULL;
 
@@ -1652,10 +1822,12 @@
 	ptr[len] = save_char;
 
 	vim_free(*file_to_find);
-	*file_to_find = vim_strsave(NameBuff);
+	file_to_findlen = STRLEN(NameBuff);
+	*file_to_find = vim_strnsave(NameBuff, file_to_findlen);
 	if (*file_to_find == NULL)	// out of memory
 	{
 	    file_name = NULL;
+	    file_to_findlen = 0;
 	    goto theend;
 	}
 	if (options & FNAME_UNESC)
@@ -1663,7 +1835,11 @@
 	    // Change all "\ " to " ".
 	    for (ptr = *file_to_find; *ptr != NUL; ++ptr)
 		if (ptr[0] == '\\' && ptr[1] == ' ')
-		    mch_memmove(ptr, ptr + 1, STRLEN(ptr));
+		{
+		    mch_memmove(ptr, ptr + 1,
+			(size_t)((*file_to_find + file_to_findlen) - (ptr + 1)) + 1);
+		    --file_to_findlen;
+		}
 	}
     }
 
@@ -1697,27 +1873,36 @@
 	{
 	    int		l;
 	    int		run;
+	    size_t	rel_fnamelen = 0;
+	    char_u	*suffix;
 
 	    if (path_with_url(*file_to_find))
 	    {
-		file_name = vim_strsave(*file_to_find);
+		file_name = vim_strnsave(*file_to_find, file_to_findlen);
 		goto theend;
 	    }
 
+	    if (rel_fname != NULL)
+		rel_fnamelen = STRLEN(rel_fname);
+
 	    // When FNAME_REL flag given first use the directory of the file.
 	    // Otherwise or when this fails use the current directory.
 	    for (run = 1; run <= 2; ++run)
 	    {
-		l = (int)STRLEN(*file_to_find);
+		l = (int)file_to_findlen;
 		if (run == 1
 			&& rel_to_curdir
 			&& (options & FNAME_REL)
 			&& rel_fname != NULL
-			&& STRLEN(rel_fname) + l < MAXPATHL)
+			&& rel_fnamelen + l < MAXPATHL)
 		{
-		    STRCPY(NameBuff, rel_fname);
-		    STRCPY(gettail(NameBuff), *file_to_find);
-		    l = (int)STRLEN(NameBuff);
+		    l = vim_snprintf(
+			(char *)NameBuff,
+			MAXPATHL,
+			"%.*s%s",
+			(int)(gettail(rel_fname) - rel_fname),
+			rel_fname,
+			*file_to_find);
 		}
 		else
 		{
@@ -1727,7 +1912,7 @@
 
 		// When the file doesn't exist, try adding parts of
 		// 'suffixesadd'.
-		buf = suffixes;
+		suffix = suffixes;
 		for (;;)
 		{
 		    if (mch_getperm(NameBuff) >= 0
@@ -1735,12 +1920,12 @@
 				 || ((find_what == FINDFILE_DIR)
 						    == mch_isdir(NameBuff))))
 		    {
-			file_name = vim_strsave(NameBuff);
+			file_name = vim_strnsave(NameBuff, l);
 			goto theend;
 		    }
-		    if (*buf == NUL)
+		    if (*suffix == NUL)
 			break;
-		    copy_option_part(&buf, NameBuff + l, MAXPATHL - l, ",");
+		    l += copy_option_part(&suffix, NameBuff + l, MAXPATHL - l, ",");
 		}
 	    }
 	}
@@ -1772,6 +1957,7 @@
 	    }
 	    else
 	    {
+		char_u	*buf;
 		char_u  *r_ptr;
 
 		if (dir == NULL || *dir == NUL)
@@ -1787,12 +1973,12 @@
 		    break;
 
 		// copy next path
-		buf[0] = 0;
+		buf[0] = NUL;
 		copy_option_part(&dir, buf, MAXPATHL, " ,");
 
 		// get the stopdir string
 		r_ptr = vim_findfile_stopdir(buf);
-		*search_ctx = vim_findfile_init(buf, *file_to_find,
+		*search_ctx = vim_findfile_init(buf, *file_to_find, file_to_findlen,
 					    r_ptr, 100, FALSE, find_what,
 					   *search_ctx, FALSE, rel_fname);
 		if (*search_ctx != NULL)
@@ -1963,20 +2149,27 @@
 
     if (file_lnum != NULL)
     {
-	char_u *p;
-	char	*line_english = " line ";
-	char	*line_transl = _(line_msg);
+	char_u	*p;
+	char	*match_text = " line ";		// english
+	size_t	match_textlen = 6;
 
 	// Get the number after the file name and a separator character.
 	// Also accept " line 999" with and without the same translation as
 	// used in last_set_msg().
 	p = ptr + len;
-	if (STRNCMP(p, line_english, STRLEN(line_english)) == 0)
-	    p += STRLEN(line_english);
-	else if (STRNCMP(p, line_transl, STRLEN(line_transl)) == 0)
-	    p += STRLEN(line_transl);
+	if (STRNCMP(p, match_text, match_textlen) == 0)
+	    p += match_textlen;
 	else
-	    p = skipwhite(p);
+	{
+	    // no match with english, try localized
+	    match_text = _(line_msg);
+	    match_textlen = STRLEN(match_text);
+
+	    if (STRNCMP(p, match_text, match_textlen) == 0)
+		p += match_textlen;
+	    else
+		p = skipwhite(p);
+	}
 	if (*p != NUL)
 	{
 	    if (!SAFE_isdigit(*p))
@@ -2177,7 +2370,7 @@
 is_unique(char_u *maybe_unique, garray_T *gap, int i)
 {
     int	    j;
-    int	    candidate_len;
+    int	    candidate_len = (int)STRLEN(maybe_unique);
     int	    other_path_len;
     char_u  **other_paths = (char_u **)gap->ga_data;
     char_u  *rival;
@@ -2187,7 +2380,6 @@
 	if (j == i)
 	    continue;  // don't compare it with itself
 
-	candidate_len = (int)STRLEN(maybe_unique);
 	other_path_len = (int)STRLEN(other_paths[j]);
 	if (other_path_len < candidate_len)
 	    continue;  // it's different when it's shorter
@@ -2217,50 +2409,60 @@
 	garray_T	*gap)
 {
     char_u	*buf;
+    size_t	buflen;
     char_u	*p;
-    int		len;
+    size_t	curdirlen = 0;
 
     if ((buf = alloc(MAXPATHL)) == NULL)
 	return;
 
     while (*path_option != NUL)
     {
-	copy_option_part(&path_option, buf, MAXPATHL, " ,");
+	buflen = copy_option_part(&path_option, buf, MAXPATHL, " ,");
 
 	if (buf[0] == '.' && (buf[1] == NUL || vim_ispathsep(buf[1])))
 	{
+	    size_t  plen;
+
 	    // Relative to current buffer:
 	    // "/path/file" + "." -> "/path/"
 	    // "/path/file"  + "./subdir" -> "/path/subdir"
 	    if (curbuf->b_ffname == NULL)
 		continue;
 	    p = gettail(curbuf->b_ffname);
-	    len = (int)(p - curbuf->b_ffname);
-	    if (len + (int)STRLEN(buf) >= MAXPATHL)
+	    plen = (size_t)(p - curbuf->b_ffname);
+	    if (plen + buflen >= MAXPATHL)
 		continue;
 	    if (buf[1] == NUL)
-		buf[len] = NUL;
+		buf[plen] = NUL;
 	    else
-		STRMOVE(buf + len, buf + 2);
-	    mch_memmove(buf, curbuf->b_ffname, len);
-	    simplify_filename(buf);
+		mch_memmove(buf + plen, buf + 2, (buflen - 2) + 1); // +1 for NUL
+	    mch_memmove(buf, curbuf->b_ffname, plen);
+	    buflen = simplify_filename(buf);
 	}
 	else if (buf[0] == NUL)
+	{
 	    // relative to current directory
 	    STRCPY(buf, curdir);
+	    if (curdirlen == 0)
+		curdirlen = STRLEN(curdir);
+	    buflen = curdirlen;
+	}
 	else if (path_with_url(buf))
 	    // URL can't be used here
 	    continue;
 	else if (!mch_isFullName(buf))
 	{
 	    // Expand relative path to their full path equivalent
-	    len = (int)STRLEN(curdir);
-	    if (len + (int)STRLEN(buf) + 3 > MAXPATHL)
+	    if (curdirlen == 0)
+		curdirlen = STRLEN(curdir);
+	    if (curdirlen + buflen + 3 > MAXPATHL)
 		continue;
-	    STRMOVE(buf + len + 1, buf);
+
+	    mch_memmove(buf + curdirlen + 1, buf, buflen + 1);	    // +1 for NUL
 	    STRCPY(buf, curdir);
-	    buf[len] = PATHSEP;
-	    simplify_filename(buf);
+	    buf[curdirlen] = PATHSEP;
+	    buflen = simplify_filename(buf);
 	}
 
 	if (ga_grow(gap, 1) == FAIL)
@@ -2269,12 +2471,11 @@
 # if defined(MSWIN)
 	// Avoid the path ending in a backslash, it fails when a comma is
 	// appended.
-	len = (int)STRLEN(buf);
-	if (buf[len - 1] == '\\')
-	    buf[len - 1] = '/';
+	if (buf[buflen - 1] == '\\')
+	    buf[buflen - 1] = '/';
 # endif
 
-	p = vim_strsave(buf);
+	p = vim_strnsave(buf, buflen);
 	if (p == NULL)
 	    break;
 	((char_u **)gap->ga_data)[gap->ga_len++] = p;
@@ -2360,8 +2561,7 @@
     if (file_pattern == NULL)
 	return;
     file_pattern[0] = '*';
-    file_pattern[1] = NUL;
-    STRCAT(file_pattern, pattern);
+    STRCPY(file_pattern + 1, pattern);
     pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, FALSE);
     vim_free(file_pattern);
     if (pat == NULL)
@@ -2387,14 +2587,13 @@
 	char_u	    *path = fnames[i];
 	int	    is_in_curdir;
 	char_u	    *dir_end = gettail_dir(path);
-	char_u	    *pathsep_p;
 	char_u	    *path_cutoff;
 
 	len = (int)STRLEN(path);
 	is_in_curdir = fnamencmp(curdir, path, dir_end - path) == 0
 					     && curdir[dir_end - path] == NUL;
 	if (is_in_curdir)
-	    in_curdir[i] = vim_strsave(path);
+	    in_curdir[i] = vim_strnsave(path, len);
 
 	// Shorten the filename while maintaining its uniqueness
 	path_cutoff = get_path_cutoff(path, &path_ga);
@@ -2415,17 +2614,20 @@
 	{
 	    // Here all files can be reached without path, so get shortest
 	    // unique path.  We start at the end of the path.
-	    pathsep_p = path + len - 1;
+	    char_u  *pathsep_p = path + len - 1;
 
 	    while (find_previous_pathsep(path, &pathsep_p))
+	    {
 		if (vim_regexec(&regmatch, pathsep_p + 1, (colnr_T)0)
 			&& is_unique(pathsep_p + 1, gap, i)
 			&& path_cutoff != NULL && pathsep_p + 1 >= path_cutoff)
 		{
 		    sort_again = TRUE;
-		    mch_memmove(path, pathsep_p + 1, STRLEN(pathsep_p));
+		    mch_memmove(path, pathsep_p + 1,
+			    (size_t)((path + len) - (pathsep_p + 1)) + 1);  // +1 for NUL
 		    break;
 		}
+	    }
 	}
 
 	if (mch_isFullName(path))
@@ -2453,9 +2655,7 @@
 # endif
 		)
 	    {
-		STRCPY(path, ".");
-		add_pathsep(path);
-		STRMOVE(path + STRLEN(path), short_name);
+		vim_snprintf((char *)path, MAXPATHL, ".%s%s", PATHSEPSTR, short_name);
 	    }
 	}
 	ui_breakcheck();
@@ -2464,8 +2664,9 @@
     // Shorten filenames in /in/current/directory/{filename}
     for (i = 0; i < gap->ga_len && !got_int; i++)
     {
-	char_u *rel_path;
-	char_u *path = in_curdir[i];
+	size_t	rel_pathsize;
+	char_u	*rel_path;
+	char_u	*path = in_curdir[i];
 
 	if (path == NULL)
 	    continue;
@@ -2481,12 +2682,12 @@
 	    continue;
 	}
 
-	rel_path = alloc(STRLEN(short_name) + STRLEN(PATHSEPSTR) + 2);
+	rel_pathsize = 1 + STRLEN_LITERAL(PATHSEPSTR) + STRLEN(short_name) + 1;
+	rel_path = alloc(rel_pathsize);
 	if (rel_path == NULL)
 	    goto theend;
-	STRCPY(rel_path, ".");
-	add_pathsep(rel_path);
-	STRCAT(rel_path, short_name);
+
+	vim_snprintf((char *)rel_path, rel_pathsize, ".%s%s", PATHSEPSTR, short_name);
 
 	vim_free(fnames[i]);
 	fnames[i] = rel_path;
@@ -2561,12 +2762,13 @@
  * resulting file name is simplified in place and will either be the same
  * length as that supplied, or shorter.
  */
-    void
+    size_t
 simplify_filename(char_u *filename)
 {
 #ifndef AMIGA	    // Amiga doesn't have "..", it uses "/"
     int		components = 0;
     char_u	*p, *tail, *start;
+    char_u	*p_end;			    // point to NUL at end of string "p"
     int		stripping_disabled = FALSE;
     int		relative = TRUE;
 
@@ -2584,11 +2786,13 @@
 	while (vim_ispathsep(*p));
     }
     start = p;	    // remember start after "c:/" or "/" or "///"
+    p_end = p + STRLEN(p);
 #ifdef UNIX
     // Posix says that "//path" is unchanged but "///path" is "/path".
     if (start > filename + 2)
     {
-	STRMOVE(filename + 1, p);
+	mch_memmove(filename + 1, p, (size_t)(p_end - p) + 1);	    // +1 for NUL
+	p_end -= (size_t)(p - (filename + 1));
 	start = p = filename + 1;
     }
 #endif
@@ -2614,8 +2818,11 @@
 	}
 	else
 # endif
-	  if (vim_ispathsep(*p))
-	    STRMOVE(p, p + 1);		// remove duplicate "/"
+	if (vim_ispathsep(*p))
+	{
+	    mch_memmove(p, p + 1, (size_t)(p_end - (p + 1)) + 1); // remove duplicate "/"
+	    --p_end;
+	}
 	else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL))
 	{
 	    if (p == start && relative)
@@ -2632,7 +2839,9 @@
 			MB_PTR_ADV(tail);
 		else if (p > start)
 		    --p;		// strip preceding path separator
-		STRMOVE(p, tail);
+
+		mch_memmove(p, tail, (size_t)(p_end - tail) + 1);
+		p_end -= (size_t)(tail - p);
 	    }
 	}
 	else if (p[0] == '.' && p[1] == '.' &&
@@ -2748,19 +2957,25 @@
 		    {
 			if (p > start && tail[-1] == '.')
 			    --p;
-			STRMOVE(p, tail);	// strip previous component
+
+			mch_memmove(p, tail, (size_t)(p_end - tail) + 1);	// strip previous component
+			p_end -= (size_t)(tail - p);
 		    }
 
 		    --components;
 		}
 	    }
 	    else if (p == start && !relative)	// leading "/.." or "/../"
-		STRMOVE(p, tail);		// strip ".." or "../"
+	    {
+		mch_memmove(p, tail, (size_t)(p_end - tail) + 1);		// strip ".." or "../"
+		p_end -= (size_t)(tail - p);
+	    }
 	    else
 	    {
 		if (p == start + 2 && p[-2] == '.')	// leading "./../"
 		{
-		    STRMOVE(p - 2, p);			// strip leading "./"
+		    mch_memmove(p - 2, p, (size_t)(p_end - p) + 1); // strip leading "./"
+		    p_end -= 2;
 		    tail -= 2;
 		}
 		p = tail;		// skip to char after ".." or "../"
@@ -2773,6 +2988,8 @@
 	}
     } while (*p != NUL);
 #endif // !AMIGA
+
+    return (size_t)(p_end - filename);
 }
 
 #if defined(FEAT_EVAL) || defined(PROTO)