2011-05-11 Sterling Augustine <saugustine@google.com>
[deliverable/binutils-gdb.git] / readline / history.c
index 3c07d11833d166c4b3afe3ce16fc8618c183e2bb..d7894cfd0a4202bacfff38ecd6c871f03a9be2cf 100644 (file)
@@ -1,70 +1,61 @@
-/* History.c -- standalone history library */
+/* history.c -- standalone history library */
 
-/* Copyright (C) 1989 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2009 Free Software Foundation, Inc.
 
-   This file contains the GNU History Library (the Library), a set of
+   This file contains the GNU History Library (History), a set of
    routines for managing the text of previously typed lines.
 
-   The Library is free software; you can redistribute it and/or modify
+   History is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 1, or (at your option)
-   any later version.
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-   The Library is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
+   History is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   The GNU General Public License is often shipped with GNU software, and
-   is generally kept in a file called COPYING or LICENSE.  If you do not
-   have a copy of the license, write to the Free Software Foundation,
-   675 Mass Ave, Cambridge, MA 02139, USA. */
+   You should have received a copy of the GNU General Public License
+   along with History.  If not, see <http://www.gnu.org/licenses/>.
+*/
 
 /* The goal is to make the implementation transparent, so that you
    don't have to know what data types are used, just what functions
    you can call.  I think I have done that. */
+#define READLINE_LIBRARY
 
-/* Remove these declarations when we have a complete libgnu.a. */
-#define STATIC_MALLOC
-#ifndef STATIC_MALLOC
-extern char *xmalloc (), *xrealloc ();
-#else
-static char *xmalloc (), *xrealloc ();
+#if defined (HAVE_CONFIG_H)
+#  include <config.h>
 #endif
 
 #include <stdio.h>
 
-#ifdef __GNUC__
-#define alloca __builtin_alloca
+#if defined (HAVE_STDLIB_H)
+#  include <stdlib.h>
 #else
-#if defined (sparc) && defined (sun)
-#include <alloca.h>
-#else
-extern char *alloca ();
-#endif
+#  include "ansi_stdlib.h"
+#endif /* HAVE_STDLIB_H */
+
+#if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
+#  include <unistd.h>
 #endif
 
 #include "history.h"
+#include "histlib.h"
 
-#ifndef savestring
-#define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x))
-#endif
-
-#ifndef whitespace
-#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
-#endif
+#include "xmalloc.h"
 
-#ifndef digit
-#define digit(c)  ((c) >= '0' && (c) <= '9')
-#endif
+/* The number of slots to increase the_history by. */
+#define DEFAULT_HISTORY_GROW_SIZE 50
 
-#ifndef member
-#define member(c, s) ((c) ? index ((s), (c)) : 0)
-#endif
+static char *hist_inittime PARAMS((void));
 
 /* **************************************************************** */
 /*                                                                 */
-/*                     History functions                           */
+/*                     History Functions                           */
 /*                                                                 */
 /* **************************************************************** */
 
@@ -73,45 +64,56 @@ static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL;
 
 /* Non-zero means that we have enforced a limit on the amount of
    history that we save. */
-static int history_stifled = 0;
+static int history_stifled;
+
+/* The current number of slots allocated to the input_history. */
+static int history_size;
 
 /* If HISTORY_STIFLED is non-zero, then this is the maximum number of
    entries to remember. */
-static int max_input_history;
+int history_max_entries;
+int max_input_history; /* backwards compatibility */
 
 /* The current location of the interactive history pointer.  Just makes
    life easier for outside callers. */
-static int history_offset = 0;
-
-/* The number of strings currently stored in the input_history list. */
-static int history_length = 0;
+int history_offset;
 
-/* The current number of slots allocated to the input_history. */
-static int history_size = 0;
+/* The number of strings currently stored in the history list. */
+int history_length;
 
-/* The number of slots to increase the_history by. */
-#define DEFAULT_HISTORY_GROW_SIZE 50
-
-/* The character that represents the start of a history expansion
-   request.  This is usually `!'. */
-char history_expansion_char = '!';
-
-/* The character that invokes word substitution if found at the start of
-   a line.  This is usually `^'. */
-char history_subst_char = '^';
+/* The logical `base' of the history array.  It defaults to 1. */
+int history_base = 1;
 
-/* During tokenization, if this character is seen as the first character
-   of a word, then it, and all subsequent characters upto a newline are
-   ignored.  For a Bourne shell, this should be '#'.  Bash special cases
-   the interactive comment character to not be a comment delimiter. */
-char history_comment_char = '\0';
+/* Return the current HISTORY_STATE of the history. */
+HISTORY_STATE *
+history_get_history_state ()
+{
+  HISTORY_STATE *state;
+
+  state = (HISTORY_STATE *)xmalloc (sizeof (HISTORY_STATE));
+  state->entries = the_history;
+  state->offset = history_offset;
+  state->length = history_length;
+  state->size = history_size;
+  state->flags = 0;
+  if (history_stifled)
+    state->flags |= HS_STIFLED;
 
-/* The list of characters which inhibit the expansion of text if found
-   immediately following history_expansion_char. */
-char *history_no_expand_chars = " \t\n\r=";
+  return (state);
+}
 
-/* The logical `base' of the history array.  It defaults to 1. */
-int history_base = 1;
+/* Set the state of the current history array to STATE. */
+void
+history_set_history_state (state)
+     HISTORY_STATE *state;
+{
+  the_history = state->entries;
+  history_offset = state->offset;
+  history_length = state->length;
+  history_size = state->size;
+  if (state->flags & HS_STIFLED)
+    history_stifled = 1;
+}
 
 /* Begin a session in which the history functions might be used.  This
    initializes interactive variables. */
@@ -121,82 +123,18 @@ using_history ()
   history_offset = history_length;
 }
 
