Blame histexpand.c

Packit a71c51
/* histexpand.c -- history expansion. */
Packit a71c51
Packit a71c51
/* Copyright (C) 1989-2010 Free Software Foundation, Inc.
Packit a71c51
Packit a71c51
   This file contains the GNU History Library (History), a set of
Packit a71c51
   routines for managing the text of previously typed lines.
Packit a71c51
Packit a71c51
   History is free software: you can redistribute it and/or modify
Packit a71c51
   it under the terms of the GNU General Public License as published by
Packit a71c51
   the Free Software Foundation, either version 3 of the License, or
Packit a71c51
   (at your option) any later version.
Packit a71c51
Packit a71c51
   History is distributed in the hope that it will be useful,
Packit a71c51
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a71c51
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit a71c51
   GNU General Public License for more details.
Packit a71c51
Packit a71c51
   You should have received a copy of the GNU General Public License
Packit a71c51
   along with History.  If not, see <http://www.gnu.org/licenses/>.
Packit a71c51
*/
Packit a71c51
Packit a71c51
#define READLINE_LIBRARY
Packit a71c51
Packit a71c51
#if defined (HAVE_CONFIG_H)
Packit a71c51
#  include <config.h>
Packit a71c51
#endif
Packit a71c51
Packit a71c51
#include <stdio.h>
Packit a71c51
Packit a71c51
#if defined (HAVE_STDLIB_H)
Packit a71c51
#  include <stdlib.h>
Packit a71c51
#else
Packit a71c51
#  include "ansi_stdlib.h"
Packit a71c51
#endif /* HAVE_STDLIB_H */
Packit a71c51
Packit a71c51
#if defined (HAVE_UNISTD_H)
Packit a71c51
#  ifndef _MINIX
Packit a71c51
#    include <sys/types.h>
Packit a71c51
#  endif
Packit a71c51
#  include <unistd.h>
Packit a71c51
#endif
Packit a71c51
Packit a71c51
#include "rlmbutil.h"
Packit a71c51
Packit a71c51
#include "history.h"
Packit a71c51
#include "histlib.h"
Packit a71c51
Packit a71c51
#include "rlshell.h"
Packit a71c51
#include "xmalloc.h"
Packit a71c51
Packit a71c51
#define HISTORY_WORD_DELIMITERS		" \t\n;&()|<>"
Packit a71c51
#define HISTORY_QUOTE_CHARACTERS	"\"'`"
Packit a71c51
Packit a71c51
#define slashify_in_quotes "\\`\"$"
Packit a71c51
Packit a71c51
typedef int _hist_search_func_t PARAMS((const char *, int));
Packit a71c51
Packit a71c51
static char error_pointer;
Packit a71c51
Packit a71c51
static char *subst_lhs;
Packit a71c51
static char *subst_rhs;
Packit a71c51
static int subst_lhs_len;
Packit a71c51
static int subst_rhs_len;
Packit a71c51
Packit a71c51
static char *get_history_word_specifier PARAMS((char *, char *, int *));
Packit a71c51
static int history_tokenize_word PARAMS((const char *, int));
Packit a71c51
static char **history_tokenize_internal PARAMS((const char *, int, int *));
Packit a71c51
static char *history_substring PARAMS((const char *, int, int));
Packit a71c51
static void freewords PARAMS((char **, int));
Packit a71c51
static char *history_find_word PARAMS((char *, int));
Packit a71c51
Packit a71c51
static char *quote_breaks PARAMS((char *));
Packit a71c51
Packit a71c51
/* Variables exported by this file. */
Packit a71c51
/* The character that represents the start of a history expansion
Packit a71c51
   request.  This is usually `!'. */
Packit a71c51
char history_expansion_char = '!';
Packit a71c51
Packit a71c51
/* The character that invokes word substitution if found at the start of
Packit a71c51
   a line.  This is usually `^'. */
Packit a71c51
char history_subst_char = '^';
Packit a71c51
Packit a71c51
/* During tokenization, if this character is seen as the first character
Packit a71c51
   of a word, then it, and all subsequent characters upto a newline are
Packit a71c51
   ignored.  For a Bourne shell, this should be '#'.  Bash special cases
Packit a71c51
   the interactive comment character to not be a comment delimiter. */
Packit a71c51
char history_comment_char = '\0';
Packit a71c51
Packit a71c51
/* The list of characters which inhibit the expansion of text if found
Packit a71c51
   immediately following history_expansion_char. */
Packit a71c51
char *history_no_expand_chars = " \t\n\r=";
Packit a71c51
Packit a71c51
/* If set to a non-zero value, single quotes inhibit history expansion.
Packit a71c51
   The default is 0. */
Packit a71c51
int history_quotes_inhibit_expansion = 0;
Packit a71c51
Packit a71c51
/* Used to split words by history_tokenize_internal. */
Packit a71c51
char *history_word_delimiters = HISTORY_WORD_DELIMITERS;
Packit a71c51
Packit a71c51
/* If set, this points to a function that is called to verify that a
Packit a71c51
   particular history expansion should be performed. */
Packit a71c51
rl_linebuf_func_t *history_inhibit_expansion_function;
Packit a71c51
Packit a71c51
/* **************************************************************** */
Packit a71c51
/*								    */
Packit a71c51
/*			History Expansion			    */
Packit a71c51
/*								    */
Packit a71c51
/* **************************************************************** */
Packit a71c51
Packit a71c51
/* Hairy history expansion on text, not tokens.  This is of general
Packit a71c51
   use, and thus belongs in this library. */
Packit a71c51
Packit a71c51
/* The last string searched for by a !?string? search. */
Packit a71c51
static char *search_string;
Packit a71c51
Packit a71c51
/* The last string matched by a !?string? search. */
Packit a71c51
static char *search_match;
Packit a71c51
Packit a71c51
/* Return the event specified at TEXT + OFFSET modifying OFFSET to
Packit a71c51
   point to after the event specifier.  Just a pointer to the history
Packit a71c51
   line is returned; NULL is returned in the event of a bad specifier.
Packit a71c51
   You pass STRING with *INDEX equal to the history_expansion_char that
Packit a71c51
   begins this specification.
Packit a71c51
   DELIMITING_QUOTE is a character that is allowed to end the string
Packit a71c51
   specification for what to search for in addition to the normal
Packit a71c51
   characters `:', ` ', `\t', `\n', and sometimes `?'.
Packit a71c51
   So you might call this function like:
Packit a71c51
   line = get_history_event ("!echo:p", &index, 0);  */
Packit a71c51
char *
Packit a71c51
get_history_event (string, caller_index, delimiting_quote)
Packit a71c51
     const char *string;
Packit a71c51
     int *caller_index;
Packit a71c51
     int delimiting_quote;
