diff options
-rw-r--r-- | Filelist | 2 | ||||
-rw-r--r-- | src/Make_bc5.mak | 1 | ||||
-rw-r--r-- | src/Make_cyg_ming.mak | 1 | ||||
-rw-r--r-- | src/Make_dice.mak | 4 | ||||
-rw-r--r-- | src/Make_ivc.mak | 5 | ||||
-rw-r--r-- | src/Make_manx.mak | 6 | ||||
-rw-r--r-- | src/Make_morph.mak | 1 | ||||
-rw-r--r-- | src/Make_mvc.mak | 4 | ||||
-rw-r--r-- | src/Make_sas.mak | 5 | ||||
-rw-r--r-- | src/Make_vms.mms | 40 | ||||
-rw-r--r-- | src/Makefile | 11 | ||||
-rw-r--r-- | src/README.txt | 2 | ||||
-rw-r--r-- | src/findfile.c | 2607 | ||||
-rw-r--r-- | src/misc1.c | 528 | ||||
-rw-r--r-- | src/misc2.c | 1926 | ||||
-rw-r--r-- | src/proto.h | 1 | ||||
-rw-r--r-- | src/proto/findfile.pro | 18 | ||||
-rw-r--r-- | src/proto/misc1.pro | 5 | ||||
-rw-r--r-- | src/proto/misc2.pro | 8 | ||||
-rw-r--r-- | src/proto/window.pro | 7 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/window.c | 315 |
22 files changed, 2775 insertions, 2724 deletions
@@ -41,6 +41,7 @@ SRC_ALL = \ src/farsi.h \ src/feature.h \ src/fileio.c \ + src/findfile.c \ src/fold.c \ src/getchar.c \ src/globals.h \ @@ -170,6 +171,7 @@ SRC_ALL = \ src/proto/ex_getln.pro \ src/proto/farsi.pro \ src/proto/fileio.pro \ + src/proto/findfile.pro \ src/proto/fold.pro \ src/proto/getchar.pro \ src/proto/gui.pro \ diff --git a/src/Make_bc5.mak b/src/Make_bc5.mak index b2977e7007..eb38dd7ccd 100644 --- a/src/Make_bc5.mak +++ b/src/Make_bc5.mak @@ -544,6 +544,7 @@ vimobj = \ $(OBJDIR)\ex_getln.obj \ $(OBJDIR)\farsi.obj \ $(OBJDIR)\fileio.obj \ + $(OBJDIR)\findfile.obj \ $(OBJDIR)\fold.obj \ $(OBJDIR)\getchar.obj \ $(OBJDIR)\hardcopy.obj \ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 1e26adda5d..48ce3974d6 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -716,6 +716,7 @@ OBJ = \ $(OUTDIR)/ex_getln.o \ $(OUTDIR)/farsi.o \ $(OUTDIR)/fileio.o \ + $(OUTDIR)/findfile.o \ $(OUTDIR)/fold.o \ $(OUTDIR)/getchar.o \ $(OUTDIR)/hardcopy.o \ diff --git a/src/Make_dice.mak b/src/Make_dice.mak index 57aed477b9..ffce805d3c 100644 --- a/src/Make_dice.mak +++ b/src/Make_dice.mak @@ -46,6 +46,7 @@ SRC = \ ex_getln.c \ farsi.c \ fileio.c \ + findfile.c \ fold.c \ getchar.c \ hardcopy.c \ @@ -105,6 +106,7 @@ OBJ = o/arabic.o \ o/ex_getln.o \ o/farsi.o \ o/fileio.o \ + o/findfile.o \ o/fold.o \ o/getchar.o \ o/hardcopy.o \ @@ -203,6 +205,8 @@ o/farsi.o: farsi.c $(SYMS) o/fileio.o: fileio.c $(SYMS) +o/findfile.o: findfile.c $(SYMS) + o/fold.o: fold.c $(SYMS) o/getchar.o: getchar.c $(SYMS) diff --git a/src/Make_ivc.mak b/src/Make_ivc.mak index 96d6a47b54..a02ba960f1 100644 --- a/src/Make_ivc.mak +++ b/src/Make_ivc.mak @@ -230,6 +230,7 @@ LINK32_OBJS= \ "$(INTDIR)/ex_getln.obj" \ "$(INTDIR)/farsi.obj" \ "$(INTDIR)/fileio.obj" \ + "$(INTDIR)/findfile.obj" \ "$(INTDIR)/fold.obj" \ "$(INTDIR)/getchar.obj" \ "$(INTDIR)/hardcopy.obj" \ @@ -419,6 +420,10 @@ SOURCE=.\farsi.c SOURCE=.\fileio.c # End Source File # Begin Source File +# +SOURCE=.\findfile.c +# End Source File +# Begin Source File SOURCE=.\fold.c # End Source File diff --git a/src/Make_manx.mak b/src/Make_manx.mak index a44ad6558c..5314a86a13 100644 --- a/src/Make_manx.mak +++ b/src/Make_manx.mak @@ -56,6 +56,7 @@ SRC = arabic.c \ ex_getln.c \ farsi.c \ fileio.c \ + findfile.c \ fold.c \ getchar.c \ hardcopy.c \ @@ -117,6 +118,7 @@ OBJ = obj/arabic.o \ obj/ex_getln.o \ obj/farsi.o \ obj/fileio.o \ + obj/findfile.o \ obj/fold.o \ obj/getchar.o \ obj/hardcopy.o \ @@ -176,6 +178,7 @@ PRO = proto/arabic.pro \ proto/ex_getln.pro \ proto/farsi.pro \ proto/fileio.pro \ + proto/findfile.pro \ proto/fold.pro \ proto/getchar.pro \ proto/hardcopy.pro \ @@ -320,6 +323,9 @@ obj/farsi.o: farsi.c obj/fileio.o: fileio.c $(CCSYM) $@ fileio.c +obj/findfile.o: findfile.c + $(CCSYM) $@ findfile.c + obj/fold.o: fold.c $(CCSYM) $@ fold.c diff --git a/src/Make_morph.mak b/src/Make_morph.mak index 6bcae1a0b1..a6e0dae178 100644 --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -44,6 +44,7 @@ SRC = arabic.c \ ex_getln.c \ farsi.c \ fileio.c \ + findfile.c \ fold.c \ getchar.c \ hardcopy.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 52deb549c3..c948f37719 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -721,6 +721,7 @@ OBJ = \ $(OUTDIR)\ex_getln.obj \ $(OUTDIR)\farsi.obj \ $(OUTDIR)\fileio.obj \ + $(OUTDIR)\findfile.obj \ $(OUTDIR)\fold.obj \ $(OUTDIR)\getchar.obj \ $(OUTDIR)\hardcopy.obj \ @@ -1407,6 +1408,8 @@ $(OUTDIR)/farsi.obj: $(OUTDIR) farsi.c $(INCL) $(OUTDIR)/fileio.obj: $(OUTDIR) fileio.c $(INCL) +$(OUTDIR)/findfile.obj: $(OUTDIR) findfile.c $(INCL) + $(OUTDIR)/fold.obj: $(OUTDIR) fold.c $(INCL) $(OUTDIR)/getchar.obj: $(OUTDIR) getchar.c $(INCL) @@ -1645,6 +1648,7 @@ proto.h: \ proto/ex_getln.pro \ proto/farsi.pro \ proto/fileio.pro \ + proto/findfile.pro \ proto/getchar.pro \ proto/hardcopy.pro \ proto/hashtab.pro \ diff --git a/src/Make_sas.mak b/src/Make_sas.mak index e7faf56771..deaa5eb5d6 100644 --- a/src/Make_sas.mak +++ b/src/Make_sas.mak @@ -109,6 +109,7 @@ SRC = \ ex_getln.c \ farsi.c \ fileio.c \ + findfile.c \ fold.c \ getchar.c \ hardcopy.c \ @@ -169,6 +170,7 @@ OBJ = \ ex_getln.o \ farsi.o \ fileio.o \ + findfile.o \ fold.o \ getchar.o \ hardcopy.o \ @@ -229,6 +231,7 @@ PRO = \ proto/ex_getln.pro \ proto/farsi.pro \ proto/fileio.pro \ + proto/findfile.pro \ proto/fold.pro \ proto/getchar.pro \ proto/hardcopy.pro \ @@ -363,6 +366,8 @@ farsi.o: farsi.c proto/farsi.pro: farsi.c fileio.o: fileio.c proto/fileio.pro: fileio.c +findfile.o: findfile.c +proto/findfile.pro: findfile.c fold.o: fold.c proto/fold.pro: fold.c getchar.o: getchar.c diff --git a/src/Make_vms.mms b/src/Make_vms.mms index 1a21d15fb9..92f5ab12ef 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -312,23 +312,31 @@ ALL_CFLAGS_VER = /def=($(MODEL_DEF)$(DEFS)$(DEBUG_DEF)$(PERL_DEF)$(PYTHON_DEF) - ALL_LIBS = $(LIBS) $(GUI_LIB_DIR) $(GUI_LIB) \ $(PERL_LIB) $(PYTHON_LIB) $(TCL_LIB) $(RUBY_LIB) -SRC = arabic.c autocmd.c beval.c blob.c blowfish.c buffer.c charset.c crypt.c crypt_zip.c dict.c diff.c digraph.c edit.c eval.c \ - evalfunc.c ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c if_cscope.c if_xcmdsrv.c farsi.c fileio.c fold.c \ - getchar.c hardcopy.c hashtab.c indent.c json.c list.c main.c mark.c menu.c mbyte.c memfile.c memline.c message.c misc1.c \ - misc2.c move.c normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c sha256.c sign.c \ - spell.c spellfile.c syntax.c tag.c term.c termlib.c textprop.c ui.c undo.c userfunc.c version.c screen.c \ - window.c os_unix.c os_vms.c pathdef.c \ +SRC = arabic.c autocmd.c beval.c blob.c blowfish.c buffer.c charset.c \ + crypt.c crypt_zip.c dict.c diff.c digraph.c edit.c eval.c evalfunc.c \ + ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c if_cscope.c \ + if_xcmdsrv.c farsi.c fileio.c findfile.c fold.c getchar.c hardcopy.c \ + hashtab.c indent.c json.c list.c main.c mark.c menu.c mbyte.c \ + memfile.c memline.c message.c misc1.c misc2.c move.c normal.c ops.c \ + option.c popupmnu.c quickfix.c regexp.c search.c sha256.c sign.c \ + spell.c spellfile.c syntax.c tag.c term.c termlib.c textprop.c ui.c \ + undo.c userfunc.c version.c screen.c window.c os_unix.c os_vms.c \ + pathdef.c $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \ $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC) -OBJ = arabic.obj autocmd.obj beval.obj blob.obj blowfish.obj buffer.obj charset.obj crypt.obj crypt_zip.obj dict.obj diff.obj digraph.obj \ - edit.obj eval.obj evalfunc.obj ex_cmds.obj ex_cmds2.obj ex_docmd.obj ex_eval.obj ex_getln.obj if_cscope.obj \ - if_xcmdsrv.obj farsi.obj fileio.obj fold.obj getchar.obj hardcopy.obj hashtab.obj indent.obj json.obj list.obj main.obj mark.obj \ - menu.obj memfile.obj memline.obj message.obj misc1.obj misc2.obj \ - move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj quickfix.obj \ - regexp.obj search.obj sha256.obj sign.obj spell.obj spellfile.obj syntax.obj tag.obj term.obj termlib.obj textprop.obj \ - ui.obj undo.obj userfunc.obj screen.obj version.obj window.obj os_unix.obj \ - os_vms.obj pathdef.obj if_mzsch.obj\ +OBJ = arabic.obj autocmd.obj beval.obj blob.obj blowfish.obj buffer.obj \ + charset.obj crypt.obj crypt_zip.obj dict.obj diff.obj digraph.obj \ + edit.obj eval.obj evalfunc.obj ex_cmds.obj ex_cmds2.obj ex_docmd.obj \ + ex_eval.obj ex_getln.obj if_cscope.obj if_xcmdsrv.obj farsi.obj \ + fileio.obj findfile.obj fold.obj getchar.obj hardcopy.obj hashtab.obj \ + indent.obj json.obj list.obj main.obj mark.obj menu.obj memfile.obj \ + memline.obj message.obj misc1.obj misc2.obj move.obj mbyte.obj \ + normal.obj ops.obj option.obj popupmnu.obj quickfix.obj regexp.obj \ + search.obj sha256.obj sign.obj spell.obj spellfile.obj syntax.obj \ + tag.obj term.obj termlib.obj textprop.obj ui.obj undo.obj \ + userfunc.obj screen.obj version.obj window.obj os_unix.obj os_vms.obj \ + pathdef.obj if_mzsch.obj \ $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \ $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ) @@ -568,6 +576,10 @@ fileio.obj : fileio.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ globals.h farsi.h arabic.h +findfile.obj : findfile.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h farsi.h arabic.h fold.obj : fold.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \ diff --git a/src/Makefile b/src/Makefile index 8b83e9540a..60828ad16b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1592,6 +1592,7 @@ BASIC_SRC = \ ex_getln.c \ farsi.c \ fileio.c \ + findfile.c \ fold.c \ getchar.c \ hardcopy.c \ @@ -1705,6 +1706,7 @@ OBJ_COMMON = \ objects/ex_getln.o \ objects/farsi.o \ objects/fileio.o \ + objects/findfile.o \ objects/fold.o \ objects/getchar.o \ objects/hardcopy.o \ @@ -1831,6 +1833,7 @@ PRO_AUTO = \ ex_getln.pro \ farsi.pro \ fileio.pro \ + findfile.pro \ fold.pro \ getchar.pro \ hardcopy.pro \ @@ -2999,6 +3002,9 @@ objects/farsi.o: farsi.c objects/fileio.o: fileio.c $(CCC) -o $@ fileio.c +objects/findfile.o: findfile.c + $(CCC) -o $@ findfile.c + objects/fold.o: fold.c $(CCC) -o $@ fold.c @@ -3471,6 +3477,11 @@ objects/fileio.o: fileio.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h farsi.h arabic.h +objects/findfile.o: findfile.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h farsi.h arabic.h libvterm/include/vterm.h \ + libvterm/include/vterm_keycodes.h objects/fold.o: fold.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ diff --git a/src/README.txt b/src/README.txt index e1dc36f69c..52edeb73e6 100644 --- a/src/README.txt +++ b/src/README.txt @@ -22,8 +22,10 @@ Most code can be found in a file with an obvious name (incomplete list): diff.c diff mode (vimdiff) eval.c expression evaluation fileio.c reading and writing files + findfile.c search for files in 'path' fold.c folding getchar.c getting characters and key mapping + indent.c C and Lisp indentation mark.c marks mbyte.c multi-byte character handling memfile.c storing lines for buffers in a swapfile diff --git a/src/findfile.c b/src/findfile.c new file mode 100644 index 0000000000..9730b35a46 --- /dev/null +++ b/src/findfile.c @@ -0,0 +1,2607 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * findfile.c: Search for files in directories listed in 'path' + */ + +#include "vim.h" + +/* + * File searching functions for 'path', 'tags' and 'cdpath' options. + * External visible functions: + * vim_findfile_init() creates/initialises the search context + * vim_findfile_free_visited() free list of visited files/dirs of search + * context + * vim_findfile() find a file in the search context + * vim_findfile_cleanup() cleanup/free search context created by + * vim_findfile_init() + * + * All static functions and variables start with 'ff_' + * + * In general it works like this: + * First you create yourself a search context by calling vim_findfile_init(). + * It is possible to give a search context from a previous call to + * vim_findfile_init(), so it can be reused. After this you call vim_findfile() + * until you are satisfied with the result or it returns NULL. On every call it + * returns the next file which matches the conditions given to + * vim_findfile_init(). If it doesn't find a next file it returns NULL. + * + * It is possible to call vim_findfile_init() again to reinitialise your search + * with some new parameters. Don't forget to pass your old search context to + * it, so it can reuse it and especially reuse the list of already visited + * directories. If you want to delete the list of already visited directories + * simply call vim_findfile_free_visited(). + * + * When you are done call vim_findfile_cleanup() to free the search context. + * + * The function vim_findfile_init() has a long comment, which describes the + * needed parameters. + * + * + * + * ATTENTION: + * ========== + * Also we use an allocated search context here, this functions are NOT + * thread-safe!!!!! + * + * To minimize parameter passing (or because I'm to lazy), only the + * external visible functions get a search context as a parameter. This is + * then assigned to a static global, which is used throughout the local + * functions. + */ + +/* + * type for the directory search stack + */ +typedef struct ff_stack +{ + struct ff_stack *ffs_prev; + + // the fix part (no wildcards) and the part containing the wildcards + // of the search path + char_u *ffs_fix_path; +#ifdef FEAT_PATH_EXTRA + char_u *ffs_wc_path; +#endif + + // files/dirs found in the above directory, matched by the first wildcard + // of wc_part + char_u **ffs_filearray; + int ffs_filearray_size; + char_u ffs_filearray_cur; // needed for partly handled dirs + + // to store status of partly handled directories + // 0: we work on this directory for the first time + // 1: this directory was partly searched in an earlier step + int ffs_stage; + + // How deep are we in the directory tree? + // Counts backward from value of level parameter to vim_findfile_init + int ffs_level; + + // Did we already expand '**' to an empty string? + int ffs_star_star_empty; +} ff_stack_T; + +/* + * type for already visited directories or files. + */ +typedef struct ff_visited +{ + struct ff_visited *ffv_next; + +#ifdef FEAT_PATH_EXTRA + // Visited directories are different if the wildcard string are + // different. So we have to save it. + char_u *ffv_wc_path; +#endif + // for unix use inode etc for comparison (needed because of links), else + // use filename. +#ifdef UNIX + int ffv_dev_valid; // ffv_dev and ffv_ino were set + dev_t ffv_dev; // device number + ino_t ffv_ino; // inode number +#endif + // The memory for this struct is allocated according to the length of + // ffv_fname. + char_u ffv_fname[1]; // actually longer +} ff_visited_T; + +/* + * We might have to manage several visited lists during a search. + * This is especially needed for the tags option. If tags is set to: + * "./++/tags,./++/TAGS,++/tags" (replace + with *) + * So we have to do 3 searches: + * 1) search from the current files directory downward for the file "tags" + * 2) search from the current files directory downward for the file "TAGS" + * 3) search from Vims current directory downwards for the file "tags" + * As you can see, the first and the third search are for the same file, so for + * the third search we can use the visited list of the first search. For the + * second search we must start from a empty visited list. + * The struct ff_visited_list_hdr is used to manage a linked list of already + * visited lists. + */ +typedef struct ff_visited_list_hdr +{ + struct ff_visited_list_hdr *ffvl_next; + + // the filename the attached visited list is for + char_u *ffvl_filename; + + ff_visited_T *ffvl_visited_list; + +} ff_visited_list_hdr_T; + + +/* + * '**' can be expanded to several directory levels. + * Set the default maximum depth. + */ +#define FF_MAX_STAR_STAR_EXPAND ((char_u)30) + +/* + * The search context: + * ffsc_stack_ptr: the stack for the dirs to search + * ffsc_visited_list: the currently active visited list + * ffsc_dir_visited_list: the currently active visited list for search dirs + * ffsc_visited_lists_list: the list of all visited lists + * ffsc_dir_visited_lists_list: the list of all visited lists for search dirs + * ffsc_file_to_search: the file to search for + * ffsc_start_dir: the starting directory, if search path was relative + * ffsc_fix_path: the fix part of the given path (without wildcards) + * Needed for upward search. + * ffsc_wc_path: the part of the given path containing wildcards + * ffsc_level: how many levels of dirs to search downwards + * ffsc_stopdirs_v: array of stop directories for upward search + * ffsc_find_what: FINDFILE_BOTH, FINDFILE_DIR or FINDFILE_FILE + * ffsc_tagfile: searching for tags file, don't use 'suffixesadd' + */ +typedef struct ff_search_ctx_T +{ + ff_stack_T *ffsc_stack_ptr; + ff_visited_list_hdr_T *ffsc_visited_list; + ff_visited_list_hdr_T *ffsc_dir_visited_list; + ff_visited_list_hdr_T *ffsc_visited_lists_list; + ff_visited_list_hdr_T *ffsc_dir_visited_lists_list; + char_u *ffsc_file_to_search; + char_u *ffsc_start_dir; + char_u *ffsc_fix_path; +#ifdef FEAT_PATH_EXTRA + char_u *ffsc_wc_path; + int ffsc_level; + char_u **ffsc_stopdirs_v; +#endif + int ffsc_find_what; + int ffsc_tagfile; +} ff_search_ctx_T; + +// locally needed functions +#ifdef FEAT_PATH_EXTRA +static int ff_check_visited(ff_visited_T **, char_u *, char_u *); +#else +static int ff_check_visited(ff_visited_T **, char_u *); +#endif +static void vim_findfile_free_visited_list(ff_visited_list_hdr_T **list_headp); +static void ff_free_visited_list(ff_visited_T *vl); +static ff_visited_list_hdr_T* ff_get_visited_list(char_u *, ff_visited_list_hdr_T **list_headp); + +static void ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr); +static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx); +static void ff_clear(ff_search_ctx_T *search_ctx); +static void ff_free_stack_element(ff_stack_T *stack_ptr); +#ifdef FEAT_PATH_EXTRA +static ff_stack_T *ff_create_stack_element(char_u *, char_u *, int, int); +#else +static ff_stack_T *ff_create_stack_element(char_u *, int, int); +#endif +#ifdef FEAT_PATH_EXTRA +static int ff_path_in_stoplist(char_u *, int, char_u **); +#endif + +static char_u e_pathtoolong[] = N_("E854: path too long for completion"); + +static char_u *ff_expand_buffer = NULL; // used for expanding filenames + +#if 0 +/* + * if someone likes findfirst/findnext, here are the functions + * NOT TESTED!! + */ + +static void *ff_fn_search_context = NULL; + + char_u * +vim_findfirst(char_u *path, char_u *filename, int level) +{ + ff_fn_search_context = + vim_findfile_init(path, filename, NULL, level, TRUE, FALSE, + ff_fn_search_context, rel_fname); + if (NULL == ff_fn_search_context) + return NULL; + else + return vim_findnext() +} + + char_u * +vim_findnext(void) +{ + char_u *ret = vim_findfile(ff_fn_search_context); + + if (NULL == ret) + { + vim_findfile_cleanup(ff_fn_search_context); + ff_fn_search_context = NULL; + } + return ret; +} +#endif + +/* + * Initialization routine for vim_findfile(). + * + * Returns the newly allocated search context or NULL if an error occurred. + * + * Don't forget to clean up by calling vim_findfile_cleanup() if you are done + * with the search context. + * + * Find the file 'filename' in the directory 'path'. + * The parameter 'path' may contain wildcards. If so only search 'level' + * directories deep. The parameter 'level' is the absolute maximum and is + * not related to restricts given to the '**' wildcard. If 'level' is 100 + * and you use '**200' vim_findfile() will stop after 100 levels. + * + * 'filename' cannot contain wildcards! It is used as-is, no backslashes to + * escape special characters. + * + * If 'stopdirs' is not NULL and nothing is found downward, the search is + * restarted on the next higher directory level. This is repeated until the + * start-directory of a search is contained in 'stopdirs'. 'stopdirs' has the + * format ";*<dirname>*\(;<dirname>\)*;\=$". + * + * If the 'path' is relative, the starting dir for the search is either VIM's + * current dir or if the path starts with "./" the current files dir. + * If the 'path' is absolute, the starting dir is that part of the path before + * the first wildcard. + * + * Upward search is only done on the starting dir. + * + * If 'free_visited' is TRUE the list of already visited files/directories is + * cleared. Set this to FALSE if you just want to search from another + * directory, but want to be sure that no directory from a previous search is + * searched again. This is useful if you search for a file at different places. + * The list of visited files/dirs can also be cleared with the function + * vim_findfile_free_visited(). + * + * Set the parameter 'find_what' to FINDFILE_DIR if you want to search for + * directories only, FINDFILE_FILE for files only, FINDFILE_BOTH for both. + * + * A search context returned by a previous call to vim_findfile_init() can be + * passed in the parameter "search_ctx_arg". This context is reused and + * reinitialized with the new parameters. The list of already visited + * directories from this context is only deleted if the parameter + * "free_visited" is true. Be aware that the passed "search_ctx_arg" is freed + * if the reinitialization fails. + * + * If you don't have a search context from a previous call "search_ctx_arg" + * must be NULL. + * + * This function silently ignores a few errors, vim_findfile() will have + * limited functionality then. + */ + void * +vim_findfile_init( + char_u *path, + char_u *filename, + char_u *stopdirs UNUSED, + int level, + int free_visited, + int find_what, + void *search_ctx_arg, + int tagfile, // expanding names of tags files + char_u *rel_fname) // file name to use for "." +{ +#ifdef FEAT_PATH_EXTRA + char_u *wc_part; +#endif + ff_stack_T *sptr; + ff_search_ctx_T *search_ctx; + + // If a search context is given by the caller, reuse it, else allocate a + // new one. + if (search_ctx_arg != NULL) + search_ctx = search_ctx_arg; + else + { + search_ctx = (ff_search_ctx_T*)alloc((unsigned)sizeof(ff_search_ctx_T)); + if (search_ctx == NULL) + goto error_return; + vim_memset(search_ctx, 0, sizeof(ff_search_ctx_T)); + } + search_ctx->ffsc_find_what = find_what; + search_ctx->ffsc_tagfile = tagfile; + + // clear the search context, but NOT the visited lists + ff_clear(search_ctx); + + // clear visited list if wanted + if (free_visited == TRUE) + vim_findfile_free_visited(search_ctx); + else + { + // Reuse old visited lists. Get the visited list for the given + // filename. If no list for the current filename exists, creates a new + // one. + search_ctx->ffsc_visited_list = ff_get_visited_list(filename, + &search_ctx->ffsc_visited_lists_list); + if (search_ctx->ffsc_visited_list == NULL) + goto error_return; + search_ctx->ffsc_dir_visited_list = ff_get_visited_list(filename, + &search_ctx->ffsc_dir_visited_lists_list); + if (search_ctx->ffsc_dir_visited_list == NULL) + goto error_return; + } + + if (ff_expand_buffer == NULL) + { + ff_expand_buffer = (char_u*)alloc(MAXPATHL); + if (ff_expand_buffer == NULL) + goto error_return; + } + + // Store information on starting dir now if path is relative. + // If path is absolute, we do that later. + if (path[0] == '.' + && (vim_ispathsep(path[1]) || path[1] == NUL) + && (!tagfile || vim_strchr(p_cpo, CPO_DOTTAG) == NULL) + && rel_fname != NULL) + { + int len = (int)(gettail(rel_fname) - rel_fname); + + if (!vim_isAbsName(rel_fname) && len + 1 < MAXPATHL) + { + // Make the start dir an absolute path name. + vim_strncpy(ff_expand_buffer, rel_fname, len); + search_ctx->ffsc_start_dir = FullName_save(ff_expand_buffer, FALSE); + } + else + search_ctx->ffsc_start_dir = vim_strnsave(rel_fname, len); + if (search_ctx->ffsc_start_dir == NULL) + goto error_return; + if (*++path != NUL) + ++path; + } + else if (*path == NUL || !vim_isAbsName(path)) + { +#ifdef BACKSLASH_IN_FILENAME + // "c:dir" needs "c:" to be expanded, otherwise use current dir + if (*path != NUL && path[1] == ':') + { + char_u drive[3]; + + drive[0] = path[0]; + drive[1] = ':'; + drive[2] = NUL; + if (vim_FullName(drive, ff_expand_buffer, MAXPATHL, TRUE) == FAIL) + goto error_return; + path += 2; + } + else +#endif + if (mch_dirname(ff_expand_buffer, MAXPATHL) == FAIL) + goto error_return; + + search_ctx->ffsc_start_dir = vim_strsave(ff_expand_buffer); + if (search_ctx->ffsc_start_dir == NULL) + goto error_return; + +#ifdef BACKSLASH_IN_FILENAME + // A path that starts with "/dir" is relative to the drive, not to the + // directory (but not for "//machine/dir"). Only use the drive name. + if ((*path == '/' || *path == '\\') + && path[1] != path[0] + && search_ctx->ffsc_start_dir[1] == ':') + search_ctx->ffsc_start_dir[2] = NUL; +#endif + } + +#ifdef FEAT_PATH_EXTRA + /* + * If stopdirs are given, split them into an array of pointers. + * If this fails (mem allocation), there is no upward search at all or a + * stop directory is not recognized -> continue silently. + * If stopdirs just contains a ";" or is empty, + * search_ctx->ffsc_stopdirs_v will only contain a NULL pointer. This + * is handled as unlimited upward search. See function + * ff_path_in_stoplist() for details. + */ + if (stopdirs != NULL) + { + char_u *walker = stopdirs; + int dircount; + + while (*walker == ';') + walker++; + + dircount = 1; + search_ctx->ffsc_stopdirs_v = + (char_u **)alloc((unsigned)sizeof(char_u *)); + + if (search_ctx->ffsc_stopdirs_v != NULL) + { + do + { + char_u *helper; + void *ptr; + + helper = walker; + ptr = vim_realloc(search_ctx->ffsc_stopdirs_v, + (dircount + 1) * sizeof(char_u *)); + if (ptr) + search_ctx->ffsc_stopdirs_v = ptr; + else + // ignore, keep what we have and continue + break; + walker = vim_strchr(walker, ';'); + if (walker) + { + search_ctx->ffsc_stopdirs_v[dircount-1] = + vim_strnsave(helper, (int)(walker - helper)); + walker++; + } + else + // this might be "", which means ascent till top + // of directory tree. + search_ctx->ffsc_stopdirs_v[dircount-1] = + vim_strsave(helper); + + dircount++; + + } while (walker != NULL); + search_ctx->ffsc_stopdirs_v[dircount-1] = NULL; + } + } +#endif + +#ifdef FEAT_PATH_EXTRA + search_ctx->ffsc_level = level; + + /* + * split into: + * -fix path + * -wildcard_stuff (might be NULL) + */ + wc_part = vim_strchr(path, '*'); + if (wc_part != NULL) + { + int llevel; + int len; + char *errpt; + + // save the fix part of the path + search_ctx->ffsc_fix_path = vim_strnsave(path, (int)(wc_part - path)); + + /* + * copy wc_path and add restricts to the '**' wildcard. + * The octet after a '**' is used as a (binary) counter. + * So '**3' is transposed to '**^C' ('^C' is ASCII value 3) + * or '**76' is transposed to '**N'( 'N' is ASCII value 76). + * For EBCDIC you get different character values. + * If no restrict is given after '**' the default is used. + * Due to this technique the path looks awful if you print it as a + * string. + */ + len = 0; + while (*wc_part != NUL) + { + if (len + 5 >= MAXPATHL) + { + emsg(_(e_pathtoolong)); + break; + } + if (STRNCMP(wc_part, "**", 2) == 0) + { + ff_expand_buffer[len++] = *wc_part++; + ff_expand_buffer[len++] = *wc_part++; + + llevel = strtol((char *)wc_part, &errpt, 10); + if ((char_u *)errpt != wc_part && llevel > 0 && llevel < 255) + ff_expand_buffer[len++] = llevel; + else if ((char_u *)errpt != wc_part && llevel == 0) + // restrict is 0 -> remove already added '**' + len -= 2; + else + ff_expand_buffer[len++] = FF_MAX_STAR_STAR_EXPAND; + wc_part = (char_u *)errpt; + if (*wc_part != NUL && !vim_ispathsep(*wc_part)) + { + semsg(_("E343: Invalid path: '**[number]' must be at the end of the path or be followed by '%s'."), PATHSEPSTR); + goto error_return; + } + } + else + ff_expand_buffer[len++] = *wc_part++; + } + ff_expand_buffer[len] = NUL; + search_ctx->ffsc_wc_path = vim_strsave(ff_expand_buffer); + + if (search_ctx->ffsc_wc_path == NULL) + goto error_return; + } + else +#endif + search_ctx->ffsc_fix_path = vim_strsave(path); + + if (search_ctx->ffsc_start_dir == NULL) + { + // store the fix part as startdir. + // This is needed if the parameter path is fully qualified. + search_ctx->ffsc_start_dir = vim_strsave(search_ctx->ffsc_fix_path); + if (search_ctx->ffsc_start_dir == NULL) + goto error_return; + search_ctx->ffsc_fix_path[0] = NUL; + } + + // create an absolute path + if (STRLEN(search_ctx->ffsc_start_dir) + + STRLEN(search_ctx->ffsc_fix_path) + 3 >= MAXPATHL) + { + emsg(_(e_pathtoolong)); + goto error_return; + } + STRCPY(ff_expand_buffer, search_ctx->ffsc_start_dir); + add_pathsep(ff_expand_buffer); + { + int eb_len = (int)STRLEN(ff_expand_buffer); + char_u *buf = alloc(eb_len + + (int)STRLEN(search_ctx->ffsc_fix_path) + 1); + + STRCPY(buf, ff_expand_buffer); + STRCPY(buf + eb_len, search_ctx->ffsc_fix_path); + if ( |