-/* Place STRING at the end of the history list.  The data field
-   is  set to NULL. */
-void
-add_history (string)
-     char *string;
-{
-  HIST_ENTRY *temp;
-
-  if (history_stifled && (history_length == max_input_history)) {
-    register int i;
-
-    /* If the history is stifled, and history_length is zero,
-       and it equals max_input_history, we don't save items. */
-    if (!history_length)
-      return;
-
-    /* If there is something in the slot, then remove it. */
-    if (the_history[0]) {
-      free (the_history[0]->line);
-      free (the_history[0]);
-    }
-
-    for (i = 0; i < history_length; i++)
-      the_history[i] = the_history[i + 1];
-
-    history_base++;
-
-  } else {
-
-    if (!history_size) {
-      the_history =
-       (HIST_ENTRY **)xmalloc ((history_size = DEFAULT_HISTORY_GROW_SIZE)
-                         * sizeof (HIST_ENTRY *));
-      history_length = 1;
-
-    } else {
-      if (history_length == (history_size - 1)) {
-       the_history =
-         (HIST_ENTRY **)xrealloc (the_history,
-                                  ((history_size += DEFAULT_HISTORY_GROW_SIZE)
-                                   * sizeof (HIST_ENTRY *)));
-      }
-      history_length++;
-    }
-  }
-
-  temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
-  temp->line = savestring (string);
-  temp->data = (char *)NULL;
-
-  the_history[history_length] = (HIST_ENTRY *)NULL;
-  the_history[history_length - 1] = temp;
-}
-
-/* Make the history entry at WHICH have LINE and DATA.  This returns
-   the old entry so you can dispose of the data.  In the case of an
-   invalid WHICH, a NULL pointer is returned. */
-HIST_ENTRY *
-replace_history_entry (which, line, data)
-     int which;
-     char *line;
-     char *data;
+/* Return the number of bytes that the primary history entries are using.
+   This just adds up the lengths of the_history->lines and the associated
+   timestamps. */
+int
+history_total_bytes ()
 {
-  HIST_ENTRY *temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
-  HIST_ENTRY *old_value;
-
-  if (which >= history_length)
-    return ((HIST_ENTRY *)NULL);
+  register int i, result;
 
-  old_value = the_history[which];
+  for (i = result = 0; the_history && the_history[i]; i++)
+    result += HISTENT_BYTES (the_history[i]);
 
-  temp->line = savestring (line);
-  temp->data = data;
-  the_history[which] = temp;
-
-  return (old_value);
+  return (result);
 }
 
 /* Returns the magic number which says what history element we are
@@ -207,237 +145,25 @@ where_history ()
   return (history_offset);
 }
 
-/* Search the history for STRING, starting at history_offset.
-   If DIRECTION < 0, then the search is through previous entries,
-   else through subsequent.  If the string is found, then
-   current_history () is the history entry, and the value of this function
-   is the offset in the line of that history entry that the string was
-   found in.  Otherwise, nothing is changed, and a -1 is returned. */
-int
-history_search (string, direction)
-     char *string;
-     int direction;
-{
-  register int i = history_offset;
-  register int reverse = (direction < 0);
-  register char *line;
-  register int index;
-  int string_len = strlen (string);
-
-  /* Take care of trivial cases first. */
-
-  if (!history_length || ((i == history_length) && !reverse))
-    return (-1);
-
-  if (reverse && (i == history_length))
-    i--;
-
-  while (1)
-    {
-      /* Search each line in the history list for STRING. */
-
-      /* At limit for direction? */
-      if ((reverse && i < 0) ||
-         (!reverse && i == history_length))
-       return (-1);
-
-      line = the_history[i]->line;
-      index = strlen (line);
-
-      /* If STRING is longer than line, no match. */
-      if (string_len > index)
-       goto next_line;
-
-      /* Do the actual search. */
-      if (reverse)
-       {
-         index -= string_len;
-
-         while (index >= 0)
-           {
-             if (strncmp (string, line + index, string_len) == 0)
-               {
-                 history_offset = i;
-                 return (index);
-               }
-             index--;
-           }
-       }
-      else
-       {
-         register int limit = (string_len - index) + 1;
-         index = 0;
-
-         while (index < limit)
-           {
-             if (strncmp (string, line + index, string_len) == 0)
-               {
-                 history_offset = i;
-                 return (index);
-               }
-             index++;
-           }
-       }
-    next_line:
-      if (reverse)
-       i--;
-      else
-       i++;
-    }
-}
-
-/* Remove history element WHICH from the history.  The removed
-   element is returned to you so you can free the line, data,
-   and containing structure. */
-HIST_ENTRY *
-remove_history (which)
-     int which;
-{
-  HIST_ENTRY *return_value;
-
-  if (which >= history_length || !history_length)
-    return_value = (HIST_ENTRY *)NULL;
-  else
-    {
-      register int i;
-      return_value = the_history[which];
-
-      for (i = which; i < history_length; i++)
-       the_history[i] = the_history[i + 1];
-
-      history_length--;
-    }
-  return (return_value);
-}
-
-/* Stifle the history list, remembering only MAX number of lines. */
-void
-stifle_history (max)
-     int max;
-{
-  if (history_length > max)
-    {
-      register int i, j;
-
-      /* This loses because we cannot free the data. */
-      for (i = 0; i < (history_length - max); i++)
-       {
-         free (the_history[i]->line);
-         free (the_history[i]);
-       }
-      history_base = i;
-      for (j = 0, i = history_length - max; j < max; i++, j++)
-       the_history[j] = the_history[i];
-      the_history[j] = (HIST_ENTRY *)NULL;
-      history_length = j;
-    }
-  history_stifled = 1;
-  max_input_history = max;
-}
-
-/* Stop stifling the history.  This returns the previous amount the history
- was stifled by.  The value is positive if the history was stifled, negative
- if it wasn't. */
-int
-unstifle_history ()
-{
-  int result = max_input_history;
-  if (history_stifled)
-    {
-      result = - result;
-      history_stifled = 0;
-    }
-  return (result);
-}
-
-/* Return the string that should be used in the place of this
-   filename.  This only matters when you don't specify the
-   filename to read_history (), or write_history (). */
-static char *
-history_filename (filename)
-     char *filename;
-{
-  char *return_val = filename ? savestring (filename) : (char *)NULL;
-
-  if (!return_val)
-    {
-      char *home = (char *)getenv ("HOME");
-      if (!home) home = ".";
-      return_val = (char *)xmalloc (2 + strlen (home) + strlen (".history"));
-      strcpy (return_val, home);
-      strcat (return_val, "/");
-      strcat (return_val, ".history");
-    }
-  return (return_val);
-}
-
-/* What to use until the line gets too big. */
-#define TYPICAL_LINE_SIZE 2048
-
-/* Add the contents of FILENAME to the history list, a line at a time.
-   If FILENAME is NULL, then read from ~/.history.  Returns 0 if
-   successful, or errno if not. */
+/* Make the current history item be the one at POS, an absolute index.
+   Returns zero if POS is out of range, else non-zero. */
 int