Packit a71c51
{
Packit a71c51
  register int i;
Packit a71c51
  register char c;
Packit a71c51
  HIST_ENTRY *entry;
Packit a71c51
  int which, sign, local_index, substring_okay;
Packit a71c51
  _hist_search_func_t *search_func;
Packit a71c51
  char *temp;
Packit a71c51
Packit a71c51
  /* The event can be specified in a number of ways.
Packit a71c51
Packit a71c51
     !!   the previous command
Packit a71c51
     !n   command line N
Packit a71c51
     !-n  current command-line minus N
Packit a71c51
     !str the most recent command starting with STR
Packit a71c51
     !?str[?]
Packit a71c51
	  the most recent command containing STR
Packit a71c51
Packit a71c51
     All values N are determined via HISTORY_BASE. */
Packit a71c51
Packit a71c51
  i = *caller_index;
Packit a71c51
Packit a71c51
  if (string[i] != history_expansion_char)
Packit a71c51
    return ((char *)NULL);
Packit a71c51
Packit a71c51
  /* Move on to the specification. */
Packit a71c51
  i++;
Packit a71c51
Packit a71c51
  sign = 1;
Packit a71c51
  substring_okay = 0;
Packit a71c51
Packit a71c51
#define RETURN_ENTRY(e, w) \
Packit a71c51
	return ((e = history_get (w)) ? e->line : (char *)NULL)
Packit a71c51
Packit a71c51
  /* Handle !! case. */
Packit a71c51
  if (string[i] == history_expansion_char)
Packit a71c51
    {
Packit a71c51
      i++;
Packit a71c51
      which = history_base + (history_length - 1);
Packit a71c51
      *caller_index = i;
Packit a71c51
      RETURN_ENTRY (entry, which);
Packit a71c51
    }
Packit a71c51
Packit a71c51
  /* Hack case of numeric line specification. */
Packit a71c51
  if (string[i] == '-')
Packit a71c51
    {
Packit a71c51
      sign = -1;
Packit a71c51
      i++;
Packit a71c51
    }
Packit a71c51
Packit a71c51
  if (_rl_digit_p (string[i]))
Packit a71c51
    {
Packit a71c51
      /* Get the extent of the digits and compute the value. */
Packit a71c51
      for (which = 0; _rl_digit_p (string[i]); i++)
Packit a71c51
	which = (which * 10) + _rl_digit_value (string[i]);
Packit a71c51
Packit a71c51
      *caller_index = i;
Packit a71c51
Packit a71c51
      if (sign < 0)
Packit a71c51
	which = (history_length + history_base) - which;
Packit a71c51
Packit a71c51
      RETURN_ENTRY (entry, which);
Packit a71c51
    }
Packit a71c51
Packit a71c51
  /* This must be something to search for.  If the spec begins with
Packit a71c51
     a '?', then the string may be anywhere on the line.  Otherwise,
Packit a71c51
     the string must be found at the start of a line. */
Packit a71c51
  if (string[i] == '?')
Packit a71c51
    {
Packit a71c51
      substring_okay++;
Packit a71c51
      i++;
Packit a71c51
    }
Packit a71c51
Packit a71c51
  /* Only a closing `?' or a newline delimit a substring search string. */
Packit a71c51
  for (local_index = i; c = string[i]; i++)
Packit a71c51
    {
Packit a71c51
#if defined (HANDLE_MULTIBYTE)
Packit a71c51
      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
Packit a71c51
	{
Packit a71c51
	  int v;
Packit a71c51
	  mbstate_t ps;
Packit a71c51
Packit a71c51
	  memset (&ps, 0, sizeof (mbstate_t));
Packit a71c51
	  /* These produce warnings because we're passing a const string to a
Packit a71c51
	     function that takes a non-const string. */
Packit a71c51
	  _rl_adjust_point ((char *)string, i, &ps);
Packit a71c51
	  if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1)
Packit a71c51
	    {
Packit a71c51
	      i += v - 1;
Packit a71c51
	      continue;
Packit a71c51
	    }
Packit a71c51
        }
Packit a71c51
Packit a71c51
#endif /* HANDLE_MULTIBYTE */
Packit a71c51
      if ((!substring_okay && (whitespace (c) || c == ':' ||
Packit a71c51
	  (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
Packit a71c51
	  string[i] == delimiting_quote)) ||
Packit a71c51
	  string[i] == '\n' ||
Packit a71c51
	  (substring_okay && string[i] == '?'))
Packit a71c51
	break;
Packit a71c51
    }
Packit a71c51
Packit a71c51
  which = i - local_index;
Packit a71c51
  temp = (char *)xmalloc (1 + which);
Packit a71c51
  if (which)
Packit a71c51
    strncpy (temp, string + local_index, which);
Packit a71c51
  temp[which] = '\0';
Packit a71c51
Packit a71c51
  if (substring_okay && string[i] == '?')
Packit a71c51
    i++;
Packit a71c51
Packit a71c51
  *caller_index = i;
Packit a71c51
Packit a71c51
#define FAIL_SEARCH() \
Packit a71c51
  do { \
Packit a71c51
    history_offset = history_length; xfree (temp) ; return (char *)NULL; \
Packit a71c51
  } while (0)
Packit a71c51
Packit a71c51
  /* If there is no search string, try to use the previous search string,
Packit a71c51
     if one exists.  If not, fail immediately. */
Packit a71c51
  if (*temp == '\0' && substring_okay)
Packit a71c51
    {
Packit a71c51
      if (search_string)
Packit a71c51
        {
Packit a71c51
          xfree (temp);
Packit a71c51
          temp = savestring (search_string);
Packit a71c51
        }
Packit a71c51
      else
Packit a71c51
        FAIL_SEARCH ();
Packit a71c51
    }
Packit a71c51
Packit a71c51
  search_func = substring_okay ? history_search : history_search_prefix;
Packit a71c51
  while (1)
Packit a71c51
    {
Packit a71c51
      local_index = (*search_func) (temp, -1);
Packit a71c51
Packit a71c51
      if (local_index < 0)
Packit a71c51
	FAIL_SEARCH ();
Packit a71c51
Packit a71c51
      if (local_index == 0 || substring_okay)
Packit a71c51
	{
Packit a71c51
	  entry = current_history ();
Packit a71c51
	  history_offset = history_length;
Packit a71c51
	
Packit a71c51
	  /* If this was a substring search, then remember the
Packit a71c51
	     string that we matched for word substitution. */
Packit a71c51
	  if (substring_okay)
Packit a71c51
	    {
Packit a71c51
	      FREE (search_string);
Packit a71c51
	      search_string = temp;
Packit a71c51
Packit a71c51
	      FREE (search_match);
Packit a71c51
	      search_match = history_find_word (entry->line, local_index);
Packit a71c51
	    }
Packit a71c51
	  else
Packit a71c51
	    xfree (temp);
Packit a71c51
Packit a71c51
	  return (entry->line);
Packit a71c51
	}
Packit a71c51
Packit a71c51
      if (history_offset)
Packit a71c51
	history_offset--;
Packit a71c51
      else
Packit a71c51
	FAIL_SEARCH ();
Packit a71c51
    }
Packit a71c51
#undef FAIL_SEARCH
Packit a71c51
#undef RETURN_ENTRY
Packit a71c51
}
Packit a71c51
Packit a71c51
/* Function for extracting single-quoted strings.  Used for inhibiting
Packit a71c51
   history expansion within single quotes. */
Packit a71c51
Packit a71c51
/* Extract the contents of STRING as if it is enclosed in single quotes.
Packit a71c51
   SINDEX, when passed in, is the offset of the character immediately
Packit a71c51
   following the opening single quote; on exit, SINDEX is left pointing
Packit a71c51
   to the closing single quote.  FLAGS currently used to allow backslash
Packit a71c51
   to escape a single quote (e.g., for bash $'...'). */
Packit a71c51
static void
Packit a71c51
hist_string_extract_single_quoted (string, sindex, flags)
Packit a71c51
     char *string;
Packit a71c51
     int *sindex, flags;
Packit a71c51
{
Packit a71c51
  register int i;
Packit a71c51
Packit a71c51
  for (i = *sindex; string[i] && string[i] != '\''; i++)
Packit a71c51
    {
Packit a71c51
      if ((flags & 1) && string[i] == '\\' && string[i+1])
Packit a71c51
        i++;
Packit a71c51
    }
Packit a71c51
Packit a71c51
  *sindex = i;
Packit a71c51
}
Packit a71c51
Packit a71c51
static char *
Packit a71c51
quote_breaks (s)
Packit a71c51
     char *s;
Packit a71c51
{
Packit a71c51
  register char *p, *r;
Packit a71c51
  char *ret;
Packit a71c51
  int len = 3;
Packit a71c51
Packit a71c51
  for (p = s; p && *p; p++, len++)
Packit a71c51
    {
Packit a71c51
      if (*p == '\'')
Packit a71c51
	len += 3;
Packit a71c51
      else if (whitespace (*p) || *p == '\n')
Packit a71c51
	len += 2;
Packit a71c51
    }
Packit a71c51
Packit a71c51
  r = ret = (char *)xmalloc (len);
Packit a71c51
  *r++ = '\'';
Packit a71c51
  for (p = s; p && *p; )
Packit a71c51
    {
Packit a71c51
      if (*p == '\'')
Packit a71c51
	{
Packit a71c51
	  *r++ = '\'';
Packit a71c51
	  *r++ = '\\';
Packit a71c51
	  *r++ = '\'';
Packit a71c51
	  *r++ = '\'';
Packit a71c51
	  p++;
Packit a71c51
	}
Packit a71c51
      else if (whitespace (*p) || *p == '\n')
Packit a71c51
	{
Packit a71c51
	  *r++ = '\'';
Packit a71c51
	  *r++ = *p++;
Packit a71c51
	  *r++ = '\'';
Packit a71c51
	}
Packit a71c51
      else
Packit a71c51
	*r++ = *p++;
Packit a71c51
    }
Packit a71c51
  *r++ = '\'';
Packit a71c51
  *r = '\0';
Packit a71c51
  return ret;
Packit a71c51
}
Packit a71c51
Packit a71c51
static char *
Packit a71c51
hist_error(s, start, current, errtype)
Packit a71c51
      char *s;
Packit a71c51
      int start, current, errtype;
Packit a71c51
{
Packit a71c51
  char *temp;
Packit a71c51
  const char *emsg;
Packit a71c51
  int ll, elen;
Packit a71c51
Packit a71c51
  ll = current - start;
Packit a71c51
Packit a71c51
  switch (errtype)
Packit a71c51
    {
Packit a71c51
    case EVENT_NOT_FOUND:
Packit a71c51
      emsg = "event not found";
Packit a71c51
      elen = 15;
Packit a71c51
      break;
Packit a71c51
    case BAD_WORD_SPEC:
Packit a71c51
      emsg = "bad word specifier";
Packit a71c51
      elen = 18;
Packit a71c51
      break;
Packit a71c51
    case SUBST_FAILED:
Packit a71c51
      emsg = "substitution failed";
Packit a71c51
      elen = 19;
Packit a71c51
      break;
Packit a71c51
    case BAD_MODIFIER:
Packit a71c51
      emsg = "unrecognized history modifier";
Packit a71c51
      elen = 29;
Packit a71c51
      break;
Packit a71c51
    case NO_PREV_SUBST:
Packit a71c51
      emsg = "no previous substitution";
Packit a71c51
      elen = 24;
Packit a71c51
      break;
Packit a71c51
    default:
Packit a71c51
      emsg = "unknown expansion error";
Packit a71c51
      elen = 23;
Packit a71c51
      break;
Packit a71c51
    }
Packit a71c51
Packit a71c51
  temp = (char *)xmalloc (ll + elen + 3);
Packit a71c51
  strncpy (temp, s + start, ll);
Packit a71c51
  temp[ll] = ':';
Packit a71c51
  temp[ll + 1] = ' ';
Packit a71c51
  strcpy (temp + ll + 2, emsg);
Packit a71c51
  return (temp);
Packit a71c51
}
Packit a71c51
Packit a71c51
/* Get a history substitution string from STR starting at *IPTR
Packit a71c51
   and return it.  The length is returned in LENPTR.
Packit a71c51
Packit a71c51
   A backslash can quote the delimiter.  If the string is the
Packit a71c51
   empty string, the previous pattern is used.  If there is
Packit a71c51
   no previous pattern for the lhs, the last history search
Packit a71c51
   string is used.
Packit a71c51
Packit a71c51
   If IS_RHS is 1, we ignore empty strings and set the pattern
Packit a71c51
   to "" anyway.  subst_lhs is not changed if the lhs is empty;
Packit a71c51
   subst_rhs is allowed to be set to the empty string. */
