]> git.ipfire.org Git - thirdparty/bash.git/blame - lib/readline/histexpand.c
Imported from ../bash-2.01.1.tar.gz.
[thirdparty/bash.git] / lib / readline / histexpand.c
CommitLineData
ccc6cda3
JA
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 1, 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 675 Mass Ave, Cambridge, MA 02139, USA. */
22
23#define READLINE_LIBRARY
24
25#if defined (HAVE_CONFIG_H)
26# include <config.h>
27#endif
28
29#include <stdio.h>
30
31#if defined (HAVE_STDLIB_H)
32# include <stdlib.h>
33#else
34# include "ansi_stdlib.h"
35#endif /* HAVE_STDLIB_H */
36
37#if defined (HAVE_UNISTD_H)
38# include <unistd.h>
39#endif
40
41#if defined (HAVE_STRING_H)
42# include <string.h>
43#else
44# include <strings.h>
45#endif /* !HAVE_STRING_H */
46
47#include "history.h"
48#include "histlib.h"
49
50static char error_pointer;
51
52static char *subst_lhs;
53static char *subst_rhs;
54static int subst_lhs_len;
55static int subst_rhs_len;
56
57static char *get_history_word_specifier ();
58static char *history_find_word ();
59
60extern int history_offset;
61
ccc6cda3 62extern char *single_quote ();
ccc6cda3
JA
63static char *quote_breaks ();
64
65extern char *xmalloc (), *xrealloc ();
66
67/* Variables exported by this file. */
68/* The character that represents the start of a history expansion
69 request. This is usually `!'. */
70char history_expansion_char = '!';
71
72/* The character that invokes word substitution if found at the start of
73 a line. This is usually `^'. */
74char history_subst_char = '^';
75
76/* During tokenization, if this character is seen as the first character
77 of a word, then it, and all subsequent characters upto a newline are
78 ignored. For a Bourne shell, this should be '#'. Bash special cases
79 the interactive comment character to not be a comment delimiter. */
80char history_comment_char = '\0';
81
82/* The list of characters which inhibit the expansion of text if found
83 immediately following history_expansion_char. */
84char *history_no_expand_chars = " \t\n\r=";
85
86/* If set to a non-zero value, single quotes inhibit history expansion.
87 The default is 0. */
88int history_quotes_inhibit_expansion = 0;
89
d166f048
JA
90/* If set, this points to a function that is called to verify that a
91 particular history expansion should be performed. */
92Function *history_inhibit_expansion_function;
93
ccc6cda3
JA
94/* **************************************************************** */
95/* */
96/* History Expansion */
97/* */
98/* **************************************************************** */
99
100/* Hairy history expansion on text, not tokens. This is of general
101 use, and thus belongs in this library. */
102
103/* The last string searched for by a !?string? search. */
104static char *search_string;
105
106/* The last string matched by a !?string? search. */
107static char *search_match;
108
109/* Return the event specified at TEXT + OFFSET modifying OFFSET to
110 point to after the event specifier. Just a pointer to the history
111 line is returned; NULL is returned in the event of a bad specifier.
112 You pass STRING with *INDEX equal to the history_expansion_char that
113 begins this specification.
114 DELIMITING_QUOTE is a character that is allowed to end the string
115 specification for what to search for in addition to the normal
116 characters `:', ` ', `\t', `\n', and sometimes `?'.
117 So you might call this function like:
118 line = get_history_event ("!echo:p", &index, 0); */
119char *
120get_history_event (string, caller_index, delimiting_quote)
121 char *string;
122 int *caller_index;
123 int delimiting_quote;
124{
125 register int i;
126 register char c;
127 HIST_ENTRY *entry;
128 int which, sign, local_index, substring_okay;
129 Function *search_func;
130 char *temp;
131
132 /* The event can be specified in a number of ways.
133
134 !! the previous command
135 !n command line N
136 !-n current command-line minus N
137 !str the most recent command starting with STR
138 !?str[?]
139 the most recent command containing STR
140
141 All values N are determined via HISTORY_BASE. */
142
143 i = *caller_index;
144
145 if (string[i] != history_expansion_char)
146 return ((char *)NULL);
147
148 /* Move on to the specification. */
149 i++;
150
151 sign = 1;
152 substring_okay = 0;
153
154#define RETURN_ENTRY(e, w) \
155 return ((e = history_get (w)) ? e->line : (char *)NULL)
156
157 /* Handle !! case. */
158 if (string[i] == history_expansion_char)
159 {
160 i++;
161 which = history_base + (history_length - 1);
162 *caller_index = i;
163 RETURN_ENTRY (entry, which);
164 }
165
166 /* Hack case of numeric line specification. */
167 if (string[i] == '-')
168 {
169 sign = -1;
170 i++;
171 }
172
173 if (_rl_digit_p (string[i]))
174 {
175 /* Get the extent of the digits and compute the value. */
176 for (which = 0; _rl_digit_p (string[i]); i++)
177 which = (which * 10) + _rl_digit_value (string[i]);
178
179 *caller_index = i;
180
181 if (sign < 0)
182 which = (history_length + history_base) - which;
183
184 RETURN_ENTRY (entry, which);
185 }
186
187 /* This must be something to search for. If the spec begins with
188 a '?', then the string may be anywhere on the line. Otherwise,
189 the string must be found at the start of a line. */
190 if (string[i] == '?')
191 {
192 substring_okay++;
193 i++;
194 }
195
196 /* Only a closing `?' or a newline delimit a substring search string. */
197 for (local_index = i; c = string[i]; i++)
198 if ((!substring_okay && (whitespace (c) || c == ':' ||
199 (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
200 string[i] == delimiting_quote)) ||
201 string[i] == '\n' ||
202 (substring_okay && string[i] == '?'))
203 break;
204
205 which = i - local_index;
206 temp = xmalloc (1 + which);
207 if (which)
208 strncpy (temp, string + local_index, which);
209 temp[which] = '\0';
210
211 if (substring_okay && string[i] == '?')
212 i++;
213
214 *caller_index = i;
215
216#define FAIL_SEARCH() \
217 do { \
218 history_offset = history_length; free (temp) ; return (char *)NULL; \
219 } while (0)
220
221 /* If there is no search string, try to use the previous search string,
222 if one exists. If not, fail immediately. */
223 if (*temp == '\0' && substring_okay)
224 {
225 if (search_string)
226 {
227 free (temp);
228 temp = savestring (search_string);
229 }
230 else
231 FAIL_SEARCH ();
232 }
233
234 search_func = substring_okay ? history_search : history_search_prefix;
235 while (1)
236 {
237 local_index = (*search_func) (temp, -1);
238
239 if (local_index < 0)
240 FAIL_SEARCH ();
241
242 if (local_index == 0 || substring_okay)
243 {
244 entry = current_history ();
245 history_offset = history_length;
246
247 /* If this was a substring search, then remember the
248 string that we matched for word substitution. */
249 if (substring_okay)
250 {
251 FREE (search_string);
252 search_string = temp;
253
254 FREE (search_match);
255 search_match = history_find_word (entry->line, local_index);
256 }
257 else
258 free (temp);
259
260 return (entry->line);
261 }
262
263 if (history_offset)
264 history_offset--;
265 else
266 FAIL_SEARCH ();
267 }
268#undef FAIL_SEARCH
269#undef RETURN_ENTRY
270}
271
272/* Function for extracting single-quoted strings. Used for inhibiting
273 history expansion within single quotes. */
274
275/* Extract the contents of STRING as if it is enclosed in single quotes.
276 SINDEX, when passed in, is the offset of the character immediately
277 following the opening single quote; on exit, SINDEX is left pointing
278 to the closing single quote. */
279static void
280hist_string_extract_single_quoted (string, sindex)
281 char *string;
282 int *sindex;
283{
284 register int i;
285
286 for (i = *sindex; string[i] && string[i] != '\''; i++)
287 ;
288
289 *sindex = i;
290}
291
ccc6cda3
JA
292static char *
293quote_breaks (s)
294 char *s;
295{
296 register char *p, *r;
297 char *ret;
298 int len = 3;
299
300 for (p = s; p && *p; p++, len++)
301 {
302 if (*p == '\'')
303 len += 3;
304 else if (whitespace (*p) || *p == '\n')
305 len += 2;
306 }
307
308 r = ret = xmalloc (len);
309 *r++ = '\'';
310 for (p = s; p && *p; )
311 {
312 if (*p == '\'')
313 {
314 *r++ = '\'';
315 *r++ = '\\';
316 *r++ = '\'';
317 *r++ = '\'';
318 p++;
319 }
320 else if (whitespace (*p) || *p == '\n')
321 {
322 *r++ = '\'';
323 *r++ = *p++;
324 *r++ = '\'';
325 }
326 else
327 *r++ = *p++;
328 }
329 *r++ = '\'';
330 *r = '\0';
331 return ret;
332}
333
334static char *
335hist_error(s, start, current, errtype)
336 char *s;
337 int start, current, errtype;
338{
339 char *temp, *emsg;
340 int ll, elen;
341
342 ll = current - start;
343
344 switch (errtype)
345 {
346 case EVENT_NOT_FOUND:
347 emsg = "event not found";
348 elen = 15;
349 break;
350 case BAD_WORD_SPEC:
351 emsg = "bad word specifier";
352 elen = 18;
353 break;
354 case SUBST_FAILED:
355 emsg = "substitution failed";
356 elen = 19;
357 break;
358 case BAD_MODIFIER:
359 emsg = "unrecognized history modifier";
360 elen = 29;
361 break;
362 default:
363 emsg = "unknown expansion error";
364 elen = 23;
365 break;
366 }
367
368 temp = xmalloc (ll + elen + 3);
369 strncpy (temp, s + start, ll);
370 temp[ll] = ':';
371 temp[ll + 1] = ' ';
372 strcpy (temp + ll + 2, emsg);
373 return (temp);
374}
375
376/* Get a history substitution string from STR starting at *IPTR
377 and return it. The length is returned in LENPTR.
378
379 A backslash can quote the delimiter. If the string is the
380 empty string, the previous pattern is used. If there is
381 no previous pattern for the lhs, the last history search
382 string is used.
383
384 If IS_RHS is 1, we ignore empty strings and set the pattern
385 to "" anyway. subst_lhs is not changed if the lhs is empty;
386 subst_rhs is allowed to be set to the empty string. */
387
388static char *
389get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
390 char *str;
391 int *iptr, delimiter, is_rhs, *lenptr;
392{
393 register int si, i, j, k;
394 char *s = (char *) NULL;
395
396 i = *iptr;
397
398 for (si = i; str[si] && str[si] != delimiter; si++)
399 if (str[si] == '\\' && str[si + 1] == delimiter)
400 si++;
401
402 if (si > i || is_rhs)
403 {
404 s = xmalloc (si - i + 1);
405 for (j = 0, k = i; k < si; j++, k++)
406 {
407 /* Remove a backslash quoting the search string delimiter. */
408 if (str[k] == '\\' && str[k + 1] == delimiter)
409 k++;
410 s[j] = str[k];
411 }
412 s[j] = '\0';
413 if (lenptr)
414 *lenptr = j;
415 }
416
417 i = si;
418 if (str[i])
419 i++;
420 *iptr = i;
421
422 return s;
423}
424
425static void
426postproc_subst_rhs ()
427{
428 char *new;
429 int i, j, new_size;
430
431 new = xmalloc (new_size = subst_rhs_len + subst_lhs_len);
432 for (i = j = 0; i < subst_rhs_len; i++)
433 {
434 if (subst_rhs[i] == '&')
435 {
436 if (j + subst_lhs_len >= new_size)
437 new = xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
438 strcpy (new + j, subst_lhs);
439 j += subst_lhs_len;
440 }
441 else
442 {
443 /* a single backslash protects the `&' from lhs interpolation */
444 if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
445 i++;
446 if (j >= new_size)
447 new = xrealloc (new, new_size *= 2);
448 new[j++] = subst_rhs[i];
449 }
450 }
451 new[j] = '\0';
452 free (subst_rhs);
453 subst_rhs = new;
454 subst_rhs_len = j;
455}
456
457/* Expand the bulk of a history specifier starting at STRING[START].
458 Returns 0 if everything is OK, -1 if an error occurred, and 1
459 if the `p' modifier was supplied and the caller should just print
460 the returned string. Returns the new index into string in
461 *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
462static int
463history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
464 char *string;
465 int start, *end_index_ptr;
466 char **ret_string;
467 char *current_line; /* for !# */
468{
469 int i, n, starting_index;
470 int substitute_globally, want_quotes, print_only;
471 char *event, *temp, *result, *tstr, *t, c, *word_spec;
472 int result_len;
473
474 result = xmalloc (result_len = 128);
475
476 i = start;
477
478 /* If it is followed by something that starts a word specifier,
479 then !! is implied as the event specifier. */
480
481 if (member (string[i + 1], ":$*%^"))
482 {
483 char fake_s[3];
484 int fake_i = 0;
485 i++;
486 fake_s[0] = fake_s[1] = history_expansion_char;
487 fake_s[2] = '\0';
488 event = get_history_event (fake_s, &fake_i, 0);
489 }
490 else if (string[i + 1] == '#')
491 {
492 i += 2;
493 event = current_line;
494 }
495 else
496 {
497 int quoted_search_delimiter = 0;
498
499 /* If the character before this `!' is a double or single
500 quote, then this expansion takes place inside of the
501 quoted string. If we have to search for some text ("!foo"),
502 allow the delimiter to end the search string. */
503 if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
504 quoted_search_delimiter = string[i - 1];
505 event = get_history_event (string, &i, quoted_search_delimiter);
506 }
507
508 if (event == 0)
509 {
510 *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
511 free (result);
512 return (-1);
513 }
514
515 /* If a word specifier is found, then do what that requires. */
516 starting_index = i;
517 word_spec = get_history_word_specifier (string, event, &i);
518
519 /* There is no such thing as a `malformed word specifier'. However,
520 it is possible for a specifier that has no match. In that case,
521 we complain. */
522 if (word_spec == (char *)&error_pointer)
523 {
524 *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
525 free (result);
526 return (-1);
527 }
528
529 /* If no word specifier, than the thing of interest was the event. */
530 temp = word_spec ? savestring (word_spec) : savestring (event);
531 FREE (word_spec);
532
533 /* Perhaps there are other modifiers involved. Do what they say. */
534 want_quotes = substitute_globally = print_only = 0;
535 starting_index = i;
536
537 while (string[i] == ':')
538 {
539 c = string[i + 1];
540
541 if (c == 'g')
542 {
543 substitute_globally = 1;
544 i++;
545 c = string[i + 1];
546 }
547
548 switch (c)
549 {
550 default:
551 *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
552 free (result);
553 free (temp);
554 return -1;
555
556 case 'q':
557 want_quotes = 'q';
558 break;
559
560 case 'x':
561 want_quotes = 'x';
562 break;
563
564 /* :p means make this the last executed line. So we
565 return an error state after adding this line to the
566 history. */
567 case 'p':
568 print_only++;
569 break;
570
571 /* :t discards all but the last part of the pathname. */
572 case 't':
573 tstr = strrchr (temp, '/');
574 if (tstr)
575 {
576 tstr++;
577 t = savestring (tstr);
578 free (temp);
579 temp = t;
580 }
581 break;
582
583 /* :h discards the last part of a pathname. */
584 case 'h':
585 tstr = strrchr (temp, '/');
586 if (tstr)
587 *tstr = '\0';
588 break;
589
590 /* :r discards the suffix. */
591 case 'r':
592 tstr = strrchr (temp, '.');
593 if (tstr)
594 *tstr = '\0';
595 break;
596
597 /* :e discards everything but the suffix. */
598 case 'e':
599 tstr = strrchr (temp, '.');
600 if (tstr)
601 {
602 t = savestring (tstr);
603 free (temp);
604 temp = t;
605 }
606 break;
607
608 /* :s/this/that substitutes `that' for the first
609 occurrence of `this'. :gs/this/that substitutes `that'
610 for each occurrence of `this'. :& repeats the last
611 substitution. :g& repeats the last substitution
612 globally. */
613
614 case '&':
615 case 's':
616 {
617 char *new_event, *t;
618 int delimiter, failed, si, l_temp;
619
620 if (c == 's')
621 {
622 if (i + 2 < (int)strlen (string))
623 delimiter = string[i + 2];
624 else
625 break; /* no search delimiter */
626
627 i += 3;
628
629 t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
630 /* An empty substitution lhs with no previous substitution
631 uses the last search string as the lhs. */
632 if (t)
633 {
634 FREE (subst_lhs);
635 subst_lhs = t;
636 }
637 else if (!subst_lhs)
638 {
639 if (search_string && *search_string)
640 {
641 subst_lhs = savestring (search_string);
642 subst_lhs_len = strlen (subst_lhs);
643 }
644 else
645 {
646 subst_lhs = (char *) NULL;
647 subst_lhs_len = 0;
648 }
649 }
650
651 /* If there is no lhs, the substitution can't succeed. */
652 if (subst_lhs_len == 0)
653 {
654 *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
655 free (result);
656 free (temp);
657 return -1;
658 }
659
660 FREE (subst_rhs);
661 subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
662
663 /* If `&' appears in the rhs, it's supposed to be replaced
664 with the lhs. */
665 if (member ('&', subst_rhs))
666 postproc_subst_rhs ();
667 }
668 else
669 i += 2;
670
671 l_temp = strlen (temp);
672 /* Ignore impossible cases. */
673 if (subst_lhs_len > l_temp)
674 {
675 *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
676 free (result);
677 free (temp);
678 return (-1);
679 }
680
681 /* Find the first occurrence of THIS in TEMP. */
682 si = 0;
683 for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
684 if (STREQN (temp+si, subst_lhs, subst_lhs_len))
685 {
686 int len = subst_rhs_len - subst_lhs_len + l_temp;
687 new_event = xmalloc (1 + len);
688 strncpy (new_event, temp, si);
689 strncpy (new_event + si, subst_rhs, subst_rhs_len);
690 strncpy (new_event + si + subst_rhs_len,
691 temp + si + subst_lhs_len,
692 l_temp - (si + subst_lhs_len));
693 new_event[len] = '\0';
694 free (temp);
695 temp = new_event;
696
697 failed = 0;
698
699 if (substitute_globally)
700 {
701 si += subst_rhs_len;
702 l_temp = strlen (temp);
703 substitute_globally++;
704 continue;
705 }
706 else
707 break;
708 }
709
710 if (substitute_globally > 1)
711 {
712 substitute_globally = 0;
713 continue; /* don't want to increment i */
714 }
715
716 if (failed == 0)
717 continue; /* don't want to increment i */
718
719 *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
720 free (result);
721 free (temp);
722 return (-1);
723 }
724 }
725 i += 2;
726 }
727 /* Done with modfiers. */
728 /* Believe it or not, we have to back the pointer up by one. */
729 --i;
730
731 if (want_quotes)
732 {
733 char *x;
734
735 if (want_quotes == 'q')
736 x = single_quote (temp);
737 else if (want_quotes == 'x')
738 x = quote_breaks (temp);
739 else
740 x = savestring (temp);
741
742 free (temp);
743 temp = x;
744 }
745
746 n = strlen (temp);
747 if (n >= result_len)
748 result = xrealloc (result, n + 2);
749 strcpy (result, temp);
750 free (temp);
751
752 *end_index_ptr = i;
753 *ret_string = result;
754 return (print_only);
755}
756
757/* Expand the string STRING, placing the result into OUTPUT, a pointer
758 to a string. Returns:
759
760 -1) If there was an error in expansion.
761 0) If no expansions took place (or, if the only change in
762 the text was the de-slashifying of the history expansion
763 character)
764 1) If expansions did take place
765 2) If the `p' modifier was given and the caller should print the result
766
767 If an error ocurred in expansion, then OUTPUT contains a descriptive
768 error message. */
769
770#define ADD_STRING(s) \
771 do \
772 { \
773 int sl = strlen (s); \
774 j += sl; \
775 if (j >= result_len) \
776 { \
777 while (j >= result_len) \
778 result_len += 128; \
779 result = xrealloc (result, result_len); \
780 } \
781 strcpy (result + j - sl, s); \
782 } \
783 while (0)
784
785#define ADD_CHAR(c) \
786 do \
787 { \
788 if (j >= result_len - 1) \
789 result = xrealloc (result, result_len += 64); \
790 result[j++] = c; \
791 result[j] = '\0'; \
792 } \
793 while (0)
794
795int
796history_expand (hstring, output)
797 char *hstring;
798 char **output;
799{
800 register int j;
801 int i, r, l, passc, cc, modified, eindex, only_printing;
802 char *string;
803
804 /* The output string, and its length. */
805 int result_len;
806 char *result;
807
808 /* Used when adding the string. */
809 char *temp;
810
811 /* Setting the history expansion character to 0 inhibits all
812 history expansion. */
813 if (history_expansion_char == 0)
814 {
815 *output = savestring (hstring);
816 return (0);
817 }
818
819 /* Prepare the buffer for printing error messages. */
820 result = xmalloc (result_len = 256);
821 result[0] = '\0';
822
823 only_printing = modified = 0;
824 l = strlen (hstring);
825
826 /* Grovel the string. Only backslash can quote the history escape
827 character. We also handle arg specifiers. */
828
829 /* Before we grovel forever, see if the history_expansion_char appears
830 anywhere within the text. */
831
832 /* The quick substitution character is a history expansion all right. That
833 is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
834 that is the substitution that we do. */
835 if (hstring[0] == history_subst_char)
836 {
837 string = xmalloc (l + 5);
838
839 string[0] = string[1] = history_expansion_char;
840 string[2] = ':';
841 string[3] = 's';
842 strcpy (string + 4, hstring);
843 l += 4;
844 }
845 else
846 {
847 string = hstring;
848 /* If not quick substitution, still maybe have to do expansion. */
849
850 /* `!' followed by one of the characters in history_no_expand_chars
851 is NOT an expansion. */
852 for (i = 0; string[i]; i++)
853 {
854 cc = string[i + 1];
855 if (string[i] == history_expansion_char)
856 {
857 if (!cc || member (cc, history_no_expand_chars))
858 continue;
d166f048
JA
859 /* If the calling application has set
860 history_inhibit_expansion_function to a function that checks
861 for special cases that should not be history expanded,
862 call the function and skip the expansion if it returns a
863 non-zero value. */
864 else if (history_inhibit_expansion_function &&
865 (*history_inhibit_expansion_function) (string, i))
ccc6cda3 866 continue;
ccc6cda3
JA
867 else
868 break;
869 }
870 else if (history_quotes_inhibit_expansion && string[i] == '\'')
871 {
872 /* If this is bash, single quotes inhibit history expansion. */
873 i++;
874 hist_string_extract_single_quoted (string, &i);
875 }
876 else if (history_quotes_inhibit_expansion && string[i] == '\\')
877 {
878 /* If this is bash, allow backslashes to quote single
879 quotes and the history expansion character. */
880 if (cc == '\'' || cc == history_expansion_char)
881 i++;
882 }
883 }
884
885 if (string[i] != history_expansion_char)
886 {
887 free (result);
888 *output = savestring (string);
889 return (0);
890 }
891 }
892
893 /* Extract and perform the substitution. */
894 for (passc = i = j = 0; i < l; i++)
895 {
896 int tchar = string[i];
897
898 if (passc)
899 {
900 passc = 0;
901 ADD_CHAR (tchar);
902 continue;
903 }
904
905 if (tchar == history_expansion_char)
906 tchar = -3;
907
908 switch (tchar)
909 {
910 default:
911 ADD_CHAR (string[i]);
912 break;
913
914 case '\\':
915 passc++;
916 ADD_CHAR (tchar);
917 break;
918
919 case '\'':
920 {
921 /* If history_quotes_inhibit_expansion is set, single quotes
922 inhibit history expansion. */
923 if (history_quotes_inhibit_expansion)
924 {
925 int quote, slen;
926
927 quote = i++;
928 hist_string_extract_single_quoted (string, &i);
929
930 slen = i - quote + 2;
931 temp = xmalloc (slen);
932 strncpy (temp, string + quote, slen);
933 temp[slen - 1] = '\0';
934 ADD_STRING (temp);
935 free (temp);
936 }
937 else
938 ADD_CHAR (string[i]);
939 break;
940 }
941
942 case -3: /* history_expansion_char */
943 cc = string[i + 1];
944
945 /* If the history_expansion_char is followed by one of the
946 characters in history_no_expand_chars, then it is not a
947 candidate for expansion of any kind. */
948 if (member (cc, history_no_expand_chars))
949 {
950 ADD_CHAR (string[i]);
951 break;
952 }
953
954#if defined (NO_BANG_HASH_MODIFIERS)
955 /* There is something that is listed as a `word specifier' in csh
956 documentation which means `the expanded text to this point'.
957 That is not a word specifier, it is an event specifier. If we
958 don't want to allow modifiers with `!#', just stick the current
959 output line in again. */
960 if (cc == '#')
961 {
962 if (result)
963 {
964 temp = xmalloc (1 + strlen (result));
965 strcpy (temp, result);
966 ADD_STRING (temp);
967 free (temp);
968 }
969 i++;
970 break;
971 }
972#endif
973
974 r = history_expand_internal (string, i, &eindex, &temp, result);
975 if (r < 0)
976 {
977 *output = temp;
978 free (result);
979 if (string != hstring)
980 free (string);
981 return -1;
982 }
983 else
984 {
985 if (temp)
986 {
987 modified++;
988 if (*temp)
989 ADD_STRING (temp);
990 free (temp);
991 }
992 only_printing = r == 1;
993 i = eindex;
994 }
995 break;
996 }
997 }
998
999 *output = result;
1000 if (string != hstring)
1001 free (string);
1002
1003 if (only_printing)
1004 {
1005 add_history (result);
1006 return (2);
1007 }
1008
1009 return (modified != 0);
1010}
1011
1012/* Return a consed string which is the word specified in SPEC, and found
1013 in FROM. NULL is returned if there is no spec. The address of
1014 ERROR_POINTER is returned if the word specified cannot be found.
1015 CALLER_INDEX is the offset in SPEC to start looking; it is updated
1016 to point to just after the last character parsed. */
1017static char *
1018get_history_word_specifier (spec, from, caller_index)
1019 char *spec, *from;
1020 int *caller_index;
1021{
1022 register int i = *caller_index;
1023 int first, last;
1024 int expecting_word_spec = 0;
1025 char *result;
1026
1027 /* The range of words to return doesn't exist yet. */
1028 first = last = 0;
1029 result = (char *)NULL;
1030
1031 /* If we found a colon, then this *must* be a word specification. If
1032 it isn't, then it is an error. */
1033 if (spec[i] == ':')
1034 {
1035 i++;
1036 expecting_word_spec++;
1037 }
1038
1039 /* Handle special cases first. */
1040
1041 /* `%' is the word last searched for. */
1042 if (spec[i] == '%')
1043 {
1044 *caller_index = i + 1;
1045 return (search_match ? savestring (search_match) : savestring (""));
1046 }
1047
1048 /* `*' matches all of the arguments, but not the command. */
1049 if (spec[i] == '*')
1050 {
1051 *caller_index = i + 1;
1052 result = history_arg_extract (1, '$', from);
1053 return (result ? result : savestring (""));
1054 }
1055
1056 /* `$' is last arg. */
1057 if (spec[i] == '$')
1058 {
1059 *caller_index = i + 1;
1060 return (history_arg_extract ('$', '$', from));
1061 }
1062
1063 /* Try to get FIRST and LAST figured out. */
1064
1065 if (spec[i] == '-')
1066 first = 0;
1067 else if (spec[i] == '^')
1068 first = 1;
1069 else if (_rl_digit_p (spec[i]) && expecting_word_spec)
1070 {
1071 for (first = 0; _rl_digit_p (spec[i]); i++)
1072 first = (first * 10) + _rl_digit_value (spec[i]);
1073 }
1074 else
1075 return ((char *)NULL); /* no valid `first' for word specifier */
1076
1077 if (spec[i] == '^' || spec[i] == '*')
1078 {
1079 last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */
1080 i++;
1081 }
1082 else if (spec[i] != '-')
1083 last = first;
1084 else
1085 {
1086 i++;
1087
1088 if (_rl_digit_p (spec[i]))
1089 {
1090 for (last = 0; _rl_digit_p (spec[i]); i++)
1091 last = (last * 10) + _rl_digit_value (spec[i]);
1092 }
1093 else if (spec[i] == '$')
1094 {
1095 i++;
1096 last = '$';
1097 }
1098 else if (!spec[i] || spec[i] == ':') /* could be modifier separator */
1099 last = -1; /* x- abbreviates x-$ omitting word `$' */
1100 }
1101
1102 *caller_index = i;
1103
1104 if (last >= first || last == '$' || last < 0)
1105 result = history_arg_extract (first, last, from);
1106
1107 return (result ? result : (char *)&error_pointer);
1108}
1109
1110/* Extract the args specified, starting at FIRST, and ending at LAST.
1111 The args are taken from STRING. If either FIRST or LAST is < 0,
1112 then make that arg count from the right (subtract from the number of
1113 tokens, so that FIRST = -1 means the next to last token on the line).
1114 If LAST is `$' the last arg from STRING is used. */
1115char *
1116history_arg_extract (first, last, string)
1117 int first, last;
1118 char *string;
1119{
1120 register int i, len;
1121 char *result;
1122 int size, offset;
1123 char **list;
1124
1125 /* XXX - think about making history_tokenize return a struct array,
1126 each struct in array being a string and a length to avoid the
1127 calls to strlen below. */
1128 if ((list = history_tokenize (string)) == NULL)
1129 return ((char *)NULL);
1130
1131 for (len = 0; list[len]; len++)
1132 ;
1133
1134 if (last < 0)
1135 last = len + last - 1;
1136
1137 if (first < 0)
1138 first = len + first - 1;
1139
1140 if (last == '$')
1141 last = len - 1;
1142
1143 if (first == '$')
1144 first = len - 1;
1145
1146 last++;
1147
1148 if (first >= len || last > len || first < 0 || last < 0 || first > last)
1149 result = ((char *)NULL);
1150 else
1151 {
1152 for (size = 0, i = first; i < last; i++)
1153 size += strlen (list[i]) + 1;
1154 result = xmalloc (size + 1);
1155 result[0] = '\0';
1156
1157 for (i = first, offset = 0; i < last; i++)
1158 {
1159 strcpy (result + offset, list[i]);
1160 offset += strlen (list[i]);
1161 if (i + 1 < last)
1162 {
1163 result[offset++] = ' ';
1164 result[offset] = 0;
1165 }
1166 }
1167 }
1168
1169 for (i = 0; i < len; i++)
1170 free (list[i]);
1171 free (list);
1172
1173 return (result);
1174}
1175
1176#define slashify_in_quotes "\\`\"$"
1177
1178/* Parse STRING into tokens and return an array of strings. If WIND is
1179 not -1 and INDP is not null, we also want the word surrounding index
1180 WIND. The position in the returned array of strings is returned in
1181 *INDP. */
1182static char **
1183history_tokenize_internal (string, wind, indp)
1184 char *string;
1185 int wind, *indp;
1186{
1187 char **result;
1188 register int i, start, result_index, size;
1189 int len, delimiter;
1190
1191 /* Get a token, and stuff it into RESULT. The tokens are split
1192 exactly where the shell would split them. */
1193 for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
1194 {
1195 delimiter = 0;
1196
1197 /* Skip leading whitespace. */
1198 for (; string[i] && whitespace (string[i]); i++)
1199 ;
1200 if (string[i] == 0 || string[i] == history_comment_char)
1201 return (result);
1202
1203 start = i;
1204
1205 if (member (string[i], "()\n"))
1206 {
1207 i++;
1208 goto got_token;
1209 }
1210
1211 if (member (string[i], "<>;&|$"))
1212 {
1213 int peek = string[i + 1];
1214
1215 if (peek == string[i] && peek != '$')
1216 {
1217 if (peek == '<' && string[i + 2] == '-')
1218 i++;
1219 i += 2;
1220 goto got_token;
1221 }
1222 else
1223 {
1224 if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
1225 ((peek == '>') && (string[i] == '&')) ||
1226 ((peek == '(') && (string[i] == '$')))
1227 {
1228 i += 2;
1229 goto got_token;
1230 }
1231 }
1232 if (string[i] != '$')
1233 {
1234 i++;
1235 goto got_token;
1236 }
1237 }
1238
1239 /* Get word from string + i; */
1240
1241 if (member (string[i], "\"'`"))
1242 delimiter = string[i++];
1243
1244 for (; string[i]; i++)
1245 {
1246 if (string[i] == '\\' && string[i + 1] == '\n')
1247 {
1248 i++;
1249 continue;
1250 }
1251
1252 if (string[i] == '\\' && delimiter != '\'' &&
1253 (delimiter != '"' || member (string[i], slashify_in_quotes)))
1254 {
1255 i++;
1256 continue;
1257 }
1258
1259 if (delimiter && string[i] == delimiter)
1260 {
1261 delimiter = 0;
1262 continue;
1263 }
1264
1265 if (!delimiter && (member (string[i], " \t\n;&()|<>")))
1266 break;
1267
1268 if (!delimiter && member (string[i], "\"'`"))
1269 delimiter = string[i];
1270 }
1271
1272 got_token:
1273
1274 /* If we are looking for the word in which the character at a
1275 particular index falls, remember it. */
1276 if (indp && wind != -1 && wind >= start && wind < i)
1277 *indp = result_index;
1278
1279 len = i - start;
1280 if (result_index + 2 >= size)
1281 result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
1282 result[result_index] = xmalloc (1 + len);
1283 strncpy (result[result_index], string + start, len);
1284 result[result_index][len] = '\0';
1285 result[++result_index] = (char *)NULL;
1286 }
1287
1288 return (result);
1289}
1290
1291/* Return an array of tokens, much as the shell might. The tokens are
1292 parsed out of STRING. */
1293char **
1294history_tokenize (string)
1295 char *string;
1296{
1297 return (history_tokenize_internal (string, -1, (int *)NULL));
1298}
1299
1300/* Find and return the word which contains the character at index IND
1301 in the history line LINE. Used to save the word matched by the
1302 last history !?string? search. */
1303static char *
1304history_find_word (line, ind)
1305 char *line;
1306 int ind;
1307{
1308 char **words, *s;
1309 int i, wind;
1310
1311 words = history_tokenize_internal (line, ind, &wind);
1312 if (wind == -1)
1313 return ((char *)NULL);
1314 s = words[wind];
1315 for (i = 0; i < wind; i++)
1316 free (words[i]);
1317 for (i = wind + 1; words[i]; i++)
1318 free (words[i]);
1319 free (words);
1320 return s;
1321}