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