Packit a71c51
Packit a71c51
static char *
Packit a71c51
get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
Packit a71c51
     char *str;
Packit a71c51
     int *iptr, delimiter, is_rhs, *lenptr;
Packit a71c51
{
Packit a71c51
  register int si, i, j, k;
Packit a71c51
  char *s;
Packit a71c51
#if defined (HANDLE_MULTIBYTE)
Packit a71c51
  mbstate_t ps;
Packit a71c51
#endif
Packit a71c51
Packit a71c51
  s = (char *)NULL;
Packit a71c51
  i = *iptr;
Packit a71c51
Packit a71c51
#if defined (HANDLE_MULTIBYTE)
Packit a71c51
  memset (&ps, 0, sizeof (mbstate_t));
Packit a71c51
  _rl_adjust_point (str, i, &ps);
Packit a71c51
#endif
Packit a71c51
Packit a71c51
  for (si = i; str[si] && str[si] != delimiter; si++)
Packit a71c51
#if defined (HANDLE_MULTIBYTE)
Packit a71c51
    if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
Packit a71c51
      {
Packit a71c51
	int v;
Packit a71c51
	if ((v = _rl_get_char_len (str + si, &ps)) > 1)
Packit a71c51
	  si += v - 1;
Packit a71c51
	else if (str[si] == '\\' && str[si + 1] == delimiter)
Packit a71c51
	  si++;
Packit a71c51
      }
Packit a71c51
    else
Packit a71c51
#endif /* HANDLE_MULTIBYTE */
Packit a71c51
      if (str[si] == '\\' && str[si + 1] == delimiter)
Packit a71c51
	si++;
Packit a71c51
Packit a71c51
  if (si > i || is_rhs)
Packit a71c51
    {
Packit a71c51
      s = (char *)xmalloc (si - i + 1);
Packit a71c51
      for (j = 0, k = i; k < si; j++, k++)
Packit a71c51
	{
Packit a71c51
	  /* Remove a backslash quoting the search string delimiter. */
Packit a71c51
	  if (str[k] == '\\' && str[k + 1] == delimiter)
Packit a71c51
	    k++;
Packit a71c51
	  s[j] = str[k];
Packit a71c51
	}
Packit a71c51
      s[j] = '\0';
Packit a71c51
      if (lenptr)
Packit a71c51
	*lenptr = j;
Packit a71c51
    }
Packit a71c51
Packit a71c51
  i = si;
Packit a71c51
  if (str[i])
Packit a71c51
    i++;
Packit a71c51
  *iptr = i;
Packit a71c51
Packit a71c51
  return s;
Packit a71c51
}
Packit a71c51
Packit a71c51
static void
Packit a71c51
postproc_subst_rhs ()
Packit a71c51
{
Packit a71c51
  char *new;
Packit a71c51
  int i, j, new_size;
Packit a71c51
Packit a71c51
  new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len);
Packit a71c51
  for (i = j = 0; i < subst_rhs_len; i++)
Packit a71c51
    {
Packit a71c51
      if (subst_rhs[i] == '&')
Packit a71c51
	{
Packit a71c51
	  if (j + subst_lhs_len >= new_size)
Packit a71c51
	    new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
Packit a71c51
	  strcpy (new + j, subst_lhs);
Packit a71c51
	  j += subst_lhs_len;
Packit a71c51
	}
Packit a71c51
      else
Packit a71c51
	{
Packit a71c51
	  /* a single backslash protects the `&' from lhs interpolation */
Packit a71c51
	  if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
Packit a71c51
	    i++;
Packit a71c51
	  if (j >= new_size)
Packit a71c51
	    new = (char *)xrealloc (new, new_size *= 2);
Packit a71c51
	  new[j++] = subst_rhs[i];
Packit a71c51
	}
Packit a71c51
    }
Packit a71c51
  new[j] = '\0';
Packit a71c51
  xfree (subst_rhs);
Packit a71c51
  subst_rhs = new;
Packit a71c51
  subst_rhs_len = j;
Packit a71c51
}
Packit a71c51
Packit a71c51
/* Expand the bulk of a history specifier starting at STRING[START].
Packit a71c51
   Returns 0 if everything is OK, -1 if an error occurred, and 1
Packit a71c51
   if the `p' modifier was supplied and the caller should just print
Packit a71c51
   the returned string.  Returns the new index into string in
Packit a71c51
   *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
Packit a71c51
static int
Packit a71c51
history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
Packit a71c51
     char *string;
Packit a71c51
     int start, *end_index_ptr;
Packit a71c51
     char **ret_string;
Packit a71c51
     char *current_line;	/* for !# */
Packit a71c51
{
Packit a71c51
  int i, n, starting_index;
Packit a71c51
  int substitute_globally, subst_bywords, want_quotes, print_only;
Packit a71c51
  char *event, *temp, *result, *tstr, *t, c, *word_spec;
Packit a71c51
  int result_len;
Packit a71c51
#if defined (HANDLE_MULTIBYTE)
Packit a71c51
  mbstate_t ps;
Packit a71c51
Packit a71c51
  memset (&ps, 0, sizeof (mbstate_t));
Packit a71c51
#endif
Packit a71c51
Packit a71c51
  result = (char *)xmalloc (result_len = 128);
Packit a71c51
Packit a71c51
  i = start;
Packit a71c51
Packit a71c51
  /* If it is followed by something that starts a word specifier,
Packit a71c51
     then !! is implied as the event specifier. */
Packit a71c51
Packit a71c51
  if (member (string[i + 1], ":$*%^"))
Packit a71c51
    {
Packit a71c51
      char fake_s[3];
Packit a71c51
      int fake_i = 0;
Packit a71c51
      i++;
Packit a71c51
      fake_s[0] = fake_s[1] = history_expansion_char;
Packit a71c51
      fake_s[2] = '\0';
Packit a71c51
      event = get_history_event (fake_s, &fake_i, 0);
Packit a71c51
    }
Packit a71c51
  else if (string[i + 1] == '#')
Packit a71c51
    {
Packit a71c51
      i += 2;
Packit a71c51
      event = current_line;
Packit a71c51
    }
Packit a71c51
  else
Packit a71c51
    {
Packit a71c51
      int quoted_search_delimiter = 0;
Packit a71c51
Packit a71c51
      /* If the character before this `!' is a double or single
Packit a71c51
	 quote, then this expansion takes place inside of the
Packit a71c51
	 quoted string.  If we have to search for some text ("!foo"),
Packit a71c51
	 allow the delimiter to end the search string. */
Packit a71c51
#if defined (HANDLE_MULTIBYTE)
Packit a71c51
      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
Packit a71c51
	{
Packit a71c51
	  int ch, l;
Packit a71c51
	  l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY);
Packit a71c51
	  ch = string[l];
Packit a71c51
	  /* XXX - original patch had i - 1 ???  If i == 0 it would fail. */
Packit a71c51
	  if (i && (ch == '\'' || ch == '"'))
Packit a71c51
	    quoted_search_delimiter = ch;
Packit a71c51
	}
Packit a71c51
      else
Packit a71c51
#endif /* HANDLE_MULTIBYTE */	  
Packit a71c51
	if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
Packit a71c51
	  quoted_search_delimiter = string[i - 1];
Packit a71c51
Packit a71c51
      event = get_history_event (string, &i, quoted_search_delimiter);
Packit a71c51
    }
Packit a71c51
	  
Packit a71c51
  if (event == 0)
Packit a71c51
    {
Packit a71c51
      *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
Packit a71c51
      xfree (result);
Packit a71c51
      return (-1);
Packit a71c51
    }
Packit a71c51
Packit a71c51
  /* If a word specifier is found, then do what that requires. */
Packit a71c51
  starting_index = i;
Packit a71c51
  word_spec = get_history_word_specifier (string, event, &i);
Packit a71c51
Packit a71c51
  /* There is no such thing as a `malformed word specifier'.  However,
Packit a71c51
     it is possible for a specifier that has no match.  In that case,
Packit a71c51
     we complain. */
Packit a71c51
  if (word_spec == (char *)&error_pointer)
Packit a71c51
    {
Packit a71c51
      *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
Packit a71c51
      xfree (result);
Packit a71c51
      return (-1);
Packit a71c51
    }
Packit a71c51
Packit a71c51
  /* If no word specifier, than the thing of interest was the event. */
Packit a71c51
  temp = word_spec ? savestring (word_spec) : savestring (event);
Packit a71c51
  FREE (word_spec);
Packit a71c51
Packit a71c51
  /* Perhaps there are other modifiers involved.  Do what they say. */
