On Fri, Feb 20, 2009 at 9:22 PM, Nazri Ramliy <[email protected]> wrote:
> There's one major outstanding issue that I'm aware of - it does not
> skip symbolic links. This may result in infinite cycle if there exists
> a symbolic link that creates a cycle in the file system.

Attached patch makes it skip symbolic links to directory on unix platform.
I don't know how to do the same for other platforms (amiga?/windows?),
sorry :)

> I don't have access to a windows platform so I can't test the patch on
> windows.  It may have problems with those different drive letters.

Upon further thoughts (on my limited brain capacity :), I think that it won't
have any problem when doing the expansion and uniquefying of the files
in the 'path' option.

For example with 'path' is set to C:/**,D:/**, doing ":find file1<tab>",
matching files like

    C:\a\file1.txt and
    D:\a\file1.txt

would be listed using their full path names in the wildmenu listing while
files like

    C:\project1\a\file1.txt and
    D:\project2\a\file2.txt

would be shortened to

    project1\a\file1.txt and
    project2\a\file2.txt

If anybody would verify that it works fine on windows platform (and/or any
other platform for that matter), that would be nice.

I've sort of stress-tested this feature by setting my path to /** (the
whole root
directory, up to the default 30 directories deep), and then searching for some
known file using part of its filename. I did an strace on the vim process (to
see the chdir system calls that it made) and also looked at its memory usage
in top during the whole searching process. All seemed to be fine.

nazri.

--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Index: ex_docmd.c
===================================================================
--- ex_docmd.c	(revision 1390)
+++ ex_docmd.c	(working copy)
@@ -3422,6 +3422,11 @@
  */
     switch (ea.cmdidx)
     {
+	case CMD_find:
+	case CMD_sfind:
+	case CMD_tabfind:
+	    xp->xp_context = EXPAND_FILES_IN_PATH;
+	    break;
 	case CMD_cd:
 	case CMD_chdir:
 	case CMD_lcd:
Index: ex_getln.c
===================================================================
--- ex_getln.c	(revision 1390)
+++ ex_getln.c	(working copy)
@@ -4063,6 +4063,7 @@
     char_u	*tail;
 
     if (context != EXPAND_FILES
+	    && context != EXPAND_FILES_IN_PATH
 	    && context != EXPAND_SHELLCMD
 	    && context != EXPAND_DIRECTORIES)
     {
@@ -4377,7 +4378,9 @@
     if (options & WILD_SILENT)
 	flags |= EW_SILENT;
 
-    if (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_DIRECTORIES)
+    if (xp->xp_context == EXPAND_FILES
+	    || xp->xp_context == EXPAND_DIRECTORIES
+	    || xp->xp_context == EXPAND_FILES_IN_PATH)
     {
 	/*
 	 * Expand file or directory names.
@@ -4407,6 +4410,8 @@
 
 	if (xp->xp_context == EXPAND_FILES)
 	    flags |= EW_FILE;
+	else if (xp->xp_context == EXPAND_FILES_IN_PATH)
+	    flags |= (EW_FILE | EW_PATH);
 	else
 	    flags = (flags | EW_DIR) & ~EW_FILE;
 	ret = expand_wildcards(1, &pat, num_file, file, flags);
Index: misc1.c
===================================================================
--- misc1.c	(revision 1390)
+++ misc1.c	(working copy)
@@ -9037,6 +9037,9 @@
 	    if ((dp->d_name[0] != '.' || starts_with_dot)
 		    && vim_regexec(&regmatch, (char_u *)dp->d_name, (colnr_T)0))
 	    {
+		if (flags & EW_NOSYMLINK && dp->d_type == DT_LNK)
+		    continue;
+
 		STRCPY(s, dp->d_name);
 		len = STRLEN(buf);
 
@@ -9097,7 +9100,254 @@
 }
 #endif
 
+#if defined(FEAT_SEARCHPATH)
+/* 
+ * Expand the files matching pattern, starting from the given path, save
+ * the matches in their equivalent fullpath.
+ */
+    static int
+expand_to_fullpath(path, gap, pattern, flags)
+    char_u	*path;
+    garray_T	*gap;
+    char_u	*pattern;
+    int		flags;		/* EW_* flags */
+{
+    int	    i;
+    int	    c;
+    int	    old_len;
+    int	    new_len;
+    char_u  **files;
+    char_u  *fullpath;
+
+    old_len = gap->ga_len;
+    c = mch_expandpath(gap, pattern, flags);
+
+    files = (gap->ga_data != NULL) ? (char_u **)gap->ga_data : (char_u **)"";
+    new_len = gap->ga_len;
+
+    for(i = old_len; i < new_len; i++)
+    {
+	fullpath = concat_fnames(path, files[i], TRUE);
+	vim_free(files[i]);
+	files[i] = fullpath;
+    }
+    return c;
+}
+
 /*
+ * Moves psep to the previous path separator in path, starting from the
+ * end of path. Returns FAIL is psep ends up at the beginning of path.
+ */
+    static int
+find_previous_pathsep(path, psep)
+    char_u *path;
+    char_u **psep;
+{
+    /* 
+     * As we're looking for the previous path separator, skip the current
+     * separator.
+     */ 
+    if (vim_ispathsep(**psep))
+	(*psep)--;
+
+    while (*psep >= path && !vim_ispathsep(**psep))
+	(*psep)--;
+
+    if (*psep != path && vim_ispathsep(**psep))
+	return OK;
+
+    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(maybe_unique, gap, i)
+    char_u	*maybe_unique;
+    garray_T	*gap;
+    int		i;
+{
+    int	    j;
+    int	    candidate_len;
+    int	    other_path_len;
+    char_u  *rival;
+    char_u  **other_paths;
+    
+    other_paths = (gap->ga_data != NULL) ? (char_u **)gap->ga_data : (char_u **)"";
+
+    for (j = 0; j < gap->ga_len && !got_int; j++)
+    {
+	/* Don't compare it with itself */
+	if(j == i)
+	    continue;
+
+	candidate_len = STRLEN(maybe_unique);
+	other_path_len = STRLEN(other_paths[j]);
+
+	if(other_path_len < candidate_len)
+	    /* It's different, */
+	    continue;
+
+	rival = other_paths[j] + other_path_len - candidate_len;
+
+	if (fnamecmp(maybe_unique, rival) == 0)
+	    return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+ * Sorts, removes duplicates and modifies all the fullpath names in gap so that
+ * they are unique with respect to each other. Beware, this is at least O(n^2)
+ * wrt gap->ga_len.
+ */
+    static void
+uniquefy_paths(gap)
+    garray_T *gap;
+{
+    int	    i;
+    int	    path_len;
+    char_u  *pathsep_p;
+    char_u  *path;
+    char_u  **fnames = (char_u **) gap->ga_data;
+
+    int j;
+    int sort_again = 0;
+
+    /* Remove duplicate entries */
+    sort_strings(fnames, gap->ga_len);
+    for (i = 0; i < gap->ga_len - 1; i++)
+	if (fnamecmp(fnames[i], fnames[i+1]) == 0)
+	{
+	    vim_free(fnames[i]);
+	    for (j = i+1; j < gap->ga_len; j++)
+		fnames[j-1] = fnames[j];
+	    gap->ga_len--;
+	    i--;
+	}
+
+    for (i = 0; i < gap->ga_len; i++)
+    {
+	path = fnames[i];
+	path_len = STRLEN(path);
+
+	/* We start at the end of the path */
+	pathsep_p = path + path_len - 1;
+
+	while (find_previous_pathsep(path, &pathsep_p))
+	    if (is_unique(pathsep_p, gap, i))
+	    {
+		sort_again = 1;
+		mch_memmove(path, pathsep_p + 1, STRLEN(pathsep_p));
+		break;
+	    }
+    }
+
+    if (sort_again)
+	sort_strings(fnames, gap->ga_len);
+}
+
+/* 
+ * Calls mch_expandpath for each 'path' values for the given pattern and stores
+ * the result in gap. Returns the total number of match.
+ */
+    static int
+expand_in_path(gap, pattern, flags)
+    garray_T	*gap;
+    char_u	*pattern;
+    int		flags;		/* EW_* flags */
+{
+    /*
+     * For each expandable 'path' option values we get the list of all
+     * directories and expand the file names in each the directories.
+     */
+    int	    c = 0;
+    char_u  *cwd = NULL;
+    char_u  *cwd_orig = NULL;
+    char_u  *path_opt;
+    char_u  *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
+
+    if ((cwd_orig = alloc((int)(MAXPATHL))) == NULL ||
+	    (cwd = alloc((int)(MAXPATHL))) == NULL ||
+	    (path_opt = alloc((int)(MAXPATHL))) == NULL)
+	return 0; /* FIXME Give emsg instead? */
+
+    if (getcwd((char *)cwd_orig, MAXPATHL) == NULL)
+	/* FIXME The directory may have been deleted, is it ok to ignore it? */
+	;
+
+    /* 
+     * Search for matching files in each 'path' options.
+     * TODO Detect circular paths (symbolic links to path)
+     */
+    for(;;)
+    {
+	int		i;
+	int		dir_count;
+	garray_T	ga_dirs;
+
+	path_opt[0] = 0;
+	copy_option_part(&path_option, path_opt, MAXPATHL, " ;,");
+
+	STRCPY(cwd, cwd_orig);
+
+	if (mch_isFullName(path_opt))
+	    STRCPY(cwd, path_opt);
+	else
+	{
+	    char_u *fullpath;
+
+	    fullpath = concat_fnames(cwd_orig, path_opt, TRUE);
+	    if (fullpath == NULL)
+		continue; /* FIXME Give emsg too? */
+
+	    STRNCPY(cwd, fullpath, MAXPATHL);
+	    vim_free(fullpath);
+	}
+
+	ga_init2(&ga_dirs, (int)sizeof(char_u *), 30);
+	dir_count = mch_expandpath(&ga_dirs, cwd, EW_DIR|EW_NOSYMLINK|EW_SILENT);
+
+	if (dir_count == 0)
+	{
+	    if (mch_chdir(cwd) == 0)
+		c += expand_to_fullpath(cwd, gap, pattern, flags);
+	}
+	else 
+	{
+	    /* Add files from each expanded path. */
+	    int	    i;
+	    char_u  **tmpdirs;
+	    tmpdirs = (ga_dirs.ga_data != NULL) ? (char_u **)ga_dirs.ga_data 
+							      : (char_u **)"";
+	    for (i = 0; i < dir_count && !got_int ; i++)
+	    {
+		/* FIXME Skip symbolic links to avoid cycle */
+		if (mch_chdir(tmpdirs[i]) == 0)
+		    c += expand_to_fullpath(tmpdirs[i], gap, pattern, flags);
+	    }
+	}
+	ga_clear_strings(&ga_dirs);
+
+	if (path_option == NULL || *path_option == NUL)
+	    break;
+    }
+
+    if (mch_chdir(cwd_orig) != 0)
+	/*  FIXME Is it safe to ignore this? */ ;
+
+    vim_free(path_opt);
+    vim_free(cwd);
+    vim_free(cwd_orig);
+
+    return c;
+}
+#endif
+
+/*
  * Generic wildcard expansion code.
  *
  * Characters in "pat" that should not be expanded must be preceded with a
@@ -9205,7 +9455,14 @@
 	     * when EW_NOTFOUND is given.
 	     */
 	    if (mch_has_exp_wildcard(p))
-		add_pat = mch_expandpath(&ga, p, flags);
+	    {
+#if defined(FEAT_SEARCHPATH)
+		if (*p != '.' && !vim_ispathsep(*p) && flags & EW_PATH)
+		    add_pat = expand_in_path(&ga, p, flags);
+		else
+#endif
+		    add_pat = mch_expandpath(&ga, p, flags);
+	    }
 	}
 
 	if (add_pat == -1 || (add_pat == 0 && (flags & EW_NOTFOUND)))
@@ -9228,6 +9485,11 @@
 	    vim_free(p);
     }
 
+#if defined(FEAT_SEARCHPATH)
+    if (flags & EW_PATH)
+	uniquefy_paths(&ga);
+#endif
+
     *num_file = ga.ga_len;
     *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data : (char_u **)"";
 
Index: vim.h
===================================================================
--- vim.h	(revision 1390)
+++ vim.h	(working copy)
@@ -708,6 +708,7 @@
 #define EXPAND_USER_DEFINED	30
 #define EXPAND_USER_LIST	31
 #define EXPAND_SHELLCMD		32
+#define EXPAND_FILES_IN_PATH    33
 
 /* Values for exmode_active (0 is no exmode) */
 #define EXMODE_NORMAL		1
@@ -739,6 +740,8 @@
 #define EW_KEEPALL	0x10	/* keep all matches */
 #define EW_SILENT	0x20	/* don't print "1 returned" from shell */
 #define EW_EXEC		0x40	/* executable files */
+#define EW_PATH		0x80	/* search in 'path' too */
+#define EW_NOSYMLINK   0x100	/* skip symlink */
 /* Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND
  * is used when executing commands and EW_SILENT for interactive expanding. */
 

Raspunde prin e-mail lui