1 /* History.c -- standalone history library */
3 /* Copyright (C) 1989 Free Software Foundation, Inc.
5 This file contains the GNU History Library (the Library), a set of
6 routines for managing the text of previously typed lines.
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)
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.
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. */
23 /* The goal is to make the implementation transparent, so that you
24 don't have to know what data types are used, just what functions
25 you can call. I think I have done that. */
27 /* Remove these declarations when we have a complete libgnu.a. */
28 #if !defined (STATIC_MALLOC)
29 extern char *xmalloc (), *xrealloc ();
31 static char *xmalloc (), *xrealloc ();
35 #include <sys/types.h>
40 #if defined (__GNUC__)
41 # define alloca __builtin_alloca
43 # if defined (sparc) || defined (HAVE_ALLOCA_H)
46 extern char *alloca ();
47 # endif /* sparc || HAVE_ALLOCA_H */
48 #endif /* !__GNU_C__ */
53 #define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x))
57 #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
61 #define digit(c) ((c) >= '0' && (c) <= '9')
65 #define member(c, s) ((c) ? index ((s), (c)) : 0)
68 /* **************************************************************** */
70 /* History Functions */
72 /* **************************************************************** */
74 /* An array of HIST_ENTRY. This is where we store the history. */
75 static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL;
77 /* Non-zero means that we have enforced a limit on the amount of
78 history that we save. */
79 int history_stifled = 0;
81 /* If HISTORY_STIFLED is non-zero, then this is the maximum number of
82 entries to remember. */
83 int max_input_history;
85 /* The current location of the interactive history pointer. Just makes
86 life easier for outside callers. */
87 static int history_offset = 0;
89 /* The number of strings currently stored in the input_history list. */
90 int history_length = 0;
92 /* The current number of slots allocated to the input_history. */
93 static int history_size = 0;
95 /* The number of slots to increase the_history by. */
96 #define DEFAULT_HISTORY_GROW_SIZE 50
98 /* The character that represents the start of a history expansion
99 request. This is usually `!'. */
100 char history_expansion_char = '!';
102 /* The character that invokes word substitution if found at the start of
103 a line. This is usually `^'. */
104 char history_subst_char = '^';
106 /* During tokenization, if this character is seen as the first character
107 of a word, then it, and all subsequent characters upto a newline are
108 ignored. For a Bourne shell, this should be '#'. Bash special cases
109 the interactive comment character to not be a comment delimiter. */
110 char history_comment_char = '\0';
112 /* The list of characters which inhibit the expansion of text if found
113 immediately following history_expansion_char. */
114 char *history_no_expand_chars = " \t\n\r=";
116 /* The logical `base' of the history array. It defaults to 1. */
117 int history_base = 1;
119 /* Begin a session in which the history functions might be used. This
120 initializes interactive variables. */
124 history_offset = history_length;
127 /* Return the number of bytes that the primary history entries are using.
128 This just adds up the lengths of the_history->lines. */
130 history_total_bytes ()
132 register int i, result;
136 for (i = 0; the_history && the_history[i]; i++)
137 result += strlen (the_history[i]->line);
142 /* Place STRING at the end of the history list. The data field
150 if (history_stifled && (history_length == max_input_history))
154 /* If the history is stifled, and history_length is zero,
155 and it equals max_input_history, we don't save items. */
159 /* If there is something in the slot, then remove it. */
162 free (the_history[0]->line);
163 free (the_history[0]);
166 for (i = 0; i < history_length; i++)
167 the_history[i] = the_history[i + 1];
176 the_history = (HIST_ENTRY **)
177 xmalloc ((history_size = DEFAULT_HISTORY_GROW_SIZE)
178 * sizeof (HIST_ENTRY *));
184 if (history_length == (history_size - 1))
186 the_history = (HIST_ENTRY **)
187 xrealloc (the_history,
188 ((history_size += DEFAULT_HISTORY_GROW_SIZE)
189 * sizeof (HIST_ENTRY *)));
195 temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
196 temp->line = savestring (string);
197 temp->data = (char *)NULL;
199 the_history[history_length] = (HIST_ENTRY *)NULL;
200 the_history[history_length - 1] = temp;
203 /* Make the history entry at WHICH have LINE and DATA. This returns
204 the old entry so you can dispose of the data. In the case of an
205 invalid WHICH, a NULL pointer is returned. */
207 replace_history_entry (which, line, data)
212 HIST_ENTRY *temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
213 HIST_ENTRY *old_value;
215 if (which >= history_length)
216 return ((HIST_ENTRY *)NULL);
218 old_value = the_history[which];
220 temp->line = savestring (line);
222 the_history[which] = temp;
227 /* Returns the magic number which says what history element we are
228 looking at now. In this implementation, it returns history_offset. */
232 return (history_offset);
235 /* Search the history for STRING, starting at history_offset.
236 If DIRECTION < 0, then the search is through previous entries, else
237 through subsequent. If ANCHORED is non-zero, the string must
238 appear at the beginning of a history line, otherwise, the string
239 may appear anywhere in the line. If the string is found, then
240 current_history () is the history entry, and the value of this
241 function is the offset in the line of that history entry that the
242 string was found in. Otherwise, nothing is changed, and a -1 is
245 #define ANCHORED_SEARCH 1
246 #define NON_ANCHORED_SEARCH 0
249 history_search_internal (string, direction, anchored)
251 int direction, anchored;
253 register int i = history_offset;
254 register int reverse = (direction < 0);
257 int string_len = strlen (string);
259 /* Take care of trivial cases first. */
261 if (!history_length || ((i == history_length) && !reverse))
264 if (reverse && (i == history_length))
269 /* Search each line in the history list for STRING. */
271 /* At limit for direction? */
272 if ((reverse && i < 0) ||
273 (!reverse && i == history_length))
276 line = the_history[i]->line;
277 index = strlen (line);
279 /* If STRING is longer than line, no match. */
280 if (string_len > index)
283 /* Handle anchored searches first. */
284 if (anchored == ANCHORED_SEARCH)
286 if (strncmp (string, line, string_len) == 0)
295 /* Do substring search. */
302 if (strncmp (string, line + index, string_len) == 0)
312 register int limit = index - string_len + 1;
315 while (index < limit)
317 if (strncmp (string, line + index, string_len) == 0)
333 /* Do a non-anchored search for STRING through the history in DIRECTION. */
335 history_search (string, direction)
339 return (history_search_internal (string, direction, NON_ANCHORED_SEARCH));
342 /* Do an anchored search for string through the history in DIRECTION. */
344 history_search_prefix (string, direction)
348 return (history_search_internal (string, direction, ANCHORED_SEARCH));
351 /* Remove history element WHICH from the history. The removed
352 element is returned to you so you can free the line, data,
353 and containing structure. */
355 remove_history (which)
358 HIST_ENTRY *return_value;
360 if (which >= history_length || !history_length)
361 return_value = (HIST_ENTRY *)NULL;
365 return_value = the_history[which];
367 for (i = which; i < history_length; i++)
368 the_history[i] = the_history[i + 1];
373 return (return_value);
376 /* Stifle the history list, remembering only MAX number of lines. */
381 if (history_length > max)
385 /* This loses because we cannot free the data. */
386 for (i = 0; i < (history_length - max); i++)
388 free (the_history[i]->line);
389 free (the_history[i]);
392 for (j = 0, i = history_length - max; j < max; i++, j++)
393 the_history[j] = the_history[i];
394 the_history[j] = (HIST_ENTRY *)NULL;
398 max_input_history = max;
401 /* Stop stifling the history. This returns the previous amount the history
402 was stifled by. The value is positive if the history was stifled, negative
407 int result = max_input_history;
416 /* Return the string that should be used in the place of this
417 filename. This only matters when you don't specify the
418 filename to read_history (), or write_history (). */
420 history_filename (filename)
423 char *return_val = filename ? savestring (filename) : (char *)NULL;
427 char *home = (char *)getenv ("HOME");
428 if (!home) home = ".";
429 return_val = (char *)xmalloc (2 + strlen (home) + strlen (".history"));
430 sprintf (return_val, "%s/.history", home);
435 /* Add the contents of FILENAME to the history list, a line at a time.
436 If FILENAME is NULL, then read from ~/.history. Returns 0 if
437 successful, or errno if not. */
439 read_history (filename)
442 return (read_history_range (filename, 0, -1));
445 /* Read a range of lines from FILENAME, adding them to the history list.
446 Start reading at the FROM'th line and end at the TO'th. If FROM
447 is zero, start at the beginning. If TO is less than FROM, read
448 until the end of the file. If FILENAME is NULL, then read from
449 ~/.history. Returns 0 if successful, or errno if not. */
451 read_history_range (filename, from, to)
455 register int line_start, line_end;
456 char *input, *buffer = (char *)NULL;
457 int file, current_line;
461 input = history_filename (filename);
462 file = open (input, O_RDONLY, 0666);
465 (stat (input, &finfo) == -1))
468 buffer = (char *)xmalloc (finfo.st_size + 1);
470 if (read (file, buffer, finfo.st_size) != finfo.st_size)
484 /* Set TO to larger than end of file if negative. */
488 /* Start at beginning of file, work to end. */
489 line_start = line_end = current_line = 0;
491 /* Skip lines until we are at FROM. */
492 while (line_start < finfo.st_size && current_line < from)
494 for (line_end = line_start; line_end < finfo.st_size; line_end++)
495 if (buffer[line_end] == '\n')
498 line_start = line_end + 1;
499 if (current_line == from)
504 /* If there are lines left to gobble, then gobble them now. */
505 for (line_end = line_start; line_end < finfo.st_size; line_end++)
506 if (buffer[line_end] == '\n')
508 buffer[line_end] = '\0';
510 if (buffer[line_start])
511 add_history (buffer + line_start);
515 if (current_line >= to)
518 line_start = line_end + 1;
523 /* Truncate the history file FNAME, leaving only LINES trailing lines.
524 If FNAME is NULL, then use ~/.history. */
525 history_truncate_file (fname, lines)
531 char *buffer = (char *)NULL, *filename;
534 filename = history_filename (fname);
535 if (stat (filename, &finfo) == -1)
538 file = open (filename, O_RDONLY, 0666);
543 buffer = (char *)xmalloc (finfo.st_size + 1);
544 read (file, buffer, finfo.st_size);
547 /* Count backwards from the end of buffer until we have passed
549 for (i = finfo.st_size; lines && i; i--)
551 if (buffer[i] == '\n')
555 /* If there are fewer lines in the file than we want to truncate to,
556 then we are all done. */
560 /* Otherwise, write from the start of this line until the end of the
563 if (buffer[i] == '\n')
569 file = open (filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
573 write (file, buffer + i, finfo.st_size - i);
583 #define HISTORY_APPEND 0
584 #define HISTORY_OVERWRITE 1
586 /* Workhorse function for writing history. Writes NELEMENT entries
587 from the history list to FILENAME. OVERWRITE is non-zero if you
588 wish to replace FILENAME with the entries. */
590 history_do_write (filename, nelements, overwrite)
592 int nelements, overwrite;
596 char *output = history_filename (filename);
601 mode = O_WRONLY | O_CREAT | O_TRUNC;
603 mode = O_WRONLY | O_APPEND;
605 if ((file = open (output, mode, 0666)) == -1)
608 if (nelements > history_length)
609 nelements = history_length;
611 for (i = history_length - nelements; i < history_length; i++)
613 if (write (file, the_history[i]->line, strlen (the_history[i]->line)) < 0)
615 if (write (file, &cr, 1) < 0)
623 /* Append NELEMENT entries to FILENAME. The entries appended are from
624 the end of the list minus NELEMENTs up to the end of the list. */
626 append_history (nelements, filename)
630 return (history_do_write (filename, nelements, HISTORY_APPEND));
633 /* Overwrite FILENAME with the current history. If FILENAME is NULL,
634 then write the history list to ~/.history. Values returned
635 are as in read_history ().*/
637 write_history (filename)
640 return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
643 /* Return the history entry at the current position, as determined by
644 history_offset. If there is no entry there, return a NULL pointer. */
648 if ((history_offset == history_length) || !the_history)
649 return ((HIST_ENTRY *)NULL);
651 return (the_history[history_offset]);
654 /* Back up history_offset to the previous history entry, and return
655 a pointer to that entry. If there is no previous entry then return
661 return ((HIST_ENTRY *)NULL);
663 return (the_history[--history_offset]);
666 /* Move history_offset forward to the next history entry, and return
667 a pointer to that entry. If there is no next entry then return a
672 if (history_offset == history_length)
673 return ((HIST_ENTRY *)NULL);
675 return (the_history[++history_offset]);
678 /* Return the current history array. The caller has to be carefull, since this
679 is the actual array of data, and could be bashed or made corrupt easily.
680 The array is terminated with a NULL pointer. */
684 return (the_history);
687 /* Return the history entry which is logically at OFFSET in the history array.
688 OFFSET is relative to history_base. */
693 int index = offset - history_base;
695 if (index >= history_length ||
698 return ((HIST_ENTRY *)NULL);
699 return (the_history[index]);
702 /* Search for STRING in the history list. DIR is < 0 for searching
703 backwards. POS is an absolute index into the history list at
704 which point to begin searching. */
706 history_search_pos (string, dir, pos)
710 int ret, old = where_history ();
711 history_set_pos (pos);
712 if (history_search (string, dir) == -1)
714 history_set_pos (old);
717 ret = where_history ();
718 history_set_pos (old);
722 /* Make the current history item be the one at POS, an absolute index.
723 Returns zero if POS is out of range, else non-zero. */
725 history_set_pos (pos)
728 if (pos > history_length || pos < 0 || !the_history)
730 history_offset = pos;
735 /* **************************************************************** */
737 /* History Expansion */
739 /* **************************************************************** */
741 /* Hairy history expansion on text, not tokens. This is of general
742 use, and thus belongs in this library. */
744 /* The last string searched for in a !?string? search. */
745 static char *search_string = (char *)NULL;
747 /* Return the event specified at TEXT + OFFSET modifying OFFSET to
748 point to after the event specifier. Just a pointer to the history
749 line is returned; NULL is returned in the event of a bad specifier.
750 You pass STRING with *INDEX equal to the history_expansion_char that
751 begins this specification.
752 DELIMITING_QUOTE is a character that is allowed to end the string
753 specification for what to search for in addition to the normal
754 characters `:', ` ', `\t', `\n', and sometimes `?'.
755 So you might call this function like:
756 line = get_history_event ("!echo:p", &index, 0); */
758 get_history_event (string, caller_index, delimiting_quote)
761 int delimiting_quote;
763 register int i = *caller_index;
767 /* The event can be specified in a number of ways.
769 !! the previous command
771 !-n current command-line minus N
772 !str the most recent command starting with STR
774 the most recent command containing STR
776 All values N are determined via HISTORY_BASE. */
778 if (string[i] != history_expansion_char)
779 return ((char *)NULL);
781 /* Move on to the specification. */
784 /* Handle !! case. */
785 if (string[i] == history_expansion_char)
788 which = history_base + (history_length - 1);
793 /* Hack case of numeric line specification. */
795 if (string[i] == '-')
801 if (digit (string[i]))
805 /* Get the extent of the digits. */
806 for (; digit (string[i]); i++);
808 /* Get the digit value. */
809 sscanf (string + start, "%d", &which);
814 which = (history_length + history_base) - which;
817 if (entry = history_get (which))
818 return (entry->line);
820 return ((char *)NULL);
823 /* This must be something to search for. If the spec begins with
824 a '?', then the string may be anywhere on the line. Otherwise,
825 the string must be found at the start of a line. */
829 int substring_okay = 0;
831 if (string[i] == '?')
837 for (index = i; string[i]; i++)
838 if (whitespace (string[i]) ||
841 (substring_okay && string[i] == '?') ||
842 string[i] == delimiting_quote)
845 temp = (char *)alloca (1 + (i - index));
846 strncpy (temp, &string[index], (i - index));
847 temp[i - index] = '\0';
849 if (string[i] == '?')
856 index = history_search_internal
857 (temp, -1, substring_okay ? NON_ANCHORED_SEARCH : ANCHORED_SEARCH);
862 history_offset = history_length;
863 return ((char *)NULL);
869 entry = current_history ();
870 history_offset = history_length;
872 /* If this was a substring search, then remember the string that
873 we matched for word substitution. */
877 free (search_string);
878 search_string = savestring (temp);
881 return (entry->line);
893 /* Expand the string STRING, placing the result into OUTPUT, a pointer
894 to a string. Returns:
896 0) If no expansions took place (or, if the only change in
897 the text was the de-slashifying of the history expansion
899 1) If expansions did take place
900 -1) If there was an error in expansion.
902 If an error ocurred in expansion, then OUTPUT contains a descriptive
905 history_expand (string, output)
909 register int j, l = strlen (string);
910 int i, word_spec_error = 0;
911 int cc, modified = 0;
912 char *word_spec, *event;
913 int starting_index, only_printing = 0, substitute_globally = 0;
915 char *get_history_word_specifier (), *rindex ();
917 /* The output string, and its length. */
919 char *result = (char *)NULL;
921 /* Used in add_string; */
922 char *temp, tt[2], tbl[3];
924 /* Prepare the buffer for printing error messages. */
925 result = (char *)xmalloc (len = 255);
927 result[0] = tt[1] = tbl[2] = '\0';
929 tbl[1] = history_expansion_char;
931 /* Grovel the string. Only backslash can quote the history escape
932 character. We also handle arg specifiers. */
934 /* Before we grovel forever, see if the history_expansion_char appears
935 anywhere within the text. */
937 /* The quick substitution character is a history expansion all right. That
938 is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
939 that is the substitution that we do. */
940 if (string[0] == history_subst_char)
942 char *format_string = (char *)alloca (10 + strlen (string));
944 sprintf (format_string, "%c%c:s%s",
945 history_expansion_char, history_expansion_char,
947 string = format_string;
952 /* If not quick substitution, still maybe have to do expansion. */
954 /* `!' followed by one of the characters in history_no_expand_chars
955 is NOT an expansion. */
956 for (i = 0; string[i]; i++)
957 if (string[i] == history_expansion_char)
958 if (!string[i + 1] || member (string[i + 1], history_no_expand_chars))
964 *output = savestring (string);
969 for (i = j = 0; i < l; i++)
971 int tchar = string[i];
972 if (tchar == history_expansion_char)
978 if (string[i + 1] == history_expansion_char)
987 /* case history_expansion_char: */
989 starting_index = i + 1;
992 /* If the history_expansion_char is followed by one of the
993 characters in history_no_expand_chars, then it is not a
994 candidate for expansion of any kind. */
995 if (member (cc, history_no_expand_chars))
998 /* There is something that is listed as a `word specifier' in csh
999 documentation which means `the expanded text to this point'.
1000 That is not a word specifier, it is an event specifier. */
1003 goto hack_pound_sign;
1005 /* If it is followed by something that starts a word specifier,
1006 then !! is implied as the event specifier. */
1008 if (member (cc, ":$*%^"))
1013 fake_s[0] = fake_s[1] = history_expansion_char;
1015 event = get_history_event (fake_s, &fake_i, 0);
1019 int quoted_search_delimiter = 0;
1021 /* If the character before this `!' is a double or single
1022 quote, then this expansion takes place inside of the
1023 quoted string. If we have to search for some text ("!foo"),
1024 allow the delimiter to end the search string. */
1025 if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
1026 quoted_search_delimiter = string[i - 1];
1028 event = get_history_event (string, &i, quoted_search_delimiter);
1034 int l = 1 + (i - starting_index);
1036 temp = (char *)alloca (1 + l);
1037 strncpy (temp, string + starting_index, l);
1039 sprintf (result, "%s: %s.", temp,
1040 word_spec_error ? "Bad word specifier" : "Event not found");
1046 /* If a word specifier is found, then do what that requires. */
1049 word_spec = get_history_word_specifier (string, event, &i);
1051 /* There is no such thing as a `malformed word specifier'. However,
1052 it is possible for a specifier that has no match. In that case,
1054 if (word_spec == (char *)-1)
1058 goto event_not_found;
1061 /* If no word specifier, than the thing of interest was the event. */
1066 temp = (char *)alloca (1 + strlen (word_spec));
1067 strcpy (temp, word_spec);
1071 /* Perhaps there are other modifiers involved. Do what they say. */
1075 if (string[i] == ':')
1079 switch (string[i + 1])
1081 /* :p means make this the last executed line. So we
1082 return an error state after adding this line to the
1088 /* :t discards all but the last part of the pathname. */
1090 tstr = rindex (temp, '/');
1095 /* :h discards the last part of a pathname. */
1097 tstr = rindex (temp, '/');
1102 /* :r discards the suffix. */
1104 tstr = rindex (temp, '.');
1109 /* :e discards everything but the suffix. */
1111 tstr = rindex (temp, '.');
1116 /* :s/this/that substitutes `this' for `that'. */
1117 /* :gs/this/that substitutes `this' for `that' globally. */
1119 if (string[i + 2] == 's')
1122 substitute_globally = 1;
1130 char *this, *that, *new_event;
1132 int si, l_this, l_that, l_temp = strlen (temp);
1134 if (i + 2 < strlen (string))
1135 delimiter = string[i + 2];
1143 for (si = i; string[si] && string[si] != delimiter; si++);
1145 this = (char *)alloca (1 + l_this);
1146 strncpy (this, string + i, l_this);
1147 this[l_this] = '\0';
1154 for (si = i; string[si] && string[si] != delimiter; si++);
1156 that = (char *)alloca (1 + l_that);
1157 strncpy (that, string + i, l_that);
1158 that[l_that] = '\0';
1161 if (string[si]) i++;
1163 /* Ignore impossible cases. */
1164 if (l_this > l_temp)
1165 goto cant_substitute;
1167 /* Find the first occurrence of THIS in TEMP. */
1169 for (; (si + l_this) <= l_temp; si++)
1170 if (strncmp (temp + si, this, l_this) == 0)
1173 (char *)alloca (1 + (l_that - l_this) + l_temp);
1174 strncpy (new_event, temp, si);
1175 strncpy (new_event + si, that, l_that);
1176 strncpy (new_event + si + l_that,
1178 l_temp - (si + l_this));
1179 new_event[(l_that - l_this) + l_temp] = '\0';
1182 if (substitute_globally)
1185 l_temp = strlen (temp);
1186 substitute_globally++;
1195 if (substitute_globally > 1)
1197 substitute_globally = 0;
1201 goto event_not_found;
1204 /* :# is the line so far. Note that we have to
1205 alloca () it since RESULT could be realloc ()'ed
1206 below in add_string. */
1211 temp = (char *)alloca (1 + strlen (result));
1212 strcpy (temp, result);
1223 /* Believe it or not, we have to back the pointer up by one. */
1227 /* A regular character. Just add it to the output string. */
1240 result = (char *)xrealloc (result, (len += 255));
1242 strcpy (result + (j - strlen (temp)), temp);
1250 add_history (result);
1254 return (modified != 0);
1257 /* Return a consed string which is the word specified in SPEC, and found
1258 in FROM. NULL is returned if there is no spec. -1 is returned if
1259 the word specified cannot be found. CALLER_INDEX is the offset in
1260 SPEC to start looking; it is updated to point to just after the last
1261 character parsed. */
1263 get_history_word_specifier (spec, from, caller_index)
1267 register int i = *caller_index;
1269 int expecting_word_spec = 0;
1270 char *history_arg_extract ();
1272 /* The range of words to return doesn't exist yet. */
1275 /* If we found a colon, then this *must* be a word specification. If
1276 it isn't, then it is an error. */
1278 i++, expecting_word_spec++;
1280 /* Handle special cases first. */
1282 /* `%' is the word last searched for. */
1285 *caller_index = i + 1;
1287 return (savestring (search_string));
1289 return (savestring (""));
1292 /* `*' matches all of the arguments, but not the command. */
1297 *caller_index = i + 1;
1298 star_result = history_arg_extract (1, '$', from);
1301 star_result = savestring ("");
1303 return (star_result);
1306 /* `$' is last arg. */
1309 *caller_index = i + 1;
1310 return (history_arg_extract ('$', '$', from));
1313 /* Try to get FIRST and LAST figured out. */
1314 if (spec[i] == '-' || spec[i] == '^')
1321 if (digit (spec[i]) && expecting_word_spec)
1323 sscanf (spec + i, "%d", &first);
1324 for (; digit (spec[i]); i++);
1327 return ((char *)NULL);
1345 if (digit (spec[i]))
1347 sscanf (spec + i, "%d", &last);
1348 for (; digit (spec[i]); i++);
1359 char *result = (char *)NULL;
1364 result = history_arg_extract (first, last, from);
1369 return ((char *)-1);
1373 /* Extract the args specified, starting at FIRST, and ending at LAST.
1374 The args are taken from STRING. If either FIRST or LAST is < 0,
1375 then make that arg count from the right (subtract from the number of
1376 tokens, so that FIRST = -1 means the next to last token on the line). */
1378 history_arg_extract (first, last, string)
1382 register int i, len;
1383 char *result = (char *)NULL;
1384 int size = 0, offset = 0;
1386 char **history_tokenize (), **list;
1388 if (!(list = history_tokenize (string)))
1389 return ((char *)NULL);
1391 for (len = 0; list[len]; len++);
1394 last = len + last - 1;
1397 first = len + first - 1;
1407 if (first > len || last > len || first < 0 || last < 0)
1408 result = ((char *)NULL);
1411 for (i = first; i < last; i++)
1413 int l = strlen (list[i]);
1416 result = (char *)xmalloc ((size = (2 + l)));
1418 result = (char *)xrealloc (result, (size += (2 + l)));
1419 strcpy (result + offset, list[i]);
1423 strcpy (result + offset, " ");
1429 for (i = 0; i < len; i++)
1437 #define slashify_in_quotes "\\`\"$"
1439 /* Return an array of tokens, much as the shell might. The tokens are
1440 parsed out of STRING. */
1442 history_tokenize (string)
1445 char **result = (char **)NULL;
1446 register int i, start, result_index, size;
1449 i = result_index = size = 0;
1451 /* Get a token, and stuff it into RESULT. The tokens are split
1452 exactly where the shell would split them. */
1455 /* Skip leading whitespace. */
1456 for (; string[i] && whitespace(string[i]); i++);
1460 if (!string[i] || string[i] == history_comment_char)
1463 if (member (string[i], "()\n")) {
1468 if (member (string[i], "<>;&|")) {
1469 int peek = string[i + 1];
1471 if (peek == string[i]) {
1473 if (string[1 + 2] == '-')
1479 if (member (peek, ">:&|")) {
1485 (string[i] == '>' || string[i] == '<')) ||
1487 (string[i] == '&'))) {
1496 /* Get word from string + i; */
1500 if (member (string[i], "\"'`"))
1501 delimiter = string[i++];
1503 for (;string[i]; i++) {
1505 if (string[i] == '\\') {
1507 if (string[i + 1] == '\n') {
1511 if (delimiter != '\'')
1512 if ((delimiter != '"') ||
1513 (member (string[i], slashify_in_quotes))) {
1520 if (delimiter && string[i] == delimiter) {
1525 if (!delimiter && (member (string[i], " \t\n;&()|<>")))
1528 if (!delimiter && member (string[i], "\"'`")) {
1529 delimiter = string[i];
1536 if (result_index + 2 >= size) {
1538 result = (char **)xmalloc ((size = 10) * (sizeof (char *)));
1541 (char **)xrealloc (result, ((size += 10) * (sizeof (char *))));
1543 result[result_index] = (char *)xmalloc (1 + len);
1544 strncpy (result[result_index], string + start, len);
1545 result[result_index][len] = '\0';
1547 result[result_index] = (char *)NULL;
1555 #if defined (STATIC_MALLOC)
1557 /* **************************************************************** */
1559 /* xmalloc and xrealloc () */
1561 /* **************************************************************** */
1563 static void memory_error_and_abort ();
1569 char *temp = (char *)malloc (bytes);
1572 memory_error_and_abort ();
1577 xrealloc (pointer, bytes)
1584 temp = (char *)xmalloc (bytes);
1586 temp = (char *)realloc (pointer, bytes);
1589 memory_error_and_abort ();
1595 memory_error_and_abort ()
1597 fprintf (stderr, "history: Out of virtual memory!\n");
1600 #endif /* STATIC_MALLOC */
1603 /* **************************************************************** */
1607 /* **************************************************************** */
1611 char line[1024], *t;
1618 fprintf (stdout, "history%% ");
1622 strcpy (line, "quit");
1631 result = history_expand (line, &expansion);
1632 strcpy (line, expansion);
1635 fprintf (stderr, "%s\n", line);
1643 if (strcmp (line, "quit") == 0) done = 1;
1644 if (strcmp (line, "save") == 0) write_history (0);
1645 if (strcmp (line, "read") == 0) read_history (0);
1646 if (strcmp (line, "list") == 0)
1648 register HIST_ENTRY **the_list = history_list ();
1652 for (i = 0; the_list[i]; i++)
1653 fprintf (stdout, "%d: %s\n", i + history_base, the_list[i]->line);
1655 if (strncmp (line, "delete", strlen ("delete")) == 0)
1658 if ((sscanf (line + strlen ("delete"), "%d", &which)) == 1)
1660 HIST_ENTRY *entry = remove_history (which);
1662 fprintf (stderr, "No such entry %d\n", which);
1671 fprintf (stderr, "non-numeric arg given to `delete'\n");
1681 * compile-command: "gcc -g -DTEST -o history history.c"