histexpand.c revision 119610
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
44119610Sache#include "rlmbutil.h"
4521308Sache
4621308Sache#include "history.h"
4721308Sache#include "histlib.h"
4821308Sache
4958310Sache#include "rlshell.h"
5058310Sache#include "xmalloc.h"
5158310Sache
5235486Sache#define HISTORY_WORD_DELIMITERS		" \t\n;&()|<>"
5335486Sache#define HISTORY_QUOTE_CHARACTERS	"\"'`"
5435486Sache
55119610Sachetypedef int _hist_search_func_t PARAMS((const char *, int));
5675406Sache
57119610Sacheextern int rl_byte_oriented;	/* declared in mbutil.c */
58119610Sache
5921308Sachestatic char error_pointer;
6021308Sache
6121308Sachestatic char *subst_lhs;
6221308Sachestatic char *subst_rhs;
6321308Sachestatic int subst_lhs_len;
6421308Sachestatic int subst_rhs_len;
6521308Sache
66119610Sachestatic char *get_history_word_specifier PARAMS((char *, char *, int *));
67119610Sachestatic char *history_find_word PARAMS((char *, int));
6821308Sache
69119610Sachestatic char *quote_breaks PARAMS((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
9475406Sache/* Used to split words by history_tokenize_internal. */
9575406Sachechar *history_word_delimiters = HISTORY_WORD_DELIMITERS;
9675406Sache
9726497Sache/* If set, this points to a function that is called to verify that a
9826497Sache   particular history expansion should be performed. */
9975406Sacherl_linebuf_func_t *history_inhibit_expansion_function;
10026497Sache
10121308Sache/* **************************************************************** */
10221308Sache/*								    */
10321308Sache/*			History Expansion			    */
10421308Sache/*								    */
10521308Sache/* **************************************************************** */
10621308Sache
10721308Sache/* Hairy history expansion on text, not tokens.  This is of general
10821308Sache   use, and thus belongs in this library. */
10921308Sache
11021308Sache/* The last string searched for by a !?string? search. */
11121308Sachestatic char *search_string;
11221308Sache
11321308Sache/* The last string matched by a !?string? search. */
11421308Sachestatic char *search_match;
11521308Sache
11621308Sache/* Return the event specified at TEXT + OFFSET modifying OFFSET to
11721308Sache   point to after the event specifier.  Just a pointer to the history
11821308Sache   line is returned; NULL is returned in the event of a bad specifier.
11921308Sache   You pass STRING with *INDEX equal to the history_expansion_char that
12021308Sache   begins this specification.
12121308Sache   DELIMITING_QUOTE is a character that is allowed to end the string
12221308Sache   specification for what to search for in addition to the normal
12321308Sache   characters `:', ` ', `\t', `\n', and sometimes `?'.
12421308Sache   So you might call this function like:
12521308Sache   line = get_history_event ("!echo:p", &index, 0);  */
12621308Sachechar *
12721308Sacheget_history_event (string, caller_index, delimiting_quote)
12875406Sache     const char *string;
12921308Sache     int *caller_index;
13021308Sache     int delimiting_quote;
13121308Sache{
13221308Sache  register int i;
13321308Sache  register char c;
13421308Sache  HIST_ENTRY *entry;
13521308Sache  int which, sign, local_index, substring_okay;
13675406Sache  _hist_search_func_t *search_func;
13721308Sache  char *temp;
13821308Sache
13921308Sache  /* The event can be specified in a number of ways.
14021308Sache
14121308Sache     !!   the previous command
14221308Sache     !n   command line N
14321308Sache     !-n  current command-line minus N
14421308Sache     !str the most recent command starting with STR
14521308Sache     !?str[?]
14621308Sache	  the most recent command containing STR
14721308Sache
14821308Sache     All values N are determined via HISTORY_BASE. */
14921308Sache
15021308Sache  i = *caller_index;
15121308Sache
15221308Sache  if (string[i] != history_expansion_char)
15321308Sache    return ((char *)NULL);
15421308Sache
15521308Sache  /* Move on to the specification. */
15621308Sache  i++;
15721308Sache
15821308Sache  sign = 1;
15921308Sache  substring_okay = 0;
16021308Sache
16121308Sache#define RETURN_ENTRY(e, w) \
16221308Sache	return ((e = history_get (w)) ? e->line : (char *)NULL)
16321308Sache
16421308Sache  /* Handle !! case. */
16521308Sache  if (string[i] == history_expansion_char)
16621308Sache    {
16721308Sache      i++;
16821308Sache      which = history_base + (history_length - 1);
16921308Sache      *caller_index = i;
17021308Sache      RETURN_ENTRY (entry, which);
17121308Sache    }
17221308Sache
17321308Sache  /* Hack case of numeric line specification. */
17421308Sache  if (string[i] == '-')
17521308Sache    {
17621308Sache      sign = -1;
17721308Sache      i++;
17821308Sache    }
17921308Sache
18021308Sache  if (_rl_digit_p (string[i]))
18121308Sache    {
18221308Sache      /* Get the extent of the digits and compute the value. */
18321308Sache      for (which = 0; _rl_digit_p (string[i]); i++)
18421308Sache	which = (which * 10) + _rl_digit_value (string[i]);
18521308Sache
18621308Sache      *caller_index = i;
18721308Sache
18821308Sache      if (sign < 0)
18921308Sache	which = (history_length + history_base) - which;
19021308Sache
19121308Sache      RETURN_ENTRY (entry, which);
19221308Sache    }
19321308Sache
19421308Sache  /* This must be something to search for.  If the spec begins with
19521308Sache     a '?', then the string may be anywhere on the line.  Otherwise,
19621308Sache     the string must be found at the start of a line. */
19721308Sache  if (string[i] == '?')
19821308Sache    {
19921308Sache      substring_okay++;
20021308Sache      i++;
20121308Sache    }
20221308Sache
20321308Sache  /* Only a closing `?' or a newline delimit a substring search string. */
20421308Sache  for (local_index = i; c = string[i]; i++)
205119610Sache#if defined (HANDLE_MULTIBYTE)
206119610Sache    if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
207119610Sache      {
208119610Sache	int v;
209119610Sache	mbstate_t ps;
21021308Sache
211119610Sache	memset (&ps, 0, sizeof (mbstate_t));
212119610Sache	/* These produce warnings because we're passing a const string to a
213119610Sache	   function that takes a non-const string. */
214119610Sache	_rl_adjust_point (string, i, &ps);
215119610Sache	if ((v = _rl_get_char_len (string + i, &ps)) > 1)
216119610Sache	  {
217119610Sache	    i += v - 1;
218119610Sache	    continue;
219119610Sache	  }
220119610Sache      }
221119610Sache    else
222119610Sache#endif /* HANDLE_MULTIBYTE */
223119610Sache      if ((!substring_okay && (whitespace (c) || c == ':' ||
224119610Sache	  (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
225119610Sache	  string[i] == delimiting_quote)) ||
226119610Sache	  string[i] == '\n' ||
227119610Sache	  (substring_okay && string[i] == '?'))
228119610Sache	break;
229119610Sache
23021308Sache  which = i - local_index;
231119610Sache  temp = (char *)xmalloc (1 + which);
23221308Sache  if (which)
23321308Sache    strncpy (temp, string + local_index, which);
23421308Sache  temp[which] = '\0';
23521308Sache
23621308Sache  if (substring_okay && string[i] == '?')
23721308Sache    i++;
23821308Sache
23921308Sache  *caller_index = i;
24021308Sache
24121308Sache#define FAIL_SEARCH() \
24221308Sache  do { \
24321308Sache    history_offset = history_length; free (temp) ; return (char *)NULL; \
24421308Sache  } while (0)
24521308Sache
24621308Sache  /* If there is no search string, try to use the previous search string,
24721308Sache     if one exists.  If not, fail immediately. */
24821308Sache  if (*temp == '\0' && substring_okay)
24921308Sache    {
25021308Sache      if (search_string)
25121308Sache        {
25221308Sache          free (temp);
25321308Sache          temp = savestring (search_string);
25421308Sache        }
25521308Sache      else
25621308Sache        FAIL_SEARCH ();
25721308Sache    }
25821308Sache
25921308Sache  search_func = substring_okay ? history_search : history_search_prefix;
26021308Sache  while (1)
26121308Sache    {
26221308Sache      local_index = (*search_func) (temp, -1);
26321308Sache
26421308Sache      if (local_index < 0)
26521308Sache	FAIL_SEARCH ();
26621308Sache
26721308Sache      if (local_index == 0 || substring_okay)
26821308Sache	{
26921308Sache	  entry = current_history ();
27021308Sache	  history_offset = history_length;
27121308Sache
27221308Sache	  /* If this was a substring search, then remember the
27321308Sache	     string that we matched for word substitution. */
27421308Sache	  if (substring_okay)
27521308Sache	    {
27621308Sache	      FREE (search_string);
27721308Sache	      search_string = temp;
27821308Sache
27921308Sache	      FREE (search_match);
28021308Sache	      search_match = history_find_word (entry->line, local_index);
28121308Sache	    }
28221308Sache	  else
28321308Sache	    free (temp);
28421308Sache
28521308Sache	  return (entry->line);
28621308Sache	}
28721308Sache
28821308Sache      if (history_offset)
28921308Sache	history_offset--;
29021308Sache      else
29121308Sache	FAIL_SEARCH ();
29221308Sache    }
29321308Sache#undef FAIL_SEARCH
29421308Sache#undef RETURN_ENTRY
29521308Sache}
29621308Sache
29721308Sache/* Function for extracting single-quoted strings.  Used for inhibiting
29821308Sache   history expansion within single quotes. */
29921308Sache
30021308Sache/* Extract the contents of STRING as if it is enclosed in single quotes.
30121308Sache   SINDEX, when passed in, is the offset of the character immediately
30221308Sache   following the opening single quote; on exit, SINDEX is left pointing
30321308Sache   to the closing single quote. */
30421308Sachestatic void
30521308Sachehist_string_extract_single_quoted (string, sindex)
30621308Sache     char *string;
30721308Sache     int *sindex;
30821308Sache{
30921308Sache  register int i;
31021308Sache
31121308Sache  for (i = *sindex; string[i] && string[i] != '\''; i++)
31221308Sache    ;
31321308Sache
31421308Sache  *sindex = i;
31521308Sache}
31621308Sache
31721308Sachestatic char *
31821308Sachequote_breaks (s)
31921308Sache     char *s;
32021308Sache{
32121308Sache  register char *p, *r;
32221308Sache  char *ret;
32321308Sache  int len = 3;
32421308Sache
32521308Sache  for (p = s; p && *p; p++, len++)
32621308Sache    {
32721308Sache      if (*p == '\'')
32821308Sache	len += 3;
32921308Sache      else if (whitespace (*p) || *p == '\n')
33021308Sache	len += 2;
33121308Sache    }
33221308Sache
333119610Sache  r = ret = (char *)xmalloc (len);
33421308Sache  *r++ = '\'';
33521308Sache  for (p = s; p && *p; )
33621308Sache    {
33721308Sache      if (*p == '\'')
33821308Sache	{
33921308Sache	  *r++ = '\'';
34021308Sache	  *r++ = '\\';
34121308Sache	  *r++ = '\'';
34221308Sache	  *r++ = '\'';
34321308Sache	  p++;
34421308Sache	}
34521308Sache      else if (whitespace (*p) || *p == '\n')
34621308Sache	{
34721308Sache	  *r++ = '\'';
34821308Sache	  *r++ = *p++;
34921308Sache	  *r++ = '\'';
35021308Sache	}
35121308Sache      else
35221308Sache	*r++ = *p++;
35321308Sache    }
35421308Sache  *r++ = '\'';
35521308Sache  *r = '\0';
35621308Sache  return ret;
35721308Sache}
35821308Sache
35921308Sachestatic char *
36021308Sachehist_error(s, start, current, errtype)
36121308Sache      char *s;
36221308Sache      int start, current, errtype;
36321308Sache{
36475406Sache  char *temp;
36575406Sache  const char *emsg;
36621308Sache  int ll, elen;
36721308Sache
36821308Sache  ll = current - start;
36921308Sache
37021308Sache  switch (errtype)
37121308Sache    {
37221308Sache    case EVENT_NOT_FOUND:
37321308Sache      emsg = "event not found";
37421308Sache      elen = 15;
37521308Sache      break;
37621308Sache    case BAD_WORD_SPEC:
37721308Sache      emsg = "bad word specifier";
37821308Sache      elen = 18;
37921308Sache      break;
38021308Sache    case SUBST_FAILED:
38121308Sache      emsg = "substitution failed";
38221308Sache      elen = 19;
38321308Sache      break;
38421308Sache    case BAD_MODIFIER:
38521308Sache      emsg = "unrecognized history modifier";
38621308Sache      elen = 29;
38721308Sache      break;
38847558Sache    case NO_PREV_SUBST:
38947558Sache      emsg = "no previous substitution";
39047558Sache      elen = 24;
39147558Sache      break;
39221308Sache    default:
39321308Sache      emsg = "unknown expansion error";
39421308Sache      elen = 23;
39521308Sache      break;
39621308Sache    }
39721308Sache
398119610Sache  temp = (char *)xmalloc (ll + elen + 3);
39921308Sache  strncpy (temp, s + start, ll);
40021308Sache  temp[ll] = ':';
40121308Sache  temp[ll + 1] = ' ';
40221308Sache  strcpy (temp + ll + 2, emsg);
40321308Sache  return (temp);
40421308Sache}
40521308Sache
40621308Sache/* Get a history substitution string from STR starting at *IPTR
40721308Sache   and return it.  The length is returned in LENPTR.
40821308Sache
40921308Sache   A backslash can quote the delimiter.  If the string is the
41021308Sache   empty string, the previous pattern is used.  If there is
41121308Sache   no previous pattern for the lhs, the last history search
41221308Sache   string is used.
41321308Sache
41421308Sache   If IS_RHS is 1, we ignore empty strings and set the pattern
41521308Sache   to "" anyway.  subst_lhs is not changed if the lhs is empty;
41621308Sache   subst_rhs is allowed to be set to the empty string. */
41721308Sache
41821308Sachestatic char *
41921308Sacheget_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
42021308Sache     char *str;
42121308Sache     int *iptr, delimiter, is_rhs, *lenptr;
42221308Sache{
42321308Sache  register int si, i, j, k;
424119610Sache  char *s;
425119610Sache#if defined (HANDLE_MULTIBYTE)
426119610Sache  mbstate_t ps;
427119610Sache#endif
42821308Sache
429119610Sache  s = (char *)NULL;
43021308Sache  i = *iptr;
43121308Sache
432119610Sache#if defined (HANDLE_MULTIBYTE)
433119610Sache  memset (&ps, 0, sizeof (mbstate_t));
434119610Sache  _rl_adjust_point (str, i, &ps);
435119610Sache#endif
436119610Sache
43721308Sache  for (si = i; str[si] && str[si] != delimiter; si++)
438119610Sache#if defined (HANDLE_MULTIBYTE)
439119610Sache    if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
440119610Sache      {
441119610Sache	int v;
442119610Sache	if ((v = _rl_get_char_len (str + si, &ps)) > 1)
443119610Sache	  si += v - 1;
444119610Sache	else if (str[si] == '\\' && str[si + 1] == delimiter)
445119610Sache	  si++;
446119610Sache      }
447119610Sache    else
448119610Sache#endif /* HANDLE_MULTIBYTE */
449119610Sache      if (str[si] == '\\' && str[si + 1] == delimiter)
450119610Sache	si++;
45121308Sache
45221308Sache  if (si > i || is_rhs)
45321308Sache    {
454119610Sache      s = (char *)xmalloc (si - i + 1);
45521308Sache      for (j = 0, k = i; k < si; j++, k++)
45621308Sache	{
45721308Sache	  /* Remove a backslash quoting the search string delimiter. */
45821308Sache	  if (str[k] == '\\' && str[k + 1] == delimiter)
45921308Sache	    k++;
46021308Sache	  s[j] = str[k];
46121308Sache	}
46221308Sache      s[j] = '\0';
46321308Sache      if (lenptr)
46421308Sache	*lenptr = j;
46521308Sache    }
46621308Sache
46721308Sache  i = si;
46821308Sache  if (str[i])
46921308Sache    i++;
47021308Sache  *iptr = i;
47121308Sache
47221308Sache  return s;
47321308Sache}
47421308Sache
47521308Sachestatic void
47621308Sachepostproc_subst_rhs ()
47721308Sache{
47821308Sache  char *new;
47921308Sache  int i, j, new_size;
48021308Sache
481119610Sache  new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len);
48221308Sache  for (i = j = 0; i < subst_rhs_len; i++)
48321308Sache    {
48421308Sache      if (subst_rhs[i] == '&')
48521308Sache	{
48621308Sache	  if (j + subst_lhs_len >= new_size)
487119610Sache	    new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
48821308Sache	  strcpy (new + j, subst_lhs);
48921308Sache	  j += subst_lhs_len;
49021308Sache	}
49121308Sache      else
49221308Sache	{
49321308Sache	  /* a single backslash protects the `&' from lhs interpolation */
49421308Sache	  if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
49521308Sache	    i++;
49621308Sache	  if (j >= new_size)
497119610Sache	    new = (char *)xrealloc (new, new_size *= 2);
49821308Sache	  new[j++] = subst_rhs[i];
49921308Sache	}
50021308Sache    }
50121308Sache  new[j] = '\0';
50221308Sache  free (subst_rhs);
50321308Sache  subst_rhs = new;
50421308Sache  subst_rhs_len = j;
50521308Sache}
50621308Sache
50721308Sache/* Expand the bulk of a history specifier starting at STRING[START].
50821308Sache   Returns 0 if everything is OK, -1 if an error occurred, and 1
50921308Sache   if the `p' modifier was supplied and the caller should just print
51021308Sache   the returned string.  Returns the new index into string in
51121308Sache   *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
51221308Sachestatic int
51321308Sachehistory_expand_internal (string, start, end_index_ptr, ret_string, current_line)
51421308Sache     char *string;
51521308Sache     int start, *end_index_ptr;
51621308Sache     char **ret_string;
51721308Sache     char *current_line;	/* for !# */
51821308Sache{
51921308Sache  int i, n, starting_index;
52021308Sache  int substitute_globally, want_quotes, print_only;
52121308Sache  char *event, *temp, *result, *tstr, *t, c, *word_spec;
52221308Sache  int result_len;
523119610Sache#if defined (HANDLE_MULTIBYTE)
524119610Sache  mbstate_t ps;
52521308Sache
526119610Sache  memset (&ps, 0, sizeof (mbstate_t));
527119610Sache#endif
52821308Sache
529119610Sache  result = (char *)xmalloc (result_len = 128);
530119610Sache
53121308Sache  i = start;
53221308Sache
53321308Sache  /* If it is followed by something that starts a word specifier,
53421308Sache     then !! is implied as the event specifier. */
53521308Sache
53621308Sache  if (member (string[i + 1], ":$*%^"))
53721308Sache    {
53821308Sache      char fake_s[3];
53921308Sache      int fake_i = 0;
54021308Sache      i++;
54121308Sache      fake_s[0] = fake_s[1] = history_expansion_char;
54221308Sache      fake_s[2] = '\0';
54321308Sache      event = get_history_event (fake_s, &fake_i, 0);
54421308Sache    }
54521308Sache  else if (string[i + 1] == '#')
54621308Sache    {
54721308Sache      i += 2;
54821308Sache      event = current_line;
54921308Sache    }
55021308Sache  else
55121308Sache    {
55221308Sache      int quoted_search_delimiter = 0;
55321308Sache
55421308Sache      /* If the character before this `!' is a double or single
55521308Sache	 quote, then this expansion takes place inside of the
55621308Sache	 quoted string.  If we have to search for some text ("!foo"),
55721308Sache	 allow the delimiter to end the search string. */
558119610Sache#if defined (HANDLE_MULTIBYTE)
559119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
560119610Sache	{
561119610Sache	  int c, l;
562119610Sache	  l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY);
563119610Sache	  c = string[l];
564119610Sache	  /* XXX - original patch had i - 1 ???  If i == 0 it would fail. */
565119610Sache	  if (i && (c == '\'' || c == '"'))
566119610Sache	    quoted_search_delimiter = c;
567119610Sache	}
568119610Sache      else
569119610Sache#endif /* HANDLE_MULTIBYTE */
570119610Sache	if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
571119610Sache	  quoted_search_delimiter = string[i - 1];
572119610Sache
57321308Sache      event = get_history_event (string, &i, quoted_search_delimiter);
57421308Sache    }
57521308Sache
57621308Sache  if (event == 0)
57721308Sache    {
57821308Sache      *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
57921308Sache      free (result);
58021308Sache      return (-1);
58121308Sache    }
58221308Sache
58321308Sache  /* If a word specifier is found, then do what that requires. */
58421308Sache  starting_index = i;
58521308Sache  word_spec = get_history_word_specifier (string, event, &i);
58621308Sache
58721308Sache  /* There is no such thing as a `malformed word specifier'.  However,
58821308Sache     it is possible for a specifier that has no match.  In that case,
58921308Sache     we complain. */
59021308Sache  if (word_spec == (char *)&error_pointer)
59121308Sache    {
59221308Sache      *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
59321308Sache      free (result);
59421308Sache      return (-1);
59521308Sache    }
59621308Sache
59721308Sache  /* If no word specifier, than the thing of interest was the event. */
59821308Sache  temp = word_spec ? savestring (word_spec) : savestring (event);
59921308Sache  FREE (word_spec);
60021308Sache
60121308Sache  /* Perhaps there are other modifiers involved.  Do what they say. */
60221308Sache  want_quotes = substitute_globally = print_only = 0;
60321308Sache  starting_index = i;
60421308Sache
60521308Sache  while (string[i] == ':')
60621308Sache    {
60721308Sache      c = string[i + 1];
60821308Sache
60921308Sache      if (c == 'g')
61021308Sache	{
61121308Sache	  substitute_globally = 1;
61221308Sache	  i++;
61321308Sache	  c = string[i + 1];
61421308Sache	}
61521308Sache
61621308Sache      switch (c)
61721308Sache	{
61821308Sache	default:
61921308Sache	  *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
62021308Sache	  free (result);
62121308Sache	  free (temp);
62221308Sache	  return -1;
62321308Sache
62421308Sache	case 'q':
62521308Sache	  want_quotes = 'q';
62621308Sache	  break;
62721308Sache
62821308Sache	case 'x':
62921308Sache	  want_quotes = 'x';
63021308Sache	  break;
63121308Sache
63221308Sache	  /* :p means make this the last executed line.  So we
63321308Sache	     return an error state after adding this line to the
63421308Sache	     history. */
63521308Sache	case 'p':
63621308Sache	  print_only++;
63721308Sache	  break;
63821308Sache
63921308Sache	  /* :t discards all but the last part of the pathname. */
64021308Sache	case 't':
64121308Sache	  tstr = strrchr (temp, '/');
64221308Sache	  if (tstr)
64321308Sache	    {
64421308Sache	      tstr++;
64521308Sache	      t = savestring (tstr);
64621308Sache	      free (temp);
64721308Sache	      temp = t;
64821308Sache	    }
64921308Sache	  break;
65021308Sache
65121308Sache	  /* :h discards the last part of a pathname. */
65221308Sache	case 'h':
65321308Sache	  tstr = strrchr (temp, '/');
65421308Sache	  if (tstr)
65521308Sache	    *tstr = '\0';
65621308Sache	  break;
65721308Sache
65821308Sache	  /* :r discards the suffix. */
65921308Sache	case 'r':
66021308Sache	  tstr = strrchr (temp, '.');
66121308Sache	  if (tstr)
66221308Sache	    *tstr = '\0';
66321308Sache	  break;
66421308Sache
66521308Sache	  /* :e discards everything but the suffix. */
66621308Sache	case 'e':
66721308Sache	  tstr = strrchr (temp, '.');
66821308Sache	  if (tstr)
66921308Sache	    {
67021308Sache	      t = savestring (tstr);
67121308Sache	      free (temp);
67221308Sache	      temp = t;
67321308Sache	    }
67421308Sache	  break;
67521308Sache
67621308Sache	/* :s/this/that substitutes `that' for the first
67721308Sache	   occurrence of `this'.  :gs/this/that substitutes `that'
67821308Sache	   for each occurrence of `this'.  :& repeats the last
67921308Sache	   substitution.  :g& repeats the last substitution
68021308Sache	   globally. */
68121308Sache
68221308Sache	case '&':
68321308Sache	case 's':
68421308Sache	  {
68575406Sache	    char *new_event;
68621308Sache	    int delimiter, failed, si, l_temp;
68721308Sache
68821308Sache	    if (c == 's')
68921308Sache	      {
69021308Sache		if (i + 2 < (int)strlen (string))
691119610Sache		  {
692119610Sache#if defined (HANDLE_MULTIBYTE)
693119610Sache		    if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
694119610Sache		      {
695119610Sache			_rl_adjust_point (string, i + 2, &ps);
696119610Sache			if (_rl_get_char_len (string + i + 2, &ps) > 1)
697119610Sache			  delimiter = 0;
698119610Sache			else
699119610Sache			  delimiter = string[i + 2];
700119610Sache		      }
701119610Sache		    else
702119610Sache#endif /* HANDLE_MULTIBYTE */
703119610Sache		      delimiter = string[i + 2];
704119610Sache		  }
70521308Sache		else
70621308Sache		  break;	/* no search delimiter */
70721308Sache
70821308Sache		i += 3;
70921308Sache
71021308Sache		t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
71121308Sache		/* An empty substitution lhs with no previous substitution
71221308Sache		   uses the last search string as the lhs. */
71321308Sache		if (t)
71421308Sache		  {
71521308Sache		    FREE (subst_lhs);
71621308Sache		    subst_lhs = t;
71721308Sache		  }
71821308Sache		else if (!subst_lhs)
71921308Sache		  {
72021308Sache		    if (search_string && *search_string)
72121308Sache		      {
72221308Sache			subst_lhs = savestring (search_string);
72321308Sache			subst_lhs_len = strlen (subst_lhs);
72421308Sache		      }
72521308Sache		    else
72621308Sache		      {
72721308Sache			subst_lhs = (char *) NULL;
72821308Sache			subst_lhs_len = 0;
72921308Sache		      }
73021308Sache		  }
73121308Sache
73221308Sache		FREE (subst_rhs);
73321308Sache		subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
73421308Sache
73521308Sache		/* If `&' appears in the rhs, it's supposed to be replaced
73621308Sache		   with the lhs. */
73721308Sache		if (member ('&', subst_rhs))
73821308Sache		  postproc_subst_rhs ();
73921308Sache	      }
74021308Sache	    else
74121308Sache	      i += 2;
74221308Sache
74347558Sache	    /* If there is no lhs, the substitution can't succeed. */
74447558Sache	    if (subst_lhs_len == 0)
74547558Sache	      {
74647558Sache		*ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
74747558Sache		free (result);
74847558Sache		free (temp);
74947558Sache		return -1;
75047558Sache	      }
75147558Sache
75221308Sache	    l_temp = strlen (temp);
75321308Sache	    /* Ignore impossible cases. */
75421308Sache	    if (subst_lhs_len > l_temp)
75521308Sache	      {
75621308Sache		*ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
75721308Sache		free (result);
75821308Sache		free (temp);
75921308Sache		return (-1);
76021308Sache	      }
76121308Sache
76221308Sache	    /* Find the first occurrence of THIS in TEMP. */
76321308Sache	    si = 0;
76421308Sache	    for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
76521308Sache	      if (STREQN (temp+si, subst_lhs, subst_lhs_len))
76621308Sache		{
76721308Sache		  int len = subst_rhs_len - subst_lhs_len + l_temp;
768119610Sache		  new_event = (char *)xmalloc (1 + len);
76921308Sache		  strncpy (new_event, temp, si);
77021308Sache		  strncpy (new_event + si, subst_rhs, subst_rhs_len);
77121308Sache		  strncpy (new_event + si + subst_rhs_len,
77221308Sache			   temp + si + subst_lhs_len,
77321308Sache			   l_temp - (si + subst_lhs_len));
77421308Sache		  new_event[len] = '\0';
77521308Sache		  free (temp);
77621308Sache		  temp = new_event;
77721308Sache
77821308Sache		  failed = 0;
77921308Sache
78021308Sache		  if (substitute_globally)
78121308Sache		    {
78221308Sache		      si += subst_rhs_len;
78321308Sache		      l_temp = strlen (temp);
78421308Sache		      substitute_globally++;
78521308Sache		      continue;
78621308Sache		    }
78721308Sache		  else
78821308Sache		    break;
78921308Sache		}
79021308Sache
79121308Sache	    if (substitute_globally > 1)
79221308Sache	      {
79321308Sache		substitute_globally = 0;
79421308Sache		continue;	/* don't want to increment i */
79521308Sache	      }
79621308Sache
79721308Sache	    if (failed == 0)
79821308Sache	      continue;		/* don't want to increment i */
79921308Sache
80021308Sache	    *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
80121308Sache	    free (result);
80221308Sache	    free (temp);
80321308Sache	    return (-1);
80421308Sache	  }
80521308Sache	}
80621308Sache      i += 2;
80721308Sache    }
80821308Sache  /* Done with modfiers. */
80921308Sache  /* Believe it or not, we have to back the pointer up by one. */
81021308Sache  --i;
81121308Sache
81221308Sache  if (want_quotes)
81321308Sache    {
81421308Sache      char *x;
81521308Sache
81621308Sache      if (want_quotes == 'q')
81775406Sache	x = sh_single_quote (temp);
81821308Sache      else if (want_quotes == 'x')
81921308Sache	x = quote_breaks (temp);
82021308Sache      else
82121308Sache	x = savestring (temp);
82221308Sache
82321308Sache      free (temp);
82421308Sache      temp = x;
82521308Sache    }
82621308Sache
82721308Sache  n = strlen (temp);
82821308Sache  if (n >= result_len)
829119610Sache    result = (char *)xrealloc (result, n + 2);
83021308Sache  strcpy (result, temp);
83121308Sache  free (temp);
83221308Sache
83321308Sache  *end_index_ptr = i;
83421308Sache  *ret_string = result;
83521308Sache  return (print_only);
83621308Sache}
83721308Sache
83821308Sache/* Expand the string STRING, placing the result into OUTPUT, a pointer
83921308Sache   to a string.  Returns:
84021308Sache
84121308Sache  -1) If there was an error in expansion.
84221308Sache   0) If no expansions took place (or, if the only change in
84321308Sache      the text was the de-slashifying of the history expansion
84421308Sache      character)
84521308Sache   1) If expansions did take place
84621308Sache   2) If the `p' modifier was given and the caller should print the result
84721308Sache
84821308Sache  If an error ocurred in expansion, then OUTPUT contains a descriptive
84921308Sache  error message. */
85021308Sache
85121308Sache#define ADD_STRING(s) \
85221308Sache	do \
85321308Sache	  { \
85421308Sache	    int sl = strlen (s); \
85521308Sache	    j += sl; \
85621308Sache	    if (j >= result_len) \
85721308Sache	      { \
85821308Sache		while (j >= result_len) \
85921308Sache		  result_len += 128; \
860119610Sache		result = (char *)xrealloc (result, result_len); \
86121308Sache	      } \
86221308Sache	    strcpy (result + j - sl, s); \
86321308Sache	  } \
86421308Sache	while (0)
86521308Sache
86621308Sache#define ADD_CHAR(c) \
86721308Sache	do \
86821308Sache	  { \
86921308Sache	    if (j >= result_len - 1) \
870119610Sache	      result = (char *)xrealloc (result, result_len += 64); \
87121308Sache	    result[j++] = c; \
87221308Sache	    result[j] = '\0'; \
87321308Sache	  } \
87421308Sache	while (0)
87521308Sache
87621308Sacheint
87721308Sachehistory_expand (hstring, output)
87821308Sache     char *hstring;
87921308Sache     char **output;
88021308Sache{
88121308Sache  register int j;
88221308Sache  int i, r, l, passc, cc, modified, eindex, only_printing;
88321308Sache  char *string;
88421308Sache
88521308Sache  /* The output string, and its length. */
88621308Sache  int result_len;
88721308Sache  char *result;
88821308Sache
889119610Sache#if defined (HANDLE_MULTIBYTE)
890119610Sache  char mb[MB_LEN_MAX];
891119610Sache  mbstate_t ps;
892119610Sache#endif
893119610Sache
89421308Sache  /* Used when adding the string. */
89521308Sache  char *temp;
89621308Sache
89775406Sache  if (output == 0)
89875406Sache    return 0;
89975406Sache
90021308Sache  /* Setting the history expansion character to 0 inhibits all
90121308Sache     history expansion. */
90221308Sache  if (history_expansion_char == 0)
90321308Sache    {
90421308Sache      *output = savestring (hstring);
90521308Sache      return (0);
90621308Sache    }
90721308Sache
90821308Sache  /* Prepare the buffer for printing error messages. */
909119610Sache  result = (char *)xmalloc (result_len = 256);
91021308Sache  result[0] = '\0';
91121308Sache
91221308Sache  only_printing = modified = 0;
91321308Sache  l = strlen (hstring);
91421308Sache
91535486Sache  /* Grovel the string.  Only backslash and single quotes can quote the
91635486Sache     history escape character.  We also handle arg specifiers. */
91721308Sache
91821308Sache  /* Before we grovel forever, see if the history_expansion_char appears
91921308Sache     anywhere within the text. */
92021308Sache
92121308Sache  /* The quick substitution character is a history expansion all right.  That
92221308Sache     is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
92321308Sache     that is the substitution that we do. */
92421308Sache  if (hstring[0] == history_subst_char)
92521308Sache    {
926119610Sache      string = (char *)xmalloc (l + 5);
92721308Sache
92821308Sache      string[0] = string[1] = history_expansion_char;
92921308Sache      string[2] = ':';
93021308Sache      string[3] = 's';
93121308Sache      strcpy (string + 4, hstring);
93221308Sache      l += 4;
93321308Sache    }
93421308Sache  else
93521308Sache    {
936119610Sache#if defined (HANDLE_MULTIBYTE)
937119610Sache      memset (&ps, 0, sizeof (mbstate_t));
938119610Sache#endif
939119610Sache
94021308Sache      string = hstring;
94121308Sache      /* If not quick substitution, still maybe have to do expansion. */
94221308Sache
94321308Sache      /* `!' followed by one of the characters in history_no_expand_chars
94421308Sache	 is NOT an expansion. */
94521308Sache      for (i = 0; string[i]; i++)
94621308Sache	{
947119610Sache#if defined (HANDLE_MULTIBYTE)
948119610Sache	  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
949119610Sache	    {
950119610Sache	      int v;
951119610Sache	      v = _rl_get_char_len (string + i, &ps);
952119610Sache	      if (v > 1)
953119610Sache		{
954119610Sache		  i += v - 1;
955119610Sache		  continue;
956119610Sache		}
957119610Sache	    }
958119610Sache#endif /* HANDLE_MULTIBYTE */
959119610Sache
96021308Sache	  cc = string[i + 1];
961119610Sache	  /* The history_comment_char, if set, appearing at the beginning
96235486Sache	     of a word signifies that the rest of the line should not have
96335486Sache	     history expansion performed on it.
96435486Sache	     Skip the rest of the line and break out of the loop. */
96535486Sache	  if (history_comment_char && string[i] == history_comment_char &&
96675406Sache	      (i == 0 || member (string[i - 1], history_word_delimiters)))
96721308Sache	    {
96835486Sache	      while (string[i])
96935486Sache		i++;
97035486Sache	      break;
97135486Sache	    }
97235486Sache	  else if (string[i] == history_expansion_char)
97335486Sache	    {
97421308Sache	      if (!cc || member (cc, history_no_expand_chars))
97521308Sache		continue;
97626497Sache	      /* If the calling application has set
97726497Sache		 history_inhibit_expansion_function to a function that checks
97826497Sache		 for special cases that should not be history expanded,
97926497Sache		 call the function and skip the expansion if it returns a
98026497Sache		 non-zero value. */
98126497Sache	      else if (history_inhibit_expansion_function &&
98226497Sache			(*history_inhibit_expansion_function) (string, i))
98321308Sache		continue;
98421308Sache	      else
98521308Sache		break;
98621308Sache	    }
98735486Sache	  /* XXX - at some point, might want to extend this to handle
98835486Sache		   double quotes as well. */
98921308Sache	  else if (history_quotes_inhibit_expansion && string[i] == '\'')
99021308Sache	    {
99121308Sache	      /* If this is bash, single quotes inhibit history expansion. */
99221308Sache	      i++;
99321308Sache	      hist_string_extract_single_quoted (string, &i);
99421308Sache	    }
99521308Sache	  else if (history_quotes_inhibit_expansion && string[i] == '\\')
99621308Sache	    {
99721308Sache	      /* If this is bash, allow backslashes to quote single
99821308Sache		 quotes and the history expansion character. */
99921308Sache	      if (cc == '\'' || cc == history_expansion_char)
100021308Sache		i++;
100121308Sache	    }
100221308Sache	}
100321308Sache
100421308Sache      if (string[i] != history_expansion_char)
100521308Sache	{
100621308Sache	  free (result);
100721308Sache	  *output = savestring (string);
100821308Sache	  return (0);
100921308Sache	}
101021308Sache    }
101121308Sache
101221308Sache  /* Extract and perform the substitution. */
101321308Sache  for (passc = i = j = 0; i < l; i++)
101421308Sache    {
101521308Sache      int tchar = string[i];
101621308Sache
101721308Sache      if (passc)
101821308Sache	{
101921308Sache	  passc = 0;
102021308Sache	  ADD_CHAR (tchar);
102121308Sache	  continue;
102221308Sache	}
102321308Sache
1024119610Sache#if defined (HANDLE_MULTIBYTE)
1025119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1026119610Sache	{
1027119610Sache	  int k, c;
1028119610Sache
1029119610Sache	  c = tchar;
1030119610Sache	  memset (mb, 0, sizeof (mb));
1031119610Sache	  for (k = 0; k < MB_LEN_MAX; k++)
1032119610Sache	    {
1033119610Sache	      mb[k] = (char)c;
1034119610Sache	      memset (&ps, 0, sizeof (mbstate_t));
1035119610Sache	      if (_rl_get_char_len (mb, &ps) == -2)
1036119610Sache		c = string[++i];
1037119610Sache	      else
1038119610Sache		break;
1039119610Sache	    }
1040119610Sache	  if (strlen (mb) > 1)
1041119610Sache	    {
1042119610Sache	      ADD_STRING (mb);
1043119610Sache	      break;
1044119610Sache	    }
1045119610Sache	}
1046119610Sache#endif /* HANDLE_MULTIBYTE */
1047119610Sache
104821308Sache      if (tchar == history_expansion_char)
104921308Sache	tchar = -3;
105035486Sache      else if (tchar == history_comment_char)
105135486Sache	tchar = -2;
105221308Sache
105321308Sache      switch (tchar)
105421308Sache	{
105521308Sache	default:
105621308Sache	  ADD_CHAR (string[i]);
105721308Sache	  break;
105821308Sache
105921308Sache	case '\\':
106021308Sache	  passc++;
106121308Sache	  ADD_CHAR (tchar);
106221308Sache	  break;
106321308Sache
106421308Sache	case '\'':
106521308Sache	  {
106621308Sache	    /* If history_quotes_inhibit_expansion is set, single quotes
106721308Sache	       inhibit history expansion. */
106821308Sache	    if (history_quotes_inhibit_expansion)
106921308Sache	      {
107021308Sache		int quote, slen;
107121308Sache
107221308Sache		quote = i++;
107321308Sache		hist_string_extract_single_quoted (string, &i);
107421308Sache
107521308Sache		slen = i - quote + 2;
1076119610Sache		temp = (char *)xmalloc (slen);
107721308Sache		strncpy (temp, string + quote, slen);
107821308Sache		temp[slen - 1] = '\0';
107921308Sache		ADD_STRING (temp);
108021308Sache		free (temp);
108121308Sache	      }
108221308Sache	    else
108321308Sache	      ADD_CHAR (string[i]);
108421308Sache	    break;
108521308Sache	  }
108621308Sache
108735486Sache	case -2:		/* history_comment_char */
108875406Sache	  if (i == 0 || member (string[i - 1], history_word_delimiters))
108935486Sache	    {
1090119610Sache	      temp = (char *)xmalloc (l - i + 1);
109135486Sache	      strcpy (temp, string + i);
109235486Sache	      ADD_STRING (temp);
109335486Sache	      free (temp);
109435486Sache	      i = l;
109535486Sache	    }
109635486Sache	  else
109735486Sache	    ADD_CHAR (string[i]);
109835486Sache	  break;
109935486Sache
110021308Sache	case -3:		/* history_expansion_char */
110121308Sache	  cc = string[i + 1];
110221308Sache
110321308Sache	  /* If the history_expansion_char is followed by one of the
110421308Sache	     characters in history_no_expand_chars, then it is not a
110521308Sache	     candidate for expansion of any kind. */
110621308Sache	  if (member (cc, history_no_expand_chars))
110721308Sache	    {
110821308Sache	      ADD_CHAR (string[i]);
110921308Sache	      break;
111021308Sache	    }
111121308Sache
111221308Sache#if defined (NO_BANG_HASH_MODIFIERS)
111321308Sache	  /* There is something that is listed as a `word specifier' in csh
111421308Sache	     documentation which means `the expanded text to this point'.
111521308Sache	     That is not a word specifier, it is an event specifier.  If we
111621308Sache	     don't want to allow modifiers with `!#', just stick the current
111721308Sache	     output line in again. */
111821308Sache	  if (cc == '#')
111921308Sache	    {
112021308Sache	      if (result)
112121308Sache		{
1122119610Sache		  temp = (char *)xmalloc (1 + strlen (result));
112321308Sache		  strcpy (temp, result);
112421308Sache		  ADD_STRING (temp);
112521308Sache		  free (temp);
112621308Sache		}
112721308Sache	      i++;
112821308Sache	      break;
112921308Sache	    }
113021308Sache#endif
113121308Sache
113221308Sache	  r = history_expand_internal (string, i, &eindex, &temp, result);
113321308Sache	  if (r < 0)
113421308Sache	    {
113521308Sache	      *output = temp;
113621308Sache	      free (result);
113721308Sache	      if (string != hstring)
113821308Sache		free (string);
113921308Sache	      return -1;
114021308Sache	    }
114121308Sache	  else
114221308Sache	    {
114321308Sache	      if (temp)
114421308Sache		{
114521308Sache		  modified++;
114621308Sache		  if (*temp)
114721308Sache		    ADD_STRING (temp);
114821308Sache		  free (temp);
114921308Sache		}
115021308Sache	      only_printing = r == 1;
115121308Sache	      i = eindex;
115221308Sache	    }
115321308Sache	  break;
115421308Sache	}
115521308Sache    }
115621308Sache
115721308Sache  *output = result;
115821308Sache  if (string != hstring)
115921308Sache    free (string);
116021308Sache
116121308Sache  if (only_printing)
116221308Sache    {
116321308Sache      add_history (result);
116421308Sache      return (2);
116521308Sache    }
116621308Sache
116721308Sache  return (modified != 0);
116821308Sache}
116921308Sache
117021308Sache/* Return a consed string which is the word specified in SPEC, and found
117121308Sache   in FROM.  NULL is returned if there is no spec.  The address of
117221308Sache   ERROR_POINTER is returned if the word specified cannot be found.
117321308Sache   CALLER_INDEX is the offset in SPEC to start looking; it is updated
117421308Sache   to point to just after the last character parsed. */
117521308Sachestatic char *
117621308Sacheget_history_word_specifier (spec, from, caller_index)
117721308Sache     char *spec, *from;
117821308Sache     int *caller_index;
117921308Sache{
118021308Sache  register int i = *caller_index;
118121308Sache  int first, last;
118221308Sache  int expecting_word_spec = 0;
118321308Sache  char *result;
118421308Sache
118521308Sache  /* The range of words to return doesn't exist yet. */
118621308Sache  first = last = 0;
118721308Sache  result = (char *)NULL;
118821308Sache
118921308Sache  /* If we found a colon, then this *must* be a word specification.  If
119021308Sache     it isn't, then it is an error. */
119121308Sache  if (spec[i] == ':')
119221308Sache    {
119321308Sache      i++;
119421308Sache      expecting_word_spec++;
119521308Sache    }
119621308Sache
119721308Sache  /* Handle special cases first. */
119821308Sache
119921308Sache  /* `%' is the word last searched for. */
120021308Sache  if (spec[i] == '%')
120121308Sache    {
120221308Sache      *caller_index = i + 1;
120321308Sache      return (search_match ? savestring (search_match) : savestring (""));
120421308Sache    }
120521308Sache
120621308Sache  /* `*' matches all of the arguments, but not the command. */
120721308Sache  if (spec[i] == '*')
120821308Sache    {
120921308Sache      *caller_index = i + 1;
121021308Sache      result = history_arg_extract (1, '$', from);
121121308Sache      return (result ? result : savestring (""));
121221308Sache    }
121321308Sache
121421308Sache  /* `$' is last arg. */
121521308Sache  if (spec[i] == '$')
121621308Sache    {
121721308Sache      *caller_index = i + 1;
121821308Sache      return (history_arg_extract ('$', '$', from));
121921308Sache    }
122021308Sache
122121308Sache  /* Try to get FIRST and LAST figured out. */
122221308Sache
122321308Sache  if (spec[i] == '-')
122421308Sache    first = 0;
122521308Sache  else if (spec[i] == '^')
122621308Sache    first = 1;
122721308Sache  else if (_rl_digit_p (spec[i]) && expecting_word_spec)
122821308Sache    {
122921308Sache      for (first = 0; _rl_digit_p (spec[i]); i++)
123021308Sache	first = (first * 10) + _rl_digit_value (spec[i]);
123121308Sache    }
123221308Sache  else
123321308Sache    return ((char *)NULL);	/* no valid `first' for word specifier */
123421308Sache
123521308Sache  if (spec[i] == '^' || spec[i] == '*')
123621308Sache    {
123721308Sache      last = (spec[i] == '^') ? 1 : '$';	/* x* abbreviates x-$ */
123821308Sache      i++;
123921308Sache    }
124021308Sache  else if (spec[i] != '-')
124121308Sache    last = first;
124221308Sache  else
124321308Sache    {
124421308Sache      i++;
124521308Sache
124621308Sache      if (_rl_digit_p (spec[i]))
124721308Sache	{
124821308Sache	  for (last = 0; _rl_digit_p (spec[i]); i++)
124921308Sache	    last = (last * 10) + _rl_digit_value (spec[i]);
125021308Sache	}
125121308Sache      else if (spec[i] == '$')
125221308Sache	{
125321308Sache	  i++;
125421308Sache	  last = '$';
125521308Sache	}
1256119610Sache#if 0
1257119610Sache      else if (!spec[i] || spec[i] == ':')
1258119610Sache	/* check against `:' because there could be a modifier separator */
1259119610Sache#else
1260119610Sache      else
1261119610Sache	/* csh seems to allow anything to terminate the word spec here,
1262119610Sache	   leaving it as an abbreviation. */
1263119610Sache#endif
126421308Sache	last = -1;		/* x- abbreviates x-$ omitting word `$' */
126521308Sache    }
126621308Sache
126721308Sache  *caller_index = i;
126821308Sache
126921308Sache  if (last >= first || last == '$' || last < 0)
127021308Sache    result = history_arg_extract (first, last, from);
127121308Sache
127221308Sache  return (result ? result : (char *)&error_pointer);
127321308Sache}
127421308Sache
127521308Sache/* Extract the args specified, starting at FIRST, and ending at LAST.
127621308Sache   The args are taken from STRING.  If either FIRST or LAST is < 0,
127721308Sache   then make that arg count from the right (subtract from the number of
127821308Sache   tokens, so that FIRST = -1 means the next to last token on the line).
127921308Sache   If LAST is `$' the last arg from STRING is used. */
128021308Sachechar *
128121308Sachehistory_arg_extract (first, last, string)
128221308Sache     int first, last;
128375406Sache     const char *string;
128421308Sache{
128521308Sache  register int i, len;
128621308Sache  char *result;
128721308Sache  int size, offset;
128821308Sache  char **list;
128921308Sache
129021308Sache  /* XXX - think about making history_tokenize return a struct array,
129121308Sache     each struct in array being a string and a length to avoid the
129221308Sache     calls to strlen below. */
129321308Sache  if ((list = history_tokenize (string)) == NULL)
129421308Sache    return ((char *)NULL);
129521308Sache
129621308Sache  for (len = 0; list[len]; len++)
129721308Sache    ;
129821308Sache
129921308Sache  if (last < 0)
130021308Sache    last = len + last - 1;
130121308Sache
130221308Sache  if (first < 0)
130321308Sache    first = len + first - 1;
130421308Sache
130521308Sache  if (last == '$')
130621308Sache    last = len - 1;
130721308Sache
130821308Sache  if (first == '$')
130921308Sache    first = len - 1;
131021308Sache
131121308Sache  last++;
131221308Sache
131321308Sache  if (first >= len || last > len || first < 0 || last < 0 || first > last)
131421308Sache    result = ((char *)NULL);
131521308Sache  else
131621308Sache    {
131721308Sache      for (size = 0, i = first; i < last; i++)
131821308Sache	size += strlen (list[i]) + 1;
1319119610Sache      result = (char *)xmalloc (size + 1);
132021308Sache      result[0] = '\0';
132121308Sache
132221308Sache      for (i = first, offset = 0; i < last; i++)
132321308Sache	{
132421308Sache	  strcpy (result + offset, list[i]);
132521308Sache	  offset += strlen (list[i]);
132621308Sache	  if (i + 1 < last)
132721308Sache	    {
132821308Sache      	      result[offset++] = ' ';
132921308Sache	      result[offset] = 0;
133021308Sache	    }
133121308Sache	}
133221308Sache    }
133321308Sache
133421308Sache  for (i = 0; i < len; i++)
133521308Sache    free (list[i]);
133621308Sache  free (list);
133721308Sache
133821308Sache  return (result);
133921308Sache}
134021308Sache
134121308Sache#define slashify_in_quotes "\\`\"$"
134221308Sache
134321308Sache/* Parse STRING into tokens and return an array of strings.  If WIND is
134421308Sache   not -1 and INDP is not null, we also want the word surrounding index
134521308Sache   WIND.  The position in the returned array of strings is returned in
134621308Sache   *INDP. */
134721308Sachestatic char **
134821308Sachehistory_tokenize_internal (string, wind, indp)
134975406Sache     const char *string;
135021308Sache     int wind, *indp;
135121308Sache{
135221308Sache  char **result;
135321308Sache  register int i, start, result_index, size;
135421308Sache  int len, delimiter;
135521308Sache
135675406Sache  /* If we're searching for a string that's not part of a word (e.g., " "),
135775406Sache     make sure we set *INDP to a reasonable value. */
135875406Sache  if (indp && wind != -1)
135975406Sache    *indp = -1;
136075406Sache
136121308Sache  /* Get a token, and stuff it into RESULT.  The tokens are split
136221308Sache     exactly where the shell would split them. */
136321308Sache  for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
136421308Sache    {
136521308Sache      delimiter = 0;
136621308Sache
136721308Sache      /* Skip leading whitespace. */
136821308Sache      for (; string[i] && whitespace (string[i]); i++)
136921308Sache	;
137021308Sache      if (string[i] == 0 || string[i] == history_comment_char)
137121308Sache	return (result);
137221308Sache
137321308Sache      start = i;
137421308Sache
137521308Sache      if (member (string[i], "()\n"))
137621308Sache	{
137721308Sache	  i++;
137821308Sache	  goto got_token;
137921308Sache	}
138021308Sache
138121308Sache      if (member (string[i], "<>;&|$"))
138221308Sache	{
138321308Sache	  int peek = string[i + 1];
138421308Sache
138521308Sache	  if (peek == string[i] && peek != '$')
138621308Sache	    {
138721308Sache	      if (peek == '<' && string[i + 2] == '-')
138821308Sache		i++;
138921308Sache	      i += 2;
139021308Sache	      goto got_token;
139121308Sache	    }
139221308Sache	  else
139321308Sache	    {
139421308Sache	      if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
139521308Sache		  ((peek == '>') && (string[i] == '&')) ||
139621308Sache		  ((peek == '(') && (string[i] == '$')))
139721308Sache		{
139821308Sache		  i += 2;
139921308Sache		  goto got_token;
140021308Sache		}
140121308Sache	    }
140221308Sache	  if (string[i] != '$')
140321308Sache	    {
140421308Sache	      i++;
140521308Sache	      goto got_token;
140621308Sache	    }
140721308Sache	}
140821308Sache
140921308Sache      /* Get word from string + i; */
141021308Sache
141135486Sache      if (member (string[i], HISTORY_QUOTE_CHARACTERS))
141221308Sache	delimiter = string[i++];
141321308Sache
141421308Sache      for (; string[i]; i++)
141521308Sache	{
141621308Sache	  if (string[i] == '\\' && string[i + 1] == '\n')
141721308Sache	    {
141821308Sache	      i++;
141921308Sache	      continue;
142021308Sache	    }
142121308Sache
142221308Sache	  if (string[i] == '\\' && delimiter != '\'' &&
142321308Sache	      (delimiter != '"' || member (string[i], slashify_in_quotes)))
142421308Sache	    {
142521308Sache	      i++;
142621308Sache	      continue;
142721308Sache	    }
142821308Sache
142921308Sache	  if (delimiter && string[i] == delimiter)
143021308Sache	    {
143121308Sache	      delimiter = 0;
143221308Sache	      continue;
143321308Sache	    }
143421308Sache
143575406Sache	  if (!delimiter && (member (string[i], history_word_delimiters)))
143621308Sache	    break;
143721308Sache
143835486Sache	  if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS))
143921308Sache	    delimiter = string[i];
144021308Sache	}
144121308Sache
144221308Sache    got_token:
144321308Sache
144421308Sache      /* If we are looking for the word in which the character at a
144521308Sache	 particular index falls, remember it. */
144621308Sache      if (indp && wind != -1 && wind >= start && wind < i)
144721308Sache        *indp = result_index;
144821308Sache
144921308Sache      len = i - start;
145021308Sache      if (result_index + 2 >= size)
145121308Sache	result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
1452119610Sache      result[result_index] = (char *)xmalloc (1 + len);
145321308Sache      strncpy (result[result_index], string + start, len);
145421308Sache      result[result_index][len] = '\0';
145521308Sache      result[++result_index] = (char *)NULL;
145621308Sache    }
145721308Sache
145821308Sache  return (result);
145921308Sache}
146021308Sache
146121308Sache/* Return an array of tokens, much as the shell might.  The tokens are
146221308Sache   parsed out of STRING. */
146321308Sachechar **
146421308Sachehistory_tokenize (string)
146575406Sache     const char *string;
146621308Sache{
146721308Sache  return (history_tokenize_internal (string, -1, (int *)NULL));
146821308Sache}
146921308Sache
147021308Sache/* Find and return the word which contains the character at index IND
147121308Sache   in the history line LINE.  Used to save the word matched by the
147221308Sache   last history !?string? search. */
147321308Sachestatic char *
147421308Sachehistory_find_word (line, ind)
147521308Sache     char *line;
147621308Sache     int ind;
147721308Sache{
147821308Sache  char **words, *s;
147921308Sache  int i, wind;
148021308Sache
148121308Sache  words = history_tokenize_internal (line, ind, &wind);
148275406Sache  if (wind == -1 || words == 0)
148321308Sache    return ((char *)NULL);
148421308Sache  s = words[wind];
148521308Sache  for (i = 0; i < wind; i++)
148621308Sache    free (words[i]);
148721308Sache  for (i = wind + 1; words[i]; i++)
148821308Sache    free (words[i]);
148921308Sache  free (words);
149021308Sache  return s;
149121308Sache}
1492