diff options
Diffstat (limited to 'readline/complete.c')
-rw-r--r-- | readline/complete.c | 338 |
1 files changed, 269 insertions, 69 deletions
diff --git a/readline/complete.c b/readline/complete.c index a4ff24e..d93c15a 100644 --- a/readline/complete.c +++ b/readline/complete.c @@ -1,6 +1,6 @@ /* complete.c -- filename completion for readline. */ -/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1987-2005 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -28,7 +28,7 @@ #include <sys/types.h> #include <fcntl.h> #if defined (HAVE_SYS_FILE_H) -#include <sys/file.h> +# include <sys/file.h> #endif #if defined (HAVE_UNISTD_H) @@ -48,7 +48,7 @@ extern int errno; #endif /* !errno */ -#ifdef HAVE_PWD_H +#if defined (HAVE_PWD_H) #include <pwd.h> #endif @@ -81,10 +81,9 @@ typedef int QSFUNC (); /* Most systems don't declare getpwent in <pwd.h> if _POSIX_SOURCE is defined. */ -#if defined(HAVE_GETPWENT) && \ - (!defined (HAVE_GETPW_DECLS) || defined (_POSIX_SOURCE)) +#if defined (HAVE_GETPWENT) && (!defined (HAVE_GETPW_DECLS) || defined (_POSIX_SOURCE)) extern struct passwd *getpwent PARAMS((void)); -#endif /* defiend (HAVE_GETPWENT) && (!HAVE_GETPW_DECLS || _POSIX_SOURCE) */ +#endif /* HAVE_GETPWENT && (!HAVE_GETPW_DECLS || _POSIX_SOURCE) */ /* 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. @@ -102,12 +101,16 @@ rl_compdisp_func_t *rl_completion_display_matches_hook = (rl_compdisp_func_t *)N 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)); @@ -133,6 +136,10 @@ static char *make_quoted_replacement PARAMS((char *, int, char *)); /* If non-zero, non-unique completions always show the list of matches. */ int _rl_complete_show_all = 0; +/* If non-zero, non-unique completions show the list of matches, unless it + is not possible to do partial completion and modify the line. */ +int _rl_complete_show_unmodified = 0; + /* If non-zero, completed directory names have a slash appended. */ int _rl_complete_mark_directories = 1; @@ -201,7 +208,8 @@ int rl_completion_type = 0; /* Up to this many items will be displayed in response to a possible-completions call. After that, we ask the user if - she is sure she wants to see them all. */ + she is sure she wants to see them all. A negative value means + don't ask. */ int rl_completion_query_items = 100; int _rl_page_completions = 1; @@ -217,7 +225,12 @@ const char *rl_basic_quote_characters = "\"'"; /* The list of characters that signal a break between words for rl_complete_internal. The default list is the contents of rl_basic_word_break_characters. */ -const char *rl_completer_word_break_characters = (const char *)NULL; +/*const*/ char *rl_completer_word_break_characters = (/*const*/ char *)NULL; + +/* Hook function to allow an application to set the completion word + break characters before readline breaks up the line. Allows + position-dependent word break characters. */ +rl_cpvfunc_t *rl_completion_word_break_hook = (rl_cpvfunc_t *)NULL; /* List of characters which can be used to quote a substring of the line. Completion occurs on the entire substring, and within the substring @@ -285,6 +298,19 @@ int rl_completion_suppress_append = 0; default is a space. */ int rl_completion_append_character = ' '; +/* If non-zero, the completion functions don't append any closing quote. + This is set to 0 by rl_complete_internal and may be changed by an + application-specific completion function. */ +int rl_completion_suppress_quote = 0; + +/* Set to any quote character readline thinks it finds before any application + completion function is called. */ +int rl_completion_quote_character; + +/* Set to a non-zero value if readline found quoting anywhere in the word to + be completed; set before any application completion function is called. */ +int rl_completion_found_quote; + /* If non-zero, a slash will be appended to completed filenames that are symbolic links to directory names, subject to the value of the mark-directories variable (which is user-settable). This exists so @@ -323,6 +349,8 @@ rl_complete (ignore, invoking_key) 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)); } @@ -355,6 +383,8 @@ rl_completion_mode (cfunc) return '?'; else if (_rl_complete_show_all) return '!'; + else if (_rl_complete_show_unmodified) + return '@'; else return TAB; } @@ -375,7 +405,7 @@ set_completion_defaults (what_to_do) rl_filename_completion_desired = 0; rl_filename_quoting_desired = 1; rl_completion_type = what_to_do; - rl_completion_suppress_append = 0; + rl_completion_suppress_append = rl_completion_suppress_quote = 0; /* The completion entry function may optionally change this. */ rl_completion_mark_symlink_dirs = _rl_complete_mark_symlink_dirs; @@ -426,6 +456,15 @@ _rl_internal_pager (lines) 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) /* Return the character which best describes FILENAME. `@' for symbolic links @@ -523,53 +562,149 @@ printable_part (pathname) return ++temp; } +/* Compute width of STRING when displayed on screen by print_filename */ +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; +} + /* Output TO_PRINT to rl_outstream. If VISIBLE_STATS is defined and we are using it, check for and output a single character for `special' filenames. Return the number of characters we output. */ -#define PUTX(c) \ - do { \ - if (CTRL_CHAR (c)) \ - { \ - putc ('^', rl_outstream); \ - putc (UNCTRL (c), rl_outstream); \ - printed_len += 2; \ - } \ - else if (c == RUBOUT) \ - { \ - putc ('^', rl_outstream); \ - putc ('?', rl_outstream); \ - printed_len += 2; \ - } \ - else \ - { \ - putc (c, rl_outstream); \ - printed_len++; \ - } \ - } while (0) - static int print_filename (to_print, full_pathname) char *to_print, *full_pathname; { - int printed_len = 0; -#if !defined (VISIBLE_STATS) - char *s; + int printed_len, extension_char, slen, tlen; + char *s, c, *new_full_pathname, *dn; - for (s = to_print; *s; s++) - { - PUTX (*s); - } -#else - char *s, c, *new_full_pathname; - int extension_char, slen, tlen; + extension_char = 0; + printed_len = fnprint (to_print); - for (s = to_print; *s; s++) - { - PUTX (*s); - } - - if (rl_filename_completion_desired && rl_visible_stats) +#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, to_print is the basename of the path passed. In this case, we try to expand the directory @@ -585,7 +720,17 @@ print_filename (to_print, full_pathname) 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 (full_pathname == 0 || *full_pathname == 0) + dn = "/"; + else if (full_pathname[0] != '/') + dn = full_pathname; + else if (full_pathname[1] == 0) + dn = "//"; /* restore trailing slash to `//' */ + else if (full_pathname[1] == '/' && full_pathname[2] == 0) + dn = "/"; /* don't turn /// into // */ + else + dn = full_pathname; + s = tilde_expand (dn); if (rl_directory_completion_hook) (*rl_directory_completion_hook) (&s); @@ -593,10 +738,20 @@ print_filename (to_print, full_pathname) 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); - extension_char = stat_char (new_full_pathname); +#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; @@ -604,7 +759,13 @@ print_filename (to_print, full_pathname) else { s = tilde_expand (full_pathname); - extension_char = stat_char (s); +#if defined (VISIBLE_STATS) + if (rl_visible_stats) + extension_char = stat_char (s); + else +#endif + if (path_isdir (s)) + extension_char = '/'; } free (s); @@ -614,7 +775,7 @@ print_filename (to_print, full_pathname) printed_len++; } } -#endif /* VISIBLE_STATS */ + return printed_len; } @@ -654,19 +815,25 @@ _rl_find_completion_word (fp, dp) int *fp, *dp; { int scan, end, found_quote, delimiter, pass_next, isbrk; - char quote_char; + 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) { /* We have a list of characters which can be used in pairs to quote substrings for the completer. Try to find the start of an unclosed quoted substring. */ /* FOUND_QUOTE is set so we know what kind of quotes we found. */ - for (scan = pass_next = 0; scan < end; scan++) + for (scan = pass_next = 0; scan < end; scan = MB_NEXTCHAR (rl_line_buffer, scan, 1, MB_FIND_ANY)) { if (pass_next) { @@ -716,15 +883,11 @@ _rl_find_completion_word (fp, dp) /* We didn't find an unclosed quoted substring upon which to do completion, so use the word break characters to find the substring on which to complete. */ -#if defined (HANDLE_MULTIBYTE) - while (rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_ANY)) -#else - while (--rl_point) -#endif + while (rl_point = MB_PREVCHAR (rl_line_buffer, rl_point, MB_FIND_ANY)) { scan = rl_line_buffer[rl_point]; - if (strchr (rl_completer_word_break_characters, scan) == 0) + if (strchr (brkchars, scan) == 0) continue; /* Call the application-specific function to tell us whether @@ -752,9 +915,9 @@ _rl_find_completion_word (fp, dp) 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; + strchr (brkchars, scan) != 0; else - isbrk = strchr (rl_completer_word_break_characters, scan) != 0; + isbrk = strchr (brkchars, scan) != 0; if (isbrk) { @@ -789,6 +952,9 @@ gen_completion_matches (text, start, end, our_func, found_quote, quote_char) { char **matches, *temp; + rl_completion_found_quote = found_quote; + rl_completion_quote_character = quote_char; + /* If the user wants to TRY to complete, but then wants to give up and use the default completion function, they set the variable rl_attempted_completion_function. */ @@ -892,6 +1058,7 @@ compute_lcd_of_matches (match_list, matches, text) { register int i, c1, c2, si; int low; /* Count of max-matched characters. */ + char *dtext; /* dequoted TEXT, if needed */ #if defined (HANDLE_MULTIBYTE) int v; mbstate_t ps1, ps2; @@ -983,6 +1150,26 @@ compute_lcd_of_matches (match_list, matches, text) the user typed in the face of multiple matches differing in case. */ if (_rl_completion_case_fold) { + /* We're making an assumption here: + IF we're completing filenames AND + the application has defined a filename dequoting function AND + we found a quote character AND + the application has requested filename quoting + THEN + we assume that TEXT was dequoted before checking against + the file system and needs to be dequoted here before we + check against the list of matches + FI */ + 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; + } + /* sort the list to get consistent answers. */ qsort (match_list+1, matches, sizeof(char *), (QSFUNC *)_rl_qsort_string_compare); @@ -1002,6 +1189,8 @@ compute_lcd_of_matches (match_list, matches, text) else /* otherwise, just use the text the user typed. */ strncpy (match_list[0], text, low); + + FREE (dtext); } else strncpy (match_list[0], match_list[1], low); @@ -1206,7 +1395,7 @@ display_matches (matches) for (max = 0, i = 1; matches[i]; i++) { temp = printable_part (matches[i]); - len = strlen (temp); + len = fnwidth (temp); if (len > max) max = len; @@ -1223,7 +1412,7 @@ display_matches (matches) /* If there are many items, then ask the user if she really wants to see them all. */ - if (len >= rl_completion_query_items) + if (rl_completion_query_items > 0 && len >= rl_completion_query_items) { rl_crlf (); fprintf (rl_outstream, "Display all %d possibilities? (y or n)", len); @@ -1341,7 +1530,8 @@ append_to_match (text, delimiter, quote_char, nontrivial_match) struct stat finfo; temp_string_index = 0; - if (quote_char && rl_point && rl_line_buffer[rl_point - 1] != quote_char) + 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) @@ -1359,7 +1549,7 @@ append_to_match (text, delimiter, quote_char, nontrivial_match) : stat (filename, &finfo); if (s == 0 && S_ISDIR (finfo.st_mode)) { - if (_rl_complete_mark_directories) + if (_rl_complete_mark_directories /* && rl_completion_suppress_append == 0 */) { /* This is clumsy. Avoid putting in a double slash if point is at the end of the line and the previous character is a @@ -1452,7 +1642,9 @@ _rl_free_match_list (matches) TAB means do standard completion. `*' means insert all of the possible completions. `!' means to do standard completion, and list all possible completions if - there is more than one. */ + there is more than one. + `@' means to do standard completion, and list all possible completions if + there is more than one and partial completion is not possible. */ int rl_complete_internal (what_to_do) int what_to_do; @@ -1471,7 +1663,6 @@ rl_complete_internal (what_to_do) our_func = rl_completion_entry_function ? rl_completion_entry_function : rl_filename_completion_function; - /* We now look backwards for the start of a filename/variable word. */ end = rl_point; found_quote = delimiter = 0; @@ -1519,6 +1710,7 @@ rl_complete_internal (what_to_do) { case TAB: case '!': + case '@': /* Insert the first match with proper quoting. */ if (*matches[0]) insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); @@ -1538,6 +1730,12 @@ rl_complete_internal (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 (); /* There are other matches remaining. */ } @@ -1665,7 +1863,7 @@ rl_username_completion_function (text, state) setpwent (); } -#ifdef HAVE_GETPWENT +#if defined (HAVE_GETPWENT) while (entry = getpwent ()) { /* Null usernames should result in all users as possible completions. */ @@ -1676,7 +1874,7 @@ rl_username_completion_function (text, state) if (entry == 0) { -#ifdef HAVE_GETPWENT +#if defined (HAVE_GETPWENT) endpwent (); #endif return ((char *)NULL); @@ -1990,9 +2188,11 @@ rl_menu_complete (count, ignore) return (0); } - match_list_index = (match_list_index + count) % match_list_size; + 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) { |