summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-02-13 23:13:28 +0100
committerBram Moolenaar <Bram@vim.org>2019-02-13 23:13:28 +0100
commit5fd0f5052f9a312bb4cfe7b4176b1211d45127ee (patch)
treeb82e59bfdcf65662e44803dabbfa77c11010bb70
parent688b3983d8b321e0d32dd51914fa474a0988daf6 (diff)
patch 8.1.0914: code related to findfile() is spread outv8.1.0914
Problem: Code related to findfile() is spread out. Solution: Put findfile() related code into a new source file. (Yegappan Lakshmanan, closes #3934)
-rw-r--r--Filelist2
-rw-r--r--src/Make_bc5.mak1
-rw-r--r--src/Make_cyg_ming.mak1
-rw-r--r--src/Make_dice.mak4
-rw-r--r--src/Make_ivc.mak5
-rw-r--r--src/Make_manx.mak6
-rw-r--r--src/Make_morph.mak1
-rw-r--r--src/Make_mvc.mak4
-rw-r--r--src/Make_sas.mak5
-rw-r--r--src/Make_vms.mms40
-rw-r--r--src/Makefile11
-rw-r--r--src/README.txt2
-rw-r--r--src/findfile.c2607
-rw-r--r--src/misc1.c528
-rw-r--r--src/misc2.c1926
-rw-r--r--src/proto.h1
-rw-r--r--src/proto/findfile.pro18
-rw-r--r--src/proto/misc1.pro5
-rw-r--r--src/proto/misc2.pro8
-rw-r--r--src/proto/window.pro7
-rw-r--r--src/version.c2
-rw-r--r--src/window.c315
22 files changed, 2775 insertions, 2724 deletions
diff --git a/Filelist b/Filelist
index cd9cbf311a..211b487673 100644
--- a/Filelist
+++ b/Filelist
@@ -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 (se