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>