summaryrefslogtreecommitdiff
path: root/data/vim/patches/8.1.0914
diff options
context:
space:
mode:
Diffstat (limited to 'data/vim/patches/8.1.0914')
-rw-r--r--data/vim/patches/8.1.09146010
1 files changed, 6010 insertions, 0 deletions
diff --git a/data/vim/patches/8.1.0914 b/data/vim/patches/8.1.0914
new file mode 100644
index 000000000..8c2e83263
--- /dev/null
+++ b/data/vim/patches/8.1.0914
@@ -0,0 +1,6010 @@
+To: vim_dev@googlegroups.com
+Subject: Patch 8.1.0914
+Fcc: outbox
+From: Bram Moolenaar <Bram@moolenaar.net>
+Mime-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+------------
+
+Patch 8.1.0914
+Problem: Code related to findfile() is spread out.
+Solution: Put findfile() related code into a new source file. (Yegappan
+ Lakshmanan, closes #3934)
+Files: Filelist, src/Make_bc5.mak, src/Make_cyg_ming.mak,
+ src/Make_dice.mak, src/Make_ivc.mak, src/Make_manx.mak,
+ src/Make_morph.mak, src/Make_mvc.mak, src/Make_sas.mak,
+ src/Make_vms.mms, src/Makefile, src/README.txt, src/findfile.c,
+ src/misc1.c, src/misc2.c, src/proto.h, src/proto/findfile.pro,
+ src/proto/misc1.pro, src/proto/misc2.pro, src/proto/window.pro,
+ src/window.c
+
+
+*** ../vim-8.1.0913/Filelist 2019-02-03 23:45:09.282345807 +0100
+--- Filelist 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 41,46 ****
+--- 41,47 ----
+ src/farsi.h \
+ src/feature.h \
+ src/fileio.c \
++ src/findfile.c \
+ src/fold.c \
+ src/getchar.c \
+ src/globals.h \
+***************
+*** 170,175 ****
+--- 171,177 ----
+ 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 \
+*** ../vim-8.1.0913/src/Make_bc5.mak 2019-01-31 13:47:51.118632672 +0100
+--- src/Make_bc5.mak 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 544,549 ****
+--- 544,550 ----
+ $(OBJDIR)\ex_getln.obj \
+ $(OBJDIR)\farsi.obj \
+ $(OBJDIR)\fileio.obj \
++ $(OBJDIR)\findfile.obj \
+ $(OBJDIR)\fold.obj \
+ $(OBJDIR)\getchar.obj \
+ $(OBJDIR)\hardcopy.obj \
+*** ../vim-8.1.0913/src/Make_cyg_ming.mak 2019-01-31 14:43:15.563570608 +0100
+--- src/Make_cyg_ming.mak 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 716,721 ****
+--- 716,722 ----
+ $(OUTDIR)/ex_getln.o \
+ $(OUTDIR)/farsi.o \
+ $(OUTDIR)/fileio.o \
++ $(OUTDIR)/findfile.o \
+ $(OUTDIR)/fold.o \
+ $(OUTDIR)/getchar.o \
+ $(OUTDIR)/hardcopy.o \
+*** ../vim-8.1.0913/src/Make_dice.mak 2019-01-31 13:47:51.118632672 +0100
+--- src/Make_dice.mak 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 46,51 ****
+--- 46,52 ----
+ ex_getln.c \
+ farsi.c \
+ fileio.c \
++ findfile.c \
+ fold.c \
+ getchar.c \
+ hardcopy.c \
+***************
+*** 105,110 ****
+--- 106,112 ----
+ o/ex_getln.o \
+ o/farsi.o \
+ o/fileio.o \
++ o/findfile.o \
+ o/fold.o \
+ o/getchar.o \
+ o/hardcopy.o \
+***************
+*** 203,208 ****
+--- 205,212 ----
+
+ o/fileio.o: fileio.c $(SYMS)
+
++ o/findfile.o: findfile.c $(SYMS)
++
+ o/fold.o: fold.c $(SYMS)
+
+ o/getchar.o: getchar.c $(SYMS)
+*** ../vim-8.1.0913/src/Make_ivc.mak 2019-01-31 13:47:51.118632672 +0100
+--- src/Make_ivc.mak 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 230,235 ****
+--- 230,236 ----
+ "$(INTDIR)/ex_getln.obj" \
+ "$(INTDIR)/farsi.obj" \
+ "$(INTDIR)/fileio.obj" \
++ "$(INTDIR)/findfile.obj" \
+ "$(INTDIR)/fold.obj" \
+ "$(INTDIR)/getchar.obj" \
+ "$(INTDIR)/hardcopy.obj" \
+***************
+*** 419,424 ****
+--- 420,429 ----
+ SOURCE=.\fileio.c
+ # End Source File
+ # Begin Source File
++ #
++ SOURCE=.\findfile.c
++ # End Source File
++ # Begin Source File
+
+ SOURCE=.\fold.c
+ # End Source File
+*** ../vim-8.1.0913/src/Make_manx.mak 2019-01-31 13:47:51.118632672 +0100
+--- src/Make_manx.mak 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 56,61 ****
+--- 56,62 ----
+ ex_getln.c \
+ farsi.c \
+ fileio.c \
++ findfile.c \
+ fold.c \
+ getchar.c \
+ hardcopy.c \
+***************
+*** 117,122 ****
+--- 118,124 ----
+ obj/ex_getln.o \
+ obj/farsi.o \
+ obj/fileio.o \
++ obj/findfile.o \
+ obj/fold.o \
+ obj/getchar.o \
+ obj/hardcopy.o \
+***************
+*** 176,181 ****
+--- 178,184 ----
+ proto/ex_getln.pro \
+ proto/farsi.pro \
+ proto/fileio.pro \
++ proto/findfile.pro \
+ proto/fold.pro \
+ proto/getchar.pro \
+ proto/hardcopy.pro \
+***************
+*** 320,325 ****
+--- 323,331 ----
+ obj/fileio.o: fileio.c
+ $(CCSYM) $@ fileio.c
+
++ obj/findfile.o: findfile.c
++ $(CCSYM) $@ findfile.c
++
+ obj/fold.o: fold.c
+ $(CCSYM) $@ fold.c
+
+*** ../vim-8.1.0913/src/Make_morph.mak 2019-01-31 13:47:51.118632672 +0100
+--- src/Make_morph.mak 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 44,49 ****
+--- 44,50 ----
+ ex_getln.c \
+ farsi.c \
+ fileio.c \
++ findfile.c \
+ fold.c \
+ getchar.c \
+ hardcopy.c \
+*** ../vim-8.1.0913/src/Make_mvc.mak 2019-01-31 13:47:51.118632672 +0100
+--- src/Make_mvc.mak 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 721,726 ****
+--- 721,727 ----
+ $(OUTDIR)\ex_getln.obj \
+ $(OUTDIR)\farsi.obj \
+ $(OUTDIR)\fileio.obj \
++ $(OUTDIR)\findfile.obj \
+ $(OUTDIR)\fold.obj \
+ $(OUTDIR)\getchar.obj \
+ $(OUTDIR)\hardcopy.obj \
+***************
+*** 1407,1412 ****
+--- 1408,1415 ----
+
+ $(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,1650 ****
+--- 1648,1654 ----
+ proto/ex_getln.pro \
+ proto/farsi.pro \
+ proto/fileio.pro \
++ proto/findfile.pro \
+ proto/getchar.pro \
+ proto/hardcopy.pro \
+ proto/hashtab.pro \
+*** ../vim-8.1.0913/src/Make_sas.mak 2019-01-31 13:47:51.118632672 +0100
+--- src/Make_sas.mak 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 109,114 ****
+--- 109,115 ----
+ ex_getln.c \
+ farsi.c \
+ fileio.c \
++ findfile.c \
+ fold.c \
+ getchar.c \
+ hardcopy.c \
+***************
+*** 169,174 ****
+--- 170,176 ----
+ ex_getln.o \
+ farsi.o \
+ fileio.o \
++ findfile.o \
+ fold.o \
+ getchar.o \
+ hardcopy.o \
+***************
+*** 229,234 ****
+--- 231,237 ----
+ proto/ex_getln.pro \
+ proto/farsi.pro \
+ proto/fileio.pro \
++ proto/findfile.pro \
+ proto/fold.pro \
+ proto/getchar.pro \
+ proto/hardcopy.pro \
+***************
+*** 363,368 ****
+--- 366,373 ----
+ 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
+*** ../vim-8.1.0913/src/Make_vms.mms 2019-01-31 13:47:51.118632672 +0100
+--- src/Make_vms.mms 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 312,334 ****
+ 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 \
+ $(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\
+ $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \
+ $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ)
+
+--- 312,342 ----
+ 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 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 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,573 ****
+--- 576,585 ----
+ 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 \
+*** ../vim-8.1.0913/src/Makefile 2019-02-01 20:42:18.718884011 +0100
+--- src/Makefile 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 1592,1597 ****
+--- 1592,1598 ----
+ ex_getln.c \
+ farsi.c \
+ fileio.c \
++ findfile.c \
+ fold.c \
+ getchar.c \
+ hardcopy.c \
+***************
+*** 1705,1710 ****
+--- 1706,1712 ----
+ objects/ex_getln.o \
+ objects/farsi.o \
+ objects/fileio.o \
++ objects/findfile.o \
+ objects/fold.o \
+ objects/getchar.o \
+ objects/hardcopy.o \
+***************
+*** 1831,1836 ****
+--- 1833,1839 ----
+ ex_getln.pro \
+ farsi.pro \
+ fileio.pro \
++ findfile.pro \
+ fold.pro \
+ getchar.pro \
+ hardcopy.pro \
+***************
+*** 2999,3004 ****
+--- 3002,3010 ----
+ 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,3476 ****
+--- 3477,3487 ----
+ 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 \
+*** ../vim-8.1.0913/src/README.txt 2019-01-26 16:20:44.260683581 +0100
+--- src/README.txt 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 22,29 ****
+--- 22,31 ----
+ 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
+*** ../vim-8.1.0913/src/findfile.c 2019-02-13 22:43:46.429534874 +0100
+--- src/findfile.c 2019-02-13 22:32:25.770956617 +0100
+***************
+*** 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 (mch_isdir(buf))
++ {
++ STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path);
++ add_pathsep(ff_expand_buffer);
++ }
++ #ifdef FEAT_PATH_EXTRA
++ else
++ {
++ char_u *p = gettail(search_ctx->ffsc_fix_path);
++ char_u *wc_path = NULL;
++ char_u *temp = NULL;
++ int len = 0;
++
++ if (p > search_ctx->ffsc_fix_path)
++ {
++ len = (int)(p - search_ctx->ffsc_fix_path) - 1;
++ STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len);
++ add_pathsep(ff_expand_buffer);
++ }
++ else
++ len = (int)STRLEN(search_ctx->ffsc_fix_path);
++
++ if (search_ctx->ffsc_wc_path != NULL)
++ {
++ wc_path = vim_strsave(search_ctx->ffsc_wc_path);
++ temp = alloc((int)(STRLEN(search_ctx->ffsc_wc_path)
++ + STRLEN(search_ctx->ffsc_fix_path + len)
++ + 1));
++ if (temp == NULL || wc_path == NULL)
++ {
++ vim_free(buf);
++ vim_free(temp);
++ vim_free(wc_path);
++ goto error_return;
++ }
++
++ STRCPY(temp, search_ctx->ffsc_fix_path + len);
++ STRCAT(temp, search_ctx->ffsc_wc_path);
++ vim_free(search_ctx->ffsc_wc_path);
++ vim_free(wc_path);
++ search_ctx->ffsc_wc_path = temp;
++ }
++ }
++ #endif
++ vim_free(buf);
++ }
++
++ sptr = ff_create_stack_element(ff_expand_buffer,
++ #ifdef FEAT_PATH_EXTRA
++ search_ctx->ffsc_wc_path,
++ #endif
++ level, 0);
++
++ if (sptr == NULL)
++ goto error_return;
++
++ ff_push(search_ctx, sptr);
++
++ search_ctx->ffsc_file_to_search = vim_strsave(filename);
++ if (search_ctx->ffsc_file_to_search == NULL)
++ goto error_return;
++
++ return search_ctx;
++
++ error_return:
++ /*
++ * We clear the search context now!
++ * Even when the caller gave us a (perhaps valid) context we free it here,
++ * as we might have already destroyed it.
++ */
++ vim_findfile_cleanup(search_ctx);
++ return NULL;
++ }
++
++ #if defined(FEAT_PATH_EXTRA) || defined(PROTO)
++ /*
++ * Get the stopdir string. Check that ';' is not escaped.
++ */
++ char_u *
++ vim_findfile_stopdir(char_u *buf)
++ {
++ char_u *r_ptr = buf;
++
++ while (*r_ptr != NUL && *r_ptr != ';')
++ {
++ if (r_ptr[0] == '\\' && r_ptr[1] == ';')
++ {
++ // Overwrite the escape char,
++ // use STRLEN(r_ptr) to move the trailing '\0'.
++ STRMOVE(r_ptr, r_ptr + 1);
++ r_ptr++;
++ }
++ r_ptr++;
++ }
++ if (*r_ptr == ';')
++ {
++ *r_ptr = 0;
++ r_ptr++;
++ }
++ else if (*r_ptr == NUL)
++ r_ptr = NULL;
++ return r_ptr;
++ }
++ #endif
++
++ /*
++ * Clean up the given search context. Can handle a NULL pointer.
++ */
++ void
++ vim_findfile_cleanup(void *ctx)
++ {
++ if (ctx == NULL)
++ return;
++
++ vim_findfile_free_visited(ctx);
++ ff_clear(ctx);
++ vim_free(ctx);
++ }
++
++ /*
++ * Find a file in a search context.
++ * The search context was created with vim_findfile_init() above.
++ * Return a pointer to an allocated file name or NULL if nothing found.
++ * To get all matching files call this function until you get NULL.
++ *
++ * If the passed search_context is NULL, NULL is returned.
++ *
++ * The search algorithm is depth first. To change this replace the
++ * stack with a list (don't forget to leave partly searched directories on the
++ * top of the list).
++ */
++ char_u *
++ vim_findfile(void *search_ctx_arg)
++ {
++ char_u *file_path;
++ #ifdef FEAT_PATH_EXTRA
++ char_u *rest_of_wildcards;
++ char_u *path_end = NULL;
++ #endif
++ ff_stack_T *stackp;
++ #if defined(FEAT_SEARCHPATH) || defined(FEAT_PATH_EXTRA)
++ int len;
++ #endif
++ int i;
++ char_u *p;
++ #ifdef FEAT_SEARCHPATH
++ char_u *suf;
++ #endif
++ ff_search_ctx_T *search_ctx;
++
++ if (search_ctx_arg == NULL)
++ return NULL;
++
++ search_ctx = (ff_search_ctx_T *)search_ctx_arg;
++
++ /*
++ * filepath is used as buffer for various actions and as the storage to
++ * return a found filename.
++ */
++ if ((file_path = alloc((int)MAXPATHL)) == NULL)
++ return NULL;
++
++ #ifdef FEAT_PATH_EXTRA
++ // store the end of the start dir -- needed for upward search
++ if (search_ctx->ffsc_start_dir != NULL)
++ path_end = &search_ctx->ffsc_start_dir[
++ STRLEN(search_ctx->ffsc_start_dir)];
++ #endif
++
++ #ifdef FEAT_PATH_EXTRA
++ // upward search loop
++ for (;;)
++ {
++ #endif
++ // downward search loop
++ for (;;)
++ {
++ // check if user user wants to stop the search
++ ui_breakcheck();
++ if (got_int)
++ break;
++
++ // get directory to work on from stack
++ stackp = ff_pop(search_ctx);
++ if (stackp == NULL)
++ break;
++
++ /*
++ * TODO: decide if we leave this test in
++ *
++ * GOOD: don't search a directory(-tree) twice.
++ * BAD: - check linked list for every new directory entered.
++ * - check for double files also done below
++ *
++ * Here we check if we already searched this directory.
++ * We already searched a directory if:
++ * 1) The directory is the same.
++ * 2) We would use the same wildcard string.
++ *
++ * Good if you have links on same directory via several ways
++ * or you have selfreferences in directories (e.g. SuSE Linux 6.3:
++ * /etc/rc.d/init.d is linked to /etc/rc.d -> endless loop)
++ *
++ * This check is only needed for directories we work on for the
++ * first time (hence stackp->ff_filearray == NULL)
++ */
++ if (stackp->ffs_filearray == NULL
++ && ff_check_visited(&search_ctx->ffsc_dir_visited_list
++ ->ffvl_visited_list,
++ stackp->ffs_fix_path
++ #ifdef FEAT_PATH_EXTRA
++ , stackp->ffs_wc_path
++ #endif
++ ) == FAIL)
++ {
++ #ifdef FF_VERBOSE
++ if (p_verbose >= 5)
++ {
++ verbose_enter_scroll();
++ smsg("Already Searched: %s (%s)",
++ stackp->ffs_fix_path, stackp->ffs_wc_path);
++ // don't overwrite this either
++ msg_puts("\n");
++ verbose_leave_scroll();
++ }
++ #endif
++ ff_free_stack_element(stackp);
++ continue;
++ }
++ #ifdef FF_VERBOSE
++ else if (p_verbose >= 5)
++ {
++ verbose_enter_scroll();
++ smsg("Searching: %s (%s)",
++ stackp->ffs_fix_path, stackp->ffs_wc_path);
++ // don't overwrite this either
++ msg_puts("\n");
++ verbose_leave_scroll();
++ }
++ #endif
++
++ // check depth
++ if (stackp->ffs_level <= 0)
++ {
++ ff_free_stack_element(stackp);
++ continue;
++ }
++
++ file_path[0] = NUL;
++
++ /*
++ * If no filearray till now expand wildcards
++ * The function expand_wildcards() can handle an array of paths
++ * and all possible expands are returned in one array. We use this
++ * to handle the expansion of '**' into an empty string.
++ */
++ if (stackp->ffs_filearray == NULL)
++ {
++ char_u *dirptrs[2];
++
++ // we use filepath to build the path expand_wildcards() should
++ // expand.
++ dirptrs[0] = file_path;
++ dirptrs[1] = NULL;
++
++ // if we have a start dir copy it in
++ if (!vim_isAbsName(stackp->ffs_fix_path)
++ && search_ctx->ffsc_start_dir)
++ {
++ if (STRLEN(search_ctx->ffsc_start_dir) + 1 < MAXPATHL)
++ {
++ STRCPY(file_path, search_ctx->ffsc_start_dir);
++ add_pathsep(file_path);
++ }
++ else
++ {
++ ff_free_stack_element(stackp);
++ goto fail;
++ }
++ }
++
++ // append the fix part of the search path
++ if (STRLEN(file_path) + STRLEN(stackp->ffs_fix_path) + 1
++ < MAXPATHL)
++ {
++ STRCAT(file_path, stackp->ffs_fix_path);
++ add_pathsep(file_path);
++ }
++ else
++ {
++ ff_free_stack_element(stackp);
++ goto fail;
++ }
++
++ #ifdef FEAT_PATH_EXTRA
++ rest_of_wildcards = stackp->ffs_wc_path;
++ if (*rest_of_wildcards != NUL)
++ {
++ len = (int)STRLEN(file_path);
++ if (STRNCMP(rest_of_wildcards, "**", 2) == 0)
++ {
++ // pointer to the restrict byte
++ // The restrict byte is not a character!
++ p = rest_of_wildcards + 2;
++
++ if (*p > 0)
++ {
++ (*p)--;
++ if (len + 1 < MAXPATHL)
++ file_path[len++] = '*';
++ else
++ {
++ ff_free_stack_element(stackp);
++ goto fail;
++ }
++ }
++
++ if (*p == 0)
++ {
++ // remove '**<numb> from wildcards
++ STRMOVE(rest_of_wildcards, rest_of_wildcards + 3);
++ }
++ else
++ rest_of_wildcards += 3;
++
++ if (stackp->ffs_star_star_empty == 0)
++ {
++ // if not done before, expand '**' to empty
++ stackp->ffs_star_star_empty = 1;
++ dirptrs[1] = stackp->ffs_fix_path;
++ }
++ }
++
++ /*
++ * Here we copy until the next path separator or the end of
++ * the path. If we stop at a path separator, there is
++ * still something else left. This is handled below by
++ * pushing every directory returned from expand_wildcards()
++ * on the stack again for further search.
++ */
++ while (*rest_of_wildcards
++ && !vim_ispathsep(*rest_of_wildcards))
++ if (len + 1 < MAXPATHL)
++ file_path[len++] = *rest_of_wildcards++;
++ else
++ {
++ ff_free_stack_element(stackp);
++ goto fail;
++ }
++
++ file_path[len] = NUL;
++ if (vim_ispathsep(*rest_of_wildcards))
++ rest_of_wildcards++;
++ }
++ #endif
++
++ /*
++ * Expand wildcards like "*" and "$VAR".
++ * If the path is a URL don't try this.
++ */
++ if (path_with_url(dirptrs[0]))
++ {
++ stackp->ffs_filearray = (char_u **)
++ alloc((unsigned)sizeof(char *));
++ if (stackp->ffs_filearray != NULL
++ && (stackp->ffs_filearray[0]
++ = vim_strsave(dirptrs[0])) != NULL)
++ stackp->ffs_filearray_size = 1;
++ else
++ stackp->ffs_filearray_size = 0;
++ }
++ else
++ // Add EW_NOTWILD because the expanded path may contain
++ // wildcard characters that are to be taken literally.
++ // This is a bit of a hack.
++ expand_wildcards((dirptrs[1] == NULL) ? 1 : 2, dirptrs,
++ &stackp->ffs_filearray_size,
++ &stackp->ffs_filearray,
++ EW_DIR|EW_ADDSLASH|EW_SILENT|EW_NOTWILD);
++
++ stackp->ffs_filearray_cur = 0;
++ stackp->ffs_stage = 0;
++ }
++ #ifdef FEAT_PATH_EXTRA
++ else
++ rest_of_wildcards = &stackp->ffs_wc_path[
++ STRLEN(stackp->ffs_wc_path)];
++ #endif
++
++ if (stackp->ffs_stage == 0)
++ {
++ // this is the first time we work on this directory
++ #ifdef FEAT_PATH_EXTRA
++ if (*rest_of_wildcards == NUL)
++ #endif
++ {
++ /*
++ * We don't have further wildcards to expand, so we have to
++ * check for the final file now.
++ */
++ for (i = stackp->ffs_filearray_cur;
++ i < stackp->ffs_filearray_size; ++i)
++ {
++ if (!path_with_url(stackp->ffs_filearray[i])
++ && !mch_isdir(stackp->ffs_filearray[i]))
++ continue; /* not a directory */
++
++ // prepare the filename to be checked for existence
++ // below
++ if (STRLEN(stackp->ffs_filearray[i]) + 1
++ + STRLEN(search_ctx->ffsc_file_to_search)
++ < MAXPATHL)
++ {
++ STRCPY(file_path, stackp->ffs_filearray[i]);
++ add_pathsep(file_path);
++ STRCAT(file_path, search_ctx->ffsc_file_to_search);
++ }
++ else
++ {
++ ff_free_stack_element(stackp);
++ goto fail;
++ }
++
++ /*
++ * Try without extra suffix and then with suffixes
++ * from 'suffixesadd'.
++ */
++ #ifdef FEAT_SEARCHPATH
++ len = (int)STRLEN(file_path);
++ if (search_ctx->ffsc_tagfile)
++ suf = (char_u *)"";
++ else
++ suf = curbuf->b_p_sua;
++ for (;;)
++ #endif
++ {
++ // if file exists and we didn't already find it
++ if ((path_with_url(file_path)
++ || (mch_getperm(file_path) >= 0
++ && (search_ctx->ffsc_find_what
++ == FINDFILE_BOTH
++ || ((search_ctx->ffsc_find_what
++ == FINDFILE_DIR)
++ == mch_isdir(file_path)))))
++ #ifndef FF_VERBOSE
++ && (ff_check_visited(
++ &search_ctx->ffsc_visited_list->ffvl_visited_list,
++ file_path
++ #ifdef FEAT_PATH_EXTRA
++ , (char_u *)""
++ #endif
++ ) == OK)
++ #endif
++ )
++ {
++ #ifdef FF_VERBOSE
++ if (ff_check_visited(
++ &search_ctx->ffsc_visited_list->ffvl_visited_list,
++ file_path
++ #ifdef FEAT_PATH_EXTRA
++ , (char_u *)""
++ #endif
++ ) == FAIL)
++ {
++ if (p_verbose >= 5)
++ {
++ verbose_enter_scroll();
++ smsg("Already: %s",
++ file_path);
++ // don't overwrite this either
++ msg_puts("\n");
++ verbose_leave_scroll();
++ }
++ continue;
++ }
++ #endif
++
++ // push dir to examine rest of subdirs later
++ stackp->ffs_filearray_cur = i + 1;
++ ff_push(search_ctx, stackp);
++
++ if (!path_with_url(file_path))
++ simplify_filename(file_path);
++ if (mch_dirname(ff_expand_buffer, MAXPATHL)
++ == OK)
++ {
++ p = shorten_fname(file_path,
++ ff_expand_buffer);
++ if (p != NULL)
++ STRMOVE(file_path, p);
++ }
++ #ifdef FF_VERBOSE
++ if (p_verbose >= 5)
++ {
++ verbose_enter_scroll();
++ smsg("HIT: %s", file_path);
++ // don't overwrite this either
++ msg_puts("\n");
++ verbose_leave_scroll();
++ }
++ #endif
++ return file_path;
++ }
++
++ #ifdef FEAT_SEARCHPATH
++ // Not found or found already, try next suffix.
++ if (*suf == NUL)
++ break;
++ copy_option_part(&suf, file_path + len,
++ MAXPATHL - len, ",");
++ #endif
++ }
++ }
++ }
++ #ifdef FEAT_PATH_EXTRA
++ else
++ {
++ /*
++ * still wildcards left, push the directories for further
++ * search
++ */
++ for (i = stackp->ffs_filearray_cur;
++ i < stackp->ffs_filearray_size; ++i)
++ {
++ if (!mch_isdir(stackp->ffs_filearray[i]))
++ continue; // not a directory
++
++ ff_push(search_ctx,
++ ff_create_stack_element(
++ stackp->ffs_filearray[i],
++ rest_of_wildcards,
++ stackp->ffs_level - 1, 0));
++ }
++ }
++ #endif
++ stackp->ffs_filearray_cur = 0;
++ stackp->ffs_stage = 1;
++ }
++
++ #ifdef FEAT_PATH_EXTRA
++ /*
++ * if wildcards contains '**' we have to descent till we reach the
++ * leaves of the directory tree.
++ */
++ if (STRNCMP(stackp->ffs_wc_path, "**", 2) == 0)
++ {
++ for (i = stackp->ffs_filearray_cur;
++ i < stackp->ffs_filearray_size; ++i)
++ {
++ if (fnamecmp(stackp->ffs_filearray[i],
++ stackp->ffs_fix_path) == 0)
++ continue; // don't repush same directory
++ if (!mch_isdir(stackp->ffs_filearray[i]))
++ continue; // not a directory
++ ff_push(search_ctx,
++ ff_create_stack_element(stackp->ffs_filearray[i],
++ stackp->ffs_wc_path, stackp->ffs_level - 1, 1));
++ }
++ }
++ #endif
++
++ // we are done with the current directory
++ ff_free_stack_element(stackp);
++
++ }
++
++ #ifdef FEAT_PATH_EXTRA
++ // If we reached this, we didn't find anything downwards.
++ // Let's check if we should do an upward search.
++ if (search_ctx->ffsc_start_dir
++ && search_ctx->ffsc_stopdirs_v != NULL && !got_int)
++ {
++ ff_stack_T *sptr;
++
++ // is the last starting directory in the stop list?
++ if (ff_path_in_stoplist(search_ctx->ffsc_start_dir,
++ (int)(path_end - search_ctx->ffsc_start_dir),
++ search_ctx->ffsc_stopdirs_v) == TRUE)
++ break;
++
++ // cut of last dir
++ while (path_end > search_ctx->ffsc_start_dir
++ && vim_ispathsep(*path_end))
++ path_end--;
++ while (path_end > search_ctx->ffsc_start_dir
++ && !vim_ispathsep(path_end[-1]))
++ path_end--;
++ *path_end = 0;
++ path_end--;
++
++ if (*search_ctx->ffsc_start_dir == 0)
++ break;
++
++ if (STRLEN(search_ctx->ffsc_start_dir) + 1
++ + STRLEN(search_ctx->ffsc_fix_path) < MAXPATHL)
++ {
++ STRCPY(file_path, search_ctx->ffsc_start_dir);
++ add_pathsep(file_path);
++ STRCAT(file_path, search_ctx->ffsc_fix_path);
++ }
++ else
++ goto fail;
++
++ // create a new stack entry
++ sptr = ff_create_stack_element(file_path,
++ search_ctx->ffsc_wc_path, search_ctx->ffsc_level, 0);
++ if (sptr == NULL)
++ break;
++ ff_push(search_ctx, sptr);
++ }
++ else
++ break;
++ }
++ #endif
++
++ fail:
++ vim_free(file_path);
++ return NULL;
++ }
++
++ /*
++ * Free the list of lists of visited files and directories
++ * Can handle it if the passed search_context is NULL;
++ */
++ void
++ vim_findfile_free_visited(void *search_ctx_arg)
++ {
++ ff_search_ctx_T *search_ctx;
++
++ if (search_ctx_arg == NULL)
++ return;
++
++ search_ctx = (ff_search_ctx_T *)search_ctx_arg;
++ vim_findfile_free_visited_list(&search_ctx->ffsc_visited_lists_list);
++ vim_findfile_free_visited_list(&search_ctx->ffsc_dir_visited_lists_list);
++ }
++
++ static void
++ vim_findfile_free_visited_list(ff_visited_list_hdr_T **list_headp)
++ {
++ ff_visited_list_hdr_T *vp;
++
++ while (*list_headp != NULL)
++ {
++ vp = (*list_headp)->ffvl_next;
++ ff_free_visited_list((*list_headp)->ffvl_visited_list);
++
++ vim_free((*list_headp)->ffvl_filename);
++ vim_free(*list_headp);
++ *list_headp = vp;
++ }
++ *list_headp = NULL;
++ }
++
++ static void
++ ff_free_visited_list(ff_visited_T *vl)
++ {
++ ff_visited_T *vp;
++
++ while (vl != NULL)
++ {
++ vp = vl->ffv_next;
++ #ifdef FEAT_PATH_EXTRA
++ vim_free(vl->ffv_wc_path);
++ #endif
++ vim_free(vl);
++ vl = vp;
++ }
++ vl = NULL;
++ }
++
++ /*
++ * Returns the already visited list for the given filename. If none is found it
++ * allocates a new one.
++ */
++ static ff_visited_list_hdr_T*
++ ff_get_visited_list(
++ char_u *filename,
++ ff_visited_list_hdr_T **list_headp)
++ {
++ ff_visited_list_hdr_T *retptr = NULL;
++
++ // check if a visited list for the given filename exists
++ if (*list_headp != NULL)
++ {
++ retptr = *list_headp;
++ while (retptr != NULL)
++ {
++ if (fnamecmp(filename, retptr->ffvl_filename) == 0)
++ {
++ #ifdef FF_VERBOSE
++ if (p_verbose >= 5)
++ {
++ verbose_enter_scroll();
++ smsg("ff_get_visited_list: FOUND list for %s",
++ filename);
++ // don't overwrite this either
++ msg_puts("\n");
++ verbose_leave_scroll();
++ }
++ #endif
++ return retptr;
++ }
++ retptr = retptr->ffvl_next;
++ }
++ }
++
++ #ifdef FF_VERBOSE
++ if (p_verbose >= 5)
++ {
++ verbose_enter_scroll();
++ smsg("ff_get_visited_list: new list for %s", filename);
++ // don't overwrite this either
++ msg_puts("\n");
++ verbose_leave_scroll();
++ }
++ #endif
++
++ /*
++ * if we reach this we didn't find a list and we have to allocate new list
++ */
++ retptr = (ff_visited_list_hdr_T*)alloc((unsigned)sizeof(*retptr));
++ if (retptr == NULL)
++ return NULL;
++
++ retptr->ffvl_visited_list = NULL;
++ retptr->ffvl_filename = vim_strsave(filename);
++ if (retptr->ffvl_filename == NULL)
++ {
++ vim_free(retptr);
++ return NULL;
++ }
++ retptr->ffvl_next = *list_headp;
++ *list_headp = retptr;
++
++ return retptr;
++ }
++
++ #ifdef FEAT_PATH_EXTRA
++ /*
++ * check if two wildcard paths are equal. Returns TRUE or FALSE.
++ * They are equal if:
++ * - both paths are NULL
++ * - they have the same length
++ * - char by char comparison is OK
++ * - the only differences are in the counters behind a '**', so
++ * '**\20' is equal to '**\24'
++ */
++ static int
++ ff_wc_equal(char_u *s1, char_u *s2)
++ {
++ int i, j;
++ int c1 = NUL;
++ int c2 = NUL;
++ int prev1 = NUL;
++ int prev2 = NUL;
++
++ if (s1 == s2)
++ return TRUE;
++
++ if (s1 == NULL || s2 == NULL)
++ return FALSE;
++
++ for (i = 0, j = 0; s1[i] != NUL && s2[j] != NUL;)
++ {
++ c1 = PTR2CHAR(s1 + i);
++ c2 = PTR2CHAR(s2 + j);
++
++ if ((p_fic ? MB_TOLOWER(c1) != MB_TOLOWER(c2) : c1 != c2)
++ && (prev1 != '*' || prev2 != '*'))
++ return FALSE;
++ prev2 = prev1;
++ prev1 = c1;
++
++ i += MB_PTR2LEN(s1 + i);
++ j += MB_PTR2LEN(s2 + j);
++ }
++ return s1[i] == s2[j];
++ }
++ #endif
++
++ /*
++ * maintains the list of already visited files and dirs
++ * returns FAIL if the given file/dir is already in the list
++ * returns OK if it is newly added
++ *
++ * TODO: What to do on memory allocation problems?
++ * -> return TRUE - Better the file is found several times instead of
++ * never.
++ */
++ static int
++ ff_check_visited(
++ ff_visited_T **visited_list,
++ char_u *fname
++ #ifdef FEAT_PATH_EXTRA
++ , char_u *wc_path
++ #endif
++ )
++ {
++ ff_visited_T *vp;
++ #ifdef UNIX
++ stat_T st;
++ int url = FALSE;
++ #endif
++
++ // For an URL we only compare the name, otherwise we compare the
++ // device/inode (unix) or the full path name (not Unix).
++ if (path_with_url(fname))
++ {
++ vim_strncpy(ff_expand_buffer, fname, MAXPATHL - 1);
++ #ifdef UNIX
++ url = TRUE;
++ #endif
++ }
++ else
++ {
++ ff_expand_buffer[0] = NUL;
++ #ifdef UNIX
++ if (mch_stat((char *)fname, &st) < 0)
++ #else
++ if (vim_FullName(fname, ff_expand_buffer, MAXPATHL, TRUE) == FAIL)
++ #endif
++ return FAIL;
++ }
++
++ // check against list of already visited files
++ for (vp = *visited_list; vp != NULL; vp = vp->ffv_next)
++ {
++ if (
++ #ifdef UNIX
++ !url ? (vp->ffv_dev_valid && vp->ffv_dev == st.st_dev
++ && vp->ffv_ino == st.st_ino)
++ :
++ #endif
++ fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0
++ )
++ {
++ #ifdef FEAT_PATH_EXTRA
++ // are the wildcard parts equal
++ if (ff_wc_equal(vp->ffv_wc_path, wc_path) == TRUE)
++ #endif
++ // already visited
++ return FAIL;
++ }
++ }
++
++ /*
++ * New file/dir. Add it to the list of visited files/dirs.
++ */
++ vp = (ff_visited_T *)alloc((unsigned)(sizeof(ff_visited_T)
++ + STRLEN(ff_expand_buffer)));
++
++ if (vp != NULL)
++ {
++ #ifdef UNIX
++ if (!url)
++ {
++ vp->ffv_dev_valid = TRUE;
++ vp->ffv_ino = st.st_ino;
++ vp->ffv_dev = st.st_dev;
++ vp->ffv_fname[0] = NUL;
++ }
++ else
++ {
++ vp->ffv_dev_valid = FALSE;
++ #endif
++ STRCPY(vp->ffv_fname, ff_expand_buffer);
++ #ifdef UNIX
++ }
++ #endif
++ #ifdef FEAT_PATH_EXTRA
++ if (wc_path != NULL)
++ vp->ffv_wc_path = vim_strsave(wc_path);
++ else
++ vp->ffv_wc_path = NULL;
++ #endif
++
++ vp->ffv_next = *visited_list;
++ *visited_list = vp;
++ }
++
++ return OK;
++ }
++
++ /*
++ * create stack element from given path pieces
++ */
++ static ff_stack_T *
++ ff_create_stack_element(
++ char_u *fix_part,
++ #ifdef FEAT_PATH_EXTRA
++ char_u *wc_part,
++ #endif
++ int level,
++ int star_star_empty)
++ {
++ ff_stack_T *new;
++
++ new = (ff_stack_T *)alloc((unsigned)sizeof(ff_stack_T));
++ if (new == NULL)
++ return NULL;
++
++ new->ffs_prev = NULL;
++ new->ffs_filearray = NULL;
++ new->ffs_filearray_size = 0;
++ new->ffs_filearray_cur = 0;
++ new->ffs_stage = 0;
++ new->ffs_level = level;
++ new->ffs_star_star_empty = star_star_empty;
++
++ // the following saves NULL pointer checks in vim_findfile
++ if (fix_part == NULL)
++ fix_part = (char_u *)"";
++ new->ffs_fix_path = vim_strsave(fix_part);
++
++ #ifdef FEAT_PATH_EXTRA
++ if (wc_part == NULL)
++ wc_part = (char_u *)"";
++ new->ffs_wc_path = vim_strsave(wc_part);
++ #endif
++
++ if (new->ffs_fix_path == NULL
++ #ifdef FEAT_PATH_EXTRA
++ || new->ffs_wc_path == NULL
++ #endif
++ )
++ {
++ ff_free_stack_element(new);
++ new = NULL;
++ }
++
++ return new;
++ }
++
++ /*
++ * Push a dir on the directory stack.
++ */
++ static void
++ ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr)
++ {
++ // check for NULL pointer, not to return an error to the user, but
++ // to prevent a crash
++ if (stack_ptr != NULL)
++ {
++ stack_ptr->ffs_prev = search_ctx->ffsc_stack_ptr;
++ search_ctx->ffsc_stack_ptr = stack_ptr;
++ }
++ }
++
++ /*
++ * Pop a dir from the directory stack.
++ * Returns NULL if stack is empty.
++ */
++ static ff_stack_T *
++ ff_pop(ff_search_ctx_T *search_ctx)
++ {
++ ff_stack_T *sptr;
++
++ sptr = search_ctx->ffsc_stack_ptr;
++ if (search_ctx->ffsc_stack_ptr != NULL)
++ search_ctx->ffsc_stack_ptr = search_ctx->ffsc_stack_ptr->ffs_prev;
++
++ return sptr;
++ }
++
++ /*
++ * free the given stack element
++ */
++ static void
++ ff_free_stack_element(ff_stack_T *stack_ptr)
++ {
++ // vim_free handles possible NULL pointers
++ vim_free(stack_ptr->ffs_fix_path);
++ #ifdef FEAT_PATH_EXTRA
++ vim_free(stack_ptr->ffs_wc_path);
++ #endif
++
++ if (stack_ptr->ffs_filearray != NULL)
++ FreeWild(stack_ptr->ffs_filearray_size, stack_ptr->ffs_filearray);
++
++ vim_free(stack_ptr);
++ }
++
++ /*
++ * Clear the search context, but NOT the visited list.
++ */
++ static void
++ ff_clear(ff_search_ctx_T *search_ctx)
++ {
++ ff_stack_T *sptr;
++
++ // clear up stack
++ while ((sptr = ff_pop(search_ctx)) != NULL)
++ ff_free_stack_element(sptr);
++
++ vim_free(search_ctx->ffsc_file_to_search);
++ vim_free(search_ctx->ffsc_start_dir);
++ vim_free(search_ctx->ffsc_fix_path);
++ #ifdef FEAT_PATH_EXTRA
++ vim_free(search_ctx->ffsc_wc_path);
++ #endif
++
++ #ifdef FEAT_PATH_EXTRA
++ if (search_ctx->ffsc_stopdirs_v != NULL)
++ {
++ int i = 0;
++
++ while (search_ctx->ffsc_stopdirs_v[i] != NULL)
++ {
++ vim_free(search_ctx->ffsc_stopdirs_v[i]);
++ i++;
++ }
++ vim_free(search_ctx->ffsc_stopdirs_v);
++ }
++ search_ctx->ffsc_stopdirs_v = NULL;
++ #endif
++
++ // reset everything
++ search_ctx->ffsc_file_to_search = NULL;
++ search_ctx->ffsc_start_dir = NULL;
++ search_ctx->ffsc_fix_path = NULL;
++ #ifdef FEAT_PATH_EXTRA
++ search_ctx->ffsc_wc_path = NULL;
++ search_ctx->ffsc_level = 0;
++ #endif
++ }
++
++ #ifdef FEAT_PATH_EXTRA
++ /*
++ * check if the given path is in the stopdirs
++ * returns TRUE if yes else FALSE
++ */
++ static int
++ ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
++ {
++ int i = 0;
++
++ // eat up trailing path separators, except the first
++ while (path_len > 1 && vim_ispathsep(path[path_len - 1]))
++ path_len--;
++
++ // if no path consider it as match
++ if (path_len == 0)
++ return TRUE;
++
++ for (i = 0; stopdirs_v[i] != NULL; i++)
++ {
++ if ((int)STRLEN(stopdirs_v[i]) > path_len)
++ {
++ // match for parent directory. So '/home' also matches
++ // '/home/rks'. Check for PATHSEP in stopdirs_v[i], else
++ // '/home/r' would also match '/home/rks'
++ if (fnamencmp(stopdirs_v[i], path, path_len) == 0
++ && vim_ispathsep(stopdirs_v[i][path_len]))
++ return TRUE;
++ }
++ else
++ {
++ if (fnamecmp(stopdirs_v[i], path) == 0)
++ return TRUE;
++ }
++ }
++ return FALSE;
++ }
++ #endif
++
++ #if defined(FEAT_SEARCHPATH) || defined(PROTO)
++ /*
++ * Find the file name "ptr[len]" in the path. Also finds directory names.
++ *
++ * On the first call set the parameter 'first' to TRUE to initialize
++ * the search. For repeating calls to FALSE.
++ *
++ * Repeating calls will return other files called 'ptr[len]' from the path.
++ *
++ * Only on the first call 'ptr' and 'len' are used. For repeating calls they
++ * don't need valid values.
++ *
++ * If nothing found on the first call the option FNAME_MESS will issue the
++ * message:
++ * 'Can't find file "<file>" in path'
++ * On repeating calls:
++ * 'No more file "<file>" found in path'
++ *
++ * options:
++ * FNAME_MESS give error message when not found
++ *
++ * Uses NameBuff[]!
++ *
++ * Returns an allocated string for the file name. NULL for error.
++ *
++ */
++ char_u *
++ find_file_in_path(
++ char_u *ptr, // file name
++ int len, // length of file name
++ int options,
++ int first, // use count'th matching file name
++ char_u *rel_fname) // file name searching relative to
++ {
++ return find_file_in_path_option(ptr, len, options, first,
++ *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path,
++ FINDFILE_BOTH, rel_fname, curbuf->b_p_sua);
++ }
++
++ static char_u *ff_file_to_find = NULL;
++ static void *fdip_search_ctx = NULL;
++
++ # if defined(EXITFREE) || defined(PROTO)
++ void
++ free_findfile(void)
++ {
++ vim_free(ff_file_to_find);
++ vim_findfile_cleanup(fdip_search_ctx);
++ vim_free(ff_expand_buffer);
++ }
++ # endif
++
++ /*
++ * Find the directory name "ptr[len]" in the path.
++ *
++ * options:
++ * FNAME_MESS give error message when not found
++ * FNAME_UNESC unescape backslashes.
++ *
++ * Uses NameBuff[]!
++ *
++ * Returns an allocated string for the file name. NULL for error.
++ */
++ char_u *
++ find_directory_in_path(
++ char_u *ptr, // file name
++ int len, // length of file name
++ int options,
++ char_u *rel_fname) // file name searching relative to
++ {
++ return find_file_in_path_option(ptr, len, options, TRUE, p_cdpath,
++ FINDFILE_DIR, rel_fname, (char_u *)"");
++ }
++
++ char_u *
++ find_file_in_path_option(
++ char_u *ptr, // file name
++ int len, // length of file name
++ int options,
++ int first, // use count'th matching file name
++ char_u *path_option, // p_path or p_cdpath
++ int find_what, // FINDFILE_FILE, _DIR or _BOTH
++ char_u *rel_fname, // file name we are looking relative to.
++ char_u *suffixes) // list of suffixes, 'suffixesadd' option
++ {
++ static char_u *dir;
++ static int did_findfile_init = FALSE;
++ char_u save_char;
++ char_u *file_name = NULL;
++ char_u *buf = NULL;
++ int rel_to_curdir;
++ # ifdef AMIGA
++ struct Process *proc = (struct Process *)FindTask(0L);
++ APTR save_winptr = proc->pr_WindowPtr;
++
++ // Avoid a requester here for a volume that doesn't exist.
++ proc->pr_WindowPtr = (APTR)-1L;
++ # endif
++
++ if (first == TRUE)
++ {
++ // copy file name into NameBuff, expanding environment variables
++ save_char = ptr[len];
++ ptr[len] = NUL;
++ expand_env_esc(ptr, NameBuff, MAXPATHL, FALSE, TRUE, NULL);
++ ptr[len] = save_char;
++
++ vim_free(ff_file_to_find);
++ ff_file_to_find = vim_strsave(NameBuff);
++ if (ff_file_to_find == NULL) // out of memory
++ {
++ file_name = NULL;
++ goto theend;
++ }
++ if (options & FNAME_UNESC)
++ {
++ // Change all "\ " to " ".
++ for (ptr = ff_file_to_find; *ptr != NUL; ++ptr)
++ if (ptr[0] == '\\' && ptr[1] == ' ')
++ mch_memmove(ptr, ptr + 1, STRLEN(ptr));
++ }
++ }
++
++ rel_to_curdir = (ff_file_to_find[0] == '.'
++ && (ff_file_to_find[1] == NUL
++ || vim_ispathsep(ff_file_to_find[1])
++ || (ff_file_to_find[1] == '.'
++ && (ff_file_to_find[2] == NUL
++ || vim_ispathsep(ff_file_to_find[2])))));
++ if (vim_isAbsName(ff_file_to_find)
++ // "..", "../path", "." and "./path": don't use the path_option
++ || rel_to_curdir
++ # if defined(MSWIN)
++ // handle "\tmp" as absolute path
++ || vim_ispathsep(ff_file_to_find[0])
++ // handle "c:name" as absolute path
++ || (ff_file_to_find[0] != NUL && ff_file_to_find[1] == ':')
++ # endif
++ # ifdef AMIGA
++ // handle ":tmp" as absolute path
++ || ff_file_to_find[0] == ':'
++ # endif
++ )
++ {
++ /*
++ * Absolute path, no need to use "path_option".
++ * If this is not a first call, return NULL. We already returned a
++ * filename on the first call.
++ */
++ if (first == TRUE)
++ {
++ int l;
++ int run;
++
++ if (path_with_url(ff_file_to_find))
++ {
++ file_name = vim_strsave(ff_file_to_find);
++ goto theend;
++ }
++
++ // When FNAME_REL flag given first use the directory of the file.
++ // Otherwise or when this fails use the current directory.
++ for (run = 1; run <= 2; ++run)
++ {
++ l = (int)STRLEN(ff_file_to_find);
++ if (run == 1
++ && rel_to_curdir
++ && (options & FNAME_REL)
++ && rel_fname != NULL
++ && STRLEN(rel_fname) + l < MAXPATHL)
++ {
++ STRCPY(NameBuff, rel_fname);
++ STRCPY(gettail(NameBuff), ff_file_to_find);
++ l = (int)STRLEN(NameBuff);
++ }
++ else
++ {
++ STRCPY(NameBuff, ff_file_to_find);
++ run = 2;
++ }
++
++ // When the file doesn't exist, try adding parts of
++ // 'suffixesadd'.
++ buf = suffixes;
++ for (;;)
++ {
++ if (mch_getperm(NameBuff) >= 0
++ && (find_what == FINDFILE_BOTH
++ || ((find_what == FINDFILE_DIR)
++ == mch_isdir(NameBuff))))
++ {
++ file_name = vim_strsave(NameBuff);
++ goto theend;
++ }
++ if (*buf == NUL)
++ break;
++ copy_option_part(&buf, NameBuff + l, MAXPATHL - l, ",");
++ }
++ }
++ }
++ }
++ else
++ {
++ /*
++ * Loop over all paths in the 'path' or 'cdpath' option.
++ * When "first" is set, first setup to the start of the option.
++ * Otherwise continue to find the next match.
++ */
++ if (first == TRUE)
++ {
++ // vim_findfile_free_visited can handle a possible NULL pointer
++ vim_findfile_free_visited(fdip_search_ctx);
++ dir = path_option;
++ did_findfile_init = FALSE;
++ }
++
++ for (;;)
++ {
++ if (did_findfile_init)
++ {
++ file_name = vim_findfile(fdip_search_ctx);
++ if (file_name != NULL)
++ break;
++
++ did_findfile_init = FALSE;
++ }
++ else
++ {
++ char_u *r_ptr;
++
++ if (dir == NULL || *dir == NUL)
++ {
++ // We searched all paths of the option, now we can
++ // free the search context.
++ vim_findfile_cleanup(fdip_search_ctx);
++ fdip_search_ctx = NULL;
++ break;
++ }
++
++ if ((buf = alloc((int)(MAXPATHL))) == NULL)
++ break;
++
++ // copy next path
++ buf[0] = 0;
++ copy_option_part(&dir, buf, MAXPATHL, " ,");
++
++ # ifdef FEAT_PATH_EXTRA
++ // get the stopdir string
++ r_ptr = vim_findfile_stopdir(buf);
++ # else
++ r_ptr = NULL;
++ # endif
++ fdip_search_ctx = vim_findfile_init(buf, ff_file_to_find,
++ r_ptr, 100, FALSE, find_what,
++ fdip_search_ctx, FALSE, rel_fname);
++ if (fdip_search_ctx != NULL)
++ did_findfile_init = TRUE;
++ vim_free(buf);
++ }
++ }
++ }
++ if (file_name == NULL && (options & FNAME_MESS))
++ {
++ if (first == TRUE)
++ {
++ if (find_what == FINDFILE_DIR)
++ semsg(_("E344: Can't find directory \"%s\" in cdpath"),
++ ff_file_to_find);
++ else
++ semsg(_("E345: Can't find file \"%s\" in path"),
++ ff_file_to_find);
++ }
++ else
++ {
++ if (find_what == FINDFILE_DIR)
++ semsg(_("E346: No more directory \"%s\" found in cdpath"),
++ ff_file_to_find);
++ else
++ semsg(_("E347: No more file \"%s\" found in path"),
++ ff_file_to_find);
++ }
++ }
++
++ theend:
++ # ifdef AMIGA
++ proc->pr_WindowPtr = save_winptr;
++ # endif
++ return file_name;
++ }
++
++ /*
++ * Get the file name at the cursor.
++ * If Visual mode is active, use the selected text if it's in one line.
++ * Returns the name in allocated memory, NULL for failure.
++ */
++ char_u *
++ grab_file_name(long count, linenr_T *file_lnum)
++ {
++ int options = FNAME_MESS|FNAME_EXP|FNAME_REL|FNAME_UNESC;
++
++ if (VIsual_active)
++ {
++ int len;
++ char_u *ptr;
++
++ if (get_visual_text(NULL, &ptr, &len) == FAIL)
++ return NULL;
++ return find_file_name_in_path(ptr, len, options,
++ count, curbuf->b_ffname);
++ }
++ return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
++ }
++
++ /*
++ * Return the file name under or after the cursor.
++ *
++ * The 'path' option is searched if the file name is not absolute.
++ * The string returned has been alloc'ed and should be freed by the caller.
++ * NULL is returned if the file name or file is not found.
++ *
++ * options:
++ * FNAME_MESS give error messages
++ * FNAME_EXP expand to path
++ * FNAME_HYP check for hypertext link
++ * FNAME_INCL apply "includeexpr"
++ */
++ char_u *
++ file_name_at_cursor(int options, long count, linenr_T *file_lnum)
++ {
++ return file_name_in_line(ml_get_curline(),
++ curwin->w_cursor.col, options, count, curbuf->b_ffname,
++ file_lnum);
++ }
++
++ /*
++ * Return the name of the file under or after ptr[col].
++ * Otherwise like file_name_at_cursor().
++ */
++ char_u *
++ file_name_in_line(
++ char_u *line,
++ int col,
++ int options,
++ long count,
++ char_u *rel_fname, // file we are searching relative to
++ linenr_T *file_lnum) // line number after the file name
++ {
++ char_u *ptr;
++ int len;
++ int in_type = TRUE;
++ int is_url = FALSE;
++
++ /*
++ * search forward for what could be the start of a file name
++ */
++ ptr = line + col;
++ while (*ptr != NUL && !vim_isfilec(*ptr))
++ MB_PTR_ADV(ptr);
++ if (*ptr == NUL) // nothing found
++ {
++ if (options & FNAME_MESS)
++ emsg(_("E446: No file name under cursor"));
++ return NULL;
++ }
++
++ /*
++ * Search backward for first char of the file name.
++ * Go one char back to ":" before "//" even when ':' is not in 'isfname'.
++ */
++ while (ptr > line)
++ {
++ if (has_mbyte && (len = (*mb_head_off)(line, ptr - 1)) > 0)
++ ptr -= len + 1;
++ else if (vim_isfilec(ptr[-1])
++ || ((options & FNAME_HYP) && path_is_url(ptr - 1)))
++ --ptr;
++ else
++ break;
++ }
++
++ /*
++ * Search forward for the last char of the file name.
++ * Also allow "://" when ':' is not in 'isfname'.
++ */
++ len = 0;
++ while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
++ || ((options & FNAME_HYP) && path_is_url(ptr + len))
++ || (is_url && vim_strchr((char_u *)"?&=", ptr[len]) != NULL))
++ {
++ // After type:// we also include ?, & and = as valid characters, so that
++ // http://google.com?q=this&that=ok works.
++ if ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z'))
++ {
++ if (in_type && path_is_url(ptr + len + 1))
++ is_url = TRUE;
++ }
++ else
++ in_type = FALSE;
++
++ if (ptr[len] == '\\')
++ // Skip over the "\" in "\ ".
++ ++len;
++ if (has_mbyte)
++ len += (*mb_ptr2len)(ptr + len);
++ else
++ ++len;
++ }
++
++ /*
++ * If there is trailing punctuation, remove it.
++ * But don't remove "..", could be a directory name.
++ */
++ if (len > 2 && vim_strchr((char_u *)".,:;!", ptr[len - 1]) != NULL
++ && ptr[len - 2] != '.')
++ --len;
++
++ if (file_lnum != NULL)
++ {
++ char_u *p;
++
++ // Get the number after the file name and a separator character
++ p = ptr + len;
++ p = skipwhite(p);
++ if (*p != NUL)
++ {
++ if (!isdigit(*p))
++ ++p; // skip the separator
++ p = skipwhite(p);
++ if (isdigit(*p))
++ *file_lnum = (int)getdigits(&p);
++ }
++ }
++
++ return find_file_name_in_path(ptr, len, options, count, rel_fname);
++ }
++
++ # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
++ static char_u *
++ eval_includeexpr(char_u *ptr, int len)
++ {
++ char_u *res;
++
++ set_vim_var_string(VV_FNAME, ptr, len);
++ res = eval_to_string_safe(curbuf->b_p_inex, NULL,
++ was_set_insecurely((char_u *)"includeexpr", OPT_LOCAL));
++ set_vim_var_string(VV_FNAME, NULL, 0);
++ return res;
++ }
++ # endif
++
++ /*
++ * Return the name of the file ptr[len] in 'path'.
++ * Otherwise like file_name_at_cursor().
++ */
++ char_u *
++ find_file_name_in_path(
++ char_u *ptr,
++ int len,
++ int options,
++ long count,
++ char_u *rel_fname) // file we are searching relative to
++ {
++ char_u *file_name;
++ int c;
++ # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
++ char_u *tofree = NULL;
++
++ if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL)
++ {
++ tofree = eval_includeexpr(ptr, len);
++ if (tofree != NULL)
++ {
++ ptr = tofree;
++ len = (int)STRLEN(ptr);
++ }
++ }
++ # endif
++
++ if (options & FNAME_EXP)
++ {
++ file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
++ TRUE, rel_fname);
++
++ # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
++ /*
++ * If the file could not be found in a normal way, try applying
++ * 'includeexpr' (unless done already).
++ */
++ if (file_name == NULL
++ && !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL)
++ {
++ tofree = eval_includeexpr(ptr, len);
++ if (tofree != NULL)
++ {
++ ptr = tofree;
++ len = (int)STRLEN(ptr);
++ file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
++ TRUE, rel_fname);
++ }
++ }
++ # endif
++ if (file_name == NULL && (options & FNAME_MESS))
++ {
++ c = ptr[len];
++ ptr[len] = NUL;
++ semsg(_("E447: Can't find file \"%s\" in path"), ptr);
++ ptr[len] = c;
++ }
++
++ // Repeat finding the file "count" times. This matters when it
++ // appears several times in the path.
++ while (file_name != NULL && --count > 0)
++ {
++ vim_free(file_name);
++ file_name = find_file_in_path(ptr, len, options, FALSE, rel_fname);
++ }
++ }
++ else
++ file_name = vim_strnsave(ptr, len);
++
++ # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
++ vim_free(tofree);
++ # endif
++
++ return file_name;
++ }
++
++ /*
++ * Return the end of the directory name, on the first path
++ * separator:
++ * "/path/file", "/path/dir/", "/path//dir", "/file"
++ * ^ ^ ^ ^
++ */
++ static char_u *
++ gettail_dir(char_u *fname)
++ {
++ char_u *dir_end = fname;
++ char_u *next_dir_end = fname;
++ int look_for_sep = TRUE;
++ char_u *p;
++
++ for (p = fname; *p != NUL; )
++ {
++ if (vim_ispathsep(*p))
++ {
++ if (look_for_sep)
++ {
++ next_dir_end = p;
++ look_for_sep = FALSE;
++ }
++ }
++ else
++ {
++ if (!look_for_sep)
++ dir_end = next_dir_end;
++ look_for_sep = TRUE;
++ }
++ MB_PTR_ADV(p);
++ }
++ return dir_end;
++ }
++
++ /*
++ * return TRUE if 'c' is a path list separator.
++ */
++ int
++ vim_ispathlistsep(int c)
++ {
++ # ifdef UNIX
++ return (c == ':');
++ # else
++ return (c == ';'); // might not be right for every system...
++ # endif
++ }
++
++ /*
++ * Moves "*psep" back to the previous path separator in "path".
++ * Returns FAIL is "*psep" ends up at the beginning of "path".
++ */
++ static int
++ find_previous_pathsep(char_u *path, char_u **psep)
++ {
++ // skip the current separator
++ if (*psep > path && vim_ispathsep(**psep))
++ --*psep;
++
++ // find the previous separator
++ while (*psep > path)
++ {
++ if (vim_ispathsep(**psep))
++ return OK;
++ MB_PTR_BACK(path, *psep);
++ }
++
++ return FAIL;
++ }
++
++ /*
++ * Returns TRUE if "maybe_unique" is unique wrt other_paths in "gap".
++ * "maybe_unique" is the end portion of "((char_u **)gap->ga_data)[i]".
++ */
++ static int
++ is_unique(char_u *maybe_unique, garray_T *gap, int i)
++ {
++ int j;
++ int candidate_len;
++ int other_path_len;
++ char_u **other_paths = (char_u **)gap->ga_data;
++ char_u *rival;
++
++ for (j = 0; j < gap->ga_len; j++)
++ {
++ if (j == i)
++ continue; // don't compare it with itself
++
++ candidate_len = (int)STRLEN(maybe_unique);
++ other_path_len = (int)STRLEN(other_paths[j]);
++ if (other_path_len < candidate_len)
++ continue; // it's different when it's shorter
++
++ rival = other_paths[j] + other_path_len - candidate_len;
++ if (fnamecmp(maybe_unique, rival) == 0
++ && (rival == other_paths[j] || vim_ispathsep(*(rival - 1))))
++ return FALSE; // match
++ }
++
++ return TRUE; // no match found
++ }
++
++ /*
++ * Split the 'path' option into an array of strings in garray_T. Relative
++ * paths are expanded to their equivalent fullpath. This includes the "."
++ * (relative to current buffer directory) and empty path (relative to current
++ * directory) notations.
++ *
++ * TODO: handle upward search (;) and path limiter (**N) notations by
++ * expanding each into their equivalent path(s).
++ */
++ static void
++ expand_path_option(char_u *curdir, garray_T *gap)
++ {
++ char_u *path_option = *curbuf->b_p_path == NUL
++ ? p_path : curbuf->b_p_path;
++ char_u *buf;
++ char_u *p;
++ int len;
++
++ if ((buf = alloc((int)MAXPATHL)) == NULL)
++ return;
++
++ while (*path_option != NUL)
++ {
++ copy_option_part(&path_option, buf, MAXPATHL, " ,");
++
++ if (buf[0] == '.' && (buf[1] == NUL || vim_ispathsep(buf[1])))
++ {
++ // Relative to current buffer:
++ // "/path/file" + "." -> "/path/"
++ // "/path/file" + "./subdir" -> "/path/subdir"
++ if (curbuf->b_ffname == NULL)
++ continue;
++ p = gettail(curbuf->b_ffname);
++ len = (int)(p - curbuf->b_ffname);
++ if (len + (int)STRLEN(buf) >= MAXPATHL)
++ continue;
++ if (buf[1] == NUL)
++ buf[len] = NUL;
++ else
++ STRMOVE(buf + len, buf + 2);
++ mch_memmove(buf, curbuf->b_ffname, len);
++ simplify_filename(buf);
++ }
++ else if (buf[0] == NUL)
++ // relative to current directory
++ STRCPY(buf, curdir);
++ else if (path_with_url(buf))
++ // URL can't be used here
++ continue;
++ else if (!mch_isFullName(buf))
++ {
++ // Expand relative path to their full path equivalent
++ len = (int)STRLEN(curdir);
++ if (len + (int)STRLEN(buf) + 3 > MAXPATHL)
++ continue;
++ STRMOVE(buf + len + 1, buf);
++ STRCPY(buf, curdir);
++ buf[len] = PATHSEP;
++ simplify_filename(buf);
++ }
++
++ if (ga_grow(gap, 1) == FAIL)
++ break;
++
++ # if defined(MSWIN)
++ // Avoid the path ending in a backslash, it fails when a comma is
++ // appended.
++ len = (int)STRLEN(buf);
++ if (buf[len - 1] == '\\')
++ buf[len - 1] = '/';
++ # endif
++
++ p = vim_strsave(buf);
++ if (p == NULL)
++ break;
++ ((char_u **)gap->ga_data)[gap->ga_len++] = p;
++ }
++
++ vim_free(buf);
++ }
++
++ /*
++ * Returns a pointer to the file or directory name in "fname" that matches the
++ * longest path in "ga"p, or NULL if there is no match. For example:
++ *
++ * path: /foo/bar/baz
++ * fname: /foo/bar/baz/quux.txt
++ * returns: ^this
++ */
++ static char_u *
++ get_path_cutoff(char_u *fname, garray_T *gap)
++ {
++ int i;
++ int maxlen = 0;
++ char_u **path_part = (char_u **)gap->ga_data;
++ char_u *cutoff = NULL;
++
++ for (i = 0; i < gap->ga_len; i++)
++ {
++ int j = 0;
++
++ while ((fname[j] == path_part[i][j]
++ # if defined(MSWIN)
++ || (vim_ispathsep(fname[j]) && vim_ispathsep(path_part[i][j]))
++ # endif
++ ) && fname[j] != NUL && path_part[i][j] != NUL)
++ j++;
++ if (j > maxlen)
++ {
++ maxlen = j;
++ cutoff = &fname[j];
++ }
++ }
++
++ // skip to the file or directory name
++ if (cutoff != NULL)
++ while (vim_ispathsep(*cutoff))
++ MB_PTR_ADV(cutoff);
++
++ return cutoff;
++ }
++
++ /*
++ * Sorts, removes duplicates and modifies all the fullpath names in "gap" so
++ * that they are unique with respect to each other while conserving the part
++ * that matches the pattern. Beware, this is at least O(n^2) wrt "gap->ga_len".
++ */
++ void
++ uniquefy_paths(garray_T *gap, char_u *pattern)
++ {
++ int i;
++ int len;
++ char_u **fnames = (char_u **)gap->ga_data;
++ int sort_again = FALSE;
++ char_u *pat;
++ char_u *file_pattern;
++ char_u *curdir;
++ regmatch_T regmatch;
++ garray_T path_ga;
++ char_u **in_curdir = NULL;
++ char_u *short_name;
++
++ remove_duplicates(gap);
++ ga_init2(&path_ga, (int)sizeof(char_u *), 1);
++
++ /*
++ * We need to prepend a '*' at the beginning of file_pattern so that the
++ * regex matches anywhere in the path. FIXME: is this valid for all
++ * possible patterns?
++ */
++ len = (int)STRLEN(pattern);
++ file_pattern = alloc(len + 2);
++ if (file_pattern == NULL)
++ return;
++ file_pattern[0] = '*';
++ file_pattern[1] = NUL;
++ STRCAT(file_pattern, pattern);
++ pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, TRUE);
++ vim_free(file_pattern);
++ if (pat == NULL)
++ return;
++
++ regmatch.rm_ic = TRUE; // always ignore case
++ regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
++ vim_free(pat);
++ if (regmatch.regprog == NULL)
++ return;
++
++ if ((curdir = alloc((int)(MAXPATHL))) == NULL)
++ goto theend;
++ mch_dirname(curdir, MAXPATHL);
++ expand_path_option(curdir, &path_ga);
++
++ in_curdir = (char_u **)alloc_clear(gap->ga_len * sizeof(char_u *));
++ if (in_curdir == NULL)
++ goto theend;
++
++ for (i = 0; i < gap->ga_len && !got_int; i++)
++ {
++ char_u *path = fnames[i];
++ int is_in_curdir;
++ char_u *dir_end = gettail_dir(path);
++ char_u *pathsep_p;
++ char_u *path_cutoff;
++
++ len = (int)STRLEN(path);
++ is_in_curdir = fnamencmp(curdir, path, dir_end - path) == 0
++ && curdir[dir_end - path] == NUL;
++ if (is_in_curdir)
++ in_curdir[i] = vim_strsave(path);
++
++ // Shorten the filename while maintaining its uniqueness
++ path_cutoff = get_path_cutoff(path, &path_ga);
++
++ // Don't assume all files can be reached without path when search
++ // pattern starts with star star slash, so only remove path_cutoff
++ // when possible.
++ if (pattern[0] == '*' && pattern[1] == '*'
++ && vim_ispathsep_nocolon(pattern[2])
++ && path_cutoff != NULL
++ && vim_regexec(&regmatch, path_cutoff, (colnr_T)0)
++ && is_unique(path_cutoff, gap, i))
++ {
++ sort_again = TRUE;
++ mch_memmove(path, path_cutoff, STRLEN(path_cutoff) + 1);
++ }
++ else
++ {
++ // Here all files can be reached without path, so get shortest
++ // unique path. We start at the end of the path.
++ pathsep_p = path + len - 1;
++
++ while (find_previous_pathsep(path, &pathsep_p))
++ if (vim_regexec(&regmatch, pathsep_p + 1, (colnr_T)0)
++ && is_unique(pathsep_p + 1, gap, i)
++ && path_cutoff != NULL && pathsep_p + 1 >= path_cutoff)
++ {
++ sort_again = TRUE;
++ mch_memmove(path, pathsep_p + 1, STRLEN(pathsep_p));
++ break;
++ }
++ }
++
++ if (mch_isFullName(path))
++ {
++ /*
++ * Last resort: shorten relative to curdir if possible.
++ * 'possible' means:
++ * 1. It is under the current directory.
++ * 2. The result is actually shorter than the original.
++ *
++ * Before curdir After
++ * /foo/bar/file.txt /foo/bar ./file.txt
++ * c:\foo\bar\file.txt c:\foo\bar .\file.txt
++ * /file.txt / /file.txt
++ * c:\file.txt c:\ .\file.txt
++ */
++ short_name = shorten_fname(path, curdir);
++ if (short_name != NULL && short_name > path + 1
++ # if defined(MSWIN)
++ // On windows,
++ // shorten_fname("c:\a\a.txt", "c:\a\b")
++ // returns "\a\a.txt", which is not really the short
++ // name, hence:
++ && !vim_ispathsep(*short_name)
++ # endif
++ )
++ {
++ STRCPY(path, ".");
++ add_pathsep(path);
++ STRMOVE(path + STRLEN(path), short_name);
++ }
++ }
++ ui_breakcheck();
++ }
++
++ // Shorten filenames in /in/current/directory/{filename}
++ for (i = 0; i < gap->ga_len && !got_int; i++)
++ {
++ char_u *rel_path;
++ char_u *path = in_curdir[i];
++
++ if (path == NULL)
++ continue;
++
++ // If the {filename} is not unique, change it to ./{filename}.
++ // Else reduce it to {filename}
++ short_name = shorten_fname(path, curdir);
++ if (short_name == NULL)
++ short_name = path;
++ if (is_unique(short_name, gap, i))
++ {
++ STRCPY(fnames[i], short_name);
++ continue;
++ }
++
++ rel_path = alloc((int)(STRLEN(short_name) + STRLEN(PATHSEPSTR) + 2));
++ if (rel_path == NULL)
++ goto theend;
++ STRCPY(rel_path, ".");
++ add_pathsep(rel_path);
++ STRCAT(rel_path, short_name);
++
++ vim_free(fnames[i]);
++ fnames[i] = rel_path;
++ sort_again = TRUE;
++ ui_breakcheck();
++ }
++
++ theend:
++ vim_free(curdir);
++ if (in_curdir != NULL)
++ {
++ for (i = 0; i < gap->ga_len; i++)
++ vim_free(in_curdir[i]);
++ vim_free(in_curdir);
++ }
++ ga_clear_strings(&path_ga);
++ vim_regfree(regmatch.regprog);
++
++ if (sort_again)
++ remove_duplicates(gap);
++ }
++
++ /*
++ * Calls globpath() with 'path' values for the given pattern and stores the
++ * result in "gap".
++ * Returns the total number of matches.
++ */
++ int
++ expand_in_path(
++ garray_T *gap,
++ char_u *pattern,
++ int flags) // EW_* flags
++ {
++ char_u *curdir;
++ garray_T path_ga;
++ char_u *paths = NULL;
++ int glob_flags = 0;
++
++ if ((curdir = alloc((unsigned)MAXPATHL)) == NULL)
++ return 0;
++ mch_dirname(curdir, MAXPATHL);
++
++ ga_init2(&path_ga, (int)sizeof(char_u *), 1);
++ expand_path_option(curdir, &path_ga);
++ vim_free(curdir);
++ if (path_ga.ga_len == 0)
++ return 0;
++
++ paths = ga_concat_strings(&path_ga, ",");
++ ga_clear_strings(&path_ga);
++ if (paths == NULL)
++ return 0;
++
++ if (flags & EW_ICASE)
++ glob_flags |= WILD_ICASE;
++ if (flags & EW_ADDSLASH)
++ glob_flags |= WILD_ADD_SLASH;
++ globpath(paths, pattern, gap, glob_flags);
++ vim_free(paths);
++
++ return gap->ga_len;
++ }
++
++ #endif // FEAT_SEARCHPATH
+*** ../vim-8.1.0913/src/misc1.c 2019-01-31 13:47:51.126632619 +0100
+--- src/misc1.c 2019-02-13 22:06:27.637777681 +0100
+***************
+*** 21,26 ****
+--- 21,29 ----
+ static char_u *vim_version_dir(char_u *vimdir);
+ static char_u *remove_tail(char_u *p, char_u *pend, char_u *name);
+
++ #define URL_SLASH 1 /* path_is_url() has found "://" */
++ #define URL_BACKSLASH 2 /* path_is_url() has found ":\\" */
++
+ /* All user names (for ~user completion as done by shell). */
+ #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
+ static garray_T ga_users;
+***************
+*** 5023,5065 ****
+ return p1;
+ }
+
+- #if defined(FEAT_SEARCHPATH)
+- /*
+- * Return the end of the directory name, on the first path
+- * separator:
+- * "/path/file", "/path/dir/", "/path//dir", "/file"
+- * ^ ^ ^ ^
+- */
+- static char_u *
+- gettail_dir(char_u *fname)
+- {
+- char_u *dir_end = fname;
+- char_u *next_dir_end = fname;
+- int look_for_sep = TRUE;
+- char_u *p;
+-
+- for (p = fname; *p != NUL; )
+- {
+- if (vim_ispathsep(*p))
+- {
+- if (look_for_sep)
+- {
+- next_dir_end = p;
+- look_for_sep = FALSE;
+- }
+- }
+- else
+- {
+- if (!look_for_sep)
+- dir_end = next_dir_end;
+- look_for_sep = TRUE;
+- }
+- MB_PTR_ADV(p);
+- }
+- return dir_end;
+- }
+- #endif
+-
+ /*
+ * Get pointer to tail of "fname", including path separators. Putting a NUL
+ * here leaves the directory name. Takes care of "c:/" and "//".
+--- 5026,5031 ----
+***************
+*** 5165,5185 ****
+ ;
+ }
+
+- #if defined(FEAT_SEARCHPATH) || defined(PROTO)
+- /*
+- * return TRUE if 'c' is a path list separator.
+- */
+- int
+- vim_ispathlistsep(int c)
+- {
+- #ifdef UNIX
+- return (c == ':');
+- #else
+- return (c == ';'); /* might not be right for every system... */
+- #endif
+- }
+- #endif
+-
+ /*
+ * Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
+ * It's done in-place.
+--- 5131,5136 ----
+***************
+*** 6183,6589 ****
+ }
+ #endif
+
+- #if defined(FEAT_SEARCHPATH)
+- /*
+- * Moves "*psep" back to the previous path separator in "path".
+- * Returns FAIL is "*psep" ends up at the beginning of "path".
+- */
+- static int
+- find_previous_pathsep(char_u *path, char_u **psep)
+- {
+- /* skip the current separator */
+- if (*psep > path && vim_ispathsep(**psep))
+- --*psep;
+-
+- /* find the previous separator */
+- while (*psep > path)
+- {
+- if (vim_ispathsep(**psep))
+- return OK;
+- MB_PTR_BACK(path, *psep);
+- }
+-
+- return FAIL;
+- }
+-
+- /*
+- * Returns TRUE if "maybe_unique" is unique wrt other_paths in "gap".
+- * "maybe_unique" is the end portion of "((char_u **)gap->ga_data)[i]".
+- */
+- static int
+- is_unique(char_u *maybe_unique, garray_T *gap, int i)
+- {
+- int j;
+- int candidate_len;
+- int other_path_len;
+- char_u **other_paths = (char_u **)gap->ga_data;
+- char_u *rival;
+-
+- for (j = 0; j < gap->ga_len; j++)
+- {
+- if (j == i)
+- continue; /* don't compare it with itself */
+-
+- candidate_len = (int)STRLEN(maybe_unique);
+- other_path_len = (int)STRLEN(other_paths[j]);
+- if (other_path_len < candidate_len)
+- continue; /* it's different when it's shorter */
+-
+- rival = other_paths[j] + other_path_len - candidate_len;
+- if (fnamecmp(maybe_unique, rival) == 0
+- && (rival == other_paths[j] || vim_ispathsep(*(rival - 1))))
+- return FALSE; /* match */
+- }
+-
+- return TRUE; /* no match found */
+- }
+-
+- /*
+- * Split the 'path' option into an array of strings in garray_T. Relative
+- * paths are expanded to their equivalent fullpath. This includes the "."
+- * (relative to current buffer directory) and empty path (relative to current
+- * directory) notations.
+- *
+- * TODO: handle upward search (;) and path limiter (**N) notations by
+- * expanding each into their equivalent path(s).
+- */
+- static void
+- expand_path_option(char_u *curdir, garray_T *gap)
+- {
+- char_u *path_option = *curbuf->b_p_path == NUL
+- ? p_path : curbuf->b_p_path;
+- char_u *buf;
+- char_u *p;
+- int len;
+-
+- if ((buf = alloc((int)MAXPATHL)) == NULL)
+- return;
+-
+- while (*path_option != NUL)
+- {
+- copy_option_part(&path_option, buf, MAXPATHL, " ,");
+-
+- if (buf[0] == '.' && (buf[1] == NUL || vim_ispathsep(buf[1])))
+- {
+- /* Relative to current buffer:
+- * "/path/file" + "." -> "/path/"
+- * "/path/file" + "./subdir" -> "/path/subdir" */
+- if (curbuf->b_ffname == NULL)
+- continue;
+- p = gettail(curbuf->b_ffname);
+- len = (int)(p - curbuf->b_ffname);
+- if (len + (int)STRLEN(buf) >= MAXPATHL)
+- continue;
+- if (buf[1] == NUL)
+- buf[len] = NUL;
+- else
+- STRMOVE(buf + len, buf + 2);
+- mch_memmove(buf, curbuf->b_ffname, len);
+- simplify_filename(buf);
+- }
+- else if (buf[0] == NUL)
+- /* relative to current directory */
+- STRCPY(buf, curdir);
+- else if (path_with_url(buf))
+- /* URL can't be used here */
+- continue;
+- else if (!mch_isFullName(buf))
+- {
+- /* Expand relative path to their full path equivalent */
+- len = (int)STRLEN(curdir);
+- if (len + (int)STRLEN(buf) + 3 > MAXPATHL)
+- continue;
+- STRMOVE(buf + len + 1, buf);
+- STRCPY(buf, curdir);
+- buf[len] = PATHSEP;
+- simplify_filename(buf);
+- }
+-
+- if (ga_grow(gap, 1) == FAIL)
+- break;
+-
+- # if defined(MSWIN)
+- /* Avoid the path ending in a backslash, it fails when a comma is
+- * appended. */
+- len = (int)STRLEN(buf);
+- if (buf[len - 1] == '\\')
+- buf[len - 1] = '/';
+- # endif
+-
+- p = vim_strsave(buf);
+- if (p == NULL)
+- break;
+- ((char_u **)gap->ga_data)[gap->ga_len++] = p;
+- }
+-
+- vim_free(buf);
+- }
+-
+- /*
+- * Returns a pointer to the file or directory name in "fname" that matches the
+- * longest path in "ga"p, or NULL if there is no match. For example:
+- *
+- * path: /foo/bar/baz
+- * fname: /foo/bar/baz/quux.txt
+- * returns: ^this
+- */
+- static char_u *
+- get_path_cutoff(char_u *fname, garray_T *gap)
+- {
+- int i;
+- int maxlen = 0;
+- char_u **path_part = (char_u **)gap->ga_data;
+- char_u *cutoff = NULL;
+-
+- for (i = 0; i < gap->ga_len; i++)
+- {
+- int j = 0;
+-
+- while ((fname[j] == path_part[i][j]
+- # if defined(MSWIN)
+- || (vim_ispathsep(fname[j]) && vim_ispathsep(path_part[i][j]))
+- #endif
+- ) && fname[j] != NUL && path_part[i][j] != NUL)
+- j++;
+- if (j > maxlen)
+- {
+- maxlen = j;
+- cutoff = &fname[j];
+- }
+- }
+-
+- /* skip to the file or directory name */
+- if (cutoff != NULL)
+- while (vim_ispathsep(*cutoff))
+- MB_PTR_ADV(cutoff);
+-
+- return cutoff;
+- }
+-
+- /*
+- * Sorts, removes duplicates and modifies all the fullpath names in "gap" so
+- * that they are unique with respect to each other while conserving the part
+- * that matches the pattern. Beware, this is at least O(n^2) wrt "gap->ga_len".
+- */
+- static void
+- uniquefy_paths(garray_T *gap, char_u *pattern)
+- {
+- int i;
+- int len;
+- char_u **fnames = (char_u **)gap->ga_data;
+- int sort_again = FALSE;
+- char_u *pat;
+- char_u *file_pattern;
+- char_u *curdir;
+- regmatch_T regmatch;
+- garray_T path_ga;
+- char_u **in_curdir = NULL;
+- char_u *short_name;
+-
+- remove_duplicates(gap);
+- ga_init2(&path_ga, (int)sizeof(char_u *), 1);
+-
+- /*
+- * We need to prepend a '*' at the beginning of file_pattern so that the
+- * regex matches anywhere in the path. FIXME: is this valid for all
+- * possible patterns?
+- */
+- len = (int)STRLEN(pattern);
+- file_pattern = alloc(len + 2);
+- if (file_pattern == NULL)
+- return;
+- file_pattern[0] = '*';
+- file_pattern[1] = NUL;
+- STRCAT(file_pattern, pattern);
+- pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, TRUE);
+- vim_free(file_pattern);
+- if (pat == NULL)
+- return;
+-
+- regmatch.rm_ic = TRUE; /* always ignore case */
+- regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
+- vim_free(pat);
+- if (regmatch.regprog == NULL)
+- return;
+-
+- if ((curdir = alloc((int)(MAXPATHL))) == NULL)
+- goto theend;
+- mch_dirname(curdir, MAXPATHL);
+- expand_path_option(curdir, &path_ga);
+-
+- in_curdir = (char_u **)alloc_clear(gap->ga_len * sizeof(char_u *));
+- if (in_curdir == NULL)
+- goto theend;
+-
+- for (i = 0; i < gap->ga_len && !got_int; i++)
+- {
+- char_u *path = fnames[i];
+- int is_in_curdir;
+- char_u *dir_end = gettail_dir(path);
+- char_u *pathsep_p;
+- char_u *path_cutoff;
+-
+- len = (int)STRLEN(path);
+- is_in_curdir = fnamencmp(curdir, path, dir_end - path) == 0
+- && curdir[dir_end - path] == NUL;
+- if (is_in_curdir)
+- in_curdir[i] = vim_strsave(path);
+-
+- /* Shorten the filename while maintaining its uniqueness */
+- path_cutoff = get_path_cutoff(path, &path_ga);
+-
+- /* Don't assume all files can be reached without path when search
+- * pattern starts with star star slash, so only remove path_cutoff
+- * when possible. */
+- if (pattern[0] == '*' && pattern[1] == '*'
+- && vim_ispathsep_nocolon(pattern[2])
+- && path_cutoff != NULL
+- && vim_regexec(&regmatch, path_cutoff, (colnr_T)0)
+- && is_unique(path_cutoff, gap, i))
+- {
+- sort_again = TRUE;
+- mch_memmove(path, path_cutoff, STRLEN(path_cutoff) + 1);
+- }
+- else
+- {
+- /* Here all files can be reached without path, so get shortest
+- * unique path. We start at the end of the path. */
+- pathsep_p = path + len - 1;
+-
+- while (find_previous_pathsep(path, &pathsep_p))
+- if (vim_regexec(&regmatch, pathsep_p + 1, (colnr_T)0)
+- && is_unique(pathsep_p + 1, gap, i)
+- && path_cutoff != NULL && pathsep_p + 1 >= path_cutoff)
+- {
+- sort_again = TRUE;
+- mch_memmove(path, pathsep_p + 1, STRLEN(pathsep_p));
+- break;
+- }
+- }
+-
+- if (mch_isFullName(path))
+- {
+- /*
+- * Last resort: shorten relative to curdir if possible.
+- * 'possible' means:
+- * 1. It is under the current directory.
+- * 2. The result is actually shorter than the original.
+- *
+- * Before curdir After
+- * /foo/bar/file.txt /foo/bar ./file.txt
+- * c:\foo\bar\file.txt c:\foo\bar .\file.txt
+- * /file.txt / /file.txt
+- * c:\file.txt c:\ .\file.txt
+- */
+- short_name = shorten_fname(path, curdir);
+- if (short_name != NULL && short_name > path + 1
+- #if defined(MSWIN)
+- /* On windows,
+- * shorten_fname("c:\a\a.txt", "c:\a\b")
+- * returns "\a\a.txt", which is not really the short
+- * name, hence: */
+- && !vim_ispathsep(*short_name)
+- #endif
+- )
+- {
+- STRCPY(path, ".");
+- add_pathsep(path);
+- STRMOVE(path + STRLEN(path), short_name);
+- }
+- }
+- ui_breakcheck();
+- }
+-
+- /* Shorten filenames in /in/current/directory/{filename} */
+- for (i = 0; i < gap->ga_len && !got_int; i++)
+- {
+- char_u *rel_path;
+- char_u *path = in_curdir[i];
+-
+- if (path == NULL)
+- continue;
+-
+- /* If the {filename} is not unique, change it to ./{filename}.
+- * Else reduce it to {filename} */
+- short_name = shorten_fname(path, curdir);
+- if (short_name == NULL)
+- short_name = path;
+- if (is_unique(short_name, gap, i))
+- {
+- STRCPY(fnames[i], short_name);
+- continue;
+- }
+-
+- rel_path = alloc((int)(STRLEN(short_name) + STRLEN(PATHSEPSTR) + 2));
+- if (rel_path == NULL)
+- goto theend;
+- STRCPY(rel_path, ".");
+- add_pathsep(rel_path);
+- STRCAT(rel_path, short_name);
+-
+- vim_free(fnames[i]);
+- fnames[i] = rel_path;
+- sort_again = TRUE;
+- ui_breakcheck();
+- }
+-
+- theend:
+- vim_free(curdir);
+- if (in_curdir != NULL)
+- {
+- for (i = 0; i < gap->ga_len; i++)
+- vim_free(in_curdir[i]);
+- vim_free(in_curdir);
+- }
+- ga_clear_strings(&path_ga);
+- vim_regfree(regmatch.regprog);
+-
+- if (sort_again)
+- remove_duplicates(gap);
+- }
+-
+- /*
+- * Calls globpath() with 'path' values for the given pattern and stores the
+- * result in "gap".
+- * Returns the total number of matches.
+- */
+- static int
+- expand_in_path(
+- garray_T *gap,
+- char_u *pattern,
+- int flags) /* EW_* flags */
+- {
+- char_u *curdir;
+- garray_T path_ga;
+- char_u *paths = NULL;
+- int glob_flags = 0;
+-
+- if ((curdir = alloc((unsigned)MAXPATHL)) == NULL)
+- return 0;
+- mch_dirname(curdir, MAXPATHL);
+-
+- ga_init2(&path_ga, (int)sizeof(char_u *), 1);
+- expand_path_option(curdir, &path_ga);
+- vim_free(curdir);
+- if (path_ga.ga_len == 0)
+- return 0;
+-
+- paths = ga_concat_strings(&path_ga, ",");
+- ga_clear_strings(&path_ga);
+- if (paths == NULL)
+- return 0;
+-
+- if (flags & EW_ICASE)
+- glob_flags |= WILD_ICASE;
+- if (flags & EW_ADDSLASH)
+- glob_flags |= WILD_ADD_SLASH;
+- globpath(paths, pattern, gap, glob_flags);
+- vim_free(paths);
+-
+- return gap->ga_len;
+- }
+- #endif
+-
+ #if defined(FEAT_SEARCHPATH) || defined(FEAT_CMDL_COMPL) || defined(PROTO)
+ /*
+ * Sort "gap" and remove duplicate entries. "gap" is expected to contain a
+--- 6134,6139 ----
+***************
+*** 7120,7122 ****
+--- 6670,6744 ----
+ #endif
+ return p;
+ }
++
++ /*
++ * Check if the "://" of a URL is at the pointer, return URL_SLASH.
++ * Also check for ":\\", which MS Internet Explorer accepts, return
++ * URL_BACKSLASH.
++ */
++ int
++ path_is_url(char_u *p)
++ {
++ if (STRNCMP(p, "://", (size_t)3) == 0)
++ return URL_SLASH;
++ else if (STRNCMP(p, ":\\\\", (size_t)3) == 0)
++ return URL_BACKSLASH;
++ return 0;
++ }
++
++ /*
++ * Check if "fname" starts with "name://". Return URL_SLASH if it does.
++ * Return URL_BACKSLASH for "name:\\".
++ * Return zero otherwise.
++ */
++ int
++ path_with_url(char_u *fname)
++ {
++ char_u *p;
++
++ for (p = fname; isalpha(*p); ++p)
++ ;
++ return path_is_url(p);
++ }
++
++ /*
++ * Return TRUE if "name" is a full (absolute) path name or URL.
++ */
++ int
++ vim_isAbsName(char_u *name)
++ {
++ return (path_with_url(name) != 0 || mch_isFullName(name));
++ }
++
++ /*
++ * Get absolute file name into buffer "buf[len]".
++ *
++ * return FAIL for failure, OK otherwise
++ */
++ int
++ vim_FullName(
++ char_u *fname,
++ char_u *buf,
++ int len,
++ int force) /* force expansion even when already absolute */
++ {
++ int retval = OK;
++ int url;
++
++ *buf = NUL;
++ if (fname == NULL)
++ return FAIL;
++
++ url = path_with_url(fname);
++ if (!url)
++ retval = mch_FullName(fname, buf, len, force);
++ if (url || retval == FAIL)
++ {
++ /* something failed; use the file name (truncate when too long) */
++ vim_strncpy(buf, fname, len - 1);
++ }
++ #if defined(MSWIN)
++ slash_adjust(buf);
++ #endif
++ return retval;
++ }
+*** ../vim-8.1.0913/src/misc2.c 2019-01-31 18:26:05.734803539 +0100
+--- src/misc2.c 2019-02-13 22:06:27.641777652 +0100
+***************
+*** 14,21 ****
+
+ static char_u *username = NULL; /* cached result of mch_get_user_name() */
+
+- static char_u *ff_expand_buffer = NULL; /* used for expanding filenames */
+-
+ static int coladvance2(pos_T *pos, int addspaces, int finetune, colnr_T wcol);
+
+ /*
+--- 14,19 ----
+***************
+*** 1047,1056 ****
+
+ #if defined(EXITFREE) || defined(PROTO)
+
+- # if defined(FEAT_SEARCHPATH)
+- static void free_findfile(void);
+- # endif
+-
+ /*
+ * Free everything that we allocated.
+ * Can be used to detect memory leaks, e.g., with ccmalloc.
+--- 1045,1050 ----
+***************
+*** 1161,1167 ****
+ vim_free(new_last_cmdline);
+ # endif
+ set_keep_msg(NULL, 0);
+- vim_free(ff_expand_buffer);
+
+ /* Clear cmdline history. */
+ p_hi = 0;
+--- 1155,1160 ----
+***************
+*** 3822,5746 ****
+ #endif /* CURSOR_SHAPE */
+
+
+- /* TODO: make some #ifdef for this */
+- /*--------[ file searching ]-------------------------------------------------*/
+- /*
+- * 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");
+-
+- #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 (mch_isdir(buf))
+- {
+- STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path);
+- add_pathsep(ff_expand_buffer);
+- }
+- #ifdef FEAT_PATH_EXTRA
+- else
+- {
+- char_u *p = gettail(search_ctx->ffsc_fix_path);
+- char_u *wc_path = NULL;
+- char_u *temp = NULL;
+- int len = 0;
+-
+- if (p > search_ctx->ffsc_fix_path)
+- {
+- len = (int)(p - search_ctx->ffsc_fix_path) - 1;
+- STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len);
+- add_pathsep(ff_expand_buffer);
+- }
+- else
+- len = (int)STRLEN(search_ctx->ffsc_fix_path);
+-
+- if (search_ctx->ffsc_wc_path != NULL)
+- {
+- wc_path = vim_strsave(search_ctx->ffsc_wc_path);
+- temp = alloc((int)(STRLEN(search_ctx->ffsc_wc_path)
+- + STRLEN(search_ctx->ffsc_fix_path + len)
+- + 1));
+- if (temp == NULL || wc_path == NULL)
+- {
+- vim_free(buf);
+- vim_free(temp);
+- vim_free(wc_path);
+- goto error_return;
+- }
+-
+- STRCPY(temp, search_ctx->ffsc_fix_path + len);
+- STRCAT(temp, search_ctx->ffsc_wc_path);
+- vim_free(search_ctx->ffsc_wc_path);
+- vim_free(wc_path);
+- search_ctx->ffsc_wc_path = temp;
+- }
+- }
+- #endif
+- vim_free(buf);
+- }
+-
+- sptr = ff_create_stack_element(ff_expand_buffer,
+- #ifdef FEAT_PATH_EXTRA
+- search_ctx->ffsc_wc_path,
+- #endif
+- level, 0);
+-
+- if (sptr == NULL)
+- goto error_return;
+-
+- ff_push(search_ctx, sptr);
+-
+- search_ctx->ffsc_file_to_search = vim_strsave(filename);
+- if (search_ctx->ffsc_file_to_search == NULL)
+- goto error_return;
+-
+- return search_ctx;
+-
+- error_return:
+- /*
+- * We clear the search context now!
+- * Even when the caller gave us a (perhaps valid) context we free it here,
+- * as we might have already destroyed it.
+- */
+- vim_findfile_cleanup(search_ctx);
+- return NULL;
+- }
+-
+- #if defined(FEAT_PATH_EXTRA) || defined(PROTO)
+- /*
+- * Get the stopdir string. Check that ';' is not escaped.
+- */
+- char_u *
+- vim_findfile_stopdir(char_u *buf)
+- {
+- char_u *r_ptr = buf;
+-
+- while (*r_ptr != NUL && *r_ptr != ';')
+- {
+- if (r_ptr[0] == '\\' && r_ptr[1] == ';')
+- {
+- /* Overwrite the escape char,
+- * use STRLEN(r_ptr) to move the trailing '\0'. */
+- STRMOVE(r_ptr, r_ptr + 1);
+- r_ptr++;
+- }
+- r_ptr++;
+- }
+- if (*r_ptr == ';')
+- {
+- *r_ptr = 0;
+- r_ptr++;
+- }
+- else if (*r_ptr == NUL)
+- r_ptr = NULL;
+- return r_ptr;
+- }
+- #endif
+-
+- /*
+- * Clean up the given search context. Can handle a NULL pointer.
+- */
+- void
+- vim_findfile_cleanup(void *ctx)
+- {
+- if (ctx == NULL)
+- return;
+-
+- vim_findfile_free_visited(ctx);
+- ff_clear(ctx);
+- vim_free(ctx);
+- }
+-
+- /*
+- * Find a file in a search context.
+- * The search context was created with vim_findfile_init() above.
+- * Return a pointer to an allocated file name or NULL if nothing found.
+- * To get all matching files call this function until you get NULL.
+- *
+- * If the passed search_context is NULL, NULL is returned.
+- *
+- * The search algorithm is depth first. To change this replace the
+- * stack with a list (don't forget to leave partly searched directories on the
+- * top of the list).
+- */
+- char_u *
+- vim_findfile(void *search_ctx_arg)
+- {
+- char_u *file_path;
+- #ifdef FEAT_PATH_EXTRA
+- char_u *rest_of_wildcards;
+- char_u *path_end = NULL;
+- #endif
+- ff_stack_T *stackp;
+- #if defined(FEAT_SEARCHPATH) || defined(FEAT_PATH_EXTRA)
+- int len;
+- #endif
+- int i;
+- char_u *p;
+- #ifdef FEAT_SEARCHPATH
+- char_u *suf;
+- #endif
+- ff_search_ctx_T *search_ctx;
+-
+- if (search_ctx_arg == NULL)
+- return NULL;
+-
+- search_ctx = (ff_search_ctx_T *)search_ctx_arg;
+-
+- /*
+- * filepath is used as buffer for various actions and as the storage to
+- * return a found filename.
+- */
+- if ((file_path = alloc((int)MAXPATHL)) == NULL)
+- return NULL;
+-
+- #ifdef FEAT_PATH_EXTRA
+- /* store the end of the start dir -- needed for upward search */
+- if (search_ctx->ffsc_start_dir != NULL)
+- path_end = &search_ctx->ffsc_start_dir[
+- STRLEN(search_ctx->ffsc_start_dir)];
+- #endif
+-
+- #ifdef FEAT_PATH_EXTRA
+- /* upward search loop */
+- for (;;)
+- {
+- #endif
+- /* downward search loop */
+- for (;;)
+- {
+- /* check if user user wants to stop the search*/
+- ui_breakcheck();
+- if (got_int)
+- break;
+-
+- /* get directory to work on from stack */
+- stackp = ff_pop(search_ctx);
+- if (stackp == NULL)
+- break;
+-
+- /*
+- * TODO: decide if we leave this test in
+- *
+- * GOOD: don't search a directory(-tree) twice.
+- * BAD: - check linked list for every new directory entered.
+- * - check for double files also done below
+- *
+- * Here we check if we already searched this directory.
+- * We already searched a directory if:
+- * 1) The directory is the same.
+- * 2) We would use the same wildcard string.
+- *
+- * Good if you have links on same directory via several ways
+- * or you have selfreferences in directories (e.g. SuSE Linux 6.3:
+- * /etc/rc.d/init.d is linked to /etc/rc.d -> endless loop)
+- *
+- * This check is only needed for directories we work on for the
+- * first time (hence stackp->ff_filearray == NULL)
+- */
+- if (stackp->ffs_filearray == NULL
+- && ff_check_visited(&search_ctx->ffsc_dir_visited_list
+- ->ffvl_visited_list,
+- stackp->ffs_fix_path
+- #ifdef FEAT_PATH_EXTRA
+- , stackp->ffs_wc_path
+- #endif
+- ) == FAIL)
+- {
+- #ifdef FF_VERBOSE
+- if (p_verbose >= 5)
+- {
+- verbose_enter_scroll();
+- smsg("Already Searched: %s (%s)",
+- stackp->ffs_fix_path, stackp->ffs_wc_path);
+- /* don't overwrite this either */
+- msg_puts("\n");
+- verbose_leave_scroll();
+- }
+- #endif
+- ff_free_stack_element(stackp);
+- continue;
+- }
+- #ifdef FF_VERBOSE
+- else if (p_verbose >= 5)
+- {
+- verbose_enter_scroll();
+- smsg("Searching: %s (%s)",
+- stackp->ffs_fix_path, stackp->ffs_wc_path);
+- /* don't overwrite this either */
+- msg_puts("\n");
+- verbose_leave_scroll();
+- }
+- #endif
+-
+- /* check depth */
+- if (stackp->ffs_level <= 0)
+- {
+- ff_free_stack_element(stackp);
+- continue;
+- }
+-
+- file_path[0] = NUL;
+-
+- /*
+- * If no filearray till now expand wildcards
+- * The function expand_wildcards() can handle an array of paths
+- * and all possible expands are returned in one array. We use this
+- * to handle the expansion of '**' into an empty string.
+- */
+- if (stackp->ffs_filearray == NULL)
+- {
+- char_u *dirptrs[2];
+-
+- /* we use filepath to build the path expand_wildcards() should
+- * expand.
+- */
+- dirptrs[0] = file_path;
+- dirptrs[1] = NULL;
+-
+- /* if we have a start dir copy it in */
+- if (!vim_isAbsName(stackp->ffs_fix_path)
+- && search_ctx->ffsc_start_dir)
+- {
+- if (STRLEN(search_ctx->ffsc_start_dir) + 1 < MAXPATHL)
+- {
+- STRCPY(file_path, search_ctx->ffsc_start_dir);
+- add_pathsep(file_path);
+- }
+- else
+- {
+- ff_free_stack_element(stackp);
+- goto fail;
+- }
+- }
+-
+- /* append the fix part of the search path */
+- if (STRLEN(file_path) + STRLEN(stackp->ffs_fix_path) + 1 < MAXPATHL)
+- {
+- STRCAT(file_path, stackp->ffs_fix_path);
+- add_pathsep(file_path);
+- }
+- else
+- {
+- ff_free_stack_element(stackp);
+- goto fail;
+- }
+-
+- #ifdef FEAT_PATH_EXTRA
+- rest_of_wildcards = stackp->ffs_wc_path;
+- if (*rest_of_wildcards != NUL)
+- {
+- len = (int)STRLEN(file_path);
+- if (STRNCMP(rest_of_wildcards, "**", 2) == 0)
+- {
+- /* pointer to the restrict byte
+- * The restrict byte is not a character!
+- */
+- p = rest_of_wildcards + 2;
+-
+- if (*p > 0)
+- {
+- (*p)--;
+- if (len + 1 < MAXPATHL)
+- file_path[len++] = '*';
+- else
+- {
+- ff_free_stack_element(stackp);
+- goto fail;
+- }
+- }
+-
+- if (*p == 0)
+- {
+- /* remove '**<numb> from wildcards */
+- STRMOVE(rest_of_wildcards, rest_of_wildcards + 3);
+- }
+- else
+- rest_of_wildcards += 3;
+-
+- if (stackp->ffs_star_star_empty == 0)
+- {
+- /* if not done before, expand '**' to empty */
+- stackp->ffs_star_star_empty = 1;
+- dirptrs[1] = stackp->ffs_fix_path;
+- }
+- }
+-
+- /*
+- * Here we copy until the next path separator or the end of
+- * the path. If we stop at a path separator, there is
+- * still something else left. This is handled below by
+- * pushing every directory returned from expand_wildcards()
+- * on the stack again for further search.
+- */
+- while (*rest_of_wildcards
+- && !vim_ispathsep(*rest_of_wildcards))
+- if (len + 1 < MAXPATHL)
+- file_path[len++] = *rest_of_wildcards++;
+- else
+- {
+- ff_free_stack_element(stackp);
+- goto fail;
+- }
+-
+- file_path[len] = NUL;
+- if (vim_ispathsep(*rest_of_wildcards))
+- rest_of_wildcards++;
+- }
+- #endif
+-
+- /*
+- * Expand wildcards like "*" and "$VAR".
+- * If the path is a URL don't try this.
+- */
+- if (path_with_url(dirptrs[0]))
+- {
+- stackp->ffs_filearray = (char_u **)
+- alloc((unsigned)sizeof(char *));
+- if (stackp->ffs_filearray != NULL
+- && (stackp->ffs_filearray[0]
+- = vim_strsave(dirptrs[0])) != NULL)
+- stackp->ffs_filearray_size = 1;
+- else
+- stackp->ffs_filearray_size = 0;
+- }
+- else
+- /* Add EW_NOTWILD because the expanded path may contain
+- * wildcard characters that are to be taken literally.
+- * This is a bit of a hack. */
+- expand_wildcards((dirptrs[1] == NULL) ? 1 : 2, dirptrs,
+- &stackp->ffs_filearray_size,
+- &stackp->ffs_filearray,
+- EW_DIR|EW_ADDSLASH|EW_SILENT|EW_NOTWILD);
+-
+- stackp->ffs_filearray_cur = 0;
+- stackp->ffs_stage = 0;
+- }
+- #ifdef FEAT_PATH_EXTRA
+- else
+- rest_of_wildcards = &stackp->ffs_wc_path[
+- STRLEN(stackp->ffs_wc_path)];
+- #endif
+-
+- if (stackp->ffs_stage == 0)
+- {
+- /* this is the first time we work on this directory */
+- #ifdef FEAT_PATH_EXTRA
+- if (*rest_of_wildcards == NUL)
+- #endif
+- {
+- /*
+- * We don't have further wildcards to expand, so we have to
+- * check for the final file now.
+- */
+- for (i = stackp->ffs_filearray_cur;
+- i < stackp->ffs_filearray_size; ++i)
+- {
+- if (!path_with_url(stackp->ffs_filearray[i])
+- && !mch_isdir(stackp->ffs_filearray[i]))
+- continue; /* not a directory */
+-
+- /* prepare the filename to be checked for existence
+- * below */
+- if (STRLEN(stackp->ffs_filearray[i]) + 1
+- + STRLEN(search_ctx->ffsc_file_to_search) < MAXPATHL)
+- {
+- STRCPY(file_path, stackp->ffs_filearray[i]);
+- add_pathsep(file_path);
+- STRCAT(file_path, search_ctx->ffsc_file_to_search);
+- }
+- else
+- {
+- ff_free_stack_element(stackp);
+- goto fail;
+- }
+-
+- /*
+- * Try without extra suffix and then with suffixes
+- * from 'suffixesadd'.
+- */
+- #ifdef FEAT_SEARCHPATH
+- len = (int)STRLEN(file_path);
+- if (search_ctx->ffsc_tagfile)
+- suf = (char_u *)"";
+- else
+- suf = curbuf->b_p_sua;
+- for (;;)
+- #endif
+- {
+- /* if file exists and we didn't already find it */
+- if ((path_with_url(file_path)
+- || (mch_getperm(file_path) >= 0
+- && (search_ctx->ffsc_find_what
+- == FINDFILE_BOTH
+- || ((search_ctx->ffsc_find_what
+- == FINDFILE_DIR)
+- == mch_isdir(file_path)))))
+- #ifndef FF_VERBOSE
+- && (ff_check_visited(
+- &search_ctx->ffsc_visited_list->ffvl_visited_list,
+- file_path
+- #ifdef FEAT_PATH_EXTRA
+- , (char_u *)""
+- #endif
+- ) == OK)
+- #endif
+- )
+- {
+- #ifdef FF_VERBOSE
+- if (ff_check_visited(
+- &search_ctx->ffsc_visited_list->ffvl_visited_list,
+- file_path
+- #ifdef FEAT_PATH_EXTRA
+- , (char_u *)""
+- #endif
+- ) == FAIL)
+- {
+- if (p_verbose >= 5)
+- {
+- verbose_enter_scroll();
+- smsg("Already: %s",
+- file_path);
+- /* don't overwrite this either */
+- msg_puts("\n");
+- verbose_leave_scroll();
+- }
+- continue;
+- }
+- #endif
+-
+- /* push dir to examine rest of subdirs later */
+- stackp->ffs_filearray_cur = i + 1;
+- ff_push(search_ctx, stackp);
+-
+- if (!path_with_url(file_path))
+- simplify_filename(file_path);
+- if (mch_dirname(ff_expand_buffer, MAXPATHL)
+- == OK)
+- {
+- p = shorten_fname(file_path,
+- ff_expand_buffer);
+- if (p != NULL)
+- STRMOVE(file_path, p);
+- }
+- #ifdef FF_VERBOSE
+- if (p_verbose >= 5)
+- {
+- verbose_enter_scroll();
+- smsg("HIT: %s", file_path);
+- /* don't overwrite this either */
+- msg_puts("\n");
+- verbose_leave_scroll();
+- }
+- #endif
+- return file_path;
+- }
+-
+- #ifdef FEAT_SEARCHPATH
+- /* Not found or found already, try next suffix. */
+- if (*suf == NUL)
+- break;
+- copy_option_part(&suf, file_path + len,
+- MAXPATHL - len, ",");
+- #endif
+- }
+- }
+- }
+- #ifdef FEAT_PATH_EXTRA
+- else
+- {
+- /*
+- * still wildcards left, push the directories for further
+- * search
+- */
+- for (i = stackp->ffs_filearray_cur;
+- i < stackp->ffs_filearray_size; ++i)
+- {
+- if (!mch_isdir(stackp->ffs_filearray[i]))
+- continue; /* not a directory */
+-
+- ff_push(search_ctx,
+- ff_create_stack_element(
+- stackp->ffs_filearray[i],
+- rest_of_wildcards,
+- stackp->ffs_level - 1, 0));
+- }
+- }
+- #endif
+- stackp->ffs_filearray_cur = 0;
+- stackp->ffs_stage = 1;
+- }
+-
+- #ifdef FEAT_PATH_EXTRA
+- /*
+- * if wildcards contains '**' we have to descent till we reach the
+- * leaves of the directory tree.
+- */
+- if (STRNCMP(stackp->ffs_wc_path, "**", 2) == 0)
+- {
+- for (i = stackp->ffs_filearray_cur;
+- i < stackp->ffs_filearray_size; ++i)
+- {
+- if (fnamecmp(stackp->ffs_filearray[i],
+- stackp->ffs_fix_path) == 0)
+- continue; /* don't repush same directory */
+- if (!mch_isdir(stackp->ffs_filearray[i]))
+- continue; /* not a directory */
+- ff_push(search_ctx,
+- ff_create_stack_element(stackp->ffs_filearray[i],
+- stackp->ffs_wc_path, stackp->ffs_level - 1, 1));
+- }
+- }
+- #endif
+-
+- /* we are done with the current directory */
+- ff_free_stack_element(stackp);
+-
+- }
+-
+- #ifdef FEAT_PATH_EXTRA
+- /* If we reached this, we didn't find anything downwards.
+- * Let's check if we should do an upward search.
+- */
+- if (search_ctx->ffsc_start_dir
+- && search_ctx->ffsc_stopdirs_v != NULL && !got_int)
+- {
+- ff_stack_T *sptr;
+-
+- /* is the last starting directory in the stop list? */
+- if (ff_path_in_stoplist(search_ctx->ffsc_start_dir,
+- (int)(path_end - search_ctx->ffsc_start_dir),
+- search_ctx->ffsc_stopdirs_v) == TRUE)
+- break;
+-
+- /* cut of last dir */
+- while (path_end > search_ctx->ffsc_start_dir
+- && vim_ispathsep(*path_end))
+- path_end--;
+- while (path_end > search_ctx->ffsc_start_dir
+- && !vim_ispathsep(path_end[-1]))
+- path_end--;
+- *path_end = 0;
+- path_end--;
+-
+- if (*search_ctx->ffsc_start_dir == 0)
+- break;
+-
+- if (STRLEN(search_ctx->ffsc_start_dir) + 1
+- + STRLEN(search_ctx->ffsc_fix_path) < MAXPATHL)
+- {
+- STRCPY(file_path, search_ctx->ffsc_start_dir);
+- add_pathsep(file_path);
+- STRCAT(file_path, search_ctx->ffsc_fix_path);
+- }
+- else
+- goto fail;
+-
+- /* create a new stack entry */
+- sptr = ff_create_stack_element(file_path,
+- search_ctx->ffsc_wc_path, search_ctx->ffsc_level, 0);
+- if (sptr == NULL)
+- break;
+- ff_push(search_ctx, sptr);
+- }
+- else
+- break;
+- }
+- #endif
+-
+- fail:
+- vim_free(file_path);
+- return NULL;
+- }
+-
+- /*
+- * Free the list of lists of visited files and directories
+- * Can handle it if the passed search_context is NULL;
+- */
+- void
+- vim_findfile_free_visited(void *search_ctx_arg)
+- {
+- ff_search_ctx_T *search_ctx;
+-
+- if (search_ctx_arg == NULL)
+- return;
+-
+- search_ctx = (ff_search_ctx_T *)search_ctx_arg;
+- vim_findfile_free_visited_list(&search_ctx->ffsc_visited_lists_list);
+- vim_findfile_free_visited_list(&search_ctx->ffsc_dir_visited_lists_list);
+- }
+-
+- static void
+- vim_findfile_free_visited_list(ff_visited_list_hdr_T **list_headp)
+- {
+- ff_visited_list_hdr_T *vp;
+-
+- while (*list_headp != NULL)
+- {
+- vp = (*list_headp)->ffvl_next;
+- ff_free_visited_list((*list_headp)->ffvl_visited_list);
+-
+- vim_free((*list_headp)->ffvl_filename);
+- vim_free(*list_headp);
+- *list_headp = vp;
+- }
+- *list_headp = NULL;
+- }
+-
+- static void
+- ff_free_visited_list(ff_visited_T *vl)
+- {
+- ff_visited_T *vp;
+-
+- while (vl != NULL)
+- {
+- vp = vl->ffv_next;
+- #ifdef FEAT_PATH_EXTRA
+- vim_free(vl->ffv_wc_path);
+- #endif
+- vim_free(vl);
+- vl = vp;
+- }
+- vl = NULL;
+- }
+-
+- /*
+- * Returns the already visited list for the given filename. If none is found it
+- * allocates a new one.
+- */
+- static ff_visited_list_hdr_T*
+- ff_get_visited_list(
+- char_u *filename,
+- ff_visited_list_hdr_T **list_headp)
+- {
+- ff_visited_list_hdr_T *retptr = NULL;
+-
+- /* check if a visited list for the given filename exists */
+- if (*list_headp != NULL)
+- {
+- retptr = *list_headp;
+- while (retptr != NULL)
+- {
+- if (fnamecmp(filename, retptr->ffvl_filename) == 0)
+- {
+- #ifdef FF_VERBOSE
+- if (p_verbose >= 5)
+- {
+- verbose_enter_scroll();
+- smsg("ff_get_visited_list: FOUND list for %s",
+- filename);
+- /* don't overwrite this either */
+- msg_puts("\n");
+- verbose_leave_scroll();
+- }
+- #endif
+- return retptr;
+- }
+- retptr = retptr->ffvl_next;
+- }
+- }
+-
+- #ifdef FF_VERBOSE
+- if (p_verbose >= 5)
+- {
+- verbose_enter_scroll();
+- smsg("ff_get_visited_list: new list for %s", filename);
+- /* don't overwrite this either */
+- msg_puts("\n");
+- verbose_leave_scroll();
+- }
+- #endif
+-
+- /*
+- * if we reach this we didn't find a list and we have to allocate new list
+- */
+- retptr = (ff_visited_list_hdr_T*)alloc((unsigned)sizeof(*retptr));
+- if (retptr == NULL)
+- return NULL;
+-
+- retptr->ffvl_visited_list = NULL;
+- retptr->ffvl_filename = vim_strsave(filename);
+- if (retptr->ffvl_filename == NULL)
+- {
+- vim_free(retptr);
+- return NULL;
+- }
+- retptr->ffvl_next = *list_headp;
+- *list_headp = retptr;
+-
+- return retptr;
+- }
+-
+- #ifdef FEAT_PATH_EXTRA
+- /*
+- * check if two wildcard paths are equal. Returns TRUE or FALSE.
+- * They are equal if:
+- * - both paths are NULL
+- * - they have the same length
+- * - char by char comparison is OK
+- * - the only differences are in the counters behind a '**', so
+- * '**\20' is equal to '**\24'
+- */
+- static int
+- ff_wc_equal(char_u *s1, char_u *s2)
+- {
+- int i, j;
+- int c1 = NUL;
+- int c2 = NUL;
+- int prev1 = NUL;
+- int prev2 = NUL;
+-
+- if (s1 == s2)
+- return TRUE;
+-
+- if (s1 == NULL || s2 == NULL)
+- return FALSE;
+-
+- for (i = 0, j = 0; s1[i] != NUL && s2[j] != NUL;)
+- {
+- c1 = PTR2CHAR(s1 + i);
+- c2 = PTR2CHAR(s2 + j);
+-
+- if ((p_fic ? MB_TOLOWER(c1) != MB_TOLOWER(c2) : c1 != c2)
+- && (prev1 != '*' || prev2 != '*'))
+- return FALSE;
+- prev2 = prev1;
+- prev1 = c1;
+-
+- i += MB_PTR2LEN(s1 + i);
+- j += MB_PTR2LEN(s2 + j);
+- }
+- return s1[i] == s2[j];
+- }
+- #endif
+-
+- /*
+- * maintains the list of already visited files and dirs
+- * returns FAIL if the given file/dir is already in the list
+- * returns OK if it is newly added
+- *
+- * TODO: What to do on memory allocation problems?
+- * -> return TRUE - Better the file is found several times instead of
+- * never.
+- */
+- static int
+- ff_check_visited(
+- ff_visited_T **visited_list,
+- char_u *fname
+- #ifdef FEAT_PATH_EXTRA
+- , char_u *wc_path
+- #endif
+- )
+- {
+- ff_visited_T *vp;
+- #ifdef UNIX
+- stat_T st;
+- int url = FALSE;
+- #endif
+-
+- /* For an URL we only compare the name, otherwise we compare the
+- * device/inode (unix) or the full path name (not Unix). */
+- if (path_with_url(fname))
+- {
+- vim_strncpy(ff_expand_buffer, fname, MAXPATHL - 1);
+- #ifdef UNIX
+- url = TRUE;
+- #endif
+- }
+- else
+- {
+- ff_expand_buffer[0] = NUL;
+- #ifdef UNIX
+- if (mch_stat((char *)fname, &st) < 0)
+- #else
+- if (vim_FullName(fname, ff_expand_buffer, MAXPATHL, TRUE) == FAIL)
+- #endif
+- return FAIL;
+- }
+-
+- /* check against list of already visited files */
+- for (vp = *visited_list; vp != NULL; vp = vp->ffv_next)
+- {
+- if (
+- #ifdef UNIX
+- !url ? (vp->ffv_dev_valid && vp->ffv_dev == st.st_dev
+- && vp->ffv_ino == st.st_ino)
+- :
+- #endif
+- fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0
+- )
+- {
+- #ifdef FEAT_PATH_EXTRA
+- /* are the wildcard parts equal */
+- if (ff_wc_equal(vp->ffv_wc_path, wc_path) == TRUE)
+- #endif
+- /* already visited */
+- return FAIL;
+- }
+- }
+-
+- /*
+- * New file/dir. Add it to the list of visited files/dirs.
+- */
+- vp = (ff_visited_T *)alloc((unsigned)(sizeof(ff_visited_T)
+- + STRLEN(ff_expand_buffer)));
+-
+- if (vp != NULL)
+- {
+- #ifdef UNIX
+- if (!url)
+- {
+- vp->ffv_dev_valid = TRUE;
+- vp->ffv_ino = st.st_ino;
+- vp->ffv_dev = st.st_dev;
+- vp->ffv_fname[0] = NUL;
+- }
+- else
+- {
+- vp->ffv_dev_valid = FALSE;
+- #endif
+- STRCPY(vp->ffv_fname, ff_expand_buffer);
+- #ifdef UNIX
+- }
+- #endif
+- #ifdef FEAT_PATH_EXTRA
+- if (wc_path != NULL)
+- vp->ffv_wc_path = vim_strsave(wc_path);
+- else
+- vp->ffv_wc_path = NULL;
+- #endif
+-
+- vp->ffv_next = *visited_list;
+- *visited_list = vp;
+- }
+-
+- return OK;
+- }
+-
+- /*
+- * create stack element from given path pieces
+- */
+- static ff_stack_T *
+- ff_create_stack_element(
+- char_u *fix_part,
+- #ifdef FEAT_PATH_EXTRA
+- char_u *wc_part,
+- #endif
+- int level,
+- int star_star_empty)
+- {
+- ff_stack_T *new;
+-
+- new = (ff_stack_T *)alloc((unsigned)sizeof(ff_stack_T));
+- if (new == NULL)
+- return NULL;
+-
+- new->ffs_prev = NULL;
+- new->ffs_filearray = NULL;
+- new->ffs_filearray_size = 0;
+- new->ffs_filearray_cur = 0;
+- new->ffs_stage = 0;
+- new->ffs_level = level;
+- new->ffs_star_star_empty = star_star_empty;
+-
+- /* the following saves NULL pointer checks in vim_findfile */
+- if (fix_part == NULL)
+- fix_part = (char_u *)"";
+- new->ffs_fix_path = vim_strsave(fix_part);
+-
+- #ifdef FEAT_PATH_EXTRA
+- if (wc_part == NULL)
+- wc_part = (char_u *)"";
+- new->ffs_wc_path = vim_strsave(wc_part);
+- #endif
+-
+- if (new->ffs_fix_path == NULL
+- #ifdef FEAT_PATH_EXTRA
+- || new->ffs_wc_path == NULL
+- #endif
+- )
+- {
+- ff_free_stack_element(new);
+- new = NULL;
+- }
+-
+- return new;
+- }
+-
+- /*
+- * Push a dir on the directory stack.
+- */
+- static void
+- ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr)
+- {
+- /* check for NULL pointer, not to return an error to the user, but
+- * to prevent a crash */
+- if (stack_ptr != NULL)
+- {
+- stack_ptr->ffs_prev = search_ctx->ffsc_stack_ptr;
+- search_ctx->ffsc_stack_ptr = stack_ptr;
+- }
+- }
+-
+- /*
+- * Pop a dir from the directory stack.
+- * Returns NULL if stack is empty.
+- */
+- static ff_stack_T *
+- ff_pop(ff_search_ctx_T *search_ctx)
+- {
+- ff_stack_T *sptr;
+-
+- sptr = search_ctx->ffsc_stack_ptr;
+- if (search_ctx->ffsc_stack_ptr != NULL)
+- search_ctx->ffsc_stack_ptr = search_ctx->ffsc_stack_ptr->ffs_prev;
+-
+- return sptr;
+- }
+-
+- /*
+- * free the given stack element
+- */
+- static void
+- ff_free_stack_element(ff_stack_T *stack_ptr)
+- {
+- /* vim_free handles possible NULL pointers */
+- vim_free(stack_ptr->ffs_fix_path);
+- #ifdef FEAT_PATH_EXTRA
+- vim_free(stack_ptr->ffs_wc_path);
+- #endif
+-
+- if (stack_ptr->ffs_filearray != NULL)
+- FreeWild(stack_ptr->ffs_filearray_size, stack_ptr->ffs_filearray);
+-
+- vim_free(stack_ptr);
+- }
+-
+- /*
+- * Clear the search context, but NOT the visited list.
+- */
+- static void
+- ff_clear(ff_search_ctx_T *search_ctx)
+- {
+- ff_stack_T *sptr;
+-
+- /* clear up stack */
+- while ((sptr = ff_pop(search_ctx)) != NULL)
+- ff_free_stack_element(sptr);
+-
+- vim_free(search_ctx->ffsc_file_to_search);
+- vim_free(search_ctx->ffsc_start_dir);
+- vim_free(search_ctx->ffsc_fix_path);
+- #ifdef FEAT_PATH_EXTRA
+- vim_free(search_ctx->ffsc_wc_path);
+- #endif
+-
+- #ifdef FEAT_PATH_EXTRA
+- if (search_ctx->ffsc_stopdirs_v != NULL)
+- {
+- int i = 0;
+-
+- while (search_ctx->ffsc_stopdirs_v[i] != NULL)
+- {
+- vim_free(search_ctx->ffsc_stopdirs_v[i]);
+- i++;
+- }
+- vim_free(search_ctx->ffsc_stopdirs_v);
+- }
+- search_ctx->ffsc_stopdirs_v = NULL;
+- #endif
+-
+- /* reset everything */
+- search_ctx->ffsc_file_to_search = NULL;
+- search_ctx->ffsc_start_dir = NULL;
+- search_ctx->ffsc_fix_path = NULL;
+- #ifdef FEAT_PATH_EXTRA
+- search_ctx->ffsc_wc_path = NULL;
+- search_ctx->ffsc_level = 0;
+- #endif
+- }
+-
+- #ifdef FEAT_PATH_EXTRA
+- /*
+- * check if the given path is in the stopdirs
+- * returns TRUE if yes else FALSE
+- */
+- static int
+- ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
+- {
+- int i = 0;
+-
+- /* eat up trailing path separators, except the first */
+- while (path_len > 1 && vim_ispathsep(path[path_len - 1]))
+- path_len--;
+-
+- /* if no path consider it as match */
+- if (path_len == 0)
+- return TRUE;
+-
+- for (i = 0; stopdirs_v[i] != NULL; i++)
+- {
+- if ((int)STRLEN(stopdirs_v[i]) > path_len)
+- {
+- /* match for parent directory. So '/home' also matches
+- * '/home/rks'. Check for PATHSEP in stopdirs_v[i], else
+- * '/home/r' would also match '/home/rks'
+- */
+- if (fnamencmp(stopdirs_v[i], path, path_len) == 0
+- && vim_ispathsep(stopdirs_v[i][path_len]))
+- return TRUE;
+- }
+- else
+- {
+- if (fnamecmp(stopdirs_v[i], path) == 0)
+- return TRUE;
+- }
+- }
+- return FALSE;
+- }
+- #endif
+-
+- #if defined(FEAT_SEARCHPATH) || defined(PROTO)
+- /*
+- * Find the file name "ptr[len]" in the path. Also finds directory names.
+- *
+- * On the first call set the parameter 'first' to TRUE to initialize
+- * the search. For repeating calls to FALSE.
+- *
+- * Repeating calls will return other files called 'ptr[len]' from the path.
+- *
+- * Only on the first call 'ptr' and 'len' are used. For repeating calls they
+- * don't need valid values.
+- *
+- * If nothing found on the first call the option FNAME_MESS will issue the
+- * message:
+- * 'Can't find file "<file>" in path'
+- * On repeating calls:
+- * 'No more file "<file>" found in path'
+- *
+- * options:
+- * FNAME_MESS give error message when not found
+- *
+- * Uses NameBuff[]!
+- *
+- * Returns an allocated string for the file name. NULL for error.
+- *
+- */
+- char_u *
+- find_file_in_path(
+- char_u *ptr, /* file name */
+- int len, /* length of file name */
+- int options,
+- int first, /* use count'th matching file name */
+- char_u *rel_fname) /* file name searching relative to */
+- {
+- return find_file_in_path_option(ptr, len, options, first,
+- *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path,
+- FINDFILE_BOTH, rel_fname, curbuf->b_p_sua);
+- }
+-
+- static char_u *ff_file_to_find = NULL;
+- static void *fdip_search_ctx = NULL;
+-
+- #if defined(EXITFREE)
+- static void
+- free_findfile(void)
+- {
+- vim_free(ff_file_to_find);
+- vim_findfile_cleanup(fdip_search_ctx);
+- }
+- #endif
+-
+- /*
+- * Find the directory name "ptr[len]" in the path.
+- *
+- * options:
+- * FNAME_MESS give error message when not found
+- * FNAME_UNESC unescape backslashes.
+- *
+- * Uses NameBuff[]!
+- *
+- * Returns an allocated string for the file name. NULL for error.
+- */
+- char_u *
+- find_directory_in_path(
+- char_u *ptr, /* file name */
+- int len, /* length of file name */
+- int options,
+- char_u *rel_fname) /* file name searching relative to */
+- {
+- return find_file_in_path_option(ptr, len, options, TRUE, p_cdpath,
+- FINDFILE_DIR, rel_fname, (char_u *)"");
+- }
+-
+- char_u *
+- find_file_in_path_option(
+- char_u *ptr, /* file name */
+- int len, /* length of file name */
+- int options,
+- int first, /* use count'th matching file name */
+- char_u *path_option, /* p_path or p_cdpath */
+- int find_what, /* FINDFILE_FILE, _DIR or _BOTH */
+- char_u *rel_fname, /* file name we are looking relative to. */
+- char_u *suffixes) /* list of suffixes, 'suffixesadd' option */
+- {
+- static char_u *dir;
+- static int did_findfile_init = FALSE;
+- char_u save_char;
+- char_u *file_name = NULL;
+- char_u *buf = NULL;
+- int rel_to_curdir;
+- #ifdef AMIGA
+- struct Process *proc = (struct Process *)FindTask(0L);
+- APTR save_winptr = proc->pr_WindowPtr;
+-
+- /* Avoid a requester here for a volume that doesn't exist. */
+- proc->pr_WindowPtr = (APTR)-1L;
+- #endif
+-
+- if (first == TRUE)
+- {
+- /* copy file name into NameBuff, expanding environment variables */
+- save_char = ptr[len];
+- ptr[len] = NUL;
+- expand_env_esc(ptr, NameBuff, MAXPATHL, FALSE, TRUE, NULL);
+- ptr[len] = save_char;
+-
+- vim_free(ff_file_to_find);
+- ff_file_to_find = vim_strsave(NameBuff);
+- if (ff_file_to_find == NULL) /* out of memory */
+- {
+- file_name = NULL;
+- goto theend;
+- }
+- if (options & FNAME_UNESC)
+- {
+- /* Change all "\ " to " ". */
+- for (ptr = ff_file_to_find; *ptr != NUL; ++ptr)
+- if (ptr[0] == '\\' && ptr[1] == ' ')
+- mch_memmove(ptr, ptr + 1, STRLEN(ptr));
+- }
+- }
+-
+- rel_to_curdir = (ff_file_to_find[0] == '.'
+- && (ff_file_to_find[1] == NUL
+- || vim_ispathsep(ff_file_to_find[1])
+- || (ff_file_to_find[1] == '.'
+- && (ff_file_to_find[2] == NUL
+- || vim_ispathsep(ff_file_to_find[2])))));
+- if (vim_isAbsName(ff_file_to_find)
+- /* "..", "../path", "." and "./path": don't use the path_option */
+- || rel_to_curdir
+- #if defined(MSWIN)
+- /* handle "\tmp" as absolute path */
+- || vim_ispathsep(ff_file_to_find[0])
+- /* handle "c:name" as absolute path */
+- || (ff_file_to_find[0] != NUL && ff_file_to_find[1] == ':')
+- #endif
+- #ifdef AMIGA
+- /* handle ":tmp" as absolute path */
+- || ff_file_to_find[0] == ':'
+- #endif
+- )
+- {
+- /*
+- * Absolute path, no need to use "path_option".
+- * If this is not a first call, return NULL. We already returned a
+- * filename on the first call.
+- */
+- if (first == TRUE)
+- {
+- int l;
+- int run;
+-
+- if (path_with_url(ff_file_to_find))
+- {
+- file_name = vim_strsave(ff_file_to_find);
+- goto theend;
+- }
+-
+- /* When FNAME_REL flag given first use the directory of the file.
+- * Otherwise or when this fails use the current directory. */
+- for (run = 1; run <= 2; ++run)
+- {
+- l = (int)STRLEN(ff_file_to_find);
+- if (run == 1
+- && rel_to_curdir
+- && (options & FNAME_REL)
+- && rel_fname != NULL
+- && STRLEN(rel_fname) + l < MAXPATHL)
+- {
+- STRCPY(NameBuff, rel_fname);
+- STRCPY(gettail(NameBuff), ff_file_to_find);
+- l = (int)STRLEN(NameBuff);
+- }
+- else
+- {
+- STRCPY(NameBuff, ff_file_to_find);
+- run = 2;
+- }
+-
+- /* When the file doesn't exist, try adding parts of
+- * 'suffixesadd'. */
+- buf = suffixes;
+- for (;;)
+- {
+- if (mch_getperm(NameBuff) >= 0
+- && (find_what == FINDFILE_BOTH
+- || ((find_what == FINDFILE_DIR)
+- == mch_isdir(NameBuff))))
+- {
+- file_name = vim_strsave(NameBuff);
+- goto theend;
+- }
+- if (*buf == NUL)
+- break;
+- copy_option_part(&buf, NameBuff + l, MAXPATHL - l, ",");
+- }
+- }
+- }
+- }
+- else
+- {
+- /*
+- * Loop over all paths in the 'path' or 'cdpath' option.
+- * When "first" is set, first setup to the start of the option.
+- * Otherwise continue to find the next match.
+- */
+- if (first == TRUE)
+- {
+- /* vim_findfile_free_visited can handle a possible NULL pointer */
+- vim_findfile_free_visited(fdip_search_ctx);
+- dir = path_option;
+- did_findfile_init = FALSE;
+- }
+-
+- for (;;)
+- {
+- if (did_findfile_init)
+- {
+- file_name = vim_findfile(fdip_search_ctx);
+- if (file_name != NULL)
+- break;
+-
+- did_findfile_init = FALSE;
+- }
+- else
+- {
+- char_u *r_ptr;
+-
+- if (dir == NULL || *dir == NUL)
+- {
+- /* We searched all paths of the option, now we can
+- * free the search context. */
+- vim_findfile_cleanup(fdip_search_ctx);
+- fdip_search_ctx = NULL;
+- break;
+- }
+-
+- if ((buf = alloc((int)(MAXPATHL))) == NULL)
+- break;
+-
+- /* copy next path */
+- buf[0] = 0;
+- copy_option_part(&dir, buf, MAXPATHL, " ,");
+-
+- #ifdef FEAT_PATH_EXTRA
+- /* get the stopdir string */
+- r_ptr = vim_findfile_stopdir(buf);
+- #else
+- r_ptr = NULL;
+- #endif
+- fdip_search_ctx = vim_findfile_init(buf, ff_file_to_find,
+- r_ptr, 100, FALSE, find_what,
+- fdip_search_ctx, FALSE, rel_fname);
+- if (fdip_search_ctx != NULL)
+- did_findfile_init = TRUE;
+- vim_free(buf);
+- }
+- }
+- }
+- if (file_name == NULL && (options & FNAME_MESS))
+- {
+- if (first == TRUE)
+- {
+- if (find_what == FINDFILE_DIR)
+- semsg(_("E344: Can't find directory \"%s\" in cdpath"),
+- ff_file_to_find);
+- else
+- semsg(_("E345: Can't find file \"%s\" in path"),
+- ff_file_to_find);
+- }
+- else
+- {
+- if (find_what == FINDFILE_DIR)
+- semsg(_("E346: No more directory \"%s\" found in cdpath"),
+- ff_file_to_find);
+- else
+- semsg(_("E347: No more file \"%s\" found in path"),
+- ff_file_to_find);
+- }
+- }
+-
+- theend:
+- #ifdef AMIGA
+- proc->pr_WindowPtr = save_winptr;
+- #endif
+- return file_name;
+- }
+-
+- #endif /* FEAT_SEARCHPATH */
+-
+ /*
+ * Change directory to "new_dir". If FEAT_SEARCHPATH is defined, search
+ * 'cdpath' for relative directory names, otherwise just mch_chdir().
+--- 3815,3820 ----
+*** ../vim-8.1.0913/src/proto.h 2019-01-31 13:47:51.126632619 +0100
+--- src/proto.h 2019-02-13 22:06:27.641777652 +0100
+***************
+*** 80,85 ****
+--- 80,86 ----
+ # include "ex_eval.pro"
+ # include "ex_getln.pro"
+ # include "fileio.pro"
++ # include "findfile.pro"
+ # include "fold.pro"
+ # include "getchar.pro"
+ # ifdef FEAT_HANGULIN
+*** ../vim-8.1.0913/src/proto/findfile.pro 2019-02-13 22:43:46.449534680 +0100
+--- src/proto/findfile.pro 2019-02-13 22:31:26.131353440 +0100
+***************
+*** 0 ****
+--- 1,18 ----
++ /* findfile.c */
++ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int level, int free_visited, int find_what, void *search_ctx_arg, int tagfile, char_u *rel_fname);
++ char_u *vim_findfile_stopdir(char_u *buf);
++ void vim_findfile_cleanup(void *ctx);
++ char_u *vim_findfile(void *search_ctx_arg);
++ void vim_findfile_free_visited(void *search_ctx_arg);
++ char_u *find_file_in_path(char_u *ptr, int len, int options, int first, char_u *rel_fname);
++ void free_findfile(void);
++ char_u *find_directory_in_path(char_u *ptr, int len, int options, char_u *rel_fname);
++ char_u *find_file_in_path_option(char_u *ptr, int len, int options, int first, char_u *path_option, int find_what, char_u *rel_fname, char_u *suffixes);
++ char_u *grab_file_name(long count, linenr_T *file_lnum);
++ char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum);
++ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u *rel_fname, linenr_T *file_lnum);
++ char_u *find_file_name_in_path(char_u *ptr, int len, int options, long count, char_u *rel_fname);
++ int vim_ispathlistsep(int c);
++ void uniquefy_paths(garray_T *gap, char_u *pattern);
++ int expand_in_path(garray_T *gap, char_u *pattern, int flags);
++ /* vim: set ft=c : */
+*** ../vim-8.1.0913/src/proto/misc1.pro 2019-01-31 13:47:51.126632619 +0100
+--- src/proto/misc1.pro 2019-02-13 22:07:22.757355997 +0100
+***************
+*** 74,80 ****
+ char_u *get_past_head(char_u *path);
+ int vim_ispathsep(int c);
+ int vim_ispathsep_nocolon(int c);
+- int vim_ispathlistsep(int c);
+ void shorten_dir(char_u *str);
+ int dir_of_file_exists(char_u *fname);
+ int vim_fnamecmp(char_u *x, char_u *y);
+--- 74,79 ----
+***************
+*** 99,102 ****
+--- 98,105 ----
+ void FreeWild(int count, char_u **files);
+ int goto_im(void);
+ char_u *get_isolated_shell_name(void);
++ int path_is_url(char_u *p);
++ int path_with_url(char_u *fname);
++ int vim_isAbsName(char_u *name);
++ int vim_FullName(char_u *fname, char_u *buf, int len, int force);
+ /* vim: set ft=c : */
+*** ../vim-8.1.0913/src/proto/misc2.pro 2019-01-13 23:38:33.407773189 +0100
+--- src/proto/misc2.pro 2019-02-13 22:07:27.497320021 +0100
+***************
+*** 90,103 ****
+ char *parse_shape_opt(int what);
+ int get_shape_idx(int mouse);
+ void update_mouseshape(int shape_idx);
+- void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int level, int free_visited, int find_what, void *search_ctx_arg, int tagfile, char_u *rel_fname);
+- char_u *vim_findfile_stopdir(char_u *buf);
+- void vim_findfile_cleanup(void *ctx);
+- char_u *vim_findfile(void *search_ctx_arg);
+- void vim_findfile_free_visited(void *search_ctx_arg);
+- char_u *find_file_in_path(char_u *ptr, int len, int options, int first, char_u *rel_fname);
+- char_u *find_directory_in_path(char_u *ptr, int len, int options, char_u *rel_fname);
+- char_u *find_file_in_path_option(char_u *ptr, int len, int options, int first, char_u *path_option, int find_what, char_u *rel_fname, char_u *suffixes);
+ int vim_chdir(char_u *new_dir);
+ int get_user_name(char_u *buf, int len);
+ void sort_strings(char_u **files, int count);
+--- 90,95 ----
+*** ../vim-8.1.0913/src/proto/window.pro 2019-02-10 22:58:58.976414779 +0100
+--- src/proto/window.pro 2019-02-13 22:07:31.661288452 +0100
+***************
+*** 65,77 ****
+ void command_height(void);
+ void last_status(int morewin);
+ int tabline_height(void);
+- char_u *grab_file_name(long count, linenr_T *file_lnum);
+- char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum);
+- char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u *rel_fname, linenr_T *file_lnum);
+- char_u *find_file_name_in_path(char_u *ptr, int len, int options, long count, char_u *rel_fname);
+- int path_with_url(char_u *fname);
+- int vim_isAbsName(char_u *name);
+- int vim_FullName(char_u *fname, char_u *buf, int len, int force);
+ int min_rows(void);
+ int only_one_window(void);
+ void check_lnums(int do_curwin);
+--- 65,70 ----
+*** ../vim-8.1.0913/src/window.c 2019-02-10 22:58:58.980414768 +0100
+--- src/window.c 2019-02-13 22:06:27.641777652 +0100
+***************
+*** 9,15 ****
+
+ #include "vim.h"
+
+- static int path_is_url(char_u *p);
+ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, long Prenum);
+ static void win_init(win_T *newp, win_T *oldp, int flags);
+ static void win_init_some(win_T *newp, win_T *oldp);
+--- 9,14 ----
+***************
+*** 61,69 ****
+
+ static win_T *win_alloc(win_T *after, int hidden);
+
+- #define URL_SLASH 1 /* path_is_url() has found "://" */
+- #define URL_BACKSLASH 2 /* path_is_url() has found ":\\" */
+-
+ #define NOWIN (win_T *)-1 /* non-existing window */
+
+ #define ROWS_AVAIL (Rows - p_ch - tabline_height())
+--- 60,65 ----
+***************
+*** 6098,6414 ****
+ return 1;
+ }
+
+- #if defined(FEAT_SEARCHPATH) || defined(PROTO)
+- /*
+- * Get the file name at the cursor.
+- * If Visual mode is active, use the selected text if it's in one line.
+- * Returns the name in allocated memory, NULL for failure.
+- */
+- char_u *
+- grab_file_name(long count, linenr_T *file_lnum)
+- {
+- int options = FNAME_MESS|FNAME_EXP|FNAME_REL|FNAME_UNESC;
+-
+- if (VIsual_active)
+- {
+- int len;
+- char_u *ptr;
+-
+- if (get_visual_text(NULL, &ptr, &len) == FAIL)
+- return NULL;
+- return find_file_name_in_path(ptr, len, options,
+- count, curbuf->b_ffname);
+- }
+- return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
+- }
+-
+- /*
+- * Return the file name under or after the cursor.
+- *
+- * The 'path' option is searched if the file name is not absolute.
+- * The string returned has been alloc'ed and should be freed by the caller.
+- * NULL is returned if the file name or file is not found.
+- *
+- * options:
+- * FNAME_MESS give error messages
+- * FNAME_EXP expand to path
+- * FNAME_HYP check for hypertext link
+- * FNAME_INCL apply "includeexpr"
+- */
+- char_u *
+- file_name_at_cursor(int options, long count, linenr_T *file_lnum)
+- {
+- return file_name_in_line(ml_get_curline(),
+- curwin->w_cursor.col, options, count, curbuf->b_ffname,
+- file_lnum);
+- }
+-
+- /*
+- * Return the name of the file under or after ptr[col].
+- * Otherwise like file_name_at_cursor().
+- */
+- char_u *
+- file_name_in_line(
+- char_u *line,
+- int col,
+- int options,
+- long count,
+- char_u *rel_fname, /* file we are searching relative to */
+- linenr_T *file_lnum) /* line number after the file name */
+- {
+- char_u *ptr;
+- int len;
+- int in_type = TRUE;
+- int is_url = FALSE;
+-
+- /*
+- * search forward for what could be the start of a file name
+- */
+- ptr = line + col;
+- while (*ptr != NUL && !vim_isfilec(*ptr))
+- MB_PTR_ADV(ptr);
+- if (*ptr == NUL) /* nothing found */
+- {
+- if (options & FNAME_MESS)
+- emsg(_("E446: No file name under cursor"));
+- return NULL;
+- }
+-
+- /*
+- * Search backward for first char of the file name.
+- * Go one char back to ":" before "//" even when ':' is not in 'isfname'.
+- */
+- while (ptr > line)
+- {
+- if (has_mbyte && (len = (*mb_head_off)(line, ptr - 1)) > 0)
+- ptr -= len + 1;
+- else if (vim_isfilec(ptr[-1])
+- || ((options & FNAME_HYP) && path_is_url(ptr - 1)))
+- --ptr;
+- else
+- break;
+- }
+-
+- /*
+- * Search forward for the last char of the file name.
+- * Also allow "://" when ':' is not in 'isfname'.
+- */
+- len = 0;
+- while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
+- || ((options & FNAME_HYP) && path_is_url(ptr + len))
+- || (is_url && vim_strchr((char_u *)"?&=", ptr[len]) != NULL))
+- {
+- /* After type:// we also include ?, & and = as valid characters, so that
+- * http://google.com?q=this&that=ok works. */
+- if ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z'))
+- {
+- if (in_type && path_is_url(ptr + len + 1))
+- is_url = TRUE;
+- }
+- else
+- in_type = FALSE;
+-
+- if (ptr[len] == '\\')
+- /* Skip over the "\" in "\ ". */
+- ++len;
+- if (has_mbyte)
+- len += (*mb_ptr2len)(ptr + len);
+- else
+- ++len;
+- }
+-
+- /*
+- * If there is trailing punctuation, remove it.
+- * But don't remove "..", could be a directory name.
+- */
+- if (len > 2 && vim_strchr((char_u *)".,:;!", ptr[len - 1]) != NULL
+- && ptr[len - 2] != '.')
+- --len;
+-
+- if (file_lnum != NULL)
+- {
+- char_u *p;
+-
+- /* Get the number after the file name and a separator character */
+- p = ptr + len;
+- p = skipwhite(p);
+- if (*p != NUL)
+- {
+- if (!isdigit(*p))
+- ++p; /* skip the separator */
+- p = skipwhite(p);
+- if (isdigit(*p))
+- *file_lnum = (int)getdigits(&p);
+- }
+- }
+-
+- return find_file_name_in_path(ptr, len, options, count, rel_fname);
+- }
+-
+- # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
+- static char_u *
+- eval_includeexpr(char_u *ptr, int len)
+- {
+- char_u *res;
+-
+- set_vim_var_string(VV_FNAME, ptr, len);
+- res = eval_to_string_safe(curbuf->b_p_inex, NULL,
+- was_set_insecurely((char_u *)"includeexpr", OPT_LOCAL));
+- set_vim_var_string(VV_FNAME, NULL, 0);
+- return res;
+- }
+- #endif
+-
+- /*
+- * Return the name of the file ptr[len] in 'path'.
+- * Otherwise like file_name_at_cursor().
+- */
+- char_u *
+- find_file_name_in_path(
+- char_u *ptr,
+- int len,
+- int options,
+- long count,
+- char_u *rel_fname) /* file we are searching relative to */
+- {
+- char_u *file_name;
+- int c;
+- # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
+- char_u *tofree = NULL;
+-
+- if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL)
+- {
+- tofree = eval_includeexpr(ptr, len);
+- if (tofree != NULL)
+- {
+- ptr = tofree;
+- len = (int)STRLEN(ptr);
+- }
+- }
+- # endif
+-
+- if (options & FNAME_EXP)
+- {
+- file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
+- TRUE, rel_fname);
+-
+- # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
+- /*
+- * If the file could not be found in a normal way, try applying
+- * 'includeexpr' (unless done already).
+- */
+- if (file_name == NULL
+- && !(options & FNAME_INCL) && *curbuf->b_p_inex != NUL)
+- {
+- tofree = eval_includeexpr(ptr, len);
+- if (tofree != NULL)
+- {
+- ptr = tofree;
+- len = (int)STRLEN(ptr);
+- file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
+- TRUE, rel_fname);
+- }
+- }
+- # endif
+- if (file_name == NULL && (options & FNAME_MESS))
+- {
+- c = ptr[len];
+- ptr[len] = NUL;
+- semsg(_("E447: Can't find file \"%s\" in path"), ptr);
+- ptr[len] = c;
+- }
+-
+- /* Repeat finding the file "count" times. This matters when it
+- * appears several times in the path. */
+- while (file_name != NULL && --count > 0)
+- {
+- vim_free(file_name);
+- file_name = find_file_in_path(ptr, len, options, FALSE, rel_fname);
+- }
+- }
+- else
+- file_name = vim_strnsave(ptr, len);
+-
+- # if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
+- vim_free(tofree);
+- # endif
+-
+- return file_name;
+- }
+- #endif /* FEAT_SEARCHPATH */
+-
+- /*
+- * Check if the "://" of a URL is at the pointer, return URL_SLASH.
+- * Also check for ":\\", which MS Internet Explorer accepts, return
+- * URL_BACKSLASH.
+- */
+- static int
+- path_is_url(char_u *p)
+- {
+- if (STRNCMP(p, "://", (size_t)3) == 0)
+- return URL_SLASH;
+- else if (STRNCMP(p, ":\\\\", (size_t)3) == 0)
+- return URL_BACKSLASH;
+- return 0;
+- }
+-
+- /*
+- * Check if "fname" starts with "name://". Return URL_SLASH if it does.
+- * Return URL_BACKSLASH for "name:\\".
+- * Return zero otherwise.
+- */
+- int
+- path_with_url(char_u *fname)
+- {
+- char_u *p;
+-
+- for (p = fname; isalpha(*p); ++p)
+- ;
+- return path_is_url(p);
+- }
+-
+- /*
+- * Return TRUE if "name" is a full (absolute) path name or URL.
+- */
+- int
+- vim_isAbsName(char_u *name)
+- {
+- return (path_with_url(name) != 0 || mch_isFullName(name));
+- }
+-
+- /*
+- * Get absolute file name into buffer "buf[len]".
+- *
+- * return FAIL for failure, OK otherwise
+- */
+- int
+- vim_FullName(
+- char_u *fname,
+- char_u *buf,
+- int len,
+- int force) /* force expansion even when already absolute */
+- {
+- int retval = OK;
+- int url;
+-
+- *buf = NUL;
+- if (fname == NULL)
+- return FAIL;
+-
+- url = path_with_url(fname);
+- if (!url)
+- retval = mch_FullName(fname, buf, len, force);
+- if (url || retval == FAIL)
+- {
+- /* something failed; use the file name (truncate when too long) */
+- vim_strncpy(buf, fname, len - 1);
+- }
+- #if defined(MSWIN)
+- slash_adjust(buf);
+- #endif
+- return retval;
+- }
+-
+ /*
+ * Return the minimal number of rows that is needed on the screen to display
+ * the current number of windows.
+--- 6094,6099 ----
+*** ../vim-8.1.0913/src/version.c 2019-02-13 21:47:32.961109662 +0100
+--- src/version.c 2019-02-13 22:04:54.862441927 +0100
+***************
+*** 785,786 ****
+--- 785,788 ----
+ { /* Add new patch number below this line */
++ /**/
++ 914,
+ /**/
+
+
+--
+./configure
+Checking whether build environment is sane ...
+build environment is grinning and holding a spatula. Guess not.
+
+ /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\
+/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
+\\\ an exciting new programming language -- http://www.Zimbu.org ///
+ \\\ help me help AIDS victims -- http://ICCF-Holland.org ///