[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: feature request: please add a zsh-AUTO_REMOVE_SLASH-like option

From: Paul Eggert
Subject: Re: feature request: please add a zsh-AUTO_REMOVE_SLASH-like option
Date: Tue, 22 May 2001 16:32:40 -0700 (PDT)

> From: Jim Meyering <address@hidden>
> Date: 20 May 2001 21:45:54 +0200
> `rm -r some-directory/' will fail because POSIX says it must be
> treated as being equivalent to `rm -r some-directory/.', and we
> can't remove `.' or `..'.  So it'd sure be nice if there were a way
> to continue using Bash's file name completion on a directory that
> I'm about to remove, without manually having to erase that trailing
> slash or to use a --strip-trailing-slashes option like I added to cp
> and mv (now in fileutils-4.1).
> Here's my original request:
>   http://mail.gnu.org/pipermail/bug-bash/2000-January/000220.html

How about the following idea instead?  It gives you the functionality
of zsh's AUTO_REMOVE_SLASH option, without requiring an extra option.
The idea is that, when encountering a symbolic link to a directory,
Bash should not append either '/' or ' ' unless the user asks for
completion after the name has already been completed.

The basic idea is that symbolic links to directories are special.
Sometimes you want the symbolic link, and sometimes you want the
pointed-to directory.  Since completion should not make any guesses
for you when there is more than one possibility, Bash should not guess
that you want the directory.  It should be up to you to choose.

For example, suppose we have the following scenario in an
otherwise-empty directory:

   $ set -o emacs
   $ mkdir directory
   $ ln -s directory symlink
   $ ls -l sym<TAB>

Under my proposal, Bash will complete the command line so that it
reads "ls symlink", without any space or "/".  If the user types TAB
again, Bash will further complete the command line so that it reads
"ls symlink/".  The distinction is important, since "ls -l symlink"
behaves quite differently from "ls -l symlink/".

If this idea is implemented, utilities needn't have
--strip-trailing-slashes options; that will save quite a bit of hassle.

Here's a Bash 2.05 patch to implement this idea.

2001-05-22  Paul Eggert  <address@hidden>

        * lib/readline/complete.c: Do not append "/" or " " when
        completing a symbolic link to a directory, unless the
        completion doesn't add anything.

        (LSTAT): New macro.
        (append_to_match) Accept extra argument NONTRIVIAL_MATCH.  All callers
        changed.  Do not append "/" or space if we encounter a symbolic link
        to a directory and if the match is nontrivial.
        (rl_complete_internal): A match is trivial if it doesn't append any
        characters other than what's already in the buffer.

RCS file: lib/readline/complete.c,v
retrieving revision 2.5
retrieving revision
diff -pu -r2.5 -r2.5.0.1
--- lib/readline/complete.c     2001/02/14 12:47:18     2.5
+++ lib/readline/complete.c     2001/05/22 23:09:28
@@ -93,7 +93,7 @@ static char *rl_quote_filename __P((char
 static char **remove_duplicate_matches __P((char **));
 static void insert_match __P((char *, int, int, char *));
-static int append_to_match __P((char *, int, int));
+static int append_to_match __P((char *, int, int, int));
 static void insert_all_matches __P((char **, int, char *));
 static void display_matches __P((char **));
 static int compute_lcd_of_matches __P((char **, int, const char *));
@@ -1098,18 +1098,24 @@ insert_match (match, start, mtype, qc)
+#if defined (HAVE_LSTAT)
+#  define LSTAT lstat
+#  define LSTAT stat
 /* Append any necessary closing quote and a separator character to the
    just-inserted match.  If the user has specified that directories
    should be marked by a trailing `/', append one of those instead.  The
    default trailing character is a space.  Returns the number of characters
    appended. */
 static int
-append_to_match (text, delimiter, quote_char)
+append_to_match (text, delimiter, quote_char, nontrivial_match)
      char *text;
-     int delimiter, quote_char;
+     int delimiter, quote_char, nontrivial_match;
   char temp_string[4], *filename;
-  int temp_string_index;
+  int temp_string_index, stat_return;
   struct stat finfo;
   temp_string_index = 0;
@@ -1126,11 +1132,19 @@ append_to_match (text, delimiter, quote_
   if (rl_filename_completion_desired)
       filename = tilde_expand (text);
-      if (stat (filename, &finfo) == 0 && S_ISDIR (finfo.st_mode))
+      stat_return = (nontrivial_match
+                    ? LSTAT (filename, &finfo)
+                    : stat (filename, &finfo));
+      if (stat_return == 0 && S_ISDIR (finfo.st_mode))
          if (_rl_complete_mark_directories && rl_line_buffer[rl_point] != '/')
            rl_insert_text ("/");
+#if defined (S_ISLNK)
+      else if (stat_return == 0 && S_ISLNK (finfo.st_mode)
+              && stat (filename, &finfo) == 0 && S_ISDIR (finfo.st_mode))
+       ;
          if (rl_point == rl_end)
@@ -1210,7 +1224,7 @@ rl_complete_internal (what_to_do)
   char **matches;
   rl_compentry_func_t *our_func;
-  int start, end, delimiter, found_quote, i;
+  int start, end, delimiter, found_quote, i, nontrivial_lcd;
   char *text, *saved_line_buffer;
   char quote_char;
@@ -1240,6 +1254,7 @@ rl_complete_internal (what_to_do)
   text = rl_copy_text (start, end);
   matches = gen_completion_matches (text, start, end, our_func, found_quote, 
+  nontrivial_lcd = matches && strcmp (text, matches[0]) != 0;
   free (text);
   if (matches == 0)
@@ -1291,7 +1306,7 @@ rl_complete_internal (what_to_do)
            rl_ding (); /* There are other matches remaining. */
-       append_to_match (matches[0], delimiter, quote_char);
+       append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd);
@@ -1745,7 +1760,8 @@ rl_menu_complete (count, ignore)
       insert_match (matches[match_list_index], orig_start, SINGLE_MATCH, 
-      append_to_match (matches[match_list_index], delimiter, quote_char);
+      append_to_match (matches[match_list_index], delimiter, quote_char,
+                      strcmp (orig_text, matches[match_list_index]));
   completion_changed_buffer = 1;

reply via email to

[Prev in Thread] Current Thread [Next in Thread]