1 /* histexpand.c -- history expansion. */
3 /* Copyright (C) 1989, 1992 Free Software Foundation, Inc.
5 This file contains the GNU History Library (the Library), a set of
6 routines for managing the text of previously typed lines.
8 The Library is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
13 The Library is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 The GNU General Public License is often shipped with GNU software, and
19 is generally kept in a file called COPYING or LICENSE. If you do not
20 have a copy of the license, write to the Free Software Foundation,
21 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
23 #define READLINE_LIBRARY
25 #if defined (HAVE_CONFIG_H)
31 #if defined (HAVE_STDLIB_H)
34 # include "ansi_stdlib.h"
35 #endif /* HAVE_STDLIB_H */
37 #if defined (HAVE_UNISTD_H)
39 # include <sys/types.h>
52 #define HISTORY_WORD_DELIMITERS " \t\n;&()|<>"
53 #define HISTORY_QUOTE_CHARACTERS "\"'`"
55 typedef int _hist_search_func_t PARAMS((const char *, int));
57 extern int rl_byte_oriented; /* declared in mbutil.c */
59 static char error_pointer;
61 static char *subst_lhs;
62 static char *subst_rhs;
63 static int subst_lhs_len;
64 static int subst_rhs_len;
66 static char *get_history_word_specifier PARAMS((char *, char *, int *));
67 static char *history_find_word PARAMS((char *, int));
69 static char *quote_breaks PARAMS((char *));
71 /* Variables exported by this file. */
72 /* The character that represents the start of a history expansion
73 request. This is usually `!'. */
74 char history_expansion_char = '!';
76 /* The character that invokes word substitution if found at the start of
77 a line. This is usually `^'. */
78 char history_subst_char = '^';
80 /* During tokenization, if this character is seen as the first character
81 of a word, then it, and all subsequent characters upto a newline are
82 ignored. For a Bourne shell, this should be '#'. Bash special cases
83 the interactive comment character to not be a comment delimiter. */
84 char history_comment_char = '\0';
86 /* The list of characters which inhibit the expansion of text if found
87 immediately following history_expansion_char. */
88 char *history_no_expand_chars = " \t\n\r=";
90 /* If set to a non-zero value, single quotes inhibit history expansion.
92 int history_quotes_inhibit_expansion = 0;
94 /* Used to split words by history_tokenize_internal. */
95 char *history_word_delimiters = HISTORY_WORD_DELIMITERS;
97 /* If set, this points to a function that is called to verify that a
98 particular history expansion should be performed. */
99 rl_linebuf_func_t *history_inhibit_expansion_function;
101 /* **************************************************************** */
103 /* History Expansion */
105 /* **************************************************************** */
107 /* Hairy history expansion on text, not tokens. This is of general
108 use, and thus belongs in this library. */
110 /* The last string searched for by a !?string? search. */
111 static char *search_string;
113 /* The last string matched by a !?string? search. */
114 static char *search_match;
116 /* Return the event specified at TEXT + OFFSET modifying OFFSET to
117 point to after the event specifier. Just a pointer to the history
118 line is returned; NULL is returned in the event of a bad specifier.
119 You pass STRING with *INDEX equal to the history_expansion_char that
120 begins this specification.
121 DELIMITING_QUOTE is a character that is allowed to end the string
122 specification for what to search for in addition to the normal
123 characters `:', ` ', `\t', `\n', and sometimes `?'.
124 So you might call this function like:
125 line = get_history_event ("!echo:p", &index, 0); */
127 get_history_event (string, caller_index, delimiting_quote)
130 int delimiting_quote;
135 int which, sign, local_index, substring_okay;
136 _hist_search_func_t *search_func;
139 /* The event can be specified in a number of ways.
141 !! the previous command
143 !-n current command-line minus N
144 !str the most recent command starting with STR
146 the most recent command containing STR
148 All values N are determined via HISTORY_BASE. */
152 if (string[i] != history_expansion_char)
153 return ((char *)NULL);
155 /* Move on to the specification. */
161 #define RETURN_ENTRY(e, w) \
162 return ((e = history_get (w)) ? e->line : (char *)NULL)
164 /* Handle !! case. */
165 if (string[i] == history_expansion_char)
168 which = history_base + (history_length - 1);
170 RETURN_ENTRY (entry, which);
173 /* Hack case of numeric line specification. */
174 if (string[i] == '-')
180 if (_rl_digit_p (string[i]))
182 /* Get the extent of the digits and compute the value. */
183 for (which = 0; _rl_digit_p (string[i]); i++)
184 which = (which * 10) + _rl_digit_value (string[i]);
189 which = (history_length + history_base) - which;
191 RETURN_ENTRY (entry, which);
194 /* This must be something to search for. If the spec begins with
195 a '?', then the string may be anywhere on the line. Otherwise,
196 the string must be found at the start of a line. */
197 if (string[i] == '?')
203 /* Only a closing `?' or a newline delimit a substring search string. */
204 for (local_index = i; c = string[i]; i++)
205 #if defined (HANDLE_MULTIBYTE)
206 if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
211 memset (&ps, 0, sizeof (mbstate_t));
212 /* These produce warnings because we're passing a const string to a
213 function that takes a non-const string. */
214 _rl_adjust_point (string, i, &ps);
215 if ((v = _rl_get_char_len (string + i, &ps)) > 1)
222 #endif /* HANDLE_MULTIBYTE */
223 if ((!substring_okay && (whitespace (c) || c == ':' ||
224 (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
225 string[i] == delimiting_quote)) ||
227 (substring_okay && string[i] == '?'))
230 which = i - local_index;
231 temp = (char *)xmalloc (1 + which);
233 strncpy (temp, string + local_index, which);
236 if (substring_okay && string[i] == '?')
241 #define FAIL_SEARCH() \
243 history_offset = history_length; free (temp) ; return (char *)NULL; \
246 /* If there is no search string, try to use the previous search string,
247 if one exists. If not, fail immediately. */
248 if (*temp == '\0' && substring_okay)
253 temp = savestring (search_string);
259 search_func = substring_okay ? history_search : history_search_prefix;
262 local_index = (*search_func) (temp, -1);
267 if (local_index == 0 || substring_okay)
269 entry = current_history ();
270 history_offset = history_length;
272 /* If this was a substring search, then remember the
273 string that we matched for word substitution. */
276 FREE (search_string);
277 search_string = temp;
280 search_match = history_find_word (entry->line, local_index);
285 return (entry->line);
297 /* Function for extracting single-quoted strings. Used for inhibiting
298 history expansion within single quotes. */
300 /* Extract the contents of STRING as if it is enclosed in single quotes.
301 SINDEX, when passed in, is the offset of the character immediately
302 following the opening single quote; on exit, SINDEX is left pointing
303 to the closing single quote. */
305 hist_string_extract_single_quoted (string, sindex)
311 for (i = *sindex; string[i] && string[i] != '\''; i++)
321 register char *p, *r;
325 for (p = s; p && *p; p++, len++)
329 else if (whitespace (*p) || *p == '\n')
333 r = ret = (char *)xmalloc (len);
335 for (p = s; p && *p; )
345 else if (whitespace (*p) || *p == '\n')
360 hist_error(s, start, current, errtype)
362 int start, current, errtype;
368 ll = current - start;
372 case EVENT_NOT_FOUND:
373 emsg = "event not found";
377 emsg = "bad word specifier";
381 emsg = "substitution failed";
385 emsg = "unrecognized history modifier";
389 emsg = "no previous substitution";
393 emsg = "unknown expansion error";
398 temp = (char *)xmalloc (ll + elen + 3);
399 strncpy (temp, s + start, ll);
402 strcpy (temp + ll + 2, emsg);
406 /* Get a history substitution string from STR starting at *IPTR
407 and return it. The length is returned in LENPTR.
409 A backslash can quote the delimiter. If the string is the
410 empty string, the previous pattern is used. If there is
411 no previous pattern for the lhs, the last history search
414 If IS_RHS is 1, we ignore empty strings and set the pattern
415 to "" anyway. subst_lhs is not changed if the lhs is empty;
416 subst_rhs is allowed to be set to the empty string. */
419 get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
421 int *iptr, delimiter, is_rhs, *lenptr;
423 register int si, i, j, k;
425 #if defined (HANDLE_MULTIBYTE)
432 #if defined (HANDLE_MULTIBYTE)
433 memset (&ps, 0, sizeof (mbstate_t));
434 _rl_adjust_point (str, i, &ps);
437 for (si = i; str[si] && str[si] != delimiter; si++)
438 #if defined (HANDLE_MULTIBYTE)
439 if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
442 if ((v = _rl_get_char_len (str + si, &ps)) > 1)
444 else if (str[si] == '\\' && str[si + 1] == delimiter)
448 #endif /* HANDLE_MULTIBYTE */
449 if (str[si] == '\\' && str[si + 1] == delimiter)
452 if (si > i || is_rhs)
454 s = (char *)xmalloc (si - i + 1);
455 for (j = 0, k = i; k < si; j++, k++)
457 /* Remove a backslash quoting the search string delimiter. */
458 if (str[k] == '\\' && str[k + 1] == delimiter)
476 postproc_subst_rhs ()
481 new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len);
482 for (i = j = 0; i < subst_rhs_len; i++)
484 if (subst_rhs[i] == '&')
486 if (j + subst_lhs_len >= new_size)
487 new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
488 strcpy (new + j, subst_lhs);
493 /* a single backslash protects the `&' from lhs interpolation */
494 if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
497 new = (char *)xrealloc (new, new_size *= 2);
498 new[j++] = subst_rhs[i];
507 /* Expand the bulk of a history specifier starting at STRING[START].
508 Returns 0 if everything is OK, -1 if an error occurred, and 1
509 if the `p' modifier was supplied and the caller should just print
510 the returned string. Returns the new index into string in
511 *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
513 history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
515 int start, *end_index_ptr;
517 char *current_line; /* for !# */
519 int i, n, starting_index;
520 int substitute_globally, want_quotes, print_only;
521 char *event, *temp, *result, *tstr, *t, c, *word_spec;
523 #if defined (HANDLE_MULTIBYTE)
526 memset (&ps, 0, sizeof (mbstate_t));
529 result = (char *)xmalloc (result_len = 128);
533 /* If it is followed by something that starts a word specifier,
534 then !! is implied as the event specifier. */
536 if (member (string[i + 1], ":$*%^"))
541 fake_s[0] = fake_s[1] = history_expansion_char;
543 event = get_history_event (fake_s, &fake_i, 0);
545 else if (string[i + 1] == '#')
548 event = current_line;
552 int quoted_search_delimiter = 0;
554 /* If the character before this `!' is a double or single
555 quote, then this expansion takes place inside of the
556 quoted string. If we have to search for some text ("!foo"),
557 allow the delimiter to end the search string. */
558 #if defined (HANDLE_MULTIBYTE)
559 if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
562 l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY);
564 /* XXX - original patch had i - 1 ??? If i == 0 it would fail. */
565 if (i && (c == '\'' || c == '"'))
566 quoted_search_delimiter = c;
569 #endif /* HANDLE_MULTIBYTE */
570 if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
571 quoted_search_delimiter = string[i - 1];
573 event = get_history_event (string, &i, quoted_search_delimiter);
578 *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
583 /* If a word specifier is found, then do what that requires. */
585 word_spec = get_history_word_specifier (string, event, &i);
587 /* There is no such thing as a `malformed word specifier'. However,
588 it is possible for a specifier that has no match. In that case,
590 if (word_spec == (char *)&error_pointer)
592 *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
597 /* If no word specifier, than the thing of interest was the event. */
598 temp = word_spec ? savestring (word_spec) : savestring (event);
601 /* Perhaps there are other modifiers involved. Do what they say. */
602 want_quotes = substitute_globally = print_only = 0;
605 while (string[i] == ':')
611 substitute_globally = 1;
619 *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
632 /* :p means make this the last executed line. So we
633 return an error state after adding this line to the
639 /* :t discards all but the last part of the pathname. */
641 tstr = strrchr (temp, '/');
645 t = savestring (tstr);
651 /* :h discards the last part of a pathname. */
653 tstr = strrchr (temp, '/');
658 /* :r discards the suffix. */
660 tstr = strrchr (temp, '.');
665 /* :e discards everything but the suffix. */
667 tstr = strrchr (temp, '.');
670 t = savestring (tstr);
676 /* :s/this/that substitutes `that' for the first
677 occurrence of `this'. :gs/this/that substitutes `that'
678 for each occurrence of `this'. :& repeats the last
679 substitution. :g& repeats the last substitution
686 int delimiter, failed, si, l_temp;
690 if (i + 2 < (int)strlen (string))
692 #if defined (HANDLE_MULTIBYTE)
693 if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
695 _rl_adjust_point (string, i + 2, &ps);
696 if (_rl_get_char_len (string + i + 2, &ps) > 1)
699 delimiter = string[i + 2];
702 #endif /* HANDLE_MULTIBYTE */
703 delimiter = string[i + 2];
706 break; /* no search delimiter */
710 t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
711 /* An empty substitution lhs with no previous substitution
712 uses the last search string as the lhs. */
720 if (search_string && *search_string)
722 subst_lhs = savestring (search_string);
723 subst_lhs_len = strlen (subst_lhs);
727 subst_lhs = (char *) NULL;
733 subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
735 /* If `&' appears in the rhs, it's supposed to be replaced
737 if (member ('&', subst_rhs))
738 postproc_subst_rhs ();
743 /* If there is no lhs, the substitution can't succeed. */
744 if (subst_lhs_len == 0)
746 *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
752 l_temp = strlen (temp);
753 /* Ignore impossible cases. */
754 if (subst_lhs_len > l_temp)
756 *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
762 /* Find the first occurrence of THIS in TEMP. */
764 for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
765 if (STREQN (temp+si, subst_lhs, subst_lhs_len))
767 int len = subst_rhs_len - subst_lhs_len + l_temp;
768 new_event = (char *)xmalloc (1 + len);
769 strncpy (new_event, temp, si);
770 strncpy (new_event + si, subst_rhs, subst_rhs_len);
771 strncpy (new_event + si + subst_rhs_len,
772 temp + si + subst_lhs_len,
773 l_temp - (si + subst_lhs_len));
774 new_event[len] = '\0';
780 if (substitute_globally)
783 l_temp = strlen (temp);
784 substitute_globally++;
791 if (substitute_globally > 1)
793 substitute_globally = 0;
794 continue; /* don't want to increment i */
798 continue; /* don't want to increment i */
800 *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
808 /* Done with modfiers. */
809 /* Believe it or not, we have to back the pointer up by one. */
816 if (want_quotes == 'q')
817 x = sh_single_quote (temp);
818 else if (want_quotes == 'x')
819 x = quote_breaks (temp);
821 x = savestring (temp);
829 result = (char *)xrealloc (result, n + 2);
830 strcpy (result, temp);
834 *ret_string = result;
838 /* Expand the string STRING, placing the result into OUTPUT, a pointer
839 to a string. Returns:
841 -1) If there was an error in expansion.
842 0) If no expansions took place (or, if the only change in
843 the text was the de-slashifying of the history expansion
845 1) If expansions did take place
846 2) If the `p' modifier was given and the caller should print the result
848 If an error ocurred in expansion, then OUTPUT contains a descriptive
851 #define ADD_STRING(s) \
854 int sl = strlen (s); \
856 if (j >= result_len) \
858 while (j >= result_len) \
860 result = (char *)xrealloc (result, result_len); \
862 strcpy (result + j - sl, s); \
866 #define ADD_CHAR(c) \
869 if (j >= result_len - 1) \
870 result = (char *)xrealloc (result, result_len += 64); \
877 history_expand (hstring, output)
882 int i, r, l, passc, cc, modified, eindex, only_printing;
885 /* The output string, and its length. */
889 #if defined (HANDLE_MULTIBYTE)
894 /* Used when adding the string. */
900 /* Setting the history expansion character to 0 inhibits all
901 history expansion. */
902 if (history_expansion_char == 0)
904 *output = savestring (hstring);
908 /* Prepare the buffer for printing error messages. */
909 result = (char *)xmalloc (result_len = 256);
912 only_printing = modified = 0;
913 l = strlen (hstring);
915 /* Grovel the string. Only backslash and single quotes can quote the
916 history escape character. We also handle arg specifiers. */
918 /* Before we grovel forever, see if the history_expansion_char appears
919 anywhere within the text. */
921 /* The quick substitution character is a history expansion all right. That
922 is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
923 that is the substitution that we do. */
924 if (hstring[0] == history_subst_char)
926 string = (char *)xmalloc (l + 5);
928 string[0] = string[1] = history_expansion_char;
931 strcpy (string + 4, hstring);
936 #if defined (HANDLE_MULTIBYTE)
937 memset (&ps, 0, sizeof (mbstate_t));
941 /* If not quick substitution, still maybe have to do expansion. */
943 /* `!' followed by one of the characters in history_no_expand_chars
944 is NOT an expansion. */
945 for (i = 0; string[i]; i++)
947 #if defined (HANDLE_MULTIBYTE)
948 if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
951 v = _rl_get_char_len (string + i, &ps);
958 #endif /* HANDLE_MULTIBYTE */
961 /* The history_comment_char, if set, appearing at the beginning
962 of a word signifies that the rest of the line should not have
963 history expansion performed on it.
964 Skip the rest of the line and break out of the loop. */
965 if (history_comment_char && string[i] == history_comment_char &&
966 (i == 0 || member (string[i - 1], history_word_delimiters)))
972 else if (string[i] == history_expansion_char)
974 if (!cc || member (cc, history_no_expand_chars))
976 /* If the calling application has set
977 history_inhibit_expansion_function to a function that checks
978 for special cases that should not be history expanded,
979 call the function and skip the expansion if it returns a
981 else if (history_inhibit_expansion_function &&
982 (*history_inhibit_expansion_function) (string, i))
987 /* XXX - at some point, might want to extend this to handle
988 double quotes as well. */
989 else if (history_quotes_inhibit_expansion && string[i] == '\'')
991 /* If this is bash, single quotes inhibit history expansion. */
993 hist_string_extract_single_quoted (string, &i);
995 else if (history_quotes_inhibit_expansion && string[i] == '\\')
997 /* If this is bash, allow backslashes to quote single
998 quotes and the history expansion character. */
999 if (cc == '\'' || cc == history_expansion_char)
1004 if (string[i] != history_expansion_char)
1007 *output = savestring (string);
1012 /* Extract and perform the substitution. */
1013 for (passc = i = j = 0; i < l; i++)
1015 int tchar = string[i];
1024 #if defined (HANDLE_MULTIBYTE)
1025 if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1030 memset (mb, 0, sizeof (mb));
1031 for (k = 0; k < MB_LEN_MAX; k++)
1034 memset (&ps, 0, sizeof (mbstate_t));
1035 if (_rl_get_char_len (mb, &ps) == -2)
1040 if (strlen (mb) > 1)
1046 #endif /* HANDLE_MULTIBYTE */
1048 if (tchar == history_expansion_char)
1050 else if (tchar == history_comment_char)
1056 ADD_CHAR (string[i]);
1066 /* If history_quotes_inhibit_expansion is set, single quotes
1067 inhibit history expansion. */
1068 if (history_quotes_inhibit_expansion)
1073 hist_string_extract_single_quoted (string, &i);
1075 slen = i - quote + 2;
1076 temp = (char *)xmalloc (slen);
1077 strncpy (temp, string + quote, slen);
1078 temp[slen - 1] = '\0';
1083 ADD_CHAR (string[i]);
1087 case -2: /* history_comment_char */
1088 if (i == 0 || member (string[i - 1], history_word_delimiters))
1090 temp = (char *)xmalloc (l - i + 1);
1091 strcpy (temp, string + i);
1097 ADD_CHAR (string[i]);
1100 case -3: /* history_expansion_char */
1103 /* If the history_expansion_char is followed by one of the
1104 characters in history_no_expand_chars, then it is not a
1105 candidate for expansion of any kind. */
1106 if (member (cc, history_no_expand_chars))
1108 ADD_CHAR (string[i]);
1112 #if defined (NO_BANG_HASH_MODIFIERS)
1113 /* There is something that is listed as a `word specifier' in csh
1114 documentation which means `the expanded text to this point'.
1115 That is not a word specifier, it is an event specifier. If we
1116 don't want to allow modifiers with `!#', just stick the current
1117 output line in again. */
1122 temp = (char *)xmalloc (1 + strlen (result));
1123 strcpy (temp, result);
1132 r = history_expand_internal (string, i, &eindex, &temp, result);
1137 if (string != hstring)
1150 only_printing = r == 1;
1158 if (string != hstring)
1163 add_history (result);
1167 return (modified != 0);
1170 /* Return a consed string which is the word specified in SPEC, and found
1171 in FROM. NULL is returned if there is no spec. The address of
1172 ERROR_POINTER is returned if the word specified cannot be found.
1173 CALLER_INDEX is the offset in SPEC to start looking; it is updated
1174 to point to just after the last character parsed. */
1176 get_history_word_specifier (spec, from, caller_index)
1180 register int i = *caller_index;
1182 int expecting_word_spec = 0;
1185 /* The range of words to return doesn't exist yet. */
1187 result = (char *)NULL;
1189 /* If we found a colon, then this *must* be a word specification. If
1190 it isn't, then it is an error. */
1194 expecting_word_spec++;
1197 /* Handle special cases first. */
1199 /* `%' is the word last searched for. */
1202 *caller_index = i + 1;
1203 return (search_match ? savestring (search_match) : savestring (""));
1206 /* `*' matches all of the arguments, but not the command. */
1209 *caller_index = i + 1;
1210 result = history_arg_extract (1, '$', from);
1211 return (result ? result : savestring (""));
1214 /* `$' is last arg. */
1217 *caller_index = i + 1;
1218 return (history_arg_extract ('$', '$', from));
1221 /* Try to get FIRST and LAST figured out. */
1225 else if (spec[i] == '^')
1227 else if (_rl_digit_p (spec[i]) && expecting_word_spec)
1229 for (first = 0; _rl_digit_p (spec[i]); i++)
1230 first = (first * 10) + _rl_digit_value (spec[i]);
1233 return ((char *)NULL); /* no valid `first' for word specifier */
1235 if (spec[i] == '^' || spec[i] == '*')
1237 last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */
1240 else if (spec[i] != '-')
1246 if (_rl_digit_p (spec[i]))
1248 for (last = 0; _rl_digit_p (spec[i]); i++)
1249 last = (last * 10) + _rl_digit_value (spec[i]);
1251 else if (spec[i] == '$')
1257 else if (!spec[i] || spec[i] == ':')
1258 /* check against `:' because there could be a modifier separator */
1261 /* csh seems to allow anything to terminate the word spec here,
1262 leaving it as an abbreviation. */
1264 last = -1; /* x- abbreviates x-$ omitting word `$' */
1269 if (last >= first || last == '$' || last < 0)
1270 result = history_arg_extract (first, last, from);
1272 return (result ? result : (char *)&error_pointer);
1275 /* Extract the args specified, starting at FIRST, and ending at LAST.
1276 The args are taken from STRING. If either FIRST or LAST is < 0,
1277 then make that arg count from the right (subtract from the number of
1278 tokens, so that FIRST = -1 means the next to last token on the line).
1279 If LAST is `$' the last arg from STRING is used. */
1281 history_arg_extract (first, last, string)
1285 register int i, len;
1290 /* XXX - think about making history_tokenize return a struct array,
1291 each struct in array being a string and a length to avoid the
1292 calls to strlen below. */
1293 if ((list = history_tokenize (string)) == NULL)
1294 return ((char *)NULL);
1296 for (len = 0; list[len]; len++)
1300 last = len + last - 1;
1303 first = len + first - 1;
1313 if (first >= len || last > len || first < 0 || last < 0 || first > last)
1314 result = ((char *)NULL);
1317 for (size = 0, i = first; i < last; i++)
1318 size += strlen (list[i]) + 1;
1319 result = (char *)xmalloc (size + 1);
1322 for (i = first, offset = 0; i < last; i++)
1324 strcpy (result + offset, list[i]);
1325 offset += strlen (list[i]);
1328 result[offset++] = ' ';
1334 for (i = 0; i < len; i++)
1341 #define slashify_in_quotes "\\`\"$"
1343 /* Parse STRING into tokens and return an array of strings. If WIND is
1344 not -1 and INDP is not null, we also want the word surrounding index
1345 WIND. The position in the returned array of strings is returned in
1348 history_tokenize_internal (string, wind, indp)
1353 register int i, start, result_index, size;
1356 /* If we're searching for a string that's not part of a word (e.g., " "),
1357 make sure we set *INDP to a reasonable value. */
1358 if (indp && wind != -1)
1361 /* Get a token, and stuff it into RESULT. The tokens are split
1362 exactly where the shell would split them. */
1363 for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
1367 /* Skip leading whitespace. */
1368 for (; string[i] && whitespace (string[i]); i++)
1370 if (string[i] == 0 || string[i] == history_comment_char)
1375 if (member (string[i], "()\n"))
1381 if (member (string[i], "<>;&|$"))
1383 int peek = string[i + 1];
1385 if (peek == string[i] && peek != '$')
1387 if (peek == '<' && string[i + 2] == '-')
1394 if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
1395 ((peek == '>') && (string[i] == '&')) ||
1396 ((peek == '(') && (string[i] == '$')))
1402 if (string[i] != '$')
1409 /* Get word from string + i; */
1411 if (member (string[i], HISTORY_QUOTE_CHARACTERS))
1412 delimiter = string[i++];
1414 for (; string[i]; i++)
1416 if (string[i] == '\\' && string[i + 1] == '\n')
1422 if (string[i] == '\\' && delimiter != '\'' &&
1423 (delimiter != '"' || member (string[i], slashify_in_quotes)))
1429 if (delimiter && string[i] == delimiter)
1435 if (!delimiter && (member (string[i], history_word_delimiters)))
1438 if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS))
1439 delimiter = string[i];
1444 /* If we are looking for the word in which the character at a
1445 particular index falls, remember it. */
1446 if (indp && wind != -1 && wind >= start && wind < i)
1447 *indp = result_index;
1450 if (result_index + 2 >= size)
1451 result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
1452 result[result_index] = (char *)xmalloc (1 + len);
1453 strncpy (result[result_index], string + start, len);
1454 result[result_index][len] = '\0';
1455 result[++result_index] = (char *)NULL;
1461 /* Return an array of tokens, much as the shell might. The tokens are
1462 parsed out of STRING. */
1464 history_tokenize (string)
1467 return (history_tokenize_internal (string, -1, (int *)NULL));
1470 /* Find and return the word which contains the character at index IND
1471 in the history line LINE. Used to save the word matched by the
1472 last history !?string? search. */
1474 history_find_word (line, ind)
1481 words = history_tokenize_internal (line, ind, &wind);
1482 if (wind == -1 || words == 0)
1483 return ((char *)NULL);
1485 for (i = 0; i < wind; i++)
1487 for (i = wind + 1; words[i]; i++)