diff options
Diffstat (limited to 'complete.c')
-rw-r--r-- | complete.c | 321 |
1 files changed, 154 insertions, 167 deletions
@@ -1,6 +1,6 @@ /* complete.c -- filename completion for readline. */ -/* Copyright (C) 1987-2021 Free Software Foundation, Inc. +/* Copyright (C) 1987-2024 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. @@ -77,11 +77,7 @@ extern int errno; # include "colors.h" #endif -#ifdef __STDC__ typedef int QSFUNC (const void *, const void *); -#else -typedef int QSFUNC (); -#endif #ifdef HAVE_LSTAT # define LSTAT lstat @@ -250,11 +246,24 @@ rl_icppfunc_t *rl_filename_stat_hook = (rl_icppfunc_t *)NULL; either return its first argument (if no conversion takes place) or newly-allocated memory. This can, for instance, convert filenames between character sets for comparison against what's typed at the - keyboard. The returned value is what is added to the list of - matches. The second argument is the length of the filename to be - converted. */ + keyboard (after its potential modification by rl_completion_rewrite_hook). + The returned value is what is added to the list of matches. + The second argument is the length of the filename to be converted. */ rl_dequote_func_t *rl_filename_rewrite_hook = (rl_dequote_func_t *)NULL; +/* If non-zero, this is the address of a function to call before + comparing the filename portion of a word to be completed with directory + entries from the filesystem. This takes the address of the partial word + to be completed, after any rl_filename_dequoting_function has been applied. + The function should either return its first argument (if no conversion + takes place) or newly-allocated memory. This can, for instance, convert + the filename portion of the completion word to a character set suitable + for comparison against directory entries read from the filesystem (after + their potential modification by rl_filename_rewrite_hook). + The returned value is what is added to the list of matches. + The second argument is the length of the filename to be converted. */ +rl_dequote_func_t *rl_completion_rewrite_hook = (rl_dequote_func_t *)NULL; + /* Non-zero means readline completion functions perform tilde expansion. */ int rl_complete_with_tilde_expansion = 0; @@ -340,6 +349,15 @@ int rl_filename_completion_desired = 0; entry finder function. */ int rl_filename_quoting_desired = 1; +/* Non-zero means we should apply filename-type quoting to all completions + even if we are not otherwise treating the matches as filenames. This is + ALWAYS zero on entry, and can only be changed within a completion entry + finder function. */ +int rl_full_quoting_desired = 0; + +#define QUOTING_DESIRED() \ + (rl_full_quoting_desired || (rl_filename_completion_desired && rl_filename_quoting_desired)) + /* This function, if defined, is called by the completer when real filename completion is done, after all the matching names have been generated. It is passed a (char**) known as matches in the code below. @@ -455,6 +473,7 @@ rl_complete (int ignore, int invoking_key) int rl_possible_completions (int ignore, int invoking_key) { + last_completion_failed = 0; rl_completion_invoking_key = invoking_key; return (rl_complete_internal ('?')); } @@ -484,6 +503,32 @@ rl_completion_mode (rl_command_func_t *cfunc) return TAB; } +/********************************************/ +/* */ +/* Completion signal handling and cleanup */ +/* */ +/********************************************/ + +/* State to clean up and free if completion is interrupted by a signal. */ +typedef struct { + char **matches; + char *saved_line; +} complete_sigcleanarg_t; + +static void +_rl_complete_sigcleanup (int sig, void *ptr) +{ + complete_sigcleanarg_t *arg; + + if (sig == SIGINT) /* XXX - for now */ + { + arg = ptr; + _rl_free_match_list (arg->matches); + FREE (arg->saved_line); + _rl_complete_display_matches_interrupt = 1; + } +} + /************************************/ /* */ /* Completion utility functions */ @@ -498,16 +543,6 @@ _rl_reset_completion_state (void) rl_completion_quote_character = 0; } -static void -_rl_complete_sigcleanup (int sig, void *ptr) -{ - if (sig == SIGINT) /* XXX - for now */ - { - _rl_free_match_list ((char **)ptr); - _rl_complete_display_matches_interrupt = 1; - } -} - /* Set default values for readline word completion. These are the variables that application completion functions can change or inspect. */ static void @@ -516,6 +551,7 @@ set_completion_defaults (int what_to_do) /* Only the completion entry function can change these. */ rl_filename_completion_desired = 0; rl_filename_quoting_desired = 1; + rl_full_quoting_desired = 0; rl_completion_type = what_to_do; rl_completion_suppress_append = rl_completion_suppress_quote = 0; rl_completion_append_character = ' '; @@ -1319,6 +1355,7 @@ compute_lcd_of_matches (char **match_list, int matches, const char *text) int low; /* Count of max-matched characters. */ int lx; char *dtext; /* dequoted TEXT, if needed */ + size_t len1, len2; #if defined (HANDLE_MULTIBYTE) int v; size_t v1, v2; @@ -1345,6 +1382,9 @@ compute_lcd_of_matches (char **match_list, int matches, const char *text) memset (&ps2, 0, sizeof (mbstate_t)); } #endif + len1 = strlen (match_list[i]); + len2 = strlen (match_list[i + 1]); + for (si = 0; (c1 = match_list[i][si]) && (c2 = match_list[i + 1][si]); si++) { if (_rl_completion_case_fold) @@ -1355,8 +1395,8 @@ compute_lcd_of_matches (char **match_list, int matches, const char *text) #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) { - 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); + v1 = MBRTOWC (&wc1, match_list[i]+si, len1 - si, &ps1); + v2 = MBRTOWC (&wc2, match_list[i+1]+si, len2 - si, &ps2); if (MB_INVALIDCH (v1) || MB_INVALIDCH (v2)) { if (c1 != c2) /* do byte comparison */ @@ -1412,10 +1452,8 @@ compute_lcd_of_matches (char **match_list, int matches, const char *text) 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) + if (QUOTING_DESIRED() && rl_completion_found_quote && + rl_filename_dequoting_function) { dtext = (*rl_filename_dequoting_function) ((char *)text, rl_completion_quote_character); text = dtext; @@ -1630,7 +1668,7 @@ rl_display_match_list (char **matches, int len, int max) if (_rl_page_completions && lines >= (_rl_screenheight - 1) && i < count) { lines = _rl_internal_pager (lines); - if (lines < 0) + if (lines < 0 || _rl_complete_display_matches_interrupt) return; } } @@ -1658,7 +1696,7 @@ rl_display_match_list (char **matches, int len, int max) if (_rl_page_completions && lines >= _rl_screenheight - 1) { lines = _rl_internal_pager (lines); - if (lines < 0) + if (lines < 0 || _rl_complete_display_matches_interrupt) return; } } @@ -1745,7 +1783,9 @@ display_matches (char **matches) } } - rl_display_match_list (matches, len, max); + /* We rely on the caller to set MATCHES to 0 when this returns. */ + if (_rl_complete_display_matches_interrupt == 0) + rl_display_match_list (matches, len, max); rl_forced_update_display (); rl_display_fixed = 1; @@ -1768,9 +1808,7 @@ make_quoted_replacement (char *match, int mtype, char *qc) matches don't require a quoted substring. */ replacement = match; - should_quote = match && rl_completer_quote_characters && - rl_filename_completion_desired && - rl_filename_quoting_desired; + should_quote = match && rl_completer_quote_characters && QUOTING_DESIRED(); if (should_quote) should_quote = should_quote && (!qc || !*qc || @@ -1784,6 +1822,11 @@ make_quoted_replacement (char *match, int mtype, char *qc) should_quote = rl_filename_quote_characters ? (_rl_strpbrk (match, rl_filename_quote_characters) != 0) : 0; + /* If we saw a quote in the original word, but readline thinks the + match doesn't need to be quoted, and the application has a filename + quoting function, give the application a chance to quote it if + needed so we don't second-guess the user. */ + should_quote |= *qc == 0 && rl_completion_found_quote && mtype != NO_MATCH && rl_filename_quoting_function; do_replace = should_quote ? mtype : NO_MATCH; /* Quote the replacement, since we found an embedded @@ -1791,6 +1834,7 @@ make_quoted_replacement (char *match, int mtype, char *qc) if (do_replace != NO_MATCH && rl_filename_quoting_function) replacement = (*rl_filename_quoting_function) (match, do_replace, qc); } + return (replacement); } @@ -1976,8 +2020,7 @@ compare_match (char *text, const char *match) char *temp; int r; - if (rl_filename_completion_desired && rl_filename_quoting_desired && - rl_completion_found_quote && rl_filename_dequoting_function) + if (QUOTING_DESIRED() && rl_completion_found_quote && rl_filename_dequoting_function) { temp = (*rl_filename_dequoting_function) (text, rl_completion_quote_character); r = strcmp (temp, match); @@ -2001,10 +2044,11 @@ rl_complete_internal (int what_to_do) { char **matches; rl_compentry_func_t *our_func; - int start, end, delimiter, found_quote, i, nontrivial_lcd; + int start, end, delimiter, found_quote, i, nontrivial_lcd, do_display; char *text, *saved_line_buffer; char quote_char; int tlen, mlen, saved_last_completion_failed; + complete_sigcleanarg_t cleanarg; /* state to clean up on signal */ RL_SETSTATE(RL_STATE_COMPLETING); @@ -2039,8 +2083,7 @@ rl_complete_internal (int what_to_do) strcmp directly. */ /* nontrivial_lcd is set if the common prefix adds something to the word being completed. */ - if (rl_filename_completion_desired && rl_filename_quoting_desired && - rl_completion_found_quote && rl_filename_dequoting_function) + if (QUOTING_DESIRED() && rl_completion_found_quote && rl_filename_dequoting_function) { char *t; t = (*rl_filename_dequoting_function) (text, rl_completion_quote_character); @@ -2084,6 +2127,8 @@ rl_complete_internal (int what_to_do) if (matches && matches[0] && *matches[0]) last_completion_failed = 0; + do_display = 0; + switch (what_to_do) { case TAB: @@ -2117,13 +2162,13 @@ rl_complete_internal (int what_to_do) { if (what_to_do == '!') { - display_matches (matches); + do_display = 1; break; } else if (what_to_do == '@') { if (nontrivial_lcd == 0) - display_matches (matches); + do_display = 1; break; } else if (rl_editing_mode != vi_mode) @@ -2147,23 +2192,11 @@ rl_complete_internal (int what_to_do) append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd); break; } - - if (rl_completion_display_matches_hook == 0) - { - _rl_sigcleanup = _rl_complete_sigcleanup; - _rl_sigcleanarg = matches; - _rl_complete_display_matches_interrupt = 0; - } - display_matches (matches); - if (_rl_complete_display_matches_interrupt) - { - matches = 0; /* already freed by rl_complete_sigcleanup */ - _rl_complete_display_matches_interrupt = 0; - if (rl_signal_event_hook) - (*rl_signal_event_hook) (); /* XXX */ - } - _rl_sigcleanup = 0; - _rl_sigcleanarg = 0; + /*FALLTHROUGH*/ + + case '%': /* used by menu_complete */ + case '|': /* add this for unconditional display */ + do_display = 1; break; default: @@ -2176,6 +2209,34 @@ rl_complete_internal (int what_to_do) return 1; } + /* If we need to display the match list, set up to clean it up on receipt of + a signal and do it here. If the application has registered a function to + display the matches, let it do the work. */ + if (do_display) + { + if (rl_completion_display_matches_hook == 0) + { + _rl_sigcleanup = _rl_complete_sigcleanup; + cleanarg.matches = matches; + cleanarg.saved_line = saved_line_buffer; + _rl_sigcleanarg = &cleanarg; + _rl_complete_display_matches_interrupt = 0; + } + + display_matches (matches); + + if (_rl_complete_display_matches_interrupt) + { + matches = 0; /* Both already freed by _rl_complete_sigcleanup */ + saved_line_buffer = 0; + _rl_complete_display_matches_interrupt = 0; + if (rl_signal_event_hook) + (*rl_signal_event_hook) (); + } + _rl_sigcleanup = 0; + _rl_sigcleanarg = 0; + } + _rl_free_match_list (matches); /* Check to see if the line has changed through all of this manipulation. */ @@ -2216,7 +2277,7 @@ rl_completion_matches (const char *text, rl_compentry_func_t *entry_function) register int i; /* Number of slots in match_list. */ - int match_list_size; + size_t match_list_size; /* The list of matches. */ char **match_list; @@ -2281,9 +2342,9 @@ rl_completion_matches (const char *text, rl_compentry_func_t *entry_function) char * rl_username_completion_function (const char *text, int state) { -#if defined (__WIN32__) || defined (__OPENNT) +#if defined (_WIN32) || defined (__OPENNT) || !defined (HAVE_GETPWENT) return (char *)NULL; -#else /* !__WIN32__ && !__OPENNT) */ +#else /* !_WIN32 && !__OPENNT) && HAVE_GETPWENT */ static char *username = (char *)NULL; static struct passwd *entry; static int namelen, first_char, first_char_loc; @@ -2298,25 +2359,19 @@ rl_username_completion_function (const char *text, int state) username = savestring (&text[first_char_loc]); namelen = strlen (username); -#if defined (HAVE_GETPWENT) setpwent (); -#endif } -#if defined (HAVE_GETPWENT) while (entry = getpwent ()) { /* Null usernames should result in all users as possible completions. */ if (namelen == 0 || (STREQN (username, entry->pw_name, namelen))) break; } -#endif if (entry == 0) { -#if defined (HAVE_GETPWENT) endpwent (); -#endif return ((char *)NULL); } else @@ -2332,7 +2387,7 @@ rl_username_completion_function (const char *text, int state) return (value); } -#endif /* !__WIN32__ && !__OPENNT */ +#endif /* !_WIN32 && !__OPENNT && HAVE_GETPWENT */ } /* Return non-zero if CONVFN matches FILENAME up to the length of FILENAME @@ -2343,18 +2398,7 @@ rl_username_completion_function (const char *text, int state) static int complete_fncmp (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 + size_t len; if (filename_len == 0) return 1; @@ -2362,100 +2406,26 @@ complete_fncmp (const char *convfn, int convlen, const char *filename, int filen 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 (_rl_completion_case_fold) { + /* Case-insensitive comparison treating _ and - as equivalent if + _rl_completion_case_map is non-zero */ #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; - } + return (_rl_mb_strcaseeqn (convfn, convlen, filename, filename_len, len, _rl_completion_case_map)); 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; + (convlen >= filename_len)) + return (_rl_strcaseeqn (convfn, filename, len, _rl_completion_case_map)); } else { + /* XXX - add new _rl_mb_streqn function (like mbsncmp) instead of + relying on byte equivalence? */ if ((convfn[0] == filename[0]) && (convlen >= filename_len) && (strncmp (filename, convfn, filename_len) == 0)) @@ -2477,7 +2447,8 @@ rl_filename_completion_function (const char *text, int state) static char *users_dirname = (char *)NULL; static int filename_len; char *temp, *dentry, *convfn; - int dirlen, dentlen, convlen; + size_t dirlen; + int dentlen, convlen; int tilde_dirname; struct dirent *entry; @@ -2586,6 +2557,18 @@ rl_filename_completion_function (const char *text, int state) } filename_len = strlen (filename); + /* Normalize the filename if the application has set a rewrite hook. */ + if (*filename && rl_completion_rewrite_hook) + { + temp = (*rl_completion_rewrite_hook) (filename, filename_len); + if (temp != filename) + { + xfree (filename); + filename = temp; + filename_len = strlen (filename); + } + } + rl_filename_completion_desired = 1; } @@ -2598,6 +2581,7 @@ rl_filename_completion_function (const char *text, int state) /* Now that we have some state, we can read the directory. */ entry = (struct dirent *)NULL; + convfn = dentry = 0; while (directory && (entry = readdir (directory))) { convfn = dentry = entry->d_name; @@ -2615,17 +2599,20 @@ rl_filename_completion_function (const char *text, int state) if (filename_len == 0) { if (_rl_match_hidden_files == 0 && HIDDEN_FILE (convfn)) - continue; + { + if (convfn != dentry) + xfree (convfn); + continue; + } if (convfn[0] != '.' || (convfn[1] && (convfn[1] != '.' || convfn[2]))) break; } - else - { - if (complete_fncmp (convfn, convlen, filename, filename_len)) - break; - } + else if (complete_fncmp (convfn, convlen, filename, filename_len)) + break; + else if (convfn != dentry) + xfree (convfn); } if (entry == 0) @@ -2847,7 +2834,7 @@ rl_menu_complete (int count, int ignore) 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, cstate; + static int delimiter; /* The first time through, we generate the list of matches and set things up to insert them. */ @@ -2943,7 +2930,7 @@ rl_menu_complete (int count, int ignore) if (rl_completion_query_items > 0 && match_list_size >= rl_completion_query_items) { rl_ding (); - FREE (matches); + _rl_free_match_list (matches); matches = (char **)0; full_completion = 1; return (0); |