The GNU Readline Library is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 1, or
+ as published by the Free Software Foundation; either version 2, or
(at your option) any later version.
The GNU Readline Library is distributed in the hope that it will be
The GNU General Public License is often shipped with GNU software, and
is generally kept in a file called COPYING or LICENSE. If you do not
have a copy of the license, write to the Free Software Foundation,
- 675 Mass Ave, Cambridge, MA 02139, USA. */
+ 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
#define READLINE_LIBRARY
#if defined (HAVE_CONFIG_H)
/* Some standard library routines. */
#include "readline.h"
+#include "xmalloc.h"
+#include "rlprivate.h"
-extern char *tilde_expand ();
-extern char *rl_copy_text ();
-extern void _rl_abort_internal ();
-extern int _rl_qsort_string_compare ();
-extern void _rl_replace_text ();
-
-extern Function *rl_last_func;
-extern int rl_editing_mode;
-extern int screenwidth;
+#ifdef __STDC__
+typedef int QSFUNC (const void *, const void *);
+#else
+typedef int QSFUNC ();
+#endif
-extern void _rl_move_vert ();
-extern int _rl_vis_botlin;
-extern int rl_display_fixed;
+/* If non-zero, then this is the address of a function to call when
+ completing a word would normally display the list of possible matches.
+ This function is called instead of actually doing the display.
+ It takes three arguments: (char **matches, int num_matches, int max_length)
+ where MATCHES is the array of strings that matched, NUM_MATCHES is the
+ number of strings in that array, and MAX_LENGTH is the length of the
+ longest string in that array. */
+VFunction *rl_completion_display_matches_hook = (VFunction *)NULL;
/* Forward declarations for functions defined and used in this file. */
-char *filename_completion_function ();
-char **completion_matches ();
+char *filename_completion_function __P((char *, int));
+char **completion_matches __P((char *, CPFunction *));
#if defined (VISIBLE_STATS)
# if !defined (X_OK)
# define X_OK 1
# endif
-static int stat_char ();
+static int stat_char __P((char *));
#endif
-static char *rl_quote_filename ();
-static char *rl_strpbrk ();
-
-static char **remove_duplicate_matches ();
-static void insert_match ();
-static int append_to_match ();
-static void insert_all_matches ();
-static void display_matches ();
-static int compute_lcd_of_matches ();
+static char *rl_quote_filename __P((char *, int, char *));
+static char *rl_strpbrk __P((char *, char *));
-extern char *xmalloc (), *xrealloc ();
+static char **remove_duplicate_matches __P((char **));
+static void insert_match __P((char *, int, int, char *));
+static int append_to_match __P((char *, int, int));
+static void insert_all_matches __P((char **, int, char *));
+static void display_matches __P((char **));
+static int compute_lcd_of_matches __P((char **, int, char *));
/* **************************************************************** */
/* */
int _rl_print_completions_horizontally;
/* Non-zero means that case is not significant in filename completion. */
+#if defined (__MSDOS__) && !defined (__DJGPP__)
+int _rl_completion_case_fold = 1;
+#else
int _rl_completion_case_fold;
+#endif
/* Global variables available to applications using readline. */
char *temp;
temp = rl_filename_completion_desired ? strrchr (pathname, '/') : (char *)NULL;
+#if defined (__MSDOS__)
+ if (rl_filename_completion_desired && temp == 0 && isalpha (pathname[0]) && pathname[1] == ':')
+ temp = pathname + 1;
+#endif
return (temp ? ++temp : pathname);
}
c = to_print[-1];
to_print[-1] = '\0';
- s = tilde_expand (full_pathname);
+ /* If setting the last slash in full_pathname to a NUL results in
+ full_pathname being the empty string, we are trying to complete
+ files in the root directory. If we pass a null string to the
+ bash directory completion hook, for example, it will expand it
+ to the current directory. We just want the `/'. */
+ s = tilde_expand (full_pathname && *full_pathname ? full_pathname : "/");
if (rl_directory_completion_hook)
(*rl_directory_completion_hook) (&s);
/* If there is an application-specific function to say whether or not
a character is quoted and we found a quote character, let that
function decide whether or not a character is a word break, even
- if it is found in rl_completer_word_break_characters. */
- if (rl_char_is_quoted_p)
- isbrk = (found_quote == 0 ||
- (*rl_char_is_quoted_p) (rl_line_buffer, rl_point) == 0) &&
- strchr (rl_completer_word_break_characters, scan) != 0;
- else
- isbrk = strchr (rl_completer_word_break_characters, scan) != 0;
-
- if (isbrk)
+ if it is found in rl_completer_word_break_characters. Don't bother
+ if we're at the end of the line, though. */
+ if (scan)
{
- /* If the character that caused the word break was a quoting
- character, then remember it as the delimiter. */
- if (rl_basic_quote_characters && strchr (rl_basic_quote_characters, scan) && (end - rl_point) > 1)
- delimiter = scan;
-
- /* If the character isn't needed to determine something special
- about what kind of completion to perform, then advance past it. */
- if (rl_special_prefixes == 0 || strchr (rl_special_prefixes, scan) == 0)
- rl_point++;
+ if (rl_char_is_quoted_p)
+ isbrk = (found_quote == 0 ||
+ (*rl_char_is_quoted_p) (rl_line_buffer, rl_point) == 0) &&
+ strchr (rl_completer_word_break_characters, scan) != 0;
+ else
+ isbrk = strchr (rl_completer_word_break_characters, scan) != 0;
+
+ if (isbrk)
+ {
+ /* If the character that caused the word break was a quoting
+ character, then remember it as the delimiter. */
+ if (rl_basic_quote_characters &&
+ strchr (rl_basic_quote_characters, scan) &&
+ (end - rl_point) > 1)
+ delimiter = scan;
+
+ /* If the character isn't needed to determine something special
+ about what kind of completion to perform, then advance past it. */
+ if (rl_special_prefixes == 0 || strchr (rl_special_prefixes, scan) == 0)
+ rl_point++;
+ }
}
if (fp)
we are doing filename completion and the application has defined a
filename dequoting function. */
temp = (char *)NULL;
+
if (found_quote && our_func == (Function *)filename_completion_function &&
rl_filename_dequoting_function)
{
text = temp; /* not freeing text is not a memory leak */
}
- matches = completion_matches (text, our_func);
+ matches = completion_matches (text, (CPFunction *)our_func);
FREE (temp);
return matches;
}
/* Sort the array without matches[0], since we need it to
stay in place no matter what. */
if (i)
- qsort (matches+1, i-1, sizeof (char *), _rl_qsort_string_compare);
+ 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]);
}
static int
-postprocess_matches (text, matchesp, matching_filenames)
- char *text;
+postprocess_matches (matchesp, matching_filenames)
char ***matchesp;
int matching_filenames;
{
if (matches == 0 || matches[0] == 0)
{
FREE (matches);
- ding ();
*matchesp = (char **)0;
return 0;
}
if (i > 1 && i < nmatch)
{
t = matches[0];
- compute_lcd_of_matches (matches, i - 1, text);
+ compute_lcd_of_matches (matches, i - 1, t);
FREE (t);
}
}
return (1);
}
-static void
-display_matches (matches)
+/* 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,
+ and MAX is the length of the longest string in MATCHES. */
+void
+rl_display_match_list (matches, len, max)
char **matches;
+ int len, max;
{
- int len, count, limit, max, printed_len;
+ int count, limit, printed_len;
int i, j, k, l;
char *temp;
- /* Move to the last visible line of a possibly-multiple-line command. */
- _rl_move_vert (_rl_vis_botlin);
-
- /* Handle simple case first. What if there is only one answer? */
- if (matches[1] == 0)
- {
- temp = printable_part (matches[0]);
- crlf ();
- print_filename (temp, matches[0]);
- crlf ();
-#if 0
- rl_on_new_line ();
-#else
- rl_forced_update_display ();
- rl_display_fixed = 1;
-#endif
- return;
- }
-
- /* There is more than one answer. Find out how many there are,
- and find the maximum printed length of a single entry. */
- for (max = 0, i = 1; matches[i]; i++)
- {
- temp = printable_part (matches[i]);
- len = strlen (temp);
-
- if (len > max)
- max = len;
- }
-
- len = i - 1;
-
- /* If there are many items, then ask the user if she really wants to
- see them all. */
- if (len >= rl_completion_query_items)
- {
- crlf ();
- fprintf (rl_outstream, "Display all %d possibilities? (y or n)", len);
- fflush (rl_outstream);
- if (get_y_or_n () == 0)
- {
- crlf ();
-#if 0
- rl_on_new_line ();
-#else
- rl_forced_update_display ();
- rl_display_fixed = 1;
-#endif
- return;
- }
- }
-
/* How many items of MAX length can we fit in the screen window? */
max += 2;
limit = screenwidth / max;
/* Sort the items if they are not already sorted. */
if (rl_ignore_completion_duplicates == 0)
- qsort (matches + 1, len, sizeof (char *), _rl_qsort_string_compare);
+ qsort (matches + 1, len, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare);
crlf ();
}
crlf ();
}
+}
+
+/* Display MATCHES, a list of matching filenames in argv format. This
+ handles the simple case -- a single match -- first. If there is more
+ than one match, we compute the number of strings in the list and the
+ length of the longest string, which will be needed by the display
+ function. If the application wants to handle displaying the list of
+ matches itself, it sets RL_COMPLETION_DISPLAY_MATCHES_HOOK to the
+ address of a function, and we just call it. If we're handling the
+ display ourselves, we just call rl_display_match_list. We also check
+ that the list of matches doesn't exceed the user-settable threshold,
+ and ask the user if he wants to see the list if there are more matches
+ than RL_COMPLETION_QUERY_ITEMS. */
+static void
+display_matches (matches)
+ char **matches;
+{
+ int len, max, i;
+ char *temp;
+
+ /* Move to the last visible line of a possibly-multiple-line command. */
+ _rl_move_vert (_rl_vis_botlin);
+
+ /* Handle simple case first. What if there is only one answer? */
+ if (matches[1] == 0)
+ {
+ temp = printable_part (matches[0]);
+ crlf ();
+ print_filename (temp, matches[0]);
+ crlf ();
+
+ rl_forced_update_display ();
+ rl_display_fixed = 1;
+
+ return;
+ }
+
+ /* There is more than one answer. Find out how many there are,
+ and find the maximum printed length of a single entry. */
+ for (max = 0, i = 1; matches[i]; i++)
+ {
+ temp = printable_part (matches[i]);
+ len = strlen (temp);
+
+ if (len > max)
+ max = len;
+ }
+
+ len = i - 1;
+
+ /* If the caller has defined a display hook, then call that now. */
+ if (rl_completion_display_matches_hook)
+ {
+ (*rl_completion_display_matches_hook) (matches, len, max);
+ return;
+ }
+
+ /* If there are many items, then ask the user if she really wants to
+ see them all. */
+ if (len >= rl_completion_query_items)
+ {
+ crlf ();
+ fprintf (rl_outstream, "Display all %d possibilities? (y or n)", len);
+ fflush (rl_outstream);
+ if (get_y_or_n () == 0)
+ {
+ crlf ();
+
+ rl_forced_update_display ();
+ rl_display_fixed = 1;
+
+ return;
+ }
+ }
+
+ rl_display_match_list (matches, len, max);
-#if 0
- rl_on_new_line ();
-#else
rl_forced_update_display ();
rl_display_fixed = 1;
-#endif
}
static char *
rl_filename_quoting_desired;
if (should_quote)
-#if defined (SHELL)
- should_quote = should_quote && (!qc || !*qc || *qc == '"' || *qc == '\'');
-#else /* !SHELL */
- should_quote = should_quote && (!qc || !*qc);
-#endif /* !SHELL */
+ should_quote = should_quote && (!qc || !*qc ||
+ (rl_completer_quote_characters && strchr (rl_completer_quote_characters, *qc)));
if (should_quote)
{
rl_end_undo_group ();
}
+static void
+free_match_list (matches)
+ char **matches;
+{
+ register int i;
+
+ for (i = 0; matches[i]; i++)
+ free (matches[i]);
+ free (matches);
+}
+
/* Complete the word at or before point.
WHAT_TO_DO says what to do with the completion.
`?' means list the possible completions.
text = rl_copy_text (start, end);
matches = gen_completion_matches (text, start, end, our_func, found_quote, quote_char);
+ free (text);
if (matches == 0)
{
ding ();
FREE (saved_line_buffer);
- free (text);
return (0);
}
+#if 0
/* If we are matching filenames, our_func will have been set to
filename_completion_function */
i = our_func == (Function *)filename_completion_function;
- if (postprocess_matches (text, &matches, i) == 0)
+#else
+ /* If we are matching filenames, the attempted completion function will
+ have set rl_filename_completion_desired to a non-zero value. The basic
+ filename_completion_function does this. */
+ i = rl_filename_completion_desired;
+#endif
+
+ if (postprocess_matches (&matches, i) == 0)
{
+ ding ();
FREE (saved_line_buffer);
- free (text);
+ completion_changed_buffer = 0;
return (0);
}
- free (text);
-
switch (what_to_do)
{
case TAB:
return 1;
}
- for (i = 0; matches[i]; i++)
- free (matches[i]);
- free (matches);
+ free_match_list (matches);
/* Check to see if the line has changed through all of this manipulation. */
if (saved_line_buffer)
character (usually `~'). */
char *
username_completion_function (text, state)
- int state;
char *text;
+ int state;
{
-#if defined (__GO32__) || defined (__WIN32__)
+#if defined (__WIN32__) || defined (__OPENNT)
return (char *)NULL;
-#else /* !__GO32__ */
+#else /* !__WIN32__ && !__OPENNT) */
static char *username = (char *)NULL;
static struct passwd *entry;
static int namelen, first_char, first_char_loc;
return (value);
}
-#endif /* !__GO32__ */
+#endif /* !__WIN32__ && !__OPENNT */
}
/* Okay, now we write the entry_function for filename completion. In the
completion for a command. */
char *
filename_completion_function (text, state)
- int state;
char *text;
+ int state;
{
static DIR *directory = (DIR *)NULL;
static char *filename = (char *)NULL;
temp = strrchr (dirname, '/');
+#if defined (__MSDOS__)
+ /* special hack for //X/... */
+ if (dirname[0] == '/' && dirname[1] == '/' && isalpha (dirname[2]) && dirname[3] == '/')
+ temp = strrchr (dirname + 3, '/');
+#endif
+
if (temp)
{
strcpy (filename, ++temp);
*temp = '\0';
}
+#if defined (__MSDOS__)
+ /* searches from current directory on the drive */
+ else if (isalpha (dirname[0]) && dirname[1] == ':')
+ {
+ strcpy (filename, dirname + 2);
+ dirname[2] = '\0';
+ }
+#endif
else
{
dirname[0] = '.';
strcpy (temp, users_dirname);
}
- strcpy (temp + dirlen, entry->d_name); /* strcat (temp, entry->d_name); */
+ strcpy (temp + dirlen, entry->d_name);
}
else
temp = savestring (entry->d_name);
/* Clean up from previous call, if any. */
FREE (orig_text);
if (matches)
- {
- for (match_list_index = 0; matches[match_list_index]; match_list_index++)
- free (matches[match_list_index]);
- free (matches);
- }
+ free_match_list (matches);
match_list_index = match_list_size = 0;
matches = (char **)NULL;
matches = gen_completion_matches (orig_text, orig_start, orig_end,
our_func, found_quote, quote_char);
+#if 0
/* If we are matching filenames, our_func will have been set to
filename_completion_function */
matching_filenames = our_func == (Function *)filename_completion_function;
- if (matches == 0 || postprocess_matches (orig_text, &matches, matching_filenames) == 0)
+#else
+ /* If we are matching filenames, the attempted completion function will
+ have set rl_filename_completion_desired to a non-zero value. The basic
+ filename_completion_function does this. */
+ matching_filenames = rl_filename_completion_desired;
+#endif
+ if (matches == 0 || postprocess_matches (&matches, matching_filenames) == 0)
{
ding ();
FREE (matches);
if (match_list_index < 0)
match_list_index += match_list_size;
- if (match_list_index == 0)
+ if (match_list_index == 0 && match_list_size > 1)
{
ding ();
insert_match (orig_text, orig_start, MULT_MATCH, "e_char);