xref: /openbsd/gnu/lib/libreadline/histexpand.c (revision db3296cf)
1 /* histexpand.c -- history expansion. */
2 
3 /* Copyright (C) 1989, 1992 Free Software Foundation, Inc.
4 
5    This file contains the GNU History Library (the Library), a set of
6    routines for managing the text of previously typed lines.
7 
8    The Library is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2, or (at your option)
11    any later version.
12 
13    The Library is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17 
18    The GNU General Public License is often shipped with GNU software, and
19    is generally kept in a file called COPYING or LICENSE.  If you do not
20    have a copy of the license, write to the Free Software Foundation,
21    59 Temple Place, Suite 330, Boston, MA 02111 USA. */
22 
23 #define READLINE_LIBRARY
24 
25 #if defined (HAVE_CONFIG_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 #include "rlshell.h"
54 #include "xmalloc.h"
55 
56 #define HISTORY_WORD_DELIMITERS		" \t\n;&()|<>"
57 #define HISTORY_QUOTE_CHARACTERS	"\"'`"
58 
59 static char error_pointer;
60 
61 static char *subst_lhs;
62 static char *subst_rhs;
63 static int subst_lhs_len;
64 static int subst_rhs_len;
65 
66 static char *get_history_word_specifier __P((char *, char *, int *));
67 static char *history_find_word __P((char *, int));
68 
69 static char *quote_breaks __P((char *));
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 /* If set, this points to a function that is called to verify that a
95    particular history expansion should be performed. */
96 Function *history_inhibit_expansion_function;
97 
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. */
108 static char *search_string;
109 
110 /* The last string matched by a !?string? search. */
111 static 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);  */
123 char *
124 get_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. */
283 static void
284 hist_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 
296 static char *
297 quote_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 
338 static char *
339 hist_error(s, start, current, errtype)
340       char *s;
341       int start, current, errtype;
342 {
343   char *temp, *emsg;
344   int ll, elen, len;
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;
366     case NO_PREV_SUBST:
367       emsg = "no previous substitution";
368       elen = 24;
369       break;
370     default:
371       emsg = "unknown expansion error";
372       elen = 23;
373       break;
374     }
375 
376   len = ll + elen + 3;
377   temp = xmalloc (len);
378   strncpy (temp, s + start, ll);
379   strlcat (temp, ": ", len);
380   strlcat (temp, emsg, len);
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 
396 static char *
397 get_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 
433 static void
434 postproc_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 	  strlcpy (new + j, subst_lhs, new_size - j);
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. */
470 static int
471 history_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 
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 
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 
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   strlcpy (result, temp, n + 2);
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 	    strlcpy (result + j - sl, s, result_len - j + sl); \
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 
803 int
804 history_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 
834   /* Grovel the string.  Only backslash and single quotes can quote the
835      history escape character.  We also handle arg specifiers. */
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       strlcpy (string + 4, hstring, l + 1);
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];
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)
875 	    {
876 	      if (!cc || member (cc, history_no_expand_chars))
877 		continue;
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))
885 		continue;
886 	      else
887 		break;
888 	    }
889 	  /* XXX - at some point, might want to extend this to handle
890 		   double quotes as well. */
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;
928       else if (tchar == history_comment_char)
929 	tchar = -2;
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 
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 	      strlcpy (temp, string + i, l - i + 1);
970 	      ADD_STRING (temp);
971 	      free (temp);
972 	      i = l;
973 	    }
974 	  else
975 	    ADD_CHAR (string[i]);
976 	  break;
977 
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 		  strlcpy (temp, result, 1 + strlen(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. */
1053 static char *
1054 get_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. */
1151 char *
1152 history_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 	  strlcpy (result + offset, list[i], size + 1 - offset);
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. */
1218 static char **
1219 history_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 
1277       if (member (string[i], HISTORY_QUOTE_CHARACTERS))
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 
1301 	  if (!delimiter && (member (string[i], HISTORY_WORD_DELIMITERS)))
1302 	    break;
1303 
1304 	  if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS))
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. */
1329 char **
1330 history_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. */
1339 static char *
1340 history_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 }
1358