Packit a71c51
  want_quotes = substitute_globally = subst_bywords = print_only = 0;
Packit a71c51
  starting_index = i;
Packit a71c51
Packit a71c51
  while (string[i] == ':')
Packit a71c51
    {
Packit a71c51
      c = string[i + 1];
Packit a71c51
Packit a71c51
      if (c == 'g' || c == 'a')
Packit a71c51
	{
Packit a71c51
	  substitute_globally = 1;
Packit a71c51
	  i++;
Packit a71c51
	  c = string[i + 1];
Packit a71c51
	}
Packit a71c51
      else if (c == 'G')
Packit a71c51
	{
Packit a71c51
	  subst_bywords = 1;
Packit a71c51
	  i++;
Packit a71c51
	  c = string[i + 1];
Packit a71c51
	}
Packit a71c51
Packit a71c51
      switch (c)
Packit a71c51
	{
Packit a71c51
	default:
Packit a71c51
	  *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
Packit a71c51
	  xfree (result);
Packit a71c51
	  xfree (temp);
Packit a71c51
	  return -1;
Packit a71c51
Packit a71c51
	case 'q':
Packit a71c51
	  want_quotes = 'q';
Packit a71c51
	  break;
Packit a71c51
Packit a71c51
	case 'x':
Packit a71c51
	  want_quotes = 'x';
Packit a71c51
	  break;
Packit a71c51
Packit a71c51
	  /* :p means make this the last executed line.  So we
Packit a71c51
	     return an error state after adding this line to the
Packit a71c51
	     history. */
Packit a71c51
	case 'p':
Packit a71c51
	  print_only++;
Packit a71c51
	  break;
Packit a71c51
Packit a71c51
	  /* :t discards all but the last part of the pathname. */
Packit a71c51
	case 't':
Packit a71c51
	  tstr = strrchr (temp, '/');
Packit a71c51
	  if (tstr)
Packit a71c51
	    {
Packit a71c51
	      tstr++;
Packit a71c51
	      t = savestring (tstr);
Packit a71c51
	      xfree (temp);
Packit a71c51
	      temp = t;
Packit a71c51
	    }
Packit a71c51
	  break;
Packit a71c51
Packit a71c51
	  /* :h discards the last part of a pathname. */
Packit a71c51
	case 'h':
Packit a71c51
	  tstr = strrchr (temp, '/');
Packit a71c51
	  if (tstr)
Packit a71c51
	    *tstr = '\0';
Packit a71c51
	  break;
Packit a71c51
Packit a71c51
	  /* :r discards the suffix. */
Packit a71c51
	case 'r':
Packit a71c51
	  tstr = strrchr (temp, '.');
Packit a71c51
	  if (tstr)
Packit a71c51
	    *tstr = '\0';
Packit a71c51
	  break;
Packit a71c51
Packit a71c51
	  /* :e discards everything but the suffix. */
Packit a71c51
	case 'e':
Packit a71c51
	  tstr = strrchr (temp, '.');
Packit a71c51
	  if (tstr)
Packit a71c51
	    {
Packit a71c51
	      t = savestring (tstr);
Packit a71c51
	      xfree (temp);
Packit a71c51
	      temp = t;
Packit a71c51
	    }
Packit a71c51
	  break;
Packit a71c51
Packit a71c51
	/* :s/this/that substitutes `that' for the first
Packit a71c51
	   occurrence of `this'.  :gs/this/that substitutes `that'
Packit a71c51
	   for each occurrence of `this'.  :& repeats the last
Packit a71c51
	   substitution.  :g& repeats the last substitution
Packit a71c51
	   globally. */
Packit a71c51
Packit a71c51
	case '&':
Packit a71c51
	case 's':
Packit a71c51
	  {
Packit a71c51
	    char *new_event;
Packit a71c51
	    int delimiter, failed, si, l_temp, ws, we;
Packit a71c51
Packit a71c51
	    if (c == 's')
Packit a71c51
	      {
Packit a71c51
		if (i + 2 < (int)strlen (string))
Packit a71c51
		  {
Packit a71c51
#if defined (HANDLE_MULTIBYTE)
Packit a71c51
		    if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
Packit a71c51
		      {
Packit a71c51
			_rl_adjust_point (string, i + 2, &ps);
Packit a71c51
			if (_rl_get_char_len (string + i + 2, &ps) > 1)
Packit a71c51
			  delimiter = 0;
Packit a71c51
			else
Packit a71c51
			  delimiter = string[i + 2];
Packit a71c51
		      }
Packit a71c51
		    else
Packit a71c51
#endif /* HANDLE_MULTIBYTE */
Packit a71c51
		      delimiter = string[i + 2];
Packit a71c51
		  }
Packit a71c51
		else
Packit a71c51
		  break;	/* no search delimiter */
Packit a71c51
Packit a71c51
		i += 3;
Packit a71c51
Packit a71c51
		t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
Packit a71c51
		/* An empty substitution lhs with no previous substitution
Packit a71c51
		   uses the last search string as the lhs. */
Packit a71c51
		if (t)
Packit a71c51
		  {
Packit a71c51
		    FREE (subst_lhs);
Packit a71c51
		    subst_lhs = t;
Packit a71c51
		  }
Packit a71c51
		else if (!subst_lhs)
Packit a71c51
		  {
Packit a71c51
		    if (search_string && *search_string)
Packit a71c51
		      {
Packit a71c51
			subst_lhs = savestring (search_string);
Packit a71c51
			subst_lhs_len = strlen (subst_lhs);
Packit a71c51
		      }
Packit a71c51
		    else
Packit a71c51
		      {
Packit a71c51
			subst_lhs = (char *) NULL;
Packit a71c51
			subst_lhs_len = 0;
Packit a71c51
		      }
Packit a71c51
		  }
Packit a71c51
Packit a71c51
		FREE (subst_rhs);
Packit a71c51
		subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
Packit a71c51
Packit a71c51
		/* If `&' appears in the rhs, it's supposed to be replaced
Packit a71c51
		   with the lhs. */
Packit a71c51
		if (member ('&', subst_rhs))
Packit a71c51
		  postproc_subst_rhs ();
Packit a71c51
	      }
Packit a71c51
	    else
Packit a71c51
	      i += 2;
Packit a71c51
Packit a71c51
	    /* If there is no lhs, the substitution can't succeed. */
Packit a71c51
	    if (subst_lhs_len == 0)
Packit a71c51
	      {
Packit a71c51
		*ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
Packit a71c51
		xfree (result);
Packit a71c51
		xfree (temp);
Packit a71c51
		return -1;
Packit a71c51
	      }
Packit a71c51
Packit a71c51
	    l_temp = strlen (temp);
Packit a71c51
	    /* Ignore impossible cases. */
Packit a71c51
	    if (subst_lhs_len > l_temp)
Packit a71c51
	      {
Packit a71c51
		*ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
Packit a71c51
		xfree (result);
Packit a71c51
		xfree (temp);
Packit a71c51
		return (-1);
Packit a71c51
	      }
Packit a71c51
Packit a71c51
	    /* Find the first occurrence of THIS in TEMP. */
Packit a71c51
	    /* Substitute SUBST_RHS for SUBST_LHS in TEMP.  There are three
Packit a71c51
	       cases to consider:
Packit a71c51
Packit a71c51
		 1.  substitute_globally == subst_bywords == 0
Packit a71c51
		 2.  substitute_globally == 1 && subst_bywords == 0
Packit a71c51
		 3.  substitute_globally == 0 && subst_bywords == 1
Packit a71c51
Packit a71c51
	       In the first case, we substitute for the first occurrence only.
Packit a71c51
	       In the second case, we substitute for every occurrence.
Packit a71c51
	       In the third case, we tokenize into words and substitute the
Packit a71c51
	       first occurrence of each word. */
Packit a71c51
Packit a71c51
	    si = we = 0;
Packit a71c51
	    for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
Packit a71c51
	      {
Packit a71c51
		/* First skip whitespace and find word boundaries if
Packit a71c51
		   we're past the end of the word boundary we found
Packit a71c51
		   the last time. */
Packit a71c51
		if (subst_bywords && si > we)
Packit a71c51
		  {
Packit a71c51
		    for (; temp[si] && whitespace (temp[si]); si++)
Packit a71c51
		      ;
Packit a71c51
		    ws = si;
Packit a71c51
		    we = history_tokenize_word (temp, si);
Packit a71c51
		  }
Packit a71c51
Packit a71c51
		if (STREQN (temp+si, subst_lhs, subst_lhs_len))
Packit a71c51
		  {
Packit a71c51
		    int len = subst_rhs_len - subst_lhs_len + l_temp;
Packit a71c51
		    new_event = (char *)xmalloc (1 + len);
Packit a71c51
		    strncpy (new_event, temp, si);
Packit a71c51
		    strncpy (new_event + si, subst_rhs, subst_rhs_len);
Packit a71c51
		    strncpy (new_event + si + subst_rhs_len,
Packit a71c51
			     temp + si + subst_lhs_len,
Packit a71c51
			     l_temp - (si + subst_lhs_len));
Packit a71c51
		    new_event[len] = '\0';
Packit a71c51
		    xfree (temp);
Packit a71c51
		    temp = new_event;
Packit a71c51
Packit a71c51
		    failed = 0;
Packit a71c51
Packit a71c51
		    if (substitute_globally)
Packit a71c51
		      {
Packit a71c51
			/* Reported to fix a bug that causes it to skip every
Packit a71c51
			   other match when matching a single character.  Was
Packit a71c51
			   si += subst_rhs_len previously. */
Packit a71c51
			si += subst_rhs_len - 1;
Packit a71c51
			l_temp = strlen (temp);
Packit a71c51
			substitute_globally++;
Packit a71c51
			continue;
Packit a71c51
		      }
Packit a71c51
		    else if (subst_bywords)
Packit a71c51
		      {
Packit a71c51
			si = we;
Packit a71c51
			l_temp = strlen (temp);
Packit a71c51
			continue;
Packit a71c51
		      }
Packit a71c51
		    else
Packit a71c51
		      break;
Packit a71c51
		  }
Packit a71c51
	      }
Packit a71c51
Packit a71c51
	    if (substitute_globally > 1)
Packit a71c51
	      {
Packit a71c51
		substitute_globally = 0;
Packit a71c51
		continue;	/* don't want to increment i */
Packit a71c51
	      }
Packit a71c51
Packit a71c51
	    if (failed == 0)
Packit a71c51
	      continue;		/* don't want to increment i */
Packit a71c51
Packit a71c51
	    *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
Packit a71c51
	    xfree (result);
Packit a71c51
	    xfree (temp);
Packit a71c51
	    return (-1);
Packit a71c51
	  }
Packit a71c51
	}
Packit a71c51
      i += 2;
Packit a71c51
    }
