|
|
1.1 ! root 1: /* History.c -- standalone history library */ ! 2: ! 3: /* Copyright (C) 1989, 1991 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 2 of the License, or ! 11: (at your option) any later version. ! 12: ! 13: The Library 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 this program; if not, write to the Free Software ! 20: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ ! 21: ! 22: /* The goal is to make the implementation transparent, so that you ! 23: don't have to know what data types are used, just what functions ! 24: you can call. I think I have done that. */ ! 25: ! 26: /* Remove these declarations when we have a complete libgnu.a. */ ! 27: #ifndef xmalloc ! 28: #if !defined (STATIC_MALLOC) ! 29: extern char *xmalloc (), *xrealloc (); ! 30: #else ! 31: static char *xmalloc (), *xrealloc (); ! 32: #endif ! 33: #endif /* !xmalloc */ ! 34: ! 35: #include "sysdep.h" ! 36: #include <stdio.h> ! 37: #include <errno.h> ! 38: #include <sys/types.h> ! 39: #ifndef NO_SYS_FILE ! 40: #include <sys/file.h> ! 41: #endif ! 42: #include <sys/stat.h> ! 43: #include <fcntl.h> ! 44: ! 45: #include "history.h" ! 46: ! 47: #ifndef savestring ! 48: #define savestring(x) (char *)strcpy ((char *)xmalloc (1 + strlen (x)), (x)) ! 49: #endif ! 50: ! 51: #ifndef whitespace ! 52: #define whitespace(c) (((c) == ' ') || ((c) == '\t')) ! 53: #endif ! 54: ! 55: #ifndef digit ! 56: #define digit(c) ((c) >= '0' && (c) <= '9') ! 57: #endif ! 58: ! 59: #ifndef member ! 60: #define member(c, s) ((c) ? index ((s), (c)) : 0) ! 61: #endif ! 62: ! 63: /* **************************************************************** */ ! 64: /* */ ! 65: /* History Functions */ ! 66: /* */ ! 67: /* **************************************************************** */ ! 68: ! 69: /* An array of HIST_ENTRY. This is where we store the history. */ ! 70: static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL; ! 71: ! 72: /* Non-zero means that we have enforced a limit on the amount of ! 73: history that we save. */ ! 74: int history_stifled = 0; ! 75: ! 76: /* If HISTORY_STIFLED is non-zero, then this is the maximum number of ! 77: entries to remember. */ ! 78: int max_input_history; ! 79: ! 80: /* The current location of the interactive history pointer. Just makes ! 81: life easier for outside callers. */ ! 82: static int history_offset = 0; ! 83: ! 84: /* The number of strings currently stored in the input_history list. */ ! 85: int history_length = 0; ! 86: ! 87: /* The current number of slots allocated to the input_history. */ ! 88: static int history_size = 0; ! 89: ! 90: /* The number of slots to increase the_history by. */ ! 91: #define DEFAULT_HISTORY_GROW_SIZE 50 ! 92: ! 93: /* The character that represents the start of a history expansion ! 94: request. This is usually `!'. */ ! 95: char history_expansion_char = '!'; ! 96: ! 97: /* The character that invokes word substitution if found at the start of ! 98: a line. This is usually `^'. */ ! 99: char history_subst_char = '^'; ! 100: ! 101: /* During tokenization, if this character is seen as the first character ! 102: of a word, then it, and all subsequent characters upto a newline are ! 103: ignored. For a Bourne shell, this should be '#'. Bash special cases ! 104: the interactive comment character to not be a comment delimiter. */ ! 105: char history_comment_char = '\0'; ! 106: ! 107: /* The list of characters which inhibit the expansion of text if found ! 108: immediately following history_expansion_char. */ ! 109: char *history_no_expand_chars = " \t\n\r="; ! 110: ! 111: /* The logical `base' of the history array. It defaults to 1. */ ! 112: int history_base = 1; ! 113: ! 114: /* Begin a session in which the history functions might be used. This ! 115: initializes interactive variables. */ ! 116: void ! 117: using_history () ! 118: { ! 119: history_offset = history_length; ! 120: } ! 121: ! 122: /* Return the number of bytes that the primary history entries are using. ! 123: This just adds up the lengths of the_history->lines. */ ! 124: int ! 125: history_total_bytes () ! 126: { ! 127: register int i, result; ! 128: ! 129: result = 0; ! 130: ! 131: for (i = 0; the_history && the_history[i]; i++) ! 132: result += strlen (the_history[i]->line); ! 133: ! 134: return (result); ! 135: } ! 136: ! 137: /* Place STRING at the end of the history list. The data field ! 138: is set to NULL. */ ! 139: void ! 140: add_history (string) ! 141: char *string; ! 142: { ! 143: HIST_ENTRY *temp; ! 144: ! 145: if (history_stifled && (history_length == max_input_history)) ! 146: { ! 147: register int i; ! 148: ! 149: /* If the history is stifled, and history_length is zero, ! 150: and it equals max_input_history, we don't save items. */ ! 151: if (!history_length) ! 152: return; ! 153: ! 154: /* If there is something in the slot, then remove it. */ ! 155: if (the_history[0]) ! 156: { ! 157: free (the_history[0]->line); ! 158: free (the_history[0]); ! 159: } ! 160: ! 161: for (i = 0; i < history_length; i++) ! 162: the_history[i] = the_history[i + 1]; ! 163: ! 164: history_base++; ! 165: ! 166: } ! 167: else ! 168: { ! 169: if (!history_size) ! 170: { ! 171: the_history = (HIST_ENTRY **) ! 172: xmalloc ((history_size = DEFAULT_HISTORY_GROW_SIZE) ! 173: * sizeof (HIST_ENTRY *)); ! 174: history_length = 1; ! 175: ! 176: } ! 177: else ! 178: { ! 179: if (history_length == (history_size - 1)) ! 180: { ! 181: the_history = (HIST_ENTRY **) ! 182: xrealloc (the_history, ! 183: ((history_size += DEFAULT_HISTORY_GROW_SIZE) ! 184: * sizeof (HIST_ENTRY *))); ! 185: } ! 186: history_length++; ! 187: } ! 188: } ! 189: ! 190: temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); ! 191: temp->line = savestring (string); ! 192: temp->data = (char *)NULL; ! 193: ! 194: the_history[history_length] = (HIST_ENTRY *)NULL; ! 195: the_history[history_length - 1] = temp; ! 196: } ! 197: ! 198: /* Make the history entry at WHICH have LINE and DATA. This returns ! 199: the old entry so you can dispose of the data. In the case of an ! 200: invalid WHICH, a NULL pointer is returned. */ ! 201: HIST_ENTRY * ! 202: replace_history_entry (which, line, data) ! 203: int which; ! 204: char *line; ! 205: char *data; ! 206: { ! 207: HIST_ENTRY *temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); ! 208: HIST_ENTRY *old_value; ! 209: ! 210: if (which >= history_length) ! 211: return ((HIST_ENTRY *)NULL); ! 212: ! 213: old_value = the_history[which]; ! 214: ! 215: temp->line = savestring (line); ! 216: temp->data = data; ! 217: the_history[which] = temp; ! 218: ! 219: return (old_value); ! 220: } ! 221: ! 222: /* Returns the magic number which says what history element we are ! 223: looking at now. In this implementation, it returns history_offset. */ ! 224: int ! 225: where_history () ! 226: { ! 227: return (history_offset); ! 228: } ! 229: ! 230: /* Search the history for STRING, starting at history_offset. ! 231: If DIRECTION < 0, then the search is through previous entries, else ! 232: through subsequent. If ANCHORED is non-zero, the string must ! 233: appear at the beginning of a history line, otherwise, the string ! 234: may appear anywhere in the line. If the string is found, then ! 235: current_history () is the history entry, and the value of this ! 236: function is the offset in the line of that history entry that the ! 237: string was found in. Otherwise, nothing is changed, and a -1 is ! 238: returned. */ ! 239: ! 240: #define ANCHORED_SEARCH 1 ! 241: #define NON_ANCHORED_SEARCH 0 ! 242: ! 243: static int ! 244: history_search_internal (string, direction, anchored) ! 245: char *string; ! 246: int direction, anchored; ! 247: { ! 248: register int i = history_offset; ! 249: register int reverse = (direction < 0); ! 250: register char *line; ! 251: register int index; ! 252: int string_len = strlen (string); ! 253: ! 254: /* Take care of trivial cases first. */ ! 255: ! 256: if (!history_length || ((i == history_length) && !reverse)) ! 257: return (-1); ! 258: ! 259: if (reverse && (i == history_length)) ! 260: i--; ! 261: ! 262: while (1) ! 263: { ! 264: /* Search each line in the history list for STRING. */ ! 265: ! 266: /* At limit for direction? */ ! 267: if ((reverse && i < 0) || ! 268: (!reverse && i == history_length)) ! 269: return (-1); ! 270: ! 271: line = the_history[i]->line; ! 272: index = strlen (line); ! 273: ! 274: /* If STRING is longer than line, no match. */ ! 275: if (string_len > index) ! 276: goto next_line; ! 277: ! 278: /* Handle anchored searches first. */ ! 279: if (anchored == ANCHORED_SEARCH) ! 280: { ! 281: if (strncmp (string, line, string_len) == 0) ! 282: { ! 283: history_offset = i; ! 284: return (0); ! 285: } ! 286: ! 287: goto next_line; ! 288: } ! 289: ! 290: /* Do substring search. */ ! 291: if (reverse) ! 292: { ! 293: index -= string_len; ! 294: ! 295: while (index >= 0) ! 296: { ! 297: if (strncmp (string, line + index, string_len) == 0) ! 298: { ! 299: history_offset = i; ! 300: return (index); ! 301: } ! 302: index--; ! 303: } ! 304: } ! 305: else ! 306: { ! 307: register int limit = index - string_len + 1; ! 308: index = 0; ! 309: ! 310: while (index < limit) ! 311: { ! 312: if (strncmp (string, line + index, string_len) == 0) ! 313: { ! 314: history_offset = i; ! 315: return (index); ! 316: } ! 317: index++; ! 318: } ! 319: } ! 320: next_line: ! 321: if (reverse) ! 322: i--; ! 323: else ! 324: i++; ! 325: } ! 326: } ! 327: ! 328: /* Do a non-anchored search for STRING through the history in DIRECTION. */ ! 329: int ! 330: history_search (string, direction) ! 331: char *string; ! 332: int direction; ! 333: { ! 334: return (history_search_internal (string, direction, NON_ANCHORED_SEARCH)); ! 335: } ! 336: ! 337: /* Do an anchored search for string through the history in DIRECTION. */ ! 338: int ! 339: history_search_prefix (string, direction) ! 340: char *string; ! 341: int direction; ! 342: { ! 343: return (history_search_internal (string, direction, ANCHORED_SEARCH)); ! 344: } ! 345: ! 346: /* Remove history element WHICH from the history. The removed ! 347: element is returned to you so you can free the line, data, ! 348: and containing structure. */ ! 349: HIST_ENTRY * ! 350: remove_history (which) ! 351: int which; ! 352: { ! 353: HIST_ENTRY *return_value; ! 354: ! 355: if (which >= history_length || !history_length) ! 356: return_value = (HIST_ENTRY *)NULL; ! 357: else ! 358: { ! 359: register int i; ! 360: return_value = the_history[which]; ! 361: ! 362: for (i = which; i < history_length; i++) ! 363: the_history[i] = the_history[i + 1]; ! 364: ! 365: history_length--; ! 366: } ! 367: ! 368: return (return_value); ! 369: } ! 370: ! 371: /* Stifle the history list, remembering only MAX number of lines. */ ! 372: void ! 373: stifle_history (max) ! 374: int max; ! 375: { ! 376: if (max < 0) ! 377: max = 0; ! 378: if (history_length > max) ! 379: { ! 380: register int i, j; ! 381: ! 382: /* This loses because we cannot free the data. */ ! 383: for (i = 0; i < (history_length - max); i++) ! 384: { ! 385: free (the_history[i]->line); ! 386: free (the_history[i]); ! 387: } ! 388: history_base = i; ! 389: for (j = 0, i = history_length - max; j < max; i++, j++) ! 390: the_history[j] = the_history[i]; ! 391: the_history[j] = (HIST_ENTRY *)NULL; ! 392: history_length = j; ! 393: } ! 394: history_stifled = 1; ! 395: max_input_history = max; ! 396: } ! 397: ! 398: /* Stop stifling the history. This returns the previous amount the history ! 399: was stifled by. The value is positive if the history was stifled, negative ! 400: if it wasn't. */ ! 401: int ! 402: unstifle_history () ! 403: { ! 404: int result = max_input_history; ! 405: if (history_stifled) ! 406: { ! 407: result = - result; ! 408: history_stifled = 0; ! 409: } ! 410: return (result); ! 411: } ! 412: ! 413: /* Return the string that should be used in the place of this ! 414: filename. This only matters when you don't specify the ! 415: filename to read_history (), or write_history (). */ ! 416: static char * ! 417: history_filename (filename) ! 418: char *filename; ! 419: { ! 420: char *return_val = filename ? savestring (filename) : (char *)NULL; ! 421: ! 422: if (!return_val) ! 423: { ! 424: char *home = (char *)getenv ("HOME"); ! 425: if (!home) home = "."; ! 426: return_val = (char *)xmalloc (2 + strlen (home) + strlen (".history")); ! 427: sprintf (return_val, "%s/.history", home); ! 428: } ! 429: return (return_val); ! 430: } ! 431: ! 432: /* Add the contents of FILENAME to the history list, a line at a time. ! 433: If FILENAME is NULL, then read from ~/.history. Returns 0 if ! 434: successful, or errno if not. */ ! 435: int ! 436: read_history (filename) ! 437: char *filename; ! 438: { ! 439: return (read_history_range (filename, 0, -1)); ! 440: } ! 441: ! 442: /* Read a range of lines from FILENAME, adding them to the history list. ! 443: Start reading at the FROM'th line and end at the TO'th. If FROM ! 444: is zero, start at the beginning. If TO is less than FROM, read ! 445: until the end of the file. If FILENAME is NULL, then read from ! 446: ~/.history. Returns 0 if successful, or errno if not. */ ! 447: int ! 448: read_history_range (filename, from, to) ! 449: char *filename; ! 450: int from, to; ! 451: { ! 452: register int line_start, line_end; ! 453: char *input, *buffer = (char *)NULL; ! 454: int file, current_line; ! 455: struct stat finfo; ! 456: extern int errno; ! 457: ! 458: input = history_filename (filename); ! 459: file = open (input, O_RDONLY, 0666); ! 460: ! 461: if ((file < 0) || ! 462: (stat (input, &finfo) == -1)) ! 463: goto error_and_exit; ! 464: ! 465: buffer = (char *)xmalloc (finfo.st_size + 1); ! 466: ! 467: if (read (file, buffer, finfo.st_size) != finfo.st_size) ! 468: error_and_exit: ! 469: { ! 470: if (file >= 0) ! 471: close (file); ! 472: ! 473: if (buffer) ! 474: free (buffer); ! 475: ! 476: return (errno); ! 477: } ! 478: ! 479: close (file); ! 480: ! 481: /* Set TO to larger than end of file if negative. */ ! 482: if (to < 0) ! 483: to = finfo.st_size; ! 484: ! 485: /* Start at beginning of file, work to end. */ ! 486: line_start = line_end = current_line = 0; ! 487: ! 488: /* Skip lines until we are at FROM. */ ! 489: while (line_start < finfo.st_size && current_line < from) ! 490: { ! 491: for (line_end = line_start; line_end < finfo.st_size; line_end++) ! 492: if (buffer[line_end] == '\n') ! 493: { ! 494: current_line++; ! 495: line_start = line_end + 1; ! 496: if (current_line == from) ! 497: break; ! 498: } ! 499: } ! 500: ! 501: /* If there are lines left to gobble, then gobble them now. */ ! 502: for (line_end = line_start; line_end < finfo.st_size; line_end++) ! 503: if (buffer[line_end] == '\n') ! 504: { ! 505: buffer[line_end] = '\0'; ! 506: ! 507: if (buffer[line_start]) ! 508: add_history (buffer + line_start); ! 509: ! 510: current_line++; ! 511: ! 512: if (current_line >= to) ! 513: break; ! 514: ! 515: line_start = line_end + 1; ! 516: } ! 517: return (0); ! 518: } ! 519: ! 520: /* Truncate the history file FNAME, leaving only LINES trailing lines. ! 521: If FNAME is NULL, then use ~/.history. */ ! 522: history_truncate_file (fname, lines) ! 523: char *fname; ! 524: register int lines; ! 525: { ! 526: register int i; ! 527: int file; ! 528: char *buffer = (char *)NULL, *filename; ! 529: struct stat finfo; ! 530: ! 531: filename = history_filename (fname); ! 532: if (stat (filename, &finfo) == -1) ! 533: goto truncate_exit; ! 534: ! 535: file = open (filename, O_RDONLY, 0666); ! 536: ! 537: if (file == -1) ! 538: goto truncate_exit; ! 539: ! 540: buffer = (char *)xmalloc (finfo.st_size + 1); ! 541: read (file, buffer, finfo.st_size); ! 542: close (file); ! 543: ! 544: /* Count backwards from the end of buffer until we have passed ! 545: LINES lines. */ ! 546: for (i = finfo.st_size; lines && i; i--) ! 547: { ! 548: if (buffer[i] == '\n') ! 549: lines--; ! 550: } ! 551: ! 552: /* If there are fewer lines in the file than we want to truncate to, ! 553: then we are all done. */ ! 554: if (!i) ! 555: goto truncate_exit; ! 556: ! 557: /* Otherwise, write from the start of this line until the end of the ! 558: buffer. */ ! 559: for (--i; i; i--) ! 560: if (buffer[i] == '\n') ! 561: { ! 562: i++; ! 563: break; ! 564: } ! 565: ! 566: file = open (filename, O_WRONLY | O_TRUNC | O_CREAT, 0666); ! 567: if (file == -1) ! 568: goto truncate_exit; ! 569: ! 570: write (file, buffer + i, finfo.st_size - i); ! 571: close (file); ! 572: ! 573: truncate_exit: ! 574: if (buffer) ! 575: free (buffer); ! 576: ! 577: free (filename); ! 578: } ! 579: ! 580: #define HISTORY_APPEND 0 ! 581: #define HISTORY_OVERWRITE 1 ! 582: ! 583: /* Workhorse function for writing history. Writes NELEMENT entries ! 584: from the history list to FILENAME. OVERWRITE is non-zero if you ! 585: wish to replace FILENAME with the entries. */ ! 586: static int ! 587: history_do_write (filename, nelements, overwrite) ! 588: char *filename; ! 589: int nelements, overwrite; ! 590: { ! 591: extern int errno; ! 592: register int i, j; ! 593: char *output = history_filename (filename); ! 594: int file, mode; ! 595: ! 596: if (overwrite) ! 597: mode = O_WRONLY | O_CREAT | O_TRUNC; ! 598: else ! 599: mode = O_WRONLY | O_APPEND; ! 600: ! 601: if ((file = open (output, mode, 0666)) == -1) ! 602: return (errno); ! 603: ! 604: if (nelements > history_length) ! 605: nelements = history_length; ! 606: ! 607: /* Build a buffer of all the lines to write, and write them in one syscall. ! 608: Suggested by Peter Ho ([email protected]). */ ! 609: { ! 610: register int j = 0; ! 611: int buffer_size = 0; ! 612: char *buffer; ! 613: ! 614: /* Calculate the total number of bytes to write. */ ! 615: for (i = history_length - nelements; i < history_length; i++) ! 616: buffer_size += 1 + strlen (the_history[i]->line); ! 617: ! 618: /* Allocate the buffer, and fill it. */ ! 619: buffer = (char *)xmalloc (buffer_size); ! 620: ! 621: for (i = history_length - nelements; i < history_length; i++) ! 622: { ! 623: strcpy (buffer + j, the_history[i]->line); ! 624: j += strlen (the_history[i]->line); ! 625: buffer[j++] = '\n'; ! 626: } ! 627: ! 628: write (file, buffer, buffer_size); ! 629: free (buffer); ! 630: } ! 631: ! 632: close (file); ! 633: return (0); ! 634: } ! 635: ! 636: /* Append NELEMENT entries to FILENAME. The entries appended are from ! 637: the end of the list minus NELEMENTs up to the end of the list. */ ! 638: int ! 639: append_history (nelements, filename) ! 640: int nelements; ! 641: char *filename; ! 642: { ! 643: return (history_do_write (filename, nelements, HISTORY_APPEND)); ! 644: } ! 645: ! 646: /* Overwrite FILENAME with the current history. If FILENAME is NULL, ! 647: then write the history list to ~/.history. Values returned ! 648: are as in read_history ().*/ ! 649: int ! 650: write_history (filename) ! 651: char *filename; ! 652: { ! 653: return (history_do_write (filename, history_length, HISTORY_OVERWRITE)); ! 654: } ! 655: ! 656: /* Return the history entry at the current position, as determined by ! 657: history_offset. If there is no entry there, return a NULL pointer. */ ! 658: HIST_ENTRY * ! 659: current_history () ! 660: { ! 661: if ((history_offset == history_length) || !the_history) ! 662: return ((HIST_ENTRY *)NULL); ! 663: else ! 664: return (the_history[history_offset]); ! 665: } ! 666: ! 667: /* Back up history_offset to the previous history entry, and return ! 668: a pointer to that entry. If there is no previous entry then return ! 669: a NULL pointer. */ ! 670: HIST_ENTRY * ! 671: previous_history () ! 672: { ! 673: if (!history_offset) ! 674: return ((HIST_ENTRY *)NULL); ! 675: else ! 676: return (the_history[--history_offset]); ! 677: } ! 678: ! 679: /* Move history_offset forward to the next history entry, and return ! 680: a pointer to that entry. If there is no next entry then return a ! 681: NULL pointer. */ ! 682: HIST_ENTRY * ! 683: next_history () ! 684: { ! 685: if (history_offset == history_length) ! 686: return ((HIST_ENTRY *)NULL); ! 687: else ! 688: return (the_history[++history_offset]); ! 689: } ! 690: ! 691: /* Return the current history array. The caller has to be carefull, since this ! 692: is the actual array of data, and could be bashed or made corrupt easily. ! 693: The array is terminated with a NULL pointer. */ ! 694: HIST_ENTRY ** ! 695: history_list () ! 696: { ! 697: return (the_history); ! 698: } ! 699: ! 700: /* Return the history entry which is logically at OFFSET in the history array. ! 701: OFFSET is relative to history_base. */ ! 702: HIST_ENTRY * ! 703: history_get (offset) ! 704: int offset; ! 705: { ! 706: int index = offset - history_base; ! 707: ! 708: if (index >= history_length || ! 709: index < 0 || ! 710: !the_history) ! 711: return ((HIST_ENTRY *)NULL); ! 712: return (the_history[index]); ! 713: } ! 714: ! 715: /* Search for STRING in the history list. DIR is < 0 for searching ! 716: backwards. POS is an absolute index into the history list at ! 717: which point to begin searching. */ ! 718: int ! 719: history_search_pos (string, dir, pos) ! 720: char *string; ! 721: int dir, pos; ! 722: { ! 723: int ret, old = where_history (); ! 724: history_set_pos (pos); ! 725: if (history_search (string, dir) == -1) ! 726: { ! 727: history_set_pos (old); ! 728: return (-1); ! 729: } ! 730: ret = where_history (); ! 731: history_set_pos (old); ! 732: return ret; ! 733: } ! 734: ! 735: /* Make the current history item be the one at POS, an absolute index. ! 736: Returns zero if POS is out of range, else non-zero. */ ! 737: int ! 738: history_set_pos (pos) ! 739: int pos; ! 740: { ! 741: if (pos > history_length || pos < 0 || !the_history) ! 742: return (0); ! 743: history_offset = pos; ! 744: return (1); ! 745: } ! 746: ! 747: ! 748: /* **************************************************************** */ ! 749: /* */ ! 750: /* History Expansion */ ! 751: /* */ ! 752: /* **************************************************************** */ ! 753: ! 754: /* Hairy history expansion on text, not tokens. This is of general ! 755: use, and thus belongs in this library. */ ! 756: ! 757: /* The last string searched for in a !?string? search. */ ! 758: static char *search_string = (char *)NULL; ! 759: ! 760: /* Return the event specified at TEXT + OFFSET modifying OFFSET to ! 761: point to after the event specifier. Just a pointer to the history ! 762: line is returned; NULL is returned in the event of a bad specifier. ! 763: You pass STRING with *INDEX equal to the history_expansion_char that ! 764: begins this specification. ! 765: DELIMITING_QUOTE is a character that is allowed to end the string ! 766: specification for what to search for in addition to the normal ! 767: characters `:', ` ', `\t', `\n', and sometimes `?'. ! 768: So you might call this function like: ! 769: line = get_history_event ("!echo:p", &index, 0); */ ! 770: char * ! 771: get_history_event (string, caller_index, delimiting_quote) ! 772: char *string; ! 773: int *caller_index; ! 774: int delimiting_quote; ! 775: { ! 776: register int i = *caller_index; ! 777: int which, sign = 1; ! 778: HIST_ENTRY *entry; ! 779: ! 780: /* The event can be specified in a number of ways. ! 781: ! 782: !! the previous command ! 783: !n command line N ! 784: !-n current command-line minus N ! 785: !str the most recent command starting with STR ! 786: !?str[?] ! 787: the most recent command containing STR ! 788: ! 789: All values N are determined via HISTORY_BASE. */ ! 790: ! 791: if (string[i] != history_expansion_char) ! 792: return ((char *)NULL); ! 793: ! 794: /* Move on to the specification. */ ! 795: i++; ! 796: ! 797: /* Handle !! case. */ ! 798: if (string[i] == history_expansion_char) ! 799: { ! 800: i++; ! 801: which = history_base + (history_length - 1); ! 802: *caller_index = i; ! 803: goto get_which; ! 804: } ! 805: ! 806: /* Hack case of numeric line specification. */ ! 807: read_which: ! 808: if (string[i] == '-') ! 809: { ! 810: sign = -1; ! 811: i++; ! 812: } ! 813: ! 814: if (digit (string[i])) ! 815: { ! 816: int start = i; ! 817: ! 818: /* Get the extent of the digits. */ ! 819: for (; digit (string[i]); i++); ! 820: ! 821: /* Get the digit value. */ ! 822: sscanf (string + start, "%d", &which); ! 823: ! 824: *caller_index = i; ! 825: ! 826: if (sign < 0) ! 827: which = (history_length + history_base) - which; ! 828: ! 829: get_which: ! 830: if (entry = history_get (which)) ! 831: return (entry->line); ! 832: ! 833: return ((char *)NULL); ! 834: } ! 835: ! 836: /* This must be something to search for. If the spec begins with ! 837: a '?', then the string may be anywhere on the line. Otherwise, ! 838: the string must be found at the start of a line. */ ! 839: { ! 840: int index; ! 841: char *temp; ! 842: int substring_okay = 0; ! 843: ! 844: if (string[i] == '?') ! 845: { ! 846: substring_okay++; ! 847: i++; ! 848: } ! 849: ! 850: for (index = i; string[i]; i++) ! 851: if (whitespace (string[i]) || ! 852: string[i] == '\n' || ! 853: string[i] == ':' || ! 854: (substring_okay && string[i] == '?') || ! 855: string[i] == delimiting_quote) ! 856: break; ! 857: ! 858: temp = (char *)alloca (1 + (i - index)); ! 859: strncpy (temp, &string[index], (i - index)); ! 860: temp[i - index] = '\0'; ! 861: ! 862: if (string[i] == '?') ! 863: i++; ! 864: ! 865: *caller_index = i; ! 866: ! 867: search_again: ! 868: ! 869: index = history_search_internal ! 870: (temp, -1, substring_okay ? NON_ANCHORED_SEARCH : ANCHORED_SEARCH); ! 871: ! 872: if (index < 0) ! 873: search_lost: ! 874: { ! 875: history_offset = history_length; ! 876: return ((char *)NULL); ! 877: } ! 878: ! 879: if (index == 0) ! 880: { ! 881: search_won: ! 882: entry = current_history (); ! 883: history_offset = history_length; ! 884: ! 885: /* If this was a substring search, then remember the string that ! 886: we matched for word substitution. */ ! 887: if (substring_okay) ! 888: { ! 889: if (search_string) ! 890: free (search_string); ! 891: search_string = savestring (temp); ! 892: } ! 893: ! 894: return (entry->line); ! 895: } ! 896: ! 897: if (history_offset) ! 898: history_offset--; ! 899: else ! 900: goto search_lost; ! 901: ! 902: goto search_again; ! 903: } ! 904: } ! 905: ! 906: /* Expand the string STRING, placing the result into OUTPUT, a pointer ! 907: to a string. Returns: ! 908: ! 909: 0) If no expansions took place (or, if the only change in ! 910: the text was the de-slashifying of the history expansion ! 911: character) ! 912: 1) If expansions did take place ! 913: -1) If there was an error in expansion. ! 914: ! 915: If an error ocurred in expansion, then OUTPUT contains a descriptive ! 916: error message. */ ! 917: int ! 918: history_expand (string, output) ! 919: char *string; ! 920: char **output; ! 921: { ! 922: register int j, l = strlen (string); ! 923: int i, word_spec_error = 0; ! 924: int cc, modified = 0; ! 925: char *word_spec, *event; ! 926: int starting_index, only_printing = 0, substitute_globally = 0; ! 927: ! 928: char *get_history_word_specifier (), *rindex (); ! 929: ! 930: /* The output string, and its length. */ ! 931: int len = 0; ! 932: char *result = (char *)NULL; ! 933: ! 934: /* Used in add_string; */ ! 935: char *temp, tt[2], tbl[3]; ! 936: ! 937: /* Prepare the buffer for printing error messages. */ ! 938: result = (char *)xmalloc (len = 255); ! 939: ! 940: result[0] = tt[1] = tbl[2] = '\0'; ! 941: tbl[0] = '\\'; ! 942: tbl[1] = history_expansion_char; ! 943: ! 944: /* Grovel the string. Only backslash can quote the history escape ! 945: character. We also handle arg specifiers. */ ! 946: ! 947: /* Before we grovel forever, see if the history_expansion_char appears ! 948: anywhere within the text. */ ! 949: ! 950: /* The quick substitution character is a history expansion all right. That ! 951: is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact, ! 952: that is the substitution that we do. */ ! 953: if (string[0] == history_subst_char) ! 954: { ! 955: char *format_string = (char *)alloca (10 + strlen (string)); ! 956: ! 957: sprintf (format_string, "%c%c:s%s", ! 958: history_expansion_char, history_expansion_char, ! 959: string); ! 960: string = format_string; ! 961: l += 4; ! 962: goto grovel; ! 963: } ! 964: ! 965: /* If not quick substitution, still maybe have to do expansion. */ ! 966: ! 967: /* `!' followed by one of the characters in history_no_expand_chars ! 968: is NOT an expansion. */ ! 969: for (i = 0; string[i]; i++) ! 970: if (string[i] == history_expansion_char) ! 971: if (!string[i + 1] || member (string[i + 1], history_no_expand_chars)) ! 972: continue; ! 973: else ! 974: goto grovel; ! 975: ! 976: free (result); ! 977: *output = savestring (string); ! 978: return (0); ! 979: ! 980: grovel: ! 981: ! 982: for (i = j = 0; i < l; i++) ! 983: { ! 984: int tchar = string[i]; ! 985: if (tchar == history_expansion_char) ! 986: tchar = -3; ! 987: ! 988: switch (tchar) ! 989: { ! 990: case '\\': ! 991: if (string[i + 1] == history_expansion_char) ! 992: { ! 993: i++; ! 994: temp = tbl; ! 995: goto do_add; ! 996: } ! 997: else ! 998: goto add_char; ! 999: ! 1000: /* case history_expansion_char: */ ! 1001: case -3: ! 1002: starting_index = i + 1; ! 1003: cc = string[i + 1]; ! 1004: ! 1005: /* If the history_expansion_char is followed by one of the ! 1006: characters in history_no_expand_chars, then it is not a ! 1007: candidate for expansion of any kind. */ ! 1008: if (member (cc, history_no_expand_chars)) ! 1009: goto add_char; ! 1010: ! 1011: /* There is something that is listed as a `word specifier' in csh ! 1012: documentation which means `the expanded text to this point'. ! 1013: That is not a word specifier, it is an event specifier. */ ! 1014: ! 1015: if (cc == '#') ! 1016: goto hack_pound_sign; ! 1017: ! 1018: /* If it is followed by something that starts a word specifier, ! 1019: then !! is implied as the event specifier. */ ! 1020: ! 1021: if (member (cc, ":$*%^")) ! 1022: { ! 1023: char fake_s[3]; ! 1024: int fake_i = 0; ! 1025: i++; ! 1026: fake_s[0] = fake_s[1] = history_expansion_char; ! 1027: fake_s[2] = '\0'; ! 1028: event = get_history_event (fake_s, &fake_i, 0); ! 1029: } ! 1030: else ! 1031: { ! 1032: int quoted_search_delimiter = 0; ! 1033: ! 1034: /* If the character before this `!' is a double or single ! 1035: quote, then this expansion takes place inside of the ! 1036: quoted string. If we have to search for some text ("!foo"), ! 1037: allow the delimiter to end the search string. */ ! 1038: if (i && (string[i - 1] == '\'' || string[i - 1] == '"')) ! 1039: quoted_search_delimiter = string[i - 1]; ! 1040: ! 1041: event = get_history_event (string, &i, quoted_search_delimiter); ! 1042: } ! 1043: ! 1044: if (!event) ! 1045: event_not_found: ! 1046: { ! 1047: int l = 1 + (i - starting_index); ! 1048: ! 1049: temp = (char *)alloca (1 + l); ! 1050: strncpy (temp, string + starting_index, l); ! 1051: temp[l - 1] = 0; ! 1052: sprintf (result, "%s: %s.", temp, ! 1053: word_spec_error ? "Bad word specifier" : "Event not found"); ! 1054: error_exit: ! 1055: *output = result; ! 1056: return (-1); ! 1057: } ! 1058: ! 1059: /* If a word specifier is found, then do what that requires. */ ! 1060: starting_index = i; ! 1061: ! 1062: word_spec = get_history_word_specifier (string, event, &i); ! 1063: ! 1064: /* There is no such thing as a `malformed word specifier'. However, ! 1065: it is possible for a specifier that has no match. In that case, ! 1066: we complain. */ ! 1067: if (word_spec == (char *)-1) ! 1068: bad_word_spec: ! 1069: { ! 1070: word_spec_error++; ! 1071: goto event_not_found; ! 1072: } ! 1073: ! 1074: /* If no word specifier, than the thing of interest was the event. */ ! 1075: if (!word_spec) ! 1076: temp = event; ! 1077: else ! 1078: { ! 1079: temp = (char *)alloca (1 + strlen (word_spec)); ! 1080: strcpy (temp, word_spec); ! 1081: free (word_spec); ! 1082: } ! 1083: ! 1084: /* Perhaps there are other modifiers involved. Do what they say. */ ! 1085: ! 1086: hack_specials: ! 1087: ! 1088: if (string[i] == ':') ! 1089: { ! 1090: char *tstr; ! 1091: ! 1092: switch (string[i + 1]) ! 1093: { ! 1094: /* :p means make this the last executed line. So we ! 1095: return an error state after adding this line to the ! 1096: history. */ ! 1097: case 'p': ! 1098: only_printing++; ! 1099: goto next_special; ! 1100: ! 1101: /* :t discards all but the last part of the pathname. */ ! 1102: case 't': ! 1103: tstr = rindex (temp, '/'); ! 1104: if (tstr) ! 1105: temp = ++tstr; ! 1106: goto next_special; ! 1107: ! 1108: /* :h discards the last part of a pathname. */ ! 1109: case 'h': ! 1110: tstr = rindex (temp, '/'); ! 1111: if (tstr) ! 1112: *tstr = '\0'; ! 1113: goto next_special; ! 1114: ! 1115: /* :r discards the suffix. */ ! 1116: case 'r': ! 1117: tstr = rindex (temp, '.'); ! 1118: if (tstr) ! 1119: *tstr = '\0'; ! 1120: goto next_special; ! 1121: ! 1122: /* :e discards everything but the suffix. */ ! 1123: case 'e': ! 1124: tstr = rindex (temp, '.'); ! 1125: if (tstr) ! 1126: temp = tstr; ! 1127: goto next_special; ! 1128: ! 1129: /* :s/this/that substitutes `this' for `that'. */ ! 1130: /* :gs/this/that substitutes `this' for `that' globally. */ ! 1131: case 'g': ! 1132: if (string[i + 2] == 's') ! 1133: { ! 1134: i++; ! 1135: substitute_globally = 1; ! 1136: goto substitute; ! 1137: } ! 1138: else ! 1139: ! 1140: case 's': ! 1141: substitute: ! 1142: { ! 1143: char *this, *that, *new_event; ! 1144: int delimiter = 0; ! 1145: int si, l_this, l_that, l_temp = strlen (temp); ! 1146: ! 1147: if (i + 2 < strlen (string)) ! 1148: delimiter = string[i + 2]; ! 1149: ! 1150: if (!delimiter) ! 1151: break; ! 1152: ! 1153: i += 3; ! 1154: ! 1155: /* Get THIS. */ ! 1156: for (si = i; string[si] && string[si] != delimiter; si++); ! 1157: l_this = (si - i); ! 1158: this = (char *)alloca (1 + l_this); ! 1159: strncpy (this, string + i, l_this); ! 1160: this[l_this] = '\0'; ! 1161: ! 1162: i = si; ! 1163: if (string[si]) ! 1164: i++; ! 1165: ! 1166: /* Get THAT. */ ! 1167: for (si = i; string[si] && string[si] != delimiter; si++); ! 1168: l_that = (si - i); ! 1169: that = (char *)alloca (1 + l_that); ! 1170: strncpy (that, string + i, l_that); ! 1171: that[l_that] = '\0'; ! 1172: ! 1173: i = si; ! 1174: if (string[si]) i++; ! 1175: ! 1176: /* Ignore impossible cases. */ ! 1177: if (l_this > l_temp) ! 1178: goto cant_substitute; ! 1179: ! 1180: /* Find the first occurrence of THIS in TEMP. */ ! 1181: si = 0; ! 1182: for (; (si + l_this) <= l_temp; si++) ! 1183: if (strncmp (temp + si, this, l_this) == 0) ! 1184: { ! 1185: new_event = ! 1186: (char *)alloca (1 + (l_that - l_this) + l_temp); ! 1187: strncpy (new_event, temp, si); ! 1188: strncpy (new_event + si, that, l_that); ! 1189: strncpy (new_event + si + l_that, ! 1190: temp + si + l_this, ! 1191: l_temp - (si + l_this)); ! 1192: new_event[(l_that - l_this) + l_temp] = '\0'; ! 1193: temp = new_event; ! 1194: ! 1195: if (substitute_globally) ! 1196: { ! 1197: si += l_that; ! 1198: l_temp = strlen (temp); ! 1199: substitute_globally++; ! 1200: continue; ! 1201: } ! 1202: ! 1203: goto hack_specials; ! 1204: } ! 1205: ! 1206: cant_substitute: ! 1207: ! 1208: if (substitute_globally > 1) ! 1209: { ! 1210: substitute_globally = 0; ! 1211: goto hack_specials; ! 1212: } ! 1213: ! 1214: goto event_not_found; ! 1215: } ! 1216: ! 1217: /* :# is the line so far. Note that we have to ! 1218: alloca () it since RESULT could be realloc ()'ed ! 1219: below in add_string. */ ! 1220: case '#': ! 1221: hack_pound_sign: ! 1222: if (result) ! 1223: { ! 1224: temp = (char *)alloca (1 + strlen (result)); ! 1225: strcpy (temp, result); ! 1226: } ! 1227: else ! 1228: temp = ""; ! 1229: ! 1230: next_special: ! 1231: i += 2; ! 1232: goto hack_specials; ! 1233: } ! 1234: ! 1235: } ! 1236: /* Believe it or not, we have to back the pointer up by one. */ ! 1237: --i; ! 1238: goto add_string; ! 1239: ! 1240: /* A regular character. Just add it to the output string. */ ! 1241: default: ! 1242: add_char: ! 1243: tt[0] = string[i]; ! 1244: temp = tt; ! 1245: goto do_add; ! 1246: ! 1247: add_string: ! 1248: modified++; ! 1249: ! 1250: do_add: ! 1251: j += strlen (temp); ! 1252: while (j > len) ! 1253: result = (char *)xrealloc (result, (len += 255)); ! 1254: ! 1255: strcpy (result + (j - strlen (temp)), temp); ! 1256: } ! 1257: } ! 1258: ! 1259: *output = result; ! 1260: ! 1261: if (only_printing) ! 1262: { ! 1263: add_history (result); ! 1264: return (-1); ! 1265: } ! 1266: ! 1267: return (modified != 0); ! 1268: } ! 1269: ! 1270: /* Return a consed string which is the word specified in SPEC, and found ! 1271: in FROM. NULL is returned if there is no spec. -1 is returned if ! 1272: the word specified cannot be found. CALLER_INDEX is the offset in ! 1273: SPEC to start looking; it is updated to point to just after the last ! 1274: character parsed. */ ! 1275: char * ! 1276: get_history_word_specifier (spec, from, caller_index) ! 1277: char *spec, *from; ! 1278: int *caller_index; ! 1279: { ! 1280: register int i = *caller_index; ! 1281: int first, last; ! 1282: int expecting_word_spec = 0; ! 1283: char *history_arg_extract (); ! 1284: ! 1285: /* The range of words to return doesn't exist yet. */ ! 1286: first = last = 0; ! 1287: ! 1288: /* If we found a colon, then this *must* be a word specification. If ! 1289: it isn't, then it is an error. */ ! 1290: if (spec[i] == ':') ! 1291: i++, expecting_word_spec++; ! 1292: ! 1293: /* Handle special cases first. */ ! 1294: ! 1295: /* `%' is the word last searched for. */ ! 1296: if (spec[i] == '%') ! 1297: { ! 1298: *caller_index = i + 1; ! 1299: if (search_string) ! 1300: return (savestring (search_string)); ! 1301: else ! 1302: return (savestring ("")); ! 1303: } ! 1304: ! 1305: /* `*' matches all of the arguments, but not the command. */ ! 1306: if (spec[i] == '*') ! 1307: { ! 1308: char *star_result; ! 1309: ! 1310: *caller_index = i + 1; ! 1311: star_result = history_arg_extract (1, '$', from); ! 1312: ! 1313: if (!star_result) ! 1314: star_result = savestring (""); ! 1315: ! 1316: return (star_result); ! 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: if (spec[i] == '-' || spec[i] == '^') ! 1328: { ! 1329: first = 1; ! 1330: goto get_last; ! 1331: } ! 1332: ! 1333: get_first: ! 1334: if (digit (spec[i]) && expecting_word_spec) ! 1335: { ! 1336: sscanf (spec + i, "%d", &first); ! 1337: for (; digit (spec[i]); i++); ! 1338: } ! 1339: else ! 1340: return ((char *)NULL); ! 1341: ! 1342: get_last: ! 1343: if (spec[i] == '^') ! 1344: { ! 1345: i++; ! 1346: last = 1; ! 1347: goto get_args; ! 1348: } ! 1349: ! 1350: if (spec[i] != '-') ! 1351: { ! 1352: last = first; ! 1353: goto get_args; ! 1354: } ! 1355: ! 1356: i++; ! 1357: ! 1358: if (digit (spec[i])) ! 1359: { ! 1360: sscanf (spec + i, "%d", &last); ! 1361: for (; digit (spec[i]); i++); ! 1362: } ! 1363: else ! 1364: if (spec[i] == '$') ! 1365: { ! 1366: i++; ! 1367: last = '$'; ! 1368: } ! 1369: ! 1370: get_args: ! 1371: { ! 1372: char *result = (char *)NULL; ! 1373: ! 1374: *caller_index = i; ! 1375: ! 1376: if (last >= first) ! 1377: result = history_arg_extract (first, last, from); ! 1378: ! 1379: if (result) ! 1380: return (result); ! 1381: else ! 1382: return ((char *)-1); ! 1383: } ! 1384: } ! 1385: ! 1386: /* Extract the args specified, starting at FIRST, and ending at LAST. ! 1387: The args are taken from STRING. If either FIRST or LAST is < 0, ! 1388: then make that arg count from the right (subtract from the number of ! 1389: tokens, so that FIRST = -1 means the next to last token on the line). */ ! 1390: char * ! 1391: history_arg_extract (first, last, string) ! 1392: int first, last; ! 1393: char *string; ! 1394: { ! 1395: register int i, len; ! 1396: char *result = (char *)NULL; ! 1397: int size = 0, offset = 0; ! 1398: ! 1399: char **history_tokenize (), **list; ! 1400: ! 1401: if (!(list = history_tokenize (string))) ! 1402: return ((char *)NULL); ! 1403: ! 1404: for (len = 0; list[len]; len++); ! 1405: ! 1406: if (last < 0) ! 1407: last = len + last - 1; ! 1408: ! 1409: if (first < 0) ! 1410: first = len + first - 1; ! 1411: ! 1412: if (last == '$') ! 1413: last = len - 1; ! 1414: ! 1415: if (first == '$') ! 1416: first = len - 1; ! 1417: ! 1418: last++; ! 1419: ! 1420: if (first > len || last > len || first < 0 || last < 0) ! 1421: result = ((char *)NULL); ! 1422: else ! 1423: { ! 1424: for (i = first; i < last; i++) ! 1425: { ! 1426: int l = strlen (list[i]); ! 1427: ! 1428: if (!result) ! 1429: result = (char *)xmalloc ((size = (2 + l))); ! 1430: else ! 1431: result = (char *)xrealloc (result, (size += (2 + l))); ! 1432: strcpy (result + offset, list[i]); ! 1433: offset += l; ! 1434: if (i + 1 < last) ! 1435: { ! 1436: strcpy (result + offset, " "); ! 1437: offset++; ! 1438: } ! 1439: } ! 1440: } ! 1441: ! 1442: for (i = 0; i < len; i++) ! 1443: free (list[i]); ! 1444: ! 1445: free (list); ! 1446: ! 1447: return (result); ! 1448: } ! 1449: ! 1450: #define slashify_in_quotes "\\`\"$" ! 1451: ! 1452: /* Return an array of tokens, much as the shell might. The tokens are ! 1453: parsed out of STRING. */ ! 1454: char ** ! 1455: history_tokenize (string) ! 1456: char *string; ! 1457: { ! 1458: char **result = (char **)NULL; ! 1459: register int i, start, result_index, size; ! 1460: int len; ! 1461: ! 1462: i = result_index = size = 0; ! 1463: ! 1464: /* Get a token, and stuff it into RESULT. The tokens are split ! 1465: exactly where the shell would split them. */ ! 1466: get_token: ! 1467: ! 1468: /* Skip leading whitespace. */ ! 1469: for (; string[i] && whitespace(string[i]); i++); ! 1470: ! 1471: start = i; ! 1472: ! 1473: if (!string[i] || string[i] == history_comment_char) ! 1474: return (result); ! 1475: ! 1476: if (member (string[i], "()\n")) { ! 1477: i++; ! 1478: goto got_token; ! 1479: } ! 1480: ! 1481: if (member (string[i], "<>;&|")) { ! 1482: int peek = string[i + 1]; ! 1483: ! 1484: if (peek == string[i]) { ! 1485: if (peek == '<') { ! 1486: if (string[1 + 2] == '-') ! 1487: i++; ! 1488: i += 2; ! 1489: goto got_token; ! 1490: } ! 1491: ! 1492: if (member (peek, ">:&|")) { ! 1493: i += 2; ! 1494: goto got_token; ! 1495: } ! 1496: } else { ! 1497: if ((peek == '&' && ! 1498: (string[i] == '>' || string[i] == '<')) || ! 1499: ((peek == '>') && ! 1500: (string[i] == '&'))) { ! 1501: i += 2; ! 1502: goto got_token; ! 1503: } ! 1504: } ! 1505: i++; ! 1506: goto got_token; ! 1507: } ! 1508: ! 1509: /* Get word from string + i; */ ! 1510: { ! 1511: int delimiter = 0; ! 1512: ! 1513: if (member (string[i], "\"'`")) ! 1514: delimiter = string[i++]; ! 1515: ! 1516: for (;string[i]; i++) { ! 1517: ! 1518: if (string[i] == '\\') { ! 1519: ! 1520: if (string[i + 1] == '\n') { ! 1521: i++; ! 1522: continue; ! 1523: } else { ! 1524: if (delimiter != '\'') ! 1525: if ((delimiter != '"') || ! 1526: (member (string[i], slashify_in_quotes))) { ! 1527: i++; ! 1528: continue; ! 1529: } ! 1530: } ! 1531: } ! 1532: ! 1533: if (delimiter && string[i] == delimiter) { ! 1534: delimiter = 0; ! 1535: continue; ! 1536: } ! 1537: ! 1538: if (!delimiter && (member (string[i], " \t\n;&()|<>"))) ! 1539: goto got_token; ! 1540: ! 1541: if (!delimiter && member (string[i], "\"'`")) { ! 1542: delimiter = string[i]; ! 1543: continue; ! 1544: } ! 1545: } ! 1546: got_token: ! 1547: ! 1548: len = i - start; ! 1549: if (result_index + 2 >= size) { ! 1550: if (!size) ! 1551: result = (char **)xmalloc ((size = 10) * (sizeof (char *))); ! 1552: else ! 1553: result = ! 1554: (char **)xrealloc (result, ((size += 10) * (sizeof (char *)))); ! 1555: } ! 1556: result[result_index] = (char *)xmalloc (1 + len); ! 1557: strncpy (result[result_index], string + start, len); ! 1558: result[result_index][len] = '\0'; ! 1559: result_index++; ! 1560: result[result_index] = (char *)NULL; ! 1561: } ! 1562: if (string[i]) ! 1563: goto get_token; ! 1564: ! 1565: return (result); ! 1566: } ! 1567: ! 1568: #if defined (STATIC_MALLOC) ! 1569: ! 1570: /* **************************************************************** */ ! 1571: /* */ ! 1572: /* xmalloc and xrealloc () */ ! 1573: /* */ ! 1574: /* **************************************************************** */ ! 1575: ! 1576: static void memory_error_and_abort (); ! 1577: ! 1578: static char * ! 1579: xmalloc (bytes) ! 1580: int bytes; ! 1581: { ! 1582: char *temp = (char *)malloc (bytes); ! 1583: ! 1584: if (!temp) ! 1585: memory_error_and_abort (); ! 1586: return (temp); ! 1587: } ! 1588: ! 1589: static char * ! 1590: xrealloc (pointer, bytes) ! 1591: char *pointer; ! 1592: int bytes; ! 1593: { ! 1594: char *temp; ! 1595: ! 1596: if (!pointer) ! 1597: temp = (char *)xmalloc (bytes); ! 1598: else ! 1599: temp = (char *)realloc (pointer, bytes); ! 1600: ! 1601: if (!temp) ! 1602: memory_error_and_abort (); ! 1603: ! 1604: return (temp); ! 1605: } ! 1606: ! 1607: static void ! 1608: memory_error_and_abort () ! 1609: { ! 1610: fprintf (stderr, "history: Out of virtual memory!\n"); ! 1611: abort (); ! 1612: } ! 1613: #endif /* STATIC_MALLOC */ ! 1614: ! 1615: ! 1616: /* **************************************************************** */ ! 1617: /* */ ! 1618: /* Test Code */ ! 1619: /* */ ! 1620: /* **************************************************************** */ ! 1621: #ifdef TEST ! 1622: main () ! 1623: { ! 1624: char line[1024], *t; ! 1625: int done = 0; ! 1626: ! 1627: line[0] = 0; ! 1628: ! 1629: while (!done) ! 1630: { ! 1631: fprintf (stdout, "history%% "); ! 1632: t = gets (line); ! 1633: ! 1634: if (!t) ! 1635: strcpy (line, "quit"); ! 1636: ! 1637: if (line[0]) ! 1638: { ! 1639: char *expansion; ! 1640: int result; ! 1641: ! 1642: using_history (); ! 1643: ! 1644: result = history_expand (line, &expansion); ! 1645: strcpy (line, expansion); ! 1646: free (expansion); ! 1647: if (result) ! 1648: fprintf (stderr, "%s\n", line); ! 1649: ! 1650: if (result < 0) ! 1651: continue; ! 1652: ! 1653: add_history (line); ! 1654: } ! 1655: ! 1656: if (strcmp (line, "quit") == 0) done = 1; ! 1657: if (strcmp (line, "save") == 0) write_history (0); ! 1658: if (strcmp (line, "read") == 0) read_history (0); ! 1659: if (strcmp (line, "list") == 0) ! 1660: { ! 1661: register HIST_ENTRY **the_list = history_list (); ! 1662: register int i; ! 1663: ! 1664: if (the_list) ! 1665: for (i = 0; the_list[i]; i++) ! 1666: fprintf (stdout, "%d: %s\n", i + history_base, the_list[i]->line); ! 1667: } ! 1668: if (strncmp (line, "delete", strlen ("delete")) == 0) ! 1669: { ! 1670: int which; ! 1671: if ((sscanf (line + strlen ("delete"), "%d", &which)) == 1) ! 1672: { ! 1673: HIST_ENTRY *entry = remove_history (which); ! 1674: if (!entry) ! 1675: fprintf (stderr, "No such entry %d\n", which); ! 1676: else ! 1677: { ! 1678: free (entry->line); ! 1679: free (entry); ! 1680: } ! 1681: } ! 1682: else ! 1683: { ! 1684: fprintf (stderr, "non-numeric arg given to `delete'\n"); ! 1685: } ! 1686: } ! 1687: } ! 1688: } ! 1689: ! 1690: #endif /* TEST */ ! 1691: ! 1692: /* ! 1693: * Local variables: ! 1694: * compile-command: "gcc -g -DTEST -o history history.c" ! 1695: * end: ! 1696: */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.