File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / readline / histexpand.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jul 30 08:16:45 2014 UTC (9 years, 11 months ago) by misho
Branches: readline, MAIN
CVS tags: v6_3p10_cross, v6_3p10, v6_3, p6, HEAD
readline 6.3

    1: /* histexpand.c -- history expansion. */
    2: 
    3: /* Copyright (C) 1989-2012 Free Software Foundation, Inc.
    4: 
    5:    This file contains the GNU History Library (History), a set of
    6:    routines for managing the text of previously typed lines.
    7: 
    8:    History is free software: you can redistribute it and/or modify
    9:    it under the terms of the GNU General Public License as published by
   10:    the Free Software Foundation, either version 3 of the License, or
   11:    (at your option) any later version.
   12: 
   13:    History is distributed in the hope that it will be useful,
   14:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   15:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16:    GNU General Public License for more details.
   17: 
   18:    You should have received a copy of the GNU General Public License
   19:    along with History.  If not, see <http://www.gnu.org/licenses/>.
   20: */
   21: 
   22: #define READLINE_LIBRARY
   23: 
   24: #if defined (HAVE_CONFIG_H)
   25: #  include <config.h>
   26: #endif
   27: 
   28: #include <stdio.h>
   29: 
   30: #if defined (HAVE_STDLIB_H)
   31: #  include <stdlib.h>
   32: #else
   33: #  include "ansi_stdlib.h"
   34: #endif /* HAVE_STDLIB_H */
   35: 
   36: #if defined (HAVE_UNISTD_H)
   37: #  ifndef _MINIX
   38: #    include <sys/types.h>
   39: #  endif
   40: #  include <unistd.h>
   41: #endif
   42: 
   43: #include "rlmbutil.h"
   44: 
   45: #include "history.h"
   46: #include "histlib.h"
   47: 
   48: #include "rlshell.h"
   49: #include "xmalloc.h"
   50: 
   51: #define HISTORY_WORD_DELIMITERS		" \t\n;&()|<>"
   52: #define HISTORY_QUOTE_CHARACTERS	"\"'`"
   53: 
   54: #define slashify_in_quotes "\\`\"$"
   55: 
   56: typedef int _hist_search_func_t PARAMS((const char *, int));
   57: 
   58: static char error_pointer;
   59: 
   60: static char *subst_lhs;
   61: static char *subst_rhs;
   62: static int subst_lhs_len;
   63: static int subst_rhs_len;
   64: 
   65: static char *get_history_word_specifier PARAMS((char *, char *, int *));
   66: static int history_tokenize_word PARAMS((const char *, int));
   67: static char **history_tokenize_internal PARAMS((const char *, int, int *));
   68: static char *history_substring PARAMS((const char *, int, int));
   69: static void freewords PARAMS((char **, int));
   70: static char *history_find_word PARAMS((char *, int));
   71: 
   72: static char *quote_breaks PARAMS((char *));
   73: 
   74: /* Variables exported by this file. */
   75: /* The character that represents the start of a history expansion
   76:    request.  This is usually `!'. */
   77: char history_expansion_char = '!';
   78: 
   79: /* The character that invokes word substitution if found at the start of
   80:    a line.  This is usually `^'. */
   81: char history_subst_char = '^';
   82: 
   83: /* During tokenization, if this character is seen as the first character
   84:    of a word, then it, and all subsequent characters upto a newline are
   85:    ignored.  For a Bourne shell, this should be '#'.  Bash special cases
   86:    the interactive comment character to not be a comment delimiter. */
   87: char history_comment_char = '\0';
   88: 
   89: /* The list of characters which inhibit the expansion of text if found
   90:    immediately following history_expansion_char. */
   91: char *history_no_expand_chars = " \t\n\r=";
   92: 
   93: /* If set to a non-zero value, single quotes inhibit history expansion.
   94:    The default is 0. */
   95: int history_quotes_inhibit_expansion = 0;
   96: 
   97: /* Used to split words by history_tokenize_internal. */
   98: char *history_word_delimiters = HISTORY_WORD_DELIMITERS;
   99: 
  100: /* If set, this points to a function that is called to verify that a
  101:    particular history expansion should be performed. */
  102: rl_linebuf_func_t *history_inhibit_expansion_function;
  103: 
  104: /* **************************************************************** */
  105: /*								    */
  106: /*			History Expansion			    */
  107: /*								    */
  108: /* **************************************************************** */
  109: 
  110: /* Hairy history expansion on text, not tokens.  This is of general
  111:    use, and thus belongs in this library. */
  112: 
  113: /* The last string searched for by a !?string? search. */
  114: static char *search_string;
  115: 
  116: /* The last string matched by a !?string? search. */
  117: static char *search_match;
  118: 
  119: /* Return the event specified at TEXT + OFFSET modifying OFFSET to
  120:    point to after the event specifier.  Just a pointer to the history
  121:    line is returned; NULL is returned in the event of a bad specifier.
  122:    You pass STRING with *INDEX equal to the history_expansion_char that
  123:    begins this specification.
  124:    DELIMITING_QUOTE is a character that is allowed to end the string
  125:    specification for what to search for in addition to the normal
  126:    characters `:', ` ', `\t', `\n', and sometimes `?'.
  127:    So you might call this function like:
  128:    line = get_history_event ("!echo:p", &index, 0);  */
  129: char *
  130: get_history_event (string, caller_index, delimiting_quote)
  131:      const char *string;
  132:      int *caller_index;
  133:      int delimiting_quote;
  134: {
  135:   register int i;
  136:   register char c;
  137:   HIST_ENTRY *entry;
  138:   int which, sign, local_index, substring_okay;
  139:   _hist_search_func_t *search_func;
  140:   char *temp;
  141: 
  142:   /* The event can be specified in a number of ways.
  143: 
  144:      !!   the previous command
  145:      !n   command line N
  146:      !-n  current command-line minus N
  147:      !str the most recent command starting with STR
  148:      !?str[?]
  149: 	  the most recent command containing STR
  150: 
  151:      All values N are determined via HISTORY_BASE. */
  152: 
  153:   i = *caller_index;
  154: 
  155:   if (string[i] != history_expansion_char)
  156:     return ((char *)NULL);
  157: 
  158:   /* Move on to the specification. */
  159:   i++;
  160: 
  161:   sign = 1;
  162:   substring_okay = 0;
  163: 
  164: #define RETURN_ENTRY(e, w) \
  165: 	return ((e = history_get (w)) ? e->line : (char *)NULL)
  166: 
  167:   /* Handle !! case. */
  168:   if (string[i] == history_expansion_char)
  169:     {
  170:       i++;
  171:       which = history_base + (history_length - 1);
  172:       *caller_index = i;
  173:       RETURN_ENTRY (entry, which);
  174:     }
  175: 
  176:   /* Hack case of numeric line specification. */
  177:   if (string[i] == '-')
  178:     {
  179:       sign = -1;
  180:       i++;
  181:     }
  182: 
  183:   if (_rl_digit_p (string[i]))
  184:     {
  185:       /* Get the extent of the digits and compute the value. */
  186:       for (which = 0; _rl_digit_p (string[i]); i++)
  187: 	which = (which * 10) + _rl_digit_value (string[i]);
  188: 
  189:       *caller_index = i;
  190: 
  191:       if (sign < 0)
  192: 	which = (history_length + history_base) - which;
  193: 
  194:       RETURN_ENTRY (entry, which);
  195:     }
  196: 
  197:   /* This must be something to search for.  If the spec begins with
  198:      a '?', then the string may be anywhere on the line.  Otherwise,
  199:      the string must be found at the start of a line. */
  200:   if (string[i] == '?')
  201:     {
  202:       substring_okay++;
  203:       i++;
  204:     }
  205: 
  206:   /* Only a closing `?' or a newline delimit a substring search string. */
  207:   for (local_index = i; c = string[i]; i++)
  208:     {
  209: #if defined (HANDLE_MULTIBYTE)
  210:       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
  211: 	{
  212: 	  int v;
  213: 	  mbstate_t ps;
  214: 
  215: 	  memset (&ps, 0, sizeof (mbstate_t));
  216: 	  /* These produce warnings because we're passing a const string to a
  217: 	     function that takes a non-const string. */
  218: 	  _rl_adjust_point ((char *)string, i, &ps);
  219: 	  if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1)
  220: 	    {
  221: 	      i += v - 1;
  222: 	      continue;
  223: 	    }
  224:         }
  225: 
  226: #endif /* HANDLE_MULTIBYTE */
  227:       if ((!substring_okay && (whitespace (c) || c == ':' ||
  228: 	  (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
  229: 	  string[i] == delimiting_quote)) ||
  230: 	  string[i] == '\n' ||
  231: 	  (substring_okay && string[i] == '?'))
  232: 	break;
  233:     }
  234: 
  235:   which = i - local_index;
  236:   temp = (char *)xmalloc (1 + which);
  237:   if (which)
  238:     strncpy (temp, string + local_index, which);
  239:   temp[which] = '\0';
  240: 
  241:   if (substring_okay && string[i] == '?')
  242:     i++;
  243: 
  244:   *caller_index = i;
  245: 
  246: #define FAIL_SEARCH() \
  247:   do { \
  248:     history_offset = history_length; xfree (temp) ; return (char *)NULL; \
  249:   } while (0)
  250: 
  251:   /* If there is no search string, try to use the previous search string,
  252:      if one exists.  If not, fail immediately. */
  253:   if (*temp == '\0' && substring_okay)
  254:     {
  255:       if (search_string)
  256:         {
  257:           xfree (temp);
  258:           temp = savestring (search_string);
  259:         }
  260:       else
  261:         FAIL_SEARCH ();
  262:     }
  263: 
  264:   search_func = substring_okay ? history_search : history_search_prefix;
  265:   while (1)
  266:     {
  267:       local_index = (*search_func) (temp, -1);
  268: 
  269:       if (local_index < 0)
  270: 	FAIL_SEARCH ();
  271: 
  272:       if (local_index == 0 || substring_okay)
  273: 	{
  274: 	  entry = current_history ();
  275: 	  if (entry == 0)
  276: 	    FAIL_SEARCH ();
  277: 	  history_offset = history_length;
  278: 	
  279: 	  /* If this was a substring search, then remember the
  280: 	     string that we matched for word substitution. */
  281: 	  if (substring_okay)
  282: 	    {
  283: 	      FREE (search_string);
  284: 	      search_string = temp;
  285: 
  286: 	      FREE (search_match);
  287: 	      search_match = history_find_word (entry->line, local_index);
  288: 	    }
  289: 	  else
  290: 	    xfree (temp);
  291: 
  292: 	  return (entry->line);
  293: 	}
  294: 
  295:       if (history_offset)
  296: 	history_offset--;
  297:       else
  298: 	FAIL_SEARCH ();
  299:     }
  300: #undef FAIL_SEARCH
  301: #undef RETURN_ENTRY
  302: }
  303: 
  304: /* Function for extracting single-quoted strings.  Used for inhibiting
  305:    history expansion within single quotes. */
  306: 
  307: /* Extract the contents of STRING as if it is enclosed in single quotes.
  308:    SINDEX, when passed in, is the offset of the character immediately
  309:    following the opening single quote; on exit, SINDEX is left pointing
  310:    to the closing single quote.  FLAGS currently used to allow backslash
  311:    to escape a single quote (e.g., for bash $'...'). */
  312: static void
  313: hist_string_extract_single_quoted (string, sindex, flags)
  314:      char *string;
  315:      int *sindex, flags;
  316: {
  317:   register int i;
  318: 
  319:   for (i = *sindex; string[i] && string[i] != '\''; i++)
  320:     {
  321:       if ((flags & 1) && string[i] == '\\' && string[i+1])
  322:         i++;
  323:     }
  324: 
  325:   *sindex = i;
  326: }
  327: 
  328: static char *
  329: quote_breaks (s)
  330:      char *s;
  331: {
  332:   register char *p, *r;
  333:   char *ret;
  334:   int len = 3;
  335: 
  336:   for (p = s; p && *p; p++, len++)
  337:     {
  338:       if (*p == '\'')
  339: 	len += 3;
  340:       else if (whitespace (*p) || *p == '\n')
  341: 	len += 2;
  342:     }
  343: 
  344:   r = ret = (char *)xmalloc (len);
  345:   *r++ = '\'';
  346:   for (p = s; p && *p; )
  347:     {
  348:       if (*p == '\'')
  349: 	{
  350: 	  *r++ = '\'';
  351: 	  *r++ = '\\';
  352: 	  *r++ = '\'';
  353: 	  *r++ = '\'';
  354: 	  p++;
  355: 	}
  356:       else if (whitespace (*p) || *p == '\n')
  357: 	{
  358: 	  *r++ = '\'';
  359: 	  *r++ = *p++;
  360: 	  *r++ = '\'';
  361: 	}
  362:       else
  363: 	*r++ = *p++;
  364:     }
  365:   *r++ = '\'';
  366:   *r = '\0';
  367:   return ret;
  368: }
  369: 
  370: static char *
  371: hist_error(s, start, current, errtype)
  372:       char *s;
  373:       int start, current, errtype;
  374: {
  375:   char *temp;
  376:   const char *emsg;
  377:   int ll, elen;
  378: 
  379:   ll = current - start;
  380: 
  381:   switch (errtype)
  382:     {
  383:     case EVENT_NOT_FOUND:
  384:       emsg = "event not found";
  385:       elen = 15;
  386:       break;
  387:     case BAD_WORD_SPEC:
  388:       emsg = "bad word specifier";
  389:       elen = 18;
  390:       break;
  391:     case SUBST_FAILED:
  392:       emsg = "substitution failed";
  393:       elen = 19;
  394:       break;
  395:     case BAD_MODIFIER:
  396:       emsg = "unrecognized history modifier";
  397:       elen = 29;
  398:       break;
  399:     case NO_PREV_SUBST:
  400:       emsg = "no previous substitution";
  401:       elen = 24;
  402:       break;
  403:     default:
  404:       emsg = "unknown expansion error";
  405:       elen = 23;
  406:       break;
  407:     }
  408: 
  409:   temp = (char *)xmalloc (ll + elen + 3);
  410:   strncpy (temp, s + start, ll);
  411:   temp[ll] = ':';
  412:   temp[ll + 1] = ' ';
  413:   strcpy (temp + ll + 2, emsg);
  414:   return (temp);
  415: }
  416: 
  417: /* Get a history substitution string from STR starting at *IPTR
  418:    and return it.  The length is returned in LENPTR.
  419: 
  420:    A backslash can quote the delimiter.  If the string is the
  421:    empty string, the previous pattern is used.  If there is
  422:    no previous pattern for the lhs, the last history search
  423:    string is used.
  424: 
  425:    If IS_RHS is 1, we ignore empty strings and set the pattern
  426:    to "" anyway.  subst_lhs is not changed if the lhs is empty;
  427:    subst_rhs is allowed to be set to the empty string. */
  428: 
  429: static char *
  430: get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
  431:      char *str;
  432:      int *iptr, delimiter, is_rhs, *lenptr;
  433: {
  434:   register int si, i, j, k;
  435:   char *s;
  436: #if defined (HANDLE_MULTIBYTE)
  437:   mbstate_t ps;
  438: #endif
  439: 
  440:   s = (char *)NULL;
  441:   i = *iptr;
  442: 
  443: #if defined (HANDLE_MULTIBYTE)
  444:   memset (&ps, 0, sizeof (mbstate_t));
  445:   _rl_adjust_point (str, i, &ps);
  446: #endif
  447: 
  448:   for (si = i; str[si] && str[si] != delimiter; si++)
  449: #if defined (HANDLE_MULTIBYTE)
  450:     if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
  451:       {
  452: 	int v;
  453: 	if ((v = _rl_get_char_len (str + si, &ps)) > 1)
  454: 	  si += v - 1;
  455: 	else if (str[si] == '\\' && str[si + 1] == delimiter)
  456: 	  si++;
  457:       }
  458:     else
  459: #endif /* HANDLE_MULTIBYTE */
  460:       if (str[si] == '\\' && str[si + 1] == delimiter)
  461: 	si++;
  462: 
  463:   if (si > i || is_rhs)
  464:     {
  465:       s = (char *)xmalloc (si - i + 1);
  466:       for (j = 0, k = i; k < si; j++, k++)
  467: 	{
  468: 	  /* Remove a backslash quoting the search string delimiter. */
  469: 	  if (str[k] == '\\' && str[k + 1] == delimiter)
  470: 	    k++;
  471: 	  s[j] = str[k];
  472: 	}
  473:       s[j] = '\0';
  474:       if (lenptr)
  475: 	*lenptr = j;
  476:     }
  477: 
  478:   i = si;
  479:   if (str[i])
  480:     i++;
  481:   *iptr = i;
  482: 
  483:   return s;
  484: }
  485: 
  486: static void
  487: postproc_subst_rhs ()
  488: {
  489:   char *new;
  490:   int i, j, new_size;
  491: 
  492:   new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len);
  493:   for (i = j = 0; i < subst_rhs_len; i++)
  494:     {
  495:       if (subst_rhs[i] == '&')
  496: 	{
  497: 	  if (j + subst_lhs_len >= new_size)
  498: 	    new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
  499: 	  strcpy (new + j, subst_lhs);
  500: 	  j += subst_lhs_len;
  501: 	}
  502:       else
  503: 	{
  504: 	  /* a single backslash protects the `&' from lhs interpolation */
  505: 	  if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
  506: 	    i++;
  507: 	  if (j >= new_size)
  508: 	    new = (char *)xrealloc (new, new_size *= 2);
  509: 	  new[j++] = subst_rhs[i];
  510: 	}
  511:     }
  512:   new[j] = '\0';
  513:   xfree (subst_rhs);
  514:   subst_rhs = new;
  515:   subst_rhs_len = j;
  516: }
  517: 
  518: /* Expand the bulk of a history specifier starting at STRING[START].
  519:    Returns 0 if everything is OK, -1 if an error occurred, and 1
  520:    if the `p' modifier was supplied and the caller should just print
  521:    the returned string.  Returns the new index into string in
  522:    *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
  523: static int
  524: history_expand_internal (string, start, qc, end_index_ptr, ret_string, current_line)
  525:      char *string;
  526:      int start, qc, *end_index_ptr;
  527:      char **ret_string;
  528:      char *current_line;	/* for !# */
  529: {
  530:   int i, n, starting_index;
  531:   int substitute_globally, subst_bywords, want_quotes, print_only;
  532:   char *event, *temp, *result, *tstr, *t, c, *word_spec;
  533:   int result_len;
  534: #if defined (HANDLE_MULTIBYTE)
  535:   mbstate_t ps;
  536: 
  537:   memset (&ps, 0, sizeof (mbstate_t));
  538: #endif
  539: 
  540:   result = (char *)xmalloc (result_len = 128);
  541: 
  542:   i = start;
  543: 
  544:   /* If it is followed by something that starts a word specifier,
  545:      then !! is implied as the event specifier. */
  546: 
  547:   if (member (string[i + 1], ":$*%^"))
  548:     {
  549:       char fake_s[3];
  550:       int fake_i = 0;
  551:       i++;
  552:       fake_s[0] = fake_s[1] = history_expansion_char;
  553:       fake_s[2] = '\0';
  554:       event = get_history_event (fake_s, &fake_i, 0);
  555:     }
  556:   else if (string[i + 1] == '#')
  557:     {
  558:       i += 2;
  559:       event = current_line;
  560:     }
  561:   else
  562:     event = get_history_event (string, &i, qc);
  563: 	  
  564:   if (event == 0)
  565:     {
  566:       *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
  567:       xfree (result);
  568:       return (-1);
  569:     }
  570: 
  571:   /* If a word specifier is found, then do what that requires. */
  572:   starting_index = i;
  573:   word_spec = get_history_word_specifier (string, event, &i);
  574: 
  575:   /* There is no such thing as a `malformed word specifier'.  However,
  576:      it is possible for a specifier that has no match.  In that case,
  577:      we complain. */
  578:   if (word_spec == (char *)&error_pointer)
  579:     {
  580:       *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
  581:       xfree (result);
  582:       return (-1);
  583:     }
  584: 
  585:   /* If no word specifier, than the thing of interest was the event. */
  586:   temp = word_spec ? savestring (word_spec) : savestring (event);
  587:   FREE (word_spec);
  588: 
  589:   /* Perhaps there are other modifiers involved.  Do what they say. */
  590:   want_quotes = substitute_globally = subst_bywords = print_only = 0;
  591:   starting_index = i;
  592: 
  593:   while (string[i] == ':')
  594:     {
  595:       c = string[i + 1];
  596: 
  597:       if (c == 'g' || c == 'a')
  598: 	{
  599: 	  substitute_globally = 1;
  600: 	  i++;
  601: 	  c = string[i + 1];
  602: 	}
  603:       else if (c == 'G')
  604: 	{
  605: 	  subst_bywords = 1;
  606: 	  i++;
  607: 	  c = string[i + 1];
  608: 	}
  609: 
  610:       switch (c)
  611: 	{
  612: 	default:
  613: 	  *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
  614: 	  xfree (result);
  615: 	  xfree (temp);
  616: 	  return -1;
  617: 
  618: 	case 'q':
  619: 	  want_quotes = 'q';
  620: 	  break;
  621: 
  622: 	case 'x':
  623: 	  want_quotes = 'x';
  624: 	  break;
  625: 
  626: 	  /* :p means make this the last executed line.  So we
  627: 	     return an error state after adding this line to the
  628: 	     history. */
  629: 	case 'p':
  630: 	  print_only++;
  631: 	  break;
  632: 
  633: 	  /* :t discards all but the last part of the pathname. */
  634: 	case 't':
  635: 	  tstr = strrchr (temp, '/');
  636: 	  if (tstr)
  637: 	    {
  638: 	      tstr++;
  639: 	      t = savestring (tstr);
  640: 	      xfree (temp);
  641: 	      temp = t;
  642: 	    }
  643: 	  break;
  644: 
  645: 	  /* :h discards the last part of a pathname. */
  646: 	case 'h':
  647: 	  tstr = strrchr (temp, '/');
  648: 	  if (tstr)
  649: 	    *tstr = '\0';
  650: 	  break;
  651: 
  652: 	  /* :r discards the suffix. */
  653: 	case 'r':
  654: 	  tstr = strrchr (temp, '.');
  655: 	  if (tstr)
  656: 	    *tstr = '\0';
  657: 	  break;
  658: 
  659: 	  /* :e discards everything but the suffix. */
  660: 	case 'e':
  661: 	  tstr = strrchr (temp, '.');
  662: 	  if (tstr)
  663: 	    {
  664: 	      t = savestring (tstr);
  665: 	      xfree (temp);
  666: 	      temp = t;
  667: 	    }
  668: 	  break;
  669: 
  670: 	/* :s/this/that substitutes `that' for the first
  671: 	   occurrence of `this'.  :gs/this/that substitutes `that'
  672: 	   for each occurrence of `this'.  :& repeats the last
  673: 	   substitution.  :g& repeats the last substitution
  674: 	   globally. */
  675: 
  676: 	case '&':
  677: 	case 's':
  678: 	  {
  679: 	    char *new_event;
  680: 	    int delimiter, failed, si, l_temp, ws, we;
  681: 
  682: 	    if (c == 's')
  683: 	      {
  684: 		if (i + 2 < (int)strlen (string))
  685: 		  {
  686: #if defined (HANDLE_MULTIBYTE)
  687: 		    if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
  688: 		      {
  689: 			_rl_adjust_point (string, i + 2, &ps);
  690: 			if (_rl_get_char_len (string + i + 2, &ps) > 1)
  691: 			  delimiter = 0;
  692: 			else
  693: 			  delimiter = string[i + 2];
  694: 		      }
  695: 		    else
  696: #endif /* HANDLE_MULTIBYTE */
  697: 		      delimiter = string[i + 2];
  698: 		  }
  699: 		else
  700: 		  break;	/* no search delimiter */
  701: 
  702: 		i += 3;
  703: 
  704: 		t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
  705: 		/* An empty substitution lhs with no previous substitution
  706: 		   uses the last search string as the lhs. */
  707: 		if (t)
  708: 		  {
  709: 		    FREE (subst_lhs);
  710: 		    subst_lhs = t;
  711: 		  }
  712: 		else if (!subst_lhs)
  713: 		  {
  714: 		    if (search_string && *search_string)
  715: 		      {
  716: 			subst_lhs = savestring (search_string);
  717: 			subst_lhs_len = strlen (subst_lhs);
  718: 		      }
  719: 		    else
  720: 		      {
  721: 			subst_lhs = (char *) NULL;
  722: 			subst_lhs_len = 0;
  723: 		      }
  724: 		  }
  725: 
  726: 		FREE (subst_rhs);
  727: 		subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
  728: 
  729: 		/* If `&' appears in the rhs, it's supposed to be replaced
  730: 		   with the lhs. */
  731: 		if (member ('&', subst_rhs))
  732: 		  postproc_subst_rhs ();
  733: 	      }
  734: 	    else
  735: 	      i += 2;
  736: 
  737: 	    /* If there is no lhs, the substitution can't succeed. */
  738: 	    if (subst_lhs_len == 0)
  739: 	      {
  740: 		*ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
  741: 		xfree (result);
  742: 		xfree (temp);
  743: 		return -1;
  744: 	      }
  745: 
  746: 	    l_temp = strlen (temp);
  747: 	    /* Ignore impossible cases. */
  748: 	    if (subst_lhs_len > l_temp)
  749: 	      {
  750: 		*ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
  751: 		xfree (result);
  752: 		xfree (temp);
  753: 		return (-1);
  754: 	      }
  755: 
  756: 	    /* Find the first occurrence of THIS in TEMP. */
  757: 	    /* Substitute SUBST_RHS for SUBST_LHS in TEMP.  There are three
  758: 	       cases to consider:
  759: 
  760: 		 1.  substitute_globally == subst_bywords == 0
  761: 		 2.  substitute_globally == 1 && subst_bywords == 0
  762: 		 3.  substitute_globally == 0 && subst_bywords == 1
  763: 
  764: 	       In the first case, we substitute for the first occurrence only.
  765: 	       In the second case, we substitute for every occurrence.
  766: 	       In the third case, we tokenize into words and substitute the
  767: 	       first occurrence of each word. */
  768: 
  769: 	    si = we = 0;
  770: 	    for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
  771: 	      {
  772: 		/* First skip whitespace and find word boundaries if
  773: 		   we're past the end of the word boundary we found
  774: 		   the last time. */
  775: 		if (subst_bywords && si > we)
  776: 		  {
  777: 		    for (; temp[si] && whitespace (temp[si]); si++)
  778: 		      ;
  779: 		    ws = si;
  780: 		    we = history_tokenize_word (temp, si);
  781: 		  }
  782: 
  783: 		if (STREQN (temp+si, subst_lhs, subst_lhs_len))
  784: 		  {
  785: 		    int len = subst_rhs_len - subst_lhs_len + l_temp;
  786: 		    new_event = (char *)xmalloc (1 + len);
  787: 		    strncpy (new_event, temp, si);
  788: 		    strncpy (new_event + si, subst_rhs, subst_rhs_len);
  789: 		    strncpy (new_event + si + subst_rhs_len,
  790: 			     temp + si + subst_lhs_len,
  791: 			     l_temp - (si + subst_lhs_len));
  792: 		    new_event[len] = '\0';
  793: 		    xfree (temp);
  794: 		    temp = new_event;
  795: 
  796: 		    failed = 0;
  797: 
  798: 		    if (substitute_globally)
  799: 		      {
  800: 			/* Reported to fix a bug that causes it to skip every
  801: 			   other match when matching a single character.  Was
  802: 			   si += subst_rhs_len previously. */
  803: 			si += subst_rhs_len - 1;
  804: 			l_temp = strlen (temp);
  805: 			substitute_globally++;
  806: 			continue;
  807: 		      }
  808: 		    else if (subst_bywords)
  809: 		      {
  810: 			si = we;
  811: 			l_temp = strlen (temp);
  812: 			continue;
  813: 		      }
  814: 		    else
  815: 		      break;
  816: 		  }
  817: 	      }
  818: 
  819: 	    if (substitute_globally > 1)
  820: 	      {
  821: 		substitute_globally = 0;
  822: 		continue;	/* don't want to increment i */
  823: 	      }
  824: 
  825: 	    if (failed == 0)
  826: 	      continue;		/* don't want to increment i */
  827: 
  828: 	    *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
  829: 	    xfree (result);
  830: 	    xfree (temp);
  831: 	    return (-1);
  832: 	  }
  833: 	}
  834:       i += 2;
  835:     }
  836:   /* Done with modifiers. */
  837:   /* Believe it or not, we have to back the pointer up by one. */
  838:   --i;
  839: 
  840:   if (want_quotes)
  841:     {
  842:       char *x;
  843: 
  844:       if (want_quotes == 'q')
  845: 	x = sh_single_quote (temp);
  846:       else if (want_quotes == 'x')
  847: 	x = quote_breaks (temp);
  848:       else
  849: 	x = savestring (temp);
  850: 
  851:       xfree (temp);
  852:       temp = x;
  853:     }
  854: 
  855:   n = strlen (temp);
  856:   if (n >= result_len)
  857:     result = (char *)xrealloc (result, n + 2);
  858:   strcpy (result, temp);
  859:   xfree (temp);
  860: 
  861:   *end_index_ptr = i;
  862:   *ret_string = result;
  863:   return (print_only);
  864: }
  865: 
  866: /* Expand the string STRING, placing the result into OUTPUT, a pointer
  867:    to a string.  Returns:
  868: 
  869:   -1) If there was an error in expansion.
  870:    0) If no expansions took place (or, if the only change in
  871:       the text was the de-slashifying of the history expansion
  872:       character)
  873:    1) If expansions did take place
  874:    2) If the `p' modifier was given and the caller should print the result
  875: 
  876:   If an error ocurred in expansion, then OUTPUT contains a descriptive
  877:   error message. */
  878: 
  879: #define ADD_STRING(s) \
  880: 	do \
  881: 	  { \
  882: 	    int sl = strlen (s); \
  883: 	    j += sl; \
  884: 	    if (j >= result_len) \
  885: 	      { \
  886: 		while (j >= result_len) \
  887: 		  result_len += 128; \
  888: 		result = (char *)xrealloc (result, result_len); \
  889: 	      } \
  890: 	    strcpy (result + j - sl, s); \
  891: 	  } \
  892: 	while (0)
  893: 
  894: #define ADD_CHAR(c) \
  895: 	do \
  896: 	  { \
  897: 	    if (j >= result_len - 1) \
  898: 	      result = (char *)xrealloc (result, result_len += 64); \
  899: 	    result[j++] = c; \
  900: 	    result[j] = '\0'; \
  901: 	  } \
  902: 	while (0)
  903: 
  904: int
  905: history_expand (hstring, output)
  906:      char *hstring;
  907:      char **output;
  908: {
  909:   register int j;
  910:   int i, r, l, passc, cc, modified, eindex, only_printing, dquote, squote, flag;
  911:   char *string;
  912: 
  913:   /* The output string, and its length. */
  914:   int result_len;
  915:   char *result;
  916: 
  917: #if defined (HANDLE_MULTIBYTE)
  918:   char mb[MB_LEN_MAX];
  919:   mbstate_t ps;
  920: #endif
  921: 
  922:   /* Used when adding the string. */
  923:   char *temp;
  924: 
  925:   if (output == 0)
  926:     return 0;
  927: 
  928:   /* Setting the history expansion character to 0 inhibits all
  929:      history expansion. */
  930:   if (history_expansion_char == 0)
  931:     {
  932:       *output = savestring (hstring);
  933:       return (0);
  934:     }
  935:     
  936:   /* Prepare the buffer for printing error messages. */
  937:   result = (char *)xmalloc (result_len = 256);
  938:   result[0] = '\0';
  939: 
  940:   only_printing = modified = 0;
  941:   l = strlen (hstring);
  942: 
  943:   /* Grovel the string.  Only backslash and single quotes can quote the
  944:      history escape character.  We also handle arg specifiers. */
  945: 
  946:   /* Before we grovel forever, see if the history_expansion_char appears
  947:      anywhere within the text. */
  948: 
  949:   /* The quick substitution character is a history expansion all right.  That
  950:      is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
  951:      that is the substitution that we do. */
  952:   if (hstring[0] == history_subst_char)
  953:     {
  954:       string = (char *)xmalloc (l + 5);
  955: 
  956:       string[0] = string[1] = history_expansion_char;
  957:       string[2] = ':';
  958:       string[3] = 's';
  959:       strcpy (string + 4, hstring);
  960:       l += 4;
  961:     }
  962:   else
  963:     {
  964: #if defined (HANDLE_MULTIBYTE)
  965:       memset (&ps, 0, sizeof (mbstate_t));
  966: #endif
  967: 
  968:       string = hstring;
  969:       /* If not quick substitution, still maybe have to do expansion. */
  970: 
  971:       /* `!' followed by one of the characters in history_no_expand_chars
  972: 	 is NOT an expansion. */
  973:       for (i = dquote = squote = 0; string[i]; i++)
  974: 	{
  975: #if defined (HANDLE_MULTIBYTE)
  976: 	  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
  977: 	    {
  978: 	      int v;
  979: 	      v = _rl_get_char_len (string + i, &ps);
  980: 	      if (v > 1)
  981: 		{
  982: 		  i += v - 1;
  983: 		  continue;
  984: 		}
  985: 	    }
  986: #endif /* HANDLE_MULTIBYTE */
  987: 
  988: 	  cc = string[i + 1];
  989: 	  /* The history_comment_char, if set, appearing at the beginning
  990: 	     of a word signifies that the rest of the line should not have
  991: 	     history expansion performed on it.
  992: 	     Skip the rest of the line and break out of the loop. */
  993: 	  if (history_comment_char && string[i] == history_comment_char &&
  994: 	      (i == 0 || member (string[i - 1], history_word_delimiters)))
  995: 	    {
  996: 	      while (string[i])
  997: 		i++;
  998: 	      break;
  999: 	    }
 1000: 	  else if (string[i] == history_expansion_char)
 1001: 	    {
 1002: 	      if (cc == 0 || member (cc, history_no_expand_chars))
 1003: 		continue;
 1004: 	      /* DQUOTE won't be set unless history_quotes_inhibit_expansion
 1005: 		 is set.  The idea here is to treat double-quoted strings the
 1006: 		 same as the word outside double quotes; in effect making the
 1007: 		 double quote part of history_no_expand_chars when DQUOTE is
 1008: 		 set. */
 1009: 	      else if (dquote && cc == '"')
 1010: 		continue;
 1011: 	      /* If the calling application has set
 1012: 		 history_inhibit_expansion_function to a function that checks
 1013: 		 for special cases that should not be history expanded,
 1014: 		 call the function and skip the expansion if it returns a
 1015: 		 non-zero value. */
 1016: 	      else if (history_inhibit_expansion_function &&
 1017: 			(*history_inhibit_expansion_function) (string, i))
 1018: 		continue;
 1019: 	      else
 1020: 		break;
 1021: 	    }
 1022: 	  /* Shell-like quoting: allow backslashes to quote double quotes
 1023: 	     inside a double-quoted string. */
 1024: 	  else if (dquote && string[i] == '\\' && cc == '"')
 1025: 	    i++;
 1026: 	  /* More shell-like quoting:  if we're paying attention to single
 1027: 	     quotes and letting them quote the history expansion character,
 1028: 	     then we need to pay attention to double quotes, because single
 1029: 	     quotes are not special inside double-quoted strings. */
 1030: 	  else if (history_quotes_inhibit_expansion && string[i] == '"')
 1031: 	    {
 1032: 	      dquote = 1 - dquote;
 1033: 	    }
 1034: 	  else if (dquote == 0 && history_quotes_inhibit_expansion && string[i] == '\'')
 1035: 	    {
 1036: 	      /* If this is bash, single quotes inhibit history expansion. */
 1037: 	      flag = (i > 0 && string[i - 1] == '$');
 1038: 	      i++;
 1039: 	      hist_string_extract_single_quoted (string, &i, flag);
 1040: 	    }
 1041: 	  else if (history_quotes_inhibit_expansion && string[i] == '\\')
 1042: 	    {
 1043: 	      /* If this is bash, allow backslashes to quote single
 1044: 		 quotes and the history expansion character. */
 1045: 	      if (cc == '\'' || cc == history_expansion_char)
 1046: 		i++;
 1047: 	    }
 1048: 	  
 1049: 	}
 1050: 	  
 1051:       if (string[i] != history_expansion_char)
 1052: 	{
 1053: 	  xfree (result);
 1054: 	  *output = savestring (string);
 1055: 	  return (0);
 1056: 	}
 1057:     }
 1058: 
 1059:   /* Extract and perform the substitution. */
 1060:   for (passc = dquote = squote = i = j = 0; i < l; i++)
 1061:     {
 1062:       int qc, tchar = string[i];
 1063: 
 1064:       if (passc)
 1065: 	{
 1066: 	  passc = 0;
 1067: 	  ADD_CHAR (tchar);
 1068: 	  continue;
 1069: 	}
 1070: 
 1071: #if defined (HANDLE_MULTIBYTE)
 1072:       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
 1073: 	{
 1074: 	  int k, c;
 1075: 
 1076: 	  c = tchar;
 1077: 	  memset (mb, 0, sizeof (mb));
 1078: 	  for (k = 0; k < MB_LEN_MAX; k++)
 1079: 	    {
 1080: 	      mb[k] = (char)c;
 1081: 	      memset (&ps, 0, sizeof (mbstate_t));
 1082: 	      if (_rl_get_char_len (mb, &ps) == -2)
 1083: 		c = string[++i];
 1084: 	      else
 1085: 		break;
 1086: 	    }
 1087: 	  if (strlen (mb) > 1)
 1088: 	    {
 1089: 	      ADD_STRING (mb);
 1090: 	      continue;
 1091: 	    }
 1092: 	}
 1093: #endif /* HANDLE_MULTIBYTE */
 1094: 
 1095:       if (tchar == history_expansion_char)
 1096: 	tchar = -3;
 1097:       else if (tchar == history_comment_char)
 1098: 	tchar = -2;
 1099: 
 1100:       switch (tchar)
 1101: 	{
 1102: 	default:
 1103: 	  ADD_CHAR (string[i]);
 1104: 	  break;
 1105: 
 1106: 	case '\\':
 1107: 	  passc++;
 1108: 	  ADD_CHAR (tchar);
 1109: 	  break;
 1110: 
 1111: 	case '"':
 1112: 	  dquote = 1 - dquote;
 1113: 	  ADD_CHAR (tchar);
 1114: 	  break;
 1115: 	  
 1116: 	case '\'':
 1117: 	  {
 1118: 	    /* If history_quotes_inhibit_expansion is set, single quotes
 1119: 	       inhibit history expansion, otherwise they are treated like
 1120: 	       double quotes. */
 1121: 	    if (squote)
 1122: 	      {
 1123: 	        squote = 0;
 1124: 	        ADD_CHAR (tchar);
 1125: 	      }
 1126: 	    else if (dquote == 0 && history_quotes_inhibit_expansion)
 1127: 	      {
 1128: 		int quote, slen;
 1129: 
 1130: 		flag = (i > 0 && string[i - 1] == '$');
 1131: 		quote = i++;
 1132: 		hist_string_extract_single_quoted (string, &i, flag);
 1133: 
 1134: 		slen = i - quote + 2;
 1135: 		temp = (char *)xmalloc (slen);
 1136: 		strncpy (temp, string + quote, slen);
 1137: 		temp[slen - 1] = '\0';
 1138: 		ADD_STRING (temp);
 1139: 		xfree (temp);
 1140: 	      }
 1141: 	    else if (dquote == 0 && squote == 0 && history_quotes_inhibit_expansion == 0)
 1142: 	      {
 1143: 	        squote = 1;
 1144: 	        ADD_CHAR (string[i]);
 1145: 	      }
 1146: 	    else
 1147: 	      ADD_CHAR (string[i]);
 1148: 	    break;
 1149: 	  }
 1150: 
 1151: 	case -2:		/* history_comment_char */
 1152: 	  if (i == 0 || member (string[i - 1], history_word_delimiters))
 1153: 	    {
 1154: 	      temp = (char *)xmalloc (l - i + 1);
 1155: 	      strcpy (temp, string + i);
 1156: 	      ADD_STRING (temp);
 1157: 	      xfree (temp);
 1158: 	      i = l;
 1159: 	    }
 1160: 	  else
 1161: 	    ADD_CHAR (string[i]);
 1162: 	  break;
 1163: 
 1164: 	case -3:		/* history_expansion_char */
 1165: 	  cc = string[i + 1];
 1166: 
 1167: 	  /* If the history_expansion_char is followed by one of the
 1168: 	     characters in history_no_expand_chars, then it is not a
 1169: 	     candidate for expansion of any kind. */
 1170: 	  if (cc == 0 || member (cc, history_no_expand_chars) ||
 1171: 			 (dquote && cc == '"') ||
 1172: 	  		 (history_inhibit_expansion_function && (*history_inhibit_expansion_function) (string, i)))
 1173: 	    {
 1174: 	      ADD_CHAR (string[i]);
 1175: 	      break;
 1176: 	    }
 1177: 
 1178: #if defined (NO_BANG_HASH_MODIFIERS)
 1179: 	  /* There is something that is listed as a `word specifier' in csh
 1180: 	     documentation which means `the expanded text to this point'.
 1181: 	     That is not a word specifier, it is an event specifier.  If we
 1182: 	     don't want to allow modifiers with `!#', just stick the current
 1183: 	     output line in again. */
 1184: 	  if (cc == '#')
 1185: 	    {
 1186: 	      if (result)
 1187: 		{
 1188: 		  temp = (char *)xmalloc (1 + strlen (result));
 1189: 		  strcpy (temp, result);
 1190: 		  ADD_STRING (temp);
 1191: 		  xfree (temp);
 1192: 		}
 1193: 	      i++;
 1194: 	      break;
 1195: 	    }
 1196: #endif
 1197: 	  qc = squote ? '\'' : (dquote ? '"' : 0);
 1198: 	  r = history_expand_internal (string, i, qc, &eindex, &temp, result);
 1199: 	  if (r < 0)
 1200: 	    {
 1201: 	      *output = temp;
 1202: 	      xfree (result);
 1203: 	      if (string != hstring)
 1204: 		xfree (string);
 1205: 	      return -1;
 1206: 	    }
 1207: 	  else
 1208: 	    {
 1209: 	      if (temp)
 1210: 		{
 1211: 		  modified++;
 1212: 		  if (*temp)
 1213: 		    ADD_STRING (temp);
 1214: 		  xfree (temp);
 1215: 		}
 1216: 	      only_printing = r == 1;
 1217: 	      i = eindex;
 1218: 	    }
 1219: 	  break;
 1220: 	}
 1221:     }
 1222: 
 1223:   *output = result;
 1224:   if (string != hstring)
 1225:     xfree (string);
 1226: 
 1227:   if (only_printing)
 1228:     {
 1229: #if 0
 1230:       add_history (result);
 1231: #endif
 1232:       return (2);
 1233:     }
 1234: 
 1235:   return (modified != 0);
 1236: }
 1237: 
 1238: /* Return a consed string which is the word specified in SPEC, and found
 1239:    in FROM.  NULL is returned if there is no spec.  The address of
 1240:    ERROR_POINTER is returned if the word specified cannot be found.
 1241:    CALLER_INDEX is the offset in SPEC to start looking; it is updated
 1242:    to point to just after the last character parsed. */
 1243: static char *
 1244: get_history_word_specifier (spec, from, caller_index)
 1245:      char *spec, *from;
 1246:      int *caller_index;
 1247: {
 1248:   register int i = *caller_index;
 1249:   int first, last;
 1250:   int expecting_word_spec = 0;
 1251:   char *result;
 1252: 
 1253:   /* The range of words to return doesn't exist yet. */
 1254:   first = last = 0;
 1255:   result = (char *)NULL;
 1256: 
 1257:   /* If we found a colon, then this *must* be a word specification.  If
 1258:      it isn't, then it is an error. */
 1259:   if (spec[i] == ':')
 1260:     {
 1261:       i++;
 1262:       expecting_word_spec++;
 1263:     }
 1264: 
 1265:   /* Handle special cases first. */
 1266: 
 1267:   /* `%' is the word last searched for. */
 1268:   if (spec[i] == '%')
 1269:     {
 1270:       *caller_index = i + 1;
 1271:       return (search_match ? savestring (search_match) : savestring (""));
 1272:     }
 1273: 
 1274:   /* `*' matches all of the arguments, but not the command. */
 1275:   if (spec[i] == '*')
 1276:     {
 1277:       *caller_index = i + 1;
 1278:       result = history_arg_extract (1, '$', from);
 1279:       return (result ? result : savestring (""));
 1280:     }
 1281: 
 1282:   /* `$' is last arg. */
 1283:   if (spec[i] == '$')
 1284:     {
 1285:       *caller_index = i + 1;
 1286:       return (history_arg_extract ('$', '$', from));
 1287:     }
 1288: 
 1289:   /* Try to get FIRST and LAST figured out. */
 1290: 
 1291:   if (spec[i] == '-')
 1292:     first = 0;
 1293:   else if (spec[i] == '^')
 1294:     {
 1295:       first = 1;
 1296:       i++;
 1297:     }
 1298:   else if (_rl_digit_p (spec[i]) && expecting_word_spec)
 1299:     {
 1300:       for (first = 0; _rl_digit_p (spec[i]); i++)
 1301: 	first = (first * 10) + _rl_digit_value (spec[i]);
 1302:     }
 1303:   else
 1304:     return ((char *)NULL);	/* no valid `first' for word specifier */
 1305: 
 1306:   if (spec[i] == '^' || spec[i] == '*')
 1307:     {
 1308:       last = (spec[i] == '^') ? 1 : '$';	/* x* abbreviates x-$ */
 1309:       i++;
 1310:     }
 1311:   else if (spec[i] != '-')
 1312:     last = first;
 1313:   else
 1314:     {
 1315:       i++;
 1316: 
 1317:       if (_rl_digit_p (spec[i]))
 1318: 	{
 1319: 	  for (last = 0; _rl_digit_p (spec[i]); i++)
 1320: 	    last = (last * 10) + _rl_digit_value (spec[i]);
 1321: 	}
 1322:       else if (spec[i] == '$')
 1323: 	{
 1324: 	  i++;
 1325: 	  last = '$';
 1326: 	}
 1327: #if 0
 1328:       else if (!spec[i] || spec[i] == ':')
 1329: 	/* check against `:' because there could be a modifier separator */
 1330: #else
 1331:       else
 1332: 	/* csh seems to allow anything to terminate the word spec here,
 1333: 	   leaving it as an abbreviation. */
 1334: #endif
 1335: 	last = -1;		/* x- abbreviates x-$ omitting word `$' */
 1336:     }
 1337: 
 1338:   *caller_index = i;
 1339: 
 1340:   if (last >= first || last == '$' || last < 0)
 1341:     result = history_arg_extract (first, last, from);
 1342: 
 1343:   return (result ? result : (char *)&error_pointer);
 1344: }
 1345: 
 1346: /* Extract the args specified, starting at FIRST, and ending at LAST.
 1347:    The args are taken from STRING.  If either FIRST or LAST is < 0,
 1348:    then make that arg count from the right (subtract from the number of
 1349:    tokens, so that FIRST = -1 means the next to last token on the line).
 1350:    If LAST is `$' the last arg from STRING is used. */
 1351: char *
 1352: history_arg_extract (first, last, string)
 1353:      int first, last;
 1354:      const char *string;
 1355: {
 1356:   register int i, len;
 1357:   char *result;
 1358:   int size, offset;
 1359:   char **list;
 1360: 
 1361:   /* XXX - think about making history_tokenize return a struct array,
 1362:      each struct in array being a string and a length to avoid the
 1363:      calls to strlen below. */
 1364:   if ((list = history_tokenize (string)) == NULL)
 1365:     return ((char *)NULL);
 1366: 
 1367:   for (len = 0; list[len]; len++)
 1368:     ;
 1369: 
 1370:   if (last < 0)
 1371:     last = len + last - 1;
 1372: 
 1373:   if (first < 0)
 1374:     first = len + first - 1;
 1375: 
 1376:   if (last == '$')
 1377:     last = len - 1;
 1378: 
 1379:   if (first == '$')
 1380:     first = len - 1;
 1381: 
 1382:   last++;
 1383: 
 1384:   if (first >= len || last > len || first < 0 || last < 0 || first > last)
 1385:     result = ((char *)NULL);
 1386:   else
 1387:     {
 1388:       for (size = 0, i = first; i < last; i++)
 1389: 	size += strlen (list[i]) + 1;
 1390:       result = (char *)xmalloc (size + 1);
 1391:       result[0] = '\0';
 1392: 
 1393:       for (i = first, offset = 0; i < last; i++)
 1394: 	{
 1395: 	  strcpy (result + offset, list[i]);
 1396: 	  offset += strlen (list[i]);
 1397: 	  if (i + 1 < last)
 1398: 	    {
 1399:       	      result[offset++] = ' ';
 1400: 	      result[offset] = 0;
 1401: 	    }
 1402: 	}
 1403:     }
 1404: 
 1405:   for (i = 0; i < len; i++)
 1406:     xfree (list[i]);
 1407:   xfree (list);
 1408: 
 1409:   return (result);
 1410: }
 1411: 
 1412: static int
 1413: history_tokenize_word (string, ind)
 1414:      const char *string;
 1415:      int ind;
 1416: {
 1417:   register int i;
 1418:   int delimiter, nestdelim, delimopen;
 1419: 
 1420:   i = ind;
 1421:   delimiter = nestdelim = 0;
 1422: 
 1423:   if (member (string[i], "()\n"))
 1424:     {
 1425:       i++;
 1426:       return i;
 1427:     }
 1428: 
 1429:   if (member (string[i], "<>;&|$"))
 1430:     {
 1431:       int peek = string[i + 1];
 1432: 
 1433:       if (peek == string[i] && peek != '$')
 1434: 	{
 1435: 	  if (peek == '<' && string[i + 2] == '-')
 1436: 	    i++;
 1437: 	  else if (peek == '<' && string[i + 2] == '<')
 1438: 	    i++;
 1439: 	  i += 2;
 1440: 	  return i;
 1441: 	}
 1442:       else if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
 1443: 		(peek == '>' && string[i] == '&'))
 1444: 	{
 1445: 	  i += 2;
 1446: 	  return i;
 1447: 	}
 1448:       /* XXX - separated out for later -- bash-4.2 */
 1449:       else if ((peek == '(' && (string[i] == '>' || string[i] == '<')) || /* ) */
 1450: 	       (peek == '(' && string[i] == '$')) /*)*/
 1451: 	{
 1452: 	  i += 2;
 1453: 	  delimopen = '(';
 1454: 	  delimiter = ')';
 1455: 	  nestdelim = 1;
 1456: 	  goto get_word;
 1457: 	}
 1458: #if 0
 1459:       else if (peek == '\'' && string[i] == '$')
 1460:         {
 1461: 	  i += 2;	/* XXX */
 1462: 	  return i;
 1463:         }
 1464: #endif
 1465: 
 1466:       if (string[i] != '$')
 1467: 	{
 1468: 	  i++;
 1469: 	  return i;
 1470: 	}
 1471:     }
 1472: 
 1473:   /* same code also used for $(...)/<(...)/>(...) above */
 1474:   if (member (string[i], "!@?+*"))
 1475:     {
 1476:       int peek = string[i + 1];
 1477: 
 1478:       if (peek == '(')		/*)*/
 1479: 	{
 1480: 	  /* Shell extended globbing patterns */
 1481: 	  i += 2;
 1482: 	  delimopen = '(';
 1483: 	  delimiter = ')';	/* XXX - not perfect */
 1484: 	  nestdelim = 1;
 1485: 	}
 1486:     }
 1487: 
 1488: get_word:
 1489:   /* Get word from string + i; */
 1490: 
 1491:   if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS))
 1492:     delimiter = string[i++];
 1493: 
 1494:   for (; string[i]; i++)
 1495:     {
 1496:       if (string[i] == '\\' && string[i + 1] == '\n')
 1497: 	{
 1498: 	  i++;
 1499: 	  continue;
 1500: 	}
 1501: 
 1502:       if (string[i] == '\\' && delimiter != '\'' &&
 1503: 	  (delimiter != '"' || member (string[i], slashify_in_quotes)))
 1504: 	{
 1505: 	  i++;
 1506: 	  continue;
 1507: 	}
 1508: 
 1509:       /* delimiter must be set and set to something other than a quote if
 1510: 	 nestdelim is set, so these tests are safe. */
 1511:       if (nestdelim && string[i] == delimopen)
 1512: 	{
 1513: 	  nestdelim++;
 1514: 	  continue;
 1515: 	}
 1516:       if (nestdelim && string[i] == delimiter)
 1517: 	{
 1518: 	  nestdelim--;
 1519: 	  if (nestdelim == 0)
 1520: 	    delimiter = 0;
 1521: 	  continue;
 1522: 	}
 1523:       
 1524:       if (delimiter && string[i] == delimiter)
 1525: 	{
 1526: 	  delimiter = 0;
 1527: 	  continue;
 1528: 	}
 1529: 
 1530:       if (delimiter == 0 && (member (string[i], history_word_delimiters)))
 1531: 	break;
 1532: 
 1533:       if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS))
 1534: 	delimiter = string[i];
 1535:     }
 1536: 
 1537:   return i;
 1538: }
 1539: 
 1540: static char *
 1541: history_substring (string, start, end)
 1542:      const char *string;
 1543:      int start, end;
 1544: {
 1545:   register int len;
 1546:   register char *result;
 1547: 
 1548:   len = end - start;
 1549:   result = (char *)xmalloc (len + 1);
 1550:   strncpy (result, string + start, len);
 1551:   result[len] = '\0';
 1552:   return result;
 1553: }
 1554: 
 1555: /* Parse STRING into tokens and return an array of strings.  If WIND is
 1556:    not -1 and INDP is not null, we also want the word surrounding index
 1557:    WIND.  The position in the returned array of strings is returned in
 1558:    *INDP. */
 1559: static char **
 1560: history_tokenize_internal (string, wind, indp)
 1561:      const char *string;
 1562:      int wind, *indp;
 1563: {
 1564:   char **result;
 1565:   register int i, start, result_index, size;
 1566: 
 1567:   /* If we're searching for a string that's not part of a word (e.g., " "),
 1568:      make sure we set *INDP to a reasonable value. */
 1569:   if (indp && wind != -1)
 1570:     *indp = -1;
 1571: 
 1572:   /* Get a token, and stuff it into RESULT.  The tokens are split
 1573:      exactly where the shell would split them. */
 1574:   for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
 1575:     {
 1576:       /* Skip leading whitespace. */
 1577:       for (; string[i] && whitespace (string[i]); i++)
 1578: 	;
 1579:       if (string[i] == 0 || string[i] == history_comment_char)
 1580: 	return (result);
 1581: 
 1582:       start = i;
 1583: 
 1584:       i = history_tokenize_word (string, start);
 1585: 
 1586:       /* If we have a non-whitespace delimiter character (which would not be
 1587: 	 skipped by the loop above), use it and any adjacent delimiters to
 1588: 	 make a separate field.  Any adjacent white space will be skipped the
 1589: 	 next time through the loop. */
 1590:       if (i == start && history_word_delimiters)
 1591: 	{
 1592: 	  i++;
 1593: 	  while (string[i] && member (string[i], history_word_delimiters))
 1594: 	    i++;
 1595: 	}
 1596: 
 1597:       /* If we are looking for the word in which the character at a
 1598: 	 particular index falls, remember it. */
 1599:       if (indp && wind != -1 && wind >= start && wind < i)
 1600:         *indp = result_index;
 1601: 
 1602:       if (result_index + 2 >= size)
 1603: 	result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
 1604: 
 1605:       result[result_index++] = history_substring (string, start, i);
 1606:       result[result_index] = (char *)NULL;
 1607:     }
 1608: 
 1609:   return (result);
 1610: }
 1611: 
 1612: /* Return an array of tokens, much as the shell might.  The tokens are
 1613:    parsed out of STRING. */
 1614: char **
 1615: history_tokenize (string)
 1616:      const char *string;
 1617: {
 1618:   return (history_tokenize_internal (string, -1, (int *)NULL));
 1619: }
 1620: 
 1621: /* Free members of WORDS from START to an empty string */
 1622: static void
 1623: freewords (words, start)
 1624:      char **words;
 1625:      int start;
 1626: {
 1627:   register int i;
 1628: 
 1629:   for (i = start; words[i]; i++)
 1630:     xfree (words[i]);
 1631: }
 1632: 
 1633: /* Find and return the word which contains the character at index IND
 1634:    in the history line LINE.  Used to save the word matched by the
 1635:    last history !?string? search. */
 1636: static char *
 1637: history_find_word (line, ind)
 1638:      char *line;
 1639:      int ind;
 1640: {
 1641:   char **words, *s;
 1642:   int i, wind;
 1643: 
 1644:   words = history_tokenize_internal (line, ind, &wind);
 1645:   if (wind == -1 || words == 0)
 1646:     {
 1647:       if (words)
 1648: 	freewords (words, 0);
 1649:       FREE (words);
 1650:       return ((char *)NULL);
 1651:     }
 1652:   s = words[wind];
 1653:   for (i = 0; i < wind; i++)
 1654:     xfree (words[i]);
 1655:   freewords (words, wind + 1);
 1656:   xfree (words);
 1657:   return s;
 1658: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>