Bug Summary

File:src/gnu/lib/libreadline/histexpand.c
Warning:line 1186, column 3
Value stored to 'first' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name histexpand.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/gnu/lib/libreadline/obj -resource-dir /usr/local/lib/clang/13.0.0 -D HAVE_CONFIG_H -I /usr/src/gnu/lib/libreadline -D PIC -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/gnu/lib/libreadline/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/gnu/lib/libreadline/histexpand.c
1/* histexpand.c -- history expansion. */
2
3/* Copyright (C) 1989, 1992 Free Software Foundation, Inc.
4
5 This file contains the GNU History Library (the Library), a set of
6 routines for managing the text of previously typed lines.
7
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)
11 any later version.
12
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.
17
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. */
22
23#define READLINE_LIBRARY
24
25#if defined (HAVE_CONFIG_H1)
26# include <config.h>
27#endif
28
29#include <stdio.h>
30
31#if defined (HAVE_STDLIB_H1)
32# include <stdlib.h>
33#else
34# include "ansi_stdlib.h"
35#endif /* HAVE_STDLIB_H */
36
37#if defined (HAVE_UNISTD_H1)
38# ifndef _MINIX
39# include <sys/types.h>
40# endif
41# include <unistd.h>
42#endif
43
44#include "rlmbutil.h"
45
46#include "history.h"
47#include "histlib.h"
48
49#include "rlshell.h"
50#include "xmalloc.h"
51
52#define HISTORY_WORD_DELIMITERS" \t\n;&()|<>" " \t\n;&()|<>"
53#define HISTORY_QUOTE_CHARACTERS"\"'`" "\"'`"
54
55typedef int _hist_search_func_t PARAMS((const char *, int))(const char *, int);
56
57extern int rl_byte_oriented; /* declared in mbutil.c */
58
59static char error_pointer;
60
61static char *subst_lhs;
62static char *subst_rhs;
63static int subst_lhs_len;
64static int subst_rhs_len;
65
66static char *get_history_word_specifier PARAMS((char *, char *, int *))(char *, char *, int *);
67static char *history_find_word PARAMS((char *, int))(char *, int);
68
69static char *quote_breaks PARAMS((char *))(char *);
70
71/* Variables exported by this file. */
72/* The character that represents the start of a history expansion
73 request. This is usually `!'. */
74char history_expansion_char = '!';
75
76/* The character that invokes word substitution if found at the start of
77 a line. This is usually `^'. */
78char history_subst_char = '^';
79
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. */
84char history_comment_char = '\0';
85
86/* The list of characters which inhibit the expansion of text if found
87 immediately following history_expansion_char. */
88char *history_no_expand_chars = " \t\n\r=";
89
90/* If set to a non-zero value, single quotes inhibit history expansion.
91 The default is 0. */
92int history_quotes_inhibit_expansion = 0;
93
94/* Used to split words by history_tokenize_internal. */
95char *history_word_delimiters = HISTORY_WORD_DELIMITERS" \t\n;&()|<>";
96
97/* If set, this points to a function that is called to verify that a
98 particular history expansion should be performed. */
99rl_linebuf_func_t *history_inhibit_expansion_function;
100
101/* **************************************************************** */
102/* */
103/* History Expansion */
104/* */
105/* **************************************************************** */
106
107/* Hairy history expansion on text, not tokens. This is of general
108 use, and thus belongs in this library. */
109
110/* The last string searched for by a !?string? search. */
111static char *search_string;
112
113/* The last string matched by a !?string? search. */
114static char *search_match;
115
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); */
126char *
127get_history_event (string, caller_index, delimiting_quote)
128 const char *string;
129 int *caller_index;
130 int delimiting_quote;
131{
132 register int i;
133 register char c;
134 HIST_ENTRY *entry;
135 int which, sign, local_index, substring_okay;
136 _hist_search_func_t *search_func;
137 char *temp;
138
139 /* The event can be specified in a number of ways.
140
141 !! the previous command
142 !n command line N
143 !-n current command-line minus N
144 !str the most recent command starting with STR
145 !?str[?]
146 the most recent command containing STR
147
148 All values N are determined via HISTORY_BASE. */
149
150 i = *caller_index;
151
152 if (string[i] != history_expansion_char)
153 return ((char *)NULL((void *)0));
154
155 /* Move on to the specification. */
156 i++;
157
158 sign = 1;
159 substring_okay = 0;
160
161#define RETURN_ENTRY(e, w) \
162 return ((e = history_get (w)) ? e->line : (char *)NULL((void *)0))
163
164 /* Handle !! case. */
165 if (string[i] == history_expansion_char)
166 {
167 i++;
168 which = history_base + (history_length - 1);
169 *caller_index = i;
170 RETURN_ENTRY (entry, which);
171 }
172
173 /* Hack case of numeric line specification. */
174 if (string[i] == '-')
175 {
176 sign = -1;
177 i++;
178 }
179
180 if (_rl_digit_p (string[i])((string[i]) >= '0' && (string[i]) <= '9'))
181 {
182 /* Get the extent of the digits and compute the value. */
183 for (which = 0; _rl_digit_p (string[i])((string[i]) >= '0' && (string[i]) <= '9'); i++)
184 which = (which * 10) + _rl_digit_value (string[i])((string[i]) - '0');
185
186 *caller_index = i;
187
188 if (sign < 0)
189 which = (history_length + history_base) - which;
190
191 RETURN_ENTRY (entry, which);
192 }
193
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] == '?')
198 {
199 substring_okay++;
200 i++;
201 }
202
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_MAX1 > 1 && rl_byte_oriented == 0)
207 {
208 int v;
209 mbstate_t ps;
210
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)
216 {
217 i += v - 1;
218 continue;
219 }
220 }
221 else
222#endif /* HANDLE_MULTIBYTE */
223 if ((!substring_okay && (whitespace (c)(((c) == ' ') || ((c) == '\t')) || c == ':' ||
224 (history_search_delimiter_chars && member (c, history_search_delimiter_chars)((c) ? ((char *)strchr ((history_search_delimiter_chars), (c)
) != (char *)((void *)0)) : 0)
) ||
225 string[i] == delimiting_quote)) ||
226 string[i] == '\n' ||
227 (substring_okay && string[i] == '?'))
228 break;
229
230 which = i - local_index;
231 temp = (char *)xmalloc (1 + which);
232 if (which)
233 strncpy (temp, string + local_index, which);
234 temp[which] = '\0';
235
236 if (substring_okay && string[i] == '?')
237 i++;
238
239 *caller_index = i;
240
241#define FAIL_SEARCH() \
242 do { \
243 history_offset = history_length; free (temp) ; return (char *)NULL((void *)0); \
244 } while (0)
245
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)
249 {
250 if (search_string)
251 {
252 free (temp);
253 temp = savestring (search_string)xstrdup(search_string);
254 }
255 else
256 FAIL_SEARCH ();
257 }
258
259 search_func = substring_okay ? history_search : history_search_prefix;
260 while (1)
261 {
262 local_index = (*search_func) (temp, -1);
263
264 if (local_index < 0)
265 FAIL_SEARCH ();
266
267 if (local_index == 0 || substring_okay)
268 {
269 entry = current_history ();
270 history_offset = history_length;
271
272 /* If this was a substring search, then remember the
273 string that we matched for word substitution. */
274 if (substring_okay)
275 {
276 FREE (search_string)if (search_string) free (search_string);
277 search_string = temp;
278
279 FREE (search_match)if (search_match) free (search_match);
280 search_match = history_find_word (entry->line, local_index);
281 }
282 else
283 free (temp);
284
285 return (entry->line);
286 }
287
288 if (history_offset)
289 history_offset--;
290 else
291 FAIL_SEARCH ();
292 }
293#undef FAIL_SEARCH
294#undef RETURN_ENTRY
295}
296
297/* Function for extracting single-quoted strings. Used for inhibiting
298 history expansion within single quotes. */
299
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. */
304static void
305hist_string_extract_single_quoted (string, sindex)
306 char *string;
307 int *sindex;
308{
309 register int i;
310
311 for (i = *sindex; string[i] && string[i] != '\''; i++)
312 ;
313
314 *sindex = i;
315}
316
317static char *
318quote_breaks (s)
319 char *s;
320{
321 register char *p, *r;
322 char *ret;
323 int len = 3;
324
325 for (p = s; p && *p; p++, len++)
326 {
327 if (*p == '\'')
328 len += 3;
329 else if (whitespace (*p)(((*p) == ' ') || ((*p) == '\t')) || *p == '\n')
330 len += 2;
331 }
332
333 r = ret = (char *)xmalloc (len);
334 *r++ = '\'';
335 for (p = s; p && *p; )
336 {
337 if (*p == '\'')
338 {
339 *r++ = '\'';
340 *r++ = '\\';
341 *r++ = '\'';
342 *r++ = '\'';
343 p++;
344 }
345 else if (whitespace (*p)(((*p) == ' ') || ((*p) == '\t')) || *p == '\n')
346 {
347 *r++ = '\'';
348 *r++ = *p++;
349 *r++ = '\'';
350 }
351 else
352 *r++ = *p++;
353 }
354 *r++ = '\'';
355 *r = '\0';
356 return ret;
357}
358
359static char *
360hist_error(s, start, current, errtype)
361 char *s;
362 int start, current, errtype;
363{
364 char *temp;
365 const char *emsg;
366 int ll, elen, len;
367
368 ll = current - start;
369
370 switch (errtype)
371 {
372 case EVENT_NOT_FOUND0:
373 emsg = "event not found";
374 elen = 15;
375 break;
376 case BAD_WORD_SPEC1:
377 emsg = "bad word specifier";
378 elen = 18;
379 break;
380 case SUBST_FAILED2:
381 emsg = "substitution failed";
382 elen = 19;
383 break;
384 case BAD_MODIFIER3:
385 emsg = "unrecognized history modifier";
386 elen = 29;
387 break;
388 case NO_PREV_SUBST4:
389 emsg = "no previous substitution";
390 elen = 24;
391 break;
392 default:
393 emsg = "unknown expansion error";
394 elen = 23;
395 break;
396 }
397
398 len = ll + elen + 3;
399 temp = (char *)xmalloc (len);
400 strncpy (temp, s + start, ll);
401 strlcat (temp, ": ", len);
402 strlcat (temp, emsg, len);
403 return (temp);
404}
405
406/* Get a history substitution string from STR starting at *IPTR
407 and return it. The length is returned in LENPTR.
408
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
412 string is used.
413
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. */
417
418static char *
419get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
420 char *str;
421 int *iptr, delimiter, is_rhs, *lenptr;
422{
423 register int si, i, j, k;
424 char *s;
425#if defined (HANDLE_MULTIBYTE)
426 mbstate_t ps;
427#endif
428
429 s = (char *)NULL((void *)0);
430 i = *iptr;
431
432#if defined (HANDLE_MULTIBYTE)
433 memset (&ps, 0, sizeof (mbstate_t));
434 _rl_adjust_point (str, i, &ps);
435#endif
436
437 for (si = i; str[si] && str[si] != delimiter; si++)
438#if defined (HANDLE_MULTIBYTE)
439 if (MB_CUR_MAX1 > 1 && rl_byte_oriented == 0)
440 {
441 int v;
442 if ((v = _rl_get_char_len (str + si, &ps)) > 1)
443 si += v - 1;
444 else if (str[si] == '\\' && str[si + 1] == delimiter)
445 si++;
446 }
447 else
448#endif /* HANDLE_MULTIBYTE */
449 if (str[si] == '\\' && str[si + 1] == delimiter)
450 si++;
451
452 if (si > i || is_rhs)
453 {
454 s = (char *)xmalloc (si - i + 1);
455 for (j = 0, k = i; k < si; j++, k++)
456 {
457 /* Remove a backslash quoting the search string delimiter. */
458 if (str[k] == '\\' && str[k + 1] == delimiter)
459 k++;
460 s[j] = str[k];
461 }
462 s[j] = '\0';
463 if (lenptr)
464 *lenptr = j;
465 }
466
467 i = si;
468 if (str[i])
469 i++;
470 *iptr = i;
471
472 return s;
473}
474
475static void
476postproc_subst_rhs ()
477{
478 char *new;
479 int i, j, new_size;
480
481 new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len);
482 for (i = j = 0; i < subst_rhs_len; i++)
483 {
484 if (subst_rhs[i] == '&')
485 {
486 if (j + subst_lhs_len >= new_size)
487 new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
488 strlcpy (new + j, subst_lhs, new_size - j);
489 j += subst_lhs_len;
490 }
491 else
492 {
493 /* a single backslash protects the `&' from lhs interpolation */
494 if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
495 i++;
496 if (j >= new_size)
497 new = (char *)xrealloc (new, new_size *= 2);
498 new[j++] = subst_rhs[i];
499 }
500 }
501 new[j] = '\0';
502 free (subst_rhs);
503 subst_rhs = new;
504 subst_rhs_len = j;
505}
506
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. */
512static int
513history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
514 char *string;
515 int start, *end_index_ptr;
516 char **ret_string;
517 char *current_line; /* for !# */
518{
519 int i, n, starting_index;
520 int substitute_globally, want_quotes, print_only;
521 char *event, *temp, *result, *tstr, *t, c, *word_spec;
522 int result_len;
523#if defined (HANDLE_MULTIBYTE)
524 mbstate_t ps;
525
526 memset (&ps, 0, sizeof (mbstate_t));
527#endif
528
529 result = (char *)xmalloc (result_len = 128);
530
531 i = start;
532
533 /* If it is followed by something that starts a word specifier,
534 then !! is implied as the event specifier. */
535
536 if (member (string[i + 1], ":$*%^")((string[i + 1]) ? ((char *)strchr ((":$*%^"), (string[i + 1]
)) != (char *)((void *)0)) : 0)
)
537 {
538 char fake_s[3];
539 int fake_i = 0;
540 i++;
541 fake_s[0] = fake_s[1] = history_expansion_char;
542 fake_s[2] = '\0';
543 event = get_history_event (fake_s, &fake_i, 0);
544 }
545 else if (string[i + 1] == '#')
546 {
547 i += 2;
548 event = current_line;
549 }
550 else
551 {
552 int quoted_search_delimiter = 0;
553
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_MAX1 > 1 && rl_byte_oriented == 0)
560 {
561 int c, l;
562 l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY)(((i) == 0) ? (i) : ((i) - 1));
563 c = string[l];
564 /* XXX - original patch had i - 1 ??? If i == 0 it would fail. */
565 if (i && (c == '\'' || c == '"'))
566 quoted_search_delimiter = c;
567 }
568 else
569#endif /* HANDLE_MULTIBYTE */
570 if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
571 quoted_search_delimiter = string[i - 1];
572
573 event = get_history_event (string, &i, quoted_search_delimiter);
574 }
575
576 if (event == 0)
577 {
578 *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND0);
579 free (result);
580 return (-1);
581 }
582
583 /* If a word specifier is found, then do what that requires. */
584 starting_index = i;
585 word_spec = get_history_word_specifier (string, event, &i);
586
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,
589 we complain. */
590 if (word_spec == (char *)&error_pointer)
591 {
592 *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC1);
593 free (result);
594 return (-1);
595 }
596
597 /* If no word specifier, than the thing of interest was the event. */
598 temp = word_spec ? savestring (word_spec)xstrdup(word_spec) : savestring (event)xstrdup(event);
599 FREE (word_spec)if (word_spec) free (word_spec);
600
601 /* Perhaps there are other modifiers involved. Do what they say. */
602 want_quotes = substitute_globally = print_only = 0;
603 starting_index = i;
604
605 while (string[i] == ':')
606 {
607 c = string[i + 1];
608
609 if (c == 'g')
610 {
611 substitute_globally = 1;
612 i++;
613 c = string[i + 1];
614 }
615
616 switch (c)
617 {
618 default:
619 *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER3);
620 free (result);
621 free (temp);
622 return -1;
623
624 case 'q':
625 want_quotes = 'q';
626 break;
627
628 case 'x':
629 want_quotes = 'x';
630 break;
631
632 /* :p means make this the last executed line. So we
633 return an error state after adding this line to the
634 history. */
635 case 'p':
636 print_only++;
637 break;
638
639 /* :t discards all but the last part of the pathname. */
640 case 't':
641 tstr = strrchr (temp, '/');
642 if (tstr)
643 {
644 tstr++;
645 t = savestring (tstr)xstrdup(tstr);
646 free (temp);
647 temp = t;
648 }
649 break;
650
651 /* :h discards the last part of a pathname. */
652 case 'h':
653 tstr = strrchr (temp, '/');
654 if (tstr)
655 *tstr = '\0';
656 break;
657
658 /* :r discards the suffix. */
659 case 'r':
660 tstr = strrchr (temp, '.');
661 if (tstr)
662 *tstr = '\0';
663 break;
664
665 /* :e discards everything but the suffix. */
666 case 'e':
667 tstr = strrchr (temp, '.');
668 if (tstr)
669 {
670 t = savestring (tstr)xstrdup(tstr);
671 free (temp);
672 temp = t;
673 }
674 break;
675
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
680 globally. */
681
682 case '&':
683 case 's':
684 {
685 char *new_event;
686 int delimiter, failed, si, l_temp;
687
688 if (c == 's')
689 {
690 if (i + 2 < (int)strlen (string))
691 {
692#if defined (HANDLE_MULTIBYTE)
693 if (MB_CUR_MAX1 > 1 && rl_byte_oriented == 0)
694 {
695 _rl_adjust_point (string, i + 2, &ps);
696 if (_rl_get_char_len (string + i + 2, &ps) > 1)
697 delimiter = 0;
698 else
699 delimiter = string[i + 2];
700 }
701 else
702#endif /* HANDLE_MULTIBYTE */
703 delimiter = string[i + 2];
704 }
705 else
706 break; /* no search delimiter */
707
708 i += 3;
709
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. */
713 if (t)
714 {
715 FREE (subst_lhs)if (subst_lhs) free (subst_lhs);
716 subst_lhs = t;
717 }
718 else if (!subst_lhs)
719 {
720 if (search_string && *search_string)
721 {
722 subst_lhs = savestring (search_string)xstrdup(search_string);
723 subst_lhs_len = strlen (subst_lhs);
724 }
725 else
726 {
727 subst_lhs = (char *) NULL((void *)0);
728 subst_lhs_len = 0;
729 }
730 }
731
732 FREE (subst_rhs)if (subst_rhs) free (subst_rhs);
733 subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
734
735 /* If `&' appears in the rhs, it's supposed to be replaced
736 with the lhs. */
737 if (member ('&', subst_rhs)(('&') ? ((char *)strchr ((subst_rhs), ('&')) != (char
*)((void *)0)) : 0)
)
738 postproc_subst_rhs ();
739 }
740 else
741 i += 2;
742
743 /* If there is no lhs, the substitution can't succeed. */
744 if (subst_lhs_len == 0)
745 {
746 *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST4);
747 free (result);
748 free (temp);
749 return -1;
750 }
751
752 l_temp = strlen (temp);
753 /* Ignore impossible cases. */
754 if (subst_lhs_len > l_temp)
755 {
756 *ret_string = hist_error (string, starting_index, i, SUBST_FAILED2);
757 free (result);
758 free (temp);
759 return (-1);
760 }
761
762 /* Find the first occurrence of THIS in TEMP. */
763 si = 0;
764 for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
765 if (STREQN (temp+si, subst_lhs, subst_lhs_len)(((subst_lhs_len) == 0) ? (1) : ((temp+si)[0] == (subst_lhs)[
0]) && (strncmp ((temp+si), (subst_lhs), (subst_lhs_len
)) == 0))
)
766 {
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';
775 free (temp);
776 temp = new_event;
777
778 failed = 0;
779
780 if (substitute_globally)
781 {
782 si += subst_rhs_len;
783 l_temp = strlen (temp);
784 substitute_globally++;
785 continue;
786 }
787 else
788 break;
789 }
790
791 if (substitute_globally > 1)
792 {
793 substitute_globally = 0;
794 continue; /* don't want to increment i */
795 }
796
797 if (failed == 0)
798 continue; /* don't want to increment i */
799
800 *ret_string = hist_error (string, starting_index, i, SUBST_FAILED2);
801 free (result);
802 free (temp);
803 return (-1);
804 }
805 }
806 i += 2;
807 }
808 /* Done with modfiers. */
809 /* Believe it or not, we have to back the pointer up by one. */
810 --i;
811
812 if (want_quotes)
813 {
814 char *x;
815
816 if (want_quotes == 'q')
817 x = sh_single_quote (temp);
818 else if (want_quotes == 'x')
819 x = quote_breaks (temp);
820 else
821 x = savestring (temp)xstrdup(temp);
822
823 free (temp);
824 temp = x;
825 }
826
827 n = strlen (temp);
828 if (n >= result_len)
829 result = (char *)xrealloc (result, n + 2);
830 strlcpy (result, temp, n + 2);
831 free (temp);
832
833 *end_index_ptr = i;
834 *ret_string = result;
835 return (print_only);
836}
837
838/* Expand the string STRING, placing the result into OUTPUT, a pointer
839 to a string. Returns:
840
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
844 character)
845 1) If expansions did take place
846 2) If the `p' modifier was given and the caller should print the result
847
848 If an error ocurred in expansion, then OUTPUT contains a descriptive
849 error message. */
850
851#define ADD_STRING(s)do { int sl = strlen (s); j += sl; if (j >= result_len) { while
(j >= result_len) result_len += 128; result = (char *)xrealloc
(result, result_len); } strlcpy (result + j - sl, s, result_len
- j + sl); } while (0)
\
852 do \
853 { \
854 int sl = strlen (s); \
855 j += sl; \
856 if (j >= result_len) \
857 { \
858 while (j >= result_len) \
859 result_len += 128; \
860 result = (char *)xrealloc (result, result_len); \
861 } \
862 strlcpy (result + j - sl, s, result_len - j + sl); \
863 } \
864 while (0)
865
866#define ADD_CHAR(c)do { if (j >= result_len - 1) result = (char *)xrealloc (result
, result_len += 64); result[j++] = c; result[j] = '\0'; } while
(0)
\
867 do \
868 { \
869 if (j >= result_len - 1) \
870 result = (char *)xrealloc (result, result_len += 64); \
871 result[j++] = c; \
872 result[j] = '\0'; \
873 } \
874 while (0)
875
876int
877history_expand (hstring, output)
878 char *hstring;
879 char **output;
880{
881 register int j;
882 int i, r, l, passc, cc, modified, eindex, only_printing;
883 char *string;
884
885 /* The output string, and its length. */
886 int result_len;
887 char *result;
888
889#if defined (HANDLE_MULTIBYTE)
890 char mb[MB_LEN_MAX1];
891 mbstate_t ps;
892#endif
893
894 /* Used when adding the string. */
895 char *temp;
896
897 if (output == 0)
898 return 0;
899
900 /* Setting the history expansion character to 0 inhibits all
901 history expansion. */
902 if (history_expansion_char == 0)
903 {
904 *output = savestring (hstring)xstrdup(hstring);
905 return (0);
906 }
907
908 /* Prepare the buffer for printing error messages. */
909 result = (char *)xmalloc (result_len = 256);
910 result[0] = '\0';
911
912 only_printing = modified = 0;
913 l = strlen (hstring);
914
915 /* Grovel the string. Only backslash and single quotes can quote the
916 history escape character. We also handle arg specifiers. */
917
918 /* Before we grovel forever, see if the history_expansion_char appears
919 anywhere within the text. */
920
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)
925 {
926 string = (char *)xmalloc (l + 5);
927
928 string[0] = string[1] = history_expansion_char;
929 string[2] = ':';
930 string[3] = 's';
931 strlcpy (string + 4, hstring, l + 1);
932 l += 4;
933 }
934 else
935 {
936#if defined (HANDLE_MULTIBYTE)
937 memset (&ps, 0, sizeof (mbstate_t));
938#endif
939
940 string = hstring;
941 /* If not quick substitution, still maybe have to do expansion. */
942
943 /* `!' followed by one of the characters in history_no_expand_chars
944 is NOT an expansion. */
945 for (i = 0; string[i]; i++)
946 {
947#if defined (HANDLE_MULTIBYTE)
948 if (MB_CUR_MAX1 > 1 && rl_byte_oriented == 0)
949 {
950 int v;
951 v = _rl_get_char_len (string + i, &ps);
952 if (v > 1)
953 {
954 i += v - 1;
955 continue;
956 }
957 }
958#endif /* HANDLE_MULTIBYTE */
959
960 cc = string[i + 1];
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)((string[i - 1]) ? ((char *)strchr ((history_word_delimiters)
, (string[i - 1])) != (char *)((void *)0)) : 0)
))
967 {
968 while (string[i])
969 i++;
970 break;
971 }
972 else if (string[i] == history_expansion_char)
973 {
974 if (!cc || member (cc, history_no_expand_chars)((cc) ? ((char *)strchr ((history_no_expand_chars), (cc)) != (
char *)((void *)0)) : 0)
)
975 continue;
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
980 non-zero value. */
981 else if (history_inhibit_expansion_function &&
982 (*history_inhibit_expansion_function) (string, i))
983 continue;
984 else
985 break;
986 }
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] == '\'')
990 {
991 /* If this is bash, single quotes inhibit history expansion. */
992 i++;
993 hist_string_extract_single_quoted (string, &i);
994 }
995 else if (history_quotes_inhibit_expansion && string[i] == '\\')
996 {
997 /* If this is bash, allow backslashes to quote single
998 quotes and the history expansion character. */
999 if (cc == '\'' || cc == history_expansion_char)
1000 i++;
1001 }
1002 }
1003
1004 if (string[i] != history_expansion_char)
1005 {
1006 free (result);
1007 *output = savestring (string)xstrdup(string);
1008 return (0);
1009 }
1010 }
1011
1012 /* Extract and perform the substitution. */
1013 for (passc = i = j = 0; i < l; i++)
1014 {
1015 int tchar = string[i];
1016
1017 if (passc)
1018 {
1019 passc = 0;
1020 ADD_CHAR (tchar)do { if (j >= result_len - 1) result = (char *)xrealloc (result
, result_len += 64); result[j++] = tchar; result[j] = '\0'; }
while (0)
;
1021 continue;
1022 }
1023
1024#if defined (HANDLE_MULTIBYTE)
1025 if (MB_CUR_MAX1 > 1 && rl_byte_oriented == 0)
1026 {
1027 int k, c;
1028
1029 c = tchar;
1030 memset (mb, 0, sizeof (mb));
1031 for (k = 0; k < MB_LEN_MAX1; k++)
1032 {
1033 mb[k] = (char)c;
1034 memset (&ps, 0, sizeof (mbstate_t));
1035 if (_rl_get_char_len (mb, &ps) == -2)
1036 c = string[++i];
1037 else
1038 break;
1039 }
1040 if (strlen (mb) > 1)
1041 {
1042 ADD_STRING (mb)do { int sl = strlen (mb); j += sl; if (j >= result_len) {
while (j >= result_len) result_len += 128; result = (char
*)xrealloc (result, result_len); } strlcpy (result + j - sl,
mb, result_len - j + sl); } while (0)
;
1043 break;
1044 }
1045 }
1046#endif /* HANDLE_MULTIBYTE */
1047
1048 if (tchar == history_expansion_char)
1049 tchar = -3;
1050 else if (tchar == history_comment_char)
1051 tchar = -2;
1052
1053 switch (tchar)
1054 {
1055 default:
1056 ADD_CHAR (string[i])do { if (j >= result_len - 1) result = (char *)xrealloc (result
, result_len += 64); result[j++] = string[i]; result[j] = '\0'
; } while (0)
;
1057 break;
1058
1059 case '\\':
1060 passc++;
1061 ADD_CHAR (tchar)do { if (j >= result_len - 1) result = (char *)xrealloc (result
, result_len += 64); result[j++] = tchar; result[j] = '\0'; }
while (0)
;
1062 break;
1063
1064 case '\'':
1065 {
1066 /* If history_quotes_inhibit_expansion is set, single quotes
1067 inhibit history expansion. */
1068 if (history_quotes_inhibit_expansion)
1069 {
1070 int quote, slen;
1071
1072 quote = i++;
1073 hist_string_extract_single_quoted (string, &i);
1074
1075 slen = i - quote + 2;
1076 temp = (char *)xmalloc (slen);
1077 strncpy (temp, string + quote, slen);
1078 temp[slen - 1] = '\0';
1079 ADD_STRING (temp)do { int sl = strlen (temp); j += sl; if (j >= result_len)
{ while (j >= result_len) result_len += 128; result = (char
*)xrealloc (result, result_len); } strlcpy (result + j - sl,
temp, result_len - j + sl); } while (0)
;
1080 free (temp);
1081 }
1082 else
1083 ADD_CHAR (string[i])do { if (j >= result_len - 1) result = (char *)xrealloc (result
, result_len += 64); result[j++] = string[i]; result[j] = '\0'
; } while (0)
;
1084 break;
1085 }
1086
1087 case -2: /* history_comment_char */
1088 if (i == 0 || member (string[i - 1], history_word_delimiters)((string[i - 1]) ? ((char *)strchr ((history_word_delimiters)
, (string[i - 1])) != (char *)((void *)0)) : 0)
)
1089 {
1090 temp = (char *)xmalloc (l - i + 1);
1091 strlcpy (temp, string + i, l - i + 1);
1092 ADD_STRING (temp)do { int sl = strlen (temp); j += sl; if (j >= result_len)
{ while (j >= result_len) result_len += 128; result = (char
*)xrealloc (result, result_len); } strlcpy (result + j - sl,
temp, result_len - j + sl); } while (0)
;
1093 free (temp);
1094 i = l;
1095 }
1096 else
1097 ADD_CHAR (string[i])do { if (j >= result_len - 1) result = (char *)xrealloc (result
, result_len += 64); result[j++] = string[i]; result[j] = '\0'
; } while (0)
;
1098 break;
1099
1100 case -3: /* history_expansion_char */
1101 cc = string[i + 1];
1102
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)((cc) ? ((char *)strchr ((history_no_expand_chars), (cc)) != (
char *)((void *)0)) : 0)
)
1107 {
1108 ADD_CHAR (string[i])do { if (j >= result_len - 1) result = (char *)xrealloc (result
, result_len += 64); result[j++] = string[i]; result[j] = '\0'
; } while (0)
;
1109 break;
1110 }
1111
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. */
1118 if (cc == '#')
1119 {
1120 if (result)
1121 {
1122 temp = (char *)xmalloc (1 + strlen (result));
1123 strlcpy (temp, result, 1 + strlen(result));
1124 ADD_STRING (temp)do { int sl = strlen (temp); j += sl; if (j >= result_len)
{ while (j >= result_len) result_len += 128; result = (char
*)xrealloc (result, result_len); } strlcpy (result + j - sl,
temp, result_len - j + sl); } while (0)
;
1125 free (temp);
1126 }
1127 i++;
1128 break;
1129 }
1130#endif
1131
1132 r = history_expand_internal (string, i, &eindex, &temp, result);
1133 if (r < 0)
1134 {
1135 *output = temp;
1136 free (result);
1137 if (string != hstring)
1138 free (string);
1139 return -1;
1140 }
1141 else
1142 {
1143 if (temp)
1144 {
1145 modified++;
1146 if (*temp)
1147 ADD_STRING (temp)do { int sl = strlen (temp); j += sl; if (j >= result_len)
{ while (j >= result_len) result_len += 128; result = (char
*)xrealloc (result, result_len); } strlcpy (result + j - sl,
temp, result_len - j + sl); } while (0)
;
1148 free (temp);
1149 }
1150 only_printing = r == 1;
1151 i = eindex;
1152 }
1153 break;
1154 }
1155 }
1156
1157 *output = result;
1158 if (string != hstring)
1159 free (string);
1160
1161 if (only_printing)
1162 {
1163 add_history (result);
1164 return (2);
1165 }
1166
1167 return (modified != 0);
1168}
1169
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. */
1175static char *
1176get_history_word_specifier (spec, from, caller_index)
1177 char *spec, *from;
1178 int *caller_index;
1179{
1180 register int i = *caller_index;
1181 int first, last;
1182 int expecting_word_spec = 0;
1183 char *result;
1184
1185 /* The range of words to return doesn't exist yet. */
1186 first = last = 0;
Value stored to 'first' is never read
1187 result = (char *)NULL((void *)0);
1188
1189 /* If we found a colon, then this *must* be a word specification. If
1190 it isn't, then it is an error. */
1191 if (spec[i] == ':')
1192 {
1193 i++;
1194 expecting_word_spec++;
1195 }
1196
1197 /* Handle special cases first. */
1198
1199 /* `%' is the word last searched for. */
1200 if (spec[i] == '%')
1201 {
1202 *caller_index = i + 1;
1203 return (search_match ? savestring (search_match)xstrdup(search_match) : savestring ("")xstrdup(""));
1204 }
1205
1206 /* `*' matches all of the arguments, but not the command. */
1207 if (spec[i] == '*')
1208 {
1209 *caller_index = i + 1;
1210 result = history_arg_extract (1, '$', from);
1211 return (result ? result : savestring ("")xstrdup(""));
1212 }
1213
1214 /* `$' is last arg. */
1215 if (spec[i] == '$')
1216 {
1217 *caller_index = i + 1;
1218 return (history_arg_extract ('$', '$', from));
1219 }
1220
1221 /* Try to get FIRST and LAST figured out. */
1222
1223 if (spec[i] == '-')
1224 first = 0;
1225 else if (spec[i] == '^')
1226 first = 1;
1227 else if (_rl_digit_p (spec[i])((spec[i]) >= '0' && (spec[i]) <= '9') && expecting_word_spec)
1228 {
1229 for (first = 0; _rl_digit_p (spec[i])((spec[i]) >= '0' && (spec[i]) <= '9'); i++)
1230 first = (first * 10) + _rl_digit_value (spec[i])((spec[i]) - '0');
1231 }
1232 else
1233 return ((char *)NULL((void *)0)); /* no valid `first' for word specifier */
1234
1235 if (spec[i] == '^' || spec[i] == '*')
1236 {
1237 last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */
1238 i++;
1239 }
1240 else if (spec[i] != '-')
1241 last = first;
1242 else
1243 {
1244 i++;
1245
1246 if (_rl_digit_p (spec[i])((spec[i]) >= '0' && (spec[i]) <= '9'))
1247 {
1248 for (last = 0; _rl_digit_p (spec[i])((spec[i]) >= '0' && (spec[i]) <= '9'); i++)
1249 last = (last * 10) + _rl_digit_value (spec[i])((spec[i]) - '0');
1250 }
1251 else if (spec[i] == '$')
1252 {
1253 i++;
1254 last = '$';
1255 }
1256#if 0
1257 else if (!spec[i] || spec[i] == ':')
1258 /* check against `:' because there could be a modifier separator */
1259#else
1260 else
1261 /* csh seems to allow anything to terminate the word spec here,
1262 leaving it as an abbreviation. */
1263#endif
1264 last = -1; /* x- abbreviates x-$ omitting word `$' */
1265 }
1266
1267 *caller_index = i;
1268
1269 if (last >= first || last == '$' || last < 0)
1270 result = history_arg_extract (first, last, from);
1271
1272 return (result ? result : (char *)&error_pointer);
1273}
1274
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. */
1280char *
1281history_arg_extract (first, last, string)
1282 int first, last;
1283 const char *string;
1284{
1285 register int i, len;
1286 char *result;
1287 int size, offset;
1288 char **list;
1289
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((void *)0))
1294 return ((char *)NULL((void *)0));
1295
1296 for (len = 0; list[len]; len++)
1297 ;
1298
1299 if (last < 0)
1300 last = len + last - 1;
1301
1302 if (first < 0)
1303 first = len + first - 1;
1304
1305 if (last == '$')
1306 last = len - 1;
1307
1308 if (first == '$')
1309 first = len - 1;
1310
1311 last++;
1312
1313 if (first >= len || last > len || first < 0 || last < 0 || first > last)
1314 result = ((char *)NULL((void *)0));
1315 else
1316 {
1317 for (size = 0, i = first; i < last; i++)
1318 size += strlen (list[i]) + 1;
1319 result = (char *)xmalloc (size + 1);
1320 result[0] = '\0';
1321
1322 for (i = first, offset = 0; i < last; i++)
1323 {
1324 strlcpy (result + offset, list[i], size + 1 - offset);
1325 offset += strlen (list[i]);
1326 if (i + 1 < last)
1327 {
1328 result[offset++] = ' ';
1329 result[offset] = 0;
1330 }
1331 }
1332 }
1333
1334 for (i = 0; i < len; i++)
1335 free (list[i]);
1336 free (list);
1337
1338 return (result);
1339}
1340
1341#define slashify_in_quotes"\\`\"$" "\\`\"$"
1342
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
1346 *INDP. */
1347static char **
1348history_tokenize_internal (string, wind, indp)
1349 const char *string;
1350 int wind, *indp;
1351{
1352 char **result;
1353 register int i, start, result_index, size;
1354 int len, delimiter;
1355
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)
1359 *indp = -1;
1360
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((void *)0); string[i]; )
1364 {
1365 delimiter = 0;
1366
1367 /* Skip leading whitespace. */
1368 for (; string[i] && whitespace (string[i])(((string[i]) == ' ') || ((string[i]) == '\t')); i++)
1369 ;
1370 if (string[i] == 0 || string[i] == history_comment_char)
1371 return (result);
1372
1373 start = i;
1374
1375 if (member (string[i], "()\n")((string[i]) ? ((char *)strchr (("()\n"), (string[i])) != (char
*)((void *)0)) : 0)
)
1376 {
1377 i++;
1378 goto got_token;
1379 }
1380
1381 if (member (string[i], "<>;&|$")((string[i]) ? ((char *)strchr (("<>;&|$"), (string
[i])) != (char *)((void *)0)) : 0)
)
1382 {
1383 int peek = string[i + 1];
1384
1385 if (peek == string[i] && peek != '$')
1386 {
1387 if (peek == '<' && string[i + 2] == '-')
1388 i++;
1389 i += 2;
1390 goto got_token;
1391 }
1392 else
1393 {
1394 if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
1395 ((peek == '>') && (string[i] == '&')) ||
1396 ((peek == '(') && (string[i] == '$')))
1397 {
1398 i += 2;
1399 goto got_token;
1400 }
1401 }
1402 if (string[i] != '$')
1403 {
1404 i++;
1405 goto got_token;
1406 }
1407 }
1408
1409 /* Get word from string + i; */
1410
1411 if (member (string[i], HISTORY_QUOTE_CHARACTERS)((string[i]) ? ((char *)strchr (("\"'`"), (string[i])) != (char
*)((void *)0)) : 0)
)
1412 delimiter = string[i++];
1413
1414 for (; string[i]; i++)
1415 {
1416 if (string[i] == '\\' && string[i + 1] == '\n')
1417 {
1418 i++;
1419 continue;
1420 }
1421
1422 if (string[i] == '\\' && delimiter != '\'' &&
1423 (delimiter != '"' || member (string[i], slashify_in_quotes)((string[i]) ? ((char *)strchr (("\\`\"$"), (string[i])) != (
char *)((void *)0)) : 0)
))
1424 {
1425 i++;
1426 continue;
1427 }
1428
1429 if (delimiter && string[i] == delimiter)
1430 {
1431 delimiter = 0;
1432 continue;
1433 }
1434
1435 if (!delimiter && (member (string[i], history_word_delimiters)((string[i]) ? ((char *)strchr ((history_word_delimiters), (string
[i])) != (char *)((void *)0)) : 0)
))
1436 break;
1437
1438 if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS)((string[i]) ? ((char *)strchr (("\"'`"), (string[i])) != (char
*)((void *)0)) : 0)
)
1439 delimiter = string[i];
1440 }
1441
1442 got_token:
1443
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;
1448
1449 len = i - start;
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((void *)0);
1456 }
1457
1458 return (result);
1459}
1460
1461/* Return an array of tokens, much as the shell might. The tokens are
1462 parsed out of STRING. */
1463char **
1464history_tokenize (string)
1465 const char *string;
1466{
1467 return (history_tokenize_internal (string, -1, (int *)NULL((void *)0)));
1468}
1469
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. */
1473static char *
1474history_find_word (line, ind)
1475 char *line;
1476 int ind;
1477{
1478 char **words, *s;
1479 int i, wind;
1480
1481 words = history_tokenize_internal (line, ind, &wind);
1482 if (wind == -1 || words == 0)
1483 return ((char *)NULL((void *)0));
1484 s = words[wind];
1485 for (i = 0; i < wind; i++)
1486 free (words[i]);
1487 for (i = wind + 1; words[i]; i++)
1488 free (words[i]);
1489 free (words);
1490 return s;
1491}