Packit a71c51
  /* Done with modfiers. */
Packit a71c51
  /* Believe it or not, we have to back the pointer up by one. */
Packit a71c51
  --i;
Packit a71c51
Packit a71c51
  if (want_quotes)
Packit a71c51
    {
Packit a71c51
      char *x;
Packit a71c51
Packit a71c51
      if (want_quotes == 'q')
Packit a71c51
	x = sh_single_quote (temp);
Packit a71c51
      else if (want_quotes == 'x')
Packit a71c51
	x = quote_breaks (temp);
Packit a71c51
      else
Packit a71c51
	x = savestring (temp);
Packit a71c51
Packit a71c51
      xfree (temp);
Packit a71c51
      temp = x;
Packit a71c51
    }
Packit a71c51
Packit a71c51
  n = strlen (temp);
Packit a71c51
  if (n >= result_len)
Packit a71c51
    result = (char *)xrealloc (result, n + 2);
Packit a71c51
  strcpy (result, temp);
Packit a71c51
  xfree (temp);
Packit a71c51
Packit a71c51
  *end_index_ptr = i;
Packit a71c51
  *ret_string = result;
Packit a71c51
  return (print_only);
Packit a71c51
}
Packit a71c51
Packit a71c51
/* Expand the string STRING, placing the result into OUTPUT, a pointer
Packit a71c51
   to a string.  Returns:
Packit a71c51
Packit a71c51
  -1) If there was an error in expansion.
Packit a71c51
   0) If no expansions took place (or, if the only change in
Packit a71c51
      the text was the de-slashifying of the history expansion
Packit a71c51
      character)
Packit a71c51
   1) If expansions did take place
Packit a71c51
   2) If the `p' modifier was given and the caller should print the result
Packit a71c51
Packit a71c51
  If an error ocurred in expansion, then OUTPUT contains a descriptive
Packit a71c51
  error message. */
Packit a71c51
Packit a71c51
#define ADD_STRING(s) \
Packit a71c51
	do \
Packit a71c51
	  { \
Packit a71c51
	    int sl = strlen (s); \
Packit a71c51
	    j += sl; \
Packit a71c51
	    if (j >= result_len) \
Packit a71c51
	      { \
Packit a71c51
		while (j >= result_len) \
Packit a71c51
		  result_len += 128; \
Packit a71c51
		result = (char *)xrealloc (result, result_len); \
Packit a71c51
	      } \
Packit a71c51
	    strcpy (result + j - sl, s); \
Packit a71c51
	  } \
Packit a71c51
	while (0)
Packit a71c51
Packit a71c51
#define ADD_CHAR(c) \
Packit a71c51
	do \
Packit a71c51
	  { \
Packit a71c51
	    if (j >= result_len - 1) \
Packit a71c51
	      result = (char *)xrealloc (result, result_len += 64); \
Packit a71c51
	    result[j++] = c; \
Packit a71c51
	    result[j] = '\0'; \
Packit a71c51
	  } \
Packit a71c51
	while (0)
Packit a71c51
Packit a71c51
int
Packit a71c51
history_expand (hstring, output)
Packit a71c51
     char *hstring;
Packit a71c51
     char **output;