-read_history (filename)
-     char *filename;
+history_set_pos (pos)
+     int pos;
 {
-  char *input = history_filename (filename);
-  FILE *file = fopen (input, "r");
-  char *line = (char *)xmalloc (TYPICAL_LINE_SIZE);
-  int line_size = TYPICAL_LINE_SIZE;
-  int done = 0;
-
-  if (!file)
-    {
-      extern int errno;
-      free (line);
-      return (errno);
-    }
-
-  while (!done)
-    {
-      int c;
-      int i;
-
-      i = 0;
-      while (!(done = ((c = getc (file)) == EOF)))
-       {
-         if (c == '\n')
-           break;
-
-         line [i++] = c;
-         if (i == line_size)
-           line = (char *)xrealloc (line, line_size += TYPICAL_LINE_SIZE);
-       }
-      line[i] = '\0';
-      if (line[0])
-       add_history (line);
-    }
-  free (line);
-  fclose (file);
-  return (0);
+  if (pos > history_length || pos < 0 || !the_history)
+    return (0);
+  history_offset = pos;
+  return (1);
 }
-
-/* Overwrite FILENAME with the current history.  If FILENAME is NULL,
-   then write the history list to ~/.history.  Values returned
-   are as in read_history ().*/
-int
-write_history (filename)
-     char *filename;
+/* Return the current history array.  The caller has to be careful, since this
+   is the actual array of data, and could be bashed or made corrupt easily.
+   The array is terminated with a NULL pointer. */
+HIST_ENTRY **
+history_list ()
 {
-  extern int errno;
-  char *output = history_filename (filename);
-  FILE *file = fopen (output, "w");
-  register int i;
-
-  if (!file) return (errno);
-  if (!history_length) return (0);
-
-  for (i = 0; i < history_length; i++)
-    fprintf (file, "%s\n", the_history[i]->line);
-
-  fclose (file);
-  return (0);
+  return (the_history);
 }
 
 /* Return the history entry at the current position, as determined by
@@ -445,10 +171,9 @@ write_history (filename)
 HIST_ENTRY *
 current_history ()
 {
-  if ((history_offset == history_length) || !the_history)
-    return ((HIST_ENTRY *)NULL);
-  else
-    return (the_history[history_offset]);
+  return ((history_offset == history_length) || the_history == 0)
+               ? (HIST_ENTRY *)NULL
+               : the_history[history_offset];
 }
 
 /* Back up history_offset to the previous history entry, and return
@@ -457,10 +182,7 @@ current_history ()
 HIST_ENTRY *
 previous_history ()
 {
-  if (!history_offset)
-    return ((HIST_ENTRY *)NULL);
-  else
-    return (the_history[--history_offset]);
+  return history_offset ? the_history[--history_offset] : (HIST_ENTRY *)NULL;
 }
 
 /* Move history_offset forward to the next history entry, and return
@@ -469,19 +191,7 @@ previous_history ()
 HIST_ENTRY *
 next_history ()
 {
-  if (history_offset == history_length)
-    return ((HIST_ENTRY *)NULL);
-  else
-    return (the_history[++history_offset]);
-}
-
-/* Return the current history array.  The caller has to be carefull, since this
-   is the actual array of data, and could be bashed or made corrupt easily.
-   The array is terminated with a NULL pointer. */
-HIST_ENTRY **
-history_list ()
-{
-  return (the_history);
+  return (history_offset == history_length) ? (HIST_ENTRY *)NULL : the_history[++history_offset];
 }
 
 /* Return the history entry which is logically at OFFSET in the history array.
@@ -490,989 +200,320 @@ HIST_ENTRY *
 history_get (offset)
      int offset;
 {
-  int index = offset - history_base;
+  int local_index;
 
-  if (index >= history_length ||
-      index < 0 ||
-      !the_history)
-    return ((HIST_ENTRY *)NULL);
-  return (the_history[index]);
+  local_index = offset - history_base;
+  return (local_index >= history_length || local_index < 0 || the_history == 0)
+               ? (HIST_ENTRY *)NULL
+               : the_history[local_index];
 }
 
-/* Search for STRING in the history list.  DIR is < 0 for searching
-   backwards.  POS is an absolute index into the history list at
-   which point to begin searching. */
-int
-history_search_pos (string, dir, pos)
+HIST_ENTRY *
+alloc_history_entry (string, ts)
      char *string;
-     int dir, pos;
+     char *ts;
 {
-  int ret, old = where_history ();
-  history_set_pos (pos);
-  if (history_search (string, dir) == -1)
-    {
-      history_set_pos (old);
-      return (-1);
-    }
-  ret = where_history ();
-  history_set_pos (old);
-  return ret;
+  HIST_ENTRY *temp;
+
+  temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
+
+  temp->line = string ? savestring (string) : string;
+  temp->data = (char *)NULL;
+  temp->timestamp = ts;
+
+  return temp;
 }
 
-/* Make the current history item be the one at POS, an absolute index.
-   Returns zero if POS is out of range, else non-zero. */
-int
-history_set_pos (pos)
-     int pos;
+time_t
+history_get_time (hist)
+     HIST_ENTRY *hist;
 {
-  if (pos > history_length || pos < 0 || !the_history)
-    return (0);
-  history_offset = pos;
-  return (1);
+  char *ts;
+  time_t t;
+
+  if (hist == 0 || hist->timestamp == 0)
+    return 0;
+  ts = hist->timestamp;
+  if (ts[0] != history_comment_char)
+    return 0;
+  t = (time_t) atol (ts + 1);          /* XXX - should use strtol() here */
+  return t;
 }
-\f
-/* **************************************************************** */
-/*                                                                 */
-/*                     History Expansion                           */
-/*                                                                 */
-/* **************************************************************** */
 
