diff options
Diffstat (limited to 'readline/histexpand.c')
-rw-r--r-- | readline/histexpand.c | 358 |
1 files changed, 230 insertions, 128 deletions
diff --git a/readline/histexpand.c b/readline/histexpand.c index 6c81196..6847014 100644 --- a/readline/histexpand.c +++ b/readline/histexpand.c @@ -1,6 +1,6 @@ /* histexpand.c -- history expansion. */ -/* Copyright (C) 1989, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989-2004 Free Software Foundation, Inc. This file contains the GNU History Library (the Library), a set of routines for managing the text of previously typed lines. @@ -52,6 +52,8 @@ #define HISTORY_WORD_DELIMITERS " \t\n;&()|<>" #define HISTORY_QUOTE_CHARACTERS "\"'`" +#define slashify_in_quotes "\\`\"$" + typedef int _hist_search_func_t PARAMS((const char *, int)); extern int rl_byte_oriented; /* declared in mbutil.c */ @@ -65,6 +67,8 @@ static int subst_rhs_len; static char *get_history_word_specifier PARAMS((char *, char *, int *)); static char *history_find_word PARAMS((char *, int)); +static int history_tokenize_word PARAMS((const char *, int)); +static char *history_substring PARAMS((const char *, int, int)); static char *quote_breaks PARAMS((char *)); @@ -202,23 +206,24 @@ get_history_event (string, caller_index, delimiting_quote) /* Only a closing `?' or a newline delimit a substring search string. */ for (local_index = i; c = string[i]; i++) + { #if defined (HANDLE_MULTIBYTE) - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) - { - int v; - mbstate_t ps; + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + int v; + mbstate_t ps; + + memset (&ps, 0, sizeof (mbstate_t)); + /* These produce warnings because we're passing a const string to a + function that takes a non-const string. */ + _rl_adjust_point ((char *)string, i, &ps); + if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1) + { + i += v - 1; + continue; + } + } - memset (&ps, 0, sizeof (mbstate_t)); - /* These produce warnings because we're passing a const string to a - function that takes a non-const string. */ - _rl_adjust_point (string, i, &ps); - if ((v = _rl_get_char_len (string + i, &ps)) > 1) - { - i += v - 1; - continue; - } - } - else #endif /* HANDLE_MULTIBYTE */ if ((!substring_okay && (whitespace (c) || c == ':' || (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) || @@ -226,6 +231,7 @@ get_history_event (string, caller_index, delimiting_quote) string[i] == '\n' || (substring_okay && string[i] == '?')) break; + } which = i - local_index; temp = (char *)xmalloc (1 + which); @@ -517,7 +523,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) char *current_line; /* for !# */ { int i, n, starting_index; - int substitute_globally, want_quotes, print_only; + int substitute_globally, subst_bywords, want_quotes, print_only; char *event, *temp, *result, *tstr, *t, c, *word_spec; int result_len; #if defined (HANDLE_MULTIBYTE) @@ -599,19 +605,25 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) FREE (word_spec); /* Perhaps there are other modifiers involved. Do what they say. */ - want_quotes = substitute_globally = print_only = 0; + want_quotes = substitute_globally = subst_bywords = print_only = 0; starting_index = i; while (string[i] == ':') { c = string[i + 1]; - if (c == 'g') + if (c == 'g' || c == 'a') { substitute_globally = 1; i++; c = string[i + 1]; } + else if (c == 'G') + { + subst_bywords = 1; + i++; + c = string[i + 1]; + } switch (c) { @@ -683,7 +695,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) case 's': { char *new_event; - int delimiter, failed, si, l_temp; + int delimiter, failed, si, l_temp, ws, we; if (c == 's') { @@ -760,33 +772,67 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) } /* Find the first occurrence of THIS in TEMP. */ - si = 0; + /* Substitute SUBST_RHS for SUBST_LHS in TEMP. There are three + cases to consider: + + 1. substitute_globally == subst_bywords == 0 + 2. substitute_globally == 1 && subst_bywords == 0 + 3. substitute_globally == 0 && subst_bywords == 1 + + In the first case, we substitute for the first occurrence only. + In the second case, we substitute for every occurrence. + In the third case, we tokenize into words and substitute the + first occurrence of each word. */ + + si = we = 0; for (failed = 1; (si + subst_lhs_len) <= l_temp; si++) - if (STREQN (temp+si, subst_lhs, subst_lhs_len)) - { - int len = subst_rhs_len - subst_lhs_len + l_temp; - new_event = (char *)xmalloc (1 + len); - strncpy (new_event, temp, si); - strncpy (new_event + si, subst_rhs, subst_rhs_len); - strncpy (new_event + si + subst_rhs_len, - temp + si + subst_lhs_len, - l_temp - (si + subst_lhs_len)); - new_event[len] = '\0'; - free (temp); - temp = new_event; - - failed = 0; - - if (substitute_globally) - { - si += subst_rhs_len; - l_temp = strlen (temp); - substitute_globally++; - continue; - } - else - break; - } + { + /* First skip whitespace and find word boundaries if + we're past the end of the word boundary we found + the last time. */ + if (subst_bywords && si > we) + { + for (; temp[si] && whitespace (temp[si]); si++) + ; + ws = si; + we = history_tokenize_word (temp, si); + } + + if (STREQN (temp+si, subst_lhs, subst_lhs_len)) + { + int len = subst_rhs_len - subst_lhs_len + l_temp; + new_event = (char *)xmalloc (1 + len); + strncpy (new_event, temp, si); + strncpy (new_event + si, subst_rhs, subst_rhs_len); + strncpy (new_event + si + subst_rhs_len, + temp + si + subst_lhs_len, + l_temp - (si + subst_lhs_len)); + new_event[len] = '\0'; + free (temp); + temp = new_event; + + failed = 0; + + if (substitute_globally) + { + /* Reported to fix a bug that causes it to skip every + other match when matching a single character. Was + si += subst_rhs_len previously. */ + si += subst_rhs_len - 1; + l_temp = strlen (temp); + substitute_globally++; + continue; + } + else if (subst_bywords) + { + si = we; + l_temp = strlen (temp); + continue; + } + else + break; + } + } if (substitute_globally > 1) { @@ -879,7 +925,7 @@ history_expand (hstring, output) char **output; { register int j; - int i, r, l, passc, cc, modified, eindex, only_printing; + int i, r, l, passc, cc, modified, eindex, only_printing, dquote; char *string; /* The output string, and its length. */ @@ -942,7 +988,7 @@ history_expand (hstring, output) /* `!' followed by one of the characters in history_no_expand_chars is NOT an expansion. */ - for (i = 0; string[i]; i++) + for (i = dquote = 0; string[i]; i++) { #if defined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) @@ -984,9 +1030,19 @@ history_expand (hstring, output) else break; } - /* XXX - at some point, might want to extend this to handle - double quotes as well. */ - else if (history_quotes_inhibit_expansion && string[i] == '\'') + /* Shell-like quoting: allow backslashes to quote double quotes + inside a double-quoted string. */ + else if (dquote && string[i] == '\\' && cc == '"') + i++; + /* More shell-like quoting: if we're paying attention to single + quotes and letting them quote the history expansion character, + then we need to pay attention to double quotes, because single + quotes are not special inside double-quoted strings. */ + else if (history_quotes_inhibit_expansion && string[i] == '"') + { + dquote = 1 - dquote; + } + else if (dquote == 0 && history_quotes_inhibit_expansion && string[i] == '\'') { /* If this is bash, single quotes inhibit history expansion. */ i++; @@ -999,6 +1055,7 @@ history_expand (hstring, output) if (cc == '\'' || cc == history_expansion_char) i++; } + } if (string[i] != history_expansion_char) @@ -1010,7 +1067,7 @@ history_expand (hstring, output) } /* Extract and perform the substitution. */ - for (passc = i = j = 0; i < l; i++) + for (passc = dquote = i = j = 0; i < l; i++) { int tchar = string[i]; @@ -1061,11 +1118,16 @@ history_expand (hstring, output) ADD_CHAR (tchar); break; + case '"': + dquote = 1 - dquote; + ADD_CHAR (tchar); + break; + case '\'': { /* If history_quotes_inhibit_expansion is set, single quotes inhibit history expansion. */ - if (history_quotes_inhibit_expansion) + if (dquote == 0 && history_quotes_inhibit_expansion) { int quote, slen; @@ -1160,7 +1222,9 @@ history_expand (hstring, output) if (only_printing) { +#if 0 add_history (result); +#endif return (2); } @@ -1223,7 +1287,10 @@ get_history_word_specifier (spec, from, caller_index) if (spec[i] == '-') first = 0; else if (spec[i] == '^') - first = 1; + { + first = 1; + i++; + } else if (_rl_digit_p (spec[i]) && expecting_word_spec) { for (first = 0; _rl_digit_p (spec[i]); i++) @@ -1338,7 +1405,103 @@ history_arg_extract (first, last, string) return (result); } -#define slashify_in_quotes "\\`\"$" +static int +history_tokenize_word (string, ind) + const char *string; + int ind; +{ + register int i; + int delimiter; + + i = ind; + delimiter = 0; + + if (member (string[i], "()\n")) + { + i++; + return i; + } + + if (member (string[i], "<>;&|$")) + { + int peek = string[i + 1]; + + if (peek == string[i] && peek != '$') + { + if (peek == '<' && string[i + 2] == '-') + i++; + i += 2; + return i; + } + else + { + if ((peek == '&' && (string[i] == '>' || string[i] == '<')) || + (peek == '>' && string[i] == '&') || + (peek == '(' && (string[i] == '>' || string[i] == '<')) || /* ) */ + (peek == '(' && string[i] == '$')) /* ) */ + { + i += 2; + return i; + } + } + + if (string[i] != '$') + { + i++; + return i; + } + } + + /* Get word from string + i; */ + + if (member (string[i], HISTORY_QUOTE_CHARACTERS)) + delimiter = string[i++]; + + for (; string[i]; i++) + { + if (string[i] == '\\' && string[i + 1] == '\n') + { + i++; + continue; + } + + if (string[i] == '\\' && delimiter != '\'' && + (delimiter != '"' || member (string[i], slashify_in_quotes))) + { + i++; + continue; + } + + if (delimiter && string[i] == delimiter) + { + delimiter = 0; + continue; + } + + if (!delimiter && (member (string[i], history_word_delimiters))) + break; + + if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS)) + delimiter = string[i]; + } + + return i; +} + +static char * +history_substring (string, start, end) + const char *string; + int start, end; +{ + register int len; + register char *result; + + len = end - start; + result = (char *)xmalloc (len + 1); + strncpy (result, string + start, len); + result[len] = '\0'; + return result; +} /* Parse STRING into tokens and return an array of strings. If WIND is not -1 and INDP is not null, we also want the word surrounding index @@ -1351,7 +1514,6 @@ history_tokenize_internal (string, wind, indp) { char **result; register int i, start, result_index, size; - int len, delimiter; /* If we're searching for a string that's not part of a word (e.g., " "), make sure we set *INDP to a reasonable value. */ @@ -1362,8 +1524,6 @@ history_tokenize_internal (string, wind, indp) exactly where the shell would split them. */ for (i = result_index = size = 0, result = (char **)NULL; string[i]; ) { - delimiter = 0; - /* Skip leading whitespace. */ for (; string[i] && whitespace (string[i]); i++) ; @@ -1371,88 +1531,30 @@ history_tokenize_internal (string, wind, indp) return (result); start = i; - - if (member (string[i], "()\n")) - { - i++; - goto got_token; - } - if (member (string[i], "<>;&|$")) - { - int peek = string[i + 1]; + i = history_tokenize_word (string, start); - if (peek == string[i] && peek != '$') - { - if (peek == '<' && string[i + 2] == '-') - i++; - i += 2; - goto got_token; - } - else - { - if ((peek == '&' && (string[i] == '>' || string[i] == '<')) || - ((peek == '>') && (string[i] == '&')) || - ((peek == '(') && (string[i] == '$'))) - { - i += 2; - goto got_token; - } - } - if (string[i] != '$') - { - i++; - goto got_token; - } - } - - /* Get word from string + i; */ - - if (member (string[i], HISTORY_QUOTE_CHARACTERS)) - delimiter = string[i++]; - - for (; string[i]; i++) + /* If we have a non-whitespace delimiter character (which would not be + skipped by the loop above), use it and any adjacent delimiters to + make a separate field. Any adjacent white space will be skipped the + next time through the loop. */ + if (i == start && history_word_delimiters) { - if (string[i] == '\\' && string[i + 1] == '\n') - { - i++; - continue; - } - - if (string[i] == '\\' && delimiter != '\'' && - (delimiter != '"' || member (string[i], slashify_in_quotes))) - { - i++; - continue; - } - - if (delimiter && string[i] == delimiter) - { - delimiter = 0; - continue; - } - - if (!delimiter && (member (string[i], history_word_delimiters))) - break; - - if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS)) - delimiter = string[i]; + i++; + while (string[i] && member (string[i], history_word_delimiters)) + i++; } - got_token: - /* If we are looking for the word in which the character at a particular index falls, remember it. */ if (indp && wind != -1 && wind >= start && wind < i) *indp = result_index; - len = i - start; if (result_index + 2 >= size) result = (char **)xrealloc (result, ((size += 10) * sizeof (char *))); - result[result_index] = (char *)xmalloc (1 + len); - strncpy (result[result_index], string + start, len); - result[result_index][len] = '\0'; - result[++result_index] = (char *)NULL; + + result[result_index++] = history_substring (string, start, i); + result[result_index] = (char *)NULL; } return (result); |