Packit a71c51
{
Packit a71c51
  register int j;
Packit a71c51
  int i, r, l, passc, cc, modified, eindex, only_printing, dquote, flag;
Packit a71c51
  char *string;
Packit a71c51
Packit a71c51
  /* The output string, and its length. */
Packit a71c51
  int result_len;
Packit a71c51
  char *result;
Packit a71c51
Packit a71c51
#if defined (HANDLE_MULTIBYTE)
Packit a71c51
  char mb[MB_LEN_MAX];
Packit a71c51
  mbstate_t ps;
Packit a71c51
#endif
Packit a71c51
Packit a71c51
  /* Used when adding the string. */
Packit a71c51
  char *temp;
Packit a71c51
Packit a71c51
  if (output == 0)
Packit a71c51
    return 0;
Packit a71c51
Packit a71c51
  /* Setting the history expansion character to 0 inhibits all
Packit a71c51
     history expansion. */
Packit a71c51
  if (history_expansion_char == 0)
Packit a71c51
    {
Packit a71c51
      *output = savestring (hstring);
Packit a71c51
      return (0);
Packit a71c51
    }
Packit a71c51
    
Packit a71c51
  /* Prepare the buffer for printing error messages. */
Packit a71c51
  result = (char *)xmalloc (result_len = 256);
Packit a71c51
  result[0] = '\0';
Packit a71c51
Packit a71c51
  only_printing = modified = 0;
Packit a71c51
  l = strlen (hstring);
Packit a71c51
Packit a71c51
  /* Grovel the string.  Only backslash and single quotes can quote the
Packit a71c51
     history escape character.  We also handle arg specifiers. */
Packit a71c51
Packit a71c51
  /* Before we grovel forever, see if the history_expansion_char appears
Packit a71c51
     anywhere within the text. */
Packit a71c51
Packit a71c51
  /* The quick substitution character is a history expansion all right.  That
Packit a71c51
     is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
Packit a71c51
     that is the substitution that we do. */
Packit a71c51
  if (hstring[0] == history_subst_char)
Packit a71c51
    {
Packit a71c51
      string = (char *)xmalloc (l + 5);
Packit a71c51
Packit a71c51
      string[0] = string[1] = history_expansion_char;
Packit a71c51
      string[2] = ':';
Packit a71c51
      string[3] = 's';
Packit a71c51
      strcpy (string + 4, hstring);
Packit a71c51
      l += 4;
Packit a71c51
    }
Packit a71c51
  else
Packit a71c51
    {
Packit a71c51
#if defined (HANDLE_MULTIBYTE)
Packit a71c51
      memset (&ps, 0, sizeof (mbstate_t));
Packit a71c51
#endif
Packit a71c51
Packit a71c51
      string = hstring;
Packit a71c51
      /* If not quick substitution, still maybe have to do expansion. */
Packit a71c51
Packit a71c51
      /* `!' followed by one of the characters in history_no_expand_chars
Packit a71c51
	 is NOT an expansion. */
Packit a71c51
      for (i = dquote = 0; string[i]; i++)
Packit a71c51
	{
Packit a71c51
#if defined (HANDLE_MULTIBYTE)
Packit a71c51
	  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
Packit a71c51
	    {
Packit a71c51
	      int v;
Packit a71c51
	      v = _rl_get_char_len (string + i, &ps);
Packit a71c51
	      if (v > 1)
Packit a71c51
		{
Packit a71c51
		  i += v - 1;
Packit a71c51
		  continue;
Packit a71c51
		}
Packit a71c51
	    }
Packit a71c51
#endif /* HANDLE_MULTIBYTE */
Packit a71c51
Packit a71c51
	  cc = string[i + 1];
Packit a71c51
	  /* The history_comment_char, if set, appearing at the beginning
Packit a71c51
	     of a word signifies that the rest of the line should not have
Packit a71c51
	     history expansion performed on it.
Packit a71c51
	     Skip the rest of the line and break out of the loop. */
Packit a71c51
	  if (history_comment_char && string[i] == history_comment_char &&
Packit a71c51
	      (i == 0 || member (string[i - 1], history_word_delimiters)))
Packit a71c51
	    {
Packit a71c51
	      while (string[i])
Packit a71c51
		i++;
Packit a71c51
	      break;
Packit a71c51
	    }
Packit a71c51
	  else if (string[i] == history_expansion_char)
Packit a71c51
	    {
Packit a71c51
	      if (cc == 0 || member (cc, history_no_expand_chars))
Packit a71c51
		continue;
Packit a71c51
	      /* If the calling application has set
Packit a71c51
		 history_inhibit_expansion_function to a function that checks
Packit a71c51
		 for special cases that should not be history expanded,
Packit a71c51
		 call the function and skip the expansion if it returns a
Packit a71c51
		 non-zero value. */
Packit a71c51
	      else if (history_inhibit_expansion_function &&
Packit a71c51
			(*history_inhibit_expansion_function) (string, i))
Packit a71c51
		continue;
Packit a71c51
	      else
Packit a71c51
		break;
Packit a71c51
	    }
Packit a71c51
	  /* Shell-like quoting: allow backslashes to quote double quotes
Packit a71c51
	     inside a double-quoted string. */
Packit a71c51
	  else if (dquote && string[i] == '\\' && cc == '"')
Packit a71c51
	    i++;
Packit a71c51
	  /* More shell-like quoting:  if we're paying attention to single
Packit a71c51
	     quotes and letting them quote the history expansion character,
Packit a71c51
	     then we need to pay attention to double quotes, because single
Packit a71c51
	     quotes are not special inside double-quoted strings. */
Packit a71c51
	  else if (history_quotes_inhibit_expansion && string[i] == '"')
Packit a71c51
	    {
Packit a71c51
	      dquote = 1 - dquote;
Packit a71c51
	    }
Packit a71c51
	  else if (dquote == 0 && history_quotes_inhibit_expansion && string[i] == '\'')
Packit a71c51
	    {
Packit a71c51
	      /* If this is bash, single quotes inhibit history expansion. */
Packit a71c51
	      flag = (i > 0 && string[i - 1] == '$');
Packit a71c51
	      i++;
Packit a71c51
	      hist_string_extract_single_quoted (string, &i, flag);
Packit a71c51
	    }
Packit a71c51
	  else if (history_quotes_inhibit_expansion && string[i] == '\\')
Packit a71c51
	    {
Packit a71c51
	      /* If this is bash, allow backslashes to quote single
Packit a71c51
		 quotes and the history expansion character. */
Packit a71c51
	      if (cc == '\'' || cc == history_expansion_char)
Packit a71c51
		i++;
Packit a71c51
	    }
Packit a71c51
	  
Packit a71c51
	}
Packit a71c51
	  
Packit a71c51
      if (string[i] != history_expansion_char)
Packit a71c51
	{
Packit a71c51
	  xfree (result);
Packit a71c51
	  *output = savestring (string);
Packit a71c51
	  return (0);
Packit a71c51
	}
Packit a71c51
    }
Packit a71c51
Packit a71c51
  /* Extract and perform the substitution. */
Packit a71c51
  for (passc = dquote = i = j = 0; i < l; i++)
Packit a71c51
    {
Packit a71c51
      int tchar = string[i];
Packit a71c51
Packit a71c51
      if (passc)
Packit a71c51
	{
Packit a71c51
	  passc = 0;
Packit a71c51
	  ADD_CHAR (tchar);
Packit a71c51
	  continue;
Packit a71c51
	}
Packit a71c51
Packit a71c51
#if defined (HANDLE_MULTIBYTE)
Packit a71c51
      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
Packit a71c51
	{
Packit a71c51
	  int k, c;
Packit a71c51
Packit a71c51
	  c = tchar;
Packit a71c51
	  memset (mb, 0, sizeof (mb));
Packit a71c51
	  for (k = 0; k < MB_LEN_MAX; k++)
Packit a71c51
	    {
Packit a71c51
	      mb[k] = (char)c;
Packit a71c51
	      memset (&ps, 0, sizeof (mbstate_t));
Packit a71c51
	      if (_rl_get_char_len (mb, &ps) == -2)
Packit a71c51
		c = string[++i];
Packit a71c51
	      else
Packit a71c51
		break;
Packit a71c51
	    }
Packit a71c51
	  if (strlen (mb) > 1)
Packit a71c51
	    {
Packit a71c51
	      ADD_STRING (mb);
Packit a71c51
	      continue;
Packit a71c51
	    }
Packit a71c51
	}
Packit a71c51
#endif /* HANDLE_MULTIBYTE */
Packit a71c51
Packit a71c51
      if (tchar == history_expansion_char)
Packit a71c51
	tchar = -3;
Packit a71c51
      else if (tchar == history_comment_char)
Packit a71c51
	tchar = -2;
Packit a71c51
Packit a71c51
      switch (tchar)
Packit a71c51
	{
Packit a71c51
	default:
Packit a71c51
	  ADD_CHAR (string[i]);
Packit a71c51
	  break;
Packit a71c51
Packit a71c51
	case '\\':
Packit a71c51
	  passc++;
Packit a71c51
	  ADD_CHAR (tchar);
Packit a71c51
	  break;
Packit a71c51
Packit a71c51
	case '"':
Packit a71c51
	  dquote = 1 - dquote;
Packit a71c51
	  ADD_CHAR (tchar);
Packit a71c51
	  break;
Packit a71c51
	  
Packit a71c51
	case '\'':
Packit a71c51
	  {
Packit a71c51
	    /* If history_quotes_inhibit_expansion is set, single quotes
Packit a71c51
	       inhibit history expansion. */
Packit a71c51
	    if (dquote == 0 && history_quotes_inhibit_expansion)
Packit a71c51
	      {
Packit a71c51
		int quote, slen;
Packit a71c51
Packit a71c51
		flag = (i > 0 && string[i - 1] == '$');
Packit a71c51
		quote = i++;
Packit a71c51
		hist_string_extract_single_quoted (string, &i, flag);
Packit a71c51
Packit a71c51
		slen = i - quote + 2;
Packit a71c51
		temp = (char *)xmalloc (slen);
Packit a71c51
		strncpy (temp, string + quote, slen);
Packit a71c51
		temp[slen - 1] = '\0';
Packit a71c51
		ADD_STRING (temp);
Packit a71c51
		xfree (temp);
Packit a71c51
	      }
Packit a71c51
	    else
Packit a71c51
	      ADD_CHAR (string[i]);
Packit a71c51
	    break;
Packit a71c51
	  }
Packit a71c51
Packit a71c51
	case -2:		/* history_comment_char */
Packit a71c51
	  if (i == 0 || member (string[i - 1], history_word_delimiters))
Packit a71c51
	    {
Packit a71c51
	      temp = (char *)xmalloc (l - i + 1);
Packit a71c51
	      strcpy (temp, string + i);
Packit a71c51
	      ADD_STRING (temp);
Packit a71c51
	      xfree (temp);
Packit a71c51
	      i = l;
Packit a71c51
	    }
Packit a71c51
	  else
Packit a71c51
	    ADD_CHAR (string[i]);
Packit a71c51
	  break;
Packit a71c51
Packit a71c51
	case -3:		/* history_expansion_char */
Packit a71c51
	  cc = string[i + 1];
Packit a71c51
Packit a71c51
	  /* If the history_expansion_char is followed by one of the
Packit a71c51
	     characters in history_no_expand_chars, then it is not a
Packit a71c51
	     candidate for expansion of any kind. */
Packit a71c51
	  if (cc == 0 || member (cc, history_no_expand_chars) ||
Packit a71c51
	  		 (history_inhibit_expansion_function && (*history_inhibit_expansion_function) (string, i)))
Packit a71c51
	    {
Packit a71c51
	      ADD_CHAR (string[i]);
Packit a71c51
	      break;
Packit a71c51
	    }
Packit a71c51
Packit a71c51
#if defined (NO_BANG_HASH_MODIFIERS)
Packit a71c51
	  /* There is something that is listed as a `word specifier' in csh
Packit a71c51
	     documentation which means `the expanded text to this point'.
Packit a71c51
	     That is not a word specifier, it is an event specifier.  If we
Packit a71c51
	     don't want to allow modifiers with `!#', just stick the current
Packit a71c51
	     output line in again. */
Packit a71c51
	  if (cc == '#')
Packit a71c51
	    {
Packit a71c51
	      if (result)
Packit a71c51
		{
Packit a71c51
		  temp = (char *)xmalloc (1 + strlen (result));
Packit a71c51
		  strcpy (temp, result);
Packit a71c51
		  ADD_STRING (temp);
Packit a71c51
		  xfree (temp);
Packit a71c51
		}
Packit a71c51
	      i++;
Packit a71c51
	      break;
Packit a71c51
	    }
Packit a71c51
#endif
Packit a71c51
Packit a71c51
	  r = history_expand_internal (string, i, &eindex, &temp, result);
Packit a71c51
	  if (r < 0)
Packit a71c51
	    {
Packit a71c51
	      *output = temp;
Packit a71c51
	      xfree (result);
Packit a71c51
	      if (string != hstring)
Packit a71c51
		xfree (string);
Packit a71c51
	      return -1;
Packit a71c51
	    }
Packit a71c51
	  else
Packit a71c51
	    {
Packit a71c51
	      if (temp)
Packit a71c51
		{
Packit a71c51
		  modified++;
Packit a71c51
		  if (*temp)
Packit a71c51
		    ADD_STRING (temp);
Packit a71c51
		  xfree (temp);
Packit a71c51
		}
Packit a71c51
	      only_printing = r == 1;
Packit a71c51
	      i = eindex;
Packit a71c51
	    }
Packit a71c51
	  break;
Packit a71c51
	}
Packit a71c51
    }
Packit a71c51
Packit a71c51
  *output = result;
Packit a71c51
  if (string != hstring)
Packit a71c51
    xfree (string);
Packit a71c51
Packit a71c51
  if (only_printing)
Packit a71c51
    {
Packit a71c51
#if 0
Packit a71c51
      add_history (result);
Packit a71c51
#endif
Packit a71c51
      return (2);
Packit a71c51
    }
Packit a71c51
Packit a71c51
  return (modified != 0);
Packit a71c51
}
Packit a71c51
Packit a71c51
/* Return a consed string which is the word specified in SPEC, and found
Packit a71c51
   in FROM.  NULL is returned if there is no spec.  The address of
Packit a71c51
   ERROR_POINTER is returned if the word specified cannot be found.
Packit a71c51
   CALLER_INDEX is the offset in SPEC to start looking; it is updated
Packit a71c51
   to point to just after the last character parsed. */