-/* Hairy history expansion on text, not tokens.  This is of general
-   use, and thus belongs in this library. */
-
-/* The last string searched for in a !?string? search. */
-static char *search_string = (char *)NULL;
-
-/* Return the event specified at TEXT + OFFSET modifying OFFSET to
-   point to after the event specifier.  Just a pointer to the history
-   line is returned; NULL is returned in the event of a bad specifier.
-   You pass STRING with *INDEX equal to the history_expansion_char that
-   begins this specification.
-   DELIMITING_QUOTE is a character that is allowed to end the string
-   specification for what to search for in addition to the normal
-   characters `:', ` ', `\t', `\n', and sometimes `?'.
-   So you might call this function like:
-   line = get_history_event ("!echo:p", &index, 0);  */
-char *
-get_history_event (string, caller_index, delimiting_quote)
-     char *string;
-     int *caller_index;
-     int delimiting_quote;
+static char *
+hist_inittime ()
 {
-  register int i = *caller_index;
-  int which, sign = 1;
-  HIST_ENTRY *entry;
-
-  /* The event can be specified in a number of ways.
-
-     !!   the previous command
-     !n   command line N
-     !-n  current command-line minus N
-     !str the most recent command starting with STR
-     !?str[?]
-         the most recent command containing STR
-
-     All values N are determined via HISTORY_BASE. */
-
-  if (string[i] != history_expansion_char)
-    return ((char *)NULL);
-
-  /* Move on to the specification. */
-  i++;
-
-  /* Handle !! case. */
-  if (string[i] == history_expansion_char)
-    {
-      i++;
-      which = history_base + (history_length - 1);
-      *caller_index = i;
-      goto get_which;
-    }
-
-  /* Hack case of numeric line specification. */
- read_which:
-  if (string[i] == '-')
-    {
-      sign = -1;
-      i++;
-    }
-
-  if (digit (string[i]))
-    {
-      int start = i;
-
-      /* Get the extent of the digits. */
-      for (; digit (string[i]); i++);
+  time_t t;
+  char ts[64], *ret;
 
-      /* Get the digit value. */
-      sscanf (string + start, "%d", &which);
-
-      *caller_index = i;
-
-      if (sign < 0)
-       which = (history_length + history_base) - which;
-
-    get_which:
-      if (entry = history_get (which))
-       return (entry->line);
-
-      return ((char *)NULL);
-    }
+  t = (time_t) time ((time_t *)0);
+#if defined (HAVE_VSNPRINTF)           /* assume snprintf if vsnprintf exists */
+  snprintf (ts, sizeof (ts) - 1, "X%lu", (unsigned long) t);
+#else
+  sprintf (ts, "X%lu", (unsigned long) t);
+#endif
+  ret = savestring (ts);
+  ret[0] = history_comment_char;
 
-  /* This must be something to search for.  If the spec begins with
-     a '?', then the string may be anywhere on the line.  Otherwise,
-     the string must be found at the start of a line. */
-  {
-    int index;
-    char *temp;
-    int substring_okay = 0;
-
-    if (string[i] == '?')
-      {
-       substring_okay++;
-       i++;
-      }
-
-    for (index = i; string[i]; i++)
-      if (whitespace (string[i]) ||
-         string[i] == '\n' ||
-         string[i] == ':' ||
-         (substring_okay && string[i] == '?') ||
-         string[i] == delimiting_quote)
-       break;
-
-    temp = (char *)alloca (1 + (i - index));
-    strncpy (temp, &string[index], (i - index));
-    temp[i - index] = '\0';
-
-    if (string[i] == '?')
-      i++;
-
-    *caller_index = i;
-
-  search_again:
-
-    index = history_search (temp, -1);
-
-    if (index < 0)
-    search_lost:
-      {
-       history_offset = history_length;
-       return ((char *)NULL);
-      }
-
-    if (index == 0 || substring_okay || 
-       (strncmp (temp, the_history[history_offset]->line,
-                 strlen (temp)) == 0))
-      {
-      search_won:
-       entry = current_history ();
-       history_offset = history_length;
-       
-       /* If this was a substring search, then remember the string that
-          we matched for word substitution. */
-       if (substring_okay)
-         {
-           if (search_string)
-             free (search_string);
-           search_string = savestring (temp);
-         }
-
-       return (entry->line);
-      }
-
-    if (history_offset)
-      history_offset--;
-    else
-      goto search_lost;
-    
-    goto search_again;
-  }
+  return ret;
 }
 
