Annotation of embedaddon/readline/histexpand.c, revision 1.1.1.1

1.1       misho       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>