Packit a71c51
static char *
Packit a71c51
get_history_word_specifier (spec, from, caller_index)
Packit a71c51
     char *spec, *from;
Packit a71c51
     int *caller_index;
Packit a71c51
{
Packit a71c51
  register int i = *caller_index;
Packit a71c51
  int first, last;
Packit a71c51
  int expecting_word_spec = 0;
Packit a71c51
  char *result;
Packit a71c51
Packit a71c51
  /* The range of words to return doesn't exist yet. */
Packit a71c51
  first = last = 0;
Packit a71c51
  result = (char *)NULL;
Packit a71c51
Packit a71c51
  /* If we found a colon, then this *must* be a word specification.  If
Packit a71c51
     it isn't, then it is an error. */
Packit a71c51
  if (spec[i] == ':')
Packit a71c51
    {
Packit a71c51
      i++;
Packit a71c51
      expecting_word_spec++;
Packit a71c51
    }
Packit a71c51
Packit a71c51
  /* Handle special cases first. */
Packit a71c51
Packit a71c51
  /* `%' is the word last searched for. */
Packit a71c51
  if (spec[i] == '%')
Packit a71c51
    {
Packit a71c51
      *caller_index = i + 1;
Packit a71c51
      return (search_match ? savestring (search_match) : savestring (""));
Packit a71c51
    }
Packit a71c51
Packit a71c51
  /* `*' matches all of the arguments, but not the command. */
Packit a71c51
  if (spec[i] == '*')
Packit a71c51
    {
Packit a71c51
      *caller_index = i + 1;
Packit a71c51
      result = history_arg_extract (1, '$', from);
Packit a71c51
      return (result ? result : savestring (""));
Packit a71c51
    }
Packit a71c51
Packit a71c51
  /* `$' is last arg. */
Packit a71c51
  if (spec[i] == '$')
Packit a71c51
    {
Packit a71c51
      *caller_index = i + 1;
Packit a71c51
      return (history_arg_extract ('$', '$', from));
Packit a71c51
    }
Packit a71c51
Packit a71c51
  /* Try to get FIRST and LAST figured out. */
Packit a71c51
Packit a71c51
  if (spec[i] == '-')
Packit a71c51
    first = 0;
Packit a71c51
  else if (spec[i] == '^')
Packit a71c51
    {
Packit a71c51
      first = 1;
Packit a71c51
      i++;
Packit a71c51
    }
Packit a71c51
  else if (_rl_digit_p (spec[i]) && expecting_word_spec)
Packit a71c51
    {
Packit a71c51
      for (first = 0; _rl_digit_p (spec[i]); i++)
Packit a71c51
	first = (first * 10) + _rl_digit_value (spec[i]);
Packit a71c51
    }
Packit a71c51
  else
Packit a71c51
    return ((char *)NULL);	/* no valid `first' for word specifier */
Packit a71c51
Packit a71c51
  if (spec[i] == '^' || spec[i] == '*')
Packit a71c51
    {
Packit a71c51
      last = (spec[i] == '^') ? 1 : '$';	/* x* abbreviates x-$ */
Packit a71c51
      i++;
Packit a71c51
    }
Packit a71c51
  else if (spec[i] != '-')
Packit a71c51
    last = first;
Packit a71c51
  else
Packit a71c51
    {
Packit a71c51
      i++;
Packit a71c51
Packit a71c51
      if (_rl_digit_p (spec[i]))
Packit a71c51
	{
Packit a71c51
	  for (last = 0; _rl_digit_p (spec[i]); i++)
Packit a71c51
	    last = (last * 10) + _rl_digit_value (spec[i]);
Packit a71c51
	}
Packit a71c51
      else if (spec[i] == '$')
Packit a71c51
	{
Packit a71c51
	  i++;
Packit a71c51
	  last = '$';
Packit a71c51
	}
Packit a71c51
#if 0
Packit a71c51
      else if (!spec[i] || spec[i] == ':')
Packit a71c51
	/* check against `:' because there could be a modifier separator */
Packit a71c51
#else
Packit a71c51
      else
Packit a71c51
	/* csh seems to allow anything to terminate the word spec here,
Packit a71c51
	   leaving it as an abbreviation. */
Packit a71c51
#endif
Packit a71c51
	last = -1;		/* x- abbreviates x-$ omitting word `$' */
Packit a71c51
    }
Packit a71c51
Packit a71c51
  *caller_index = i;
Packit a71c51
Packit a71c51
  if (last >= first || last == '$' || last < 0)
Packit a71c51
    result = history_arg_extract (first, last, from);
Packit a71c51
Packit a71c51
  return (result ? result : (char *)&error_pointer);
Packit a71c51
}
Packit a71c51
Packit a71c51
/* Extract the args specified, starting at FIRST, and ending at LAST.
Packit a71c51
   The args are taken from STRING.  If either FIRST or LAST is < 0,
Packit a71c51
   then make that arg count from the right (subtract from the number of
Packit a71c51
   tokens, so that FIRST = -1 means the next to last token on the line).
Packit a71c51
   If LAST is `$' the last arg from STRING is used. */
Packit a71c51
char *
Packit a71c51
history_arg_extract (first, last, string)
Packit a71c51
     int first, last;
Packit a71c51
     const char *string;
Packit a71c51
{
Packit a71c51
  register int i, len;
Packit a71c51
  char *result;
Packit a71c51
  int size, offset;
Packit a71c51
  char **list;
Packit a71c51
Packit a71c51
  /* XXX - think about making history_tokenize return a struct array,
Packit a71c51
     each struct in array being a string and a length to avoid the
Packit a71c51
     calls to strlen below. */
Packit a71c51
  if ((list = history_tokenize (string)) == NULL)
Packit a71c51
    return ((char *)NULL);
Packit a71c51
Packit a71c51
  for (len = 0; list[len]; len++)
Packit a71c51
    ;
Packit a71c51
Packit a71c51
  if (last < 0)
Packit a71c51
    last = len + last - 1;
Packit a71c51
Packit a71c51
  if (first < 0)
Packit a71c51
    first = len + first - 1;
Packit a71c51
Packit a71c51
  if (last == '$')
Packit a71c51
    last = len - 1;
Packit a71c51
Packit a71c51
  if (first == '$')
Packit a71c51
    first = len - 1;
Packit a71c51
Packit a71c51
  last++;
Packit a71c51
Packit a71c51
  if (first >= len || last > len || first < 0 || last < 0 || first > last)
Packit a71c51
    result = ((char *)NULL);
Packit a71c51
  else
Packit a71c51
    {
Packit a71c51
      for (size = 0, i = first; i < last; i++)
Packit a71c51
	size += strlen (list[i]) + 1;
Packit a71c51
      result = (char *)xmalloc (size + 1);
Packit a71c51
      result[0] = '\0';
Packit a71c51
Packit a71c51
      for (i = first, offset = 0; i < last; i++)
Packit a71c51
	{
Packit a71c51
	  strcpy (result + offset, list[i]);
Packit a71c51
	  offset += strlen (list[i]);
Packit a71c51
	  if (i + 1 < last)
Packit a71c51
	    {
Packit a71c51
      	      result[offset++] = ' ';
Packit a71c51
	      result[offset] = 0;
Packit a71c51
	    }
Packit a71c51
	}
Packit a71c51
    }
Packit a71c51
Packit a71c51
  for (i = 0; i < len; i++)
Packit a71c51
    xfree (list[i]);
Packit a71c51
  xfree (list);
Packit a71c51
Packit a71c51
  return (result);
Packit a71c51
}
Packit a71c51
Packit a71c51
static int
Packit a71c51
history_tokenize_word (string, ind)
Packit a71c51
     const char *string;
Packit a71c51
     int ind;
