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. */
30 extern char *xmalloc (), *xrealloc ();
32 static char *xmalloc (), *xrealloc ();
38 #define alloca __builtin_alloca
40 #if defined (sparc) && defined (sun)
43 extern char *alloca ();
50 #define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x))
54 #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
58 #define digit(c) ((c) >= '0' && (c) <= '9')
62 #define member(c, s) ((c) ? index ((s), (c)) : 0)
65 /* **************************************************************** */
67 /* History functions */
69 /* **************************************************************** */
71 /* An array of HIST_ENTRY. This is where we store the history. */
72 static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL;
74 /* Non-zero means that we have enforced a limit on the amount of
75 history that we save. */
76 static int history_stifled = 0;
78 /* If HISTORY_STIFLED is non-zero, then this is the maximum number of
79 entries to remember. */
80 static int max_input_history;
82 /* The current location of the interactive history pointer. Just makes
83 life easier for outside callers. */
84 static int history_offset = 0;
86 /* The number of strings currently stored in the input_history list. */
87 static int history_length = 0;
89 /* The current number of slots allocated to the input_history. */
90 static int history_size = 0;
92 /* The number of slots to increase the_history by. */
93 #define DEFAULT_HISTORY_GROW_SIZE 50
95 /* The character that represents the start of a history expansion
96 request. This is usually `!'. */
97 char history_expansion_char = '!';
99 /* The character that invokes word substitution if found at the start of
100 a line. This is usually `^'. */
101 char history_subst_char = '^';
103 /* During tokenization, if this character is seen as the first character
104 of a word, then it, and all subsequent characters upto a newline are
105 ignored. For a Bourne shell, this should be '#'. Bash special cases
106 the interactive comment character to not be a comment delimiter. */
107 char history_comment_char = '\0';
109 /* The list of characters which inhibit the expansion of text if found
110 immediately following history_expansion_char. */
111 char *history_no_expand_chars = " \t\n\r=";
113 /* The logical `base' of the history array. It defaults to 1. */
114 int history_base = 1;
116 /* Begin a session in which the history functions might be used. This
117 initializes interactive variables. */
121 history_offset = history_length;
124 /* Place STRING at the end of the history list. The data field
132 if (history_stifled && (history_length == max_input_history)) {
135 /* If the history is stifled, and history_length is zero,
136 and it equals max_input_history, we don't save items. */
140 /* If there is something in the slot, then remove it. */
141 if (the_history[0]) {
142 free (the_history[0]->line);
143 free (the_history[0]);
146 for (i = 0; i < history_length; i++)
147 the_history[i] = the_history[i + 1];
155 (HIST_ENTRY **)xmalloc ((history_size = DEFAULT_HISTORY_GROW_SIZE)
156 * sizeof (HIST_ENTRY *));
160 if (history_length == (history_size - 1)) {
162 (HIST_ENTRY **)xrealloc (the_history,
163 ((history_size += DEFAULT_HISTORY_GROW_SIZE)
164 * sizeof (HIST_ENTRY *)));
170 temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
171 temp->line = savestring (string);
172 temp->data = (char *)NULL;
174 the_history[history_length] = (HIST_ENTRY *)NULL;
175 the_history[history_length - 1] = temp;
178 /* Make the history entry at WHICH have LINE and DATA. This returns
179 the old entry so you can dispose of the data. In the case of an
180 invalid WHICH, a NULL pointer is returned. */
182 replace_history_entry (which, line, data)
187 HIST_ENTRY *temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
188 HIST_ENTRY *old_value;
190 if (which >= history_length)
191 return ((HIST_ENTRY *)NULL);
193 old_value = the_history[which];
195 temp->line = savestring (line);
197 the_history[which] = temp;
202 /* Returns the magic number which says what history element we are
203 looking at now. In this implementation, it returns history_offset. */
207 return (history_offset);
210 /* Search the history for STRING, starting at history_offset.
211 If DIRECTION < 0, then the search is through previous entries,
212 else through subsequent. If the string is found, then
213 current_history () is the history entry, and the value of this function
214 is the offset in the line of that history entry that the string was
215 found in. Otherwise, nothing is changed, and a -1 is returned. */
217 history_search (string, direction)
221 register int i = history_offset;
222 register int reverse = (direction < 0);
225 int string_len = strlen (string);
227 /* Take care of trivial cases first. */
229 if (!history_length || ((i == history_length) && !reverse))
232 if (reverse && (i == history_length))
237 /* Search each line in the history list for STRING. */
239 /* At limit for direction? */
240 if ((reverse && i < 0) ||
241 (!reverse && i == history_length))
244 line = the_history[i]->line;
245 index = strlen (line);
247 /* If STRING is longer than line, no match. */
248 if (string_len > index)
251 /* Do the actual search. */
258 if (strncmp (string, line + index, string_len) == 0)
268 register int limit = (string_len - index) + 1;
271 while (index < limit)
273 if (strncmp (string, line + index, string_len) == 0)
289 /* Remove history element WHICH from the history. The removed
290 element is returned to you so you can free the line, data,
291 and containing structure. */
293 remove_history (which)
296 HIST_ENTRY *return_value;
298 if (which >= history_length || !history_length)
299 return_value = (HIST_ENTRY *)NULL;
303 return_value = the_history[which];
305 for (i = which; i < history_length; i++)
306 the_history[i] = the_history[i + 1];
310 return (return_value);
313 /* Stifle the history list, remembering only MAX number of lines. */
318 if (history_length > max)
322 /* This loses because we cannot free the data. */
323 for (i = 0; i < (history_length - max); i++)
325 free (the_history[i]->line);
326 free (the_history[i]);
329 for (j = 0, i = history_length - max; j < max; i++, j++)
330 the_history[j] = the_history[i];
331 the_history[j] = (HIST_ENTRY *)NULL;
335 max_input_history = max;
338 /* Stop stifling the history. This returns the previous amount the history
339 was stifled by. The value is positive if the history was stifled, negative
344 int result = max_input_history;
353 /* Return the string that should be used in the place of this
354 filename. This only matters when you don't specify the
355 filename to read_history (), or write_history (). */
357 history_filename (filename)
360 char *return_val = filename ? savestring (filename) : (char *)NULL;
364 char *home = (char *)getenv ("HOME");
365 if (!home) home = ".";
366 return_val = (char *)xmalloc (2 + strlen (home) + strlen (".history"));
367 strcpy (return_val, home);
368 strcat (return_val, "/");
369 strcat (return_val, ".history");
374 /* What to use until the line gets too big. */
375 #define TYPICAL_LINE_SIZE 2048
377 /* Add the contents of FILENAME to the history list, a line at a time.
378 If FILENAME is NULL, then read from ~/.history. Returns 0 if
379 successful, or errno if not. */
381 read_history (filename)
384 char *input = history_filename (filename);
385 FILE *file = fopen (input, "r");
386 char *line = (char *)xmalloc (TYPICAL_LINE_SIZE);
387 int line_size = TYPICAL_LINE_SIZE;
403 while (!(done = ((c = getc (file)) == EOF)))
410 line = (char *)xrealloc (line, line_size += TYPICAL_LINE_SIZE);
421 /* Overwrite FILENAME with the current history. If FILENAME is NULL,
422 then write the history list to ~/.history. Values returned
423 are as in read_history ().*/
425 write_history (filename)
429 char *output = history_filename (filename);
430 FILE *file = fopen (output, "w");
433 if (!file) return (errno);
434 if (!history_length) return (0);
436 for (i = 0; i < history_length; i++)
437 fprintf (file, "%s\n", the_history[i]->line);
443 /* Return the history entry at the current position, as determined by
444 history_offset. If there is no entry there, return a NULL pointer. */
448 if ((history_offset == history_length) || !the_history)
449 return ((HIST_ENTRY *)NULL);
451 return (the_history[history_offset]);
454 /* Back up history_offset to the previous history entry, and return
455 a pointer to that entry. If there is no previous entry then return
461 return ((HIST_ENTRY *)NULL);
463 return (the_history[--history_offset]);
466 /* Move history_offset forward to the next history entry, and return
467 a pointer to that entry. If there is no next entry then return a
472 if (history_offset == history_length)
473 return ((HIST_ENTRY *)NULL);
475 return (the_history[++history_offset]);
478 /* Return the current history array. The caller has to be carefull, since this
479 is the actual array of data, and could be bashed or made corrupt easily.
480 The array is terminated with a NULL pointer. */
484 return (the_history);
487 /* Return the history entry which is logically at OFFSET in the history array.
488 OFFSET is relative to history_base. */
493 int index = offset - history_base;
495 if (index >= history_length ||
498 return ((HIST_ENTRY *)NULL);
499 return (the_history[index]);
502 /* Search for STRING in the history list. DIR is < 0 for searching
503 backwards. POS is an absolute index into the history list at
504 which point to begin searching. */
506 history_search_pos (string, dir, pos)
510 int ret, old = where_history ();
511 history_set_pos (pos);
512 if (history_search (string, dir) == -1)
514 history_set_pos (old);
517 ret = where_history ();
518 history_set_pos (old);
522 /* Make the current history item be the one at POS, an absolute index.
523 Returns zero if POS is out of range, else non-zero. */
525 history_set_pos (pos)
528 if (pos > history_length || pos < 0 || !the_history)
530 history_offset = pos;
535 /* **************************************************************** */
537 /* History Expansion */
539 /* **************************************************************** */
541 /* Hairy history expansion on text, not tokens. This is of general
542 use, and thus belongs in this library. */
544 /* The last string searched for in a !?string? search. */
545 static char *search_string = (char *)NULL;
547 /* Return the event specified at TEXT + OFFSET modifying OFFSET to
548 point to after the event specifier. Just a pointer to the history
549 line is returned; NULL is returned in the event of a bad specifier.
550 You pass STRING with *INDEX equal to the history_expansion_char that
551 begins this specification.
552 DELIMITING_QUOTE is a character that is allowed to end the string
553 specification for what to search for in addition to the normal
554 characters `:', ` ', `\t', `\n', and sometimes `?'.
555 So you might call this function like:
556 line = get_history_event ("!echo:p", &index, 0); */
558 get_history_event (string, caller_index, delimiting_quote)
561 int delimiting_quote;
563 register int i = *caller_index;
567 /* The event can be specified in a number of ways.
569 !! the previous command
571 !-n current command-line minus N
572 !str the most recent command starting with STR
574 the most recent command containing STR
576 All values N are determined via HISTORY_BASE. */
578 if (string[i] != history_expansion_char)
579 return ((char *)NULL);
581 /* Move on to the specification. */
584 /* Handle !! case. */
585 if (string[i] == history_expansion_char)
588 which = history_base + (history_length - 1);
593 /* Hack case of numeric line specification. */
595 if (string[i] == '-')
601 if (digit (string[i]))
605 /* Get the extent of the digits. */
606 for (; digit (string[i]); i++);
608 /* Get the digit value. */
609 sscanf (string + start, "%d", &which);
614 which = (history_length + history_base) - which;
617 if (entry = history_get (which))
618 return (entry->line);
620 return ((char *)NULL);
623 /* This must be something to search for. If the spec begins with
624 a '?', then the string may be anywhere on the line. Otherwise,
625 the string must be found at the start of a line. */
629 int substring_okay = 0;
631 if (string[i] == '?')
637 for (index = i; string[i]; i++)
638 if (whitespace (string[i]) ||
641 (substring_okay && string[i] == '?') ||
642 string[i] == delimiting_quote)
645 temp = (char *)alloca (1 + (i - index));
646 strncpy (temp, &string[index], (i - index));
647 temp[i - index] = '\0';
649 if (string[i] == '?')
656 index = history_search (temp, -1);
661 history_offset = history_length;
662 return ((char *)NULL);
665 if (index == 0 || substring_okay ||
666 (strncmp (temp, the_history[history_offset]->line,
667 strlen (temp)) == 0))
670 entry = current_history ();
671 history_offset = history_length;
673 /* If this was a substring search, then remember the string that
674 we matched for word substitution. */
678 free (search_string);
679 search_string = savestring (temp);
682 return (entry->line);
694 /* Expand the string STRING, placing the result into OUTPUT, a pointer
695 to a string. Returns:
697 0) If no expansions took place (or, if the only change in
698 the text was the de-slashifying of the history expansion
700 1) If expansions did take place
701 -1) If there was an error in expansion.
703 If an error ocurred in expansion, then OUTPUT contains a descriptive
706 history_expand (string, output)
710 register int j, l = strlen (string);
711 int i, word_spec_error = 0;
712 int cc, modified = 0;
713 char *word_spec, *event;
714 int starting_index, only_printing = 0, substitute_globally = 0;
716 char *get_history_word_specifier (), *rindex ();
718 /* The output string, and its length. */
720 char *result = (char *)NULL;
722 /* Used in add_string; */
723 char *temp, tt[2], tbl[3];
725 /* Prepare the buffer for printing error messages. */
726 result = (char *)xmalloc (len = 255);
728 result[0] = tt[1] = tbl[2] = '\0';
730 tbl[1] = history_expansion_char;
732 /* Grovel the string. Only backslash can quote the history escape
733 character. We also handle arg specifiers. */
735 /* Before we grovel forever, see if the history_expansion_char appears
736 anywhere within the text. */
738 /* The quick substitution character is a history expansion all right. That
739 is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
740 that is the substitution that we do. */
741 if (string[0] == history_subst_char)
743 char *format_string = (char *)alloca (10 + strlen (string));
745 sprintf (format_string, "%c%c:s%s",
746 history_expansion_char, history_expansion_char,
748 string = format_string;
753 /* If not quick substitution, still maybe have to do expansion. */
755 /* `!' followed by one of the characters in history_no_expand_chars
756 is NOT an expansion. */
757 for (i = 0; string[i]; i++)
758 if (string[i] == history_expansion_char)
759 if (!string[i + 1] || member (string[i + 1], history_no_expand_chars))
765 *output = savestring (string);
770 for (i = j = 0; i < l; i++)
772 int tchar = string[i];
773 if (tchar == history_expansion_char)
779 if (string[i + 1] == history_expansion_char)
788 /* case history_expansion_char: */
790 starting_index = i + 1;
793 /* If the history_expansion_char is followed by one of the
794 characters in history_no_expand_chars, then it is not a
795 candidate for expansion of any kind. */
796 if (member (cc, history_no_expand_chars))
799 /* There is something that is listed as a `word specifier' in csh
800 documentation which means `the expanded text to this point'.
801 That is not a word specifier, it is an event specifier. */
804 goto hack_pound_sign;
806 /* If it is followed by something that starts a word specifier,
807 then !! is implied as the event specifier. */
809 if (member (cc, ":$*%^"))
814 fake_s[0] = fake_s[1] = history_expansion_char;
816 event = get_history_event (fake_s, &fake_i, 0);
820 int quoted_search_delimiter = 0;
822 /* If the character before this `!' is a double or single
823 quote, then this expansion takes place inside of the
824 quoted string. If we have to search for some text ("!foo"),
825 allow the delimiter to end the search string. */
826 if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
827 quoted_search_delimiter = string[i - 1];
829 event = get_history_event (string, &i, quoted_search_delimiter);
835 int l = 1 + (i - starting_index);
837 temp = (char *)alloca (1 + l);
838 strncpy (temp, string + starting_index, l);
840 sprintf (result, "%s: %s.", temp,
841 word_spec_error ? "Bad word specifier" : "Event not found");
847 /* If a word specifier is found, then do what that requires. */
850 word_spec = get_history_word_specifier (string, event, &i);
852 /* There is no such thing as a `malformed word specifier'. However,
853 it is possible for a specifier that has no match. In that case,
855 if (word_spec == (char *)-1)
859 goto event_not_found;
862 /* If no word specifier, than the thing of interest was the event. */
867 temp = (char *)alloca (1 + strlen (word_spec));
868 strcpy (temp, word_spec);
872 /* Perhaps there are other modifiers involved. Do what they say. */
876 if (string[i] == ':')
880 switch (string[i + 1])
882 /* :p means make this the last executed line. So we
883 return an error state after adding this line to the
889 /* :t discards all but the last part of the pathname. */
891 tstr = rindex (temp, '/');
896 /* :h discards the last part of a pathname. */
898 tstr = rindex (temp, '/');
903 /* :r discards the suffix. */
905 tstr = rindex (temp, '.');
910 /* :e discards everything but the suffix. */
912 tstr = rindex (temp, '.');
917 /* :s/this/that substitutes `this' for `that'. */
918 /* :gs/this/that substitutes `this' for `that' globally. */
920 if (string[i + 2] == 's')
923 substitute_globally = 1;
931 char *this, *that, *new_event;
933 int si, l_this, l_that, l_temp = strlen (temp);
935 if (i + 2 < strlen (string))
936 delimiter = string[i + 2];
944 for (si = i; string[si] && string[si] != delimiter; si++);
946 this = (char *)alloca (1 + l_this);
947 strncpy (this, string + i, l_this);
955 for (si = i; string[si] && string[si] != delimiter; si++);
957 that = (char *)alloca (1 + l_that);
958 strncpy (that, string + i, l_that);
964 /* Ignore impossible cases. */
966 goto cant_substitute;
968 /* Find the first occurrence of THIS in TEMP. */
970 for (; (si + l_this) <= l_temp; si++)
971 if (strncmp (temp + si, this, l_this) == 0)
974 (char *)alloca (1 + (l_that - l_this) + l_temp);
975 strncpy (new_event, temp, si);
976 strncpy (new_event + si, that, l_that);
977 strncpy (new_event + si + l_that,
979 l_temp - (si + l_this));
980 new_event[(l_that - l_this) + l_temp] = '\0';
983 if (substitute_globally)
986 l_temp = strlen (temp);
987 substitute_globally++;
996 if (substitute_globally > 1)
998 substitute_globally = 0;
1002 goto event_not_found;
1005 /* :# is the line so far. Note that we have to
1006 alloca () it since RESULT could be realloc ()'ed
1007 below in add_string. */
1012 temp = (char *)alloca (1 + strlen (result));
1013 strcpy (temp, result);
1024 /* Believe it or not, we have to back the pointer up by one. */
1028 /* A regular character. Just add it to the output string. */
1041 result = (char *)xrealloc (result, (len += 255));
1043 strcpy (result + (j - strlen (temp)), temp);
1051 add_history (result);
1055 return (modified != 0);
1058 /* Return a consed string which is the word specified in SPEC, and found
1059 in FROM. NULL is returned if there is no spec. -1 is returned if
1060 the word specified cannot be found. CALLER_INDEX is the offset in
1061 SPEC to start looking; it is updated to point to just after the last
1062 character parsed. */
1064 get_history_word_specifier (spec, from, caller_index)
1068 register int i = *caller_index;
1070 int expecting_word_spec = 0;
1071 char *history_arg_extract ();
1073 /* The range of words to return doesn't exist yet. */
1076 /* If we found a colon, then this *must* be a word specification. If
1077 it isn't, then it is an error. */
1079 i++, expecting_word_spec++;
1081 /* Handle special cases first. */
1083 /* `%' is the word last searched for. */
1086 *caller_index = i + 1;
1088 return (savestring (search_string));
1090 return (savestring (""));
1093 /* `*' matches all of the arguments, but not the command. */
1098 *caller_index = i + 1;
1099 star_result = history_arg_extract (1, '$', from);
1102 star_result = savestring ("");
1104 return (star_result);
1107 /* `$' is last arg. */
1110 *caller_index = i + 1;
1111 return (history_arg_extract ('$', '$', from));
1114 /* Try to get FIRST and LAST figured out. */
1115 if (spec[i] == '-' || spec[i] == '^')
1122 if (digit (spec[i]) && expecting_word_spec)
1124 sscanf (spec + i, "%d", &first);
1125 for (; digit (spec[i]); i++);
1128 return ((char *)NULL);
1146 if (digit (spec[i]))
1148 sscanf (spec + i, "%d", &last);
1149 for (; digit (spec[i]); i++);
1160 char *result = (char *)NULL;
1165 result = history_arg_extract (first, last, from);
1170 return ((char *)-1);
1174 /* Extract the args specified, starting at FIRST, and ending at LAST.
1175 The args are taken from STRING. If either FIRST or LAST is < 0,
1176 then make that arg count from the right (subtract from the number of
1177 tokens, so that FIRST = -1 means the next to last token on the line. */
1179 history_arg_extract (first, last, string)
1183 register int i, len;
1184 char *result = (char *)NULL;
1185 int size = 0, offset = 0;
1187 char **history_tokenize (), **list;
1189 if (!(list = history_tokenize (string)))
1190 return ((char *)NULL);
1192 for (len = 0; list[len]; len++);
1195 last = len + last - 1;
1198 first = len + first - 1;
1208 if (first > len || last > len)
1209 result = ((char *)NULL);
1212 for (i = first; i < last; i++)
1214 int l = strlen (list[i]);
1217 result = (char *)xmalloc ((size = (2 + l)));
1219 result = (char *)xrealloc (result, (size += (2 + l)));
1220 strcpy (result + offset, list[i]);
1224 strcpy (result + offset, " ");
1230 for (i = 0; i < len; i++)
1238 #define slashify_in_quotes "\\`\"$"
1240 /* Return an array of tokens, much as the shell might. The tokens are
1241 parsed out of STRING. */
1243 history_tokenize (string)
1246 char **result = (char **)NULL;
1247 register int i, start, result_index, size;
1250 i = result_index = size = 0;
1252 /* Get a token, and stuff it into RESULT. The tokens are split
1253 exactly where the shell would split them. */
1256 /* Skip leading whitespace. */
1257 for (; string[i] && whitespace(string[i]); i++);
1261 if (!string[i] || string[i] == history_comment_char)
1264 if (member (string[i], "()\n")) {
1269 if (member (string[i], "<>;&|")) {
1270 int peek = string[i + 1];
1272 if (peek == string[i]) {
1274 if (string[1 + 2] == '-')
1280 if (member (peek, ">:&|")) {
1286 (string[i] == '>' || string[i] == '<')) ||
1288 (string[i] == '&'))) {
1297 /* Get word from string + i; */
1301 if (member (string[i], "\"'`"))
1302 delimiter = string[i++];
1304 for (;string[i]; i++) {
1306 if (string[i] == '\\') {
1308 if (string[i + 1] == '\n') {
1312 if (delimiter != '\'')
1313 if ((delimiter != '"') ||
1314 (member (string[i], slashify_in_quotes))) {
1321 if (delimiter && string[i] == delimiter) {
1326 if (!delimiter && (member (string[i], " \t\n;&()|<>")))
1329 if (!delimiter && member (string[i], "\"'`")) {
1330 delimiter = string[i];
1337 if (result_index + 2 >= size) {
1339 result = (char **)xmalloc ((size = 10) * (sizeof (char *)));
1342 (char **)xrealloc (result, ((size += 10) * (sizeof (char *))));
1344 result[result_index] = (char *)xmalloc (1 + len);
1345 strncpy (result[result_index], string + start, len);
1346 result[result_index][len] = '\0';
1348 result[result_index] = (char *)NULL;
1356 #ifdef STATIC_MALLOC
1358 /* **************************************************************** */
1360 /* xmalloc and xrealloc () */
1362 /* **************************************************************** */
1364 static void memory_error_and_abort ();
1370 char *temp = (char *)malloc (bytes);
1373 memory_error_and_abort ();
1378 xrealloc (pointer, bytes)
1382 char *temp = (char *)realloc (pointer, bytes);
1385 memory_error_and_abort ();
1390 memory_error_and_abort ()
1392 fprintf (stderr, "history: Out of virtual memory!\n");
1395 #endif /* STATIC_MALLOC */
1398 /* **************************************************************** */
1402 /* **************************************************************** */
1406 char line[1024], *t;
1413 fprintf (stdout, "history%% ");
1417 strcpy (line, "quit");
1426 result = history_expand (line, &expansion);
1427 strcpy (line, expansion);
1430 fprintf (stderr, "%s\n", line);
1438 if (strcmp (line, "quit") == 0) done = 1;
1439 if (strcmp (line, "save") == 0) write_history (0);
1440 if (strcmp (line, "read") == 0) read_history (0);
1441 if (strcmp (line, "list") == 0)
1443 register HIST_ENTRY **the_list = history_list ();
1447 for (i = 0; the_list[i]; i++)
1448 fprintf (stdout, "%d: %s\n", i + history_base, the_list[i]->line);
1450 if (strncmp (line, "delete", strlen ("delete")) == 0)
1453 if ((sscanf (line + strlen ("delete"), "%d", &which)) == 1)
1455 HIST_ENTRY *entry = remove_history (which);
1457 fprintf (stderr, "No such entry %d\n", which);
1466 fprintf (stderr, "non-numeric arg given to `delete'\n");
1476 * compile-command: "gcc -g -DTEST -o history history.c"