histexpand.c revision 75406
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
5975406Sachetypedef int _hist_search_func_t __P((const char *, int));
6075406Sache
6121308Sachestatic char error_pointer;
6221308Sache
6321308Sachestatic char *subst_lhs;
6421308Sachestatic char *subst_rhs;
6521308Sachestatic int subst_lhs_len;
6621308Sachestatic int subst_rhs_len;
6721308Sache
6858310Sachestatic char *get_history_word_specifier __P((char *, char *, int *));
6958310Sachestatic char *history_find_word __P((char *, int));
7021308Sache
7158310Sachestatic char *quote_breaks __P((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++)
20721308Sache    if ((!substring_okay && (whitespace (c) || c == ':' ||
20821308Sache	(history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
20921308Sache	string[i] == delimiting_quote)) ||
21021308Sache	string[i] == '\n' ||
21121308Sache	(substring_okay && string[i] == '?'))
21221308Sache      break;
21321308Sache
21421308Sache  which = i - local_index;
21521308Sache  temp = xmalloc (1 + which);
21621308Sache  if (which)
21721308Sache    strncpy (temp, string + local_index, which);
21821308Sache  temp[which] = '\0';
21921308Sache
22021308Sache  if (substring_okay && string[i] == '?')
22121308Sache    i++;
22221308Sache
22321308Sache  *caller_index = i;
22421308Sache
22521308Sache#define FAIL_SEARCH() \
22621308Sache  do { \
22721308Sache    history_offset = history_length; free (temp) ; return (char *)NULL; \
22821308Sache  } while (0)
22921308Sache
23021308Sache  /* If there is no search string, try to use the previous search string,
23121308Sache     if one exists.  If not, fail immediately. */
23221308Sache  if (*temp == '\0' && substring_okay)
23321308Sache    {
23421308Sache      if (search_string)
23521308Sache        {
23621308Sache          free (temp);
23721308Sache          temp = savestring (search_string);
23821308Sache        }
23921308Sache      else
24021308Sache        FAIL_SEARCH ();
24121308Sache    }
24221308Sache
24321308Sache  search_func = substring_okay ? history_search : history_search_prefix;
24421308Sache  while (1)
24521308Sache    {
24621308Sache      local_index = (*search_func) (temp, -1);
24721308Sache
24821308Sache      if (local_index < 0)
24921308Sache	FAIL_SEARCH ();
25021308Sache
25121308Sache      if (local_index == 0 || substring_okay)
25221308Sache	{
25321308Sache	  entry = current_history ();
25421308Sache	  history_offset = history_length;
25521308Sache
25621308Sache	  /* If this was a substring search, then remember the
25721308Sache	     string that we matched for word substitution. */
25821308Sache	  if (substring_okay)
25921308Sache	    {
26021308Sache	      FREE (search_string);
26121308Sache	      search_string = temp;
26221308Sache
26321308Sache	      FREE (search_match);
26421308Sache	      search_match = history_find_word (entry->line, local_index);
26521308Sache	    }
26621308Sache	  else
26721308Sache	    free (temp);
26821308Sache
26921308Sache	  return (entry->line);
27021308Sache	}
27121308Sache
27221308Sache      if (history_offset)
27321308Sache	history_offset--;
27421308Sache      else
27521308Sache	FAIL_SEARCH ();
27621308Sache    }
27721308Sache#undef FAIL_SEARCH
27821308Sache#undef RETURN_ENTRY
27921308Sache}
28021308Sache
28121308Sache/* Function for extracting single-quoted strings.  Used for inhibiting
28221308Sache   history expansion within single quotes. */
28321308Sache
28421308Sache/* Extract the contents of STRING as if it is enclosed in single quotes.
28521308Sache   SINDEX, when passed in, is the offset of the character immediately
28621308Sache   following the opening single quote; on exit, SINDEX is left pointing
28721308Sache   to the closing single quote. */
28821308Sachestatic void
28921308Sachehist_string_extract_single_quoted (string, sindex)
29021308Sache     char *string;
29121308Sache     int *sindex;
29221308Sache{
29321308Sache  register int i;
29421308Sache
29521308Sache  for (i = *sindex; string[i] && string[i] != '\''; i++)
29621308Sache    ;
29721308Sache
29821308Sache  *sindex = i;
29921308Sache}
30021308Sache
30121308Sachestatic char *
30221308Sachequote_breaks (s)
30321308Sache     char *s;
30421308Sache{
30521308Sache  register char *p, *r;
30621308Sache  char *ret;
30721308Sache  int len = 3;
30821308Sache
30921308Sache  for (p = s; p && *p; p++, len++)
31021308Sache    {
31121308Sache      if (*p == '\'')
31221308Sache	len += 3;
31321308Sache      else if (whitespace (*p) || *p == '\n')
31421308Sache	len += 2;
31521308Sache    }
31621308Sache
31721308Sache  r = ret = xmalloc (len);
31821308Sache  *r++ = '\'';
31921308Sache  for (p = s; p && *p; )
32021308Sache    {
32121308Sache      if (*p == '\'')
32221308Sache	{
32321308Sache	  *r++ = '\'';
32421308Sache	  *r++ = '\\';
32521308Sache	  *r++ = '\'';
32621308Sache	  *r++ = '\'';
32721308Sache	  p++;
32821308Sache	}
32921308Sache      else if (whitespace (*p) || *p == '\n')
33021308Sache	{
33121308Sache	  *r++ = '\'';
33221308Sache	  *r++ = *p++;
33321308Sache	  *r++ = '\'';
33421308Sache	}
33521308Sache      else
33621308Sache	*r++ = *p++;
33721308Sache    }
33821308Sache  *r++ = '\'';
33921308Sache  *r = '\0';
34021308Sache  return ret;
34121308Sache}
34221308Sache
34321308Sachestatic char *
34421308Sachehist_error(s, start, current, errtype)
34521308Sache      char *s;
34621308Sache      int start, current, errtype;
34721308Sache{
34875406Sache  char *temp;
34975406Sache  const char *emsg;
35021308Sache  int ll, elen;
35121308Sache
35221308Sache  ll = current - start;
35321308Sache
35421308Sache  switch (errtype)
35521308Sache    {
35621308Sache    case EVENT_NOT_FOUND:
35721308Sache      emsg = "event not found";
35821308Sache      elen = 15;
35921308Sache      break;
36021308Sache    case BAD_WORD_SPEC:
36121308Sache      emsg = "bad word specifier";
36221308Sache      elen = 18;
36321308Sache      break;
36421308Sache    case SUBST_FAILED:
36521308Sache      emsg = "substitution failed";
36621308Sache      elen = 19;
36721308Sache      break;
36821308Sache    case BAD_MODIFIER:
36921308Sache      emsg = "unrecognized history modifier";
37021308Sache      elen = 29;
37121308Sache      break;
37247558Sache    case NO_PREV_SUBST:
37347558Sache      emsg = "no previous substitution";
37447558Sache      elen = 24;
37547558Sache      break;
37621308Sache    default:
37721308Sache      emsg = "unknown expansion error";
37821308Sache      elen = 23;
37921308Sache      break;
38021308Sache    }
38121308Sache
38221308Sache  temp = xmalloc (ll + elen + 3);
38321308Sache  strncpy (temp, s + start, ll);
38421308Sache  temp[ll] = ':';
38521308Sache  temp[ll + 1] = ' ';
38621308Sache  strcpy (temp + ll + 2, emsg);
38721308Sache  return (temp);
38821308Sache}
38921308Sache
39021308Sache/* Get a history substitution string from STR starting at *IPTR
39121308Sache   and return it.  The length is returned in LENPTR.
39221308Sache
39321308Sache   A backslash can quote the delimiter.  If the string is the
39421308Sache   empty string, the previous pattern is used.  If there is
39521308Sache   no previous pattern for the lhs, the last history search
39621308Sache   string is used.
39721308Sache
39821308Sache   If IS_RHS is 1, we ignore empty strings and set the pattern
39921308Sache   to "" anyway.  subst_lhs is not changed if the lhs is empty;
40021308Sache   subst_rhs is allowed to be set to the empty string. */
40121308Sache
40221308Sachestatic char *
40321308Sacheget_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
40421308Sache     char *str;
40521308Sache     int *iptr, delimiter, is_rhs, *lenptr;
40621308Sache{
40721308Sache  register int si, i, j, k;
40821308Sache  char *s = (char *) NULL;
40921308Sache
41021308Sache  i = *iptr;
41121308Sache
41221308Sache  for (si = i; str[si] && str[si] != delimiter; si++)
41321308Sache    if (str[si] == '\\' && str[si + 1] == delimiter)
41421308Sache      si++;
41521308Sache
41621308Sache  if (si > i || is_rhs)
41721308Sache    {
41821308Sache      s = xmalloc (si - i + 1);
41921308Sache      for (j = 0, k = i; k < si; j++, k++)
42021308Sache	{
42121308Sache	  /* Remove a backslash quoting the search string delimiter. */
42221308Sache	  if (str[k] == '\\' && str[k + 1] == delimiter)
42321308Sache	    k++;
42421308Sache	  s[j] = str[k];
42521308Sache	}
42621308Sache      s[j] = '\0';
42721308Sache      if (lenptr)
42821308Sache	*lenptr = j;
42921308Sache    }
43021308Sache
43121308Sache  i = si;
43221308Sache  if (str[i])
43321308Sache    i++;
43421308Sache  *iptr = i;
43521308Sache
43621308Sache  return s;
43721308Sache}
43821308Sache
43921308Sachestatic void
44021308Sachepostproc_subst_rhs ()
44121308Sache{
44221308Sache  char *new;
44321308Sache  int i, j, new_size;
44421308Sache
44521308Sache  new = xmalloc (new_size = subst_rhs_len + subst_lhs_len);
44621308Sache  for (i = j = 0; i < subst_rhs_len; i++)
44721308Sache    {
44821308Sache      if (subst_rhs[i] == '&')
44921308Sache	{
45021308Sache	  if (j + subst_lhs_len >= new_size)
45121308Sache	    new = xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
45221308Sache	  strcpy (new + j, subst_lhs);
45321308Sache	  j += subst_lhs_len;
45421308Sache	}
45521308Sache      else
45621308Sache	{
45721308Sache	  /* a single backslash protects the `&' from lhs interpolation */
45821308Sache	  if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
45921308Sache	    i++;
46021308Sache	  if (j >= new_size)
46121308Sache	    new = xrealloc (new, new_size *= 2);
46221308Sache	  new[j++] = subst_rhs[i];
46321308Sache	}
46421308Sache    }
46521308Sache  new[j] = '\0';
46621308Sache  free (subst_rhs);
46721308Sache  subst_rhs = new;
46821308Sache  subst_rhs_len = j;
46921308Sache}
47021308Sache
47121308Sache/* Expand the bulk of a history specifier starting at STRING[START].
47221308Sache   Returns 0 if everything is OK, -1 if an error occurred, and 1
47321308Sache   if the `p' modifier was supplied and the caller should just print
47421308Sache   the returned string.  Returns the new index into string in
47521308Sache   *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
47621308Sachestatic int
47721308Sachehistory_expand_internal (string, start, end_index_ptr, ret_string, current_line)
47821308Sache     char *string;
47921308Sache     int start, *end_index_ptr;
48021308Sache     char **ret_string;
48121308Sache     char *current_line;	/* for !# */
48221308Sache{
48321308Sache  int i, n, starting_index;
48421308Sache  int substitute_globally, want_quotes, print_only;
48521308Sache  char *event, *temp, *result, *tstr, *t, c, *word_spec;
48621308Sache  int result_len;
48721308Sache
48821308Sache  result = xmalloc (result_len = 128);
48921308Sache
49021308Sache  i = start;
49121308Sache
49221308Sache  /* If it is followed by something that starts a word specifier,
49321308Sache     then !! is implied as the event specifier. */
49421308Sache
49521308Sache  if (member (string[i + 1], ":$*%^"))
49621308Sache    {
49721308Sache      char fake_s[3];
49821308Sache      int fake_i = 0;
49921308Sache      i++;
50021308Sache      fake_s[0] = fake_s[1] = history_expansion_char;
50121308Sache      fake_s[2] = '\0';
50221308Sache      event = get_history_event (fake_s, &fake_i, 0);
50321308Sache    }
50421308Sache  else if (string[i + 1] == '#')
50521308Sache    {
50621308Sache      i += 2;
50721308Sache      event = current_line;
50821308Sache    }
50921308Sache  else
51021308Sache    {
51121308Sache      int quoted_search_delimiter = 0;
51221308Sache
51321308Sache      /* If the character before this `!' is a double or single
51421308Sache	 quote, then this expansion takes place inside of the
51521308Sache	 quoted string.  If we have to search for some text ("!foo"),
51621308Sache	 allow the delimiter to end the search string. */
51721308Sache      if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
51821308Sache	quoted_search_delimiter = string[i - 1];
51921308Sache      event = get_history_event (string, &i, quoted_search_delimiter);
52021308Sache    }
52121308Sache
52221308Sache  if (event == 0)
52321308Sache    {
52421308Sache      *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
52521308Sache      free (result);
52621308Sache      return (-1);
52721308Sache    }
52821308Sache
52921308Sache  /* If a word specifier is found, then do what that requires. */
53021308Sache  starting_index = i;
53121308Sache  word_spec = get_history_word_specifier (string, event, &i);
53221308Sache
53321308Sache  /* There is no such thing as a `malformed word specifier'.  However,
53421308Sache     it is possible for a specifier that has no match.  In that case,
53521308Sache     we complain. */
53621308Sache  if (word_spec == (char *)&error_pointer)
53721308Sache    {
53821308Sache      *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
53921308Sache      free (result);
54021308Sache      return (-1);
54121308Sache    }
54221308Sache
54321308Sache  /* If no word specifier, than the thing of interest was the event. */
54421308Sache  temp = word_spec ? savestring (word_spec) : savestring (event);
54521308Sache  FREE (word_spec);
54621308Sache
54721308Sache  /* Perhaps there are other modifiers involved.  Do what they say. */
54821308Sache  want_quotes = substitute_globally = print_only = 0;
54921308Sache  starting_index = i;
55021308Sache
55121308Sache  while (string[i] == ':')
55221308Sache    {
55321308Sache      c = string[i + 1];
55421308Sache
55521308Sache      if (c == 'g')
55621308Sache	{
55721308Sache	  substitute_globally = 1;
55821308Sache	  i++;
55921308Sache	  c = string[i + 1];
56021308Sache	}
56121308Sache
56221308Sache      switch (c)
56321308Sache	{
56421308Sache	default:
56521308Sache	  *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
56621308Sache	  free (result);
56721308Sache	  free (temp);
56821308Sache	  return -1;
56921308Sache
57021308Sache	case 'q':
57121308Sache	  want_quotes = 'q';
57221308Sache	  break;
57321308Sache
57421308Sache	case 'x':
57521308Sache	  want_quotes = 'x';
57621308Sache	  break;
57721308Sache
57821308Sache	  /* :p means make this the last executed line.  So we
57921308Sache	     return an error state after adding this line to the
58021308Sache	     history. */
58121308Sache	case 'p':
58221308Sache	  print_only++;
58321308Sache	  break;
58421308Sache
58521308Sache	  /* :t discards all but the last part of the pathname. */
58621308Sache	case 't':
58721308Sache	  tstr = strrchr (temp, '/');
58821308Sache	  if (tstr)
58921308Sache	    {
59021308Sache	      tstr++;
59121308Sache	      t = savestring (tstr);
59221308Sache	      free (temp);
59321308Sache	      temp = t;
59421308Sache	    }
59521308Sache	  break;
59621308Sache
59721308Sache	  /* :h discards the last part of a pathname. */
59821308Sache	case 'h':
59921308Sache	  tstr = strrchr (temp, '/');
60021308Sache	  if (tstr)
60121308Sache	    *tstr = '\0';
60221308Sache	  break;
60321308Sache
60421308Sache	  /* :r discards the suffix. */
60521308Sache	case 'r':
60621308Sache	  tstr = strrchr (temp, '.');
60721308Sache	  if (tstr)
60821308Sache	    *tstr = '\0';
60921308Sache	  break;
61021308Sache
61121308Sache	  /* :e discards everything but the suffix. */
61221308Sache	case 'e':
61321308Sache	  tstr = strrchr (temp, '.');
61421308Sache	  if (tstr)
61521308Sache	    {
61621308Sache	      t = savestring (tstr);
61721308Sache	      free (temp);
61821308Sache	      temp = t;
61921308Sache	    }
62021308Sache	  break;
62121308Sache
62221308Sache	/* :s/this/that substitutes `that' for the first
62321308Sache	   occurrence of `this'.  :gs/this/that substitutes `that'
62421308Sache	   for each occurrence of `this'.  :& repeats the last
62521308Sache	   substitution.  :g& repeats the last substitution
62621308Sache	   globally. */
62721308Sache
62821308Sache	case '&':
62921308Sache	case 's':
63021308Sache	  {
63175406Sache	    char *new_event;
63221308Sache	    int delimiter, failed, si, l_temp;
63321308Sache
63421308Sache	    if (c == 's')
63521308Sache	      {
63621308Sache		if (i + 2 < (int)strlen (string))
63721308Sache		  delimiter = string[i + 2];
63821308Sache		else
63921308Sache		  break;	/* no search delimiter */
64021308Sache
64121308Sache		i += 3;
64221308Sache
64321308Sache		t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
64421308Sache		/* An empty substitution lhs with no previous substitution
64521308Sache		   uses the last search string as the lhs. */
64621308Sache		if (t)
64721308Sache		  {
64821308Sache		    FREE (subst_lhs);
64921308Sache		    subst_lhs = t;
65021308Sache		  }
65121308Sache		else if (!subst_lhs)
65221308Sache		  {
65321308Sache		    if (search_string && *search_string)
65421308Sache		      {
65521308Sache			subst_lhs = savestring (search_string);
65621308Sache			subst_lhs_len = strlen (subst_lhs);
65721308Sache		      }
65821308Sache		    else
65921308Sache		      {
66021308Sache			subst_lhs = (char *) NULL;
66121308Sache			subst_lhs_len = 0;
66221308Sache		      }
66321308Sache		  }
66421308Sache
66521308Sache		FREE (subst_rhs);
66621308Sache		subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
66721308Sache
66821308Sache		/* If `&' appears in the rhs, it's supposed to be replaced
66921308Sache		   with the lhs. */
67021308Sache		if (member ('&', subst_rhs))
67121308Sache		  postproc_subst_rhs ();
67221308Sache	      }
67321308Sache	    else
67421308Sache	      i += 2;
67521308Sache
67647558Sache	    /* If there is no lhs, the substitution can't succeed. */
67747558Sache	    if (subst_lhs_len == 0)
67847558Sache	      {
67947558Sache		*ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
68047558Sache		free (result);
68147558Sache		free (temp);
68247558Sache		return -1;
68347558Sache	      }
68447558Sache
68521308Sache	    l_temp = strlen (temp);
68621308Sache	    /* Ignore impossible cases. */
68721308Sache	    if (subst_lhs_len > l_temp)
68821308Sache	      {
68921308Sache		*ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
69021308Sache		free (result);
69121308Sache		free (temp);
69221308Sache		return (-1);
69321308Sache	      }
69421308Sache
69521308Sache	    /* Find the first occurrence of THIS in TEMP. */
69621308Sache	    si = 0;
69721308Sache	    for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
69821308Sache	      if (STREQN (temp+si, subst_lhs, subst_lhs_len))
69921308Sache		{
70021308Sache		  int len = subst_rhs_len - subst_lhs_len + l_temp;
70121308Sache		  new_event = xmalloc (1 + len);
70221308Sache		  strncpy (new_event, temp, si);
70321308Sache		  strncpy (new_event + si, subst_rhs, subst_rhs_len);
70421308Sache		  strncpy (new_event + si + subst_rhs_len,
70521308Sache			   temp + si + subst_lhs_len,
70621308Sache			   l_temp - (si + subst_lhs_len));
70721308Sache		  new_event[len] = '\0';
70821308Sache		  free (temp);
70921308Sache		  temp = new_event;
71021308Sache
71121308Sache		  failed = 0;
71221308Sache
71321308Sache		  if (substitute_globally)
71421308Sache		    {
71521308Sache		      si += subst_rhs_len;
71621308Sache		      l_temp = strlen (temp);
71721308Sache		      substitute_globally++;
71821308Sache		      continue;
71921308Sache		    }
72021308Sache		  else
72121308Sache		    break;
72221308Sache		}
72321308Sache
72421308Sache	    if (substitute_globally > 1)
72521308Sache	      {
72621308Sache		substitute_globally = 0;
72721308Sache		continue;	/* don't want to increment i */
72821308Sache	      }
72921308Sache
73021308Sache	    if (failed == 0)
73121308Sache	      continue;		/* don't want to increment i */
73221308Sache
73321308Sache	    *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
73421308Sache	    free (result);
73521308Sache	    free (temp);
73621308Sache	    return (-1);
73721308Sache	  }
73821308Sache	}
73921308Sache      i += 2;
74021308Sache    }
74121308Sache  /* Done with modfiers. */
74221308Sache  /* Believe it or not, we have to back the pointer up by one. */
74321308Sache  --i;
74421308Sache
74521308Sache  if (want_quotes)
74621308Sache    {
74721308Sache      char *x;
74821308Sache
74921308Sache      if (want_quotes == 'q')
75075406Sache	x = sh_single_quote (temp);
75121308Sache      else if (want_quotes == 'x')
75221308Sache	x = quote_breaks (temp);
75321308Sache      else
75421308Sache	x = savestring (temp);
75521308Sache
75621308Sache      free (temp);
75721308Sache      temp = x;
75821308Sache    }
75921308Sache
76021308Sache  n = strlen (temp);
76121308Sache  if (n >= result_len)
76221308Sache    result = xrealloc (result, n + 2);
76321308Sache  strcpy (result, temp);
76421308Sache  free (temp);
76521308Sache
76621308Sache  *end_index_ptr = i;
76721308Sache  *ret_string = result;
76821308Sache  return (print_only);
76921308Sache}
77021308Sache
77121308Sache/* Expand the string STRING, placing the result into OUTPUT, a pointer
77221308Sache   to a string.  Returns:
77321308Sache
77421308Sache  -1) If there was an error in expansion.
77521308Sache   0) If no expansions took place (or, if the only change in
77621308Sache      the text was the de-slashifying of the history expansion
77721308Sache      character)
77821308Sache   1) If expansions did take place
77921308Sache   2) If the `p' modifier was given and the caller should print the result
78021308Sache
78121308Sache  If an error ocurred in expansion, then OUTPUT contains a descriptive
78221308Sache  error message. */
78321308Sache
78421308Sache#define ADD_STRING(s) \
78521308Sache	do \
78621308Sache	  { \
78721308Sache	    int sl = strlen (s); \
78821308Sache	    j += sl; \
78921308Sache	    if (j >= result_len) \
79021308Sache	      { \
79121308Sache		while (j >= result_len) \
79221308Sache		  result_len += 128; \
79321308Sache		result = xrealloc (result, result_len); \
79421308Sache	      } \
79521308Sache	    strcpy (result + j - sl, s); \
79621308Sache	  } \
79721308Sache	while (0)
79821308Sache
79921308Sache#define ADD_CHAR(c) \
80021308Sache	do \
80121308Sache	  { \
80221308Sache	    if (j >= result_len - 1) \
80321308Sache	      result = xrealloc (result, result_len += 64); \
80421308Sache	    result[j++] = c; \
80521308Sache	    result[j] = '\0'; \
80621308Sache	  } \
80721308Sache	while (0)
80821308Sache
80921308Sacheint
81021308Sachehistory_expand (hstring, output)
81121308Sache     char *hstring;
81221308Sache     char **output;
81321308Sache{
81421308Sache  register int j;
81521308Sache  int i, r, l, passc, cc, modified, eindex, only_printing;
81621308Sache  char *string;
81721308Sache
81821308Sache  /* The output string, and its length. */
81921308Sache  int result_len;
82021308Sache  char *result;
82121308Sache
82221308Sache  /* Used when adding the string. */
82321308Sache  char *temp;
82421308Sache
82575406Sache  if (output == 0)
82675406Sache    return 0;
82775406Sache
82821308Sache  /* Setting the history expansion character to 0 inhibits all
82921308Sache     history expansion. */
83021308Sache  if (history_expansion_char == 0)
83121308Sache    {
83221308Sache      *output = savestring (hstring);
83321308Sache      return (0);
83421308Sache    }
83521308Sache
83621308Sache  /* Prepare the buffer for printing error messages. */
83721308Sache  result = xmalloc (result_len = 256);
83821308Sache  result[0] = '\0';
83921308Sache
84021308Sache  only_printing = modified = 0;
84121308Sache  l = strlen (hstring);
84221308Sache
84335486Sache  /* Grovel the string.  Only backslash and single quotes can quote the
84435486Sache     history escape character.  We also handle arg specifiers. */
84521308Sache
84621308Sache  /* Before we grovel forever, see if the history_expansion_char appears
84721308Sache     anywhere within the text. */
84821308Sache
84921308Sache  /* The quick substitution character is a history expansion all right.  That
85021308Sache     is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
85121308Sache     that is the substitution that we do. */
85221308Sache  if (hstring[0] == history_subst_char)
85321308Sache    {
85421308Sache      string = xmalloc (l + 5);
85521308Sache
85621308Sache      string[0] = string[1] = history_expansion_char;
85721308Sache      string[2] = ':';
85821308Sache      string[3] = 's';
85921308Sache      strcpy (string + 4, hstring);
86021308Sache      l += 4;
86121308Sache    }
86221308Sache  else
86321308Sache    {
86421308Sache      string = hstring;
86521308Sache      /* If not quick substitution, still maybe have to do expansion. */
86621308Sache
86721308Sache      /* `!' followed by one of the characters in history_no_expand_chars
86821308Sache	 is NOT an expansion. */
86921308Sache      for (i = 0; string[i]; i++)
87021308Sache	{
87121308Sache	  cc = string[i + 1];
87235486Sache	  /* The history_comment_char, if set, appearing that the beginning
87335486Sache	     of a word signifies that the rest of the line should not have
87435486Sache	     history expansion performed on it.
87535486Sache	     Skip the rest of the line and break out of the loop. */
87635486Sache	  if (history_comment_char && string[i] == history_comment_char &&
87775406Sache	      (i == 0 || member (string[i - 1], history_word_delimiters)))
87821308Sache	    {
87935486Sache	      while (string[i])
88035486Sache		i++;
88135486Sache	      break;
88235486Sache	    }
88335486Sache	  else if (string[i] == history_expansion_char)
88435486Sache	    {
88521308Sache	      if (!cc || member (cc, history_no_expand_chars))
88621308Sache		continue;
88726497Sache	      /* If the calling application has set
88826497Sache		 history_inhibit_expansion_function to a function that checks
88926497Sache		 for special cases that should not be history expanded,
89026497Sache		 call the function and skip the expansion if it returns a
89126497Sache		 non-zero value. */
89226497Sache	      else if (history_inhibit_expansion_function &&
89326497Sache			(*history_inhibit_expansion_function) (string, i))
89421308Sache		continue;
89521308Sache	      else
89621308Sache		break;
89721308Sache	    }
89835486Sache	  /* XXX - at some point, might want to extend this to handle
89935486Sache		   double quotes as well. */
90021308Sache	  else if (history_quotes_inhibit_expansion && string[i] == '\'')
90121308Sache	    {
90221308Sache	      /* If this is bash, single quotes inhibit history expansion. */
90321308Sache	      i++;
90421308Sache	      hist_string_extract_single_quoted (string, &i);
90521308Sache	    }
90621308Sache	  else if (history_quotes_inhibit_expansion && string[i] == '\\')
90721308Sache	    {
90821308Sache	      /* If this is bash, allow backslashes to quote single
90921308Sache		 quotes and the history expansion character. */
91021308Sache	      if (cc == '\'' || cc == history_expansion_char)
91121308Sache		i++;
91221308Sache	    }
91321308Sache	}
91421308Sache
91521308Sache      if (string[i] != history_expansion_char)
91621308Sache	{
91721308Sache	  free (result);
91821308Sache	  *output = savestring (string);
91921308Sache	  return (0);
92021308Sache	}
92121308Sache    }
92221308Sache
92321308Sache  /* Extract and perform the substitution. */
92421308Sache  for (passc = i = j = 0; i < l; i++)
92521308Sache    {
92621308Sache      int tchar = string[i];
92721308Sache
92821308Sache      if (passc)
92921308Sache	{
93021308Sache	  passc = 0;
93121308Sache	  ADD_CHAR (tchar);
93221308Sache	  continue;
93321308Sache	}
93421308Sache
93521308Sache      if (tchar == history_expansion_char)
93621308Sache	tchar = -3;
93735486Sache      else if (tchar == history_comment_char)
93835486Sache	tchar = -2;
93921308Sache
94021308Sache      switch (tchar)
94121308Sache	{
94221308Sache	default:
94321308Sache	  ADD_CHAR (string[i]);
94421308Sache	  break;
94521308Sache
94621308Sache	case '\\':
94721308Sache	  passc++;
94821308Sache	  ADD_CHAR (tchar);
94921308Sache	  break;
95021308Sache
95121308Sache	case '\'':
95221308Sache	  {
95321308Sache	    /* If history_quotes_inhibit_expansion is set, single quotes
95421308Sache	       inhibit history expansion. */
95521308Sache	    if (history_quotes_inhibit_expansion)
95621308Sache	      {
95721308Sache		int quote, slen;
95821308Sache
95921308Sache		quote = i++;
96021308Sache		hist_string_extract_single_quoted (string, &i);
96121308Sache
96221308Sache		slen = i - quote + 2;
96321308Sache		temp = xmalloc (slen);
96421308Sache		strncpy (temp, string + quote, slen);
96521308Sache		temp[slen - 1] = '\0';
96621308Sache		ADD_STRING (temp);
96721308Sache		free (temp);
96821308Sache	      }
96921308Sache	    else
97021308Sache	      ADD_CHAR (string[i]);
97121308Sache	    break;
97221308Sache	  }
97321308Sache
97435486Sache	case -2:		/* history_comment_char */
97575406Sache	  if (i == 0 || member (string[i - 1], history_word_delimiters))
97635486Sache	    {
97735486Sache	      temp = xmalloc (l - i + 1);
97835486Sache	      strcpy (temp, string + i);
97935486Sache	      ADD_STRING (temp);
98035486Sache	      free (temp);
98135486Sache	      i = l;
98235486Sache	    }
98335486Sache	  else
98435486Sache	    ADD_CHAR (string[i]);
98535486Sache	  break;
98635486Sache
98721308Sache	case -3:		/* history_expansion_char */
98821308Sache	  cc = string[i + 1];
98921308Sache
99021308Sache	  /* If the history_expansion_char is followed by one of the
99121308Sache	     characters in history_no_expand_chars, then it is not a
99221308Sache	     candidate for expansion of any kind. */
99321308Sache	  if (member (cc, history_no_expand_chars))
99421308Sache	    {
99521308Sache	      ADD_CHAR (string[i]);
99621308Sache	      break;
99721308Sache	    }
99821308Sache
99921308Sache#if defined (NO_BANG_HASH_MODIFIERS)
100021308Sache	  /* There is something that is listed as a `word specifier' in csh
100121308Sache	     documentation which means `the expanded text to this point'.
100221308Sache	     That is not a word specifier, it is an event specifier.  If we
100321308Sache	     don't want to allow modifiers with `!#', just stick the current
100421308Sache	     output line in again. */
100521308Sache	  if (cc == '#')
100621308Sache	    {
100721308Sache	      if (result)
100821308Sache		{
100921308Sache		  temp = xmalloc (1 + strlen (result));
101021308Sache		  strcpy (temp, result);
101121308Sache		  ADD_STRING (temp);
101221308Sache		  free (temp);
101321308Sache		}
101421308Sache	      i++;
101521308Sache	      break;
101621308Sache	    }
101721308Sache#endif
101821308Sache
101921308Sache	  r = history_expand_internal (string, i, &eindex, &temp, result);
102021308Sache	  if (r < 0)
102121308Sache	    {
102221308Sache	      *output = temp;
102321308Sache	      free (result);
102421308Sache	      if (string != hstring)
102521308Sache		free (string);
102621308Sache	      return -1;
102721308Sache	    }
102821308Sache	  else
102921308Sache	    {
103021308Sache	      if (temp)
103121308Sache		{
103221308Sache		  modified++;
103321308Sache		  if (*temp)
103421308Sache		    ADD_STRING (temp);
103521308Sache		  free (temp);
103621308Sache		}
103721308Sache	      only_printing = r == 1;
103821308Sache	      i = eindex;
103921308Sache	    }
104021308Sache	  break;
104121308Sache	}
104221308Sache    }
104321308Sache
104421308Sache  *output = result;
104521308Sache  if (string != hstring)
104621308Sache    free (string);
104721308Sache
104821308Sache  if (only_printing)
104921308Sache    {
105021308Sache      add_history (result);
105121308Sache      return (2);
105221308Sache    }
105321308Sache
105421308Sache  return (modified != 0);
105521308Sache}
105621308Sache
105721308Sache/* Return a consed string which is the word specified in SPEC, and found
105821308Sache   in FROM.  NULL is returned if there is no spec.  The address of
105921308Sache   ERROR_POINTER is returned if the word specified cannot be found.
106021308Sache   CALLER_INDEX is the offset in SPEC to start looking; it is updated
106121308Sache   to point to just after the last character parsed. */
106221308Sachestatic char *
106321308Sacheget_history_word_specifier (spec, from, caller_index)
106421308Sache     char *spec, *from;
106521308Sache     int *caller_index;
106621308Sache{
106721308Sache  register int i = *caller_index;
106821308Sache  int first, last;
106921308Sache  int expecting_word_spec = 0;
107021308Sache  char *result;
107121308Sache
107221308Sache  /* The range of words to return doesn't exist yet. */
107321308Sache  first = last = 0;
107421308Sache  result = (char *)NULL;
107521308Sache
107621308Sache  /* If we found a colon, then this *must* be a word specification.  If
107721308Sache     it isn't, then it is an error. */
107821308Sache  if (spec[i] == ':')
107921308Sache    {
108021308Sache      i++;
108121308Sache      expecting_word_spec++;
108221308Sache    }
108321308Sache
108421308Sache  /* Handle special cases first. */
108521308Sache
108621308Sache  /* `%' is the word last searched for. */
108721308Sache  if (spec[i] == '%')
108821308Sache    {
108921308Sache      *caller_index = i + 1;
109021308Sache      return (search_match ? savestring (search_match) : savestring (""));
109121308Sache    }
109221308Sache
109321308Sache  /* `*' matches all of the arguments, but not the command. */
109421308Sache  if (spec[i] == '*')
109521308Sache    {
109621308Sache      *caller_index = i + 1;
109721308Sache      result = history_arg_extract (1, '$', from);
109821308Sache      return (result ? result : savestring (""));
109921308Sache    }
110021308Sache
110121308Sache  /* `$' is last arg. */
110221308Sache  if (spec[i] == '$')
110321308Sache    {
110421308Sache      *caller_index = i + 1;
110521308Sache      return (history_arg_extract ('$', '$', from));
110621308Sache    }
110721308Sache
110821308Sache  /* Try to get FIRST and LAST figured out. */
110921308Sache
111021308Sache  if (spec[i] == '-')
111121308Sache    first = 0;
111221308Sache  else if (spec[i] == '^')
111321308Sache    first = 1;
111421308Sache  else if (_rl_digit_p (spec[i]) && expecting_word_spec)
111521308Sache    {
111621308Sache      for (first = 0; _rl_digit_p (spec[i]); i++)
111721308Sache	first = (first * 10) + _rl_digit_value (spec[i]);
111821308Sache    }
111921308Sache  else
112021308Sache    return ((char *)NULL);	/* no valid `first' for word specifier */
112121308Sache
112221308Sache  if (spec[i] == '^' || spec[i] == '*')
112321308Sache    {
112421308Sache      last = (spec[i] == '^') ? 1 : '$';	/* x* abbreviates x-$ */
112521308Sache      i++;
112621308Sache    }
112721308Sache  else if (spec[i] != '-')
112821308Sache    last = first;
112921308Sache  else
113021308Sache    {
113121308Sache      i++;
113221308Sache
113321308Sache      if (_rl_digit_p (spec[i]))
113421308Sache	{
113521308Sache	  for (last = 0; _rl_digit_p (spec[i]); i++)
113621308Sache	    last = (last * 10) + _rl_digit_value (spec[i]);
113721308Sache	}
113821308Sache      else if (spec[i] == '$')
113921308Sache	{
114021308Sache	  i++;
114121308Sache	  last = '$';
114221308Sache	}
114321308Sache      else if (!spec[i] || spec[i] == ':')  /* could be modifier separator */
114421308Sache	last = -1;		/* x- abbreviates x-$ omitting word `$' */
114521308Sache    }
114621308Sache
114721308Sache  *caller_index = i;
114821308Sache
114921308Sache  if (last >= first || last == '$' || last < 0)
115021308Sache    result = history_arg_extract (first, last, from);
115121308Sache
115221308Sache  return (result ? result : (char *)&error_pointer);
115321308Sache}
115421308Sache
115521308Sache/* Extract the args specified, starting at FIRST, and ending at LAST.
115621308Sache   The args are taken from STRING.  If either FIRST or LAST is < 0,
115721308Sache   then make that arg count from the right (subtract from the number of
115821308Sache   tokens, so that FIRST = -1 means the next to last token on the line).
115921308Sache   If LAST is `$' the last arg from STRING is used. */
116021308Sachechar *
116121308Sachehistory_arg_extract (first, last, string)
116221308Sache     int first, last;
116375406Sache     const char *string;
116421308Sache{
116521308Sache  register int i, len;
116621308Sache  char *result;
116721308Sache  int size, offset;
116821308Sache  char **list;
116921308Sache
117021308Sache  /* XXX - think about making history_tokenize return a struct array,
117121308Sache     each struct in array being a string and a length to avoid the
117221308Sache     calls to strlen below. */
117321308Sache  if ((list = history_tokenize (string)) == NULL)
117421308Sache    return ((char *)NULL);
117521308Sache
117621308Sache  for (len = 0; list[len]; len++)
117721308Sache    ;
117821308Sache
117921308Sache  if (last < 0)
118021308Sache    last = len + last - 1;
118121308Sache
118221308Sache  if (first < 0)
118321308Sache    first = len + first - 1;
118421308Sache
118521308Sache  if (last == '$')
118621308Sache    last = len - 1;
118721308Sache
118821308Sache  if (first == '$')
118921308Sache    first = len - 1;
119021308Sache
119121308Sache  last++;
119221308Sache
119321308Sache  if (first >= len || last > len || first < 0 || last < 0 || first > last)
119421308Sache    result = ((char *)NULL);
119521308Sache  else
119621308Sache    {
119721308Sache      for (size = 0, i = first; i < last; i++)
119821308Sache	size += strlen (list[i]) + 1;
119921308Sache      result = xmalloc (size + 1);
120021308Sache      result[0] = '\0';
120121308Sache
120221308Sache      for (i = first, offset = 0; i < last; i++)
120321308Sache	{
120421308Sache	  strcpy (result + offset, list[i]);
120521308Sache	  offset += strlen (list[i]);
120621308Sache	  if (i + 1 < last)
120721308Sache	    {
120821308Sache      	      result[offset++] = ' ';
120921308Sache	      result[offset] = 0;
121021308Sache	    }
121121308Sache	}
121221308Sache    }
121321308Sache
121421308Sache  for (i = 0; i < len; i++)
121521308Sache    free (list[i]);
121621308Sache  free (list);
121721308Sache
121821308Sache  return (result);
121921308Sache}
122021308Sache
122121308Sache#define slashify_in_quotes "\\`\"$"
122221308Sache
122321308Sache/* Parse STRING into tokens and return an array of strings.  If WIND is
122421308Sache   not -1 and INDP is not null, we also want the word surrounding index
122521308Sache   WIND.  The position in the returned array of strings is returned in
122621308Sache   *INDP. */
122721308Sachestatic char **
122821308Sachehistory_tokenize_internal (string, wind, indp)
122975406Sache     const char *string;
123021308Sache     int wind, *indp;
123121308Sache{
123221308Sache  char **result;
123321308Sache  register int i, start, result_index, size;
123421308Sache  int len, delimiter;
123521308Sache
123675406Sache  /* If we're searching for a string that's not part of a word (e.g., " "),
123775406Sache     make sure we set *INDP to a reasonable value. */
123875406Sache  if (indp && wind != -1)
123975406Sache    *indp = -1;
124075406Sache
124121308Sache  /* Get a token, and stuff it into RESULT.  The tokens are split
124221308Sache     exactly where the shell would split them. */
124321308Sache  for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
124421308Sache    {
124521308Sache      delimiter = 0;
124621308Sache
124721308Sache      /* Skip leading whitespace. */
124821308Sache      for (; string[i] && whitespace (string[i]); i++)
124921308Sache	;
125021308Sache      if (string[i] == 0 || string[i] == history_comment_char)
125121308Sache	return (result);
125221308Sache
125321308Sache      start = i;
125421308Sache
125521308Sache      if (member (string[i], "()\n"))
125621308Sache	{
125721308Sache	  i++;
125821308Sache	  goto got_token;
125921308Sache	}
126021308Sache
126121308Sache      if (member (string[i], "<>;&|$"))
126221308Sache	{
126321308Sache	  int peek = string[i + 1];
126421308Sache
126521308Sache	  if (peek == string[i] && peek != '$')
126621308Sache	    {
126721308Sache	      if (peek == '<' && string[i + 2] == '-')
126821308Sache		i++;
126921308Sache	      i += 2;
127021308Sache	      goto got_token;
127121308Sache	    }
127221308Sache	  else
127321308Sache	    {
127421308Sache	      if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
127521308Sache		  ((peek == '>') && (string[i] == '&')) ||
127621308Sache		  ((peek == '(') && (string[i] == '$')))
127721308Sache		{
127821308Sache		  i += 2;
127921308Sache		  goto got_token;
128021308Sache		}
128121308Sache	    }
128221308Sache	  if (string[i] != '$')
128321308Sache	    {
128421308Sache	      i++;
128521308Sache	      goto got_token;
128621308Sache	    }
128721308Sache	}
128821308Sache
128921308Sache      /* Get word from string + i; */
129021308Sache
129135486Sache      if (member (string[i], HISTORY_QUOTE_CHARACTERS))
129221308Sache	delimiter = string[i++];
129321308Sache
129421308Sache      for (; string[i]; i++)
129521308Sache	{
129621308Sache	  if (string[i] == '\\' && string[i + 1] == '\n')
129721308Sache	    {
129821308Sache	      i++;
129921308Sache	      continue;
130021308Sache	    }
130121308Sache
130221308Sache	  if (string[i] == '\\' && delimiter != '\'' &&
130321308Sache	      (delimiter != '"' || member (string[i], slashify_in_quotes)))
130421308Sache	    {
130521308Sache	      i++;
130621308Sache	      continue;
130721308Sache	    }
130821308Sache
130921308Sache	  if (delimiter && string[i] == delimiter)
131021308Sache	    {
131121308Sache	      delimiter = 0;
131221308Sache	      continue;
131321308Sache	    }
131421308Sache
131575406Sache	  if (!delimiter && (member (string[i], history_word_delimiters)))
131621308Sache	    break;
131721308Sache
131835486Sache	  if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS))
131921308Sache	    delimiter = string[i];
132021308Sache	}
132121308Sache
132221308Sache    got_token:
132321308Sache
132421308Sache      /* If we are looking for the word in which the character at a
132521308Sache	 particular index falls, remember it. */
132621308Sache      if (indp && wind != -1 && wind >= start && wind < i)
132721308Sache        *indp = result_index;
132821308Sache
132921308Sache      len = i - start;
133021308Sache      if (result_index + 2 >= size)
133121308Sache	result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
133221308Sache      result[result_index] = xmalloc (1 + len);
133321308Sache      strncpy (result[result_index], string + start, len);
133421308Sache      result[result_index][len] = '\0';
133521308Sache      result[++result_index] = (char *)NULL;
133621308Sache    }
133721308Sache
133821308Sache  return (result);
133921308Sache}
134021308Sache
134121308Sache/* Return an array of tokens, much as the shell might.  The tokens are
134221308Sache   parsed out of STRING. */
134321308Sachechar **
134421308Sachehistory_tokenize (string)
134575406Sache     const char *string;
134621308Sache{
134721308Sache  return (history_tokenize_internal (string, -1, (int *)NULL));
134821308Sache}
134921308Sache
135021308Sache/* Find and return the word which contains the character at index IND
135121308Sache   in the history line LINE.  Used to save the word matched by the
135221308Sache   last history !?string? search. */
135321308Sachestatic char *
135421308Sachehistory_find_word (line, ind)
135521308Sache     char *line;
135621308Sache     int ind;
135721308Sache{
135821308Sache  char **words, *s;
135921308Sache  int i, wind;
136021308Sache
136121308Sache  words = history_tokenize_internal (line, ind, &wind);
136275406Sache  if (wind == -1 || words == 0)
136321308Sache    return ((char *)NULL);
136421308Sache  s = words[wind];
136521308Sache  for (i = 0; i < wind; i++)
136621308Sache    free (words[i]);
136721308Sache  for (i = wind + 1; words[i]; i++)
136821308Sache    free (words[i]);
136921308Sache  free (words);
137021308Sache  return s;
137121308Sache}
1372