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