On Sun, Jun 26, 2011 at 05:42:31PM +0200, Raphaël Droz wrote:
> Attached is an updated version of "dont-bug-command-name-completion.patch".
(really) attached
commit b3572793f7e77bf3ccb4e58121aabe457ddbe39c
Author: Raphaël Droz <raphael.droz+fl...@gmail.com>
Date:   Sat Jun 25 22:55:11 2011 +0200

    Colored completion, bugfix (8/6).
    
    When a filename completion needs $PATH traversal (like the
    completion of a command name), don't hide the absolute path
    of matches to readline.
    Fewer matches will be considered as missing files when completing
    a command name.

diff --git a/bashline.c b/bashline.c
index 692b912..b2d2e06 100644
--- a/bashline.c
+++ b/bashline.c
@@ -1899,12 +1899,20 @@ globword:
         bash execution code won't find executables in directories which
         appear in directories in $PATH when they're specified using
         relative pathnames. */
-      if (match && (searching_path ? executable_file (val) : 
executable_or_directory (val)))
+      int is_executable_file = 0;
+      if (match && (searching_path ? ( is_executable_file = executable_file 
(val) ) : executable_or_directory (val)))
 #endif
        {
-         free (val);
-         val = "";             /* So it won't be NULL. */
-         return (temp);
+         if(is_executable_file) {
+           rl_executable_completion_desired = 1;
+           free (temp);
+           return val;
+         }
+         else {
+           free (val);
+           val = "";           /* So it won't be NULL. */
+           return (temp);
+         }
        }
       else
        {
diff --git a/lib/readline/complete.c b/lib/readline/complete.c
index 661a5fa..e534bfe 100644
--- a/lib/readline/complete.c
+++ b/lib/readline/complete.c
@@ -317,6 +317,15 @@ int rl_ignore_completion_duplicates = 1;
    within a completion entry finder function. */
 int rl_filename_completion_desired = 0;
 
+/* Non-zero means that at least one of the results of the matches is
+   an absolute path.  This is ALWAYS zero on entry, and can only be changed
+   within the entry_function callback given to rl_completion_matches.
+   If it is set to a non-zero value the lower common denominator computation
+   will take care of stripping directory names each filename match belongs to.
+   The matches should expect to be processed by printable_part().
+   print_filename() will receive a correct full_pathname parameter. */
+int rl_executable_completion_desired = 0;
+
 /* Non-zero means that the results of the matches are to be quoted using
    double quotes (or an application-specific quoting mechanism) if the
    filename contains any characters in rl_filename_quote_chars.  This is
@@ -1144,6 +1153,17 @@ gen_completion_matches (text, start, end, our_func, 
found_quote, quote_char)
   return matches;  
 }
 
+/* Used by compute_lcd_of_matches() and remove_duplicate_matches()
+   when, during the lower common denominator calculation,
+   we have interest in stripping a possible directory part of an
+   absolute path (= if rl_executable_completion_desired > 0). */
+static char *possibly_strip_dirname(char *match) {
+  if( rl_executable_completion_desired )
+    return printable_part(match);
+  else
+    return match;
+}
+
 /* Filter out duplicates in MATCHES.  This frees up the strings in
    MATCHES. */
 static char **
@@ -1161,15 +1181,19 @@ remove_duplicate_matches (matches)
 
   /* Sort the array without matches[0], since we need it to
      stay in place no matter what. */
-  if (i && rl_sort_completion_matches)
-    qsort (matches+1, i-1, sizeof (char *), (QSFUNC 
*)_rl_qsort_string_compare);
+  if (i && rl_sort_completion_matches) {
+    if(rl_executable_completion_desired)
+      qsort (matches+1, i-1, sizeof (char *), (QSFUNC 
*)_rl_qsort_basename_string_compare);
+    else
+      qsort (matches+1, i-1, sizeof (char *), (QSFUNC 
*)_rl_qsort_string_compare);
+  }
 
   /* Remember the lowest common denominator for it may be unique. */
   lowest_common = savestring (matches[0]);
 
   for (i = newlen = 0; matches[i + 1]; i++)
     {
-      if (strcmp (matches[i], matches[i + 1]) == 0)
+      if (strcmp (possibly_strip_dirname(matches[i]), 
possibly_strip_dirname(matches[i + 1])) == 0)
        {
          xfree (matches[i]);
          matches[i] = (char *)&dead_slot;
@@ -1202,6 +1226,10 @@ remove_duplicate_matches (matches)
       xfree (temp_array[1]);
       temp_array[1] = (char *)NULL;
     }
+  else if (j == 2 && rl_executable_completion_desired ) {
+    strcpy (temp_array[0], printable_part(temp_array[1]));
+    temp_array[1] = (char *)NULL;
+  }
   return (temp_array);
 }
 
@@ -1227,7 +1255,19 @@ compute_lcd_of_matches (match_list, matches, text)
      stop matching. */
   if (matches == 1)
     {
-      match_list[0] = match_list[1];
+      /* command name completion requested but one match only was found.
+        We forget (and reverse) about the absolute path.
+        See bash:command_word_completion_function(). */
+      if(rl_executable_completion_desired == 1) {
+       char *temp = printable_part(match_list[1]);
+       match_list[0] = (char *)xmalloc (strlen (temp) + 1);
+       strcpy (match_list[0], temp);
+       xfree (match_list[1]);
+       /* this simpler alternative causes xfree() problems */
+       //match_list[0] = printable_part(match_list[1]);
+      } else {
+       match_list[0] = match_list[1];
+      }
       match_list[1] = (char *)NULL;
       return 1;
     }
@@ -1244,14 +1284,14 @@ compute_lcd_of_matches (match_list, matches, text)
       if (_rl_completion_case_fold)
        {
          for (si = 0;
-              (c1 = _rl_to_lower(match_list[i][si])) &&
-              (c2 = _rl_to_lower(match_list[i + 1][si]));
+              (c1 = _rl_to_lower(possibly_strip_dirname(match_list[i])[si])) &&
+              (c2 = _rl_to_lower(possibly_strip_dirname(match_list[i + 
1])[si]));
               si++)
 #if defined (HANDLE_MULTIBYTE)
            if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
              {
-               v = mbrtowc (&wc1, match_list[i]+si, strlen (match_list[i]+si), 
&ps1);
-               mbrtowc (&wc2, match_list[i+1]+si, strlen (match_list[i+1]+si), 
&ps2);
+               v = mbrtowc (&wc1, possibly_strip_dirname(match_list[i])+si, 
strlen (possibly_strip_dirname(match_list[i])+si), &ps1);
+               mbrtowc (&wc2, possibly_strip_dirname(match_list[i+1])+si, 
strlen (possibly_strip_dirname(match_list[i+1])+si), &ps2);
                wc1 = towlower (wc1);
                wc2 = towlower (wc2);
                if (wc1 != wc2)
@@ -1267,17 +1307,17 @@ compute_lcd_of_matches (match_list, matches, text)
       else
        {
          for (si = 0;
-              (c1 = match_list[i][si]) &&
-              (c2 = match_list[i + 1][si]);
+              (c1 = possibly_strip_dirname(match_list[i])[si]) &&
+              (c2 = possibly_strip_dirname(match_list[i + 1])[si]);
               si++)
 #if defined (HANDLE_MULTIBYTE)
            if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
              {
                mbstate_t ps_back;
                ps_back = ps1;
-               if (!_rl_compare_chars (match_list[i], si, &ps1, 
match_list[i+1], si, &ps2))
+               if (!_rl_compare_chars (possibly_strip_dirname(match_list[i]), 
si, &ps1, possibly_strip_dirname(match_list[i+1]), si, &ps2))
                  break;
-               else if ((v = _rl_get_char_len (&match_list[i][si], &ps_back)) 
> 1)
+               else if ((v = _rl_get_char_len 
(&(possibly_strip_dirname(match_list[i])[si]), &ps_back)) > 1)
                  si += v - 1;
              }
            else
@@ -1342,7 +1382,7 @@ compute_lcd_of_matches (match_list, matches, text)
                  }
              /* no casematch, use first entry */
              if (i > matches)
-               strncpy (match_list[0], match_list[1], low);
+               strncpy (match_list[0], possibly_strip_dirname(match_list[1]), 
low);
            }
          else
            /* otherwise, just use the text the user typed. */
@@ -1351,7 +1391,7 @@ compute_lcd_of_matches (match_list, matches, text)
          FREE (dtext);
        }
       else
-        strncpy (match_list[0], match_list[1], low);
+        strncpy (match_list[0], possibly_strip_dirname(match_list[1]), low);
 
       match_list[0][low] = '\0';
     }
