histexpand.c revision 58310
121308Sache/* histexpand.c -- history expansion. */
221308Sache
321308Sache/* Copyright (C) 1989, 1992 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
4421308Sache#if defined (HAVE_STRING_H)
4521308Sache#  include <string.h>
4621308Sache#else
4721308Sache#  include <strings.h>
4821308Sache#endif /* !HAVE_STRING_H */
4921308Sache
5021308Sache#include "history.h"
5121308Sache#include "histlib.h"
5221308Sache
5358310Sache#include "rlshell.h"
5458310Sache#include "xmalloc.h"
5558310Sache
5635486Sache#define HISTORY_WORD_DELIMITERS		" \t\n;&()|<>"
5735486Sache#define HISTORY_QUOTE_CHARACTERS	"\"'`"
5835486Sache
5921308Sachestatic char error_pointer;
6021308Sache
6121308Sachestatic char *subst_lhs;
6221308Sachestatic char *subst_rhs;
6321308Sachestatic int subst_lhs_len;
6421308Sachestatic int subst_rhs_len;
6521308Sache
6658310Sachestatic char *get_history_word_specifier __P((char *, char *, int *));
6758310Sachestatic char *history_find_word __P((char *, int));
6821308Sache
6958310Sachestatic char *quote_breaks __P((char *));
7021308Sache
7121308Sache/* Variables exported by this file. */
7221308Sache/* The character that represents the start of a history expansion
7321308Sache   request.  This is usually `!'. */
7421308Sachechar history_expansion_char = '!';
7521308Sache
7621308Sache/* The character that invokes word substitution if found at the start of
7721308Sache   a line.  This is usually `^'. */
7821308Sachechar history_subst_char = '^';
7921308Sache
8021308Sache/* During tokenization, if this character is seen as the first character
8121308Sache   of a word, then it, and all subsequent characters upto a newline are
8221308Sache   ignored.  For a Bourne shell, this should be '#'.  Bash special cases
8321308Sache   the interactive comment character to not be a comment delimiter. */
8421308Sachechar history_comment_char = '\0';
8521308Sache
8621308Sache/* The list of characters which inhibit the expansion of text if found
8721308Sache   immediately following history_expansion_char. */
8821308Sachechar *history_no_expand_chars = " \t\n\r=";
8921308Sache
9021308Sache/* If set to a non-zero value, single quotes inhibit history expansion.
9121308Sache   The default is 0. */
9221308Sacheint history_quotes_inhibit_expansion = 0;
9321308Sache
9426497Sache/* If set, this points to a function that is called to verify that a
9526497Sache   particular history expansion should be performed. */
9626497SacheFunction *history_inhibit_expansion_function;
9726497Sache
9821308Sache/* **************************************************************** */
9921308Sache/*								    */
10021308Sache/*			History Expansion			    */
10121308Sache/*								    */
10221308Sache/* **************************************************************** */
10321308Sache
10421308Sache/* Hairy history expansion on text, not tokens.  This is of general
10521308Sache   use, and thus belongs in this library. */
10621308Sache
10721308Sache/* The last string searched for by a !?string? search. */
10821308Sachestatic char *search_string;
10921308Sache
11021308Sache/* The last string matched by a !?string? search. */
11121308Sachestatic char *search_match;
11221308Sache
11321308Sache/* Return the event specified at TEXT + OFFSET modifying OFFSET to
11421308Sache   point to after the event specifier.  Just a pointer to the history
11521308Sache   line is returned; NULL is returned in the event of a bad specifier.
11621308Sache   You pass STRING with *INDEX equal to the history_expansion_char that
11721308Sache   begins this specification.
11821308Sache   DELIMITING_QUOTE is a character that is allowed to end the string
11921308Sache   specification for what to search for in addition to the normal
12021308Sache   characters `:', ` ', `\t', `\n', and sometimes `?'.
12121308Sache   So you might call this function like:
12221308Sache   line = get_history_event ("!echo:p", &index, 0);  */
12321308Sachechar *
12421308Sacheget_history_event (string, caller_index, delimiting_quote)
12521308Sache     char *string;
12621308Sache     int *caller_index;
12721308Sache     int delimiting_quote;
12821308Sache{
12921308Sache  register int i;
13021308Sache  register char c;
13121308Sache  HIST_ENTRY *entry;
13221308Sache  int which, sign, local_index, substring_okay;
13321308Sache  Function *search_func;
13421308Sache  char *temp;
13521308Sache
13621308Sache  /* The event can be specified in a number of ways.
13721308Sache
13821308Sache     !!   the previous command
13921308Sache     !n   command line N
14021308Sache     !-n  current command-line minus N
14121308Sache     !str the most recent command starting with STR
14221308Sache     !?str[?]
14321308Sache	  the most recent command containing STR
14421308Sache
14521308Sache     All values N are determined via HISTORY_BASE. */
14621308Sache
14721308Sache  i = *caller_index;
14821308Sache
14921308Sache  if (string[i] != history_expansion_char)
15021308Sache    return ((char *)NULL);
15121308Sache
15221308Sache  /* Move on to the specification. */
15321308Sache  i++;
15421308Sache
15521308Sache  sign = 1;
15621308Sache  substring_okay = 0;
15721308Sache
15821308Sache#define RETURN_ENTRY(e, w) \
15921308Sache	return ((e = history_get (w)) ? e->line : (char *)NULL)
16021308Sache
16121308Sache  /* Handle !! case. */
16221308Sache  if (string[i] == history_expansion_char)
16321308Sache    {
16421308Sache      i++;
16521308Sache      which = history_base + (history_length - 1);
16621308Sache      *caller_index = i;
16721308Sache      RETURN_ENTRY (entry, which);
16821308Sache    }
16921308Sache
17021308Sache  /* Hack case of numeric line specification. */
17121308Sache  if (string[i] == '-')
17221308Sache    {
17321308Sache      sign = -1;
17421308Sache      i++;
17521308Sache    }
17621308Sache
17721308Sache  if (_rl_digit_p (string[i]))
17821308Sache    {
17921308Sache      /* Get the extent of the digits and compute the value. */
18021308Sache      for (which = 0; _rl_digit_p (string[i]); i++)
18121308Sache	which = (which * 10) + _rl_digit_value (string[i]);
18221308Sache
18321308Sache      *caller_index = i;
18421308Sache
18521308Sache      if (sign < 0)
18621308Sache	which = (history_length + history_base) - which;
18721308Sache
18821308Sache      RETURN_ENTRY (entry, which);
18921308Sache    }
19021308Sache
19121308Sache  /* This must be something to search for.  If the spec begins with
19221308Sache     a '?', then the string may be anywhere on the line.  Otherwise,
19321308Sache     the string must be found at the start of a line. */
19421308Sache  if (string[i] == '?')
19521308Sache    {
19621308Sache      substring_okay++;
19721308Sache      i++;
19821308Sache    }
19921308Sache
20021308Sache  /* Only a closing `?' or a newline delimit a substring search string. */
20121308Sache  for (local_index = i; c = string[i]; i++)
20221308Sache    if ((!substring_okay && (whitespace (c) || c == ':' ||
20321308Sache	(history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
20421308Sache	string[i] == delimiting_quote)) ||
20521308Sache	string[i] == '\n' ||
20621308Sache	(substring_okay && string[i] == '?'))
20721308Sache      break;
20821308Sache
20921308Sache  which = i - local_index;
21021308Sache  temp = xmalloc (1 + which);
21121308Sache  if (which)
21221308Sache    strncpy (temp, string + local_index, which);
21321308Sache  temp[which] = '\0';
21421308Sache
21521308Sache  if (substring_okay && string[i] == '?')
21621308Sache    i++;
21721308Sache
21821308Sache  *caller_index = i;
21921308Sache
22021308Sache#define FAIL_SEARCH() \
22121308Sache  do { \
22221308Sache    history_offset = history_length; free (temp) ; return (char *)NULL; \
22321308Sache  } while (0)
22421308Sache
22521308Sache  /* If there is no search string, try to use the previous search string,
22621308Sache     if one exists.  If not, fail immediately. */
22721308Sache  if (*temp == '\0' && substring_okay)
22821308Sache    {
22921308Sache      if (search_string)
23021308Sache        {
23121308Sache          free (temp);
23221308Sache          temp = savestring (search_string);
23321308Sache        }
23421308Sache      else
23521308Sache        FAIL_SEARCH ();
23621308Sache    }
23721308Sache
23821308Sache  search_func = substring_okay ? history_search : history_search_prefix;
23921308Sache  while (1)
24021308Sache    {
24121308Sache      local_index = (*search_func) (temp, -1);
24221308Sache
24321308Sache      if (local_index < 0)
24421308Sache	FAIL_SEARCH ();
24521308Sache
24621308Sache      if (local_index == 0 || substring_okay)
24721308Sache	{
24821308Sache	  entry = current_history ();
24921308Sache	  history_offset = history_length;
25021308Sache
25121308Sache	  /* If this was a substring search, then remember the
25221308Sache	     string that we matched for word substitution. */
25321308Sache	  if (substring_okay)
25421308Sache	    {
25521308Sache	      FREE (search_string);
25621308Sache	      search_string = temp;
25721308Sache
25821308Sache	      FREE (search_match);
25921308Sache	      search_match = history_find_word (entry->line, local_index);
26021308Sache	    }
26121308Sache	  else
26221308Sache	    free (temp);
26321308Sache
26421308Sache	  return (entry->line);
26521308Sache	}
26621308Sache
26721308Sache      if (history_offset)
26821308Sache	history_offset--;
26921308Sache      else
27021308Sache	FAIL_SEARCH ();
27121308Sache    }
27221308Sache#undef FAIL_SEARCH
27321308Sache#undef RETURN_ENTRY
27421308Sache}
27521308Sache
27621308Sache/* Function for extracting single-quoted strings.  Used for inhibiting
27721308Sache   history expansion within single quotes. */
27821308Sache
27921308Sache/* Extract the contents of STRING as if it is enclosed in single quotes.
28021308Sache   SINDEX, when passed in, is the offset of the character immediately
28121308Sache   following the opening single quote; on exit, SINDEX is left pointing
28221308Sache   to the closing single quote. */
28321308Sachestatic void
28421308Sachehist_string_extract_single_quoted (string, sindex)
28521308Sache     char *string;
28621308Sache     int *sindex;
28721308Sache{
28821308Sache  register int i;
28921308Sache
29021308Sache  for (i = *sindex; string[i] && string[i] != '\''; i++)
29121308Sache    ;
29221308Sache
29321308Sache  *sindex = i;
29421308Sache}
29521308Sache
29621308Sachestatic char *
29721308Sachequote_breaks (s)
29821308Sache     char *s;
29921308Sache{
30021308Sache  register char *p, *r;
30121308Sache  char *ret;
30221308Sache  int len = 3;
30321308Sache
30421308Sache  for (p = s; p && *p; p++, len++)
30521308Sache    {
30621308Sache      if (*p == '\'')
30721308Sache	len += 3;
30821308Sache      else if (whitespace (*p) || *p == '\n')
30921308Sache	len += 2;
31021308Sache    }
31121308Sache
31221308Sache  r = ret = xmalloc (len);
31321308Sache  *r++ = '\'';
31421308Sache  for (p = s; p && *p; )
31521308Sache    {
31621308Sache      if (*p == '\'')
31721308Sache	{
31821308Sache	  *r++ = '\'';
31921308Sache	  *r++ = '\\';
32021308Sache	  *r++ = '\'';
32121308Sache	  *r++ = '\'';
32221308Sache	  p++;
32321308Sache	}
32421308Sache      else if (whitespace (*p) || *p == '\n')
32521308Sache	{
32621308Sache	  *r++ = '\'';
32721308Sache	  *r++ = *p++;
32821308Sache	  *r++ = '\'';
32921308Sache	}
33021308Sache      else
33121308Sache	*r++ = *p++;
33221308Sache    }
33321308Sache  *r++ = '\'';
33421308Sache  *r = '\0';
33521308Sache  return ret;
33621308Sache}
33721308Sache
33821308Sachestatic char *
33921308Sachehist_error(s, start, current, errtype)
34021308Sache      char *s;
34121308Sache      int start, current, errtype;
34221308Sache{
34321308Sache  char *temp, *emsg;
34421308Sache  int ll, elen;
34521308Sache
34621308Sache  ll = current - start;
34721308Sache
34821308Sache  switch (errtype)
34921308Sache    {
35021308Sache    case EVENT_NOT_FOUND:
35121308Sache      emsg = "event not found";
35221308Sache      elen = 15;
35321308Sache      break;
35421308Sache    case BAD_WORD_SPEC:
35521308Sache      emsg = "bad word specifier";
35621308Sache      elen = 18;
35721308Sache      break;
35821308Sache    case SUBST_FAILED:
35921308Sache      emsg = "substitution failed";
36021308Sache      elen = 19;
36121308Sache      break;
36221308Sache    case BAD_MODIFIER:
36321308Sache      emsg = "unrecognized history modifier";
36421308Sache      elen = 29;
36521308Sache      break;
36647558Sache    case NO_PREV_SUBST:
36747558Sache      emsg = "no previous substitution";
36847558Sache      elen = 24;
36947558Sache      break;
37021308Sache    default:
37121308Sache      emsg = "unknown expansion error";
37221308Sache      elen = 23;
37321308Sache      break;
37421308Sache    }
37521308Sache
37621308Sache  temp = xmalloc (ll + elen + 3);
37721308Sache  strncpy (temp, s + start, ll);
37821308Sache  temp[ll] = ':';
37921308Sache  temp[ll + 1] = ' ';
38021308Sache  strcpy (temp + ll + 2, emsg);
38121308Sache  return (temp);
38221308Sache}
38321308Sache
38421308Sache/* Get a history substitution string from STR starting at *IPTR
38521308Sache   and return it.  The length is returned in LENPTR.
38621308Sache
38721308Sache   A backslash can quote the delimiter.  If the string is the
38821308Sache   empty string, the previous pattern is used.  If there is
38921308Sache   no previous pattern for the lhs, the last history search
39021308Sache   string is used.
39121308Sache
39221308Sache   If IS_RHS is 1, we ignore empty strings and set the pattern
39321308Sache   to "" anyway.  subst_lhs is not changed if the lhs is empty;
39421308Sache   subst_rhs is allowed to be set to the empty string. */
39521308Sache
39621308Sachestatic char *
39721308Sacheget_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
39821308Sache     char *str;
39921308Sache     int *iptr, delimiter, is_rhs, *lenptr;
40021308Sache{
40121308Sache  register int si, i, j, k;
40221308Sache  char *s = (char *) NULL;
40321308Sache
40421308Sache  i = *iptr;
40521308Sache
40621308Sache  for (si = i; str[si] && str[si] != delimiter; si++)
40721308Sache    if (str[si] == '\\' && str[si + 1] == delimiter)
40821308Sache      si++;
40921308Sache
41021308Sache  if (si > i || is_rhs)
41121308Sache    {
41221308Sache      s = xmalloc (si - i + 1);
41321308Sache      for (j = 0, k = i; k < si; j++, k++)
41421308Sache	{
41521308Sache	  /* Remove a backslash quoting the search string delimiter. */
41621308Sache	  if (str[k] == '\\' && str[k + 1] == delimiter)
41721308Sache	    k++;
41821308Sache	  s[j] = str[k];
41921308Sache	}
42021308Sache      s[j] = '\0';
42121308Sache      if (lenptr)
42221308Sache	*lenptr = j;
42321308Sache    }
42421308Sache
42521308Sache  i = si;
42621308Sache  if (str[i])
42721308Sache    i++;
42821308Sache  *iptr = i;
42921308Sache
43021308Sache  return s;
43121308Sache}
43221308Sache
43321308Sachestatic void
43421308Sachepostproc_subst_rhs ()
43521308Sache{
43621308Sache  char *new;
43721308Sache  int i, j, new_size;
43821308Sache
43921308Sache  new = xmalloc (new_size = subst_rhs_len + subst_lhs_len);
44021308Sache  for (i = j = 0; i < subst_rhs_len; i++)
44121308Sache    {
44221308Sache      if (subst_rhs[i] == '&')
44321308Sache	{
44421308Sache	  if (j + subst_lhs_len >= new_size)
44521308Sache	    new = xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
44621308Sache	  strcpy (new + j, subst_lhs);
44721308Sache	  j += subst_lhs_len;
44821308Sache	}
44921308Sache      else
45021308Sache	{
45121308Sache	  /* a single backslash protects the `&' from lhs interpolation */
45221308Sache	  if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
45321308Sache	    i++;
45421308Sache	  if (j >= new_size)
45521308Sache	    new = xrealloc (new, new_size *= 2);
45621308Sache	  new[j++] = subst_rhs[i];
45721308Sache	}
45821308Sache    }
45921308Sache  new[j] = '\0';
46021308Sache  free (subst_rhs);
46121308Sache  subst_rhs = new;
46221308Sache  subst_rhs_len = j;
46321308Sache}
46421308Sache
46521308Sache/* Expand the bulk of a history specifier starting at STRING[START].
46621308Sache   Returns 0 if everything is OK, -1 if an error occurred, and 1
46721308Sache   if the `p' modifier was supplied and the caller should just print
46821308Sache   the returned string.  Returns the new index into string in
46921308Sache   *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
47021308Sachestatic int
47121308Sachehistory_expand_internal (string, start, end_index_ptr, ret_string, current_line)
47221308Sache     char *string;
47321308Sache     int start, *end_index_ptr;
47421308Sache     char **ret_string;
47521308Sache     char *current_line;	/* for !# */
47621308Sache{
47721308Sache  int i, n, starting_index;
47821308Sache  int substitute_globally, want_quotes, print_only;
47921308Sache  char *event, *temp, *result, *tstr, *t, c, *word_spec;
48021308Sache  int result_len;
48121308Sache
48221308Sache  result = xmalloc (result_len = 128);
48321308Sache
48421308Sache  i = start;
48521308Sache
48621308Sache  /* If it is followed by something that starts a word specifier,
48721308Sache     then !! is implied as the event specifier. */
48821308Sache
48921308Sache  if (member (string[i + 1], ":$*%^"))
49021308Sache    {
49121308Sache      char fake_s[3];
49221308Sache      int fake_i = 0;
49321308Sache      i++;
49421308Sache      fake_s[0] = fake_s[1] = history_expansion_char;
49521308Sache      fake_s[2] = '\0';
49621308Sache      event = get_history_event (fake_s, &fake_i, 0);
49721308Sache    }
49821308Sache  else if (string[i + 1] == '#')
49921308Sache    {
50021308Sache      i += 2;
50121308Sache      event = current_line;
50221308Sache    }
50321308Sache  else
50421308Sache    {
50521308Sache      int quoted_search_delimiter = 0;
50621308Sache
50721308Sache      /* If the character before this `!' is a double or single
50821308Sache	 quote, then this expansion takes place inside of the
50921308Sache	 quoted string.  If we have to search for some text ("!foo"),
51021308Sache	 allow the delimiter to end the search string. */
51121308Sache      if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
51221308Sache	quoted_search_delimiter = string[i - 1];
51321308Sache      event = get_history_event (string, &i, quoted_search_delimiter);
51421308Sache    }
51521308Sache
51621308Sache  if (event == 0)
51721308Sache    {
51821308Sache      *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
51921308Sache      free (result);
52021308Sache      return (-1);
52121308Sache    }
52221308Sache
52321308Sache  /* If a word specifier is found, then do what that requires. */
52421308Sache  starting_index = i;
52521308Sache  word_spec = get_history_word_specifier (string, event, &i);
52621308Sache
52721308Sache  /* There is no such thing as a `malformed word specifier'.  However,
52821308Sache     it is possible for a specifier that has no match.  In that case,
52921308Sache     we complain. */
53021308Sache  if (word_spec == (char *)&error_pointer)
53121308Sache    {
53221308Sache      *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
53321308Sache      free (result);
53421308Sache      return (-1);
53521308Sache    }
53621308Sache
53721308Sache  /* If no word specifier, than the thing of interest was the event. */
53821308Sache  temp = word_spec ? savestring (word_spec) : savestring (event);
53921308Sache  FREE (word_spec);
54021308Sache
54121308Sache  /* Perhaps there are other modifiers involved.  Do what they say. */
54221308Sache  want_quotes = substitute_globally = print_only = 0;
54321308Sache  starting_index = i;
54421308Sache
54521308Sache  while (string[i] == ':')
54621308Sache    {
54721308Sache      c = string[i + 1];
54821308Sache
54921308Sache      if (c == 'g')
55021308Sache	{
55121308Sache	  substitute_globally = 1;
55221308Sache	  i++;
55321308Sache	  c = string[i + 1];
55421308Sache	}
55521308Sache
55621308Sache      switch (c)
55721308Sache	{
55821308Sache	default:
55921308Sache	  *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
56021308Sache	  free (result);
56121308Sache	  free (temp);
56221308Sache	  return -1;
56321308Sache
56421308Sache	case 'q':
56521308Sache	  want_quotes = 'q';
56621308Sache	  break;
56721308Sache
56821308Sache	case 'x':
56921308Sache	  want_quotes = 'x';
57021308Sache	  break;
57121308Sache
57221308Sache	  /* :p means make this the last executed line.  So we
57321308Sache	     return an error state after adding this line to the
57421308Sache	     history. */
57521308Sache	case 'p':
57621308Sache	  print_only++;
57721308Sache	  break;
57821308Sache
57921308Sache	  /* :t discards all but the last part of the pathname. */
58021308Sache	case 't':
58121308Sache	  tstr = strrchr (temp, '/');
58221308Sache	  if (tstr)
58321308Sache	    {
58421308Sache	      tstr++;
58521308Sache	      t = savestring (tstr);
58621308Sache	      free (temp);
58721308Sache	      temp = t;
58821308Sache	    }
58921308Sache	  break;
59021308Sache
59121308Sache	  /* :h discards the last part of a pathname. */
59221308Sache	case 'h':
59321308Sache	  tstr = strrchr (temp, '/');
59421308Sache	  if (tstr)
59521308Sache	    *tstr = '\0';
59621308Sache	  break;
59721308Sache
59821308Sache	  /* :r discards the suffix. */
59921308Sache	case 'r':
60021308Sache	  tstr = strrchr (temp, '.');
60121308Sache	  if (tstr)
60221308Sache	    *tstr = '\0';
60321308Sache	  break;
60421308Sache
60521308Sache	  /* :e discards everything but the suffix. */
60621308Sache	case 'e':
60721308Sache	  tstr = strrchr (temp, '.');
60821308Sache	  if (tstr)
60921308Sache	    {
61021308Sache	      t = savestring (tstr);
61121308Sache	      free (temp);
61221308Sache	      temp = t;
61321308Sache	    }
61421308Sache	  break;
61521308Sache
61621308Sache	/* :s/this/that substitutes `that' for the first
61721308Sache	   occurrence of `this'.  :gs/this/that substitutes `that'
61821308Sache	   for each occurrence of `this'.  :& repeats the last
61921308Sache	   substitution.  :g& repeats the last substitution
62021308Sache	   globally. */
62121308Sache
62221308Sache	case '&':
62321308Sache	case 's':
62421308Sache	  {
62521308Sache	    char *new_event, *t;
62621308Sache	    int delimiter, failed, si, l_temp;
62721308Sache
62821308Sache	    if (c == 's')
62921308Sache	      {
63021308Sache		if (i + 2 < (int)strlen (string))
63121308Sache		  delimiter = string[i + 2];
63221308Sache		else
63321308Sache		  break;	/* no search delimiter */
63421308Sache
63521308Sache		i += 3;
63621308Sache
63721308Sache		t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
63821308Sache		/* An empty substitution lhs with no previous substitution
63921308Sache		   uses the last search string as the lhs. */
64021308Sache		if (t)
64121308Sache		  {
64221308Sache		    FREE (subst_lhs);
64321308Sache		    subst_lhs = t;
64421308Sache		  }
64521308Sache		else if (!subst_lhs)
64621308Sache		  {
64721308Sache		    if (search_string && *search_string)
64821308Sache		      {
64921308Sache			subst_lhs = savestring (search_string);
65021308Sache			subst_lhs_len = strlen (subst_lhs);
65121308Sache		      }
65221308Sache		    else
65321308Sache		      {
65421308Sache			subst_lhs = (char *) NULL;
65521308Sache			subst_lhs_len = 0;
65621308Sache		      }
65721308Sache		  }
65821308Sache
65921308Sache		FREE (subst_rhs);
66021308Sache		subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
66121308Sache
66221308Sache		/* If `&' appears in the rhs, it's supposed to be replaced
66321308Sache		   with the lhs. */
66421308Sache		if (member ('&', subst_rhs))
66521308Sache		  postproc_subst_rhs ();
66621308Sache	      }
66721308Sache	    else
66821308Sache	      i += 2;
66921308Sache
67047558Sache	    /* If there is no lhs, the substitution can't succeed. */
67147558Sache	    if (subst_lhs_len == 0)
67247558Sache	      {
67347558Sache		*ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
67447558Sache		free (result);
67547558Sache		free (temp);
67647558Sache		return -1;
67747558Sache	      }
67847558Sache
67921308Sache	    l_temp = strlen (temp);
68021308Sache	    /* Ignore impossible cases. */
68121308Sache	    if (subst_lhs_len > l_temp)
68221308Sache	      {
68321308Sache		*ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
68421308Sache		free (result);
68521308Sache		free (temp);
68621308Sache		return (-1);
68721308Sache	      }
68821308Sache
68921308Sache	    /* Find the first occurrence of THIS in TEMP. */
69021308Sache	    si = 0;
69121308Sache	    for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
69221308Sache	      if (STREQN (temp+si, subst_lhs, subst_lhs_len))
69321308Sache		{
69421308Sache		  int len = subst_rhs_len - subst_lhs_len + l_temp;
69521308Sache		  new_event = xmalloc (1 + len);
69621308Sache		  strncpy (new_event, temp, si);
69721308Sache		  strncpy (new_event + si, subst_rhs, subst_rhs_len);
69821308Sache		  strncpy (new_event + si + subst_rhs_len,
69921308Sache			   temp + si + subst_lhs_len,
70021308Sache			   l_temp - (si + subst_lhs_len));
70121308Sache		  new_event[len] = '\0';
70221308Sache		  free (temp);
70321308Sache		  temp = new_event;
70421308Sache
70521308Sache		  failed = 0;
70621308Sache
70721308Sache		  if (substitute_globally)
70821308Sache		    {
70921308Sache		      si += subst_rhs_len;
71021308Sache		      l_temp = strlen (temp);
71121308Sache		      substitute_globally++;
71221308Sache		      continue;
71321308Sache		    }
71421308Sache		  else
71521308Sache		    break;
71621308Sache		}
71721308Sache
71821308Sache	    if (substitute_globally > 1)
71921308Sache	      {
72021308Sache		substitute_globally = 0;
72121308Sache		continue;	/* don't want to increment i */
72221308Sache	      }
72321308Sache
72421308Sache	    if (failed == 0)
72521308Sache	      continue;		/* don't want to increment i */
72621308Sache
72721308Sache	    *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
72821308Sache	    free (result);
72921308Sache	    free (temp);
73021308Sache	    return (-1);
73121308Sache	  }
73221308Sache	}
73321308Sache      i += 2;
73421308Sache    }
73521308Sache  /* Done with modfiers. */
73621308Sache  /* Believe it or not, we have to back the pointer up by one. */
73721308Sache  --i;
73821308Sache
73921308Sache  if (want_quotes)
74021308Sache    {
74121308Sache      char *x;
74221308Sache
74321308Sache      if (want_quotes == 'q')
74421308Sache	x = single_quote (temp);
74521308Sache      else if (want_quotes == 'x')
74621308Sache	x = quote_breaks (temp);
74721308Sache      else
74821308Sache	x = savestring (temp);
74921308Sache
75021308Sache      free (temp);
75121308Sache      temp = x;
75221308Sache    }
75321308Sache
75421308Sache  n = strlen (temp);
75521308Sache  if (n >= result_len)
75621308Sache    result = xrealloc (result, n + 2);
75721308Sache  strcpy (result, temp);
75821308Sache  free (temp);
75921308Sache
76021308Sache  *end_index_ptr = i;
76121308Sache  *ret_string = result;
76221308Sache  return (print_only);
76321308Sache}
76421308Sache
76521308Sache/* Expand the string STRING, placing the result into OUTPUT, a pointer
76621308Sache   to a string.  Returns:
76721308Sache
76821308Sache  -1) If there was an error in expansion.
76921308Sache   0) If no expansions took place (or, if the only change in
77021308Sache      the text was the de-slashifying of the history expansion
77121308Sache      character)
77221308Sache   1) If expansions did take place
77321308Sache   2) If the `p' modifier was given and the caller should print the result
77421308Sache
77521308Sache  If an error ocurred in expansion, then OUTPUT contains a descriptive
77621308Sache  error message. */
77721308Sache
77821308Sache#define ADD_STRING(s) \
77921308Sache	do \
78021308Sache	  { \
78121308Sache	    int sl = strlen (s); \
78221308Sache	    j += sl; \
78321308Sache	    if (j >= result_len) \
78421308Sache	      { \
78521308Sache		while (j >= result_len) \
78621308Sache		  result_len += 128; \
78721308Sache		result = xrealloc (result, result_len); \
78821308Sache	      } \
78921308Sache	    strcpy (result + j - sl, s); \
79021308Sache	  } \
79121308Sache	while (0)
79221308Sache
79321308Sache#define ADD_CHAR(c) \
79421308Sache	do \
79521308Sache	  { \
79621308Sache	    if (j >= result_len - 1) \
79721308Sache	      result = xrealloc (result, result_len += 64); \
79821308Sache	    result[j++] = c; \
79921308Sache	    result[j] = '\0'; \
80021308Sache	  } \
80121308Sache	while (0)
80221308Sache
80321308Sacheint
80421308Sachehistory_expand (hstring, output)
80521308Sache     char *hstring;
80621308Sache     char **output;
80721308Sache{
80821308Sache  register int j;
80921308Sache  int i, r, l, passc, cc, modified, eindex, only_printing;
81021308Sache  char *string;
81121308Sache
81221308Sache  /* The output string, and its length. */
81321308Sache  int result_len;
81421308Sache  char *result;
81521308Sache
81621308Sache  /* Used when adding the string. */
81721308Sache  char *temp;
81821308Sache
81921308Sache  /* Setting the history expansion character to 0 inhibits all
82021308Sache     history expansion. */
82121308Sache  if (history_expansion_char == 0)
82221308Sache    {
82321308Sache      *output = savestring (hstring);
82421308Sache      return (0);
82521308Sache    }
82621308Sache
82721308Sache  /* Prepare the buffer for printing error messages. */
82821308Sache  result = xmalloc (result_len = 256);
82921308Sache  result[0] = '\0';
83021308Sache
83121308Sache  only_printing = modified = 0;
83221308Sache  l = strlen (hstring);
83321308Sache
83435486Sache  /* Grovel the string.  Only backslash and single quotes can quote the
83535486Sache     history escape character.  We also handle arg specifiers. */
83621308Sache
83721308Sache  /* Before we grovel forever, see if the history_expansion_char appears
83821308Sache     anywhere within the text. */
83921308Sache
84021308Sache  /* The quick substitution character is a history expansion all right.  That
84121308Sache     is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
84221308Sache     that is the substitution that we do. */
84321308Sache  if (hstring[0] == history_subst_char)
84421308Sache    {
84521308Sache      string = xmalloc (l + 5);
84621308Sache
84721308Sache      string[0] = string[1] = history_expansion_char;
84821308Sache      string[2] = ':';
84921308Sache      string[3] = 's';
85021308Sache      strcpy (string + 4, hstring);
85121308Sache      l += 4;
85221308Sache    }
85321308Sache  else
85421308Sache    {
85521308Sache      string = hstring;
85621308Sache      /* If not quick substitution, still maybe have to do expansion. */
85721308Sache
85821308Sache      /* `!' followed by one of the characters in history_no_expand_chars
85921308Sache	 is NOT an expansion. */
86021308Sache      for (i = 0; string[i]; i++)
86121308Sache	{
86221308Sache	  cc = string[i + 1];
86335486Sache	  /* The history_comment_char, if set, appearing that the beginning
86435486Sache	     of a word signifies that the rest of the line should not have
86535486Sache	     history expansion performed on it.
86635486Sache	     Skip the rest of the line and break out of the loop. */
86735486Sache	  if (history_comment_char && string[i] == history_comment_char &&
86835486Sache	      (i == 0 || member (string[i - 1], HISTORY_WORD_DELIMITERS)))
86921308Sache	    {
87035486Sache	      while (string[i])
87135486Sache		i++;
87235486Sache	      break;
87335486Sache	    }
87435486Sache	  else if (string[i] == history_expansion_char)
87535486Sache	    {
87621308Sache	      if (!cc || member (cc, history_no_expand_chars))
87721308Sache		continue;
87826497Sache	      /* If the calling application has set
87926497Sache		 history_inhibit_expansion_function to a function that checks
88026497Sache		 for special cases that should not be history expanded,
88126497Sache		 call the function and skip the expansion if it returns a
88226497Sache		 non-zero value. */
88326497Sache	      else if (history_inhibit_expansion_function &&
88426497Sache			(*history_inhibit_expansion_function) (string, i))
88521308Sache		continue;
88621308Sache	      else
88721308Sache		break;
88821308Sache	    }
88935486Sache	  /* XXX - at some point, might want to extend this to handle
89035486Sache		   double quotes as well. */
89121308Sache	  else if (history_quotes_inhibit_expansion && string[i] == '\'')
89221308Sache	    {
89321308Sache	      /* If this is bash, single quotes inhibit history expansion. */
89421308Sache	      i++;
89521308Sache	      hist_string_extract_single_quoted (string, &i);
89621308Sache	    }
89721308Sache	  else if (history_quotes_inhibit_expansion && string[i] == '\\')
89821308Sache	    {
89921308Sache	      /* If this is bash, allow backslashes to quote single
90021308Sache		 quotes and the history expansion character. */
90121308Sache	      if (cc == '\'' || cc == history_expansion_char)
90221308Sache		i++;
90321308Sache	    }
90421308Sache	}
90521308Sache
90621308Sache      if (string[i] != history_expansion_char)
90721308Sache	{
90821308Sache	  free (result);
90921308Sache	  *output = savestring (string);
91021308Sache	  return (0);
91121308Sache	}
91221308Sache    }
91321308Sache
91421308Sache  /* Extract and perform the substitution. */
91521308Sache  for (passc = i = j = 0; i < l; i++)
91621308Sache    {
91721308Sache      int tchar = string[i];
91821308Sache
91921308Sache      if (passc)
92021308Sache	{
92121308Sache	  passc = 0;
92221308Sache	  ADD_CHAR (tchar);
92321308Sache	  continue;
92421308Sache	}
92521308Sache
92621308Sache      if (tchar == history_expansion_char)
92721308Sache	tchar = -3;
92835486Sache      else if (tchar == history_comment_char)
92935486Sache	tchar = -2;
93021308Sache
93121308Sache      switch (tchar)
93221308Sache	{
93321308Sache	default:
93421308Sache	  ADD_CHAR (string[i]);
93521308Sache	  break;
93621308Sache
93721308Sache	case '\\':
93821308Sache	  passc++;
93921308Sache	  ADD_CHAR (tchar);
94021308Sache	  break;
94121308Sache
94221308Sache	case '\'':
94321308Sache	  {
94421308Sache	    /* If history_quotes_inhibit_expansion is set, single quotes
94521308Sache	       inhibit history expansion. */
94621308Sache	    if (history_quotes_inhibit_expansion)
94721308Sache	      {
94821308Sache		int quote, slen;
94921308Sache
95021308Sache		quote = i++;
95121308Sache		hist_string_extract_single_quoted (string, &i);
95221308Sache
95321308Sache		slen = i - quote + 2;
95421308Sache		temp = xmalloc (slen);
95521308Sache		strncpy (temp, string + quote, slen);
95621308Sache		temp[slen - 1] = '\0';
95721308Sache		ADD_STRING (temp);
95821308Sache		free (temp);
95921308Sache	      }
96021308Sache	    else
96121308Sache	      ADD_CHAR (string[i]);
96221308Sache	    break;
96321308Sache	  }
96421308Sache
96535486Sache	case -2:		/* history_comment_char */
96635486Sache	  if (i == 0 || member (string[i - 1], HISTORY_WORD_DELIMITERS))
96735486Sache	    {
96835486Sache	      temp = xmalloc (l - i + 1);
96935486Sache	      strcpy (temp, string + i);
97035486Sache	      ADD_STRING (temp);
97135486Sache	      free (temp);
97235486Sache	      i = l;
97335486Sache	    }
97435486Sache	  else
97535486Sache	    ADD_CHAR (string[i]);
97635486Sache	  break;
97735486Sache
97821308Sache	case -3:		/* history_expansion_char */
97921308Sache	  cc = string[i + 1];
98021308Sache
98121308Sache	  /* If the history_expansion_char is followed by one of the
98221308Sache	     characters in history_no_expand_chars, then it is not a
98321308Sache	     candidate for expansion of any kind. */
98421308Sache	  if (member (cc, history_no_expand_chars))
98521308Sache	    {
98621308Sache	      ADD_CHAR (string[i]);
98721308Sache	      break;
98821308Sache	    }
98921308Sache
99021308Sache#if defined (NO_BANG_HASH_MODIFIERS)
99121308Sache	  /* There is something that is listed as a `word specifier' in csh
99221308Sache	     documentation which means `the expanded text to this point'.
99321308Sache	     That is not a word specifier, it is an event specifier.  If we
99421308Sache	     don't want to allow modifiers with `!#', just stick the current
99521308Sache	     output line in again. */
99621308Sache	  if (cc == '#')
99721308Sache	    {
99821308Sache	      if (result)
99921308Sache		{
100021308Sache		  temp = xmalloc (1 + strlen (result));
100121308Sache		  strcpy (temp, result);
100221308Sache		  ADD_STRING (temp);
100321308Sache		  free (temp);
100421308Sache		}
100521308Sache	      i++;
100621308Sache	      break;
100721308Sache	    }
100821308Sache#endif
100921308Sache
101021308Sache	  r = history_expand_internal (string, i, &eindex, &temp, result);
101121308Sache	  if (r < 0)
101221308Sache	    {
101321308Sache	      *output = temp;
101421308Sache	      free (result);
101521308Sache	      if (string != hstring)
101621308Sache		free (string);
101721308Sache	      return -1;
101821308Sache	    }
101921308Sache	  else
102021308Sache	    {
102121308Sache	      if (temp)
102221308Sache		{
102321308Sache		  modified++;
102421308Sache		  if (*temp)
102521308Sache		    ADD_STRING (temp);
102621308Sache		  free (temp);
102721308Sache		}
102821308Sache	      only_printing = r == 1;
102921308Sache	      i = eindex;
103021308Sache	    }
103121308Sache	  break;
103221308Sache	}
103321308Sache    }
103421308Sache
103521308Sache  *output = result;
103621308Sache  if (string != hstring)
103721308Sache    free (string);
103821308Sache
103921308Sache  if (only_printing)
104021308Sache    {
104121308Sache      add_history (result);
104221308Sache      return (2);
104321308Sache    }
104421308Sache
104521308Sache  return (modified != 0);
104621308Sache}
104721308Sache
104821308Sache/* Return a consed string which is the word specified in SPEC, and found
104921308Sache   in FROM.  NULL is returned if there is no spec.  The address of
105021308Sache   ERROR_POINTER is returned if the word specified cannot be found.
105121308Sache   CALLER_INDEX is the offset in SPEC to start looking; it is updated
105221308Sache   to point to just after the last character parsed. */
105321308Sachestatic char *
105421308Sacheget_history_word_specifier (spec, from, caller_index)
105521308Sache     char *spec, *from;
105621308Sache     int *caller_index;
105721308Sache{
105821308Sache  register int i = *caller_index;
105921308Sache  int first, last;
106021308Sache  int expecting_word_spec = 0;
106121308Sache  char *result;
106221308Sache
106321308Sache  /* The range of words to return doesn't exist yet. */
106421308Sache  first = last = 0;
106521308Sache  result = (char *)NULL;
106621308Sache
106721308Sache  /* If we found a colon, then this *must* be a word specification.  If
106821308Sache     it isn't, then it is an error. */
106921308Sache  if (spec[i] == ':')
107021308Sache    {
107121308Sache      i++;
107221308Sache      expecting_word_spec++;
107321308Sache    }
107421308Sache
107521308Sache  /* Handle special cases first. */
107621308Sache
107721308Sache  /* `%' is the word last searched for. */
107821308Sache  if (spec[i] == '%')
107921308Sache    {
108021308Sache      *caller_index = i + 1;
108121308Sache      return (search_match ? savestring (search_match) : savestring (""));
108221308Sache    }
108321308Sache
108421308Sache  /* `*' matches all of the arguments, but not the command. */
108521308Sache  if (spec[i] == '*')
108621308Sache    {
108721308Sache      *caller_index = i + 1;
108821308Sache      result = history_arg_extract (1, '$', from);
108921308Sache      return (result ? result : savestring (""));
109021308Sache    }
109121308Sache
109221308Sache  /* `$' is last arg. */
109321308Sache  if (spec[i] == '$')
109421308Sache    {
109521308Sache      *caller_index = i + 1;
109621308Sache      return (history_arg_extract ('$', '$', from));
109721308Sache    }
109821308Sache
109921308Sache  /* Try to get FIRST and LAST figured out. */
110021308Sache
110121308Sache  if (spec[i] == '-')
110221308Sache    first = 0;
110321308Sache  else if (spec[i] == '^')
110421308Sache    first = 1;
110521308Sache  else if (_rl_digit_p (spec[i]) && expecting_word_spec)
110621308Sache    {
110721308Sache      for (first = 0; _rl_digit_p (spec[i]); i++)
110821308Sache	first = (first * 10) + _rl_digit_value (spec[i]);
110921308Sache    }
111021308Sache  else
111121308Sache    return ((char *)NULL);	/* no valid `first' for word specifier */
111221308Sache
111321308Sache  if (spec[i] == '^' || spec[i] == '*')
111421308Sache    {
111521308Sache      last = (spec[i] == '^') ? 1 : '$';	/* x* abbreviates x-$ */
111621308Sache      i++;
111721308Sache    }
111821308Sache  else if (spec[i] != '-')
111921308Sache    last = first;
112021308Sache  else
112121308Sache    {
112221308Sache      i++;
112321308Sache
112421308Sache      if (_rl_digit_p (spec[i]))
112521308Sache	{
112621308Sache	  for (last = 0; _rl_digit_p (spec[i]); i++)
112721308Sache	    last = (last * 10) + _rl_digit_value (spec[i]);
112821308Sache	}
112921308Sache      else if (spec[i] == '$')
113021308Sache	{
113121308Sache	  i++;
113221308Sache	  last = '$';
113321308Sache	}
113421308Sache      else if (!spec[i] || spec[i] == ':')  /* could be modifier separator */
113521308Sache	last = -1;		/* x- abbreviates x-$ omitting word `$' */
113621308Sache    }
113721308Sache
113821308Sache  *caller_index = i;
113921308Sache
114021308Sache  if (last >= first || last == '$' || last < 0)
114121308Sache    result = history_arg_extract (first, last, from);
114221308Sache
114321308Sache  return (result ? result : (char *)&error_pointer);
114421308Sache}
114521308Sache
114621308Sache/* Extract the args specified, starting at FIRST, and ending at LAST.
114721308Sache   The args are taken from STRING.  If either FIRST or LAST is < 0,
114821308Sache   then make that arg count from the right (subtract from the number of
114921308Sache   tokens, so that FIRST = -1 means the next to last token on the line).
115021308Sache   If LAST is `$' the last arg from STRING is used. */
115121308Sachechar *
115221308Sachehistory_arg_extract (first, last, string)
115321308Sache     int first, last;
115421308Sache     char *string;
115521308Sache{
115621308Sache  register int i, len;
115721308Sache  char *result;
115821308Sache  int size, offset;
115921308Sache  char **list;
116021308Sache
116121308Sache  /* XXX - think about making history_tokenize return a struct array,
116221308Sache     each struct in array being a string and a length to avoid the
116321308Sache     calls to strlen below. */
116421308Sache  if ((list = history_tokenize (string)) == NULL)
116521308Sache    return ((char *)NULL);
116621308Sache
116721308Sache  for (len = 0; list[len]; len++)
116821308Sache    ;
116921308Sache
117021308Sache  if (last < 0)
117121308Sache    last = len + last - 1;
117221308Sache
117321308Sache  if (first < 0)
117421308Sache    first = len + first - 1;
117521308Sache
117621308Sache  if (last == '$')
117721308Sache    last = len - 1;
117821308Sache
117921308Sache  if (first == '$')
118021308Sache    first = len - 1;
118121308Sache
118221308Sache  last++;
118321308Sache
118421308Sache  if (first >= len || last > len || first < 0 || last < 0 || first > last)
118521308Sache    result = ((char *)NULL);
118621308Sache  else
118721308Sache    {
118821308Sache      for (size = 0, i = first; i < last; i++)
118921308Sache	size += strlen (list[i]) + 1;
119021308Sache      result = xmalloc (size + 1);
119121308Sache      result[0] = '\0';
119221308Sache
119321308Sache      for (i = first, offset = 0; i < last; i++)
119421308Sache	{
119521308Sache	  strcpy (result + offset, list[i]);
119621308Sache	  offset += strlen (list[i]);
119721308Sache	  if (i + 1 < last)
119821308Sache	    {
119921308Sache      	      result[offset++] = ' ';
120021308Sache	      result[offset] = 0;
120121308Sache	    }
120221308Sache	}
120321308Sache    }
120421308Sache
120521308Sache  for (i = 0; i < len; i++)
120621308Sache    free (list[i]);
120721308Sache  free (list);
120821308Sache
120921308Sache  return (result);
121021308Sache}
121121308Sache
121221308Sache#define slashify_in_quotes "\\`\"$"
121321308Sache
121421308Sache/* Parse STRING into tokens and return an array of strings.  If WIND is
121521308Sache   not -1 and INDP is not null, we also want the word surrounding index
121621308Sache   WIND.  The position in the returned array of strings is returned in
121721308Sache   *INDP. */
121821308Sachestatic char **
121921308Sachehistory_tokenize_internal (string, wind, indp)
122021308Sache     char *string;
122121308Sache     int wind, *indp;
122221308Sache{
122321308Sache  char **result;
122421308Sache  register int i, start, result_index, size;
122521308Sache  int len, delimiter;
122621308Sache
122721308Sache  /* Get a token, and stuff it into RESULT.  The tokens are split
122821308Sache     exactly where the shell would split them. */
122921308Sache  for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
123021308Sache    {
123121308Sache      delimiter = 0;
123221308Sache
123321308Sache      /* Skip leading whitespace. */
123421308Sache      for (; string[i] && whitespace (string[i]); i++)
123521308Sache	;
123621308Sache      if (string[i] == 0 || string[i] == history_comment_char)
123721308Sache	return (result);
123821308Sache
123921308Sache      start = i;
124021308Sache
124121308Sache      if (member (string[i], "()\n"))
124221308Sache	{
124321308Sache	  i++;
124421308Sache	  goto got_token;
124521308Sache	}
124621308Sache
124721308Sache      if (member (string[i], "<>;&|$"))
124821308Sache	{
124921308Sache	  int peek = string[i + 1];
125021308Sache
125121308Sache	  if (peek == string[i] && peek != '$')
125221308Sache	    {
125321308Sache	      if (peek == '<' && string[i + 2] == '-')
125421308Sache		i++;
125521308Sache	      i += 2;
125621308Sache	      goto got_token;
125721308Sache	    }
125821308Sache	  else
125921308Sache	    {
126021308Sache	      if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
126121308Sache		  ((peek == '>') && (string[i] == '&')) ||
126221308Sache		  ((peek == '(') && (string[i] == '$')))
126321308Sache		{
126421308Sache		  i += 2;
126521308Sache		  goto got_token;
126621308Sache		}
126721308Sache	    }
126821308Sache	  if (string[i] != '$')
126921308Sache	    {
127021308Sache	      i++;
127121308Sache	      goto got_token;
127221308Sache	    }
127321308Sache	}
127421308Sache
127521308Sache      /* Get word from string + i; */
127621308Sache
127735486Sache      if (member (string[i], HISTORY_QUOTE_CHARACTERS))
127821308Sache	delimiter = string[i++];
127921308Sache
128021308Sache      for (; string[i]; i++)
128121308Sache	{
128221308Sache	  if (string[i] == '\\' && string[i + 1] == '\n')
128321308Sache	    {
128421308Sache	      i++;
128521308Sache	      continue;
128621308Sache	    }
128721308Sache
128821308Sache	  if (string[i] == '\\' && delimiter != '\'' &&
128921308Sache	      (delimiter != '"' || member (string[i], slashify_in_quotes)))
129021308Sache	    {
129121308Sache	      i++;
129221308Sache	      continue;
129321308Sache	    }
129421308Sache
129521308Sache	  if (delimiter && string[i] == delimiter)
129621308Sache	    {
129721308Sache	      delimiter = 0;
129821308Sache	      continue;
129921308Sache	    }
130021308Sache
130135486Sache	  if (!delimiter && (member (string[i], HISTORY_WORD_DELIMITERS)))
130221308Sache	    break;
130321308Sache
130435486Sache	  if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS))
130521308Sache	    delimiter = string[i];
130621308Sache	}
130721308Sache
130821308Sache    got_token:
130921308Sache
131021308Sache      /* If we are looking for the word in which the character at a
131121308Sache	 particular index falls, remember it. */
131221308Sache      if (indp && wind != -1 && wind >= start && wind < i)
131321308Sache        *indp = result_index;
131421308Sache
131521308Sache      len = i - start;
131621308Sache      if (result_index + 2 >= size)
131721308Sache	result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
131821308Sache      result[result_index] = xmalloc (1 + len);
131921308Sache      strncpy (result[result_index], string + start, len);
132021308Sache      result[result_index][len] = '\0';
132121308Sache      result[++result_index] = (char *)NULL;
132221308Sache    }
132321308Sache
132421308Sache  return (result);
132521308Sache}
132621308Sache
132721308Sache/* Return an array of tokens, much as the shell might.  The tokens are
132821308Sache   parsed out of STRING. */
132921308Sachechar **
133021308Sachehistory_tokenize (string)
133121308Sache     char *string;
133221308Sache{
133321308Sache  return (history_tokenize_internal (string, -1, (int *)NULL));
133421308Sache}
133521308Sache
133621308Sache/* Find and return the word which contains the character at index IND
133721308Sache   in the history line LINE.  Used to save the word matched by the
133821308Sache   last history !?string? search. */
133921308Sachestatic char *
134021308Sachehistory_find_word (line, ind)
134121308Sache     char *line;
134221308Sache     int ind;
134321308Sache{
134421308Sache  char **words, *s;
134521308Sache  int i, wind;
134621308Sache
134721308Sache  words = history_tokenize_internal (line, ind, &wind);
134821308Sache  if (wind == -1)
134921308Sache    return ((char *)NULL);
135021308Sache  s = words[wind];
135121308Sache  for (i = 0; i < wind; i++)
135221308Sache    free (words[i]);
135321308Sache  for (i = wind + 1; words[i]; i++)
135421308Sache    free (words[i]);
135521308Sache  free (words);
135621308Sache  return s;
135721308Sache}
1358