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