-/* Expand the string STRING, placing the result into OUTPUT, a pointer
-   to a string.  Returns:
-
-   0) If no expansions took place (or, if the only change in
-      the text was the de-slashifying of the history expansion
-      character)
-   1) If expansions did take place
-  -1) If there was an error in expansion.
-
-  If an error ocurred in expansion, then OUTPUT contains a descriptive
-  error message. */
-int
-history_expand (string, output)
-     char *string;
-     char **output;
+/* Place STRING at the end of the history list.  The data field
+   is  set to NULL. */
+void
+add_history (string)
+     const char *string;
 {
-  register int j, l = strlen (string);
-  int i, word_spec_error = 0;
-  int cc, modified = 0;
-  char *word_spec, *event;
-  int starting_index, only_printing = 0, substitute_globally = 0;
-
-  char *get_history_word_specifier (), *rindex ();
-
-  /* The output string, and its length. */
-  int len = 0;
-  char *result = (char *)NULL;
-
-  /* Used in add_string; */
-  char *temp, tt[2], tbl[3];
-
-  /* Prepare the buffer for printing error messages. */
-  result = (char *)xmalloc (len = 255);
-
-  result[0] = tt[1] = tbl[2] = '\0';
-  tbl[0] = '\\';
-  tbl[1] = history_expansion_char;
-
-  /* Grovel the string.  Only backslash can quote the history escape
-     character.  We also handle arg specifiers. */
-
-  /* Before we grovel forever, see if the history_expansion_char appears
-     anywhere within the text. */
+  HIST_ENTRY *temp;
 
-  /* The quick substitution character is a history expansion all right.  That
-     is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
-     that is the substitution that we do. */
-  if (string[0] == history_subst_char)
+  if (history_stifled && (history_length == history_max_entries))
     {
-      char *format_string = (char *)alloca (10 + strlen (string));
-
-      sprintf (format_string, "%c%c:s%s",
-              history_expansion_char, history_expansion_char,
-              string);
-      string = format_string;
-      l += 4;
-      goto grovel;
-    }
-
-  /* If not quick substitution, still maybe have to do expansion. */
+      register int i;
 
-  /* `!' followed by one of the characters in history_no_expand_chars
-     is NOT an expansion. */
-  for (i = 0; string[i]; i++)
-    if (string[i] == history_expansion_char)
-      if (!string[i + 1] || member (string[i + 1], history_no_expand_chars))
-       continue;
-      else
-       goto grovel;
+      /* If the history is stifled, and history_length is zero,
+        and it equals history_max_entries, we don't save items. */
+      if (history_length == 0)
+       return;
 
-  free (result);
-  *output = savestring (string);
-  return (0);
+      /* If there is something in the slot, then remove it. */
+      if (the_history[0])
+       (void) free_history_entry (the_history[0]);
 
- grovel:
+      /* Copy the rest of the entries, moving down one slot. */
+      for (i = 0; i < history_length; i++)
+       the_history[i] = the_history[i + 1];
 
-  for (i = j = 0; i < l; i++)
+      history_base++;
+    }
+  else
     {
-      int tchar = string[i];
-      if (tchar == history_expansion_char)
-       tchar = -3;
-
-      switch (tchar)
+      if (history_size == 0)
        {
-       case '\\':
-         if (string[i + 1] == history_expansion_char)
-           {
-             i++;
-             temp = tbl;
-             goto do_add;
-           }
-         else
-           goto add_char;
-
-         /* case history_expansion_char: */
-       case -3:
-         starting_index = i + 1;
-         cc = string[i + 1];
-
-         /* If the history_expansion_char is followed by one of the
-            characters in history_no_expand_chars, then it is not a
-            candidate for expansion of any kind. */
-         if (member (cc, history_no_expand_chars))
-           goto add_char;
-
-         /* There is something that is listed as a `word specifier' in csh
-            documentation which means `the expanded text to this point'.
-            That is not a word specifier, it is an event specifier. */
-
-         if (cc == '#')
-           goto hack_pound_sign;
-
-         /* If it is followed by something that starts a word specifier,
-            then !! is implied as the event specifier. */
-
-         if (member (cc, ":$*%^"))
-           {
-             char fake_s[3];
-             int fake_i = 0;
-             i++;
-             fake_s[0] = fake_s[1] = history_expansion_char;
-             fake_s[2] = '\0';
-             event = get_history_event (fake_s, &fake_i, 0);
-           }
-         else
-           {
-             int quoted_search_delimiter = 0;
-
-             /* If the character before this `!' is a double or single
-                quote, then this expansion takes place inside of the
-                quoted string.  If we have to search for some text ("!foo"),
-                allow the delimiter to end the search string. */
-             if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
-               quoted_search_delimiter = string[i - 1];
-
-             event = get_history_event (string, &i, quoted_search_delimiter);
-           }
-         
-         if (!event)
-         event_not_found:
-           {
-           int l = 1 + (i - starting_index);
-
-           temp = (char *)alloca (1 + l);
-           strncpy (temp, string + starting_index, l);
-           temp[l - 1] = 0;
-           sprintf (result, "%s: %s.", temp,
-                    word_spec_error ? "Bad word specifier" : "Event not found");
-         error_exit:
-           *output = result;
-           return (-1);
-         }
-
-         /* If a word specifier is found, then do what that requires. */
-         starting_index = i;
-
-         word_spec = get_history_word_specifier (string, event, &i);
-
-         /* There is no such thing as a `malformed word specifier'.  However,
-            it is possible for a specifier that has no match.  In that case,
-            we complain. */
-         if (word_spec == (char *)-1)
-         bad_word_spec:
-         {
-           word_spec_error++;
-           goto event_not_found;
-         }
-
-         /* If no word specifier, than the thing of interest was the event. */
-         if (!word_spec)
-           temp = event;
-         else
-           {
-             temp = (char *)alloca (1 + strlen (word_spec));
-             strcpy (temp, word_spec);
-             free (word_spec);
-           }
-
-         /* Perhaps there are other modifiers involved.  Do what they say. */
-
-       hack_specials:
-
-         if (string[i] == ':')
+         history_size = DEFAULT_HISTORY_GROW_SIZE;
+         the_history = (HIST_ENTRY **)xmalloc (history_size * sizeof (HIST_ENTRY *));
+         history_length = 1;
+       }
+      else
+       {
+         if (history_length == (history_size - 1))
            {
-             char *tstr;
-
-             switch (string[i + 1])
-               {
-                 /* :p means make this the last executed line.  So we
-                    return an error state after adding this line to the
-                    history. */
-               case 'p':
-                 only_printing++;
-                 goto next_special;
-
-                 /* :t discards all but the last part of the pathname. */
-               case 't':
-                 tstr = rindex (temp, '/');
-                 if (tstr)
-                   temp = ++tstr;
-                 goto next_special;
-
-                 /* :h discards the last part of a pathname. */
-               case 'h':
-                 tstr = rindex (temp, '/');
-                 if (tstr)
-                   *tstr = '\0';
-                 goto next_special;
-
-                 /* :r discards the suffix. */
-               case 'r':
-                 tstr = rindex (temp, '.');
-                 if (tstr)
-                   *tstr = '\0';
-                 goto next_special;
-
-                 /* :e discards everything but the suffix. */
-               case 'e':
-                 tstr = rindex (temp, '.');
-                 if (tstr)
-                   temp = tstr;
-                 goto next_special;
-
-                 /* :s/this/that substitutes `this' for `that'. */
-                 /* :gs/this/that substitutes `this' for `that' globally. */
-               case 'g':
-                 if (string[i + 2] == 's')
-                   {
-                     i++;
-                     substitute_globally = 1;
-                     goto substitute;
-                   }
-                 else
-                   
-               case 's':
-                 substitute:
-                 {
-                   char *this, *that, *new_event;
-                   int delimiter = 0;
-                   int si, l_this, l_that, l_temp = strlen (temp);
-
-                   if (i + 2 < strlen (string))
-                     delimiter = string[i + 2];
-
-                   if (!delimiter)
-                     break;
-
-                   i += 3;
-
-                   /* Get THIS. */
-                   for (si = i; string[si] && string[si] != delimiter; si++);
-                   l_this = (si - i);
-                   this = (char *)alloca (1 + l_this);
-                   strncpy (this, string + i, l_this);
-                   this[l_this] = '\0';
-
-                   i = si;
-                   if (string[si])
-                     i++;
-
-                   /* Get THAT. */
-                   for (si = i; string[si] && string[si] != delimiter; si++);
-                   l_that = (si - i);
-                   that = (char *)alloca (1 + l_that);
-                   strncpy (that, string + i, l_that);
-                   that[l_that] = '\0';
-
-                   i = si;
-                   if (string[si]) i++;
-
-                   /* Ignore impossible cases. */
-                   if (l_this > l_temp)
-                     goto cant_substitute;
-
-                   /* Find the first occurrence of THIS in TEMP. */
-                   si = 0;
-                   for (; (si + l_this) <= l_temp; si++)
-                     if (strncmp (temp + si, this, l_this) == 0)
-                       {
-                         new_event =
-                           (char *)alloca (1 + (l_that - l_this) + l_temp);
-                         strncpy (new_event, temp, si);
-                         strncpy (new_event + si, that, l_that);
-                         strncpy (new_event + si + l_that,
-                                  temp + si + l_this,
-                                  l_temp - (si + l_this));
-                         new_event[(l_that - l_this) + l_temp] = '\0';
-                         temp = new_event;
-
-                         if (substitute_globally)
-                           {
-                             si += l_that;
-                             l_temp = strlen (temp);
-                             substitute_globally++;
-                             continue;
-                           }
-
-                         goto hack_specials;
-                       }
-
-                 cant_substitute:
-
-                   if (substitute_globally > 1)
-                     {
-                       substitute_globally = 0;
-                       goto hack_specials;
-                     }
-
-                   goto event_not_found;
-                 }
-
-                 /* :# is the line so far.  Note that we have to
-                    alloca () it since RESULT could be realloc ()'ed
-                    below in add_string. */
-               case '#':
-               hack_pound_sign:
-                 if (result)
-                   {
-                     temp = (char *)alloca (1 + strlen (result));
-                     strcpy (temp, result);
-                   }
-                 else
-                   temp = "";
-
-               next_special:
-                 i += 2;
-                 goto hack_specials;
-               }
-
+             history_size += DEFAULT_HISTORY_GROW_SIZE;
+             the_history = (HIST_ENTRY **)
+               xrealloc (the_history, history_size * sizeof (HIST_ENTRY *));
            }
-         /* Believe it or not, we have to back the pointer up by one. */
-         --i;
-         goto add_string;
-
-         /* A regular character.  Just add it to the output string. */
-       default:
-       add_char:
-         tt[0] = string[i];
-         temp = tt;
-         goto do_add;
-
-       add_string:
-         modified++;
-
-       do_add:
-         j += strlen (temp);
-         while (j > len)
-           result = (char *)xrealloc (result, (len += 255));
-
-         strcpy (result + (j - strlen (temp)), temp);
+         history_length++;
        }
     }
 
