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