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