summaryrefslogtreecommitdiff
path: root/complete.c
diff options
context:
space:
mode:
Diffstat (limited to 'complete.c')
-rw-r--r--complete.c373
1 files changed, 302 insertions, 71 deletions
diff --git a/complete.c b/complete.c
index e67cfeb..cd9aebe 100644
--- a/complete.c
+++ b/complete.c
@@ -1,6 +1,6 @@
/* complete.c -- filename completion for readline. */
-/* Copyright (C) 1987-2011 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2012 Free Software Foundation, Inc.
This file is part of the GNU Readline Library (Readline), a library
for reading lines of text with interactive input and history editing.
@@ -31,6 +31,8 @@
# include <sys/file.h>
#endif
+#include <signal.h>
+
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
@@ -64,6 +66,10 @@ extern int errno;
#include "xmalloc.h"
#include "rlprivate.h"
+#if defined (COLOR_SUPPORT)
+# include "colors.h"
+#endif
+
#ifdef __STDC__
typedef int QSFUNC (const void *, const void *);
#else
@@ -94,17 +100,27 @@ extern struct passwd *getpwent PARAMS((void));
longest string in that array. */
rl_compdisp_func_t *rl_completion_display_matches_hook = (rl_compdisp_func_t *)NULL;
-#if defined (VISIBLE_STATS)
+#if defined (VISIBLE_STATS) || defined (COLOR_SUPPORT)
# if !defined (X_OK)
# define X_OK 1
# endif
+#endif
+
+#if defined (VISIBLE_STATS)
static int stat_char PARAMS((char *));
#endif
+#if defined (COLOR_SUPPORT)
+static int colored_stat_start PARAMS((char *));
+static void colored_stat_end PARAMS((void));
+#endif
+
static int path_isdir PARAMS((const char *));
static char *rl_quote_filename PARAMS((char *, int, char *));
+static void _rl_complete_sigcleanup PARAMS((int, void *));
+
static void set_completion_defaults PARAMS((int));
static int get_y_or_n PARAMS((int));
static int _rl_internal_pager PARAMS((int));
@@ -189,6 +205,12 @@ int _rl_completion_columns = -1;
int rl_visible_stats = 0;
#endif /* VISIBLE_STATS */
+#if defined (COLOR_SUPPORT)
+/* Non-zero means to use colors to indicate file type when listing possible
+ completions. The colors used are taken from $LS_COLORS, if set. */
+int _rl_colored_stats = 0;
+#endif
+
/* If non-zero, when completing in the middle of a word, don't insert
characters from the match that match characters following point in
the word. This means, for instance, completing when the cursor is
@@ -206,6 +228,8 @@ rl_icppfunc_t *rl_directory_completion_hook = (rl_icppfunc_t *)NULL;
rl_icppfunc_t *rl_directory_rewrite_hook = (rl_icppfunc_t *)NULL;
+rl_icppfunc_t *rl_filename_stat_hook = (rl_icppfunc_t *)NULL;
+
/* If non-zero, this is the address of a function to call when reading
directory entries from the filesystem for completion and comparing
them to the partial word to be completed. The function should
@@ -457,6 +481,15 @@ _rl_reset_completion_state ()
rl_completion_quote_character = 0;
}
+static void
+_rl_complete_sigcleanup (sig, ptr)
+ int sig;
+ void *ptr;
+{
+ if (sig == SIGINT) /* XXX - for now */
+ _rl_free_match_list ((char **)ptr);
+}
+
/* Set default values for readline word completion. These are the variables
that application completion functions can change or inspect. */
static void
@@ -551,6 +584,8 @@ stat_char (filename)
{
struct stat finfo;
int character, r;
+ char *f;
+ const char *fn;
/* Short-circuit a //server on cygwin, since that will always behave as
a directory. */
@@ -559,10 +594,20 @@ stat_char (filename)
return '/';
#endif
+ f = 0;
+ if (rl_filename_stat_hook)
+ {
+ f = savestring (filename);
+ (*rl_filename_stat_hook) (&f);
+ fn = f;
+ }
+ else
+ fn = filename;
+
#if defined (HAVE_LSTAT) && defined (S_ISLNK)
- r = lstat (filename, &finfo);
+ r = lstat (fn, &finfo);
#else
- r = stat (filename, &finfo);
+ r = stat (fn, &finfo);
#endif
if (r == -1)
@@ -596,10 +641,29 @@ stat_char (filename)
if (access (filename, X_OK) == 0)
character = '*';
}
+
+ free (f);
return (character);
}
#endif /* VISIBLE_STATS */
+#if defined (COLOR_SUPPORT)
+static int
+colored_stat_start (filename)
+ char *filename;
+{
+ _rl_set_normal_color ();
+ return (_rl_print_color_indicator (filename));
+}
+
+static void
+colored_stat_end ()
+{
+ _rl_prep_non_filename_text ();
+ _rl_put_indicator (&_rl_color_indicator[C_CLR_TO_EOL]);
+}
+#endif
+
/* Return the portion of PATHNAME that should be output when listing
possible completions. If we are hacking filename completion, we
are only interested in the basename, the portion following the
@@ -679,7 +743,7 @@ fnwidth (string)
else
{
pos += clen;
- w = wcwidth (wc);
+ w = WCWIDTH (wc);
width += (w >= 0) ? w : 1;
}
#else
@@ -766,7 +830,7 @@ fnprint (to_print, prefix_bytes)
break;
else
{
- w = wcwidth (wc);
+ w = WCWIDTH (wc);
width = (w >= 0) ? w : 1;
}
fwrite (s, 1, tlen, rl_outstream);
@@ -796,13 +860,20 @@ print_filename (to_print, full_pathname, prefix_bytes)
char *s, c, *new_full_pathname, *dn;
extension_char = 0;
- printed_len = fnprint (to_print, prefix_bytes);
+#if defined (COLOR_SUPPORT)
+ /* Defer printing if we want to prefix with a color indicator */
+ if (_rl_colored_stats == 0 || rl_filename_completion_desired == 0)
+#endif
+ printed_len = fnprint (to_print, prefix_bytes);
+ if (rl_filename_completion_desired && (
#if defined (VISIBLE_STATS)
- if (rl_filename_completion_desired && (rl_visible_stats || _rl_complete_mark_directories))
-#else
- if (rl_filename_completion_desired && _rl_complete_mark_directories)
+ rl_visible_stats ||
#endif
+#if defined (COLOR_SUPPORT)
+ _rl_colored_stats ||
+#endif
+ _rl_complete_mark_directories))
{
/* If to_print != full_pathname, to_print is the basename of the
path passed. In this case, we try to expand the directory
@@ -848,8 +919,28 @@ print_filename (to_print, full_pathname, prefix_bytes)
extension_char = stat_char (new_full_pathname);
else
#endif
- if (path_isdir (new_full_pathname))
- extension_char = '/';
+ if (_rl_complete_mark_directories)
+ {
+ dn = 0;
+ if (rl_directory_completion_hook == 0 && rl_filename_stat_hook)
+ {
+ dn = savestring (new_full_pathname);
+ (*rl_filename_stat_hook) (&dn);
+ free (new_full_pathname);
+ new_full_pathname = dn;
+ }
+ if (path_isdir (new_full_pathname))
+ extension_char = '/';
+ }
+
+#if defined (COLOR_SUPPORT)
+ if (_rl_colored_stats)
+ {
+ colored_stat_start (new_full_pathname);
+ printed_len = fnprint (to_print, prefix_bytes);
+ colored_stat_end ();
+ }
+#endif
xfree (new_full_pathname);
to_print[-1] = c;
@@ -862,8 +953,18 @@ print_filename (to_print, full_pathname, prefix_bytes)
extension_char = stat_char (s);
else
#endif
- if (path_isdir (s))
+ if (_rl_complete_mark_directories && path_isdir (s))
extension_char = '/';
+
+#if defined (COLOR_SUPPORT)
+ if (_rl_colored_stats)
+ {
+ colored_stat_start (s);
+ printed_len = fnprint (to_print, prefix_bytes);
+ colored_stat_end ();
+ }
+#endif
+
}
xfree (s);
@@ -1058,10 +1159,13 @@ gen_completion_matches (text, start, end, our_func, found_quote, quote_char)
variable rl_attempted_completion_function. */
if (rl_attempted_completion_function)
{
- _rl_interrupt_immediately++;
matches = (*rl_attempted_completion_function) (text, start, end);
- if (_rl_interrupt_immediately > 0)
- _rl_interrupt_immediately--;
+ if (RL_SIG_RECEIVED())
+ {
+ _rl_free_match_list (matches);
+ matches = 0;
+ RL_CHECK_SIGNALS ();
+ }
if (matches || rl_attempted_completion_over)
{
@@ -1072,7 +1176,15 @@ gen_completion_matches (text, start, end, our_func, found_quote, quote_char)
/* XXX -- filename dequoting moved into rl_filename_completion_function */
+ /* rl_completion_matches will check for signals as well to avoid a long
+ delay while reading a directory. */
matches = rl_completion_matches (text, our_func);
+ if (RL_SIG_RECEIVED())
+ {
+ _rl_free_match_list (matches);
+ matches = 0;
+ RL_CHECK_SIGNALS ();
+ }
return matches;
}
@@ -1147,9 +1259,11 @@ compute_lcd_of_matches (match_list, matches, text)
{
register int i, c1, c2, si;
int low; /* Count of max-matched characters. */
+ int lx;
char *dtext; /* dequoted TEXT, if needed */
#if defined (HANDLE_MULTIBYTE)
int v;
+ size_t v1, v2;
mbstate_t ps1, ps2;
wchar_t wc1, wc2;
#endif
@@ -1182,14 +1296,20 @@ compute_lcd_of_matches (match_list, matches, text)
#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);
+ v1 = mbrtowc(&wc1, match_list[i]+si, strlen (match_list[i]+si), &ps1);
+ v2 = mbrtowc (&wc2, match_list[i+1]+si, strlen (match_list[i+1]+si), &ps2);
+ if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2))
+ {
+ if (c1 != c2) /* do byte comparison */
+ break;
+ continue;
+ }
wc1 = towlower (wc1);
wc2 = towlower (wc2);
if (wc1 != wc2)
break;
- else if (v > 1)
- si += v - 1;
+ else if (v1 > 1)
+ si += v1 - 1;
}
else
#endif
@@ -1264,21 +1384,20 @@ compute_lcd_of_matches (match_list, matches, text)
qsort (match_list+1, matches, sizeof(char *), (QSFUNC *)_rl_qsort_string_compare);
si = strlen (text);
- if (si <= low)
- {
- for (i = 1; i <= matches; i++)
- if (strncmp (match_list[i], text, si) == 0)
- {
- strncpy (match_list[0], match_list[i], low);
- break;
- }
- /* no casematch, use first entry */
- if (i > matches)
- strncpy (match_list[0], match_list[1], low);
- }
- else
- /* otherwise, just use the text the user typed. */
- strncpy (match_list[0], text, low);
+ lx = (si <= low) ? si : low; /* check shorter of text and matches */
+ /* Try to preserve the case of what the user typed in the presence of
+ multiple matches: check each match for something that matches
+ what the user typed taking case into account; use it up to common
+ length of matches if one is found. If not, just use first match. */
+ for (i = 1; i <= matches; i++)
+ if (strncmp (match_list[i], text, lx) == 0)
+ {
+ strncpy (match_list[0], match_list[i], low);
+ break;
+ }
+ /* no casematch, use first entry */
+ if (i > matches)
+ strncpy (match_list[0], match_list[1], low);
FREE (dtext);
}
@@ -1305,7 +1424,7 @@ postprocess_matches (matchesp, matching_filenames)
return 0;
/* It seems to me that in all the cases we handle we would like
- to ignore duplicate possiblilities. Scan for the text to
+ to ignore duplicate possibilities. Scan for the text to
insert being identical to the other completions. */
if (rl_ignore_completion_duplicates)
{
@@ -1463,7 +1582,7 @@ rl_display_match_list (matches, len, max)
/* Have we reached the end of this line? */
if (matches[i+1])
{
- if (i && (limit > 1) && (i % limit) == 0)
+ if (limit == 1 || (i && (limit > 1) && (i % limit) == 0))
{
rl_crlf ();
lines++;
@@ -1672,7 +1791,7 @@ append_to_match (text, delimiter, quote_char, nontrivial_match)
char *text;
int delimiter, quote_char, nontrivial_match;
{
- char temp_string[4], *filename;
+ char temp_string[4], *filename, *fn;
int temp_string_index, s;
struct stat finfo;
@@ -1691,6 +1810,13 @@ append_to_match (text, delimiter, quote_char, nontrivial_match)
if (rl_filename_completion_desired)
{
filename = tilde_expand (text);
+ if (rl_filename_stat_hook)
+ {
+ fn = savestring (filename);
+ (*rl_filename_stat_hook) (&fn);
+ xfree (filename);
+ filename = fn;
+ }
s = (nontrivial_match && rl_completion_mark_symlink_dirs == 0)
? LSTAT (filename, &finfo)
: stat (filename, &finfo);
@@ -1710,8 +1836,7 @@ append_to_match (text, delimiter, quote_char, nontrivial_match)
#ifdef S_ISLNK
/* Don't add anything if the filename is a symlink and resolves to a
directory. */
- else if (s == 0 && S_ISLNK (finfo.st_mode) &&
- stat (filename, &finfo) == 0 && S_ISDIR (finfo.st_mode))
+ else if (s == 0 && S_ISLNK (finfo.st_mode) && path_isdir (filename))
;
#endif
else
@@ -1831,10 +1956,8 @@ rl_complete_internal (what_to_do)
/* nontrivial_lcd is set if the common prefix adds something to the word
being completed. */
nontrivial_lcd = matches && strcmp (text, matches[0]) != 0;
-#if 1
if (what_to_do == '!' || what_to_do == '@')
tlen = strlen (text);
-#endif
xfree (text);
if (matches == 0)
@@ -1868,10 +1991,6 @@ rl_complete_internal (what_to_do)
case '!':
case '@':
/* Insert the first match with proper quoting. */
-#if 0
- if (*matches[0])
- insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, &quote_char);
-#else
if (what_to_do == TAB)
{
if (*matches[0])
@@ -1886,7 +2005,6 @@ rl_complete_internal (what_to_do)
if (mlen >= tlen)
insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, &quote_char);
}
-#endif
/* If there are more matches, ring the bell to indicate.
If we are in vi mode, Posix.2 says to not ring the bell.
@@ -1922,7 +2040,14 @@ rl_complete_internal (what_to_do)
break;
case '?':
+ if (rl_completion_display_matches_hook == 0)
+ {
+ _rl_sigcleanup = _rl_complete_sigcleanup;
+ _rl_sigcleanarg = matches;
+ }
display_matches (matches);
+ _rl_sigcleanup = 0;
+ _rl_sigcleanarg = 0;
break;
default:
@@ -1930,6 +2055,7 @@ rl_complete_internal (what_to_do)
rl_ding ();
FREE (saved_line_buffer);
RL_UNSETSTATE(RL_STATE_COMPLETING);
+ _rl_free_match_list (matches);
_rl_reset_completion_state ();
return 1;
}
@@ -1971,6 +2097,8 @@ rl_completion_matches (text, entry_function)
const char *text;
rl_compentry_func_t *entry_function;
{
+ register int i;
+
/* Number of slots in match_list. */
int match_list_size;
@@ -1988,18 +2116,36 @@ rl_completion_matches (text, entry_function)
match_list = (char **)xmalloc ((match_list_size + 1) * sizeof (char *));
match_list[1] = (char *)NULL;
- _rl_interrupt_immediately++;
while (string = (*entry_function) (text, matches))
{
- if (matches + 1 == match_list_size)
+ if (RL_SIG_RECEIVED ())
+ {
+ /* Start at 1 because we don't set matches[0] in this function.
+ Only free the list members if we're building match list from
+ rl_filename_completion_function, since we know that doesn't
+ free the strings it returns. */
+ if (entry_function == rl_filename_completion_function)
+ {
+ for (i = 1; match_list[i]; i++)
+ xfree (match_list[i]);
+ }
+ xfree (match_list);
+ match_list = 0;
+ match_list_size = 0;
+ matches = 0;
+ RL_CHECK_SIGNALS ();
+ }
+
+ if (matches + 1 >= match_list_size)
match_list = (char **)xrealloc
(match_list, ((match_list_size += 10) + 1) * sizeof (char *));
+ if (match_list == 0)
+ return (match_list);
+
match_list[++matches] = string;
match_list[matches + 1] = (char *)NULL;
}
- if (_rl_interrupt_immediately > 0)
- _rl_interrupt_immediately--;
/* If there were any matches, then look through them finding out the
lowest common denominator. That then becomes match_list[0]. */
@@ -2038,7 +2184,9 @@ rl_username_completion_function (text, state)
username = savestring (&text[first_char_loc]);
namelen = strlen (username);
+#if defined (HAVE_GETPWENT)
setpwent ();
+#endif
}
#if defined (HAVE_GETPWENT)
@@ -2075,8 +2223,9 @@ rl_username_completion_function (text, state)
/* Return non-zero if CONVFN matches FILENAME up to the length of FILENAME
(FILENAME_LEN). If _rl_completion_case_fold is set, compare without
- regard to the alphabetic case of characters. CONVFN is the possibly-
- converted directory entry; FILENAME is what the user typed. */
+ regard to the alphabetic case of characters. If
+ _rl_completion_case_map is set, make `-' and `_' equivalent. CONVFN is
+ the possibly-converted directory entry; FILENAME is what the user typed. */
static int
complete_fncmp (convfn, convlen, filename, filename_len)
const char *convfn;
@@ -2086,34 +2235,110 @@ complete_fncmp (convfn, convlen, filename, filename_len)
{
register char *s1, *s2;
int d, len;
+#if defined (HANDLE_MULTIBYTE)
+ size_t v1, v2;
+ mbstate_t ps1, ps2;
+ wchar_t wc1, wc2;
+#endif
+
+#if defined (HANDLE_MULTIBYTE)
+ memset (&ps1, 0, sizeof (mbstate_t));
+ memset (&ps2, 0, sizeof (mbstate_t));
+#endif
+
+ if (filename_len == 0)
+ return 1;
+ if (convlen < filename_len)
+ return 0;
+
+ len = filename_len;
+ s1 = (char *)convfn;
+ s2 = (char *)filename;
/* Otherwise, if these match up to the length of filename, then
it is a match. */
if (_rl_completion_case_fold && _rl_completion_case_map)
{
/* Case-insensitive comparison treating _ and - as equivalent */
- if (filename_len == 0)
- return 1;
- if (convlen < filename_len)
- return 0;
- s1 = (char *)convfn;
- s2 = (char *)filename;
- len = filename_len;
- do
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ do
+ {
+ v1 = mbrtowc (&wc1, s1, convlen, &ps1);
+ v2 = mbrtowc (&wc2, s2, filename_len, &ps2);
+ if (v1 == 0 && v2 == 0)
+ return 1;
+ else if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2))
+ {
+ if (*s1 != *s2) /* do byte comparison */
+ return 0;
+ else if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_'))
+ return 0;
+ s1++; s2++; len--;
+ continue;
+ }
+ wc1 = towlower (wc1);
+ wc2 = towlower (wc2);
+ s1 += v1;
+ s2 += v1;
+ len -= v1;
+ if ((wc1 == L'-' || wc1 == L'_') && (wc2 == L'-' || wc2 == L'_'))
+ continue;
+ if (wc1 != wc2)
+ return 0;
+ }
+ while (len != 0);
+ }
+ else
+#endif
{
- d = _rl_to_lower (*s1) - _rl_to_lower (*s2);
- /* *s1 == [-_] && *s2 == [-_] */
- if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_'))
- d = 0;
- if (d != 0)
- return 0;
- s1++; s2++; /* already checked convlen >= filename_len */
+ do
+ {
+ d = _rl_to_lower (*s1) - _rl_to_lower (*s2);
+ /* *s1 == [-_] && *s2 == [-_] */
+ if ((*s1 == '-' || *s1 == '_') && (*s2 == '-' || *s2 == '_'))
+ d = 0;
+ if (d != 0)
+ return 0;
+ s1++; s2++; /* already checked convlen >= filename_len */
+ }
+ while (--len != 0);
}
- while (--len != 0);
+
return 1;
}
else if (_rl_completion_case_fold)
{
+#if defined (HANDLE_MULTIBYTE)
+ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+ {
+ do
+ {
+ v1 = mbrtowc (&wc1, s1, convlen, &ps1);
+ v2 = mbrtowc (&wc2, s2, filename_len, &ps2);
+ if (v1 == 0 && v2 == 0)
+ return 1;
+ else if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2))
+ {
+ if (*s1 != *s2) /* do byte comparison */
+ return 0;
+ s1++; s2++; len--;
+ continue;
+ }
+ wc1 = towlower (wc1);
+ wc2 = towlower (wc2);
+ if (wc1 != wc2)
+ return 0;
+ s1 += v1;
+ s2 += v1;
+ len -= v1;
+ }
+ while (len != 0);
+ return 1;
+ }
+ else
+#endif
if ((_rl_to_lower (convfn[0]) == _rl_to_lower (filename[0])) &&
(convlen >= filename_len) &&
(_rl_strnicmp (filename, convfn, filename_len) == 0))
@@ -2233,8 +2458,9 @@ rl_filename_completion_function (text, state)
}
directory = opendir (dirname);
- /* Now dequote a non-null filename. */
- if (filename && *filename && rl_completion_found_quote && rl_filename_dequoting_function)
+ /* Now dequote a non-null filename. FILENAME will not be NULL, but may
+ be empty. */
+ if (*filename && rl_completion_found_quote && rl_filename_dequoting_function)
{
/* delete single and double quotes */
temp = (*rl_filename_dequoting_function) (filename, rl_completion_quote_character);
@@ -2598,6 +2824,11 @@ rl_menu_complete (count, ignore)
full_completion = 1;
return (0);
}
+ else if (_rl_menu_complete_prefix_first)
+ {
+ rl_ding ();
+ return (0);
+ }
}
else if (match_list_size <= 1)
{