@@ -2057,6 +2097,12 @@ rl_completion_matches (text, entry_function)
   /* Temporary string binder. */
   char *string;
 
+  /* The entry_function alone can inform us about the way it consider
+     the matches.
+     For example, the bash "command_word_completion_function" callback
+     will change this value in case it uses colored filenames completion. */
+  rl_executable_completion_desired = 0;
+
   matches = 0;
   match_list_size = 10;
   match_list = (char **)xmalloc ((match_list_size + 1) * sizeof (char *));
diff --git a/lib/readline/funmap.c b/lib/readline/funmap.c
index 86e375f..473b13f 100644
--- a/lib/readline/funmap.c
+++ b/lib/readline/funmap.c
@@ -47,6 +47,7 @@ typedef int QSFUNC ();
 #endif
 
 extern int _rl_qsort_string_compare PARAMS((char **, char **));
+extern int _rl_qsort_basename_string_compare PARAMS((char **, char **));
 
 FUNMAP **funmap;
 static int funmap_size;
diff --git a/lib/readline/readline.h b/lib/readline/readline.h
index dc8b29a..748097a 100644
--- a/lib/readline/readline.h
+++ b/lib/readline/readline.h
@@ -747,6 +747,15 @@ extern rl_compdisp_func_t 
*rl_completion_display_matches_hook;
    within a completion entry finder function. */
 extern int rl_filename_completion_desired;
 
