1/* history.c -- standalone history library */
2
3/* Copyright (C) 1989-2005 Free Software Foundation, Inc.
4
5   This file contains the GNU History Library (the Library), a set of
6   routines for managing the text of previously typed lines.
7
8   The Library is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2, or (at your option)
11   any later version.
12
13   The Library is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   General Public License for more details.
17
18   The GNU General Public License is often shipped with GNU software, and
19   is generally kept in a file called COPYING or LICENSE.  If you do not
20   have a copy of the license, write to the Free Software Foundation,
21   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
22
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. */
26#define READLINE_LIBRARY
27
28#if defined (HAVE_CONFIG_H)
29#  include <config.h>
30#endif
31
32#include <stdio.h>
33
34#if defined (HAVE_STDLIB_H)
35#  include <stdlib.h>
36#else
37#  include "ansi_stdlib.h"
38#endif /* HAVE_STDLIB_H */
39
40#if defined (HAVE_UNISTD_H)
41#  ifdef _MINIX
42#    include <sys/types.h>
43#  endif
44#  include <unistd.h>
45#endif
46
47#include "history.h"
48#include "histlib.h"
49
50#include "xmalloc.h"
51
52/* The number of slots to increase the_history by. */
53#define DEFAULT_HISTORY_GROW_SIZE 50
54
55static char *hist_inittime PARAMS((void));
56
57/* **************************************************************** */
58/*								    */
59/*			History Functions			    */
60/*								    */
61/* **************************************************************** */
62
63/* An array of HIST_ENTRY.  This is where we store the history. */
64static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL;
65
66/* Non-zero means that we have enforced a limit on the amount of
67   history that we save. */
68static int history_stifled;
69
70/* The current number of slots allocated to the input_history. */
71static int history_size;
72
73/* If HISTORY_STIFLED is non-zero, then this is the maximum number of
74   entries to remember. */
75int history_max_entries;
76int max_input_history;	/* backwards compatibility */
77
78/* The current location of the interactive history pointer.  Just makes
79   life easier for outside callers. */
80int history_offset;
81
82/* The number of strings currently stored in the history list. */
83int history_length;
84
85/* The logical `base' of the history array.  It defaults to 1. */
86int history_base = 1;
87
88/* Return the current HISTORY_STATE of the history. */
89HISTORY_STATE *
90history_get_history_state ()
91{
92  HISTORY_STATE *state;
93
94  state = (HISTORY_STATE *)xmalloc (sizeof (HISTORY_STATE));
95  state->entries = the_history;
96  state->offset = history_offset;
97  state->length = history_length;
98  state->size = history_size;
99  state->flags = 0;
100  if (history_stifled)
101    state->flags |= HS_STIFLED;
102
103  return (state);
104}
105
106/* Set the state of the current history array to STATE. */
107void
108history_set_history_state (state)
109     HISTORY_STATE *state;
110{
111  the_history = state->entries;
112  history_offset = state->offset;
113  history_length = state->length;
114  history_size = state->size;
115  if (state->flags & HS_STIFLED)
116    history_stifled = 1;
117}
118
119/* Begin a session in which the history functions might be used.  This
120   initializes interactive variables. */
121void
122using_history ()
123{
124  history_offset = history_length;
125}
126
127/* Return the number of bytes that the primary history entries are using.
128   This just adds up the lengths of the_history->lines and the associated
129   timestamps. */
130int
131history_total_bytes ()
132{
133  register int i, result;
134
135  for (i = result = 0; the_history && the_history[i]; i++)
136    result += HISTENT_BYTES (the_history[i]);
137
138  return (result);
139}
140
141/* Returns the magic number which says what history element we are
142   looking at now.  In this implementation, it returns history_offset. */
143int
144where_history ()
145{
146  return (history_offset);
147}
148
149/* Make the current history item be the one at POS, an absolute index.
150   Returns zero if POS is out of range, else non-zero. */
151int
152history_set_pos (pos)
153     int pos;
154{
155  if (pos > history_length || pos < 0 || !the_history)
156    return (0);
157  history_offset = pos;
158  return (1);
159}
160
161/* Return the current history array.  The caller has to be carefull, since this
162   is the actual array of data, and could be bashed or made corrupt easily.
163   The array is terminated with a NULL pointer. */
164HIST_ENTRY **
165history_list ()
166{
167  return (the_history);
168}
169
170/* Return the history entry at the current position, as determined by
171   history_offset.  If there is no entry there, return a NULL pointer. */
172HIST_ENTRY *
173current_history ()
174{
175  return ((history_offset == history_length) || the_history == 0)
176		? (HIST_ENTRY *)NULL
177		: the_history[history_offset];
178}
179
180/* Back up history_offset to the previous history entry, and return
181   a pointer to that entry.  If there is no previous entry then return
182   a NULL pointer. */
183HIST_ENTRY *
184previous_history ()
185{
186  return history_offset ? the_history[--history_offset] : (HIST_ENTRY *)NULL;
187}
188
189/* Move history_offset forward to the next history entry, and return
190   a pointer to that entry.  If there is no next entry then return a
191   NULL pointer. */
192HIST_ENTRY *
193next_history ()
194{
195  return (history_offset == history_length) ? (HIST_ENTRY *)NULL : the_history[++history_offset];
196}
197
198/* Return the history entry which is logically at OFFSET in the history array.
199   OFFSET is relative to history_base. */
200HIST_ENTRY *
201history_get (offset)
202     int offset;
203{
204  int local_index;
205
206  local_index = offset - history_base;
207  return (local_index >= history_length || local_index < 0 || the_history == 0)
208		? (HIST_ENTRY *)NULL
209		: the_history[local_index];
210}
211
212time_t
213history_get_time (hist)
214     HIST_ENTRY *hist;
215{
216  char *ts;
217  time_t t;
218
219  if (hist == 0 || hist->timestamp == 0)
220    return 0;
221  ts = hist->timestamp;
222  if (ts[0] != history_comment_char)
223    return 0;
224  t = (time_t) atol (ts + 1);		/* XXX - should use strtol() here */
225  return t;
226}
227
228static char *
229hist_inittime ()
230{
231  time_t t;
232  char ts[64], *ret;
233
234  t = (time_t) time ((time_t *)0);
235#if defined (HAVE_VSNPRINTF)		/* assume snprintf if vsnprintf exists */
236  snprintf (ts, sizeof (ts) - 1, "X%lu", (unsigned long) t);
237#else
238  sprintf (ts, "X%lu", (unsigned long) t);
239#endif
240  ret = savestring (ts);
241  ret[0] = history_comment_char;
242
243  return ret;
244}
245
246/* Place STRING at the end of the history list.  The data field
247   is  set to NULL. */
248void
249add_history (string)
250     const char *string;
251{
252  HIST_ENTRY *temp;
253
254  if (history_stifled && (history_length == history_max_entries))
255    {
256      register int i;
257
258      /* If the history is stifled, and history_length is zero,
259	 and it equals history_max_entries, we don't save items. */
260      if (history_length == 0)
261	return;
262
263      /* If there is something in the slot, then remove it. */
264      if (the_history[0])
265	(void) free_history_entry (the_history[0]);
266
267      /* Copy the rest of the entries, moving down one slot. */
268      for (i = 0; i < history_length; i++)
269	the_history[i] = the_history[i + 1];
270
271      history_base++;
272    }
273  else
274    {
275      if (history_size == 0)
276	{
277	  history_size = DEFAULT_HISTORY_GROW_SIZE;
278	  the_history = (HIST_ENTRY **)xmalloc (history_size * sizeof (HIST_ENTRY *));
279	  history_length = 1;
280	}
281      else
282	{
283	  if (history_length == (history_size - 1))
284	    {
285	      history_size += DEFAULT_HISTORY_GROW_SIZE;
286	      the_history = (HIST_ENTRY **)
287		xrealloc (the_history, history_size * sizeof (HIST_ENTRY *));
288	    }
289	  history_length++;
290	}
291    }
292
293  temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
294  temp->line = savestring (string);
295  temp->data = (char *)NULL;
296
297  temp->timestamp = hist_inittime ();
298
299  the_history[history_length] = (HIST_ENTRY *)NULL;
300  the_history[history_length - 1] = temp;
301}
302
303/* Change the time stamp of the most recent history entry to STRING. */
304void
305add_history_time (string)
306     const char *string;
307{
308  HIST_ENTRY *hs;
309
310  hs = the_history[history_length - 1];
311  FREE (hs->timestamp);
312  hs->timestamp = savestring (string);
313}
314
315/* Free HIST and return the data so the calling application can free it
316   if necessary and desired. */
317histdata_t
318free_history_entry (hist)
319     HIST_ENTRY *hist;
320{
321  histdata_t x;
322
323  if (hist == 0)
324    return ((histdata_t) 0);
325  FREE (hist->line);
326  FREE (hist->timestamp);
327  x = hist->data;
328  free (hist);
329  return (x);
330}
331
332/* Make the history entry at WHICH have LINE and DATA.  This returns
333   the old entry so you can dispose of the data.  In the case of an
334   invalid WHICH, a NULL pointer is returned. */
335HIST_ENTRY *
336replace_history_entry (which, line, data)
337     int which;
338     const char *line;
339     histdata_t data;
340{
341  HIST_ENTRY *temp, *old_value;
342
343  if (which < 0 || which >= history_length)
344    return ((HIST_ENTRY *)NULL);
345
346  temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
347  old_value = the_history[which];
348
349  temp->line = savestring (line);
350  temp->data = data;
351  temp->timestamp = savestring (old_value->timestamp);
352  the_history[which] = temp;
353
354  return (old_value);
355}
356
357/* Remove history element WHICH from the history.  The removed
358   element is returned to you so you can free the line, data,
359   and containing structure. */
360HIST_ENTRY *
361remove_history (which)
362     int which;
363{
364  HIST_ENTRY *return_value;
365  register int i;
366
367  if (which < 0 || which >= history_length || history_length ==  0 || the_history == 0)
368    return ((HIST_ENTRY *)NULL);
369
370  return_value = the_history[which];
371
372  for (i = which; i < history_length; i++)
373    the_history[i] = the_history[i + 1];
374
375  history_length--;
376
377  return (return_value);
378}
379
380/* Stifle the history list, remembering only MAX number of lines. */
381void
382stifle_history (max)
383     int max;
384{
385  register int i, j;
386
387  if (max < 0)
388    max = 0;
389
390  if (history_length > max)
391    {
392      /* This loses because we cannot free the data. */
393      for (i = 0, j = history_length - max; i < j; i++)
394	free_history_entry (the_history[i]);
395
396      history_base = i;
397      for (j = 0, i = history_length - max; j < max; i++, j++)
398	the_history[j] = the_history[i];
399      the_history[j] = (HIST_ENTRY *)NULL;
400      history_length = j;
401    }
402
403  history_stifled = 1;
404  max_input_history = history_max_entries = max;
405}
406
407/* Stop stifling the history.  This returns the previous maximum
408   number of history entries.  The value is positive if the history
409   was stifled,  negative if it wasn't. */
410int
411unstifle_history ()
412{
413  if (history_stifled)
414    {
415      history_stifled = 0;
416      return (history_max_entries);
417    }
418  else
419    return (-history_max_entries);
420}
421
422int
423history_is_stifled ()
424{
425  return (history_stifled);
426}
427
428void
429clear_history ()
430{
431  register int i;
432
433  /* This loses because we cannot free the data. */
434  for (i = 0; i < history_length; i++)
435    {
436      free_history_entry (the_history[i]);
437      the_history[i] = (HIST_ENTRY *)NULL;
438    }
439
440  history_offset = history_length = 0;
441}
442