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