+/* Non-zero means that at least one of the results of the matches is
+   an absolute path.  This is ALWAYS zero on entry, and can only be changed
+   within the entry_function callback given to rl_completion_matches.
+   If it is set to a non-zero value the lower common denominator computation
+   will take care of stripping directory names each filename match belongs to.
+   The matches should expect to be processed by printable_part().
+   print_filename() will receive a correct full_pathname parameter. */
+extern int rl_executable_completion_desired;
+
 /* Non-zero means that the results of the matches are to be quoted using
    double quotes (or an application-specific quoting mechanism) if the
    filename contains any characters in rl_word_break_chars.  This is
diff --git a/lib/readline/rlprivate.h b/lib/readline/rlprivate.h
index 46834f9..3595851 100644
--- a/lib/readline/rlprivate.h
+++ b/lib/readline/rlprivate.h
@@ -382,6 +382,7 @@ extern int _rl_abort_internal PARAMS((void));
 extern int _rl_null_function PARAMS((int, int));
 extern char *_rl_strindex PARAMS((const char *, const char *));
 extern int _rl_qsort_string_compare PARAMS((char **, char **));
+extern int _rl_qsort_basename_string_compare PARAMS((char **, char **));
 extern int (_rl_uppercase_p) PARAMS((int));
 extern int (_rl_lowercase_p) PARAMS((int));
 extern int (_rl_pure_alphabetic) PARAMS((int));
diff --git a/lib/readline/util.c b/lib/readline/util.c
index 6c68ad8..a20cfe3 100644
--- a/lib/readline/util.c
+++ b/lib/readline/util.c
@@ -437,6 +437,54 @@ _rl_qsort_string_compare (s1, s2)
 #endif
 }
 
+/* raw copy of the version in complete.c, used by 
_rl_qsort_basename_string_compare() */
+static char *
+printable_part (pathname)
+      char *pathname;
+{
+  char *temp, *x;
+
+  temp = strrchr (pathname, '/');
+#if defined (__MSDOS__)
+  if (temp == 0 && ISALPHA ((unsigned char)pathname[0]) && pathname[1] == ':')
+    temp = pathname + 1;
+#endif
+
+  if (temp == 0 || *temp == '\0')
+    return (pathname);
+  /* If the basename is NULL, we might have a pathname like '/usr/src/'.
+     Look for a previous slash and, if one is found, return the portion
+     following that slash.  If there's no previous slash, just return the
+     pathname we were passed. */
+  else if (temp[1] == '\0')
+    {
+      for (x = temp - 1; x > pathname; x--)
+        if (*x == '/')
+          break;
+      return ((*x == '/') ? x + 1 : pathname);
+    }
+  else
+    return ++temp;
+}
+
+/* comparison routine for qsort () ing strings according the basename. */
+int
+_rl_qsort_basename_string_compare (s1, s2)
+  char **s1, **s2;
+{
+#if defined (HAVE_STRCOLL)
+  return (strcoll (printable_part(*s1), printable_part(*s2)));
+#else
+  int result;
+
+  result = **s1 - **s2;
+  if (result == 0)
+    result = strcmp (printable_part(*s1), printable_part(*s2));
+
+  return result;
+#endif
+}
+
 /* Function equivalents for the macros defined in chardefs.h. */
 #define FUNCTION_FOR_MACRO(f)  int (f) (c) int c; { return f (c); }
 

Reply via email to