Packit a71c51
{
Packit a71c51
  register int i;
Packit a71c51
  int delimiter, nestdelim, delimopen;
Packit a71c51
Packit a71c51
  i = ind;
Packit a71c51
  delimiter = nestdelim = 0;
Packit a71c51
Packit a71c51
  if (member (string[i], "()\n"))
Packit a71c51
    {
Packit a71c51
      i++;
Packit a71c51
      return i;
Packit a71c51
    }
Packit a71c51
Packit a71c51
  if (member (string[i], "<>;&|$"))
Packit a71c51
    {
Packit a71c51
      int peek = string[i + 1];
Packit a71c51
Packit a71c51
      if (peek == string[i] && peek != '$')
Packit a71c51
	{
Packit a71c51
	  if (peek == '<' && string[i + 2] == '-')
Packit a71c51
	    i++;
Packit a71c51
	  else if (peek == '<' && string[i + 2] == '<')
Packit a71c51
	    i++;
Packit a71c51
	  i += 2;
Packit a71c51
	  return i;
Packit a71c51
	}
Packit a71c51
      else if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
Packit a71c51
		(peek == '>' && string[i] == '&'))
Packit a71c51
	{
Packit a71c51
	  i += 2;
Packit a71c51
	  return i;
Packit a71c51
	}
Packit a71c51
      /* XXX - separated out for later -- bash-4.2 */
Packit a71c51
      else if ((peek == '(' && (string[i] == '>' || string[i] == '<')) || /* ) */
Packit a71c51
	       (peek == '(' && string[i] == '$')) /*)*/
Packit a71c51
	{
Packit a71c51
	  i += 2;
Packit a71c51
	  delimopen = '(';
Packit a71c51
	  delimiter = ')';
Packit a71c51
	  nestdelim = 1;
Packit a71c51
	  goto get_word;
Packit a71c51
	}
Packit a71c51
#if 0
Packit a71c51
      else if (peek == '\'' && string[i] == '$')
Packit a71c51
        {
Packit a71c51
	  i += 2;	/* XXX */
Packit a71c51
	  return i;
Packit a71c51
        }
Packit a71c51
#endif
Packit a71c51
Packit a71c51
      if (string[i] != '$')
Packit a71c51
	{
Packit a71c51
	  i++;
Packit a71c51
	  return i;
Packit a71c51
	}
Packit a71c51
    }
Packit a71c51
Packit a71c51
  /* same code also used for $(...)/<(...)/>(...) above */
Packit a71c51
  if (member (string[i], "!@?+*"))
Packit a71c51
    {
Packit a71c51
      int peek = string[i + 1];
Packit a71c51
Packit a71c51
      if (peek == '(')		/*)*/
Packit a71c51
	{
Packit a71c51
	  /* Shell extended globbing patterns */
Packit a71c51
	  i += 2;
Packit a71c51
	  delimopen = '(';
Packit a71c51
	  delimiter = ')';	/* XXX - not perfect */
Packit a71c51
	  nestdelim = 1;
Packit a71c51
	}
Packit a71c51
    }
Packit a71c51
Packit a71c51
get_word:
Packit a71c51
  /* Get word from string + i; */
Packit a71c51
Packit a71c51
  if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS))
Packit a71c51
    delimiter = string[i++];
Packit a71c51
Packit a71c51
  for (; string[i]; i++)
Packit a71c51
    {
Packit a71c51
      if (string[i] == '\\' && string[i + 1] == '\n')
Packit a71c51
	{
Packit a71c51
	  i++;
Packit a71c51
	  continue;
Packit a71c51
	}
Packit a71c51
Packit a71c51
      if (string[i] == '\\' && delimiter != '\'' &&
Packit a71c51
	  (delimiter != '"' || member (string[i], slashify_in_quotes)))
Packit a71c51
	{
Packit a71c51
	  i++;
Packit a71c51
	  continue;
Packit a71c51
	}
Packit a71c51
Packit a71c51
      /* delimiter must be set and set to something other than a quote if
Packit a71c51
	 nestdelim is set, so these tests are safe. */
Packit a71c51
      if (nestdelim && string[i] == delimopen)
Packit a71c51
	{
Packit a71c51
	  nestdelim++;
Packit a71c51
	  continue;
Packit a71c51
	}
Packit a71c51
      if (nestdelim && string[i] == delimiter)
Packit a71c51
	{
Packit a71c51
	  nestdelim--;
Packit a71c51
	  if (nestdelim == 0)
Packit a71c51
	    delimiter = 0;
Packit a71c51
	  continue;
Packit a71c51
	}
Packit a71c51
      
Packit a71c51
      if (delimiter && string[i] == delimiter)
Packit a71c51
	{
Packit a71c51
	  delimiter = 0;
Packit a71c51
	  continue;
Packit a71c51
	}
Packit a71c51
Packit a71c51
      if (delimiter == 0 && (member (string[i], history_word_delimiters)))
Packit a71c51
	break;
Packit a71c51
Packit a71c51
      if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS))
Packit a71c51
	delimiter = string[i];
Packit a71c51
    }
Packit a71c51
Packit a71c51
  return i;
Packit a71c51
}
Packit a71c51
Packit a71c51
static char *
Packit a71c51
history_substring (string, start, end)
Packit a71c51
     const char *string;
Packit a71c51
     int start, end;
Packit a71c51
{
Packit a71c51
  register int len;
Packit a71c51
  register char *result;
Packit a71c51
Packit a71c51
  len = end - start;
Packit a71c51
  result = (char *)xmalloc (len + 1);
Packit a71c51
  strncpy (result, string + start, len);
Packit a71c51
  result[len] = '\0';
Packit a71c51
  return result;
Packit a71c51
}
Packit a71c51
Packit a71c51
/* Parse STRING into tokens and return an array of strings.  If WIND is
Packit a71c51
   not -1 and INDP is not null, we also want the word surrounding index
Packit a71c51
   WIND.  The position in the returned array of strings is returned in
Packit a71c51
   *INDP. */
Packit a71c51
static char **
Packit a71c51
history_tokenize_internal (string, wind, indp)
Packit a71c51
     const char *string;
Packit a71c51
     int wind, *indp;
Packit a71c51
{
Packit a71c51
  char **result;
Packit a71c51
  register int i, start, result_index, size;
Packit a71c51
Packit a71c51
  /* If we're searching for a string that's not part of a word (e.g., " "),
Packit a71c51
     make sure we set *INDP to a reasonable value. */
Packit a71c51
  if (indp && wind != -1)
Packit a71c51
    *indp = -1;
Packit a71c51
Packit a71c51
  /* Get a token, and stuff it into RESULT.  The tokens are split
Packit a71c51
     exactly where the shell would split them. */
Packit a71c51
  for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
Packit a71c51
    {
Packit a71c51
      /* Skip leading whitespace. */
Packit a71c51
      for (; string[i] && whitespace (string[i]); i++)
Packit a71c51
	;
Packit a71c51
      if (string[i] == 0 || string[i] == history_comment_char)
Packit a71c51
	return (result);
Packit a71c51
Packit a71c51
      start = i;
Packit a71c51
Packit a71c51
      i = history_tokenize_word (string, start);
Packit a71c51
Packit a71c51
      /* If we have a non-whitespace delimiter character (which would not be
Packit a71c51
	 skipped by the loop above), use it and any adjacent delimiters to
Packit a71c51
	 make a separate field.  Any adjacent white space will be skipped the
Packit a71c51
	 next time through the loop. */
Packit a71c51
      if (i == start && history_word_delimiters)
Packit a71c51
	{
Packit a71c51
	  i++;
Packit a71c51
	  while (string[i] && member (string[i], history_word_delimiters))
Packit a71c51
	    i++;
Packit a71c51
	}
Packit a71c51
Packit a71c51
      /* If we are looking for the word in which the character at a
Packit a71c51
	 particular index falls, remember it. */
Packit a71c51
      if (indp && wind != -1 && wind >= start && wind < i)
Packit a71c51
        *indp = result_index;
Packit a71c51
Packit a71c51
      if (result_index + 2 >= size)
Packit a71c51
	result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
Packit a71c51
Packit a71c51
      result[result_index++] = history_substring (string, start, i);
Packit a71c51
      result[result_index] = (char *)NULL;
Packit a71c51
    }
Packit a71c51
Packit a71c51
  return (result);
Packit a71c51
}
Packit a71c51
Packit a71c51
/* Return an array of tokens, much as the shell might.  The tokens are
Packit a71c51
   parsed out of STRING. */
Packit a71c51
char **
Packit a71c51
history_tokenize (string)
Packit a71c51
     const char *string;
Packit a71c51
{
Packit a71c51
  return (history_tokenize_internal (string, -1, (int *)NULL));
Packit a71c51
}
Packit a71c51
Packit a71c51
/* Free members of WORDS from START to an empty string */
Packit a71c51
static void
Packit a71c51
freewords (words, start)
Packit a71c51
     char **words;
Packit a71c51
     int start;
Packit a71c51
{
Packit a71c51
  register int i;
Packit a71c51
Packit a71c51
  for (i = start; words[i]; i++)
Packit a71c51
    xfree (words[i]);
Packit a71c51
}
Packit a71c51
Packit a71c51
/* Find and return the word which contains the character at index IND
Packit a71c51
   in the history line LINE.  Used to save the word matched by the
Packit a71c51
   last history !?string? search. */
Packit a71c51
static char *
Packit a71c51
history_find_word (line, ind)
Packit a71c51
     char *line;
Packit a71c51
     int ind;
Packit a71c51
{
Packit a71c51
  char **words, *s;
Packit a71c51
  int i, wind;
Packit a71c51
Packit a71c51
  words = history_tokenize_internal (line, ind, &wind);
Packit a71c51
  if (wind == -1 || words == 0)
Packit a71c51
    {
Packit a71c51
      if (words)
Packit a71c51
	freewords (words, 0);
Packit a71c51
      FREE (words);
Packit a71c51
      return ((char *)NULL);
Packit a71c51
    }
Packit a71c51
  s = words[wind];
Packit a71c51
  for (i = 0; i < wind; i++)
Packit a71c51
    xfree (words[i]);
Packit a71c51
  freewords (words, wind + 1);
Packit a71c51
  xfree (words);
Packit a71c51
  return s;
Packit a71c51
}