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 <[email protected]>
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); }