/* complete.c -- filename completion for readline. */
-/* Copyright (C) 1987-2009 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.
# include <sys/file.h>
#endif
+#include <signal.h>
+
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include "xmalloc.h"
#include "rlprivate.h"
+#if defined (COLOR_SUPPORT)
+# include "colors.h"
+#endif
+
#ifdef __STDC__
typedef int QSFUNC (const void *, const void *);
#else
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));
static void insert_match PARAMS((char *, int, int, char *));
static int append_to_match PARAMS((char *, int, int, int));
static void insert_all_matches PARAMS((char **, int, char *));
+static int complete_fncmp PARAMS((const char *, int, const char *, int));
static void display_matches PARAMS((char **));
static int compute_lcd_of_matches PARAMS((char **, int, const char *));
static int postprocess_matches PARAMS((char ***, int));
+static int complete_get_screenwidth PARAMS((void));
static char *make_quoted_replacement PARAMS((char *, int, char *));
#if defined (__MSDOS__) && !defined (__DJGPP__)
int _rl_completion_case_fold = 1;
#else
-int _rl_completion_case_fold;
+int _rl_completion_case_fold = 0;
#endif
-/* If non-zero, don't match hidden files (filenames beginning with a `.' on
+/* Non-zero means that `-' and `_' are equivalent when comparing filenames
+ for completion. */
+int _rl_completion_case_map = 0;
+
+/* If zero, don't match hidden files (filenames beginning with a `.' on
Unix) when doing filename completion. */
int _rl_match_hidden_files = 1;
display prefix replaced with an ellipsis. */
int _rl_completion_prefix_display_length = 0;
+/* The readline-private number of screen columns to use when displaying
+ matches. If < 0 or > _rl_screenwidth, it is ignored. */
+int _rl_completion_columns = -1;
+
/* Global variables available to applications using readline. */
#if defined (VISIBLE_STATS)
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
after the `e' in `Makefile' won't result in `Makefilefile'. */
int _rl_skip_completed_text = 0;
+/* If non-zero, menu completion displays the common prefix first in the
+ cycle of possible completions instead of the last. */
+int _rl_menu_complete_prefix_first = 0;
+
/* If non-zero, then this is the address of a function to call when
completing on a directory name. The function is called with
the address of a string (the current directory name) as an arg. */
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
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
{
int c;
+ /* For now, disable pager in callback mode, until we later convert to state
+ driven functions. Have to wait until next major version to add new
+ state definition, since it will change value of RL_STATE_DONE. */
+#if defined (READLINE_CALLBACKS)
+ if (RL_ISSTATE (RL_STATE_CALLBACK))
+ return 1;
+#endif
+
for (;;)
{
RL_SETSTATE(RL_STATE_MOREINPUT);
{
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. */
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)
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
else
{
pos += clen;
- w = wcwidth (wc);
+ w = WCWIDTH (wc);
width += (w >= 0) ? w : 1;
}
#else
break;
else
{
- w = wcwidth (wc);
+ w = WCWIDTH (wc);
width = (w >= 0) ? w : 1;
}
fwrite (s, 1, tlen, rl_outstream);
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
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 = '/';
+ }
- free (new_full_pathname);
+#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;
}
else
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
+
}
- free (s);
+ xfree (s);
if (extension_char)
{
putc (extension_char, rl_outstream);
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)
{
/* 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;
}
{
if (strcmp (matches[i], matches[i + 1]) == 0)
{
- free (matches[i]);
+ xfree (matches[i]);
matches[i] = (char *)&dead_slot;
}
else
temp_array[j] = (char *)NULL;
if (matches[0] != (char *)&dead_slot)
- free (matches[0]);
+ xfree (matches[0]);
/* Place the lowest common denominator back in [0]. */
temp_array[0] = lowest_common;
insert. */
if (j == 2 && strcmp (temp_array[0], temp_array[1]) == 0)
{
- free (temp_array[1]);
+ xfree (temp_array[1]);
temp_array[1] = (char *)NULL;
}
return (temp_array);
{
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
#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
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);
}
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)
{
temp_matches = remove_duplicate_matches (matches);
- free (matches);
+ xfree (matches);
matches = temp_matches;
}
return (1);
}
+static int
+complete_get_screenwidth ()
+{
+ int cols;
+ char *envcols;
+
+ cols = _rl_completion_columns;
+ if (cols >= 0 && cols <= _rl_screenwidth)
+ return cols;
+ envcols = getenv ("COLUMNS");
+ if (envcols && *envcols)
+ cols = atoi (envcols);
+ if (cols >= 0 && cols <= _rl_screenwidth)
+ return cols;
+ return _rl_screenwidth;
+}
+
/* A convenience function for displaying a list of strings in
columnar format on readline's output stream. MATCHES is the list
of strings, in argv format, LEN is the number of strings in MATCHES,
char **matches;
int len, max;
{
- int count, limit, printed_len, lines;
+ int count, limit, printed_len, lines, cols;
int i, j, k, l, common_length, sind;
char *temp, *t;
}
/* How many items of MAX length can we fit in the screen window? */
+ cols = complete_get_screenwidth ();
max += 2;
- limit = _rl_screenwidth / max;
- if (limit != 1 && (limit * max == _rl_screenwidth))
+ limit = cols / max;
+ if (limit != 1 && (limit * max == cols))
limit--;
- /* Avoid a possible floating exception. If max > _rl_screenwidth,
+ /* If cols == 0, limit will end up -1 */
+ if (cols < _rl_screenwidth && limit < 0)
+ limit = 1;
+
+ /* Avoid a possible floating exception. If max > cols,
limit will be 0 and a divide-by-zero fault will result. */
if (limit == 0)
limit = 1;
/* 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++;
else
_rl_replace_text (replacement, start, end);
if (replacement != match)
- free (replacement);
+ xfree (replacement);
}
}
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;
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);
#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
if (rl_point == rl_end && temp_string_index)
rl_insert_text (temp_string);
}
- free (filename);
+ xfree (filename);
}
else
{
rl_insert_text (rp);
rl_insert_text (" ");
if (rp != matches[i])
- free (rp);
+ xfree (rp);
}
}
else
rl_insert_text (rp);
rl_insert_text (" ");
if (rp != matches[0])
- free (rp);
+ xfree (rp);
}
rl_end_undo_group ();
}
return;
for (i = 0; matches[i]; i++)
- free (matches[i]);
- free (matches);
+ xfree (matches[i]);
+ xfree (matches);
}
/* Complete the word at or before point.
int start, end, delimiter, found_quote, i, nontrivial_lcd;
char *text, *saved_line_buffer;
char quote_char;
+#if 1
+ int tlen, mlen;
+#endif
RL_SETSTATE(RL_STATE_COMPLETING);
/* nontrivial_lcd is set if the common prefix adds something to the word
being completed. */
nontrivial_lcd = matches && strcmp (text, matches[0]) != 0;
- free (text);
+ if (what_to_do == '!' || what_to_do == '@')
+ tlen = strlen (text);
+ xfree (text);
if (matches == 0)
{
case '!':
case '@':
/* Insert the first match with proper quoting. */
- if (*matches[0])
+ if (what_to_do == TAB)
+ {
+ if (*matches[0])
+ insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char);
+ }
+ else if (*matches[0] && matches[1] == 0)
+ /* should we perform the check only if there are multiple matches? */
insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char);
+ else if (*matches[0]) /* what_to_do != TAB && multiple matches */
+ {
+ mlen = *matches[0] ? strlen (matches[0]) : 0;
+ if (mlen >= tlen)
+ insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char);
+ }
/* If there are more matches, ring the bell to indicate.
If we are in vi mode, Posix.2 says to not ring the bell.
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:
rl_ding ();
FREE (saved_line_buffer);
RL_UNSETSTATE(RL_STATE_COMPLETING);
+ _rl_free_match_list (matches);
_rl_reset_completion_state ();
return 1;
}
if (saved_line_buffer)
{
completion_changed_buffer = strcmp (rl_line_buffer, saved_line_buffer) != 0;
- free (saved_line_buffer);
+ xfree (saved_line_buffer);
}
RL_UNSETSTATE(RL_STATE_COMPLETING);
const char *text;
rl_compentry_func_t *entry_function;
{
+ register int i;
+
/* Number of slots in match_list. */
int match_list_size;
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]. */
compute_lcd_of_matches (match_list, matches, text);
else /* There were no matches. */
{
- free (match_list);
+ xfree (match_list);
match_list = (char **)NULL;
}
return (match_list);
username = savestring (&text[first_char_loc]);
namelen = strlen (username);
+#if defined (HAVE_GETPWENT)
setpwent ();
+#endif
}
#if defined (HAVE_GETPWENT)
#endif /* !__WIN32__ && !__OPENNT */
}
+/* 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. 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;
+ int convlen;
+ const char *filename;
+ int 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 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
+ {
+ 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);
+ }
+
+ 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))
+ return 1;
+ }
+ else
+ {
+ if ((convfn[0] == filename[0]) &&
+ (convlen >= filename_len) &&
+ (strncmp (filename, convfn, filename_len) == 0))
+ return 1;
+ }
+ return 0;
+}
+
/* Okay, now we write the entry_function for filename completion. In the
general case. Note that completion in the shell is a little different
because of all the pathnames that must be followed when looking up the
/* We aren't done yet. We also support the "~user" syntax. */
- /* Save the version of the directory that the user typed. */
- users_dirname = savestring (dirname);
+ /* Save the version of the directory that the user typed, dequoting
+ it if necessary. */
+ if (rl_completion_found_quote && rl_filename_dequoting_function)
+ users_dirname = (*rl_filename_dequoting_function) (dirname, rl_completion_quote_character);
+ else
+ users_dirname = savestring (dirname);
if (*dirname == '~')
{
temp = tilde_expand (dirname);
- free (dirname);
+ xfree (dirname);
dirname = temp;
}
+ /* We have saved the possibly-dequoted version of the directory name
+ the user typed. Now transform the directory name we're going to
+ pass to opendir(2). The directory rewrite hook modifies only the
+ directory name; the directory completion hook modifies both the
+ directory name passed to opendir(2) and the version the user
+ typed. Both the directory completion and rewrite hooks should perform
+ any necessary dequoting. The hook functions return 1 if they modify
+ the directory name argument. If either hook returns 0, it should
+ not modify the directory name pointer passed as an argument. */
if (rl_directory_rewrite_hook)
(*rl_directory_rewrite_hook) (&dirname);
-
- /* The directory completion hook should perform any necessary
- dequoting. */
- if (rl_directory_completion_hook && (*rl_directory_completion_hook) (&dirname))
+ else if (rl_directory_completion_hook && (*rl_directory_completion_hook) (&dirname))
{
- free (users_dirname);
+ xfree (users_dirname);
users_dirname = savestring (dirname);
}
else if (rl_completion_found_quote && rl_filename_dequoting_function)
{
/* delete single and double quotes */
- temp = (*rl_filename_dequoting_function) (users_dirname, rl_completion_quote_character);
- free (users_dirname);
- users_dirname = temp;
+ xfree (dirname);
+ dirname = savestring (users_dirname);
}
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);
- free (filename);
+ xfree (filename);
filename = temp;
}
filename_len = strlen (filename);
All other entries except "." and ".." match. */
if (filename_len == 0)
{
- if (_rl_match_hidden_files == 0 && HIDDEN_FILE (entry->d_name))
+ if (_rl_match_hidden_files == 0 && HIDDEN_FILE (convfn))
continue;
if (convfn[0] != '.' ||
}
else
{
- /* Otherwise, if these match up to the length of filename, then
- it is a match. */
- if (_rl_completion_case_fold)
- {
- if ((_rl_to_lower (convfn[0]) == _rl_to_lower (filename[0])) &&
- (convlen >= filename_len) &&
- (_rl_strnicmp (filename, convfn, filename_len) == 0))
- break;
- }
- else
- {
- if ((convfn[0] == filename[0]) &&
- (convlen >= filename_len) &&
- (strncmp (filename, convfn, filename_len) == 0))
- break;
- }
+ if (complete_fncmp (convfn, convlen, filename, filename_len))
+ break;
}
}
}
if (dirname)
{
- free (dirname);
+ xfree (dirname);
dirname = (char *)NULL;
}
if (filename)
{
- free (filename);
+ xfree (filename);
filename = (char *)NULL;
}
if (users_dirname)
{
- free (users_dirname);
+ xfree (users_dirname);
users_dirname = (char *)NULL;
}
temp[dirlen++] = '/';
}
- strcpy (temp + dirlen, entry->d_name);
+ strcpy (temp + dirlen, convfn);
}
else
temp = savestring (convfn);
if (convfn != dentry)
- free (convfn);
+ xfree (convfn);
return (temp);
}
if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0)
{
- rl_ding ();
+ rl_ding ();
FREE (matches);
matches = (char **)0;
FREE (orig_text);
orig_text = (char *)0;
- completion_changed_buffer = 0;
- RL_UNSETSTATE(RL_STATE_COMPLETING);
- return (0);
+ completion_changed_buffer = 0;
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+ return (0);
}
RL_UNSETSTATE(RL_STATE_COMPLETING);
match_list_index += count;
if (match_list_index < 0)
- match_list_index += match_list_size;
+ {
+ while (match_list_index < 0)
+ match_list_index += match_list_size;
+ }
else
match_list_index %= match_list_size;
static int full_completion = 0; /* set to 1 if menu completion should reinitialize on next call */
static int orig_start, orig_end;
static char quote_char;
- static int delimiter;
+ static int delimiter, cstate;
/* The first time through, we generate the list of matches and set things
up to insert them. */
if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0)
{
- rl_ding ();
+ rl_ding ();
FREE (matches);
matches = (char **)0;
FREE (orig_text);
orig_text = (char *)0;
- completion_changed_buffer = 0;
- RL_UNSETSTATE(RL_STATE_COMPLETING);
- return (0);
+ completion_changed_buffer = 0;
+ RL_UNSETSTATE(RL_STATE_COMPLETING);
+ return (0);
}
RL_UNSETSTATE(RL_STATE_COMPLETING);
full_completion = 1;
return (0);
}
+ else if (_rl_menu_complete_prefix_first)
+ {
+ rl_ding ();
+ return (0);
+ }
}
else if (match_list_size <= 1)
{
full_completion = 1;
return (0);
}
+ else if (_rl_menu_complete_prefix_first && match_list_size > 1)
+ {
+ rl_ding ();
+ return (0);
+ }
}
/* Now we have the list of matches. Replace the text between
match_list_index += count;
if (match_list_index < 0)
- match_list_index += match_list_size;
+ {
+ while (match_list_index < 0)
+ match_list_index += match_list_size;
+ }
else
match_list_index %= match_list_size;