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

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

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