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 1, 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 675 Mass Ave, Cambridge, MA 02139, 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>
44 #if defined (HAVE_STRING_H)
48 #endif /* !HAVE_STRING_H */
53 #define HISTORY_WORD_DELIMITERS " \t\n;&()|<>"
54 #define HISTORY_QUOTE_CHARACTERS "\"'`"
56 static char error_pointer;
58 static char *subst_lhs;
59 static char *subst_rhs;
60 static int subst_lhs_len;
61 static int subst_rhs_len;
63 static char *get_history_word_specifier ();
64 static char *history_find_word ();
66 extern int history_offset;
68 extern char *single_quote ();
69 static char *quote_breaks ();
71 extern char *xmalloc (), *xrealloc ();
73 /* Variables exported by this file. */
74 /* The character that represents the start of a history expansion
75 request. This is usually `!'. */
76 char history_expansion_char = '!';
78 /* The character that invokes word substitution if found at the start of
79 a line. This is usually `^'. */
80 char history_subst_char = '^';
82 /* During tokenization, if this character is seen as the first character
83 of a word, then it, and all subsequent characters upto a newline are
84 ignored. For a Bourne shell, this should be '#'. Bash special cases
85 the interactive comment character to not be a comment delimiter. */
86 char history_comment_char = '\0';
88 /* The list of characters which inhibit the expansion of text if found
89 immediately following history_expansion_char. */
90 char *history_no_expand_chars = " \t\n\r=";
92 /* If set to a non-zero value, single quotes inhibit history expansion.
94 int history_quotes_inhibit_expansion = 0;
96 /* If set, this points to a function that is called to verify that a
97 particular history expansion should be performed. */
98 Function *history_inhibit_expansion_function;
100 /* **************************************************************** */
102 /* History Expansion */
104 /* **************************************************************** */
106 /* Hairy history expansion on text, not tokens. This is of general
107 use, and thus belongs in this library. */
109 /* The last string searched for by a !?string? search. */
110 static char *search_string;
112 /* The last string matched by a !?string? search. */
113 static char *search_match;
115 /* Return the event specified at TEXT + OFFSET modifying OFFSET to
116 point to after the event specifier. Just a pointer to the history
117 line is returned; NULL is returned in the event of a bad specifier.
118 You pass STRING with *INDEX equal to the history_expansion_char that
119 begins this specification.
120 DELIMITING_QUOTE is a character that is allowed to end the string
121 specification for what to search for in addition to the normal
122 characters `:', ` ', `\t', `\n', and sometimes `?'.
123 So you might call this function like:
124 line = get_history_event ("!echo:p", &index, 0); */
126 get_history_event (string, caller_index, delimiting_quote)
129 int delimiting_quote;
134 int which, sign, local_index, substring_okay;
135 Function *search_func;
138 /* The event can be specified in a number of ways.
140 !! the previous command
142 !-n current command-line minus N
143 !str the most recent command starting with STR
145 the most recent command containing STR
147 All values N are determined via HISTORY_BASE. */
151 if (string[i] != history_expansion_char)
152 return ((char *)NULL);
154 /* Move on to the specification. */
160 #define RETURN_ENTRY(e, w) \
161 return ((e = history_get (w)) ? e->line : (char *)NULL)
163 /* Handle !! case. */
164 if (string[i] == history_expansion_char)
167 which = history_base + (history_length - 1);
169 RETURN_ENTRY (entry, which);
172 /* Hack case of numeric line specification. */
173 if (string[i] == '-')
179 if (_rl_digit_p (string[i]))
181 /* Get the extent of the digits and compute the value. */
182 for (which = 0; _rl_digit_p (string[i]); i++)
183 which = (which * 10) + _rl_digit_value (string[i]);
188 which = (history_length + history_base) - which;
190 RETURN_ENTRY (entry, which);
193 /* This must be something to search for. If the spec begins with
194 a '?', then the string may be anywhere on the line. Otherwise,
195 the string must be found at the start of a line. */
196 if (string[i] == '?')
202 /* Only a closing `?' or a newline delimit a substring search string. */
203 for (local_index = i; c = string[i]; i++)
204 if ((!substring_okay && (whitespace (c) || c == ':' ||
205 (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
206 string[i] == delimiting_quote)) ||
208 (substring_okay && string[i] == '?'))
211 which = i - local_index;
212 temp = xmalloc (1 + which);
214 strncpy (temp, string + local_index, which);
217 if (substring_okay && string[i] == '?')
222 #define FAIL_SEARCH() \
224 history_offset = history_length; free (temp) ; return (char *)NULL; \
227 /* If there is no search string, try to use the previous search string,
228 if one exists. If not, fail immediately. */
229 if (*temp == '\0' && substring_okay)
234 temp = savestring (search_string);
240 search_func = substring_okay ? history_search : history_search_prefix;
243 local_index = (*search_func) (temp, -1);
248 if (local_index == 0 || substring_okay)
250 entry = current_history ();
251 history_offset = history_length;
253 /* If this was a substring search, then remember the
254 string that we matched for word substitution. */
257 FREE (search_string);
258 search_string = temp;
261 search_match = history_find_word (entry->line, local_index);
266 return (entry->line);
278 /* Function for extracting single-quoted strings. Used for inhibiting
279 history expansion within single quotes. */
281 /* Extract the contents of STRING as if it is enclosed in single quotes.
282 SINDEX, when passed in, is the offset of the character immediately
283 following the opening single quote; on exit, SINDEX is left pointing
284 to the closing single quote. */
286 hist_string_extract_single_quoted (string, sindex)
292 for (i = *sindex; string[i] && string[i] != '\''; i++)
302 register char *p, *r;
306 for (p = s; p && *p; p++, len++)
310 else if (whitespace (*p) || *p == '\n')
314 r = ret = xmalloc (len);
316 for (p = s; p && *p; )
326 else if (whitespace (*p) || *p == '\n')
341 hist_error(s, start, current, errtype)
343 int start, current, errtype;
348 ll = current - start;
352 case EVENT_NOT_FOUND:
353 emsg = "event not found";
357 emsg = "bad word specifier";
361 emsg = "substitution failed";
365 emsg = "unrecognized history modifier";
369 emsg = "no previous substitution";
373 emsg = "unknown expansion error";
378 temp = xmalloc (ll + elen + 3);
379 strncpy (temp, s + start, ll);
382 strcpy (temp + ll + 2, emsg);
386 /* Get a history substitution string from STR starting at *IPTR
387 and return it. The length is returned in LENPTR.
389 A backslash can quote the delimiter. If the string is the
390 empty string, the previous pattern is used. If there is
391 no previous pattern for the lhs, the last history search
394 If IS_RHS is 1, we ignore empty strings and set the pattern
395 to "" anyway. subst_lhs is not changed if the lhs is empty;
396 subst_rhs is allowed to be set to the empty string. */
399 get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
401 int *iptr, delimiter, is_rhs, *lenptr;
403 register int si, i, j, k;
404 char *s = (char *) NULL;
408 for (si = i; str[si] && str[si] != delimiter; si++)
409 if (str[si] == '\\' && str[si + 1] == delimiter)
412 if (si > i || is_rhs)
414 s = xmalloc (si - i + 1);
415 for (j = 0, k = i; k < si; j++, k++)
417 /* Remove a backslash quoting the search string delimiter. */
418 if (str[k] == '\\' && str[k + 1] == delimiter)
436 postproc_subst_rhs ()
441 new = xmalloc (new_size = subst_rhs_len + subst_lhs_len);
442 for (i = j = 0; i < subst_rhs_len; i++)
444 if (subst_rhs[i] == '&')
446 if (j + subst_lhs_len >= new_size)
447 new = xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
448 strcpy (new + j, subst_lhs);
453 /* a single backslash protects the `&' from lhs interpolation */
454 if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
457 new = xrealloc (new, new_size *= 2);
458 new[j++] = subst_rhs[i];
467 /* Expand the bulk of a history specifier starting at STRING[START].
468 Returns 0 if everything is OK, -1 if an error occurred, and 1
469 if the `p' modifier was supplied and the caller should just print
470 the returned string. Returns the new index into string in
471 *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
473 history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
475 int start, *end_index_ptr;
477 char *current_line; /* for !# */
479 int i, n, starting_index;
480 int substitute_globally, want_quotes, print_only;
481 char *event, *temp, *result, *tstr, *t, c, *word_spec;
484 result = xmalloc (result_len = 128);
488 /* If it is followed by something that starts a word specifier,
489 then !! is implied as the event specifier. */
491 if (member (string[i + 1], ":$*%^"))
496 fake_s[0] = fake_s[1] = history_expansion_char;
498 event = get_history_event (fake_s, &fake_i, 0);
500 else if (string[i + 1] == '#')
503 event = current_line;
507 int quoted_search_delimiter = 0;
509 /* If the character before this `!' is a double or single
510 quote, then this expansion takes place inside of the
511 quoted string. If we have to search for some text ("!foo"),
512 allow the delimiter to end the search string. */
513 if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
514 quoted_search_delimiter = string[i - 1];
515 event = get_history_event (string, &i, quoted_search_delimiter);
520 *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
525 /* If a word specifier is found, then do what that requires. */
527 word_spec = get_history_word_specifier (string, event, &i);
529 /* There is no such thing as a `malformed word specifier'. However,
530 it is possible for a specifier that has no match. In that case,
532 if (word_spec == (char *)&error_pointer)
534 *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
539 /* If no word specifier, than the thing of interest was the event. */
540 temp = word_spec ? savestring (word_spec) : savestring (event);
543 /* Perhaps there are other modifiers involved. Do what they say. */
544 want_quotes = substitute_globally = print_only = 0;
547 while (string[i] == ':')
553 substitute_globally = 1;
561 *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
574 /* :p means make this the last executed line. So we
575 return an error state after adding this line to the
581 /* :t discards all but the last part of the pathname. */
583 tstr = strrchr (temp, '/');
587 t = savestring (tstr);
593 /* :h discards the last part of a pathname. */
595 tstr = strrchr (temp, '/');
600 /* :r discards the suffix. */
602 tstr = strrchr (temp, '.');
607 /* :e discards everything but the suffix. */
609 tstr = strrchr (temp, '.');
612 t = savestring (tstr);
618 /* :s/this/that substitutes `that' for the first
619 occurrence of `this'. :gs/this/that substitutes `that'
620 for each occurrence of `this'. :& repeats the last
621 substitution. :g& repeats the last substitution
628 int delimiter, failed, si, l_temp;
632 if (i + 2 < (int)strlen (string))
633 delimiter = string[i + 2];
635 break; /* no search delimiter */
639 t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
640 /* An empty substitution lhs with no previous substitution
641 uses the last search string as the lhs. */
649 if (search_string && *search_string)
651 subst_lhs = savestring (search_string);
652 subst_lhs_len = strlen (subst_lhs);
656 subst_lhs = (char *) NULL;
662 subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
664 /* If `&' appears in the rhs, it's supposed to be replaced
666 if (member ('&', subst_rhs))
667 postproc_subst_rhs ();
672 /* If there is no lhs, the substitution can't succeed. */
673 if (subst_lhs_len == 0)
675 *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
681 l_temp = strlen (temp);
682 /* Ignore impossible cases. */
683 if (subst_lhs_len > l_temp)
685 *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
691 /* Find the first occurrence of THIS in TEMP. */
693 for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
694 if (STREQN (temp+si, subst_lhs, subst_lhs_len))
696 int len = subst_rhs_len - subst_lhs_len + l_temp;
697 new_event = xmalloc (1 + len);
698 strncpy (new_event, temp, si);
699 strncpy (new_event + si, subst_rhs, subst_rhs_len);
700 strncpy (new_event + si + subst_rhs_len,
701 temp + si + subst_lhs_len,
702 l_temp - (si + subst_lhs_len));
703 new_event[len] = '\0';
709 if (substitute_globally)
712 l_temp = strlen (temp);
713 substitute_globally++;
720 if (substitute_globally > 1)
722 substitute_globally = 0;
723 continue; /* don't want to increment i */
727 continue; /* don't want to increment i */
729 *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
737 /* Done with modfiers. */
738 /* Believe it or not, we have to back the pointer up by one. */
745 if (want_quotes == 'q')
746 x = single_quote (temp);
747 else if (want_quotes == 'x')
748 x = quote_breaks (temp);
750 x = savestring (temp);
758 result = xrealloc (result, n + 2);
759 strcpy (result, temp);
763 *ret_string = result;
767 /* Expand the string STRING, placing the result into OUTPUT, a pointer
768 to a string. Returns:
770 -1) If there was an error in expansion.
771 0) If no expansions took place (or, if the only change in
772 the text was the de-slashifying of the history expansion
774 1) If expansions did take place
775 2) If the `p' modifier was given and the caller should print the result
777 If an error ocurred in expansion, then OUTPUT contains a descriptive
780 #define ADD_STRING(s) \
783 int sl = strlen (s); \
785 if (j >= result_len) \
787 while (j >= result_len) \
789 result = xrealloc (result, result_len); \
791 strcpy (result + j - sl, s); \
795 #define ADD_CHAR(c) \
798 if (j >= result_len - 1) \
799 result = xrealloc (result, result_len += 64); \
806 history_expand (hstring, output)
811 int i, r, l, passc, cc, modified, eindex, only_printing;
814 /* The output string, and its length. */
818 /* Used when adding the string. */
821 /* Setting the history expansion character to 0 inhibits all
822 history expansion. */
823 if (history_expansion_char == 0)
825 *output = savestring (hstring);
829 /* Prepare the buffer for printing error messages. */
830 result = xmalloc (result_len = 256);
833 only_printing = modified = 0;
834 l = strlen (hstring);
836 /* Grovel the string. Only backslash and single quotes can quote the
837 history escape character. We also handle arg specifiers. */
839 /* Before we grovel forever, see if the history_expansion_char appears
840 anywhere within the text. */
842 /* The quick substitution character is a history expansion all right. That
843 is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
844 that is the substitution that we do. */
845 if (hstring[0] == history_subst_char)
847 string = xmalloc (l + 5);
849 string[0] = string[1] = history_expansion_char;
852 strcpy (string + 4, hstring);
858 /* If not quick substitution, still maybe have to do expansion. */
860 /* `!' followed by one of the characters in history_no_expand_chars
861 is NOT an expansion. */
862 for (i = 0; string[i]; i++)
865 /* The history_comment_char, if set, appearing that the beginning
866 of a word signifies that the rest of the line should not have
867 history expansion performed on it.
868 Skip the rest of the line and break out of the loop. */
869 if (history_comment_char && string[i] == history_comment_char &&
870 (i == 0 || member (string[i - 1], HISTORY_WORD_DELIMITERS)))
876 else if (string[i] == history_expansion_char)
878 if (!cc || member (cc, history_no_expand_chars))
880 /* If the calling application has set
881 history_inhibit_expansion_function to a function that checks
882 for special cases that should not be history expanded,
883 call the function and skip the expansion if it returns a
885 else if (history_inhibit_expansion_function &&
886 (*history_inhibit_expansion_function) (string, i))
891 /* XXX - at some point, might want to extend this to handle
892 double quotes as well. */
893 else if (history_quotes_inhibit_expansion && string[i] == '\'')
895 /* If this is bash, single quotes inhibit history expansion. */
897 hist_string_extract_single_quoted (string, &i);
899 else if (history_quotes_inhibit_expansion && string[i] == '\\')
901 /* If this is bash, allow backslashes to quote single
902 quotes and the history expansion character. */
903 if (cc == '\'' || cc == history_expansion_char)
908 if (string[i] != history_expansion_char)
911 *output = savestring (string);
916 /* Extract and perform the substitution. */
917 for (passc = i = j = 0; i < l; i++)
919 int tchar = string[i];
928 if (tchar == history_expansion_char)
930 else if (tchar == history_comment_char)
936 ADD_CHAR (string[i]);
946 /* If history_quotes_inhibit_expansion is set, single quotes
947 inhibit history expansion. */
948 if (history_quotes_inhibit_expansion)
953 hist_string_extract_single_quoted (string, &i);
955 slen = i - quote + 2;
956 temp = xmalloc (slen);
957 strncpy (temp, string + quote, slen);
958 temp[slen - 1] = '\0';
963 ADD_CHAR (string[i]);
967 case -2: /* history_comment_char */
968 if (i == 0 || member (string[i - 1], HISTORY_WORD_DELIMITERS))
970 temp = xmalloc (l - i + 1);
971 strcpy (temp, string + i);
977 ADD_CHAR (string[i]);
980 case -3: /* history_expansion_char */
983 /* If the history_expansion_char is followed by one of the
984 characters in history_no_expand_chars, then it is not a
985 candidate for expansion of any kind. */
986 if (member (cc, history_no_expand_chars))
988 ADD_CHAR (string[i]);
992 #if defined (NO_BANG_HASH_MODIFIERS)
993 /* There is something that is listed as a `word specifier' in csh
994 documentation which means `the expanded text to this point'.
995 That is not a word specifier, it is an event specifier. If we
996 don't want to allow modifiers with `!#', just stick the current
997 output line in again. */
1002 temp = xmalloc (1 + strlen (result));
1003 strcpy (temp, result);
1012 r = history_expand_internal (string, i, &eindex, &temp, result);
1017 if (string != hstring)
1030 only_printing = r == 1;
1038 if (string != hstring)
1043 add_history (result);
1047 return (modified != 0);
1050 /* Return a consed string which is the word specified in SPEC, and found
1051 in FROM. NULL is returned if there is no spec. The address of
1052 ERROR_POINTER is returned if the word specified cannot be found.
1053 CALLER_INDEX is the offset in SPEC to start looking; it is updated
1054 to point to just after the last character parsed. */
1056 get_history_word_specifier (spec, from, caller_index)
1060 register int i = *caller_index;
1062 int expecting_word_spec = 0;
1065 /* The range of words to return doesn't exist yet. */
1067 result = (char *)NULL;
1069 /* If we found a colon, then this *must* be a word specification. If
1070 it isn't, then it is an error. */
1074 expecting_word_spec++;
1077 /* Handle special cases first. */
1079 /* `%' is the word last searched for. */
1082 *caller_index = i + 1;
1083 return (search_match ? savestring (search_match) : savestring (""));
1086 /* `*' matches all of the arguments, but not the command. */
1089 *caller_index = i + 1;
1090 result = history_arg_extract (1, '$', from);
1091 return (result ? result : savestring (""));
1094 /* `$' is last arg. */
1097 *caller_index = i + 1;
1098 return (history_arg_extract ('$', '$', from));
1101 /* Try to get FIRST and LAST figured out. */
1105 else if (spec[i] == '^')
1107 else if (_rl_digit_p (spec[i]) && expecting_word_spec)
1109 for (first = 0; _rl_digit_p (spec[i]); i++)
1110 first = (first * 10) + _rl_digit_value (spec[i]);
1113 return ((char *)NULL); /* no valid `first' for word specifier */
1115 if (spec[i] == '^' || spec[i] == '*')
1117 last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */
1120 else if (spec[i] != '-')
1126 if (_rl_digit_p (spec[i]))
1128 for (last = 0; _rl_digit_p (spec[i]); i++)
1129 last = (last * 10) + _rl_digit_value (spec[i]);
1131 else if (spec[i] == '$')
1136 else if (!spec[i] || spec[i] == ':') /* could be modifier separator */
1137 last = -1; /* x- abbreviates x-$ omitting word `$' */
1142 if (last >= first || last == '$' || last < 0)
1143 result = history_arg_extract (first, last, from);
1145 return (result ? result : (char *)&error_pointer);
1148 /* Extract the args specified, starting at FIRST, and ending at LAST.
1149 The args are taken from STRING. If either FIRST or LAST is < 0,
1150 then make that arg count from the right (subtract from the number of
1151 tokens, so that FIRST = -1 means the next to last token on the line).
1152 If LAST is `$' the last arg from STRING is used. */
1154 history_arg_extract (first, last, string)
1158 register int i, len;
1163 /* XXX - think about making history_tokenize return a struct array,
1164 each struct in array being a string and a length to avoid the
1165 calls to strlen below. */
1166 if ((list = history_tokenize (string)) == NULL)
1167 return ((char *)NULL);
1169 for (len = 0; list[len]; len++)
1173 last = len + last - 1;
1176 first = len + first - 1;
1186 if (first >= len || last > len || first < 0 || last < 0 || first > last)
1187 result = ((char *)NULL);
1190 for (size = 0, i = first; i < last; i++)
1191 size += strlen (list[i]) + 1;
1192 result = xmalloc (size + 1);
1195 for (i = first, offset = 0; i < last; i++)
1197 strcpy (result + offset, list[i]);
1198 offset += strlen (list[i]);
1201 result[offset++] = ' ';
1207 for (i = 0; i < len; i++)
1214 #define slashify_in_quotes "\\`\"$"
1216 /* Parse STRING into tokens and return an array of strings. If WIND is
1217 not -1 and INDP is not null, we also want the word surrounding index
1218 WIND. The position in the returned array of strings is returned in
1221 history_tokenize_internal (string, wind, indp)
1226 register int i, start, result_index, size;
1229 /* Get a token, and stuff it into RESULT. The tokens are split
1230 exactly where the shell would split them. */
1231 for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
1235 /* Skip leading whitespace. */
1236 for (; string[i] && whitespace (string[i]); i++)
1238 if (string[i] == 0 || string[i] == history_comment_char)
1243 if (member (string[i], "()\n"))
1249 if (member (string[i], "<>;&|$"))
1251 int peek = string[i + 1];
1253 if (peek == string[i] && peek != '$')
1255 if (peek == '<' && string[i + 2] == '-')
1262 if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
1263 ((peek == '>') && (string[i] == '&')) ||
1264 ((peek == '(') && (string[i] == '$')))
1270 if (string[i] != '$')
1277 /* Get word from string + i; */
1279 if (member (string[i], HISTORY_QUOTE_CHARACTERS))
1280 delimiter = string[i++];
1282 for (; string[i]; i++)
1284 if (string[i] == '\\' && string[i + 1] == '\n')
1290 if (string[i] == '\\' && delimiter != '\'' &&
1291 (delimiter != '"' || member (string[i], slashify_in_quotes)))
1297 if (delimiter && string[i] == delimiter)
1303 if (!delimiter && (member (string[i], HISTORY_WORD_DELIMITERS)))
1306 if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS))
1307 delimiter = string[i];
1312 /* If we are looking for the word in which the character at a
1313 particular index falls, remember it. */
1314 if (indp && wind != -1 && wind >= start && wind < i)
1315 *indp = result_index;
1318 if (result_index + 2 >= size)
1319 result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
1320 result[result_index] = xmalloc (1 + len);
1321 strncpy (result[result_index], string + start, len);
1322 result[result_index][len] = '\0';
1323 result[++result_index] = (char *)NULL;
1329 /* Return an array of tokens, much as the shell might. The tokens are
1330 parsed out of STRING. */
1332 history_tokenize (string)
1335 return (history_tokenize_internal (string, -1, (int *)NULL));
1338 /* Find and return the word which contains the character at index IND
1339 in the history line LINE. Used to save the word matched by the
1340 last history !?string? search. */
1342 history_find_word (line, ind)
1349 words = history_tokenize_internal (line, ind, &wind);
1351 return ((char *)NULL);
1353 for (i = 0; i < wind; i++)
1355 for (i = wind + 1; words[i]; i++)