-  *output = result;
+  temp = alloc_history_entry (string, hist_inittime ());
 
-  if (only_printing)
-    {
-      add_history (result);
-      return (-1);
-    }
+  the_history[history_length] = (HIST_ENTRY *)NULL;
+  the_history[history_length - 1] = temp;
+}
+
+/* Change the time stamp of the most recent history entry to STRING. */
+void
+add_history_time (string)
+     const char *string;
+{
+  HIST_ENTRY *hs;
 
-  return (modified != 0);
+  if (string == 0)
+    return;
+  hs = the_history[history_length - 1];
+  FREE (hs->timestamp);
+  hs->timestamp = savestring (string);
 }
 
-/* Return a consed string which is the word specified in SPEC, and found
-   in FROM.  NULL is returned if there is no spec.  -1 is returned if
-   the word specified cannot be found.  CALLER_INDEX is the offset in
-   SPEC to start looking; it is updated to point to just after the last
-   character parsed. */
-char *
-get_history_word_specifier (spec, from, caller_index)
-     char *spec, *from;
-     int *caller_index;
+/* Free HIST and return the data so the calling application can free it
+   if necessary and desired. */
+histdata_t
+free_history_entry (hist)
+     HIST_ENTRY *hist;
 {
-  register int i = *caller_index;
-  int first, last;
-  int expecting_word_spec = 0;
-  char *history_arg_extract ();
+  histdata_t x;
+
+  if (hist == 0)
+    return ((histdata_t) 0);
+  FREE (hist->line);
+  FREE (hist->timestamp);
+  x = hist->data;
+  xfree (hist);
+  return (x);
+}
 
-  /* The range of words to return doesn't exist yet. */
-  first = last = 0;
+HIST_ENTRY *
+copy_history_entry (hist)
+     HIST_ENTRY *hist;
+{
+  HIST_ENTRY *ret;
+  char *ts;
 
-  /* If we found a colon, then this *must* be a word specification.  If
-     it isn't, then it is an error. */
-  if (spec[i] == ':')
-    i++, expecting_word_spec++;
+  if (hist == 0)
+    return hist;
 
-  /* Handle special cases first. */
+  ret = alloc_history_entry (hist->line, (char *)NULL);
 
-  /* `%' is the word last searched for. */
-  if (spec[i] == '%')
-    {
-      *caller_index = i + 1;
-      if (search_string)
-       return (savestring (search_string));
-      else
-       return (savestring (""));
-    }
+  ts = hist->timestamp ? savestring (hist->timestamp) : hist->timestamp;
+  ret->timestamp = ts;
 
-  /* `*' matches all of the arguments, but not the command. */
-  if (spec[i] == '*')
-    {
-      char *star_result;
+  ret->data = hist->data;
 
-      *caller_index = i + 1;
-      star_result = history_arg_extract (1, '$', from);
+  return ret;
+}
+  
+/* Make the history entry at WHICH have LINE and DATA.  This returns
+   the old entry so you can dispose of the data.  In the case of an
+   invalid WHICH, a NULL pointer is returned. */
+HIST_ENTRY *
+replace_history_entry (which, line, data)
+     int which;
+     const char *line;
+     histdata_t data;
+{
+  HIST_ENTRY *temp, *old_value;
 
-      if (!star_result)
-       star_result = savestring ("");
+  if (which < 0 || which >= history_length)
+    return ((HIST_ENTRY *)NULL);
 
-      return (star_result);
-    }
+  temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
+  old_value = the_history[which];
 
-  /* `$' is last arg. */
-  if (spec[i] == '$')
-    {
-      *caller_index = i + 1;
-      return (history_arg_extract ('$', '$', from));
-    }
+  temp->line = savestring (line);
+  temp->data = data;
+  temp->timestamp = savestring (old_value->timestamp);
+  the_history[which] = temp;
 
-  /* Try to get FIRST and LAST figured out. */
-  if (spec[i] == '-' || spec[i] == '^')
-    {
-      first = 1;
-      goto get_last;
-    }
+  return (old_value);
+}
 
