xref: /vim-8.2.3635/src/findfile.c (revision ed37d9b3)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * findfile.c: Search for files in directories listed in 'path'
12  */
13 
14 #include "vim.h"
15 
16 /*
17  * File searching functions for 'path', 'tags' and 'cdpath' options.
18  * External visible functions:
19  * vim_findfile_init()		creates/initialises the search context
20  * vim_findfile_free_visited()	free list of visited files/dirs of search
21  *				context
22  * vim_findfile()		find a file in the search context
23  * vim_findfile_cleanup()	cleanup/free search context created by
24  *				vim_findfile_init()
25  *
26  * All static functions and variables start with 'ff_'
27  *
28  * In general it works like this:
29  * First you create yourself a search context by calling vim_findfile_init().
30  * It is possible to give a search context from a previous call to
31  * vim_findfile_init(), so it can be reused. After this you call vim_findfile()
32  * until you are satisfied with the result or it returns NULL. On every call it
33  * returns the next file which matches the conditions given to
34  * vim_findfile_init(). If it doesn't find a next file it returns NULL.
35  *
36  * It is possible to call vim_findfile_init() again to reinitialise your search
37  * with some new parameters. Don't forget to pass your old search context to
38  * it, so it can reuse it and especially reuse the list of already visited
39  * directories. If you want to delete the list of already visited directories
40  * simply call vim_findfile_free_visited().
41  *
42  * When you are done call vim_findfile_cleanup() to free the search context.
43  *
44  * The function vim_findfile_init() has a long comment, which describes the
45  * needed parameters.
46  *
47  *
48  *
49  * ATTENTION:
50  * ==========
51  *	Also we use an allocated search context here, this functions are NOT
52  *	thread-safe!!!!!
53  *
54  *	To minimize parameter passing (or because I'm to lazy), only the
55  *	external visible functions get a search context as a parameter. This is
56  *	then assigned to a static global, which is used throughout the local
57  *	functions.
58  */
59 
60 /*
61  * type for the directory search stack
62  */
63 typedef struct ff_stack
64 {
65     struct ff_stack	*ffs_prev;
66 
67     // the fix part (no wildcards) and the part containing the wildcards
68     // of the search path
69     char_u		*ffs_fix_path;
70 #ifdef FEAT_PATH_EXTRA
71     char_u		*ffs_wc_path;
72 #endif
73 
74     // files/dirs found in the above directory, matched by the first wildcard
75     // of wc_part
76     char_u		**ffs_filearray;
77     int			ffs_filearray_size;
78     char_u		ffs_filearray_cur;   // needed for partly handled dirs
79 
80     // to store status of partly handled directories
81     // 0: we work on this directory for the first time
82     // 1: this directory was partly searched in an earlier step
83     int			ffs_stage;
84 
85     // How deep are we in the directory tree?
86     // Counts backward from value of level parameter to vim_findfile_init
87     int			ffs_level;
88 
89     // Did we already expand '**' to an empty string?
90     int			ffs_star_star_empty;
91 } ff_stack_T;
92 
93 /*
94  * type for already visited directories or files.
95  */
96 typedef struct ff_visited
97 {
98     struct ff_visited	*ffv_next;
99 
100 #ifdef FEAT_PATH_EXTRA
101     // Visited directories are different if the wildcard string are
102     // different. So we have to save it.
103     char_u		*ffv_wc_path;
104 #endif
105     // for unix use inode etc for comparison (needed because of links), else
106     // use filename.
107 #ifdef UNIX
108     int			ffv_dev_valid;	// ffv_dev and ffv_ino were set
109     dev_t		ffv_dev;	// device number
110     ino_t		ffv_ino;	// inode number
111 #endif
112     // The memory for this struct is allocated according to the length of
113     // ffv_fname.
114     char_u		ffv_fname[1];	// actually longer
115 } ff_visited_T;
116 
117 /*
118  * We might have to manage several visited lists during a search.
119  * This is especially needed for the tags option. If tags is set to:
120  *      "./++/tags,./++/TAGS,++/tags"  (replace + with *)
121  * So we have to do 3 searches:
122  *   1) search from the current files directory downward for the file "tags"
123  *   2) search from the current files directory downward for the file "TAGS"
124  *   3) search from Vims current directory downwards for the file "tags"
125  * As you can see, the first and the third search are for the same file, so for
126  * the third search we can use the visited list of the first search. For the
127  * second search we must start from a empty visited list.
128  * The struct ff_visited_list_hdr is used to manage a linked list of already
129  * visited lists.
130  */
131 typedef struct ff_visited_list_hdr
132 {
133     struct ff_visited_list_hdr	*ffvl_next;
134 
135     // the filename the attached visited list is for
136     char_u			*ffvl_filename;
137 
138     ff_visited_T		*ffvl_visited_list;
139 
140 } ff_visited_list_hdr_T;
141 
142 
143 /*
144  * '**' can be expanded to several directory levels.
145  * Set the default maximum depth.
146  */
147 #define FF_MAX_STAR_STAR_EXPAND ((char_u)30)
148 
149 /*
150  * The search context:
151  *   ffsc_stack_ptr:	the stack for the dirs to search
152  *   ffsc_visited_list: the currently active visited list
153  *   ffsc_dir_visited_list: the currently active visited list for search dirs
154  *   ffsc_visited_lists_list: the list of all visited lists
155  *   ffsc_dir_visited_lists_list: the list of all visited lists for search dirs
156  *   ffsc_file_to_search:     the file to search for
157  *   ffsc_start_dir:	the starting directory, if search path was relative
158  *   ffsc_fix_path:	the fix part of the given path (without wildcards)
159  *			Needed for upward search.
160  *   ffsc_wc_path:	the part of the given path containing wildcards
161  *   ffsc_level:	how many levels of dirs to search downwards
162  *   ffsc_stopdirs_v:	array of stop directories for upward search
163  *   ffsc_find_what:	FINDFILE_BOTH, FINDFILE_DIR or FINDFILE_FILE
164  *   ffsc_tagfile:	searching for tags file, don't use 'suffixesadd'
165  */
166 typedef struct ff_search_ctx_T
167 {
168      ff_stack_T			*ffsc_stack_ptr;
169      ff_visited_list_hdr_T	*ffsc_visited_list;
170      ff_visited_list_hdr_T	*ffsc_dir_visited_list;
171      ff_visited_list_hdr_T	*ffsc_visited_lists_list;
172      ff_visited_list_hdr_T	*ffsc_dir_visited_lists_list;
173      char_u			*ffsc_file_to_search;
174      char_u			*ffsc_start_dir;
175      char_u			*ffsc_fix_path;
176 #ifdef FEAT_PATH_EXTRA
177      char_u			*ffsc_wc_path;
178      int			ffsc_level;
179      char_u			**ffsc_stopdirs_v;
180 #endif
181      int			ffsc_find_what;
182      int			ffsc_tagfile;
183 } ff_search_ctx_T;
184 
185 // locally needed functions
186 #ifdef FEAT_PATH_EXTRA
187 static int ff_check_visited(ff_visited_T **, char_u *, char_u *);
188 #else
189 static int ff_check_visited(ff_visited_T **, char_u *);
190 #endif
191 static void vim_findfile_free_visited(void *search_ctx_arg);
192 static void vim_findfile_free_visited_list(ff_visited_list_hdr_T **list_headp);
193 static void ff_free_visited_list(ff_visited_T *vl);
194 static ff_visited_list_hdr_T* ff_get_visited_list(char_u *, ff_visited_list_hdr_T **list_headp);
195 
196 static void ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr);
197 static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx);
198 static void ff_clear(ff_search_ctx_T *search_ctx);
199 static void ff_free_stack_element(ff_stack_T *stack_ptr);
200 #ifdef FEAT_PATH_EXTRA
201 static ff_stack_T *ff_create_stack_element(char_u *, char_u *, int, int);
202 #else
203 static ff_stack_T *ff_create_stack_element(char_u *, int, int);
204 #endif
205 #ifdef FEAT_PATH_EXTRA
206 static int ff_path_in_stoplist(char_u *, int, char_u **);
207 #endif
208 
209 static char_u e_pathtoolong[] = N_("E854: path too long for completion");
210 
211 static char_u	*ff_expand_buffer = NULL; // used for expanding filenames
212 
213 #if 0
214 /*
215  * if someone likes findfirst/findnext, here are the functions
216  * NOT TESTED!!
217  */
218 
219 static void *ff_fn_search_context = NULL;
220 
221     char_u *
222 vim_findfirst(char_u *path, char_u *filename, int level)
223 {
224     ff_fn_search_context =
225 	vim_findfile_init(path, filename, NULL, level, TRUE, FALSE,
226 		ff_fn_search_context, rel_fname);
227     if (NULL == ff_fn_search_context)
228 	return NULL;
229     else
230 	return vim_findnext()
231 }
232 
233     char_u *
234 vim_findnext(void)
235 {
236     char_u *ret = vim_findfile(ff_fn_search_context);
237 
238     if (NULL == ret)
239     {
240 	vim_findfile_cleanup(ff_fn_search_context);
241 	ff_fn_search_context = NULL;
242     }
243     return ret;
244 }
245 #endif
246 
247 /*
248  * Initialization routine for vim_findfile().
249  *
250  * Returns the newly allocated search context or NULL if an error occurred.
251  *
252  * Don't forget to clean up by calling vim_findfile_cleanup() if you are done
253  * with the search context.
254  *
255  * Find the file 'filename' in the directory 'path'.
256  * The parameter 'path' may contain wildcards. If so only search 'level'
257  * directories deep. The parameter 'level' is the absolute maximum and is
258  * not related to restricts given to the '**' wildcard. If 'level' is 100
259  * and you use '**200' vim_findfile() will stop after 100 levels.
260  *
261  * 'filename' cannot contain wildcards!  It is used as-is, no backslashes to
262  * escape special characters.
263  *
264  * If 'stopdirs' is not NULL and nothing is found downward, the search is
265  * restarted on the next higher directory level. This is repeated until the
266  * start-directory of a search is contained in 'stopdirs'. 'stopdirs' has the
267  * format ";*<dirname>*\(;<dirname>\)*;\=$".
268  *
269  * If the 'path' is relative, the starting dir for the search is either VIM's
270  * current dir or if the path starts with "./" the current files dir.
271  * If the 'path' is absolute, the starting dir is that part of the path before
272  * the first wildcard.
273  *
274  * Upward search is only done on the starting dir.
275  *
276  * If 'free_visited' is TRUE the list of already visited files/directories is
277  * cleared. Set this to FALSE if you just want to search from another
278  * directory, but want to be sure that no directory from a previous search is
279  * searched again. This is useful if you search for a file at different places.
280  * The list of visited files/dirs can also be cleared with the function
281  * vim_findfile_free_visited().
282  *
283  * Set the parameter 'find_what' to FINDFILE_DIR if you want to search for
284  * directories only, FINDFILE_FILE for files only, FINDFILE_BOTH for both.
285  *
286  * A search context returned by a previous call to vim_findfile_init() can be
287  * passed in the parameter "search_ctx_arg".  This context is reused and
288  * reinitialized with the new parameters.  The list of already visited
289  * directories from this context is only deleted if the parameter
290  * "free_visited" is true.  Be aware that the passed "search_ctx_arg" is freed
291  * if the reinitialization fails.
292  *
293  * If you don't have a search context from a previous call "search_ctx_arg"
294  * must be NULL.
295  *
296  * This function silently ignores a few errors, vim_findfile() will have
297  * limited functionality then.
298  */
299     void *
300 vim_findfile_init(
301     char_u	*path,
302     char_u	*filename,
303     char_u	*stopdirs UNUSED,
304     int		level,
305     int		free_visited,
306     int		find_what,
307     void	*search_ctx_arg,
308     int		tagfile,	// expanding names of tags files
309     char_u	*rel_fname)	// file name to use for "."
310 {
311 #ifdef FEAT_PATH_EXTRA
312     char_u		*wc_part;
313 #endif
314     ff_stack_T		*sptr;
315     ff_search_ctx_T	*search_ctx;
316 
317     // If a search context is given by the caller, reuse it, else allocate a
318     // new one.
319     if (search_ctx_arg != NULL)
320 	search_ctx = search_ctx_arg;
321     else
322     {
323 	search_ctx = ALLOC_CLEAR_ONE(ff_search_ctx_T);
324 	if (search_ctx == NULL)
325 	    goto error_return;
326     }
327     search_ctx->ffsc_find_what = find_what;
328     search_ctx->ffsc_tagfile = tagfile;
329 
330     // clear the search context, but NOT the visited lists
331     ff_clear(search_ctx);
332 
333     // clear visited list if wanted
334     if (free_visited == TRUE)
335 	vim_findfile_free_visited(search_ctx);
336     else
337     {
338 	// Reuse old visited lists. Get the visited list for the given
339 	// filename. If no list for the current filename exists, creates a new
340 	// one.
341 	search_ctx->ffsc_visited_list = ff_get_visited_list(filename,
342 					&search_ctx->ffsc_visited_lists_list);
343 	if (search_ctx->ffsc_visited_list == NULL)
344 	    goto error_return;
345 	search_ctx->ffsc_dir_visited_list = ff_get_visited_list(filename,
346 				    &search_ctx->ffsc_dir_visited_lists_list);
347 	if (search_ctx->ffsc_dir_visited_list == NULL)
348 	    goto error_return;
349     }
350 
351     if (ff_expand_buffer == NULL)
352     {
353 	ff_expand_buffer = alloc(MAXPATHL);
354 	if (ff_expand_buffer == NULL)
355 	    goto error_return;
356     }
357 
358     // Store information on starting dir now if path is relative.
359     // If path is absolute, we do that later.
360     if (path[0] == '.'
361 	    && (vim_ispathsep(path[1]) || path[1] == NUL)
362 	    && (!tagfile || vim_strchr(p_cpo, CPO_DOTTAG) == NULL)
363 	    && rel_fname != NULL)
364     {
365 	int	len = (int)(gettail(rel_fname) - rel_fname);
366 
367 	if (!vim_isAbsName(rel_fname) && len + 1 < MAXPATHL)
368 	{
369 	    // Make the start dir an absolute path name.
370 	    vim_strncpy(ff_expand_buffer, rel_fname, len);
371 	    search_ctx->ffsc_start_dir = FullName_save(ff_expand_buffer, FALSE);
372 	}
373 	else
374 	    search_ctx->ffsc_start_dir = vim_strnsave(rel_fname, len);
375 	if (search_ctx->ffsc_start_dir == NULL)
376 	    goto error_return;
377 	if (*++path != NUL)
378 	    ++path;
379     }
380     else if (*path == NUL || !vim_isAbsName(path))
381     {
382 #ifdef BACKSLASH_IN_FILENAME
383 	// "c:dir" needs "c:" to be expanded, otherwise use current dir
384 	if (*path != NUL && path[1] == ':')
385 	{
386 	    char_u  drive[3];
387 
388 	    drive[0] = path[0];
389 	    drive[1] = ':';
390 	    drive[2] = NUL;
391 	    if (vim_FullName(drive, ff_expand_buffer, MAXPATHL, TRUE) == FAIL)
392 		goto error_return;
393 	    path += 2;
394 	}
395 	else
396 #endif
397 	if (mch_dirname(ff_expand_buffer, MAXPATHL) == FAIL)
398 	    goto error_return;
399 
400 	search_ctx->ffsc_start_dir = vim_strsave(ff_expand_buffer);
401 	if (search_ctx->ffsc_start_dir == NULL)
402 	    goto error_return;
403 
404 #ifdef BACKSLASH_IN_FILENAME
405 	// A path that starts with "/dir" is relative to the drive, not to the
406 	// directory (but not for "//machine/dir").  Only use the drive name.
407 	if ((*path == '/' || *path == '\\')
408 		&& path[1] != path[0]
409 		&& search_ctx->ffsc_start_dir[1] == ':')
410 	    search_ctx->ffsc_start_dir[2] = NUL;
411 #endif
412     }
413 
414 #ifdef FEAT_PATH_EXTRA
415     /*
416      * If stopdirs are given, split them into an array of pointers.
417      * If this fails (mem allocation), there is no upward search at all or a
418      * stop directory is not recognized -> continue silently.
419      * If stopdirs just contains a ";" or is empty,
420      * search_ctx->ffsc_stopdirs_v will only contain a  NULL pointer. This
421      * is handled as unlimited upward search.  See function
422      * ff_path_in_stoplist() for details.
423      */
424     if (stopdirs != NULL)
425     {
426 	char_u	*walker = stopdirs;
427 	int	dircount;
428 
429 	while (*walker == ';')
430 	    walker++;
431 
432 	dircount = 1;
433 	search_ctx->ffsc_stopdirs_v = ALLOC_ONE(char_u *);
434 
435 	if (search_ctx->ffsc_stopdirs_v != NULL)
436 	{
437 	    do
438 	    {
439 		char_u	*helper;
440 		void	*ptr;
441 
442 		helper = walker;
443 		ptr = vim_realloc(search_ctx->ffsc_stopdirs_v,
444 					   (dircount + 1) * sizeof(char_u *));
445 		if (ptr)
446 		    search_ctx->ffsc_stopdirs_v = ptr;
447 		else
448 		    // ignore, keep what we have and continue
449 		    break;
450 		walker = vim_strchr(walker, ';');
451 		if (walker)
452 		{
453 		    search_ctx->ffsc_stopdirs_v[dircount-1] =
454 				 vim_strnsave(helper, (int)(walker - helper));
455 		    walker++;
456 		}
457 		else
458 		    // this might be "", which means ascent till top
459 		    // of directory tree.
460 		    search_ctx->ffsc_stopdirs_v[dircount-1] =
461 							  vim_strsave(helper);
462 
463 		dircount++;
464 
465 	    } while (walker != NULL);
466 	    search_ctx->ffsc_stopdirs_v[dircount-1] = NULL;
467 	}
468     }
469 #endif
470 
471 #ifdef FEAT_PATH_EXTRA
472     search_ctx->ffsc_level = level;
473 
474     /*
475      * split into:
476      *  -fix path
477      *  -wildcard_stuff (might be NULL)
478      */
479     wc_part = vim_strchr(path, '*');
480     if (wc_part != NULL)
481     {
482 	int	llevel;
483 	int	len;
484 	char	*errpt;
485 
486 	// save the fix part of the path
487 	search_ctx->ffsc_fix_path = vim_strnsave(path, (int)(wc_part - path));
488 
489 	/*
490 	 * copy wc_path and add restricts to the '**' wildcard.
491 	 * The octet after a '**' is used as a (binary) counter.
492 	 * So '**3' is transposed to '**^C' ('^C' is ASCII value 3)
493 	 * or '**76' is transposed to '**N'( 'N' is ASCII value 76).
494 	 * For EBCDIC you get different character values.
495 	 * If no restrict is given after '**' the default is used.
496 	 * Due to this technique the path looks awful if you print it as a
497 	 * string.
498 	 */
499 	len = 0;
500 	while (*wc_part != NUL)
501 	{
502 	    if (len + 5 >= MAXPATHL)
503 	    {
504 		emsg(_(e_pathtoolong));
505 		break;
506 	    }
507 	    if (STRNCMP(wc_part, "**", 2) == 0)
508 	    {
509 		ff_expand_buffer[len++] = *wc_part++;
510 		ff_expand_buffer[len++] = *wc_part++;
511 
512 		llevel = strtol((char *)wc_part, &errpt, 10);
513 		if ((char_u *)errpt != wc_part && llevel > 0 && llevel < 255)
514 		    ff_expand_buffer[len++] = llevel;
515 		else if ((char_u *)errpt != wc_part && llevel == 0)
516 		    // restrict is 0 -> remove already added '**'
517 		    len -= 2;
518 		else
519 		    ff_expand_buffer[len++] = FF_MAX_STAR_STAR_EXPAND;
520 		wc_part = (char_u *)errpt;
521 		if (*wc_part != NUL && !vim_ispathsep(*wc_part))
522 		{
523 		    semsg(_("E343: Invalid path: '**[number]' must be at the end of the path or be followed by '%s'."), PATHSEPSTR);
524 		    goto error_return;
525 		}
526 	    }
527 	    else
528 		ff_expand_buffer[len++] = *wc_part++;
529 	}
530 	ff_expand_buffer[len] = NUL;
531 	search_ctx->ffsc_wc_path = vim_strsave(ff_expand_buffer);
532 
533 	if (search_ctx->ffsc_wc_path == NULL)
534 	    goto error_return;
535     }
536     else
537 #endif
538 	search_ctx->ffsc_fix_path = vim_strsave(path);
539 
540     if (search_ctx->ffsc_start_dir == NULL)
541     {
542 	// store the fix part as startdir.
543 	// This is needed if the parameter path is fully qualified.
544 	search_ctx->ffsc_start_dir = vim_strsave(search_ctx->ffsc_fix_path);
545 	if (search_ctx->ffsc_start_dir == NULL)
546 	    goto error_return;
547 	search_ctx->ffsc_fix_path[0] = NUL;
548     }
549 
550     // create an absolute path
551     if (STRLEN(search_ctx->ffsc_start_dir)
552 			  + STRLEN(search_ctx->ffsc_fix_path) + 3 >= MAXPATHL)
553     {
554 	emsg(_(e_pathtoolong));
555 	goto error_return;
556     }
557     STRCPY(ff_expand_buffer, search_ctx->ffsc_start_dir);
558     add_pathsep(ff_expand_buffer);
559     {
560 	int    eb_len = (int)STRLEN(ff_expand_buffer);
561 	char_u *buf = alloc(eb_len
562 				+ (int)STRLEN(search_ctx->ffsc_fix_path) + 1);
563 
564 	STRCPY(buf, ff_expand_buffer);
565 	STRCPY(buf + eb_len, search_ctx->ffsc_fix_path);
566 	if (mch_isdir(buf))
567 	{
568 	    STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path);
569 	    add_pathsep(ff_expand_buffer);
570 	}
571 #ifdef FEAT_PATH_EXTRA
572 	else
573 	{
574 	    char_u *p =  gettail(search_ctx->ffsc_fix_path);
575 	    char_u *wc_path = NULL;
576 	    char_u *temp = NULL;
577 	    int    len = 0;
578 
579 	    if (p > search_ctx->ffsc_fix_path)
580 	    {
581 		len = (int)(p - search_ctx->ffsc_fix_path) - 1;
582 		STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len);
583 		add_pathsep(ff_expand_buffer);
584 	    }
585 	    else
586 		len = (int)STRLEN(search_ctx->ffsc_fix_path);
587 
588 	    if (search_ctx->ffsc_wc_path != NULL)
589 	    {
590 		wc_path = vim_strsave(search_ctx->ffsc_wc_path);
591 		temp = alloc(STRLEN(search_ctx->ffsc_wc_path)
592 				 + STRLEN(search_ctx->ffsc_fix_path + len)
593 				 + 1);
594 		if (temp == NULL || wc_path == NULL)
595 		{
596 		    vim_free(buf);
597 		    vim_free(temp);
598 		    vim_free(wc_path);
599 		    goto error_return;
600 		}
601 
602 		STRCPY(temp, search_ctx->ffsc_fix_path + len);
603 		STRCAT(temp, search_ctx->ffsc_wc_path);
604 		vim_free(search_ctx->ffsc_wc_path);
605 		vim_free(wc_path);
606 		search_ctx->ffsc_wc_path = temp;
607 	    }
608 	}
609 #endif
610 	vim_free(buf);
611     }
612 
613     sptr = ff_create_stack_element(ff_expand_buffer,
614 #ifdef FEAT_PATH_EXTRA
615 	    search_ctx->ffsc_wc_path,
616 #endif
617 	    level, 0);
618 
619     if (sptr == NULL)
620 	goto error_return;
621 
622     ff_push(search_ctx, sptr);
623 
624     search_ctx->ffsc_file_to_search = vim_strsave(filename);
625     if (search_ctx->ffsc_file_to_search == NULL)
626 	goto error_return;
627 
628     return search_ctx;
629 
630 error_return:
631     /*
632      * We clear the search context now!
633      * Even when the caller gave us a (perhaps valid) context we free it here,
634      * as we might have already destroyed it.
635      */
636     vim_findfile_cleanup(search_ctx);
637     return NULL;
638 }
639 
640 #if defined(FEAT_PATH_EXTRA) || defined(PROTO)
641 /*
642  * Get the stopdir string.  Check that ';' is not escaped.
643  */
644     char_u *
645 vim_findfile_stopdir(char_u *buf)
646 {
647     char_u	*r_ptr = buf;
648 
649     while (*r_ptr != NUL && *r_ptr != ';')
650     {
651 	if (r_ptr[0] == '\\' && r_ptr[1] == ';')
652 	{
653 	    // Overwrite the escape char,
654 	    // use STRLEN(r_ptr) to move the trailing '\0'.
655 	    STRMOVE(r_ptr, r_ptr + 1);
656 	    r_ptr++;
657 	}
658 	r_ptr++;
659     }
660     if (*r_ptr == ';')
661     {
662 	*r_ptr = 0;
663 	r_ptr++;
664     }
665     else if (*r_ptr == NUL)
666 	r_ptr = NULL;
667     return r_ptr;
668 }
669 #endif
670 
671 /*
672  * Clean up the given search context. Can handle a NULL pointer.
673  */
674     void
675 vim_findfile_cleanup(void *ctx)
676 {
677     if (ctx == NULL)
678 	return;
679 
680     vim_findfile_free_visited(ctx);
681     ff_clear(ctx);
682     vim_free(ctx);
683 }
684 
685 /*
686  * Find a file in a search context.
687  * The search context was created with vim_findfile_init() above.
688  * Return a pointer to an allocated file name or NULL if nothing found.
689  * To get all matching files call this function until you get NULL.
690  *
691  * If the passed search_context is NULL, NULL is returned.
692  *
693  * The search algorithm is depth first. To change this replace the
694  * stack with a list (don't forget to leave partly searched directories on the
695  * top of the list).
696  */
697     char_u *
698 vim_findfile(void *search_ctx_arg)
699 {
700     char_u	*file_path;
701 #ifdef FEAT_PATH_EXTRA
702     char_u	*rest_of_wildcards;
703     char_u	*path_end = NULL;
704 #endif
705     ff_stack_T	*stackp;
706 #if defined(FEAT_SEARCHPATH) || defined(FEAT_PATH_EXTRA)
707     int		len;
708 #endif
709     int		i;
710     char_u	*p;
711 #ifdef FEAT_SEARCHPATH
712     char_u	*suf;
713 #endif
714     ff_search_ctx_T *search_ctx;
715 
716     if (search_ctx_arg == NULL)
717 	return NULL;
718 
719     search_ctx = (ff_search_ctx_T *)search_ctx_arg;
720 
721     /*
722      * filepath is used as buffer for various actions and as the storage to
723      * return a found filename.
724      */
725     if ((file_path = alloc(MAXPATHL)) == NULL)
726 	return NULL;
727 
728 #ifdef FEAT_PATH_EXTRA
729     // store the end of the start dir -- needed for upward search
730     if (search_ctx->ffsc_start_dir != NULL)
731 	path_end = &search_ctx->ffsc_start_dir[
732 					  STRLEN(search_ctx->ffsc_start_dir)];
733 #endif
734 
735 #ifdef FEAT_PATH_EXTRA
736     // upward search loop
737     for (;;)
738     {
739 #endif
740 	// downward search loop
741 	for (;;)
742 	{
743 	    // check if user user wants to stop the search
744 	    ui_breakcheck();
745 	    if (got_int)
746 		break;
747 
748 	    // get directory to work on from stack
749 	    stackp = ff_pop(search_ctx);
750 	    if (stackp == NULL)
751 		break;
752 
753 	    /*
754 	     * TODO: decide if we leave this test in
755 	     *
756 	     * GOOD: don't search a directory(-tree) twice.
757 	     * BAD:  - check linked list for every new directory entered.
758 	     *       - check for double files also done below
759 	     *
760 	     * Here we check if we already searched this directory.
761 	     * We already searched a directory if:
762 	     * 1) The directory is the same.
763 	     * 2) We would use the same wildcard string.
764 	     *
765 	     * Good if you have links on same directory via several ways
766 	     *  or you have selfreferences in directories (e.g. SuSE Linux 6.3:
767 	     *  /etc/rc.d/init.d is linked to /etc/rc.d -> endless loop)
768 	     *
769 	     * This check is only needed for directories we work on for the
770 	     * first time (hence stackp->ff_filearray == NULL)
771 	     */
772 	    if (stackp->ffs_filearray == NULL
773 		    && ff_check_visited(&search_ctx->ffsc_dir_visited_list
774 							  ->ffvl_visited_list,
775 			stackp->ffs_fix_path
776 #ifdef FEAT_PATH_EXTRA
777 			, stackp->ffs_wc_path
778 #endif
779 			) == FAIL)
780 	    {
781 #ifdef FF_VERBOSE
782 		if (p_verbose >= 5)
783 		{
784 		    verbose_enter_scroll();
785 		    smsg("Already Searched: %s (%s)",
786 				   stackp->ffs_fix_path, stackp->ffs_wc_path);
787 		    // don't overwrite this either
788 		    msg_puts("\n");
789 		    verbose_leave_scroll();
790 		}
791 #endif
792 		ff_free_stack_element(stackp);
793 		continue;
794 	    }
795 #ifdef FF_VERBOSE
796 	    else if (p_verbose >= 5)
797 	    {
798 		verbose_enter_scroll();
799 		smsg("Searching: %s (%s)",
800 				   stackp->ffs_fix_path, stackp->ffs_wc_path);
801 		// don't overwrite this either
802 		msg_puts("\n");
803 		verbose_leave_scroll();
804 	    }
805 #endif
806 
807 	    // check depth
808 	    if (stackp->ffs_level <= 0)
809 	    {
810 		ff_free_stack_element(stackp);
811 		continue;
812 	    }
813 
814 	    file_path[0] = NUL;
815 
816 	    /*
817 	     * If no filearray till now expand wildcards
818 	     * The function expand_wildcards() can handle an array of paths
819 	     * and all possible expands are returned in one array. We use this
820 	     * to handle the expansion of '**' into an empty string.
821 	     */
822 	    if (stackp->ffs_filearray == NULL)
823 	    {
824 		char_u *dirptrs[2];
825 
826 		// we use filepath to build the path expand_wildcards() should
827 		// expand.
828 		dirptrs[0] = file_path;
829 		dirptrs[1] = NULL;
830 
831 		// if we have a start dir copy it in
832 		if (!vim_isAbsName(stackp->ffs_fix_path)
833 						&& search_ctx->ffsc_start_dir)
834 		{
835 		    if (STRLEN(search_ctx->ffsc_start_dir) + 1 < MAXPATHL)
836 		    {
837 			STRCPY(file_path, search_ctx->ffsc_start_dir);
838 			add_pathsep(file_path);
839 		    }
840 		    else
841 		    {
842 			ff_free_stack_element(stackp);
843 			goto fail;
844 		    }
845 		}
846 
847 		// append the fix part of the search path
848 		if (STRLEN(file_path) + STRLEN(stackp->ffs_fix_path) + 1
849 								    < MAXPATHL)
850 		{
851 		    STRCAT(file_path, stackp->ffs_fix_path);
852 		    add_pathsep(file_path);
853 		}
854 		else
855 		{
856 		    ff_free_stack_element(stackp);
857 		    goto fail;
858 		}
859 
860 #ifdef FEAT_PATH_EXTRA
861 		rest_of_wildcards = stackp->ffs_wc_path;
862 		if (*rest_of_wildcards != NUL)
863 		{
864 		    len = (int)STRLEN(file_path);
865 		    if (STRNCMP(rest_of_wildcards, "**", 2) == 0)
866 		    {
867 			// pointer to the restrict byte
868 			// The restrict byte is not a character!
869 			p = rest_of_wildcards + 2;
870 
871 			if (*p > 0)
872 			{
873 			    (*p)--;
874 			    if (len + 1 < MAXPATHL)
875 				file_path[len++] = '*';
876 			    else
877 			    {
878 				ff_free_stack_element(stackp);
879 				goto fail;
880 			    }
881 			}
882 
883 			if (*p == 0)
884 			{
885 			    // remove '**<numb> from wildcards
886 			    STRMOVE(rest_of_wildcards, rest_of_wildcards + 3);
887 			}
888 			else
889 			    rest_of_wildcards += 3;
890 
891 			if (stackp->ffs_star_star_empty == 0)
892 			{
893 			    // if not done before, expand '**' to empty
894 			    stackp->ffs_star_star_empty = 1;
895 			    dirptrs[1] = stackp->ffs_fix_path;
896 			}
897 		    }
898 
899 		    /*
900 		     * Here we copy until the next path separator or the end of
901 		     * the path. If we stop at a path separator, there is
902 		     * still something else left. This is handled below by
903 		     * pushing every directory returned from expand_wildcards()
904 		     * on the stack again for further search.
905 		     */
906 		    while (*rest_of_wildcards
907 			    && !vim_ispathsep(*rest_of_wildcards))
908 			if (len + 1 < MAXPATHL)
909 			    file_path[len++] = *rest_of_wildcards++;
910 			else
911 			{
912 			    ff_free_stack_element(stackp);
913 			    goto fail;
914 			}
915 
916 		    file_path[len] = NUL;
917 		    if (vim_ispathsep(*rest_of_wildcards))
918 			rest_of_wildcards++;
919 		}
920 #endif
921 
922 		/*
923 		 * Expand wildcards like "*" and "$VAR".
924 		 * If the path is a URL don't try this.
925 		 */
926 		if (path_with_url(dirptrs[0]))
927 		{
928 		    stackp->ffs_filearray = ALLOC_ONE(char_u *);
929 		    if (stackp->ffs_filearray != NULL
930 			    && (stackp->ffs_filearray[0]
931 				= vim_strsave(dirptrs[0])) != NULL)
932 			stackp->ffs_filearray_size = 1;
933 		    else
934 			stackp->ffs_filearray_size = 0;
935 		}
936 		else
937 		    // Add EW_NOTWILD because the expanded path may contain
938 		    // wildcard characters that are to be taken literally.
939 		    // This is a bit of a hack.
940 		    expand_wildcards((dirptrs[1] == NULL) ? 1 : 2, dirptrs,
941 			    &stackp->ffs_filearray_size,
942 			    &stackp->ffs_filearray,
943 			    EW_DIR|EW_ADDSLASH|EW_SILENT|EW_NOTWILD);
944 
945 		stackp->ffs_filearray_cur = 0;
946 		stackp->ffs_stage = 0;
947 	    }
948 #ifdef FEAT_PATH_EXTRA
949 	    else
950 		rest_of_wildcards = &stackp->ffs_wc_path[
951 						 STRLEN(stackp->ffs_wc_path)];
952 #endif
953 
954 	    if (stackp->ffs_stage == 0)
955 	    {
956 		// this is the first time we work on this directory
957 #ifdef FEAT_PATH_EXTRA
958 		if (*rest_of_wildcards == NUL)
959 #endif
960 		{
961 		    /*
962 		     * We don't have further wildcards to expand, so we have to
963 		     * check for the final file now.
964 		     */
965 		    for (i = stackp->ffs_filearray_cur;
966 					  i < stackp->ffs_filearray_size; ++i)
967 		    {
968 			if (!path_with_url(stackp->ffs_filearray[i])
969 				      && !mch_isdir(stackp->ffs_filearray[i]))
970 			    continue;   // not a directory
971 
972 			// prepare the filename to be checked for existence
973 			// below
974 			if (STRLEN(stackp->ffs_filearray[i]) + 1
975 				+ STRLEN(search_ctx->ffsc_file_to_search)
976 								    < MAXPATHL)
977 			{
978 			    STRCPY(file_path, stackp->ffs_filearray[i]);
979 			    add_pathsep(file_path);
980 			    STRCAT(file_path, search_ctx->ffsc_file_to_search);
981 			}
982 			else
983 			{
984 			    ff_free_stack_element(stackp);
985 			    goto fail;
986 			}
987 
988 			/*
989 			 * Try without extra suffix and then with suffixes
990 			 * from 'suffixesadd'.
991 			 */
992 #ifdef FEAT_SEARCHPATH
993 			len = (int)STRLEN(file_path);
994 			if (search_ctx->ffsc_tagfile)
995 			    suf = (char_u *)"";
996 			else
997 			    suf = curbuf->b_p_sua;
998 			for (;;)
999 #endif
1000 			{
1001 			    // if file exists and we didn't already find it
1002 			    if ((path_with_url(file_path)
1003 				  || (mch_getperm(file_path) >= 0
1004 				      && (search_ctx->ffsc_find_what
1005 							      == FINDFILE_BOTH
1006 					  || ((search_ctx->ffsc_find_what
1007 							      == FINDFILE_DIR)
1008 						   == mch_isdir(file_path)))))
1009 #ifndef FF_VERBOSE
1010 				    && (ff_check_visited(
1011 					    &search_ctx->ffsc_visited_list->ffvl_visited_list,
1012 					    file_path
1013 #ifdef FEAT_PATH_EXTRA
1014 					    , (char_u *)""
1015 #endif
1016 					    ) == OK)
1017 #endif
1018 			       )
1019 			    {
1020 #ifdef FF_VERBOSE
1021 				if (ff_check_visited(
1022 					    &search_ctx->ffsc_visited_list->ffvl_visited_list,
1023 					    file_path
1024 #ifdef FEAT_PATH_EXTRA
1025 					    , (char_u *)""
1026 #endif
1027 						    ) == FAIL)
1028 				{
1029 				    if (p_verbose >= 5)
1030 				    {
1031 					verbose_enter_scroll();
1032 					smsg("Already: %s",
1033 								   file_path);
1034 					// don't overwrite this either
1035 					msg_puts("\n");
1036 					verbose_leave_scroll();
1037 				    }
1038 				    continue;
1039 				}
1040 #endif
1041 
1042 				// push dir to examine rest of subdirs later
1043 				stackp->ffs_filearray_cur = i + 1;
1044 				ff_push(search_ctx, stackp);
1045 
1046 				if (!path_with_url(file_path))
1047 				    simplify_filename(file_path);
1048 				if (mch_dirname(ff_expand_buffer, MAXPATHL)
1049 									== OK)
1050 				{
1051 				    p = shorten_fname(file_path,
1052 							    ff_expand_buffer);
1053 				    if (p != NULL)
1054 					STRMOVE(file_path, p);
1055 				}
1056 #ifdef FF_VERBOSE
1057 				if (p_verbose >= 5)
1058 				{
1059 				    verbose_enter_scroll();
1060 				    smsg("HIT: %s", file_path);
1061 				    // don't overwrite this either
1062 				    msg_puts("\n");
1063 				    verbose_leave_scroll();
1064 				}
1065 #endif
1066 				return file_path;
1067 			    }
1068 
1069 #ifdef FEAT_SEARCHPATH
1070 			    // Not found or found already, try next suffix.
1071 			    if (*suf == NUL)
1072 				break;
1073 			    copy_option_part(&suf, file_path + len,
1074 							 MAXPATHL - len, ",");
1075 #endif
1076 			}
1077 		    }
1078 		}
1079 #ifdef FEAT_PATH_EXTRA
1080 		else
1081 		{
1082 		    /*
1083 		     * still wildcards left, push the directories for further
1084 		     * search
1085 		     */
1086 		    for (i = stackp->ffs_filearray_cur;
1087 					  i < stackp->ffs_filearray_size; ++i)
1088 		    {
1089 			if (!mch_isdir(stackp->ffs_filearray[i]))
1090 			    continue;	// not a directory
1091 
1092 			ff_push(search_ctx,
1093 				ff_create_stack_element(
1094 						     stackp->ffs_filearray[i],
1095 						     rest_of_wildcards,
1096 						     stackp->ffs_level - 1, 0));
1097 		    }
1098 		}
1099 #endif
1100 		stackp->ffs_filearray_cur = 0;
1101 		stackp->ffs_stage = 1;
1102 	    }
1103 
1104 #ifdef FEAT_PATH_EXTRA
1105 	    /*
1106 	     * if wildcards contains '**' we have to descent till we reach the
1107 	     * leaves of the directory tree.
1108 	     */
1109 	    if (STRNCMP(stackp->ffs_wc_path, "**", 2) == 0)
1110 	    {
1111 		for (i = stackp->ffs_filearray_cur;
1112 					  i < stackp->ffs_filearray_size; ++i)
1113 		{
1114 		    if (fnamecmp(stackp->ffs_filearray[i],
1115 						   stackp->ffs_fix_path) == 0)
1116 			continue; // don't repush same directory
1117 		    if (!mch_isdir(stackp->ffs_filearray[i]))
1118 			continue;   // not a directory
1119 		    ff_push(search_ctx,
1120 			    ff_create_stack_element(stackp->ffs_filearray[i],
1121 				stackp->ffs_wc_path, stackp->ffs_level - 1, 1));
1122 		}
1123 	    }
1124 #endif
1125 
1126 	    // we are done with the current directory
1127 	    ff_free_stack_element(stackp);
1128 
1129 	}
1130 
1131 #ifdef FEAT_PATH_EXTRA
1132 	// If we reached this, we didn't find anything downwards.
1133 	// Let's check if we should do an upward search.
1134 	if (search_ctx->ffsc_start_dir
1135 		&& search_ctx->ffsc_stopdirs_v != NULL && !got_int)
1136 	{
1137 	    ff_stack_T  *sptr;
1138 
1139 	    // is the last starting directory in the stop list?
1140 	    if (ff_path_in_stoplist(search_ctx->ffsc_start_dir,
1141 		       (int)(path_end - search_ctx->ffsc_start_dir),
1142 		       search_ctx->ffsc_stopdirs_v) == TRUE)
1143 		break;
1144 
1145 	    // cut of last dir
1146 	    while (path_end > search_ctx->ffsc_start_dir
1147 						  && vim_ispathsep(*path_end))
1148 		path_end--;
1149 	    while (path_end > search_ctx->ffsc_start_dir
1150 					      && !vim_ispathsep(path_end[-1]))
1151 		path_end--;
1152 	    *path_end = 0;
1153 	    path_end--;
1154 
1155 	    if (*search_ctx->ffsc_start_dir == 0)
1156 		break;
1157 
1158 	    if (STRLEN(search_ctx->ffsc_start_dir) + 1
1159 		    + STRLEN(search_ctx->ffsc_fix_path) < MAXPATHL)
1160 	    {
1161 		STRCPY(file_path, search_ctx->ffsc_start_dir);
1162 		add_pathsep(file_path);
1163 		STRCAT(file_path, search_ctx->ffsc_fix_path);
1164 	    }
1165 	    else
1166 		goto fail;
1167 
1168 	    // create a new stack entry
1169 	    sptr = ff_create_stack_element(file_path,
1170 		    search_ctx->ffsc_wc_path, search_ctx->ffsc_level, 0);
1171 	    if (sptr == NULL)
1172 		break;
1173 	    ff_push(search_ctx, sptr);
1174 	}
1175 	else
1176 	    break;
1177     }
1178 #endif
1179 
1180 fail:
1181     vim_free(file_path);
1182     return NULL;
1183 }
1184 
1185 /*
1186  * Free the list of lists of visited files and directories
1187  * Can handle it if the passed search_context is NULL;
1188  */
1189     static void
1190 vim_findfile_free_visited(void *search_ctx_arg)
1191 {
1192     ff_search_ctx_T *search_ctx;
1193 
1194     if (search_ctx_arg == NULL)
1195 	return;
1196 
1197     search_ctx = (ff_search_ctx_T *)search_ctx_arg;
1198     vim_findfile_free_visited_list(&search_ctx->ffsc_visited_lists_list);
1199     vim_findfile_free_visited_list(&search_ctx->ffsc_dir_visited_lists_list);
1200 }
1201 
1202     static void
1203 vim_findfile_free_visited_list(ff_visited_list_hdr_T **list_headp)
1204 {
1205     ff_visited_list_hdr_T *vp;
1206 
1207     while (*list_headp != NULL)
1208     {
1209 	vp = (*list_headp)->ffvl_next;
1210 	ff_free_visited_list((*list_headp)->ffvl_visited_list);
1211 
1212 	vim_free((*list_headp)->ffvl_filename);
1213 	vim_free(*list_headp);
1214 	*list_headp = vp;
1215     }
1216     *list_headp = NULL;
1217 }
1218 
1219     static void
1220 ff_free_visited_list(ff_visited_T *vl)
1221 {
1222     ff_visited_T *vp;
1223 
1224     while (vl != NULL)
1225     {
1226 	vp = vl->ffv_next;
1227 #ifdef FEAT_PATH_EXTRA
1228 	vim_free(vl->ffv_wc_path);
1229 #endif
1230 	vim_free(vl);
1231 	vl = vp;
1232     }
1233     vl = NULL;
1234 }
1235 
1236 /*
1237  * Returns the already visited list for the given filename. If none is found it
1238  * allocates a new one.
1239  */
1240     static ff_visited_list_hdr_T*
1241 ff_get_visited_list(
1242     char_u			*filename,
1243     ff_visited_list_hdr_T	**list_headp)
1244 {
1245     ff_visited_list_hdr_T  *retptr = NULL;
1246 
1247     // check if a visited list for the given filename exists
1248     if (*list_headp != NULL)
1249     {
1250 	retptr = *list_headp;
1251 	while (retptr != NULL)
1252 	{
1253 	    if (fnamecmp(filename, retptr->ffvl_filename) == 0)
1254 	    {
1255 #ifdef FF_VERBOSE
1256 		if (p_verbose >= 5)
1257 		{
1258 		    verbose_enter_scroll();
1259 		    smsg("ff_get_visited_list: FOUND list for %s",
1260 								    filename);
1261 		    // don't overwrite this either
1262 		    msg_puts("\n");
1263 		    verbose_leave_scroll();
1264 		}
1265 #endif
1266 		return retptr;
1267 	    }
1268 	    retptr = retptr->ffvl_next;
1269 	}
1270     }
1271 
1272 #ifdef FF_VERBOSE
1273     if (p_verbose >= 5)
1274     {
1275 	verbose_enter_scroll();
1276 	smsg("ff_get_visited_list: new list for %s", filename);
1277 	// don't overwrite this either
1278 	msg_puts("\n");
1279 	verbose_leave_scroll();
1280     }
1281 #endif
1282 
1283     /*
1284      * if we reach this we didn't find a list and we have to allocate new list
1285      */
1286     retptr = ALLOC_ONE(ff_visited_list_hdr_T);
1287     if (retptr == NULL)
1288 	return NULL;
1289 
1290     retptr->ffvl_visited_list = NULL;
1291     retptr->ffvl_filename = vim_strsave(filename);
1292     if (retptr->ffvl_filename == NULL)
1293     {
1294 	vim_free(retptr);
1295 	return NULL;
1296     }
1297     retptr->ffvl_next = *list_headp;
1298     *list_headp = retptr;
1299 
1300     return retptr;
1301 }
1302 
1303 #ifdef FEAT_PATH_EXTRA
1304 /*
1305  * check if two wildcard paths are equal. Returns TRUE or FALSE.
1306  * They are equal if:
1307  *  - both paths are NULL
1308  *  - they have the same length
1309  *  - char by char comparison is OK
1310  *  - the only differences are in the counters behind a '**', so
1311  *    '**\20' is equal to '**\24'
1312  */
1313     static int
1314 ff_wc_equal(char_u *s1, char_u *s2)
1315 {
1316     int		i, j;
1317     int		c1 = NUL;
1318     int		c2 = NUL;
1319     int		prev1 = NUL;
1320     int		prev2 = NUL;
1321 
1322     if (s1 == s2)
1323 	return TRUE;
1324 
1325     if (s1 == NULL || s2 == NULL)
1326 	return FALSE;
1327 
1328     for (i = 0, j = 0; s1[i] != NUL && s2[j] != NUL;)
1329     {
1330 	c1 = PTR2CHAR(s1 + i);
1331 	c2 = PTR2CHAR(s2 + j);
1332 
1333 	if ((p_fic ? MB_TOLOWER(c1) != MB_TOLOWER(c2) : c1 != c2)
1334 		&& (prev1 != '*' || prev2 != '*'))
1335 	    return FALSE;
1336 	prev2 = prev1;
1337 	prev1 = c1;
1338 
1339 	i += mb_ptr2len(s1 + i);
1340 	j += mb_ptr2len(s2 + j);
1341     }
1342     return s1[i] == s2[j];
1343 }
1344 #endif
1345 
1346 /*
1347  * maintains the list of already visited files and dirs
1348  * returns FAIL if the given file/dir is already in the list
1349  * returns OK if it is newly added
1350  *
1351  * TODO: What to do on memory allocation problems?
1352  *	 -> return TRUE - Better the file is found several times instead of
1353  *	    never.
1354  */
1355     static int
1356 ff_check_visited(
1357     ff_visited_T	**visited_list,
1358     char_u		*fname
1359 #ifdef FEAT_PATH_EXTRA
1360     , char_u		*wc_path
1361 #endif
1362     )
1363 {
1364     ff_visited_T	*vp;
1365 #ifdef UNIX
1366     stat_T		st;
1367     int			url = FALSE;
1368 #endif
1369 
1370     // For an URL we only compare the name, otherwise we compare the
1371     // device/inode (unix) or the full path name (not Unix).
1372     if (path_with_url(fname))
1373     {
1374 	vim_strncpy(ff_expand_buffer, fname, MAXPATHL - 1);
1375 #ifdef UNIX
1376 	url = TRUE;
1377 #endif
1378     }
1379     else
1380     {
1381 	ff_expand_buffer[0] = NUL;
1382 #ifdef UNIX
1383 	if (mch_stat((char *)fname, &st) < 0)
1384 #else
1385 	if (vim_FullName(fname, ff_expand_buffer, MAXPATHL, TRUE) == FAIL)
1386 #endif
1387 	    return FAIL;
1388     }
1389 
1390     // check against list of already visited files
1391     for (vp = *visited_list; vp != NULL; vp = vp->ffv_next)
1392     {
1393 	if (
1394 #ifdef UNIX
1395 		!url ? (vp->ffv_dev_valid && vp->ffv_dev == st.st_dev
1396 						  && vp->ffv_ino == st.st_ino)
1397 		     :
1398 #endif
1399 		fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0
1400 	   )
1401 	{
1402 #ifdef FEAT_PATH_EXTRA
1403 	    // are the wildcard parts equal
1404 	    if (ff_wc_equal(vp->ffv_wc_path, wc_path) == TRUE)
1405 #endif
1406 		// already visited
1407 		return FAIL;
1408 	}
1409     }
1410 
1411     /*
1412      * New file/dir.  Add it to the list of visited files/dirs.
1413      */
1414     vp = alloc(sizeof(ff_visited_T) + STRLEN(ff_expand_buffer));
1415 
1416     if (vp != NULL)
1417     {
1418 #ifdef UNIX
1419 	if (!url)
1420 	{
1421 	    vp->ffv_dev_valid = TRUE;
1422 	    vp->ffv_ino = st.st_ino;
1423 	    vp->ffv_dev = st.st_dev;
1424 	    vp->ffv_fname[0] = NUL;
1425 	}
1426 	else
1427 	{
1428 	    vp->ffv_dev_valid = FALSE;
1429 #endif
1430 	    STRCPY(vp->ffv_fname, ff_expand_buffer);
1431 #ifdef UNIX
1432 	}
1433 #endif
1434 #ifdef FEAT_PATH_EXTRA
1435 	if (wc_path != NULL)
1436 	    vp->ffv_wc_path = vim_strsave(wc_path);
1437 	else
1438 	    vp->ffv_wc_path = NULL;
1439 #endif
1440 
1441 	vp->ffv_next = *visited_list;
1442 	*visited_list = vp;
1443     }
1444 
1445     return OK;
1446 }
1447 
1448 /*
1449  * create stack element from given path pieces
1450  */
1451     static ff_stack_T *
1452 ff_create_stack_element(
1453     char_u	*fix_part,
1454 #ifdef FEAT_PATH_EXTRA
1455     char_u	*wc_part,
1456 #endif
1457     int		level,
1458     int		star_star_empty)
1459 {
1460     ff_stack_T	*new;
1461 
1462     new = ALLOC_ONE(ff_stack_T);
1463     if (new == NULL)
1464 	return NULL;
1465 
1466     new->ffs_prev	   = NULL;
1467     new->ffs_filearray	   = NULL;
1468     new->ffs_filearray_size = 0;
1469     new->ffs_filearray_cur  = 0;
1470     new->ffs_stage	   = 0;
1471     new->ffs_level	   = level;
1472     new->ffs_star_star_empty = star_star_empty;
1473 
1474     // the following saves NULL pointer checks in vim_findfile
1475     if (fix_part == NULL)
1476 	fix_part = (char_u *)"";
1477     new->ffs_fix_path = vim_strsave(fix_part);
1478 
1479 #ifdef FEAT_PATH_EXTRA
1480     if (wc_part == NULL)
1481 	wc_part  = (char_u *)"";
1482     new->ffs_wc_path = vim_strsave(wc_part);
1483 #endif
1484 
1485     if (new->ffs_fix_path == NULL
1486 #ifdef FEAT_PATH_EXTRA
1487 	    || new->ffs_wc_path == NULL
1488 #endif
1489 	    )
1490     {
1491 	ff_free_stack_element(new);
1492 	new = NULL;
1493     }
1494 
1495     return new;
1496 }
1497 
1498 /*
1499  * Push a dir on the directory stack.
1500  */
1501     static void
1502 ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr)
1503 {
1504     // check for NULL pointer, not to return an error to the user, but
1505     // to prevent a crash
1506     if (stack_ptr != NULL)
1507     {
1508 	stack_ptr->ffs_prev = search_ctx->ffsc_stack_ptr;
1509 	search_ctx->ffsc_stack_ptr = stack_ptr;
1510     }
1511 }
1512 
1513 /*
1514  * Pop a dir from the directory stack.
1515  * Returns NULL if stack is empty.
1516  */
1517     static ff_stack_T *
1518 ff_pop(ff_search_ctx_T *search_ctx)
1519 {
1520     ff_stack_T  *sptr;
1521 
1522     sptr = search_ctx->ffsc_stack_ptr;
1523     if (search_ctx->ffsc_stack_ptr != NULL)
1524 	search_ctx->ffsc_stack_ptr = search_ctx->ffsc_stack_ptr->ffs_prev;
1525 
1526     return sptr;
1527 }
1528 
1529 /*
1530  * free the given stack element
1531  */
1532     static void
1533 ff_free_stack_element(ff_stack_T *stack_ptr)
1534 {
1535     // vim_free handles possible NULL pointers
1536     vim_free(stack_ptr->ffs_fix_path);
1537 #ifdef FEAT_PATH_EXTRA
1538     vim_free(stack_ptr->ffs_wc_path);
1539 #endif
1540 
1541     if (stack_ptr->ffs_filearray != NULL)
1542 	FreeWild(stack_ptr->ffs_filearray_size, stack_ptr->ffs_filearray);
1543 
1544     vim_free(stack_ptr);
1545 }
1546 
1547 /*
1548  * Clear the search context, but NOT the visited list.
1549  */
1550     static void
1551 ff_clear(ff_search_ctx_T *search_ctx)
1552 {
1553     ff_stack_T   *sptr;
1554 
1555     // clear up stack
1556     while ((sptr = ff_pop(search_ctx)) != NULL)
1557 	ff_free_stack_element(sptr);
1558 
1559     vim_free(search_ctx->ffsc_file_to_search);
1560     vim_free(search_ctx->ffsc_start_dir);
1561     vim_free(search_ctx->ffsc_fix_path);
1562 #ifdef FEAT_PATH_EXTRA
1563     vim_free(search_ctx->ffsc_wc_path);
1564 #endif
1565 
1566 #ifdef FEAT_PATH_EXTRA
1567     if (search_ctx->ffsc_stopdirs_v != NULL)
1568     {
1569 	int  i = 0;
1570 
1571 	while (search_ctx->ffsc_stopdirs_v[i] != NULL)
1572 	{
1573 	    vim_free(search_ctx->ffsc_stopdirs_v[i]);
1574 	    i++;
1575 	}
1576 	vim_free(search_ctx->ffsc_stopdirs_v);
1577     }
1578     search_ctx->ffsc_stopdirs_v = NULL;
1579 #endif
1580 
1581     // reset everything
1582     search_ctx->ffsc_file_to_search = NULL;
1583     search_ctx->ffsc_start_dir = NULL;
1584     search_ctx->ffsc_fix_path = NULL;
1585 #ifdef FEAT_PATH_EXTRA
1586     search_ctx->ffsc_wc_path = NULL;
1587     search_ctx->ffsc_level = 0;
1588 #endif
1589 }
1590 
1591 #ifdef FEAT_PATH_EXTRA
1592 /*
1593  * check if the given path is in the stopdirs
1594  * returns TRUE if yes else FALSE
1595  */
1596     static int
1597 ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
1598 {
1599     int		i = 0;
1600 
1601     // eat up trailing path separators, except the first
1602     while (path_len > 1 && vim_ispathsep(path[path_len - 1]))
1603 	path_len--;
1604 
1605     // if no path consider it as match
1606     if (path_len == 0)
1607 	return TRUE;
1608 
1609     for (i = 0; stopdirs_v[i] != NULL; i++)
1610     {
1611 	if ((int)STRLEN(stopdirs_v[i]) > path_len)
1612 	{
1613 	    // match for parent directory. So '/home' also matches
1614 	    // '/home/rks'. Check for PATHSEP in stopdirs_v[i], else
1615 	    // '/home/r' would also match '/home/rks'
1616 	    if (fnamencmp(stopdirs_v[i], path, path_len) == 0
1617 		    && vim_ispathsep(stopdirs_v[i][path_len]))
1618 		return TRUE;
1619 	}
1620 	else
1621 	{
1622 	    if (fnamecmp(stopdirs_v[i], path) == 0)
1623 		return TRUE;
1624 	}
1625     }
1626     return FALSE;
1627 }
1628 #endif
1629 
1630 #if defined(FEAT_SEARCHPATH) || defined(PROTO)
1631 /*
1632  * Find the file name "ptr[len]" in the path.  Also finds directory names.
1633  *
1634  * On the first call set the parameter 'first' to TRUE to initialize
1635  * the search.  For repeating calls to FALSE.
1636  *
1637  * Repeating calls will return other files called 'ptr[len]' from the path.
1638  *
1639  * Only on the first call 'ptr' and 'len' are used.  For repeating calls they
1640  * don't need valid values.
1641  *
1642  * If nothing found on the first call the option FNAME_MESS will issue the
1643  * message:
1644  *	    'Can't find file "<file>" in path'
1645  * On repeating calls:
1646  *	    'No more file "<file>" found in path'
1647  *
1648  * options:
1649  * FNAME_MESS	    give error message when not found
1650  *
1651  * Uses NameBuff[]!
1652  *
1653  * Returns an allocated string for the file name.  NULL for error.
1654  *
1655  */
1656     char_u *
1657 find_file_in_path(
1658     char_u	*ptr,		// file name
1659     int		len,		// length of file name
1660     int		options,
1661     int		first,		// use count'th matching file name
1662     char_u	*rel_fname)	// file name searching relative to
1663 {
1664     return find_file_in_path_option(ptr, len, options, first,
1665 	    *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path,
1666 	    FINDFILE_BOTH, rel_fname, curbuf->b_p_sua);
1667 }
1668 
1669 static char_u	*ff_file_to_find = NULL;
1670 static void	*fdip_search_ctx = NULL;
1671 
1672 # if defined(EXITFREE) || defined(PROTO)
1673     void
1674 free_findfile(void)
1675 {
1676     vim_free(ff_file_to_find);
1677     vim_findfile_cleanup(fdip_search_ctx);
1678     vim_free(ff_expand_buffer);
1679 }
1680 # endif
1681 
1682 /*
1683  * Find the directory name "ptr[len]" in the path.
1684  *
1685  * options:
1686  * FNAME_MESS	    give error message when not found
1687  * FNAME_UNESC	    unescape backslashes.
1688  *
1689  * Uses NameBuff[]!
1690  *
1691  * Returns an allocated string for the file name.  NULL for error.
1692  */
1693     char_u *
1694 find_directory_in_path(
1695     char_u	*ptr,		// file name
1696     int		len,		// length of file name
1697     int		options,
1698     char_u	*rel_fname)	// file name searching relative to
1699 {
1700     return find_file_in_path_option(ptr, len, options, TRUE, p_cdpath,
1701 				       FINDFILE_DIR, rel_fname, (char_u *)"");
1702 }
1703 
1704     char_u *
1705 find_file_in_path_option(
1706     char_u	*ptr,		// file name
1707     int		len,		// length of file name
1708     int		options,
1709     int		first,		// use count'th matching file name
1710     char_u	*path_option,	// p_path or p_cdpath
1711     int		find_what,	// FINDFILE_FILE, _DIR or _BOTH
1712     char_u	*rel_fname,	// file name we are looking relative to.
1713     char_u	*suffixes)	// list of suffixes, 'suffixesadd' option
1714 {
1715     static char_u	*dir;
1716     static int		did_findfile_init = FALSE;
1717     char_u		save_char;
1718     char_u		*file_name = NULL;
1719     char_u		*buf = NULL;
1720     int			rel_to_curdir;
1721 # ifdef AMIGA
1722     struct Process	*proc = (struct Process *)FindTask(0L);
1723     APTR		save_winptr = proc->pr_WindowPtr;
1724 
1725     // Avoid a requester here for a volume that doesn't exist.
1726     proc->pr_WindowPtr = (APTR)-1L;
1727 # endif
1728 
1729     if (first == TRUE)
1730     {
1731 	// copy file name into NameBuff, expanding environment variables
1732 	save_char = ptr[len];
1733 	ptr[len] = NUL;
1734 	expand_env_esc(ptr, NameBuff, MAXPATHL, FALSE, TRUE, NULL);
1735 	ptr[len] = save_char;
1736 
1737 	vim_free(ff_file_to_find);
1738 	ff_file_to_find = vim_strsave(NameBuff);
1739 	if (ff_file_to_find == NULL)	// out of memory
1740 	{
1741 	    file_name = NULL;
1742 	    goto theend;
1743 	}
1744 	if (options & FNAME_UNESC)
1745 	{
1746 	    // Change all "\ " to " ".
1747 	    for (ptr = ff_file_to_find; *ptr != NUL; ++ptr)
1748 		if (ptr[0] == '\\' && ptr[1] == ' ')
1749 		    mch_memmove(ptr, ptr + 1, STRLEN(ptr));
1750 	}
1751     }
1752 
1753     rel_to_curdir = (ff_file_to_find[0] == '.'
1754 		    && (ff_file_to_find[1] == NUL
1755 			|| vim_ispathsep(ff_file_to_find[1])
1756 			|| (ff_file_to_find[1] == '.'
1757 			    && (ff_file_to_find[2] == NUL
1758 				|| vim_ispathsep(ff_file_to_find[2])))));
1759     if (vim_isAbsName(ff_file_to_find)
1760 	    // "..", "../path", "." and "./path": don't use the path_option
1761 	    || rel_to_curdir
1762 # if defined(MSWIN)
1763 	    // handle "\tmp" as absolute path
1764 	    || vim_ispathsep(ff_file_to_find[0])
1765 	    // handle "c:name" as absolute path
1766 	    || (ff_file_to_find[0] != NUL && ff_file_to_find[1] == ':')
1767 # endif
1768 # ifdef AMIGA
1769 	    // handle ":tmp" as absolute path
1770 	    || ff_file_to_find[0] == ':'
1771 # endif
1772        )
1773     {
1774 	/*
1775 	 * Absolute path, no need to use "path_option".
1776 	 * If this is not a first call, return NULL.  We already returned a
1777 	 * filename on the first call.
1778 	 */
1779 	if (first == TRUE)
1780 	{
1781 	    int		l;
1782 	    int		run;
1783 
1784 	    if (path_with_url(ff_file_to_find))
1785 	    {
1786 		file_name = vim_strsave(ff_file_to_find);
1787 		goto theend;
1788 	    }
1789 
1790 	    // When FNAME_REL flag given first use the directory of the file.
1791 	    // Otherwise or when this fails use the current directory.
1792 	    for (run = 1; run <= 2; ++run)
1793 	    {
1794 		l = (int)STRLEN(ff_file_to_find);
1795 		if (run == 1
1796 			&& rel_to_curdir
1797 			&& (options & FNAME_REL)
1798 			&& rel_fname != NULL
1799 			&& STRLEN(rel_fname) + l < MAXPATHL)
1800 		{
1801 		    STRCPY(NameBuff, rel_fname);
1802 		    STRCPY(gettail(NameBuff), ff_file_to_find);
1803 		    l = (int)STRLEN(NameBuff);
1804 		}
1805 		else
1806 		{
1807 		    STRCPY(NameBuff, ff_file_to_find);
1808 		    run = 2;
1809 		}
1810 
1811 		// When the file doesn't exist, try adding parts of
1812 		// 'suffixesadd'.
1813 		buf = suffixes;
1814 		for (;;)
1815 		{
1816 		    if (mch_getperm(NameBuff) >= 0
1817 			     && (find_what == FINDFILE_BOTH
1818 				 || ((find_what == FINDFILE_DIR)
1819 						    == mch_isdir(NameBuff))))
1820 		    {
1821 			file_name = vim_strsave(NameBuff);
1822 			goto theend;
1823 		    }
1824 		    if (*buf == NUL)
1825 			break;
1826 		    copy_option_part(&buf, NameBuff + l, MAXPATHL - l, ",");
1827 		}
1828 	    }
1829 	}
1830     }
1831     else
1832     {
1833 	/*
1834 	 * Loop over all paths in the 'path' or 'cdpath' option.
1835 	 * When "first" is set, first setup to the start of the option.
1836 	 * Otherwise continue to find the next match.
1837 	 */
1838 	if (first == TRUE)
1839 	{
1840 	    // vim_findfile_free_visited can handle a possible NULL pointer
1841 	    vim_findfile_free_visited(fdip_search_ctx);
1842 	    dir = path_option;
1843 	    did_findfile_init = FALSE;
1844 	}
1845 
1846 	for (;;)
1847 	{
1848 	    if (did_findfile_init)
1849 	    {
1850 		file_name = vim_findfile(fdip_search_ctx);
1851 		if (file_name != NULL)
1852 		    break;
1853 
1854 		did_findfile_init = FALSE;
1855 	    }
1856 	    else
1857 	    {
1858 		char_u  *r_ptr;
1859 
1860 		if (dir == NULL || *dir == NUL)
1861 		{
1862 		    // We searched all paths of the option, now we can
1863 		    // free the search context.
1864 		    vim_findfile_cleanup(fdip_search_ctx);
1865 		    fdip_search_ctx = NULL;
1866 		    break;
1867 		}
1868 
1869 		if ((buf = alloc(MAXPATHL)) == NULL)
1870 		    break;
1871 
1872 		// copy next path
1873 		buf[0] = 0;
1874 		copy_option_part(&dir, buf, MAXPATHL, " ,");
1875 
1876 # ifdef FEAT_PATH_EXTRA
1877 		// get the stopdir string
1878 		r_ptr = vim_findfile_stopdir(buf);
1879 # else
1880 		r_ptr = NULL;
1881 # endif
1882 		fdip_search_ctx = vim_findfile_init(buf, ff_file_to_find,
1883 					    r_ptr, 100, FALSE, find_what,
1884 					   fdip_search_ctx, FALSE, rel_fname);
1885 		if (fdip_search_ctx != NULL)
1886 		    did_findfile_init = TRUE;
1887 		vim_free(buf);
1888 	    }
1889 	}
1890     }
1891     if (file_name == NULL && (options & FNAME_MESS))
1892     {
1893 	if (first == TRUE)
1894 	{
1895 	    if (find_what == FINDFILE_DIR)
1896 		semsg(_("E344: Can't find directory \"%s\" in cdpath"),
1897 			ff_file_to_find);
1898 	    else
1899 		semsg(_("E345: Can't find file \"%s\" in path"),
1900 			ff_file_to_find);
1901 	}
1902 	else
1903 	{
1904 	    if (find_what == FINDFILE_DIR)
1905 		semsg(_("E346: No more directory \"%s\" found in cdpath"),
1906 			ff_file_to_find);
1907 	    else
1908 		semsg(_("E347: No more file \"%s\" found in path"),
1909 			ff_file_to_find);
1910 	}
1911     }
1912 
1913 theend:
1914 # ifdef AMIGA
1915     proc->pr_WindowPtr = save_winptr;
1916 # endif
1917     return file_name;
1918 }
1919 
1920 /*
1921  * Get the file name at the cursor.
1922  * If Visual mode is active, use the selected text if it's in one line.
1923  * Returns the name in allocated memory, NULL for failure.
1924  */
1925     char_u *
1926 grab_file_name(long count, linenr_T *file_lnum)
1927 {
1928     int options = FNAME_MESS|FNAME_EXP|FNAME_REL|FNAME_UNESC;
1929 
1930     if (VIsual_active)
1931     {
1932 	int	len;
1933 	char_u	*ptr;
1934 
1935 	if (get_visual_text(NULL, &ptr, &len) == FAIL)
1936 	    return NULL;
1937 	return find_file_name_in_path(ptr, len, options,
1938 						     count, curbuf->b_ffname);
1939     }
1940     return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
1941 }
1942 
1943 /*
1944  * Return the file name under or after the cursor.
1945  *
1946  * The 'path' option is searched if the file name is not absolute.
1947  * The string returned has been alloc'ed and should be freed by the caller.
1948  * NULL is returned if the file name or file is not found.
1949  *
1950  * options:
1951  * FNAME_MESS	    give error messages
1952  * FNAME_EXP	    expand to path
1953  * FNAME_HYP	    check for hypertext link
1954  * FNAME_INCL	    apply "includeexpr"
1955  */
1956     char_u *
1957 file_name_at_cursor(int options, long count, linenr_T *file_lnum)
1958 {
1959     return file_name_in_line(ml_get_curline(),
1960 		      curwin->w_cursor.col, options, count, curbuf->b_ffname,
1961 		      file_lnum);
1962 }
1963 
1964 /*
1965  * Return the name of the file under or after ptr[col].
1966  * Otherwise like file_name_at_cursor().
1967  */
1968     char_u *
1969 file_name_in_line(
1970     char_u	*line,
1971     int		col,
1972     int		options,
1973     long	count,
1974     char_u	*rel_fname,	// file we are searching relative to
1975     linenr_T	*file_lnum)	// line number after the file name
1976 {
1977     char_u	*ptr;
1978     int		len;
1979     int		in_type = TRUE;
1980     int		is_url = FALSE;
1981 
1982     /*
1983      * search forward for what could be the start of a file name
1984      */
1985     ptr = line + col;
1986     while (*ptr != NUL && !vim_isfilec(*ptr))
1987 	MB_PTR_ADV(ptr);
1988     if (*ptr == NUL)		// nothing found
1989     {
1990 	if (options & FNAME_MESS)
1991 	    emsg(_("E446: No file name under cursor"));
1992 	return NULL;
1993     }
1994 
1995     /*
1996      * Search backward for first char of the file name.
1997      * Go one char back to ":" before "//" even when ':' is not in 'isfname'.
1998      */
1999     while (ptr > line)
2000     {
2001 	if (has_mbyte && (len = (*mb_head_off)(line, ptr - 1)) > 0)
2002 	    ptr -= len + 1;
2003 	else if (vim_isfilec(ptr[-1])
2004 		|| ((options & FNAME_HYP) && path_is_url(ptr - 1)))
2005 	    --ptr;
2006 	else
2007 	    break;
2008     }
2009 
2010     /*
2011      * Search forward for the last char of the file name.
2012      * Also allow "://" when ':' is not in 'isfname'.
2013      */
2014     len = 0;
2015     while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
2016 			 || ((options & FNAME_HYP) && path_is_url(ptr + len))
2017 			 || (is_url && vim_strchr((char_u *)":?&=", ptr[len]) != NULL))
2018     {
2019 	// After type:// we also include :, ?, & and = as valid characters, so that
2020 	// http://google.com:8080?q=this&that=ok works.
2021 	if ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z'))
2022 	{
2023 	    if (in_type && path_is_url(ptr + len + 1))
2024 		is_url = TRUE;
2025 	}
2026 	else
2027 	    in_type = FALSE;
2028 
2029 	if (ptr[len] == '\\')
2030 	    // Skip over the "\" in "\ ".
2031 	    ++len;
2032 	if (has_mbyte)
2033 	    len += (*mb_ptr2len)(ptr + len);
2034 	else
2035 	    ++len;
2036     }
2037 
2038     /*
2039      * If there is trailing punctuation, remove it.
2040      * But don't remove "..", could be a directory name.
2041      */
2042     if (len > 2 && vim_strchr((char_u *)".,:;!", ptr[len - 1]) != NULL
2043 						       && ptr[len - 2] != '.')
2044 	--len;
2045 
2046     if (file_lnum != NULL)
2047     {
2048 	char_u *p;
2049 	char	*line_english = " line ";
2050 	char	*line_transl = _(line_msg);
2051 
2052 	// Get the number after the file name and a separator character.
2053 	// Also accept " line 999" with and without the same translation as
2054 	// used in last_set_msg().
2055 	p = ptr + len;
2056 	if (STRNCMP(p, line_english, STRLEN(line_english)) == 0)
2057 	    p += STRLEN(line_english);
2058 	else if (STRNCMP(p, line_transl, STRLEN(line_transl)) == 0)
2059 	    p += STRLEN(line_transl);
2060 	else
2061 	    p = skipwhite(p);
2062 	if (*p != NUL)
2063 	{
2064 	    if (!isdigit(*p))
2065 		++p;		    // skip the separator
2066 	    p = skipwhite(p);
2067 	    if (isdigit(*p))
2068 		*file_lnum = (int)getdigits(&p);
2069 	}
2070     }
2071 
2072     return find_file_name_in_path(ptr, len, options, count, rel_fname);
2073 }
2074 
2075 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
2076     static char_u *
2077 eval_includeexpr(char_u *ptr, int len)
2078 {
2079     char_u	*res;
2080 
2081     set_vim_var_string(VV_FNAME, ptr, len);
2082     res = eval_to_string_safe(curbuf->b_p_inex, NULL,
2083 		      was_set_insecurely((char_u *)"includeexpr", OPT_LOCAL));
2084     set_vim_var_string(VV_FNAME, NULL, 0);
2085     return res;
2086 }
2087 # endif
2088 
2089 /*
2090  * Return the name of the file ptr[len] in 'path'.
2091  * Otherwise like file_name_at_cursor().
2092  */
2093     char_u *
2094 find_file_name_in_path(
2095     char_u	*ptr,
2096     int		len,
2097     int		options,
2098     long	count,
2099     char_u	*rel_fname)	// file we are searching relative to
2100 {
2101     char_u	*file_name;
2102     int		c;
2103 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
2104     char_u	*tofree = NULL;
2105 
2106     if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL)
2107     {
2108 	tofree = eval_includeexpr(ptr, len);
2109 	if (tofree != NULL)
2110 	{
2111 	    ptr = tofree;
2112 	    len = (int)STRLEN(ptr);
2113 	}
2114     }
2115 # endif
2116 
2117     if (options & FNAME_EXP)
2118     {
2119 	file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
2120 							     TRUE, rel_fname);
2121 
2122 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
2123 	/*
2124 	 * If the file could not be found in a normal way, try applying
2125 	 * 'includeexpr' (unless done already).
2126 	 */
2127 	if (file_name == NULL
2128 		&& !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL)
2129 	{
2130 	    tofree = eval_includeexpr(ptr, len);
2131 	    if (tofree != NULL)
2132 	    {
2133 		ptr = tofree;
2134 		len = (int)STRLEN(ptr);
2135 		file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
2136 							     TRUE, rel_fname);
2137 	    }
2138 	}
2139 # endif
2140 	if (file_name == NULL && (options & FNAME_MESS))
2141 	{
2142 	    c = ptr[len];
2143 	    ptr[len] = NUL;
2144 	    semsg(_("E447: Can't find file \"%s\" in path"), ptr);
2145 	    ptr[len] = c;
2146 	}
2147 
2148 	// Repeat finding the file "count" times.  This matters when it
2149 	// appears several times in the path.
2150 	while (file_name != NULL && --count > 0)
2151 	{
2152 	    vim_free(file_name);
2153 	    file_name = find_file_in_path(ptr, len, options, FALSE, rel_fname);
2154 	}
2155     }
2156     else
2157 	file_name = vim_strnsave(ptr, len);
2158 
2159 # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
2160     vim_free(tofree);
2161 # endif
2162 
2163     return file_name;
2164 }
2165 
2166 /*
2167  * Return the end of the directory name, on the first path
2168  * separator:
2169  * "/path/file", "/path/dir/", "/path//dir", "/file"
2170  *	 ^	       ^	     ^	      ^
2171  */
2172     static char_u *
2173 gettail_dir(char_u *fname)
2174 {
2175     char_u	*dir_end = fname;
2176     char_u	*next_dir_end = fname;
2177     int		look_for_sep = TRUE;
2178     char_u	*p;
2179 
2180     for (p = fname; *p != NUL; )
2181     {
2182 	if (vim_ispathsep(*p))
2183 	{
2184 	    if (look_for_sep)
2185 	    {
2186 		next_dir_end = p;
2187 		look_for_sep = FALSE;
2188 	    }
2189 	}
2190 	else
2191 	{
2192 	    if (!look_for_sep)
2193 		dir_end = next_dir_end;
2194 	    look_for_sep = TRUE;
2195 	}
2196 	MB_PTR_ADV(p);
2197     }
2198     return dir_end;
2199 }
2200 
2201 /*
2202  * return TRUE if 'c' is a path list separator.
2203  */
2204     int
2205 vim_ispathlistsep(int c)
2206 {
2207 # ifdef UNIX
2208     return (c == ':');
2209 # else
2210     return (c == ';');	// might not be right for every system...
2211 # endif
2212 }
2213 
2214 /*
2215  * Moves "*psep" back to the previous path separator in "path".
2216  * Returns FAIL is "*psep" ends up at the beginning of "path".
2217  */
2218     static int
2219 find_previous_pathsep(char_u *path, char_u **psep)
2220 {
2221     // skip the current separator
2222     if (*psep > path && vim_ispathsep(**psep))
2223 	--*psep;
2224 
2225     // find the previous separator
2226     while (*psep > path)
2227     {
2228 	if (vim_ispathsep(**psep))
2229 	    return OK;
2230 	MB_PTR_BACK(path, *psep);
2231     }
2232 
2233     return FAIL;
2234 }
2235 
2236 /*
2237  * Returns TRUE if "maybe_unique" is unique wrt other_paths in "gap".
2238  * "maybe_unique" is the end portion of "((char_u **)gap->ga_data)[i]".
2239  */
2240     static int
2241 is_unique(char_u *maybe_unique, garray_T *gap, int i)
2242 {
2243     int	    j;
2244     int	    candidate_len;
2245     int	    other_path_len;
2246     char_u  **other_paths = (char_u **)gap->ga_data;
2247     char_u  *rival;
2248 
2249     for (j = 0; j < gap->ga_len; j++)
2250     {
2251 	if (j == i)
2252 	    continue;  // don't compare it with itself
2253 
2254 	candidate_len = (int)STRLEN(maybe_unique);
2255 	other_path_len = (int)STRLEN(other_paths[j]);
2256 	if (other_path_len < candidate_len)
2257 	    continue;  // it's different when it's shorter
2258 
2259 	rival = other_paths[j] + other_path_len - candidate_len;
2260 	if (fnamecmp(maybe_unique, rival) == 0
2261 		&& (rival == other_paths[j] || vim_ispathsep(*(rival - 1))))
2262 	    return FALSE;  // match
2263     }
2264 
2265     return TRUE;  // no match found
2266 }
2267 
2268 /*
2269  * Split the 'path' option into an array of strings in garray_T.  Relative
2270  * paths are expanded to their equivalent fullpath.  This includes the "."
2271  * (relative to current buffer directory) and empty path (relative to current
2272  * directory) notations.
2273  *
2274  * TODO: handle upward search (;) and path limiter (**N) notations by
2275  * expanding each into their equivalent path(s).
2276  */
2277     static void
2278 expand_path_option(char_u *curdir, garray_T *gap)
2279 {
2280     char_u	*path_option = *curbuf->b_p_path == NUL
2281 						  ? p_path : curbuf->b_p_path;
2282     char_u	*buf;
2283     char_u	*p;
2284     int		len;
2285 
2286     if ((buf = alloc(MAXPATHL)) == NULL)
2287 	return;
2288 
2289     while (*path_option != NUL)
2290     {
2291 	copy_option_part(&path_option, buf, MAXPATHL, " ,");
2292 
2293 	if (buf[0] == '.' && (buf[1] == NUL || vim_ispathsep(buf[1])))
2294 	{
2295 	    // Relative to current buffer:
2296 	    // "/path/file" + "." -> "/path/"
2297 	    // "/path/file"  + "./subdir" -> "/path/subdir"
2298 	    if (curbuf->b_ffname == NULL)
2299 		continue;
2300 	    p = gettail(curbuf->b_ffname);
2301 	    len = (int)(p - curbuf->b_ffname);
2302 	    if (len + (int)STRLEN(buf) >= MAXPATHL)
2303 		continue;
2304 	    if (buf[1] == NUL)
2305 		buf[len] = NUL;
2306 	    else
2307 		STRMOVE(buf + len, buf + 2);
2308 	    mch_memmove(buf, curbuf->b_ffname, len);
2309 	    simplify_filename(buf);
2310 	}
2311 	else if (buf[0] == NUL)
2312 	    // relative to current directory
2313 	    STRCPY(buf, curdir);
2314 	else if (path_with_url(buf))
2315 	    // URL can't be used here
2316 	    continue;
2317 	else if (!mch_isFullName(buf))
2318 	{
2319 	    // Expand relative path to their full path equivalent
2320 	    len = (int)STRLEN(curdir);
2321 	    if (len + (int)STRLEN(buf) + 3 > MAXPATHL)
2322 		continue;
2323 	    STRMOVE(buf + len + 1, buf);
2324 	    STRCPY(buf, curdir);
2325 	    buf[len] = PATHSEP;
2326 	    simplify_filename(buf);
2327 	}
2328 
2329 	if (ga_grow(gap, 1) == FAIL)
2330 	    break;
2331 
2332 # if defined(MSWIN)
2333 	// Avoid the path ending in a backslash, it fails when a comma is
2334 	// appended.
2335 	len = (int)STRLEN(buf);
2336 	if (buf[len - 1] == '\\')
2337 	    buf[len - 1] = '/';
2338 # endif
2339 
2340 	p = vim_strsave(buf);
2341 	if (p == NULL)
2342 	    break;
2343 	((char_u **)gap->ga_data)[gap->ga_len++] = p;
2344     }
2345 
2346     vim_free(buf);
2347 }
2348 
2349 /*
2350  * Returns a pointer to the file or directory name in "fname" that matches the
2351  * longest path in "ga"p, or NULL if there is no match. For example:
2352  *
2353  *    path: /foo/bar/baz
2354  *   fname: /foo/bar/baz/quux.txt
2355  * returns:		 ^this
2356  */
2357     static char_u *
2358 get_path_cutoff(char_u *fname, garray_T *gap)
2359 {
2360     int	    i;
2361     int	    maxlen = 0;
2362     char_u  **path_part = (char_u **)gap->ga_data;
2363     char_u  *cutoff = NULL;
2364 
2365     for (i = 0; i < gap->ga_len; i++)
2366     {
2367 	int j = 0;
2368 
2369 	while ((fname[j] == path_part[i][j]
2370 # if defined(MSWIN)
2371 		|| (vim_ispathsep(fname[j]) && vim_ispathsep(path_part[i][j]))
2372 # endif
2373 			     ) && fname[j] != NUL && path_part[i][j] != NUL)
2374 	    j++;
2375 	if (j > maxlen)
2376 	{
2377 	    maxlen = j;
2378 	    cutoff = &fname[j];
2379 	}
2380     }
2381 
2382     // skip to the file or directory name
2383     if (cutoff != NULL)
2384 	while (vim_ispathsep(*cutoff))
2385 	    MB_PTR_ADV(cutoff);
2386 
2387     return cutoff;
2388 }
2389 
2390 /*
2391  * Sorts, removes duplicates and modifies all the fullpath names in "gap" so
2392  * that they are unique with respect to each other while conserving the part
2393  * that matches the pattern. Beware, this is at least O(n^2) wrt "gap->ga_len".
2394  */
2395     void
2396 uniquefy_paths(garray_T *gap, char_u *pattern)
2397 {
2398     int		i;
2399     int		len;
2400     char_u	**fnames = (char_u **)gap->ga_data;
2401     int		sort_again = FALSE;
2402     char_u	*pat;
2403     char_u      *file_pattern;
2404     char_u	*curdir;
2405     regmatch_T	regmatch;
2406     garray_T	path_ga;
2407     char_u	**in_curdir = NULL;
2408     char_u	*short_name;
2409 
2410     remove_duplicates(gap);
2411     ga_init2(&path_ga, (int)sizeof(char_u *), 1);
2412 
2413     /*
2414      * We need to prepend a '*' at the beginning of file_pattern so that the
2415      * regex matches anywhere in the path. FIXME: is this valid for all
2416      * possible patterns?
2417      */
2418     len = (int)STRLEN(pattern);
2419     file_pattern = alloc(len + 2);
2420     if (file_pattern == NULL)
2421 	return;
2422     file_pattern[0] = '*';
2423     file_pattern[1] = NUL;
2424     STRCAT(file_pattern, pattern);
2425     pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, TRUE);
2426     vim_free(file_pattern);
2427     if (pat == NULL)
2428 	return;
2429 
2430     regmatch.rm_ic = TRUE;		// always ignore case
2431     regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
2432     vim_free(pat);
2433     if (regmatch.regprog == NULL)
2434 	return;
2435 
2436     if ((curdir = alloc(MAXPATHL)) == NULL)
2437 	goto theend;
2438     mch_dirname(curdir, MAXPATHL);
2439     expand_path_option(curdir, &path_ga);
2440 
2441     in_curdir = ALLOC_CLEAR_MULT(char_u *, gap->ga_len);
2442     if (in_curdir == NULL)
2443 	goto theend;
2444 
2445     for (i = 0; i < gap->ga_len && !got_int; i++)
2446     {
2447 	char_u	    *path = fnames[i];
2448 	int	    is_in_curdir;
2449 	char_u	    *dir_end = gettail_dir(path);
2450 	char_u	    *pathsep_p;
2451 	char_u	    *path_cutoff;
2452 
2453 	len = (int)STRLEN(path);
2454 	is_in_curdir = fnamencmp(curdir, path, dir_end - path) == 0
2455 					     && curdir[dir_end - path] == NUL;
2456 	if (is_in_curdir)
2457 	    in_curdir[i] = vim_strsave(path);
2458 
2459 	// Shorten the filename while maintaining its uniqueness
2460 	path_cutoff = get_path_cutoff(path, &path_ga);
2461 
2462 	// Don't assume all files can be reached without path when search
2463 	// pattern starts with star star slash, so only remove path_cutoff
2464 	// when possible.
2465 	if (pattern[0] == '*' && pattern[1] == '*'
2466 		&& vim_ispathsep_nocolon(pattern[2])
2467 		&& path_cutoff != NULL
2468 		&& vim_regexec(&regmatch, path_cutoff, (colnr_T)0)
2469 		&& is_unique(path_cutoff, gap, i))
2470 	{
2471 	    sort_again = TRUE;
2472 	    mch_memmove(path, path_cutoff, STRLEN(path_cutoff) + 1);
2473 	}
2474 	else
2475 	{
2476 	    // Here all files can be reached without path, so get shortest
2477 	    // unique path.  We start at the end of the path.
2478 	    pathsep_p = path + len - 1;
2479 
2480 	    while (find_previous_pathsep(path, &pathsep_p))
2481 		if (vim_regexec(&regmatch, pathsep_p + 1, (colnr_T)0)
2482 			&& is_unique(pathsep_p + 1, gap, i)
2483 			&& path_cutoff != NULL && pathsep_p + 1 >= path_cutoff)
2484 		{
2485 		    sort_again = TRUE;
2486 		    mch_memmove(path, pathsep_p + 1, STRLEN(pathsep_p));
2487 		    break;
2488 		}
2489 	}
2490 
2491 	if (mch_isFullName(path))
2492 	{
2493 	    /*
2494 	     * Last resort: shorten relative to curdir if possible.
2495 	     * 'possible' means:
2496 	     * 1. It is under the current directory.
2497 	     * 2. The result is actually shorter than the original.
2498 	     *
2499 	     *	    Before		  curdir	After
2500 	     *	    /foo/bar/file.txt	  /foo/bar	./file.txt
2501 	     *	    c:\foo\bar\file.txt   c:\foo\bar	.\file.txt
2502 	     *	    /file.txt		  /		/file.txt
2503 	     *	    c:\file.txt		  c:\		.\file.txt
2504 	     */
2505 	    short_name = shorten_fname(path, curdir);
2506 	    if (short_name != NULL && short_name > path + 1
2507 # if defined(MSWIN)
2508 		    // On windows,
2509 		    //	    shorten_fname("c:\a\a.txt", "c:\a\b")
2510 		    // returns "\a\a.txt", which is not really the short
2511 		    // name, hence:
2512 		    && !vim_ispathsep(*short_name)
2513 # endif
2514 		)
2515 	    {
2516 		STRCPY(path, ".");
2517 		add_pathsep(path);
2518 		STRMOVE(path + STRLEN(path), short_name);
2519 	    }
2520 	}
2521 	ui_breakcheck();
2522     }
2523 
2524     // Shorten filenames in /in/current/directory/{filename}
2525     for (i = 0; i < gap->ga_len && !got_int; i++)
2526     {
2527 	char_u *rel_path;
2528 	char_u *path = in_curdir[i];
2529 
2530 	if (path == NULL)
2531 	    continue;
2532 
2533 	// If the {filename} is not unique, change it to ./{filename}.
2534 	// Else reduce it to {filename}
2535 	short_name = shorten_fname(path, curdir);
2536 	if (short_name == NULL)
2537 	    short_name = path;
2538 	if (is_unique(short_name, gap, i))
2539 	{
2540 	    STRCPY(fnames[i], short_name);
2541 	    continue;
2542 	}
2543 
2544 	rel_path = alloc(STRLEN(short_name) + STRLEN(PATHSEPSTR) + 2);
2545 	if (rel_path == NULL)
2546 	    goto theend;
2547 	STRCPY(rel_path, ".");
2548 	add_pathsep(rel_path);
2549 	STRCAT(rel_path, short_name);
2550 
2551 	vim_free(fnames[i]);
2552 	fnames[i] = rel_path;
2553 	sort_again = TRUE;
2554 	ui_breakcheck();
2555     }
2556 
2557 theend:
2558     vim_free(curdir);
2559     if (in_curdir != NULL)
2560     {
2561 	for (i = 0; i < gap->ga_len; i++)
2562 	    vim_free(in_curdir[i]);
2563 	vim_free(in_curdir);
2564     }
2565     ga_clear_strings(&path_ga);
2566     vim_regfree(regmatch.regprog);
2567 
2568     if (sort_again)
2569 	remove_duplicates(gap);
2570 }
2571 
2572 /*
2573  * Calls globpath() with 'path' values for the given pattern and stores the
2574  * result in "gap".
2575  * Returns the total number of matches.
2576  */
2577     int
2578 expand_in_path(
2579     garray_T	*gap,
2580     char_u	*pattern,
2581     int		flags)		// EW_* flags
2582 {
2583     char_u	*curdir;
2584     garray_T	path_ga;
2585     char_u	*paths = NULL;
2586     int		glob_flags = 0;
2587 
2588     if ((curdir = alloc(MAXPATHL)) == NULL)
2589 	return 0;
2590     mch_dirname(curdir, MAXPATHL);
2591 
2592     ga_init2(&path_ga, (int)sizeof(char_u *), 1);
2593     expand_path_option(curdir, &path_ga);
2594     vim_free(curdir);
2595     if (path_ga.ga_len == 0)
2596 	return 0;
2597 
2598     paths = ga_concat_strings(&path_ga, ",");
2599     ga_clear_strings(&path_ga);
2600     if (paths == NULL)
2601 	return 0;
2602 
2603     if (flags & EW_ICASE)
2604 	glob_flags |= WILD_ICASE;
2605     if (flags & EW_ADDSLASH)
2606 	glob_flags |= WILD_ADD_SLASH;
2607     globpath(paths, pattern, gap, glob_flags);
2608     vim_free(paths);
2609 
2610     return gap->ga_len;
2611 }
2612 
2613 #endif // FEAT_SEARCHPATH
2614 
2615 /*
2616  * Converts a file name into a canonical form. It simplifies a file name into
2617  * its simplest form by stripping out unneeded components, if any.  The
2618  * resulting file name is simplified in place and will either be the same
2619  * length as that supplied, or shorter.
2620  */
2621     void
2622 simplify_filename(char_u *filename)
2623 {
2624 #ifndef AMIGA	    // Amiga doesn't have "..", it uses "/"
2625     int		components = 0;
2626     char_u	*p, *tail, *start;
2627     int		stripping_disabled = FALSE;
2628     int		relative = TRUE;
2629 
2630     p = filename;
2631 # ifdef BACKSLASH_IN_FILENAME
2632     if (p[1] == ':')	    // skip "x:"
2633 	p += 2;
2634 # endif
2635 
2636     if (vim_ispathsep(*p))
2637     {
2638 	relative = FALSE;
2639 	do
2640 	    ++p;
2641 	while (vim_ispathsep(*p));
2642     }
2643     start = p;	    // remember start after "c:/" or "/" or "///"
2644 
2645     do
2646     {
2647 	// At this point "p" is pointing to the char following a single "/"
2648 	// or "p" is at the "start" of the (absolute or relative) path name.
2649 # ifdef VMS
2650 	// VMS allows device:[path] - don't strip the [ in directory
2651 	if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':')
2652 	{
2653 	    // :[ or :< composition: vms directory component
2654 	    ++components;
2655 	    p = getnextcomp(p + 1);
2656 	}
2657 	// allow remote calls as host"user passwd"::device:[path]
2658 	else if (p[0] == ':' && p[1] == ':' && p > filename && p[-1] == '"' )
2659 	{
2660 	    // ":: composition: vms host/passwd component
2661 	    ++components;
2662 	    p = getnextcomp(p + 2);
2663 	}
2664 	else
2665 # endif
2666 	  if (vim_ispathsep(*p))
2667 	    STRMOVE(p, p + 1);		// remove duplicate "/"
2668 	else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL))
2669 	{
2670 	    if (p == start && relative)
2671 		p += 1 + (p[1] != NUL);	// keep single "." or leading "./"
2672 	    else
2673 	    {
2674 		// Strip "./" or ".///".  If we are at the end of the file name
2675 		// and there is no trailing path separator, either strip "/." if
2676 		// we are after "start", or strip "." if we are at the beginning
2677 		// of an absolute path name .
2678 		tail = p + 1;
2679 		if (p[1] != NUL)
2680 		    while (vim_ispathsep(*tail))
2681 			MB_PTR_ADV(tail);
2682 		else if (p > start)
2683 		    --p;		// strip preceding path separator
2684 		STRMOVE(p, tail);
2685 	    }
2686 	}
2687 	else if (p[0] == '.' && p[1] == '.' &&
2688 	    (vim_ispathsep(p[2]) || p[2] == NUL))
2689 	{
2690 	    // Skip to after ".." or "../" or "..///".
2691 	    tail = p + 2;
2692 	    while (vim_ispathsep(*tail))
2693 		MB_PTR_ADV(tail);
2694 
2695 	    if (components > 0)		// strip one preceding component
2696 	    {
2697 		int		do_strip = FALSE;
2698 		char_u		saved_char;
2699 		stat_T		st;
2700 
2701 		// Don't strip for an erroneous file name.
2702 		if (!stripping_disabled)
2703 		{
2704 		    // If the preceding component does not exist in the file
2705 		    // system, we strip it.  On Unix, we don't accept a symbolic
2706 		    // link that refers to a non-existent file.
2707 		    saved_char = p[-1];
2708 		    p[-1] = NUL;
2709 # ifdef UNIX
2710 		    if (mch_lstat((char *)filename, &st) < 0)
2711 # else
2712 			if (mch_stat((char *)filename, &st) < 0)
2713 # endif
2714 			    do_strip = TRUE;
2715 		    p[-1] = saved_char;
2716 
2717 		    --p;
2718 		    // Skip back to after previous '/'.
2719 		    while (p > start && !after_pathsep(start, p))
2720 			MB_PTR_BACK(start, p);
2721 
2722 		    if (!do_strip)
2723 		    {
2724 			// If the component exists in the file system, check
2725 			// that stripping it won't change the meaning of the
2726 			// file name.  First get information about the
2727 			// unstripped file name.  This may fail if the component
2728 			// to strip is not a searchable directory (but a regular
2729 			// file, for instance), since the trailing "/.." cannot
2730 			// be applied then.  We don't strip it then since we
2731 			// don't want to replace an erroneous file name by
2732 			// a valid one, and we disable stripping of later
2733 			// components.
2734 			saved_char = *tail;
2735 			*tail = NUL;
2736 			if (mch_stat((char *)filename, &st) >= 0)
2737 			    do_strip = TRUE;
2738 			else
2739 			    stripping_disabled = TRUE;
2740 			*tail = saved_char;
2741 # ifdef UNIX
2742 			if (do_strip)
2743 			{
2744 			    stat_T	new_st;
2745 
2746 			    // On Unix, the check for the unstripped file name
2747 			    // above works also for a symbolic link pointing to
2748 			    // a searchable directory.  But then the parent of
2749 			    // the directory pointed to by the link must be the
2750 			    // same as the stripped file name.  (The latter
2751 			    // exists in the file system since it is the
2752 			    // component's parent directory.)
2753 			    if (p == start && relative)
2754 				(void)mch_stat(".", &new_st);
2755 			    else
2756 			    {
2757 				saved_char = *p;
2758 				*p = NUL;
2759 				(void)mch_stat((char *)filename, &new_st);
2760 				*p = saved_char;
2761 			    }
2762 
2763 			    if (new_st.st_ino != st.st_ino ||
2764 				new_st.st_dev != st.st_dev)
2765 			    {
2766 				do_strip = FALSE;
2767 				// We don't disable stripping of later
2768 				// components since the unstripped path name is
2769 				// still valid.
2770 			    }
2771 			}
2772 # endif
2773 		    }
2774 		}
2775 
2776 		if (!do_strip)
2777 		{
2778 		    // Skip the ".." or "../" and reset the counter for the
2779 		    // components that might be stripped later on.
2780 		    p = tail;
2781 		    components = 0;
2782 		}
2783 		else
2784 		{
2785 		    // Strip previous component.  If the result would get empty
2786 		    // and there is no trailing path separator, leave a single
2787 		    // "." instead.  If we are at the end of the file name and
2788 		    // there is no trailing path separator and a preceding
2789 		    // component is left after stripping, strip its trailing
2790 		    // path separator as well.
2791 		    if (p == start && relative && tail[-1] == '.')
2792 		    {
2793 			*p++ = '.';
2794 			*p = NUL;
2795 		    }
2796 		    else
2797 		    {
2798 			if (p > start && tail[-1] == '.')
2799 			    --p;
2800 			STRMOVE(p, tail);	// strip previous component
2801 		    }
2802 
2803 		    --components;
2804 		}
2805 	    }
2806 	    else if (p == start && !relative)	// leading "/.." or "/../"
2807 		STRMOVE(p, tail);		// strip ".." or "../"
2808 	    else
2809 	    {
2810 		if (p == start + 2 && p[-2] == '.')	// leading "./../"
2811 		{
2812 		    STRMOVE(p - 2, p);			// strip leading "./"
2813 		    tail -= 2;
2814 		}
2815 		p = tail;		// skip to char after ".." or "../"
2816 	    }
2817 	}
2818 	else
2819 	{
2820 	    ++components;		// simple path component
2821 	    p = getnextcomp(p);
2822 	}
2823     } while (*p != NUL);
2824 #endif // !AMIGA
2825 }
2826 
2827 #if defined(FEAT_EVAL) || defined(PROTO)
2828 /*
2829  * "simplify()" function
2830  */
2831     void
2832 f_simplify(typval_T *argvars, typval_T *rettv)
2833 {
2834     char_u	*p;
2835 
2836     p = tv_get_string(&argvars[0]);
2837     rettv->vval.v_string = vim_strsave(p);
2838     simplify_filename(rettv->vval.v_string);	// simplify in place
2839     rettv->v_type = VAR_STRING;
2840 }
2841 #endif // FEAT_EVAL
2842