121308Sache/* histexpand.c -- history expansion. */
221308Sache
3136644Sache/* Copyright (C) 1989-2004 Free Software Foundation, Inc.
421308Sache
521308Sache   This file contains the GNU History Library (the Library), a set of
621308Sache   routines for managing the text of previously typed lines.
721308Sache
821308Sache   The Library is free software; you can redistribute it and/or modify
921308Sache   it under the terms of the GNU General Public License as published by
1058310Sache   the Free Software Foundation; either version 2, or (at your option)
1121308Sache   any later version.
1221308Sache
1321308Sache   The Library is distributed in the hope that it will be useful, but
1421308Sache   WITHOUT ANY WARRANTY; without even the implied warranty of
1521308Sache   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1621308Sache   General Public License for more details.
1721308Sache
1821308Sache   The GNU General Public License is often shipped with GNU software, and
1921308Sache   is generally kept in a file called COPYING or LICENSE.  If you do not
2021308Sache   have a copy of the license, write to the Free Software Foundation,
2158310Sache   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
2221308Sache
2321308Sache#define READLINE_LIBRARY
2421308Sache
2521308Sache#if defined (HAVE_CONFIG_H)
2621308Sache#  include <config.h>
2721308Sache#endif
2821308Sache
2921308Sache#include <stdio.h>
3021308Sache
3121308Sache#if defined (HAVE_STDLIB_H)
3221308Sache#  include <stdlib.h>
3321308Sache#else
3421308Sache#  include "ansi_stdlib.h"
3521308Sache#endif /* HAVE_STDLIB_H */
3621308Sache
3721308Sache#if defined (HAVE_UNISTD_H)
3835486Sache#  ifndef _MINIX
3935486Sache#    include <sys/types.h>
4035486Sache#  endif
4121308Sache#  include <unistd.h>
4221308Sache#endif
4321308Sache
44119610Sache#include "rlmbutil.h"
4521308Sache
4621308Sache#include "history.h"
4721308Sache#include "histlib.h"
4821308Sache
4958310Sache#include "rlshell.h"
5058310Sache#include "xmalloc.h"
5158310Sache
5235486Sache#define HISTORY_WORD_DELIMITERS		" \t\n;&()|<>"
5335486Sache#define HISTORY_QUOTE_CHARACTERS	"\"'`"
5435486Sache
55136644Sache#define slashify_in_quotes "\\`\"$"
56136644Sache
57119610Sachetypedef int _hist_search_func_t PARAMS((const char *, int));
5875406Sache
5921308Sachestatic char error_pointer;
6021308Sache
6121308Sachestatic char *subst_lhs;
6221308Sachestatic char *subst_rhs;
6321308Sachestatic int subst_lhs_len;
6421308Sachestatic int subst_rhs_len;
6521308Sache
66119610Sachestatic char *get_history_word_specifier PARAMS((char *, char *, int *));
67119610Sachestatic char *history_find_word PARAMS((char *, int));
68136644Sachestatic int history_tokenize_word PARAMS((const char *, int));
69136644Sachestatic char *history_substring PARAMS((const char *, int, int));
7021308Sache
71119610Sachestatic char *quote_breaks PARAMS((char *));
7221308Sache
7321308Sache/* Variables exported by this file. */
7421308Sache/* The character that represents the start of a history expansion
7521308Sache   request.  This is usually `!'. */
7621308Sachechar history_expansion_char = '!';
7721308Sache
7821308Sache/* The character that invokes word substitution if found at the start of
7921308Sache   a line.  This is usually `^'. */
8021308Sachechar history_subst_char = '^';
8121308Sache
8221308Sache/* During tokenization, if this character is seen as the first character
8321308Sache   of a word, then it, and all subsequent characters upto a newline are
8421308Sache   ignored.  For a Bourne shell, this should be '#'.  Bash special cases
8521308Sache   the interactive comment character to not be a comment delimiter. */
8621308Sachechar history_comment_char = '\0';
8721308Sache
8821308Sache/* The list of characters which inhibit the expansion of text if found
8921308Sache   immediately following history_expansion_char. */
9021308Sachechar *history_no_expand_chars = " \t\n\r=";
9121308Sache
9221308Sache/* If set to a non-zero value, single quotes inhibit history expansion.
9321308Sache   The default is 0. */
9421308Sacheint history_quotes_inhibit_expansion = 0;
9521308Sache
9675406Sache/* Used to split words by history_tokenize_internal. */
9775406Sachechar *history_word_delimiters = HISTORY_WORD_DELIMITERS;
9875406Sache
9926497Sache/* If set, this points to a function that is called to verify that a
10026497Sache   particular history expansion should be performed. */
10175406Sacherl_linebuf_func_t *history_inhibit_expansion_function;
10226497Sache
10321308Sache/* **************************************************************** */
10421308Sache/*								    */
10521308Sache/*			History Expansion			    */
10621308Sache/*								    */
10721308Sache/* **************************************************************** */
10821308Sache
10921308Sache/* Hairy history expansion on text, not tokens.  This is of general
11021308Sache   use, and thus belongs in this library. */
11121308Sache
11221308Sache/* The last string searched for by a !?string? search. */
11321308Sachestatic char *search_string;
11421308Sache
11521308Sache/* The last string matched by a !?string? search. */
11621308Sachestatic char *search_match;
11721308Sache
11821308Sache/* Return the event specified at TEXT + OFFSET modifying OFFSET to
11921308Sache   point to after the event specifier.  Just a pointer to the history
12021308Sache   line is returned; NULL is returned in the event of a bad specifier.
12121308Sache   You pass STRING with *INDEX equal to the history_expansion_char that
12221308Sache   begins this specification.
12321308Sache   DELIMITING_QUOTE is a character that is allowed to end the string
12421308Sache   specification for what to search for in addition to the normal
12521308Sache   characters `:', ` ', `\t', `\n', and sometimes `?'.
12621308Sache   So you might call this function like:
12721308Sache   line = get_history_event ("!echo:p", &index, 0);  */
12821308Sachechar *
12921308Sacheget_history_event (string, caller_index, delimiting_quote)
13075406Sache     const char *string;
13121308Sache     int *caller_index;
13221308Sache     int delimiting_quote;
13321308Sache{
13421308Sache  register int i;
13521308Sache  register char c;
13621308Sache  HIST_ENTRY *entry;
13721308Sache  int which, sign, local_index, substring_okay;
13875406Sache  _hist_search_func_t *search_func;
13921308Sache  char *temp;
14021308Sache
14121308Sache  /* The event can be specified in a number of ways.
14221308Sache
14321308Sache     !!   the previous command
14421308Sache     !n   command line N
14521308Sache     !-n  current command-line minus N
14621308Sache     !str the most recent command starting with STR
14721308Sache     !?str[?]
14821308Sache	  the most recent command containing STR
14921308Sache
15021308Sache     All values N are determined via HISTORY_BASE. */
15121308Sache
15221308Sache  i = *caller_index;
15321308Sache
15421308Sache  if (string[i] != history_expansion_char)
15521308Sache    return ((char *)NULL);
15621308Sache
15721308Sache  /* Move on to the specification. */
15821308Sache  i++;
15921308Sache
16021308Sache  sign = 1;
16121308Sache  substring_okay = 0;
16221308Sache
16321308Sache#define RETURN_ENTRY(e, w) \
16421308Sache	return ((e = history_get (w)) ? e->line : (char *)NULL)
16521308Sache
16621308Sache  /* Handle !! case. */
16721308Sache  if (string[i] == history_expansion_char)
16821308Sache    {
16921308Sache      i++;
17021308Sache      which = history_base + (history_length - 1);
17121308Sache      *caller_index = i;
17221308Sache      RETURN_ENTRY (entry, which);
17321308Sache    }
17421308Sache
17521308Sache  /* Hack case of numeric line specification. */
17621308Sache  if (string[i] == '-')
17721308Sache    {
17821308Sache      sign = -1;
17921308Sache      i++;
18021308Sache    }
18121308Sache
18221308Sache  if (_rl_digit_p (string[i]))
18321308Sache    {
18421308Sache      /* Get the extent of the digits and compute the value. */
18521308Sache      for (which = 0; _rl_digit_p (string[i]); i++)
18621308Sache	which = (which * 10) + _rl_digit_value (string[i]);
18721308Sache
18821308Sache      *caller_index = i;
18921308Sache
19021308Sache      if (sign < 0)
19121308Sache	which = (history_length + history_base) - which;
19221308Sache
19321308Sache      RETURN_ENTRY (entry, which);
19421308Sache    }
19521308Sache
19621308Sache  /* This must be something to search for.  If the spec begins with
19721308Sache     a '?', then the string may be anywhere on the line.  Otherwise,
19821308Sache     the string must be found at the start of a line. */
19921308Sache  if (string[i] == '?')
20021308Sache    {
20121308Sache      substring_okay++;
20221308Sache      i++;
20321308Sache    }
20421308Sache
20521308Sache  /* Only a closing `?' or a newline delimit a substring search string. */
20621308Sache  for (local_index = i; c = string[i]; i++)
207157184Sache    {
208119610Sache#if defined (HANDLE_MULTIBYTE)
209157184Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
210157184Sache	{
211157184Sache	  int v;
212157184Sache	  mbstate_t ps;
21321308Sache
214157184Sache	  memset (&ps, 0, sizeof (mbstate_t));
215157184Sache	  /* These produce warnings because we're passing a const string to a
216157184Sache	     function that takes a non-const string. */
217157184Sache	  _rl_adjust_point ((char *)string, i, &ps);
218157184Sache	  if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1)
219157184Sache	    {
220157184Sache	      i += v - 1;
221157184Sache	      continue;
222157184Sache	    }
223157184Sache        }
224157184Sache
225119610Sache#endif /* HANDLE_MULTIBYTE */
226119610Sache      if ((!substring_okay && (whitespace (c) || c == ':' ||
227119610Sache	  (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
228119610Sache	  string[i] == delimiting_quote)) ||
229119610Sache	  string[i] == '\n' ||
230119610Sache	  (substring_okay && string[i] == '?'))
231119610Sache	break;
232157184Sache    }
233119610Sache
23421308Sache  which = i - local_index;
235119610Sache  temp = (char *)xmalloc (1 + which);
23621308Sache  if (which)
23721308Sache    strncpy (temp, string + local_index, which);
23821308Sache  temp[which] = '\0';
23921308Sache
24021308Sache  if (substring_okay && string[i] == '?')
24121308Sache    i++;
24221308Sache
24321308Sache  *caller_index = i;
24421308Sache
24521308Sache#define FAIL_SEARCH() \
24621308Sache  do { \
24721308Sache    history_offset = history_length; free (temp) ; return (char *)NULL; \
24821308Sache  } while (0)
24921308Sache
25021308Sache  /* If there is no search string, try to use the previous search string,
25121308Sache     if one exists.  If not, fail immediately. */
25221308Sache  if (*temp == '\0' && substring_okay)
25321308Sache    {
25421308Sache      if (search_string)
25521308Sache        {
25621308Sache          free (temp);
25721308Sache          temp = savestring (search_string);
25821308Sache        }
25921308Sache      else
26021308Sache        FAIL_SEARCH ();
26121308Sache    }
26221308Sache
26321308Sache  search_func = substring_okay ? history_search : history_search_prefix;
26421308Sache  while (1)
26521308Sache    {
26621308Sache      local_index = (*search_func) (temp, -1);
26721308Sache
26821308Sache      if (local_index < 0)
26921308Sache	FAIL_SEARCH ();
27021308Sache
27121308Sache      if (local_index == 0 || substring_okay)
27221308Sache	{
27321308Sache	  entry = current_history ();
27421308Sache	  history_offset = history_length;
27521308Sache
27621308Sache	  /* If this was a substring search, then remember the
27721308Sache	     string that we matched for word substitution. */
27821308Sache	  if (substring_okay)
27921308Sache	    {
28021308Sache	      FREE (search_string);
28121308Sache	      search_string = temp;
28221308Sache
28321308Sache	      FREE (search_match);
28421308Sache	      search_match = history_find_word (entry->line, local_index);
28521308Sache	    }
28621308Sache	  else
28721308Sache	    free (temp);
28821308Sache
28921308Sache	  return (entry->line);
29021308Sache	}
29121308Sache
29221308Sache      if (history_offset)
29321308Sache	history_offset--;
29421308Sache      else
29521308Sache	FAIL_SEARCH ();
29621308Sache    }
29721308Sache#undef FAIL_SEARCH
29821308Sache#undef RETURN_ENTRY
29921308Sache}
30021308Sache
30121308Sache/* Function for extracting single-quoted strings.  Used for inhibiting
30221308Sache   history expansion within single quotes. */
30321308Sache
30421308Sache/* Extract the contents of STRING as if it is enclosed in single quotes.
30521308Sache   SINDEX, when passed in, is the offset of the character immediately
30621308Sache   following the opening single quote; on exit, SINDEX is left pointing
30721308Sache   to the closing single quote. */
30821308Sachestatic void
30921308Sachehist_string_extract_single_quoted (string, sindex)
31021308Sache     char *string;
31121308Sache     int *sindex;
31221308Sache{
31321308Sache  register int i;
31421308Sache
31521308Sache  for (i = *sindex; string[i] && string[i] != '\''; i++)
31621308Sache    ;
31721308Sache
31821308Sache  *sindex = i;
31921308Sache}
32021308Sache
32121308Sachestatic char *
32221308Sachequote_breaks (s)
32321308Sache     char *s;
32421308Sache{
32521308Sache  register char *p, *r;
32621308Sache  char *ret;
32721308Sache  int len = 3;
32821308Sache
32921308Sache  for (p = s; p && *p; p++, len++)
33021308Sache    {
33121308Sache      if (*p == '\'')
33221308Sache	len += 3;
33321308Sache      else if (whitespace (*p) || *p == '\n')
33421308Sache	len += 2;
33521308Sache    }
33621308Sache
337119610Sache  r = ret = (char *)xmalloc (len);
33821308Sache  *r++ = '\'';
33921308Sache  for (p = s; p && *p; )
34021308Sache    {
34121308Sache      if (*p == '\'')
34221308Sache	{
34321308Sache	  *r++ = '\'';
34421308Sache	  *r++ = '\\';
34521308Sache	  *r++ = '\'';
34621308Sache	  *r++ = '\'';
34721308Sache	  p++;
34821308Sache	}
34921308Sache      else if (whitespace (*p) || *p == '\n')
35021308Sache	{
35121308Sache	  *r++ = '\'';
35221308Sache	  *r++ = *p++;
35321308Sache	  *r++ = '\'';
35421308Sache	}
35521308Sache      else
35621308Sache	*r++ = *p++;
35721308Sache    }
35821308Sache  *r++ = '\'';
35921308Sache  *r = '\0';
36021308Sache  return ret;
36121308Sache}
36221308Sache
36321308Sachestatic char *
36421308Sachehist_error(s, start, current, errtype)
36521308Sache      char *s;
36621308Sache      int start, current, errtype;
36721308Sache{
36875406Sache  char *temp;
36975406Sache  const char *emsg;
37021308Sache  int ll, elen;
37121308Sache
37221308Sache  ll = current - start;
37321308Sache
37421308Sache  switch (errtype)
37521308Sache    {
37621308Sache    case EVENT_NOT_FOUND:
37721308Sache      emsg = "event not found";
37821308Sache      elen = 15;
37921308Sache      break;
38021308Sache    case BAD_WORD_SPEC:
38121308Sache      emsg = "bad word specifier";
38221308Sache      elen = 18;
38321308Sache      break;
38421308Sache    case SUBST_FAILED:
38521308Sache      emsg = "substitution failed";
38621308Sache      elen = 19;
38721308Sache      break;
38821308Sache    case BAD_MODIFIER:
38921308Sache      emsg = "unrecognized history modifier";
39021308Sache      elen = 29;
39121308Sache      break;
39247558Sache    case NO_PREV_SUBST:
39347558Sache      emsg = "no previous substitution";
39447558Sache      elen = 24;
39547558Sache      break;
39621308Sache    default:
39721308Sache      emsg = "unknown expansion error";
39821308Sache      elen = 23;
39921308Sache      break;
40021308Sache    }
40121308Sache
402119610Sache  temp = (char *)xmalloc (ll + elen + 3);
40321308Sache  strncpy (temp, s + start, ll);
40421308Sache  temp[ll] = ':';
40521308Sache  temp[ll + 1] = ' ';
40621308Sache  strcpy (temp + ll + 2, emsg);
40721308Sache  return (temp);
40821308Sache}
40921308Sache
41021308Sache/* Get a history substitution string from STR starting at *IPTR
41121308Sache   and return it.  The length is returned in LENPTR.
41221308Sache
41321308Sache   A backslash can quote the delimiter.  If the string is the
41421308Sache   empty string, the previous pattern is used.  If there is
41521308Sache   no previous pattern for the lhs, the last history search
41621308Sache   string is used.
41721308Sache
41821308Sache   If IS_RHS is 1, we ignore empty strings and set the pattern
41921308Sache   to "" anyway.  subst_lhs is not changed if the lhs is empty;
42021308Sache   subst_rhs is allowed to be set to the empty string. */
42121308Sache
42221308Sachestatic char *
42321308Sacheget_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
42421308Sache     char *str;
42521308Sache     int *iptr, delimiter, is_rhs, *lenptr;
42621308Sache{
42721308Sache  register int si, i, j, k;
428119610Sache  char *s;
429119610Sache#if defined (HANDLE_MULTIBYTE)
430119610Sache  mbstate_t ps;
431119610Sache#endif
43221308Sache
433119610Sache  s = (char *)NULL;
43421308Sache  i = *iptr;
43521308Sache
436119610Sache#if defined (HANDLE_MULTIBYTE)
437119610Sache  memset (&ps, 0, sizeof (mbstate_t));
438119610Sache  _rl_adjust_point (str, i, &ps);
439119610Sache#endif
440119610Sache
44121308Sache  for (si = i; str[si] && str[si] != delimiter; si++)
442119610Sache#if defined (HANDLE_MULTIBYTE)
443119610Sache    if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
444119610Sache      {
445119610Sache	int v;
446119610Sache	if ((v = _rl_get_char_len (str + si, &ps)) > 1)
447119610Sache	  si += v - 1;
448119610Sache	else if (str[si] == '\\' && str[si + 1] == delimiter)
449119610Sache	  si++;
450119610Sache      }
451119610Sache    else
452119610Sache#endif /* HANDLE_MULTIBYTE */
453119610Sache      if (str[si] == '\\' && str[si + 1] == delimiter)
454119610Sache	si++;
45521308Sache
45621308Sache  if (si > i || is_rhs)
45721308Sache    {
458119610Sache      s = (char *)xmalloc (si - i + 1);
45921308Sache      for (j = 0, k = i; k < si; j++, k++)
46021308Sache	{
46121308Sache	  /* Remove a backslash quoting the search string delimiter. */
46221308Sache	  if (str[k] == '\\' && str[k + 1] == delimiter)
46321308Sache	    k++;
46421308Sache	  s[j] = str[k];
46521308Sache	}
46621308Sache      s[j] = '\0';
46721308Sache      if (lenptr)
46821308Sache	*lenptr = j;
46921308Sache    }
47021308Sache
47121308Sache  i = si;
47221308Sache  if (str[i])
47321308Sache    i++;
47421308Sache  *iptr = i;
47521308Sache
47621308Sache  return s;
47721308Sache}
47821308Sache
47921308Sachestatic void
48021308Sachepostproc_subst_rhs ()
48121308Sache{
48221308Sache  char *new;
48321308Sache  int i, j, new_size;
48421308Sache
485119610Sache  new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len);
48621308Sache  for (i = j = 0; i < subst_rhs_len; i++)
48721308Sache    {
48821308Sache      if (subst_rhs[i] == '&')
48921308Sache	{
49021308Sache	  if (j + subst_lhs_len >= new_size)
491119610Sache	    new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
49221308Sache	  strcpy (new + j, subst_lhs);
49321308Sache	  j += subst_lhs_len;
49421308Sache	}
49521308Sache      else
49621308Sache	{
49721308Sache	  /* a single backslash protects the `&' from lhs interpolation */
49821308Sache	  if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
49921308Sache	    i++;
50021308Sache	  if (j >= new_size)
501119610Sache	    new = (char *)xrealloc (new, new_size *= 2);
50221308Sache	  new[j++] = subst_rhs[i];
50321308Sache	}
50421308Sache    }
50521308Sache  new[j] = '\0';
50621308Sache  free (subst_rhs);
50721308Sache  subst_rhs = new;
50821308Sache  subst_rhs_len = j;
50921308Sache}
51021308Sache
51121308Sache/* Expand the bulk of a history specifier starting at STRING[START].
51221308Sache   Returns 0 if everything is OK, -1 if an error occurred, and 1
51321308Sache   if the `p' modifier was supplied and the caller should just print
51421308Sache   the returned string.  Returns the new index into string in
51521308Sache   *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
51621308Sachestatic int
51721308Sachehistory_expand_internal (string, start, end_index_ptr, ret_string, current_line)
51821308Sache     char *string;
51921308Sache     int start, *end_index_ptr;
52021308Sache     char **ret_string;
52121308Sache     char *current_line;	/* for !# */
52221308Sache{
52321308Sache  int i, n, starting_index;
524136644Sache  int substitute_globally, subst_bywords, want_quotes, print_only;
52521308Sache  char *event, *temp, *result, *tstr, *t, c, *word_spec;
52621308Sache  int result_len;
527119610Sache#if defined (HANDLE_MULTIBYTE)
528119610Sache  mbstate_t ps;
52921308Sache
530119610Sache  memset (&ps, 0, sizeof (mbstate_t));
531119610Sache#endif
53221308Sache
533119610Sache  result = (char *)xmalloc (result_len = 128);
534119610Sache
53521308Sache  i = start;
53621308Sache
53721308Sache  /* If it is followed by something that starts a word specifier,
53821308Sache     then !! is implied as the event specifier. */
53921308Sache
54021308Sache  if (member (string[i + 1], ":$*%^"))
54121308Sache    {
54221308Sache      char fake_s[3];
54321308Sache      int fake_i = 0;
54421308Sache      i++;
54521308Sache      fake_s[0] = fake_s[1] = history_expansion_char;
54621308Sache      fake_s[2] = '\0';
54721308Sache      event = get_history_event (fake_s, &fake_i, 0);
54821308Sache    }
54921308Sache  else if (string[i + 1] == '#')
55021308Sache    {
55121308Sache      i += 2;
55221308Sache      event = current_line;
55321308Sache    }
55421308Sache  else
55521308Sache    {
55621308Sache      int quoted_search_delimiter = 0;
55721308Sache
55821308Sache      /* If the character before this `!' is a double or single
55921308Sache	 quote, then this expansion takes place inside of the
56021308Sache	 quoted string.  If we have to search for some text ("!foo"),
56121308Sache	 allow the delimiter to end the search string. */
562119610Sache#if defined (HANDLE_MULTIBYTE)
563119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
564119610Sache	{
565165670Sache	  int ch, l;
566119610Sache	  l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY);
567165670Sache	  ch = string[l];
568119610Sache	  /* XXX - original patch had i - 1 ???  If i == 0 it would fail. */
569165670Sache	  if (i && (ch == '\'' || ch == '"'))
570165670Sache	    quoted_search_delimiter = ch;
571119610Sache	}
572119610Sache      else
573119610Sache#endif /* HANDLE_MULTIBYTE */
574119610Sache	if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
575119610Sache	  quoted_search_delimiter = string[i - 1];
576119610Sache
57721308Sache      event = get_history_event (string, &i, quoted_search_delimiter);
57821308Sache    }
57921308Sache
58021308Sache  if (event == 0)
58121308Sache    {
58221308Sache      *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
58321308Sache      free (result);
58421308Sache      return (-1);
58521308Sache    }
58621308Sache
58721308Sache  /* If a word specifier is found, then do what that requires. */
58821308Sache  starting_index = i;
58921308Sache  word_spec = get_history_word_specifier (string, event, &i);
59021308Sache
59121308Sache  /* There is no such thing as a `malformed word specifier'.  However,
59221308Sache     it is possible for a specifier that has no match.  In that case,
59321308Sache     we complain. */
59421308Sache  if (word_spec == (char *)&error_pointer)
59521308Sache    {
59621308Sache      *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
59721308Sache      free (result);
59821308Sache      return (-1);
59921308Sache    }
60021308Sache
60121308Sache  /* If no word specifier, than the thing of interest was the event. */
60221308Sache  temp = word_spec ? savestring (word_spec) : savestring (event);
60321308Sache  FREE (word_spec);
60421308Sache
60521308Sache  /* Perhaps there are other modifiers involved.  Do what they say. */
606136644Sache  want_quotes = substitute_globally = subst_bywords = print_only = 0;
60721308Sache  starting_index = i;
60821308Sache
60921308Sache  while (string[i] == ':')
61021308Sache    {
61121308Sache      c = string[i + 1];
61221308Sache
613136644Sache      if (c == 'g' || c == 'a')
61421308Sache	{
61521308Sache	  substitute_globally = 1;
61621308Sache	  i++;
61721308Sache	  c = string[i + 1];
61821308Sache	}
619136644Sache      else if (c == 'G')
620136644Sache	{
621136644Sache	  subst_bywords = 1;
622136644Sache	  i++;
623136644Sache	  c = string[i + 1];
624136644Sache	}
62521308Sache
62621308Sache      switch (c)
62721308Sache	{
62821308Sache	default:
62921308Sache	  *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
63021308Sache	  free (result);
63121308Sache	  free (temp);
63221308Sache	  return -1;
63321308Sache
63421308Sache	case 'q':
63521308Sache	  want_quotes = 'q';
63621308Sache	  break;
63721308Sache
63821308Sache	case 'x':
63921308Sache	  want_quotes = 'x';
64021308Sache	  break;
64121308Sache
64221308Sache	  /* :p means make this the last executed line.  So we
64321308Sache	     return an error state after adding this line to the
64421308Sache	     history. */
64521308Sache	case 'p':
64621308Sache	  print_only++;
64721308Sache	  break;
64821308Sache
64921308Sache	  /* :t discards all but the last part of the pathname. */
65021308Sache	case 't':
65121308Sache	  tstr = strrchr (temp, '/');
65221308Sache	  if (tstr)
65321308Sache	    {
65421308Sache	      tstr++;
65521308Sache	      t = savestring (tstr);
65621308Sache	      free (temp);
65721308Sache	      temp = t;
65821308Sache	    }
65921308Sache	  break;
66021308Sache
66121308Sache	  /* :h discards the last part of a pathname. */
66221308Sache	case 'h':
66321308Sache	  tstr = strrchr (temp, '/');
66421308Sache	  if (tstr)
66521308Sache	    *tstr = '\0';
66621308Sache	  break;
66721308Sache
66821308Sache	  /* :r discards the suffix. */
66921308Sache	case 'r':
67021308Sache	  tstr = strrchr (temp, '.');
67121308Sache	  if (tstr)
67221308Sache	    *tstr = '\0';
67321308Sache	  break;
67421308Sache
67521308Sache	  /* :e discards everything but the suffix. */
67621308Sache	case 'e':
67721308Sache	  tstr = strrchr (temp, '.');
67821308Sache	  if (tstr)
67921308Sache	    {
68021308Sache	      t = savestring (tstr);
68121308Sache	      free (temp);
68221308Sache	      temp = t;
68321308Sache	    }
68421308Sache	  break;
68521308Sache
68621308Sache	/* :s/this/that substitutes `that' for the first
68721308Sache	   occurrence of `this'.  :gs/this/that substitutes `that'
68821308Sache	   for each occurrence of `this'.  :& repeats the last
68921308Sache	   substitution.  :g& repeats the last substitution
69021308Sache	   globally. */
69121308Sache
69221308Sache	case '&':
69321308Sache	case 's':
69421308Sache	  {
69575406Sache	    char *new_event;
696136644Sache	    int delimiter, failed, si, l_temp, ws, we;
69721308Sache
69821308Sache	    if (c == 's')
69921308Sache	      {
70021308Sache		if (i + 2 < (int)strlen (string))
701119610Sache		  {
702119610Sache#if defined (HANDLE_MULTIBYTE)
703119610Sache		    if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
704119610Sache		      {
705119610Sache			_rl_adjust_point (string, i + 2, &ps);
706119610Sache			if (_rl_get_char_len (string + i + 2, &ps) > 1)
707119610Sache			  delimiter = 0;
708119610Sache			else
709119610Sache			  delimiter = string[i + 2];
710119610Sache		      }
711119610Sache		    else
712119610Sache#endif /* HANDLE_MULTIBYTE */
713119610Sache		      delimiter = string[i + 2];
714119610Sache		  }
71521308Sache		else
71621308Sache		  break;	/* no search delimiter */
71721308Sache
71821308Sache		i += 3;
71921308Sache
72021308Sache		t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
72121308Sache		/* An empty substitution lhs with no previous substitution
72221308Sache		   uses the last search string as the lhs. */
72321308Sache		if (t)
72421308Sache		  {
72521308Sache		    FREE (subst_lhs);
72621308Sache		    subst_lhs = t;
72721308Sache		  }
72821308Sache		else if (!subst_lhs)
72921308Sache		  {
73021308Sache		    if (search_string && *search_string)
73121308Sache		      {
73221308Sache			subst_lhs = savestring (search_string);
73321308Sache			subst_lhs_len = strlen (subst_lhs);
73421308Sache		      }
73521308Sache		    else
73621308Sache		      {
73721308Sache			subst_lhs = (char *) NULL;
73821308Sache			subst_lhs_len = 0;
73921308Sache		      }
74021308Sache		  }
74121308Sache
74221308Sache		FREE (subst_rhs);
74321308Sache		subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
74421308Sache
74521308Sache		/* If `&' appears in the rhs, it's supposed to be replaced
74621308Sache		   with the lhs. */
74721308Sache		if (member ('&', subst_rhs))
74821308Sache		  postproc_subst_rhs ();
74921308Sache	      }
75021308Sache	    else
75121308Sache	      i += 2;
75221308Sache
75347558Sache	    /* If there is no lhs, the substitution can't succeed. */
75447558Sache	    if (subst_lhs_len == 0)
75547558Sache	      {
75647558Sache		*ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
75747558Sache		free (result);
75847558Sache		free (temp);
75947558Sache		return -1;
76047558Sache	      }
76147558Sache
76221308Sache	    l_temp = strlen (temp);
76321308Sache	    /* Ignore impossible cases. */
76421308Sache	    if (subst_lhs_len > l_temp)
76521308Sache	      {
76621308Sache		*ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
76721308Sache		free (result);
76821308Sache		free (temp);
76921308Sache		return (-1);
77021308Sache	      }
77121308Sache
77221308Sache	    /* Find the first occurrence of THIS in TEMP. */
773136644Sache	    /* Substitute SUBST_RHS for SUBST_LHS in TEMP.  There are three
774136644Sache	       cases to consider:
775136644Sache
776136644Sache		 1.  substitute_globally == subst_bywords == 0
777136644Sache		 2.  substitute_globally == 1 && subst_bywords == 0
778136644Sache		 3.  substitute_globally == 0 && subst_bywords == 1
779136644Sache
780136644Sache	       In the first case, we substitute for the first occurrence only.
781136644Sache	       In the second case, we substitute for every occurrence.
782136644Sache	       In the third case, we tokenize into words and substitute the
783136644Sache	       first occurrence of each word. */
784136644Sache
785136644Sache	    si = we = 0;
78621308Sache	    for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
787136644Sache	      {
788136644Sache		/* First skip whitespace and find word boundaries if
789136644Sache		   we're past the end of the word boundary we found
790136644Sache		   the last time. */
791136644Sache		if (subst_bywords && si > we)
792136644Sache		  {
793136644Sache		    for (; temp[si] && whitespace (temp[si]); si++)
794136644Sache		      ;
795136644Sache		    ws = si;
796136644Sache		    we = history_tokenize_word (temp, si);
797136644Sache		  }
79821308Sache
799136644Sache		if (STREQN (temp+si, subst_lhs, subst_lhs_len))
800136644Sache		  {
801136644Sache		    int len = subst_rhs_len - subst_lhs_len + l_temp;
802136644Sache		    new_event = (char *)xmalloc (1 + len);
803136644Sache		    strncpy (new_event, temp, si);
804136644Sache		    strncpy (new_event + si, subst_rhs, subst_rhs_len);
805136644Sache		    strncpy (new_event + si + subst_rhs_len,
806136644Sache			     temp + si + subst_lhs_len,
807136644Sache			     l_temp - (si + subst_lhs_len));
808136644Sache		    new_event[len] = '\0';
809136644Sache		    free (temp);
810136644Sache		    temp = new_event;
81121308Sache
812136644Sache		    failed = 0;
81321308Sache
814136644Sache		    if (substitute_globally)
815136644Sache		      {
816136644Sache			/* Reported to fix a bug that causes it to skip every
817136644Sache			   other match when matching a single character.  Was
818136644Sache			   si += subst_rhs_len previously. */
819136644Sache			si += subst_rhs_len - 1;
820136644Sache			l_temp = strlen (temp);
821136644Sache			substitute_globally++;
822136644Sache			continue;
823136644Sache		      }
824136644Sache		    else if (subst_bywords)
825136644Sache		      {
826136644Sache			si = we;
827136644Sache			l_temp = strlen (temp);
828136644Sache			continue;
829136644Sache		      }
830136644Sache		    else
831136644Sache		      break;
832136644Sache		  }
833136644Sache	      }
834136644Sache
83521308Sache	    if (substitute_globally > 1)
83621308Sache	      {
83721308Sache		substitute_globally = 0;
83821308Sache		continue;	/* don't want to increment i */
83921308Sache	      }
84021308Sache
84121308Sache	    if (failed == 0)
84221308Sache	      continue;		/* don't want to increment i */
84321308Sache
84421308Sache	    *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
84521308Sache	    free (result);
84621308Sache	    free (temp);
84721308Sache	    return (-1);
84821308Sache	  }
84921308Sache	}
85021308Sache      i += 2;
85121308Sache    }
85221308Sache  /* Done with modfiers. */
85321308Sache  /* Believe it or not, we have to back the pointer up by one. */
85421308Sache  --i;
85521308Sache
85621308Sache  if (want_quotes)
85721308Sache    {
85821308Sache      char *x;
85921308Sache
86021308Sache      if (want_quotes == 'q')
86175406Sache	x = sh_single_quote (temp);
86221308Sache      else if (want_quotes == 'x')
86321308Sache	x = quote_breaks (temp);
86421308Sache      else
86521308Sache	x = savestring (temp);
86621308Sache
86721308Sache      free (temp);
86821308Sache      temp = x;
86921308Sache    }
87021308Sache
87121308Sache  n = strlen (temp);
87221308Sache  if (n >= result_len)
873119610Sache    result = (char *)xrealloc (result, n + 2);
87421308Sache  strcpy (result, temp);
87521308Sache  free (temp);
87621308Sache
87721308Sache  *end_index_ptr = i;
87821308Sache  *ret_string = result;
87921308Sache  return (print_only);
88021308Sache}
88121308Sache
88221308Sache/* Expand the string STRING, placing the result into OUTPUT, a pointer
88321308Sache   to a string.  Returns:
88421308Sache
88521308Sache  -1) If there was an error in expansion.
88621308Sache   0) If no expansions took place (or, if the only change in
88721308Sache      the text was the de-slashifying of the history expansion
88821308Sache      character)
88921308Sache   1) If expansions did take place
89021308Sache   2) If the `p' modifier was given and the caller should print the result
89121308Sache
89221308Sache  If an error ocurred in expansion, then OUTPUT contains a descriptive
89321308Sache  error message. */
89421308Sache
89521308Sache#define ADD_STRING(s) \
89621308Sache	do \
89721308Sache	  { \
89821308Sache	    int sl = strlen (s); \
89921308Sache	    j += sl; \
90021308Sache	    if (j >= result_len) \
90121308Sache	      { \
90221308Sache		while (j >= result_len) \
90321308Sache		  result_len += 128; \
904119610Sache		result = (char *)xrealloc (result, result_len); \
90521308Sache	      } \
90621308Sache	    strcpy (result + j - sl, s); \
90721308Sache	  } \
90821308Sache	while (0)
90921308Sache
91021308Sache#define ADD_CHAR(c) \
91121308Sache	do \
91221308Sache	  { \
91321308Sache	    if (j >= result_len - 1) \
914119610Sache	      result = (char *)xrealloc (result, result_len += 64); \
91521308Sache	    result[j++] = c; \
91621308Sache	    result[j] = '\0'; \
91721308Sache	  } \
91821308Sache	while (0)
91921308Sache
92021308Sacheint
92121308Sachehistory_expand (hstring, output)
92221308Sache     char *hstring;
92321308Sache     char **output;
92421308Sache{
92521308Sache  register int j;
926136644Sache  int i, r, l, passc, cc, modified, eindex, only_printing, dquote;
92721308Sache  char *string;
92821308Sache
92921308Sache  /* The output string, and its length. */
93021308Sache  int result_len;
93121308Sache  char *result;
93221308Sache
933119610Sache#if defined (HANDLE_MULTIBYTE)
934119610Sache  char mb[MB_LEN_MAX];
935119610Sache  mbstate_t ps;
936119610Sache#endif
937119610Sache
93821308Sache  /* Used when adding the string. */
93921308Sache  char *temp;
94021308Sache
94175406Sache  if (output == 0)
94275406Sache    return 0;
94375406Sache
94421308Sache  /* Setting the history expansion character to 0 inhibits all
94521308Sache     history expansion. */
94621308Sache  if (history_expansion_char == 0)
94721308Sache    {
94821308Sache      *output = savestring (hstring);
94921308Sache      return (0);
95021308Sache    }
95121308Sache
95221308Sache  /* Prepare the buffer for printing error messages. */
953119610Sache  result = (char *)xmalloc (result_len = 256);
95421308Sache  result[0] = '\0';
95521308Sache
95621308Sache  only_printing = modified = 0;
95721308Sache  l = strlen (hstring);
95821308Sache
95935486Sache  /* Grovel the string.  Only backslash and single quotes can quote the
96035486Sache     history escape character.  We also handle arg specifiers. */
96121308Sache
96221308Sache  /* Before we grovel forever, see if the history_expansion_char appears
96321308Sache     anywhere within the text. */
96421308Sache
96521308Sache  /* The quick substitution character is a history expansion all right.  That
96621308Sache     is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
96721308Sache     that is the substitution that we do. */
96821308Sache  if (hstring[0] == history_subst_char)
96921308Sache    {
970119610Sache      string = (char *)xmalloc (l + 5);
97121308Sache
97221308Sache      string[0] = string[1] = history_expansion_char;
97321308Sache      string[2] = ':';
97421308Sache      string[3] = 's';
97521308Sache      strcpy (string + 4, hstring);
97621308Sache      l += 4;
97721308Sache    }
97821308Sache  else
97921308Sache    {
980119610Sache#if defined (HANDLE_MULTIBYTE)
981119610Sache      memset (&ps, 0, sizeof (mbstate_t));
982119610Sache#endif
983119610Sache
98421308Sache      string = hstring;
98521308Sache      /* If not quick substitution, still maybe have to do expansion. */
98621308Sache
98721308Sache      /* `!' followed by one of the characters in history_no_expand_chars
98821308Sache	 is NOT an expansion. */
989136644Sache      for (i = dquote = 0; string[i]; i++)
99021308Sache	{
991119610Sache#if defined (HANDLE_MULTIBYTE)
992119610Sache	  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
993119610Sache	    {
994119610Sache	      int v;
995119610Sache	      v = _rl_get_char_len (string + i, &ps);
996119610Sache	      if (v > 1)
997119610Sache		{
998119610Sache		  i += v - 1;
999119610Sache		  continue;
1000119610Sache		}
1001119610Sache	    }
1002119610Sache#endif /* HANDLE_MULTIBYTE */
1003119610Sache
100421308Sache	  cc = string[i + 1];
1005119610Sache	  /* The history_comment_char, if set, appearing at the beginning
100635486Sache	     of a word signifies that the rest of the line should not have
100735486Sache	     history expansion performed on it.
100835486Sache	     Skip the rest of the line and break out of the loop. */
100935486Sache	  if (history_comment_char && string[i] == history_comment_char &&
101075406Sache	      (i == 0 || member (string[i - 1], history_word_delimiters)))
101121308Sache	    {
101235486Sache	      while (string[i])
101335486Sache		i++;
101435486Sache	      break;
101535486Sache	    }
101635486Sache	  else if (string[i] == history_expansion_char)
101735486Sache	    {
101821308Sache	      if (!cc || member (cc, history_no_expand_chars))
101921308Sache		continue;
102026497Sache	      /* If the calling application has set
102126497Sache		 history_inhibit_expansion_function to a function that checks
102226497Sache		 for special cases that should not be history expanded,
102326497Sache		 call the function and skip the expansion if it returns a
102426497Sache		 non-zero value. */
102526497Sache	      else if (history_inhibit_expansion_function &&
102626497Sache			(*history_inhibit_expansion_function) (string, i))
102721308Sache		continue;
102821308Sache	      else
102921308Sache		break;
103021308Sache	    }
1031136644Sache	  /* Shell-like quoting: allow backslashes to quote double quotes
1032136644Sache	     inside a double-quoted string. */
1033136644Sache	  else if (dquote && string[i] == '\\' && cc == '"')
1034136644Sache	    i++;
1035136644Sache	  /* More shell-like quoting:  if we're paying attention to single
1036136644Sache	     quotes and letting them quote the history expansion character,
1037136644Sache	     then we need to pay attention to double quotes, because single
1038136644Sache	     quotes are not special inside double-quoted strings. */
1039136644Sache	  else if (history_quotes_inhibit_expansion && string[i] == '"')
104021308Sache	    {
1041136644Sache	      dquote = 1 - dquote;
1042136644Sache	    }
1043136644Sache	  else if (dquote == 0 && history_quotes_inhibit_expansion && string[i] == '\'')
1044136644Sache	    {
104521308Sache	      /* If this is bash, single quotes inhibit history expansion. */
104621308Sache	      i++;
104721308Sache	      hist_string_extract_single_quoted (string, &i);
104821308Sache	    }
104921308Sache	  else if (history_quotes_inhibit_expansion && string[i] == '\\')
105021308Sache	    {
105121308Sache	      /* If this is bash, allow backslashes to quote single
105221308Sache		 quotes and the history expansion character. */
105321308Sache	      if (cc == '\'' || cc == history_expansion_char)
105421308Sache		i++;
105521308Sache	    }
1056136644Sache
105721308Sache	}
105821308Sache
105921308Sache      if (string[i] != history_expansion_char)
106021308Sache	{
106121308Sache	  free (result);
106221308Sache	  *output = savestring (string);
106321308Sache	  return (0);
106421308Sache	}
106521308Sache    }
106621308Sache
106721308Sache  /* Extract and perform the substitution. */
1068136644Sache  for (passc = dquote = i = j = 0; i < l; i++)
106921308Sache    {
107021308Sache      int tchar = string[i];
107121308Sache
107221308Sache      if (passc)
107321308Sache	{
107421308Sache	  passc = 0;
107521308Sache	  ADD_CHAR (tchar);
107621308Sache	  continue;
107721308Sache	}
107821308Sache
1079119610Sache#if defined (HANDLE_MULTIBYTE)
1080119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1081119610Sache	{
1082119610Sache	  int k, c;
1083119610Sache
1084119610Sache	  c = tchar;
1085119610Sache	  memset (mb, 0, sizeof (mb));
1086119610Sache	  for (k = 0; k < MB_LEN_MAX; k++)
1087119610Sache	    {
1088119610Sache	      mb[k] = (char)c;
1089119610Sache	      memset (&ps, 0, sizeof (mbstate_t));
1090119610Sache	      if (_rl_get_char_len (mb, &ps) == -2)
1091119610Sache		c = string[++i];
1092119610Sache	      else
1093119610Sache		break;
1094119610Sache	    }
1095119610Sache	  if (strlen (mb) > 1)
1096119610Sache	    {
1097119610Sache	      ADD_STRING (mb);
1098119610Sache	      break;
1099119610Sache	    }
1100119610Sache	}
1101119610Sache#endif /* HANDLE_MULTIBYTE */
1102119610Sache
110321308Sache      if (tchar == history_expansion_char)
110421308Sache	tchar = -3;
110535486Sache      else if (tchar == history_comment_char)
110635486Sache	tchar = -2;
110721308Sache
110821308Sache      switch (tchar)
110921308Sache	{
111021308Sache	default:
111121308Sache	  ADD_CHAR (string[i]);
111221308Sache	  break;
111321308Sache
111421308Sache	case '\\':
111521308Sache	  passc++;
111621308Sache	  ADD_CHAR (tchar);
111721308Sache	  break;
111821308Sache
1119136644Sache	case '"':
1120136644Sache	  dquote = 1 - dquote;
1121136644Sache	  ADD_CHAR (tchar);
1122136644Sache	  break;
1123136644Sache
112421308Sache	case '\'':
112521308Sache	  {
112621308Sache	    /* If history_quotes_inhibit_expansion is set, single quotes
112721308Sache	       inhibit history expansion. */
1128136644Sache	    if (dquote == 0 && history_quotes_inhibit_expansion)
112921308Sache	      {
113021308Sache		int quote, slen;
113121308Sache
113221308Sache		quote = i++;
113321308Sache		hist_string_extract_single_quoted (string, &i);
113421308Sache
113521308Sache		slen = i - quote + 2;
1136119610Sache		temp = (char *)xmalloc (slen);
113721308Sache		strncpy (temp, string + quote, slen);
113821308Sache		temp[slen - 1] = '\0';
113921308Sache		ADD_STRING (temp);
114021308Sache		free (temp);
114121308Sache	      }
114221308Sache	    else
114321308Sache	      ADD_CHAR (string[i]);
114421308Sache	    break;
114521308Sache	  }
114621308Sache
114735486Sache	case -2:		/* history_comment_char */
114875406Sache	  if (i == 0 || member (string[i - 1], history_word_delimiters))
114935486Sache	    {
1150119610Sache	      temp = (char *)xmalloc (l - i + 1);
115135486Sache	      strcpy (temp, string + i);
115235486Sache	      ADD_STRING (temp);
115335486Sache	      free (temp);
115435486Sache	      i = l;
115535486Sache	    }
115635486Sache	  else
115735486Sache	    ADD_CHAR (string[i]);
115835486Sache	  break;
115935486Sache
116021308Sache	case -3:		/* history_expansion_char */
116121308Sache	  cc = string[i + 1];
116221308Sache
116321308Sache	  /* If the history_expansion_char is followed by one of the
116421308Sache	     characters in history_no_expand_chars, then it is not a
116521308Sache	     candidate for expansion of any kind. */
116621308Sache	  if (member (cc, history_no_expand_chars))
116721308Sache	    {
116821308Sache	      ADD_CHAR (string[i]);
116921308Sache	      break;
117021308Sache	    }
117121308Sache
117221308Sache#if defined (NO_BANG_HASH_MODIFIERS)
117321308Sache	  /* There is something that is listed as a `word specifier' in csh
117421308Sache	     documentation which means `the expanded text to this point'.
117521308Sache	     That is not a word specifier, it is an event specifier.  If we
117621308Sache	     don't want to allow modifiers with `!#', just stick the current
117721308Sache	     output line in again. */
117821308Sache	  if (cc == '#')
117921308Sache	    {
118021308Sache	      if (result)
118121308Sache		{
1182119610Sache		  temp = (char *)xmalloc (1 + strlen (result));
118321308Sache		  strcpy (temp, result);
118421308Sache		  ADD_STRING (temp);
118521308Sache		  free (temp);
118621308Sache		}
118721308Sache	      i++;
118821308Sache	      break;
118921308Sache	    }
119021308Sache#endif
119121308Sache
119221308Sache	  r = history_expand_internal (string, i, &eindex, &temp, result);
119321308Sache	  if (r < 0)
119421308Sache	    {
119521308Sache	      *output = temp;
119621308Sache	      free (result);
119721308Sache	      if (string != hstring)
119821308Sache		free (string);
119921308Sache	      return -1;
120021308Sache	    }
120121308Sache	  else
120221308Sache	    {
120321308Sache	      if (temp)
120421308Sache		{
120521308Sache		  modified++;
120621308Sache		  if (*temp)
120721308Sache		    ADD_STRING (temp);
120821308Sache		  free (temp);
120921308Sache		}
121021308Sache	      only_printing = r == 1;
121121308Sache	      i = eindex;
121221308Sache	    }
121321308Sache	  break;
121421308Sache	}
121521308Sache    }
121621308Sache
121721308Sache  *output = result;
121821308Sache  if (string != hstring)
121921308Sache    free (string);
122021308Sache
122121308Sache  if (only_printing)
122221308Sache    {
1223136644Sache#if 0
122421308Sache      add_history (result);
1225136644Sache#endif
122621308Sache      return (2);
122721308Sache    }
122821308Sache
122921308Sache  return (modified != 0);
123021308Sache}
123121308Sache
123221308Sache/* Return a consed string which is the word specified in SPEC, and found
123321308Sache   in FROM.  NULL is returned if there is no spec.  The address of
123421308Sache   ERROR_POINTER is returned if the word specified cannot be found.
123521308Sache   CALLER_INDEX is the offset in SPEC to start looking; it is updated
123621308Sache   to point to just after the last character parsed. */
123721308Sachestatic char *
123821308Sacheget_history_word_specifier (spec, from, caller_index)
123921308Sache     char *spec, *from;
124021308Sache     int *caller_index;
124121308Sache{
124221308Sache  register int i = *caller_index;
124321308Sache  int first, last;
124421308Sache  int expecting_word_spec = 0;
124521308Sache  char *result;
124621308Sache
124721308Sache  /* The range of words to return doesn't exist yet. */
124821308Sache  first = last = 0;
124921308Sache  result = (char *)NULL;
125021308Sache
125121308Sache  /* If we found a colon, then this *must* be a word specification.  If
125221308Sache     it isn't, then it is an error. */
125321308Sache  if (spec[i] == ':')
125421308Sache    {
125521308Sache      i++;
125621308Sache      expecting_word_spec++;
125721308Sache    }
125821308Sache
125921308Sache  /* Handle special cases first. */
126021308Sache
126121308Sache  /* `%' is the word last searched for. */
126221308Sache  if (spec[i] == '%')
126321308Sache    {
126421308Sache      *caller_index = i + 1;
126521308Sache      return (search_match ? savestring (search_match) : savestring (""));
126621308Sache    }
126721308Sache
126821308Sache  /* `*' matches all of the arguments, but not the command. */
126921308Sache  if (spec[i] == '*')
127021308Sache    {
127121308Sache      *caller_index = i + 1;
127221308Sache      result = history_arg_extract (1, '$', from);
127321308Sache      return (result ? result : savestring (""));
127421308Sache    }
127521308Sache
127621308Sache  /* `$' is last arg. */
127721308Sache  if (spec[i] == '$')
127821308Sache    {
127921308Sache      *caller_index = i + 1;
128021308Sache      return (history_arg_extract ('$', '$', from));
128121308Sache    }
128221308Sache
128321308Sache  /* Try to get FIRST and LAST figured out. */
128421308Sache
128521308Sache  if (spec[i] == '-')
128621308Sache    first = 0;
128721308Sache  else if (spec[i] == '^')
1288136644Sache    {
1289136644Sache      first = 1;
1290136644Sache      i++;
1291136644Sache    }
129221308Sache  else if (_rl_digit_p (spec[i]) && expecting_word_spec)
129321308Sache    {
129421308Sache      for (first = 0; _rl_digit_p (spec[i]); i++)
129521308Sache	first = (first * 10) + _rl_digit_value (spec[i]);
129621308Sache    }
129721308Sache  else
129821308Sache    return ((char *)NULL);	/* no valid `first' for word specifier */
129921308Sache
130021308Sache  if (spec[i] == '^' || spec[i] == '*')
130121308Sache    {
130221308Sache      last = (spec[i] == '^') ? 1 : '$';	/* x* abbreviates x-$ */
130321308Sache      i++;
130421308Sache    }
130521308Sache  else if (spec[i] != '-')
130621308Sache    last = first;
130721308Sache  else
130821308Sache    {
130921308Sache      i++;
131021308Sache
131121308Sache      if (_rl_digit_p (spec[i]))
131221308Sache	{
131321308Sache	  for (last = 0; _rl_digit_p (spec[i]); i++)
131421308Sache	    last = (last * 10) + _rl_digit_value (spec[i]);
131521308Sache	}
131621308Sache      else if (spec[i] == '$')
131721308Sache	{
131821308Sache	  i++;
131921308Sache	  last = '$';
132021308Sache	}
1321119610Sache#if 0
1322119610Sache      else if (!spec[i] || spec[i] == ':')
1323119610Sache	/* check against `:' because there could be a modifier separator */
1324119610Sache#else
1325119610Sache      else
1326119610Sache	/* csh seems to allow anything to terminate the word spec here,
1327119610Sache	   leaving it as an abbreviation. */
1328119610Sache#endif
132921308Sache	last = -1;		/* x- abbreviates x-$ omitting word `$' */
133021308Sache    }
133121308Sache
133221308Sache  *caller_index = i;
133321308Sache
133421308Sache  if (last >= first || last == '$' || last < 0)
133521308Sache    result = history_arg_extract (first, last, from);
133621308Sache
133721308Sache  return (result ? result : (char *)&error_pointer);
133821308Sache}
133921308Sache
134021308Sache/* Extract the args specified, starting at FIRST, and ending at LAST.
134121308Sache   The args are taken from STRING.  If either FIRST or LAST is < 0,
134221308Sache   then make that arg count from the right (subtract from the number of
134321308Sache   tokens, so that FIRST = -1 means the next to last token on the line).
134421308Sache   If LAST is `$' the last arg from STRING is used. */
134521308Sachechar *
134621308Sachehistory_arg_extract (first, last, string)
134721308Sache     int first, last;
134875406Sache     const char *string;
134921308Sache{
135021308Sache  register int i, len;
135121308Sache  char *result;
135221308Sache  int size, offset;
135321308Sache  char **list;
135421308Sache
135521308Sache  /* XXX - think about making history_tokenize return a struct array,
135621308Sache     each struct in array being a string and a length to avoid the
135721308Sache     calls to strlen below. */
135821308Sache  if ((list = history_tokenize (string)) == NULL)
135921308Sache    return ((char *)NULL);
136021308Sache
136121308Sache  for (len = 0; list[len]; len++)
136221308Sache    ;
136321308Sache
136421308Sache  if (last < 0)
136521308Sache    last = len + last - 1;
136621308Sache
136721308Sache  if (first < 0)
136821308Sache    first = len + first - 1;
136921308Sache
137021308Sache  if (last == '$')
137121308Sache    last = len - 1;
137221308Sache
137321308Sache  if (first == '$')
137421308Sache    first = len - 1;
137521308Sache
137621308Sache  last++;
137721308Sache
137821308Sache  if (first >= len || last > len || first < 0 || last < 0 || first > last)
137921308Sache    result = ((char *)NULL);
138021308Sache  else
138121308Sache    {
138221308Sache      for (size = 0, i = first; i < last; i++)
138321308Sache	size += strlen (list[i]) + 1;
1384119610Sache      result = (char *)xmalloc (size + 1);
138521308Sache      result[0] = '\0';
138621308Sache
138721308Sache      for (i = first, offset = 0; i < last; i++)
138821308Sache	{
138921308Sache	  strcpy (result + offset, list[i]);
139021308Sache	  offset += strlen (list[i]);
139121308Sache	  if (i + 1 < last)
139221308Sache	    {
139321308Sache      	      result[offset++] = ' ';
139421308Sache	      result[offset] = 0;
139521308Sache	    }
139621308Sache	}
139721308Sache    }
139821308Sache
139921308Sache  for (i = 0; i < len; i++)
140021308Sache    free (list[i]);
140121308Sache  free (list);
140221308Sache
140321308Sache  return (result);
140421308Sache}
140521308Sache
1406136644Sachestatic int
1407136644Sachehistory_tokenize_word (string, ind)
1408136644Sache     const char *string;
1409136644Sache     int ind;
1410136644Sache{
1411136644Sache  register int i;
1412136644Sache  int delimiter;
141321308Sache
1414136644Sache  i = ind;
1415136644Sache  delimiter = 0;
1416136644Sache
1417136644Sache  if (member (string[i], "()\n"))
1418136644Sache    {
1419136644Sache      i++;
1420136644Sache      return i;
1421136644Sache    }
1422136644Sache
1423136644Sache  if (member (string[i], "<>;&|$"))
1424136644Sache    {
1425136644Sache      int peek = string[i + 1];
1426136644Sache
1427136644Sache      if (peek == string[i] && peek != '$')
1428136644Sache	{
1429136644Sache	  if (peek == '<' && string[i + 2] == '-')
1430136644Sache	    i++;
1431165670Sache	  else if (peek == '<' && string[i + 2] == '<')
1432165670Sache	    i++;
1433136644Sache	  i += 2;
1434136644Sache	  return i;
1435136644Sache	}
1436136644Sache      else
1437136644Sache	{
1438136644Sache	  if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
1439136644Sache	      (peek == '>' && string[i] == '&') ||
1440136644Sache	      (peek == '(' && (string[i] == '>' || string[i] == '<')) || /* ) */
1441136644Sache	      (peek == '(' && string[i] == '$')) /* ) */
1442136644Sache	    {
1443136644Sache	      i += 2;
1444136644Sache	      return i;
1445136644Sache	    }
1446136644Sache	}
1447136644Sache
1448136644Sache      if (string[i] != '$')
1449136644Sache	{
1450136644Sache	  i++;
1451136644Sache	  return i;
1452136644Sache	}
1453136644Sache    }
1454136644Sache
1455136644Sache  /* Get word from string + i; */
1456136644Sache
1457136644Sache  if (member (string[i], HISTORY_QUOTE_CHARACTERS))
1458136644Sache    delimiter = string[i++];
1459136644Sache
1460136644Sache  for (; string[i]; i++)
1461136644Sache    {
1462136644Sache      if (string[i] == '\\' && string[i + 1] == '\n')
1463136644Sache	{
1464136644Sache	  i++;
1465136644Sache	  continue;
1466136644Sache	}
1467136644Sache
1468136644Sache      if (string[i] == '\\' && delimiter != '\'' &&
1469136644Sache	  (delimiter != '"' || member (string[i], slashify_in_quotes)))
1470136644Sache	{
1471136644Sache	  i++;
1472136644Sache	  continue;
1473136644Sache	}
1474136644Sache
1475136644Sache      if (delimiter && string[i] == delimiter)
1476136644Sache	{
1477136644Sache	  delimiter = 0;
1478136644Sache	  continue;
1479136644Sache	}
1480136644Sache
1481136644Sache      if (!delimiter && (member (string[i], history_word_delimiters)))
1482136644Sache	break;
1483136644Sache
1484136644Sache      if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS))
1485136644Sache	delimiter = string[i];
1486136644Sache    }
1487136644Sache
1488136644Sache  return i;
1489136644Sache}
1490136644Sache
1491136644Sachestatic char *
1492136644Sachehistory_substring (string, start, end)
1493136644Sache     const char *string;
1494136644Sache     int start, end;
1495136644Sache{
1496136644Sache  register int len;
1497136644Sache  register char *result;
1498136644Sache
1499136644Sache  len = end - start;
1500136644Sache  result = (char *)xmalloc (len + 1);
1501136644Sache  strncpy (result, string + start, len);
1502136644Sache  result[len] = '\0';
1503136644Sache  return result;
1504136644Sache}
1505136644Sache
150621308Sache/* Parse STRING into tokens and return an array of strings.  If WIND is
150721308Sache   not -1 and INDP is not null, we also want the word surrounding index
150821308Sache   WIND.  The position in the returned array of strings is returned in
150921308Sache   *INDP. */
151021308Sachestatic char **
151121308Sachehistory_tokenize_internal (string, wind, indp)
151275406Sache     const char *string;
151321308Sache     int wind, *indp;
151421308Sache{
151521308Sache  char **result;
151621308Sache  register int i, start, result_index, size;
151721308Sache
151875406Sache  /* If we're searching for a string that's not part of a word (e.g., " "),
151975406Sache     make sure we set *INDP to a reasonable value. */
152075406Sache  if (indp && wind != -1)
152175406Sache    *indp = -1;
152275406Sache
152321308Sache  /* Get a token, and stuff it into RESULT.  The tokens are split
152421308Sache     exactly where the shell would split them. */
152521308Sache  for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
152621308Sache    {
152721308Sache      /* Skip leading whitespace. */
152821308Sache      for (; string[i] && whitespace (string[i]); i++)
152921308Sache	;
153021308Sache      if (string[i] == 0 || string[i] == history_comment_char)
153121308Sache	return (result);
153221308Sache
153321308Sache      start = i;
153421308Sache
1535136644Sache      i = history_tokenize_word (string, start);
153621308Sache
1537136644Sache      /* If we have a non-whitespace delimiter character (which would not be
1538136644Sache	 skipped by the loop above), use it and any adjacent delimiters to
1539136644Sache	 make a separate field.  Any adjacent white space will be skipped the
1540136644Sache	 next time through the loop. */
1541136644Sache      if (i == start && history_word_delimiters)
154221308Sache	{
1543136644Sache	  i++;
1544136644Sache	  while (string[i] && member (string[i], history_word_delimiters))
1545136644Sache	    i++;
154621308Sache	}
154721308Sache
154821308Sache      /* If we are looking for the word in which the character at a
154921308Sache	 particular index falls, remember it. */
155021308Sache      if (indp && wind != -1 && wind >= start && wind < i)
155121308Sache        *indp = result_index;
155221308Sache
155321308Sache      if (result_index + 2 >= size)
155421308Sache	result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
1555136644Sache
1556136644Sache      result[result_index++] = history_substring (string, start, i);
1557136644Sache      result[result_index] = (char *)NULL;
155821308Sache    }
155921308Sache
156021308Sache  return (result);
156121308Sache}
156221308Sache
156321308Sache/* Return an array of tokens, much as the shell might.  The tokens are
156421308Sache   parsed out of STRING. */
156521308Sachechar **
156621308Sachehistory_tokenize (string)
156775406Sache     const char *string;
156821308Sache{
156921308Sache  return (history_tokenize_internal (string, -1, (int *)NULL));
157021308Sache}
157121308Sache
157221308Sache/* Find and return the word which contains the character at index IND
157321308Sache   in the history line LINE.  Used to save the word matched by the
157421308Sache   last history !?string? search. */
157521308Sachestatic char *
157621308Sachehistory_find_word (line, ind)
157721308Sache     char *line;
157821308Sache     int ind;
157921308Sache{
158021308Sache  char **words, *s;
158121308Sache  int i, wind;
158221308Sache
158321308Sache  words = history_tokenize_internal (line, ind, &wind);
158475406Sache  if (wind == -1 || words == 0)
158521308Sache    return ((char *)NULL);
158621308Sache  s = words[wind];
158721308Sache  for (i = 0; i < wind; i++)
158821308Sache    free (words[i]);
158921308Sache  for (i = wind + 1; words[i]; i++)
159021308Sache    free (words[i]);
159121308Sache  free (words);
159221308Sache  return s;
159321308Sache}
1594