- get_first:
-  if (digit (spec[i]) && expecting_word_spec)
-    {
-      sscanf (spec + i, "%d", &first);
-      for (; digit (spec[i]); i++);
-    }
-  else
-    return ((char *)NULL);
+/* Replace the DATA in the specified history entries, replacing OLD with
+   NEW.  WHICH says which one(s) to replace:  WHICH == -1 means to replace
+   all of the history entries where entry->data == OLD; WHICH == -2 means
+   to replace the `newest' history entry where entry->data == OLD; and
+   WHICH >= 0 means to replace that particular history entry's data, as
+   long as it matches OLD. */
+void
+replace_history_data (which,old, new)
+     int which;
+     histdata_t *old, *new;
+{
+  HIST_ENTRY *entry;
+  register int i, last;
 
- get_last:
-  if (spec[i] == '^')
+  if (which < -2 || which >= history_length || history_length == 0 || the_history == 0)
+    return;
+
+  if (which >= 0)
     {
-      i++;
-      last = 1;
-      goto get_args;
+      entry = the_history[which];
+      if (entry && entry->data == old)
+       entry->data = new;
+      return;
     }
 
-  if (spec[i] != '-')
+  last = -1;
+  for (i = 0; i < history_length; i++)
     {
-      last = first;
-      goto get_args;
+      entry = the_history[i];
+      if (entry == 0)
+       continue;
+      if (entry->data == old)
+       {
+         last = i;
+         if (which == -1)
+           entry->data = new;
+       }
     }
-
-  i++;
-
-  if (digit (spec[i]))
+  if (which == -2 && last >= 0)
     {
-      sscanf (spec + i, "%d", &last);
-      for (; digit (spec[i]); i++);
+      entry = the_history[last];
+      entry->data = new;       /* XXX - we don't check entry->old */
     }
-  else
-    if (spec[i] == '$')
-      {
-       i++;
-       last = '$';
-      }
-
- get_args:
-  {
-    char *result = (char *)NULL;
-
-    *caller_index = i;
-
-    if (last >= first)
-      result = history_arg_extract (first, last, from);
-
-    if (result)
-      return (result);
-    else
-      return ((char *)-1);
-  }
-}
-
-/* Extract the args specified, starting at FIRST, and ending at LAST.
-   The args are taken from STRING.  If either FIRST or LAST is < 0,
-   then make that arg count from the right (subtract from the number of
-   tokens, so that FIRST = -1 means the next to last token on the line. */
-char *
-history_arg_extract (first, last, string)
-     int first, last;
-     char *string;
+}      
+  
+/* Remove history element WHICH from the history.  The removed
+   element is returned to you so you can free the line, data,
+   and containing structure. */
+HIST_ENTRY *
+remove_history (which)
+     int which;
 {
-  register int i, len;
-  char *result = (char *)NULL;
-  int size = 0, offset = 0;
-
-  char **history_tokenize (), **list;
-
-  if (!(list = history_tokenize (string)))
-    return ((char *)NULL);
-
-  for (len = 0; list[len]; len++);
-
-  if (last < 0)
-    last = len + last - 1;
-
-  if (first < 0)
-    first = len + first - 1;
-
-  if (last == '$')
-    last = len - 1;
-
-  if (first == '$')
-    first = len - 1;
+  HIST_ENTRY *return_value;
+  register int i;
 
-  last++;
+  if (which < 0 || which >= history_length || history_length ==  0 || the_history == 0)
+    return ((HIST_ENTRY *)NULL);
 
-  if (first > len || last > len)
-    result = ((char *)NULL);
-  else
-    {
-      for (i = first; i < last; i++)
-       {
-         int l = strlen (list[i]);
-
-         if (!result)
-           result = (char *)xmalloc ((size = (2 + l)));
-         else
-           result = (char *)xrealloc (result, (size += (2 + l)));
-         strcpy (result + offset, list[i]);
-         offset += l;
-         if (i + 1 < last)
-           {
-             strcpy (result + offset, " ");
-             offset++;
-           }
-       }
-    }
+  return_value = the_history[which];
 
-  for (i = 0; i < len; i++)
-    free (list[i]);
+  for (i = which; i < history_length; i++)
+    the_history[i] = the_history[i + 1];
 
-  free (list);
+  history_length--;
 
-  return (result);
+  return (return_value);
 }
 
