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