Annotation of embedaddon/readline/histexpand.c, revision 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>