X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=readline%2Fhistexpand.c;h=8fb3798b7cafc5169b3beb761602b70a5299af42;hb=005cb1192ac986f689938e91c4702dbd49635982;hp=78da3e585a651983535fe672b05eadcaee09cfe1;hpb=2ee563b53258d390d7446e90a67f465d504ae44c;p=platform%2Fupstream%2Fbinutils.git diff --git a/readline/histexpand.c b/readline/histexpand.c index 78da3e5..8fb3798 100644 --- a/readline/histexpand.c +++ b/readline/histexpand.c @@ -1,24 +1,23 @@ /* histexpand.c -- history expansion. */ -/* Copyright (C) 1989, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1989-2010 Free Software Foundation, Inc. - This file contains the GNU History Library (the Library), a set of + This file contains the GNU History Library (History), a set of routines for managing the text of previously typed lines. - The Library is free software; you can redistribute it and/or modify + History 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 2, or (at your option) - any later version. + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - The Library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + History is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - 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, - 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + You should have received a copy of the GNU General Public License + along with History. If not, see . +*/ #define READLINE_LIBRARY @@ -41,11 +40,7 @@ # include #endif -#if defined (HAVE_STRING_H) -# include -#else -# include -#endif /* !HAVE_STRING_H */ +#include "rlmbutil.h" #include "history.h" #include "histlib.h" @@ -56,6 +51,10 @@ #define HISTORY_WORD_DELIMITERS " \t\n;&()|<>" #define HISTORY_QUOTE_CHARACTERS "\"'`" +#define slashify_in_quotes "\\`\"$" + +typedef int _hist_search_func_t PARAMS((const char *, int)); + static char error_pointer; static char *subst_lhs; @@ -63,10 +62,14 @@ static char *subst_rhs; static int subst_lhs_len; static int subst_rhs_len; -static char *get_history_word_specifier __P((char *, char *, int *)); -static char *history_find_word __P((char *, int)); +static char *get_history_word_specifier PARAMS((char *, char *, int *)); +static int history_tokenize_word PARAMS((const char *, int)); +static char **history_tokenize_internal PARAMS((const char *, int, int *)); +static char *history_substring PARAMS((const char *, int, int)); +static void freewords PARAMS((char **, int)); +static char *history_find_word PARAMS((char *, int)); -static char *quote_breaks __P((char *)); +static char *quote_breaks PARAMS((char *)); /* Variables exported by this file. */ /* The character that represents the start of a history expansion @@ -91,9 +94,12 @@ char *history_no_expand_chars = " \t\n\r="; The default is 0. */ int history_quotes_inhibit_expansion = 0; +/* Used to split words by history_tokenize_internal. */ +char *history_word_delimiters = HISTORY_WORD_DELIMITERS; + /* If set, this points to a function that is called to verify that a particular history expansion should be performed. */ -Function *history_inhibit_expansion_function; +rl_linebuf_func_t *history_inhibit_expansion_function; /* **************************************************************** */ /* */ @@ -122,7 +128,7 @@ static char *search_match; line = get_history_event ("!echo:p", &index, 0); */ char * get_history_event (string, caller_index, delimiting_quote) - char *string; + const char *string; int *caller_index; int delimiting_quote; { @@ -130,7 +136,7 @@ get_history_event (string, caller_index, delimiting_quote) register char c; HIST_ENTRY *entry; int which, sign, local_index, substring_okay; - Function *search_func; + _hist_search_func_t *search_func; char *temp; /* The event can be specified in a number of ways. @@ -199,15 +205,35 @@ 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 ((!substring_okay && (whitespace (c) || c == ':' || - (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) || - string[i] == delimiting_quote)) || - string[i] == '\n' || - (substring_okay && string[i] == '?')) - break; + { +#if defined (HANDLE_MULTIBYTE) + 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; + } + } + +#endif /* HANDLE_MULTIBYTE */ + if ((!substring_okay && (whitespace (c) || c == ':' || + (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) || + string[i] == delimiting_quote)) || + string[i] == '\n' || + (substring_okay && string[i] == '?')) + break; + } which = i - local_index; - temp = xmalloc (1 + which); + temp = (char *)xmalloc (1 + which); if (which) strncpy (temp, string + local_index, which); temp[which] = '\0'; @@ -219,7 +245,7 @@ get_history_event (string, caller_index, delimiting_quote) #define FAIL_SEARCH() \ do { \ - history_offset = history_length; free (temp) ; return (char *)NULL; \ + history_offset = history_length; xfree (temp) ; return (char *)NULL; \ } while (0) /* If there is no search string, try to use the previous search string, @@ -228,7 +254,7 @@ get_history_event (string, caller_index, delimiting_quote) { if (search_string) { - free (temp); + xfree (temp); temp = savestring (search_string); } else @@ -259,7 +285,7 @@ get_history_event (string, caller_index, delimiting_quote) search_match = history_find_word (entry->line, local_index); } else - free (temp); + xfree (temp); return (entry->line); } @@ -279,16 +305,20 @@ get_history_event (string, caller_index, delimiting_quote) /* Extract the contents of STRING as if it is enclosed in single quotes. SINDEX, when passed in, is the offset of the character immediately following the opening single quote; on exit, SINDEX is left pointing - to the closing single quote. */ + to the closing single quote. FLAGS currently used to allow backslash + to escape a single quote (e.g., for bash $'...'). */ static void -hist_string_extract_single_quoted (string, sindex) +hist_string_extract_single_quoted (string, sindex, flags) char *string; - int *sindex; + int *sindex, flags; { register int i; for (i = *sindex; string[i] && string[i] != '\''; i++) - ; + { + if ((flags & 1) && string[i] == '\\' && string[i+1]) + i++; + } *sindex = i; } @@ -309,7 +339,7 @@ quote_breaks (s) len += 2; } - r = ret = xmalloc (len); + r = ret = (char *)xmalloc (len); *r++ = '\''; for (p = s; p && *p; ) { @@ -340,7 +370,8 @@ hist_error(s, start, current, errtype) char *s; int start, current, errtype; { - char *temp, *emsg; + char *temp; + const char *emsg; int ll, elen; ll = current - start; @@ -373,7 +404,7 @@ hist_error(s, start, current, errtype) break; } - temp = xmalloc (ll + elen + 3); + temp = (char *)xmalloc (ll + elen + 3); strncpy (temp, s + start, ll); temp[ll] = ':'; temp[ll + 1] = ' '; @@ -399,17 +430,37 @@ get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr) int *iptr, delimiter, is_rhs, *lenptr; { register int si, i, j, k; - char *s = (char *) NULL; + char *s; +#if defined (HANDLE_MULTIBYTE) + mbstate_t ps; +#endif + s = (char *)NULL; i = *iptr; +#if defined (HANDLE_MULTIBYTE) + memset (&ps, 0, sizeof (mbstate_t)); + _rl_adjust_point (str, i, &ps); +#endif + for (si = i; str[si] && str[si] != delimiter; si++) - if (str[si] == '\\' && str[si + 1] == delimiter) - si++; +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + int v; + if ((v = _rl_get_char_len (str + si, &ps)) > 1) + si += v - 1; + else if (str[si] == '\\' && str[si + 1] == delimiter) + si++; + } + else +#endif /* HANDLE_MULTIBYTE */ + if (str[si] == '\\' && str[si + 1] == delimiter) + si++; if (si > i || is_rhs) { - s = xmalloc (si - i + 1); + s = (char *)xmalloc (si - i + 1); for (j = 0, k = i; k < si; j++, k++) { /* Remove a backslash quoting the search string delimiter. */ @@ -436,13 +487,13 @@ postproc_subst_rhs () char *new; int i, j, new_size; - new = xmalloc (new_size = subst_rhs_len + subst_lhs_len); + new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len); for (i = j = 0; i < subst_rhs_len; i++) { if (subst_rhs[i] == '&') { if (j + subst_lhs_len >= new_size) - new = xrealloc (new, (new_size = new_size * 2 + subst_lhs_len)); + new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len)); strcpy (new + j, subst_lhs); j += subst_lhs_len; } @@ -452,12 +503,12 @@ postproc_subst_rhs () if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&') i++; if (j >= new_size) - new = xrealloc (new, new_size *= 2); + new = (char *)xrealloc (new, new_size *= 2); new[j++] = subst_rhs[i]; } } new[j] = '\0'; - free (subst_rhs); + xfree (subst_rhs); subst_rhs = new; subst_rhs_len = j; } @@ -475,11 +526,16 @@ 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) + mbstate_t ps; + + memset (&ps, 0, sizeof (mbstate_t)); +#endif - result = xmalloc (result_len = 128); + result = (char *)xmalloc (result_len = 128); i = start; @@ -508,15 +564,28 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) quote, then this expansion takes place inside of the quoted string. If we have to search for some text ("!foo"), allow the delimiter to end the search string. */ - if (i && (string[i - 1] == '\'' || string[i - 1] == '"')) - quoted_search_delimiter = string[i - 1]; +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + int ch, l; + l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY); + ch = string[l]; + /* XXX - original patch had i - 1 ??? If i == 0 it would fail. */ + if (i && (ch == '\'' || ch == '"')) + quoted_search_delimiter = ch; + } + else +#endif /* HANDLE_MULTIBYTE */ + if (i && (string[i - 1] == '\'' || string[i - 1] == '"')) + quoted_search_delimiter = string[i - 1]; + event = get_history_event (string, &i, quoted_search_delimiter); } if (event == 0) { *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND); - free (result); + xfree (result); return (-1); } @@ -530,7 +599,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) if (word_spec == (char *)&error_pointer) { *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC); - free (result); + xfree (result); return (-1); } @@ -539,26 +608,32 @@ 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) { default: *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER); - free (result); - free (temp); + xfree (result); + xfree (temp); return -1; case 'q': @@ -583,7 +658,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) { tstr++; t = savestring (tstr); - free (temp); + xfree (temp); temp = t; } break; @@ -608,7 +683,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) if (tstr) { t = savestring (tstr); - free (temp); + xfree (temp); temp = t; } break; @@ -622,13 +697,26 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) case '&': case 's': { - char *new_event, *t; - int delimiter, failed, si, l_temp; + char *new_event; + int delimiter, failed, si, l_temp, ws, we; if (c == 's') { if (i + 2 < (int)strlen (string)) - delimiter = string[i + 2]; + { +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + _rl_adjust_point (string, i + 2, &ps); + if (_rl_get_char_len (string + i + 2, &ps) > 1) + delimiter = 0; + else + delimiter = string[i + 2]; + } + else +#endif /* HANDLE_MULTIBYTE */ + delimiter = string[i + 2]; + } else break; /* no search delimiter */ @@ -671,8 +759,8 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) if (subst_lhs_len == 0) { *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST); - free (result); - free (temp); + xfree (result); + xfree (temp); return -1; } @@ -681,39 +769,73 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) if (subst_lhs_len > l_temp) { *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); - free (result); - free (temp); + xfree (result); + xfree (temp); return (-1); } /* 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 = 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'; + xfree (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) { @@ -725,8 +847,8 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) continue; /* don't want to increment i */ *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); - free (result); - free (temp); + xfree (result); + xfree (temp); return (-1); } } @@ -741,21 +863,21 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) char *x; if (want_quotes == 'q') - x = single_quote (temp); + x = sh_single_quote (temp); else if (want_quotes == 'x') x = quote_breaks (temp); else x = savestring (temp); - free (temp); + xfree (temp); temp = x; } n = strlen (temp); if (n >= result_len) - result = xrealloc (result, n + 2); + result = (char *)xrealloc (result, n + 2); strcpy (result, temp); - free (temp); + xfree (temp); *end_index_ptr = i; *ret_string = result; @@ -784,7 +906,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) { \ while (j >= result_len) \ result_len += 128; \ - result = xrealloc (result, result_len); \ + result = (char *)xrealloc (result, result_len); \ } \ strcpy (result + j - sl, s); \ } \ @@ -794,7 +916,7 @@ history_expand_internal (string, start, end_index_ptr, ret_string, current_line) do \ { \ if (j >= result_len - 1) \ - result = xrealloc (result, result_len += 64); \ + result = (char *)xrealloc (result, result_len += 64); \ result[j++] = c; \ result[j] = '\0'; \ } \ @@ -806,16 +928,24 @@ 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, flag; char *string; /* The output string, and its length. */ int result_len; char *result; +#if defined (HANDLE_MULTIBYTE) + char mb[MB_LEN_MAX]; + mbstate_t ps; +#endif + /* Used when adding the string. */ char *temp; + if (output == 0) + return 0; + /* Setting the history expansion character to 0 inhibits all history expansion. */ if (history_expansion_char == 0) @@ -825,7 +955,7 @@ history_expand (hstring, output) } /* Prepare the buffer for printing error messages. */ - result = xmalloc (result_len = 256); + result = (char *)xmalloc (result_len = 256); result[0] = '\0'; only_printing = modified = 0; @@ -842,7 +972,7 @@ history_expand (hstring, output) that is the substitution that we do. */ if (hstring[0] == history_subst_char) { - string = xmalloc (l + 5); + string = (char *)xmalloc (l + 5); string[0] = string[1] = history_expansion_char; string[2] = ':'; @@ -852,20 +982,37 @@ history_expand (hstring, output) } else { +#if defined (HANDLE_MULTIBYTE) + memset (&ps, 0, sizeof (mbstate_t)); +#endif + string = hstring; /* If not quick substitution, still maybe have to do expansion. */ /* `!' 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) + { + int v; + v = _rl_get_char_len (string + i, &ps); + if (v > 1) + { + i += v - 1; + continue; + } + } +#endif /* HANDLE_MULTIBYTE */ + cc = string[i + 1]; - /* The history_comment_char, if set, appearing that the beginning + /* The history_comment_char, if set, appearing at the beginning of a word signifies that the rest of the line should not have history expansion performed on it. Skip the rest of the line and break out of the loop. */ if (history_comment_char && string[i] == history_comment_char && - (i == 0 || member (string[i - 1], HISTORY_WORD_DELIMITERS))) + (i == 0 || member (string[i - 1], history_word_delimiters))) { while (string[i]) i++; @@ -873,7 +1020,7 @@ history_expand (hstring, output) } else if (string[i] == history_expansion_char) { - if (!cc || member (cc, history_no_expand_chars)) + if (cc == 0 || member (cc, history_no_expand_chars)) continue; /* If the calling application has set history_inhibit_expansion_function to a function that checks @@ -886,13 +1033,24 @@ 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. */ + flag = (i > 0 && string[i - 1] == '$'); i++; - hist_string_extract_single_quoted (string, &i); + hist_string_extract_single_quoted (string, &i, flag); } else if (history_quotes_inhibit_expansion && string[i] == '\\') { @@ -901,18 +1059,19 @@ history_expand (hstring, output) if (cc == '\'' || cc == history_expansion_char) i++; } + } if (string[i] != history_expansion_char) { - free (result); + xfree (result); *output = savestring (string); return (0); } } /* 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]; @@ -923,6 +1082,30 @@ history_expand (hstring, output) continue; } +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + { + int k, c; + + c = tchar; + memset (mb, 0, sizeof (mb)); + for (k = 0; k < MB_LEN_MAX; k++) + { + mb[k] = (char)c; + memset (&ps, 0, sizeof (mbstate_t)); + if (_rl_get_char_len (mb, &ps) == -2) + c = string[++i]; + else + break; + } + if (strlen (mb) > 1) + { + ADD_STRING (mb); + continue; + } + } +#endif /* HANDLE_MULTIBYTE */ + if (tchar == history_expansion_char) tchar = -3; else if (tchar == history_comment_char) @@ -939,23 +1122,29 @@ 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; + flag = (i > 0 && string[i - 1] == '$'); quote = i++; - hist_string_extract_single_quoted (string, &i); + hist_string_extract_single_quoted (string, &i, flag); slen = i - quote + 2; - temp = xmalloc (slen); + temp = (char *)xmalloc (slen); strncpy (temp, string + quote, slen); temp[slen - 1] = '\0'; ADD_STRING (temp); - free (temp); + xfree (temp); } else ADD_CHAR (string[i]); @@ -963,12 +1152,12 @@ history_expand (hstring, output) } case -2: /* history_comment_char */ - if (i == 0 || member (string[i - 1], HISTORY_WORD_DELIMITERS)) + if (i == 0 || member (string[i - 1], history_word_delimiters)) { - temp = xmalloc (l - i + 1); + temp = (char *)xmalloc (l - i + 1); strcpy (temp, string + i); ADD_STRING (temp); - free (temp); + xfree (temp); i = l; } else @@ -981,7 +1170,8 @@ history_expand (hstring, output) /* If the history_expansion_char is followed by one of the characters in history_no_expand_chars, then it is not a candidate for expansion of any kind. */ - if (member (cc, history_no_expand_chars)) + if (cc == 0 || member (cc, history_no_expand_chars) || + (history_inhibit_expansion_function && (*history_inhibit_expansion_function) (string, i))) { ADD_CHAR (string[i]); break; @@ -997,10 +1187,10 @@ history_expand (hstring, output) { if (result) { - temp = xmalloc (1 + strlen (result)); + temp = (char *)xmalloc (1 + strlen (result)); strcpy (temp, result); ADD_STRING (temp); - free (temp); + xfree (temp); } i++; break; @@ -1011,9 +1201,9 @@ history_expand (hstring, output) if (r < 0) { *output = temp; - free (result); + xfree (result); if (string != hstring) - free (string); + xfree (string); return -1; } else @@ -1023,7 +1213,7 @@ history_expand (hstring, output) modified++; if (*temp) ADD_STRING (temp); - free (temp); + xfree (temp); } only_printing = r == 1; i = eindex; @@ -1034,11 +1224,13 @@ history_expand (hstring, output) *output = result; if (string != hstring) - free (string); + xfree (string); if (only_printing) { +#if 0 add_history (result); +#endif return (2); } @@ -1101,7 +1293,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++) @@ -1131,7 +1326,14 @@ get_history_word_specifier (spec, from, caller_index) i++; last = '$'; } - else if (!spec[i] || spec[i] == ':') /* could be modifier separator */ +#if 0 + else if (!spec[i] || spec[i] == ':') + /* check against `:' because there could be a modifier separator */ +#else + else + /* csh seems to allow anything to terminate the word spec here, + leaving it as an abbreviation. */ +#endif last = -1; /* x- abbreviates x-$ omitting word `$' */ } @@ -1151,7 +1353,7 @@ get_history_word_specifier (spec, from, caller_index) char * history_arg_extract (first, last, string) int first, last; - char *string; + const char *string; { register int i, len; char *result; @@ -1187,7 +1389,7 @@ history_arg_extract (first, last, string) { for (size = 0, i = first; i < last; i++) size += strlen (list[i]) + 1; - result = xmalloc (size + 1); + result = (char *)xmalloc (size + 1); result[0] = '\0'; for (i = first, offset = 0; i < last; i++) @@ -1203,13 +1405,154 @@ history_arg_extract (first, last, string) } for (i = 0; i < len; i++) - free (list[i]); - free (list); + xfree (list[i]); + xfree (list); return (result); } -#define slashify_in_quotes "\\`\"$" +static int +history_tokenize_word (string, ind) + const char *string; + int ind; +{ + register int i; + int delimiter, nestdelim, delimopen; + + i = ind; + delimiter = nestdelim = 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++; + else if (peek == '<' && string[i + 2] == '<') + i++; + i += 2; + return i; + } + else if ((peek == '&' && (string[i] == '>' || string[i] == '<')) || + (peek == '>' && string[i] == '&')) + { + i += 2; + return i; + } + /* XXX - separated out for later -- bash-4.2 */ + else if ((peek == '(' && (string[i] == '>' || string[i] == '<')) || /* ) */ + (peek == '(' && string[i] == '$')) /*)*/ + { + i += 2; + delimopen = '('; + delimiter = ')'; + nestdelim = 1; + goto get_word; + } +#if 0 + else if (peek == '\'' && string[i] == '$') + { + i += 2; /* XXX */ + return i; + } +#endif + + if (string[i] != '$') + { + i++; + return i; + } + } + + /* same code also used for $(...)/<(...)/>(...) above */ + if (member (string[i], "!@?+*")) + { + int peek = string[i + 1]; + + if (peek == '(') /*)*/ + { + /* Shell extended globbing patterns */ + i += 2; + delimopen = '('; + delimiter = ')'; /* XXX - not perfect */ + nestdelim = 1; + } + } + +get_word: + /* Get word from string + i; */ + + if (delimiter == 0 && 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; + } + + /* delimiter must be set and set to something other than a quote if + nestdelim is set, so these tests are safe. */ + if (nestdelim && string[i] == delimopen) + { + nestdelim++; + continue; + } + if (nestdelim && string[i] == delimiter) + { + nestdelim--; + if (nestdelim == 0) + delimiter = 0; + continue; + } + + if (delimiter && string[i] == delimiter) + { + delimiter = 0; + continue; + } + + if (delimiter == 0 && (member (string[i], history_word_delimiters))) + break; + + if (delimiter == 0 && 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 @@ -1217,19 +1560,21 @@ history_arg_extract (first, last, string) *INDP. */ static char ** history_tokenize_internal (string, wind, indp) - char *string; + const char *string; int 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. */ + if (indp && wind != -1) + *indp = -1; /* Get a token, and stuff it into RESULT. The tokens are split 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++) ; @@ -1237,88 +1582,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] = 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); @@ -1328,11 +1615,23 @@ history_tokenize_internal (string, wind, indp) parsed out of STRING. */ char ** history_tokenize (string) - char *string; + const char *string; { return (history_tokenize_internal (string, -1, (int *)NULL)); } +/* Free members of WORDS from START to an empty string */ +static void +freewords (words, start) + char **words; + int start; +{ + register int i; + + for (i = start; words[i]; i++) + xfree (words[i]); +} + /* Find and return the word which contains the character at index IND in the history line LINE. Used to save the word matched by the last history !?string? search. */ @@ -1345,13 +1644,17 @@ history_find_word (line, ind) int i, wind; words = history_tokenize_internal (line, ind, &wind); - if (wind == -1) - return ((char *)NULL); + if (wind == -1 || words == 0) + { + if (words) + freewords (words, 0); + FREE (words); + return ((char *)NULL); + } s = words[wind]; for (i = 0; i < wind; i++) - free (words[i]); - for (i = wind + 1; words[i]; i++) - free (words[i]); - free (words); + xfree (words[i]); + freewords (words, wind + 1); + xfree (words); return s; }