diff options
Diffstat (limited to 'data/vim/patches/8.1.0914')
-rw-r--r-- | data/vim/patches/8.1.0914 | 6010 |
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(®match, 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(®match, 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(®match, 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(®match, 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 /// |