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