commit b3572793f7e77bf3ccb4e58121aabe457ddbe39c Author: Raphaƫl Droz 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); }