#define READLINE_LIBRARY
#if defined (HAVE_CONFIG_H)
# include <config.h>
#endif
#include <sys/types.h>
#include <fcntl.h>
#if defined (HAVE_SYS_FILE_H)
# include <sys/file.h>
#endif
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#if defined (HAVE_STDLIB_H)
# include <stdlib.h>
#else
# include "ansi_stdlib.h"
#endif
#include <stdio.h>
#include <errno.h>
#if !defined (errno)
extern int errno;
#endif
#if defined (HAVE_PWD_H)
#include <pwd.h>
#endif
#include "posixdir.h"
#include "posixstat.h"
#include "rldefs.h"
#include "rlmbutil.h"
#include "readline.h"
#include "xmalloc.h"
#include "rlprivate.h"
#ifdef __STDC__
typedef int QSFUNC (const void *, const void *);
#else
typedef int QSFUNC ();
#endif
#ifdef HAVE_LSTAT
# define LSTAT lstat
#else
# define LSTAT stat
#endif
#define HIDDEN_FILE(fname) ((fname)[0] == '.')
#if defined (HAVE_GETPWENT) && (!defined (HAVE_GETPW_DECLS) || defined (_POSIX_SOURCE))
extern struct passwd *getpwent PARAMS((void));
#endif
rl_compdisp_func_t *rl_completion_display_matches_hook = (rl_compdisp_func_t *)NULL;
#if defined (VISIBLE_STATS)
# if !defined (X_OK)
# define X_OK 1
# endif
static int stat_char PARAMS((char *));
#endif
static int path_isdir PARAMS((const char *));
static char *rl_quote_filename PARAMS((char *, int, char *));
static void set_completion_defaults PARAMS((int));
static int get_y_or_n PARAMS((int));
static int _rl_internal_pager PARAMS((int));
static char *printable_part PARAMS((char *));
static int fnwidth PARAMS((const char *));
static int fnprint PARAMS((const char *));
static int print_filename PARAMS((char *, char *));
static char **gen_completion_matches PARAMS((char *, int, int, rl_compentry_func_t *, int, int));
static char **remove_duplicate_matches PARAMS((char **));
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 void display_matches PARAMS((char **));
static int compute_lcd_of_matches PARAMS((char **, int, const char *));
static int postprocess_matches PARAMS((char ***, int));
static char *make_quoted_replacement PARAMS((char *, int, char *));
int _rl_complete_show_all = 0;
int _rl_complete_show_unmodified = 0;
int _rl_complete_mark_directories = 1;
int _rl_complete_mark_symlink_dirs = 0;
int _rl_print_completions_horizontally;
#if defined (__MSDOS__) && !defined (__DJGPP__)
int _rl_completion_case_fold = 1;
#else
int _rl_completion_case_fold;
#endif
int _rl_match_hidden_files = 1;
#if defined (VISIBLE_STATS)
int rl_visible_stats = 0;
#endif
rl_icppfunc_t *rl_directory_completion_hook = (rl_icppfunc_t *)NULL;
rl_icppfunc_t *rl_directory_rewrite_hook = (rl_icppfunc_t *)NULL;
int rl_complete_with_tilde_expansion = 0;
rl_compentry_func_t *rl_completion_entry_function = (rl_compentry_func_t *)NULL;
rl_completion_func_t *rl_attempted_completion_function = (rl_completion_func_t *)NULL;
int rl_attempted_completion_over = 0;
int rl_completion_type = 0;
int rl_completion_query_items = 100;
int _rl_page_completions = 1;
const char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(";
const char *rl_basic_quote_characters = "\"'";
char *rl_completer_word_break_characters = ( char *)NULL;
rl_cpvfunc_t *rl_completion_word_break_hook = (rl_cpvfunc_t *)NULL;
const char *rl_completer_quote_characters = (const char *)NULL;
const char *rl_filename_quote_characters = (const char *)NULL;
const char *rl_special_prefixes = (const char *)NULL;
int rl_ignore_completion_duplicates = 1;
int rl_filename_completion_desired = 0;
int rl_filename_quoting_desired = 1;
rl_compignore_func_t *rl_ignore_some_completions_function = (rl_compignore_func_t *)NULL;
rl_quote_func_t *rl_filename_quoting_function = rl_quote_filename;
rl_dequote_func_t *rl_filename_dequoting_function = (rl_dequote_func_t *)NULL;
rl_linebuf_func_t *rl_char_is_quoted_p = (rl_linebuf_func_t *)NULL;
int rl_completion_suppress_append = 0;
int rl_completion_append_character = ' ';
int rl_completion_suppress_quote = 0;
int rl_completion_quote_character;
int rl_completion_found_quote;
int rl_completion_mark_symlink_dirs;
int rl_inhibit_completion;
static int completion_changed_buffer;
int
rl_complete (ignore, invoking_key)
int ignore, invoking_key;
{
if (rl_inhibit_completion)
return (_rl_insert_char (ignore, invoking_key));
else if (rl_last_func == rl_complete && !completion_changed_buffer)
return (rl_complete_internal ('?'));
else if (_rl_complete_show_all)
return (rl_complete_internal ('!'));
else if (_rl_complete_show_unmodified)
return (rl_complete_internal ('@'));
else
return (rl_complete_internal (TAB));
}
int
rl_possible_completions (ignore, invoking_key)
int ignore, invoking_key;
{
return (rl_complete_internal ('?'));
}
int
rl_insert_completions (ignore, invoking_key)
int ignore, invoking_key;
{
return (rl_complete_internal ('*'));
}
int
rl_completion_mode (cfunc)
rl_command_func_t *cfunc;
{
if (rl_last_func == cfunc && !completion_changed_buffer)
return '?';
else if (_rl_complete_show_all)
return '!';
else if (_rl_complete_show_unmodified)
return '@';
else
return TAB;
}
static void
set_completion_defaults (what_to_do)
int what_to_do;
{
rl_filename_completion_desired = 0;
rl_filename_quoting_desired = 1;
rl_completion_type = what_to_do;
rl_completion_suppress_append = rl_completion_suppress_quote = 0;
rl_completion_mark_symlink_dirs = _rl_complete_mark_symlink_dirs;
}
static int
get_y_or_n (for_pager)
int for_pager;
{
int c;
for (;;)
{
RL_SETSTATE(RL_STATE_MOREINPUT);
c = rl_read_key ();
RL_UNSETSTATE(RL_STATE_MOREINPUT);
if (c == 'y' || c == 'Y' || c == ' ')
return (1);
if (c == 'n' || c == 'N' || c == RUBOUT)
return (0);
if (c == ABORT_CHAR || c < 0)
_rl_abort_internal ();
if (for_pager && (c == NEWLINE || c == RETURN))
return (2);
if (for_pager && (c == 'q' || c == 'Q'))
return (0);
rl_ding ();
}
}
static int
_rl_internal_pager (lines)
int lines;
{
int i;
fprintf (rl_outstream, "--More--");
fflush (rl_outstream);
i = get_y_or_n (1);
_rl_erase_entire_line ();
if (i == 0)
return -1;
else if (i == 2)
return (lines - 1);
else
return 0;
}
static int
path_isdir (filename)
const char *filename;
{
struct stat finfo;
return (stat (filename, &finfo) == 0 && S_ISDIR (finfo.st_mode));
}
#if defined (VISIBLE_STATS)
static int
stat_char (filename)
char *filename;
{
struct stat finfo;
int character, r;
#if defined (HAVE_LSTAT) && defined (S_ISLNK)
r = lstat (filename, &finfo);
#else
r = stat (filename, &finfo);
#endif
if (r == -1)
return (0);
character = 0;
if (S_ISDIR (finfo.st_mode))
character = '/';
#if defined (S_ISCHR)
else if (S_ISCHR (finfo.st_mode))
character = '%';
#endif
#if defined (S_ISBLK)
else if (S_ISBLK (finfo.st_mode))
character = '#';
#endif
#if defined (S_ISLNK)
else if (S_ISLNK (finfo.st_mode))
character = '@';
#endif
#if defined (S_ISSOCK)
else if (S_ISSOCK (finfo.st_mode))
character = '=';
#endif
#if defined (S_ISFIFO)
else if (S_ISFIFO (finfo.st_mode))
character = '|';
#endif
else if (S_ISREG (finfo.st_mode))
{
if (access (filename, X_OK) == 0)
character = '*';
}
return (character);
}
#endif
static char *
printable_part (pathname)
char *pathname;
{
char *temp, *x;
if (rl_filename_completion_desired == 0)
return (pathname);
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);
else if (temp[1] == '\0')
{
for (x = temp - 1; x > pathname; x--)
if (*x == '/')
break;
return ((*x == '/') ? x + 1 : pathname);
}
else
return ++temp;
}
static int
fnwidth (string)
const char *string;
{
int width, pos;
#if defined (HANDLE_MULTIBYTE)
mbstate_t ps;
int left, w;
size_t clen;
wchar_t wc;
left = strlen (string) + 1;
memset (&ps, 0, sizeof (mbstate_t));
#endif
width = pos = 0;
while (string[pos])
{
if (CTRL_CHAR (*string) || *string == RUBOUT)
{
width += 2;
pos++;
}
else
{
#if defined (HANDLE_MULTIBYTE)
clen = mbrtowc (&wc, string + pos, left - pos, &ps);
if (MB_INVALIDCH (clen))
{
width++;
pos++;
memset (&ps, 0, sizeof (mbstate_t));
}
else if (MB_NULLWCH (clen))
break;
else
{
pos += clen;
w = wcwidth (wc);
width += (w >= 0) ? w : 1;
}
#else
width++;
pos++;
#endif
}
}
return width;
}
static int
fnprint (to_print)
const char *to_print;
{
int printed_len;
const char *s;
#if defined (HANDLE_MULTIBYTE)
mbstate_t ps;
const char *end;
size_t tlen;
int width, w;
wchar_t wc;
end = to_print + strlen (to_print) + 1;
memset (&ps, 0, sizeof (mbstate_t));
#endif
printed_len = 0;
s = to_print;
while (*s)
{
if (CTRL_CHAR (*s))
{
putc ('^', rl_outstream);
putc (UNCTRL (*s), rl_outstream);
printed_len += 2;
s++;
#if defined (HANDLE_MULTIBYTE)
memset (&ps, 0, sizeof (mbstate_t));
#endif
}
else if (*s == RUBOUT)
{
putc ('^', rl_outstream);
putc ('?', rl_outstream);
printed_len += 2;
s++;
#if defined (HANDLE_MULTIBYTE)
memset (&ps, 0, sizeof (mbstate_t));
#endif
}
else
{
#if defined (HANDLE_MULTIBYTE)
tlen = mbrtowc (&wc, s, end - s, &ps);
if (MB_INVALIDCH (tlen))
{
tlen = 1;
width = 1;
memset (&ps, 0, sizeof (mbstate_t));
}
else if (MB_NULLWCH (tlen))
break;
else
{
w = wcwidth (wc);
width = (w >= 0) ? w : 1;
}
fwrite (s, 1, tlen, rl_outstream);
s += tlen;
printed_len += width;
#else
putc (*s, rl_outstream);
s++;
printed_len++;
#endif
}
}
return printed_len;
}
static int
print_filename (to_print, full_pathname)
char *to_print, *full_pathname;
{
int printed_len, extension_char, slen, tlen;
char *s, c, *new_full_pathname, *dn;
extension_char = 0;
printed_len = fnprint (to_print);
#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)
#endif
{
if (to_print != full_pathname)
{
c = to_print[-1];
to_print[-1] = '\0';
if (full_pathname == 0 || *full_pathname == 0)
dn = "/";
else if (full_pathname[0] != '/')
dn = full_pathname;
else if (full_pathname[1] == 0)
dn = "//";
else if (full_pathname[1] == '/' && full_pathname[2] == 0)
dn = "/";
else
dn = full_pathname;
s = tilde_expand (dn);
if (rl_directory_completion_hook)
(*rl_directory_completion_hook) (&s);
slen = strlen (s);
tlen = strlen (to_print);
new_full_pathname = (char *)xmalloc (slen + tlen + 2);
strcpy (new_full_pathname, s);
if (s[slen - 1] == '/')
slen--;
else
new_full_pathname[slen] = '/';
new_full_pathname[slen] = '/';
strcpy (new_full_pathname + slen + 1, to_print);
#if defined (VISIBLE_STATS)
if (rl_visible_stats)
extension_char = stat_char (new_full_pathname);
else
#endif
if (path_isdir (new_full_pathname))
extension_char = '/';
free (new_full_pathname);
to_print[-1] = c;
}
else
{
s = tilde_expand (full_pathname);
#if defined (VISIBLE_STATS)
if (rl_visible_stats)
extension_char = stat_char (s);
else
#endif
if (path_isdir (s))
extension_char = '/';
}
free (s);
if (extension_char)
{
putc (extension_char, rl_outstream);
printed_len++;
}
}
return printed_len;
}
static char *
rl_quote_filename (s, rtype, qcp)
char *s;
int rtype;
char *qcp;
{
char *r;
r = (char *)xmalloc (strlen (s) + 2);
*r = *rl_completer_quote_characters;
strcpy (r + 1, s);
if (qcp)
*qcp = *rl_completer_quote_characters;
return r;
}
char
_rl_find_completion_word (fp, dp)
int *fp, *dp;
{
int scan, end, found_quote, delimiter, pass_next, isbrk;
char quote_char, *brkchars;
end = rl_point;
found_quote = delimiter = 0;
quote_char = '\0';
brkchars = 0;
if (rl_completion_word_break_hook)
brkchars = (*rl_completion_word_break_hook) ();
if (brkchars == 0)
brkchars = rl_completer_word_break_characters;
if (rl_completer_quote_characters)
{
for (scan = pass_next = 0; scan < end; scan = MB_NEXTCHAR (rl_line_buffer, scan, 1, MB_FIND_ANY))
{
if (pass_next)
{
pass_next = 0;
continue;
}
if (quote_char != '\'' && rl_line_buffer[scan] == '\\')
{
pass_next = 1;
found_quote |= RL_QF_BACKSLASH;
continue;
}
if (quote_char != '\0')
{
if (rl_line_buffer[scan] == quote_char)
{
quote_char = '\0';
rl_point = end;
}
}
else if (strchr (rl_completer_quote_characters, rl_line_buffer[scan]))
{
quote_char = rl_line_buffer[scan];
rl_point = scan + 1;
if (quote_char == '\'')
found_quote |= RL_QF_SINGLE_QUOTE;
else if (quote_char == '"')
found_quote |= RL_QF_DOUBLE_QUOTE;
else
found_quote |= RL_QF_OTHER_QUOTE;
}
}
}
if (rl_point == end && quote_char == '\0')
{
while (rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_ANY))
{
scan = rl_line_buffer[rl_point];
if (strchr (brkchars, scan) == 0)
continue;
if (rl_char_is_quoted_p && found_quote &&
(*rl_char_is_quoted_p) (rl_line_buffer, rl_point))
continue;
break;
}
}
scan = rl_line_buffer[rl_point];
if (scan)
{
if (rl_char_is_quoted_p)
isbrk = (found_quote == 0 ||
(*rl_char_is_quoted_p) (rl_line_buffer, rl_point) == 0) &&
strchr (brkchars, scan) != 0;
else
isbrk = strchr (brkchars, scan) != 0;
if (isbrk)
{
if (rl_basic_quote_characters &&
strchr (rl_basic_quote_characters, scan) &&
(end - rl_point) > 1)
delimiter = scan;
if (rl_special_prefixes == 0 || strchr (rl_special_prefixes, scan) == 0)
rl_point++;
}
}
if (fp)
*fp = found_quote;
if (dp)
*dp = delimiter;
return (quote_char);
}
static char **
gen_completion_matches (text, start, end, our_func, found_quote, quote_char)
char *text;
int start, end;
rl_compentry_func_t *our_func;
int found_quote, quote_char;
{
char **matches;
rl_completion_found_quote = found_quote;
rl_completion_quote_character = quote_char;
if (rl_attempted_completion_function)
{
matches = (*rl_attempted_completion_function) (text, start, end);
if (matches || rl_attempted_completion_over)
{
rl_attempted_completion_over = 0;
return (matches);
}
}
matches = rl_completion_matches (text, our_func);
return matches;
}
static char **
remove_duplicate_matches (matches)
char **matches;
{
char *lowest_common;
int i, j, newlen;
char dead_slot;
char **temp_array;
for (i = 0; matches[i]; i++)
;
if (i)
qsort (matches+1, i-1, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare);
lowest_common = savestring (matches[0]);
for (i = newlen = 0; matches[i + 1]; i++)
{
if (strcmp (matches[i], matches[i + 1]) == 0)
{
free (matches[i]);
matches[i] = (char *)&dead_slot;
}
else
newlen++;
}
temp_array = (char **)xmalloc ((3 + newlen) * sizeof (char *));
for (i = j = 1; matches[i]; i++)
{
if (matches[i] != (char *)&dead_slot)
temp_array[j++] = matches[i];
}
temp_array[j] = (char *)NULL;
if (matches[0] != (char *)&dead_slot)
free (matches[0]);
temp_array[0] = lowest_common;
if (j == 2 && strcmp (temp_array[0], temp_array[1]) == 0)
{
free (temp_array[1]);
temp_array[1] = (char *)NULL;
}
return (temp_array);
}
static int
compute_lcd_of_matches (match_list, matches, text)
char **match_list;
int matches;
const char *text;
{
register int i, c1, c2, si;
int low;
char *dtext;
#if defined (HANDLE_MULTIBYTE)
int v;
mbstate_t ps1, ps2;
wchar_t wc1, wc2;
#endif
if (matches == 1)
{
match_list[0] = match_list[1];
match_list[1] = (char *)NULL;
return 1;
}
for (i = 1, low = 100000; i < matches; i++)
{
#if defined (HANDLE_MULTIBYTE)
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{
memset (&ps1, 0, sizeof (mbstate_t));
memset (&ps2, 0, sizeof (mbstate_t));
}
#endif
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]));
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);
wc1 = towlower (wc1);
wc2 = towlower (wc2);
if (wc1 != wc2)
break;
else if (v > 1)
si += v - 1;
}
else
#endif
if (c1 != c2)
break;
}
else
{
for (si = 0;
(c1 = match_list[i][si]) &&
(c2 = 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))
break;
else if ((v = _rl_get_char_len (&match_list[i][si], &ps_back)) > 1)
si += v - 1;
}
else
#endif
if (c1 != c2)
break;
}
if (low > si)
low = si;
}
if (low == 0 && text && *text)
{
match_list[0] = (char *)xmalloc (strlen (text) + 1);
strcpy (match_list[0], text);
}
else
{
match_list[0] = (char *)xmalloc (low + 1);
if (_rl_completion_case_fold)
{
dtext = (char *)NULL;
if (rl_filename_completion_desired &&
rl_filename_dequoting_function &&
rl_completion_found_quote &&
rl_filename_quoting_desired)
{
dtext = (*rl_filename_dequoting_function) ((char *)text, rl_completion_quote_character);
text = dtext;
}
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;
}
if (i > matches)
strncpy (match_list[0], match_list[1], low);
}
else
strncpy (match_list[0], text, low);
FREE (dtext);
}
else
strncpy (match_list[0], match_list[1], low);
match_list[0][low] = '\0';
}
return matches;
}
static int
postprocess_matches (matchesp, matching_filenames)
char ***matchesp;
int matching_filenames;
{
char *t, **matches, **temp_matches;
int nmatch, i;
matches = *matchesp;
if (matches == 0)
return 0;
if (rl_ignore_completion_duplicates)
{
temp_matches = remove_duplicate_matches (matches);
free (matches);
matches = temp_matches;
}
if (rl_ignore_some_completions_function && matching_filenames)
{
for (nmatch = 1; matches[nmatch]; nmatch++)
;
(void)(*rl_ignore_some_completions_function) (matches);
if (matches == 0 || matches[0] == 0)
{
FREE (matches);
*matchesp = (char **)0;
return 0;
}
else
{
for (i = 1; matches[i]; i++)
;
if (i > 1 && i < nmatch)
{
t = matches[0];
compute_lcd_of_matches (matches, i - 1, t);
FREE (t);
}
}
}
*matchesp = matches;
return (1);
}
void
rl_display_match_list (matches, len, max)
char **matches;
int len, max;
{
int count, limit, printed_len, lines;
int i, j, k, l;
char *temp;
max += 2;
limit = _rl_screenwidth / max;
if (limit != 1 && (limit * max == _rl_screenwidth))
limit--;
if (limit == 0)
limit = 1;
count = (len + (limit - 1)) / limit;
if (rl_ignore_completion_duplicates == 0)
qsort (matches + 1, len, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare);
rl_crlf ();
lines = 0;
if (_rl_print_completions_horizontally == 0)
{
for (i = 1; i <= count; i++)
{
for (j = 0, l = i; j < limit; j++)
{
if (l > len || matches[l] == 0)
break;
else
{
temp = printable_part (matches[l]);
printed_len = print_filename (temp, matches[l]);
if (j + 1 < limit)
for (k = 0; k < max - printed_len; k++)
putc (' ', rl_outstream);
}
l += count;
}
rl_crlf ();
lines++;
if (_rl_page_completions && lines >= (_rl_screenheight - 1) && i < count)
{
lines = _rl_internal_pager (lines);
if (lines < 0)
return;
}
}
}
else
{
for (i = 1; matches[i]; i++)
{
temp = printable_part (matches[i]);
printed_len = print_filename (temp, matches[i]);
if (matches[i+1])
{
if (i && (limit > 1) && (i % limit) == 0)
{
rl_crlf ();
lines++;
if (_rl_page_completions && lines >= _rl_screenheight - 1)
{
lines = _rl_internal_pager (lines);
if (lines < 0)
return;
}
}
else
for (k = 0; k < max - printed_len; k++)
putc (' ', rl_outstream);
}
}
rl_crlf ();
}
}
static void
display_matches (matches)
char **matches;
{
int len, max, i;
char *temp;
_rl_move_vert (_rl_vis_botlin);
if (matches[1] == 0)
{
temp = printable_part (matches[0]);
rl_crlf ();
print_filename (temp, matches[0]);
rl_crlf ();
rl_forced_update_display ();
rl_display_fixed = 1;
return;
}
for (max = 0, i = 1; matches[i]; i++)
{
temp = printable_part (matches[i]);
len = fnwidth (temp);
if (len > max)
max = len;
}
len = i - 1;
if (rl_completion_display_matches_hook)
{
(*rl_completion_display_matches_hook) (matches, len, max);
return;
}
if (rl_completion_query_items > 0 && len >= rl_completion_query_items)
{
rl_crlf ();
fprintf (rl_outstream, "Display all %d possibilities? (y or n)", len);
fflush (rl_outstream);
if (get_y_or_n (0) == 0)
{
rl_crlf ();
rl_forced_update_display ();
rl_display_fixed = 1;
return;
}
}
rl_display_match_list (matches, len, max);
rl_forced_update_display ();
rl_display_fixed = 1;
}
static char *
make_quoted_replacement (match, mtype, qc)
char *match;
int mtype;
char *qc;
{
int should_quote, do_replace;
char *replacement;
replacement = match;
should_quote = match && rl_completer_quote_characters &&
rl_filename_completion_desired &&
rl_filename_quoting_desired;
if (should_quote)
should_quote = should_quote && (!qc || !*qc ||
(rl_completer_quote_characters && strchr (rl_completer_quote_characters, *qc)));
if (should_quote)
{
should_quote = rl_filename_quote_characters
? (_rl_strpbrk (match, rl_filename_quote_characters) != 0)
: 0;
do_replace = should_quote ? mtype : NO_MATCH;
if (do_replace != NO_MATCH && rl_filename_quoting_function)
replacement = (*rl_filename_quoting_function) (match, do_replace, qc);
}
return (replacement);
}
static void
insert_match (match, start, mtype, qc)
char *match;
int start, mtype;
char *qc;
{
char *replacement;
char oqc;
oqc = qc ? *qc : '\0';
replacement = make_quoted_replacement (match, mtype, qc);
if (replacement)
{
if (qc && *qc && start && rl_line_buffer[start - 1] == *qc &&
replacement[0] == *qc)
start--;
else if (qc && (*qc != oqc) && start && rl_line_buffer[start - 1] == oqc &&
replacement[0] != oqc)
start--;
_rl_replace_text (replacement, start, rl_point - 1);
if (replacement != match)
free (replacement);
}
}
static int
append_to_match (text, delimiter, quote_char, nontrivial_match)
char *text;
int delimiter, quote_char, nontrivial_match;
{
char temp_string[4], *filename;
int temp_string_index, s;
struct stat finfo;
temp_string_index = 0;
if (quote_char && rl_point && rl_completion_suppress_quote == 0 &&
rl_line_buffer[rl_point - 1] != quote_char)
temp_string[temp_string_index++] = quote_char;
if (delimiter)
temp_string[temp_string_index++] = delimiter;
else if (rl_completion_suppress_append == 0 && rl_completion_append_character)
temp_string[temp_string_index++] = rl_completion_append_character;
temp_string[temp_string_index++] = '\0';
if (rl_filename_completion_desired)
{
filename = tilde_expand (text);
s = (nontrivial_match && rl_completion_mark_symlink_dirs == 0)
? LSTAT (filename, &finfo)
: stat (filename, &finfo);
if (s == 0 && S_ISDIR (finfo.st_mode))
{
if (_rl_complete_mark_directories )
{
if (rl_point && rl_line_buffer[rl_point] == '\0' && rl_line_buffer[rl_point - 1] == '/')
;
else if (rl_line_buffer[rl_point] != '/')
rl_insert_text ("/");
}
}
#ifdef S_ISLNK
else if (s == 0 && S_ISLNK (finfo.st_mode) &&
stat (filename, &finfo) == 0 && S_ISDIR (finfo.st_mode))
;
#endif
else
{
if (rl_point == rl_end && temp_string_index)
rl_insert_text (temp_string);
}
free (filename);
}
else
{
if (rl_point == rl_end && temp_string_index)
rl_insert_text (temp_string);
}
return (temp_string_index);
}
static void
insert_all_matches (matches, point, qc)
char **matches;
int point;
char *qc;
{
int i;
char *rp;
rl_begin_undo_group ();
if (qc && *qc && point && rl_line_buffer[point - 1] == *qc)
point--;
rl_delete_text (point, rl_point);
rl_point = point;
if (matches[1])
{
for (i = 1; matches[i]; i++)
{
rp = make_quoted_replacement (matches[i], SINGLE_MATCH, qc);
rl_insert_text (rp);
rl_insert_text (" ");
if (rp != matches[i])
free (rp);
}
}
else
{
rp = make_quoted_replacement (matches[0], SINGLE_MATCH, qc);
rl_insert_text (rp);
rl_insert_text (" ");
if (rp != matches[0])
free (rp);
}
rl_end_undo_group ();
}
void
_rl_free_match_list (matches)
char **matches;
{
register int i;
if (matches == 0)
return;
for (i = 0; matches[i]; i++)
free (matches[i]);
free (matches);
}
int
rl_complete_internal (what_to_do)
int what_to_do;
{
char **matches;
rl_compentry_func_t *our_func;
int start, end, delimiter, found_quote, i, nontrivial_lcd;
char *text, *saved_line_buffer;
char quote_char;
RL_SETSTATE(RL_STATE_COMPLETING);
set_completion_defaults (what_to_do);
saved_line_buffer = rl_line_buffer ? savestring (rl_line_buffer) : (char *)NULL;
our_func = rl_completion_entry_function
? rl_completion_entry_function
: rl_filename_completion_function;
end = rl_point;
found_quote = delimiter = 0;
quote_char = '\0';
if (rl_point)
quote_char = _rl_find_completion_word (&found_quote, &delimiter);
start = rl_point;
rl_point = end;
text = rl_copy_text (start, end);
matches = gen_completion_matches (text, start, end, our_func, found_quote, quote_char);
nontrivial_lcd = matches && strcmp (text, matches[0]) != 0;
free (text);
if (matches == 0)
{
rl_ding ();
FREE (saved_line_buffer);
completion_changed_buffer = 0;
RL_UNSETSTATE(RL_STATE_COMPLETING);
return (0);
}
i = rl_filename_completion_desired;
if (postprocess_matches (&matches, i) == 0)
{
rl_ding ();
FREE (saved_line_buffer);
completion_changed_buffer = 0;
RL_UNSETSTATE(RL_STATE_COMPLETING);
return (0);
}
switch (what_to_do)
{
case TAB:
case '!':
case '@':
if (*matches[0])
insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char);
if (matches[1])
{
if (what_to_do == '!')
{
display_matches (matches);
break;
}
else if (what_to_do == '@')
{
if (nontrivial_lcd == 0)
display_matches (matches);
break;
}
else if (rl_editing_mode != vi_mode)
rl_ding ();
}
else
append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd);
break;
case '*':
insert_all_matches (matches, start, "e_char);
break;
case '?':
display_matches (matches);
break;
default:
fprintf (stderr, "\r\nreadline: bad value %d for what_to_do in rl_complete\n", what_to_do);
rl_ding ();
FREE (saved_line_buffer);
RL_UNSETSTATE(RL_STATE_COMPLETING);
return 1;
}
_rl_free_match_list (matches);
if (saved_line_buffer)
{
completion_changed_buffer = strcmp (rl_line_buffer, saved_line_buffer) != 0;
free (saved_line_buffer);
}
RL_UNSETSTATE(RL_STATE_COMPLETING);
return 0;
}
char **
rl_completion_matches (text, entry_function)
const char *text;
rl_compentry_func_t *entry_function;
{
int match_list_size;
char **match_list;
int matches;
char *string;
matches = 0;
match_list_size = 10;
match_list = (char **)xmalloc ((match_list_size + 1) * sizeof (char *));
match_list[1] = (char *)NULL;
while (string = (*entry_function) (text, matches))
{
if (matches + 1 == match_list_size)
match_list = (char **)xrealloc
(match_list, ((match_list_size += 10) + 1) * sizeof (char *));
match_list[++matches] = string;
match_list[matches + 1] = (char *)NULL;
}
if (matches)
compute_lcd_of_matches (match_list, matches, text);
else
{
free (match_list);
match_list = (char **)NULL;
}
return (match_list);
}
char *
rl_username_completion_function (text, state)
const char *text;
int state;
{
#if defined (__WIN32__) || defined (__OPENNT)
return (char *)NULL;
#else
static char *username = (char *)NULL;
static struct passwd *entry;
static int namelen, first_char, first_char_loc;
char *value;
if (state == 0)
{
FREE (username);
first_char = *text;
first_char_loc = first_char == '~';
username = savestring (&text[first_char_loc]);
namelen = strlen (username);
setpwent ();
}
#if defined (HAVE_GETPWENT)
while (entry = getpwent ())
{
if (namelen == 0 || (STREQN (username, entry->pw_name, namelen)))
break;
}
#endif
if (entry == 0)
{
#if defined (HAVE_GETPWENT)
endpwent ();
#endif
return ((char *)NULL);
}
else
{
value = (char *)xmalloc (2 + strlen (entry->pw_name));
*value = *text;
strcpy (value + first_char_loc, entry->pw_name);
if (first_char == '~')
rl_filename_completion_desired = 1;
return (value);
}
#endif
}
char *
rl_filename_completion_function (text, state)
const char *text;
int state;
{
static DIR *directory = (DIR *)NULL;
static char *filename = (char *)NULL;
static char *dirname = (char *)NULL;
static char *users_dirname = (char *)NULL;
static int filename_len;
char *temp;
int dirlen;
struct dirent *entry;
if (state == 0)
{
if (directory)
{
closedir (directory);
directory = (DIR *)NULL;
}
FREE (dirname);
FREE (filename);
FREE (users_dirname);
filename = savestring (text);
if (*text == 0)
text = ".";
dirname = savestring (text);
temp = strrchr (dirname, '/');
#if defined (__MSDOS__)
if (dirname[0] == '/' && dirname[1] == '/' && ISALPHA ((unsigned char)dirname[2]) && dirname[3] == '/')
temp = strrchr (dirname + 3, '/');
#endif
if (temp)
{
strcpy (filename, ++temp);
*temp = '\0';
}
#if defined (__MSDOS__)
else if (ISALPHA ((unsigned char)dirname[0]) && dirname[1] == ':')
{
strcpy (filename, dirname + 2);
dirname[2] = '\0';
}
#endif
else
{
dirname[0] = '.';
dirname[1] = '\0';
}
users_dirname = savestring (dirname);
if (*dirname == '~')
{
temp = tilde_expand (dirname);
free (dirname);
dirname = temp;
}
if (rl_directory_rewrite_hook)
(*rl_directory_rewrite_hook) (&dirname);
if (rl_directory_completion_hook && (*rl_directory_completion_hook) (&dirname))
{
free (users_dirname);
users_dirname = savestring (dirname);
}
else if (rl_completion_found_quote && rl_filename_dequoting_function)
{
temp = (*rl_filename_dequoting_function) (users_dirname, rl_completion_quote_character);
free (users_dirname);
users_dirname = temp;
}
directory = opendir (dirname);
if (filename && *filename && rl_completion_found_quote && rl_filename_dequoting_function)
{
temp = (*rl_filename_dequoting_function) (filename, rl_completion_quote_character);
free (filename);
filename = temp;
}
filename_len = strlen (filename);
rl_filename_completion_desired = 1;
}
entry = (struct dirent *)NULL;
while (directory && (entry = readdir (directory)))
{
if (filename_len == 0)
{
if (_rl_match_hidden_files == 0 && HIDDEN_FILE (entry->d_name))
continue;
if (entry->d_name[0] != '.' ||
(entry->d_name[1] &&
(entry->d_name[1] != '.' || entry->d_name[2])))
break;
}
else
{
if (_rl_completion_case_fold)
{
if ((_rl_to_lower (entry->d_name[0]) == _rl_to_lower (filename[0])) &&
(((int)D_NAMLEN (entry)) >= filename_len) &&
(_rl_strnicmp (filename, entry->d_name, filename_len) == 0))
break;
}
else
{
if ((entry->d_name[0] == filename[0]) &&
(((int)D_NAMLEN (entry)) >= filename_len) &&
(strncmp (filename, entry->d_name, filename_len) == 0))
break;
}
}
}
if (entry == 0)
{
if (directory)
{
closedir (directory);
directory = (DIR *)NULL;
}
if (dirname)
{
free (dirname);
dirname = (char *)NULL;
}
if (filename)
{
free (filename);
filename = (char *)NULL;
}
if (users_dirname)
{
free (users_dirname);
users_dirname = (char *)NULL;
}
return (char *)NULL;
}
else
{
if (dirname && (dirname[0] != '.' || dirname[1]))
{
if (rl_complete_with_tilde_expansion && *users_dirname == '~')
{
dirlen = strlen (dirname);
temp = (char *)xmalloc (2 + dirlen + D_NAMLEN (entry));
strcpy (temp, dirname);
if (dirname[dirlen - 1] != '/')
{
temp[dirlen++] = '/';
temp[dirlen] = '\0';
}
}
else
{
dirlen = strlen (users_dirname);
temp = (char *)xmalloc (2 + dirlen + D_NAMLEN (entry));
strcpy (temp, users_dirname);
if (users_dirname[dirlen - 1] != '/')
temp[dirlen++] = '/';
}
strcpy (temp + dirlen, entry->d_name);
}
else
temp = savestring (entry->d_name);
return (temp);
}
}
int
rl_menu_complete (count, ignore)
int count, ignore;
{
rl_compentry_func_t *our_func;
int matching_filenames, found_quote;
static char *orig_text;
static char **matches = (char **)0;
static int match_list_index = 0;
static int match_list_size = 0;
static int orig_start, orig_end;
static char quote_char;
static int delimiter;
if (rl_last_func != rl_menu_complete)
{
FREE (orig_text);
if (matches)
_rl_free_match_list (matches);
match_list_index = match_list_size = 0;
matches = (char **)NULL;
set_completion_defaults ('%');
our_func = rl_completion_entry_function
? rl_completion_entry_function
: rl_filename_completion_function;
orig_end = rl_point;
found_quote = delimiter = 0;
quote_char = '\0';
if (rl_point)
quote_char = _rl_find_completion_word (&found_quote, &delimiter);
orig_start = rl_point;
rl_point = orig_end;
orig_text = rl_copy_text (orig_start, orig_end);
matches = gen_completion_matches (orig_text, orig_start, orig_end,
our_func, found_quote, quote_char);
matching_filenames = rl_filename_completion_desired;
if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0)
{
rl_ding ();
FREE (matches);
matches = (char **)0;
FREE (orig_text);
orig_text = (char *)0;
completion_changed_buffer = 0;
return (0);
}
for (match_list_size = 0; matches[match_list_size]; match_list_size++)
;
}
if (matches == 0 || match_list_size == 0)
{
rl_ding ();
FREE (matches);
matches = (char **)0;
completion_changed_buffer = 0;
return (0);
}
match_list_index += count;
if (match_list_index < 0)
match_list_index += match_list_size;
else
match_list_index %= match_list_size;
if (match_list_index == 0 && match_list_size > 1)
{
rl_ding ();
insert_match (orig_text, orig_start, MULT_MATCH, "e_char);
}
else
{
insert_match (matches[match_list_index], orig_start, SINGLE_MATCH, "e_char);
append_to_match (matches[match_list_index], delimiter, quote_char,
strcmp (orig_text, matches[match_list_index]));
}
completion_changed_buffer = 1;
return (0);
}