-#define slashify_in_quotes "\\`\"$"
-
-/* Return an array of tokens, much as the shell might.  The tokens are
-   parsed out of STRING. */
-char **
-history_tokenize (string)
-     char *string;
+/* Stifle the history list, remembering only MAX number of lines. */
+void
+stifle_history (max)
+     int max;
 {
-  char **result = (char **)NULL;
-  register int i, start, result_index, size;
-  int len;
-
-  i = result_index = size = 0;
-
-  /* Get a token, and stuff it into RESULT.  The tokens are split
-     exactly where the shell would split them. */
- get_token:
-
-  /* Skip leading whitespace. */
-  for (; string[i] && whitespace(string[i]); i++);
-
-  start = i;
-
-  if (!string[i] || string[i] == history_comment_char)
-    return (result);
-
-  if (member (string[i], "()\n")) {
-    i++;
-    goto got_token;
-  }
-
-  if (member (string[i], "<>;&|")) {
-    int peek = string[i + 1];
-
-    if (peek == string[i]) {
-      if (peek ==  '<') {
-       if (string[1 + 2] == '-')
-         i++;
-       i += 2;
-       goto got_token;
-      }
-
-      if (member (peek, ">:&|")) {
-       i += 2;
-       goto got_token;
-      }
-    } else {
-      if ((peek == '&' &&
-         (string[i] == '>' || string[i] == '<')) ||
-         ((peek == '>') &&
-         (string[i] == '&'))) {
-       i += 2;
-       goto got_token;
-      }
-    }
-    i++;
-    goto got_token;
-  }
-
-  /* Get word from string + i; */
-  {
-    int delimiter = 0;
-
-    if (member (string[i], "\"'`"))
-      delimiter = string[i++];
-
-    for (;string[i]; i++) {
-
-      if (string[i] == '\\') {
-
-       if (string[i + 1] == '\n') {
-         i++;
-         continue;
-       } else {
-         if (delimiter != '\'')
-           if ((delimiter != '"') ||
-               (member (string[i], slashify_in_quotes))) {
-             i++;
-             continue;
-           }
-       }
-      }
-
-      if (delimiter && string[i] == delimiter) {
-       delimiter = 0;
-       continue;
-      }
+  register int i, j;
 
-      if (!delimiter && (member (string[i], " \t\n;&()|<>")))
-       goto got_token;
+  if (max < 0)
+    max = 0;
 
-      if (!delimiter && member (string[i], "\"'`")) {
-       delimiter = string[i];
-       continue;
-      }
-    }
-    got_token:
+  if (history_length > max)
+    {
+      /* This loses because we cannot free the data. */
+      for (i = 0, j = history_length - max; i < j; i++)
+       free_history_entry (the_history[i]);
 
-    len = i - start;
-    if (result_index + 2 >= size) {
-      if (!size)
-       result = (char **)xmalloc ((size = 10) * (sizeof (char *)));
-      else
-       result =
-         (char **)xrealloc (result, ((size += 10) * (sizeof (char *))));
+      history_base = i;
+      for (j = 0, i = history_length - max; j < max; i++, j++)
+       the_history[j] = the_history[i];
+      the_history[j] = (HIST_ENTRY *)NULL;
+      history_length = j;
     }
-    result[result_index] = (char *)xmalloc (1 + len);
-    strncpy (result[result_index], string + start, len);
-    result[result_index][len] = '\0';
-    result_index++;
-    result[result_index] = (char *)NULL;
-  }
-  if (string[i])
-    goto get_token;
-
-  return (result);
-}
-
-#ifdef STATIC_MALLOC
-\f
-/* **************************************************************** */
-/*                                                                 */
-/*                     xmalloc and xrealloc ()                     */
-/*                                                                 */
-/* **************************************************************** */
-
-static void memory_error_and_abort ();
-
-static char *
-xmalloc (bytes)
-     int bytes;
-{
-  char *temp = (char *)malloc (bytes);
 
-  if (!temp)
-    memory_error_and_abort ();
-  return (temp);
+  history_stifled = 1;
+  max_input_history = history_max_entries = max;
 }
 
-static char *
-xrealloc (pointer, bytes)
-     char *pointer;
-     int bytes;
+/* Stop stifling the history.  This returns the previous maximum
+   number of history entries.  The value is positive if the history
+   was stifled, negative if it wasn't. */
+int
+unstifle_history ()
 {
-  char *temp = (char *)realloc (pointer, bytes);
-
-  if (!temp)
-    memory_error_and_abort ();
-  return (temp);
+  if (history_stifled)
+    {
+      history_stifled = 0;
+      return (history_max_entries);
+    }
+  else
+    return (-history_max_entries);
 }
 
-static void
-memory_error_and_abort ()
+int
+history_is_stifled ()
 {
-  fprintf (stderr, "history: Out of virtual memory!\n");
-  abort ();
+  return (history_stifled);
 }
-#endif /* STATIC_MALLOC */
 
-\f
-/* **************************************************************** */
-/*                                                                 */
-/*                             Test Code                           */
-/*                                                                 */
-/* **************************************************************** */
-#ifdef TEST
-main ()
+void
+clear_history ()
 {
-  char line[1024], *t;
-  int done = 0;
-
-  line[0] = 0;
+  register int i;
 
-  while (!done)
+  /* This loses because we cannot free the data. */
+  for (i = 0; i < history_length; i++)
     {
-      fprintf (stdout, "history%% ");
-      t = gets (line);
-
-      if (!t)
-       strcpy (line, "quit");
-
-      if (line[0])
-       {
-         char *expansion;
-         int result;
-
-         using_history ();
-
-         result = history_expand (line, &expansion);
-         strcpy (line, expansion);
-         free (expansion);
-         if (result)
-           fprintf (stderr, "%s\n", line);
-
-         if (result < 0)
-           continue;
-
-         add_history (line);
-       }
-
-      if (strcmp (line, "quit") == 0) done = 1;
-      if (strcmp (line, "save") == 0) write_history (0);
-      if (strcmp (line, "read") == 0) read_history (0);
-      if (strcmp (line, "list") == 0)
-       {
-         register HIST_ENTRY **the_list = history_list ();
-         register int i;
-
-         if (the_list)
-           for (i = 0; the_list[i]; i++)
-             fprintf (stdout, "%d: %s\n", i + history_base, the_list[i]->line);
-       }
-      if (strncmp (line, "delete", strlen ("delete")) == 0)
-       {
-         int which;
-         if ((sscanf (line + strlen ("delete"), "%d", &which)) == 1)
-           {
-             HIST_ENTRY *entry = remove_history (which);
-             if (!entry)
-               fprintf (stderr, "No such entry %d\n", which);
-             else
-               {
-                 free (entry->line);
-                 free (entry);
-               }
-           }
-         else
-           {
-             fprintf (stderr, "non-numeric arg given to `delete'\n");
-           }
-       }
+      free_history_entry (the_history[i]);
+      the_history[i] = (HIST_ENTRY *)NULL;
     }
-}
 
-#endif                         /* TEST */
-\f
-/*
-* Local variables:
-* compile-command: "gcc -g -DTEST -o history history.c"
-* end:
-*/
+  history_offset = history_length = 0;
+}
This page took 0.040259 seconds and 4 git commands to generate.