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 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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_readline.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 | |
55 | static 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. */ |
64 | static 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. */ |
68 | static int history_stifled; |
69 | |
70 | /* The current number of slots allocated to the input_history. */ |
71 | static int history_size; |
72 | |
73 | /* If HISTORY_STIFLED is non-zero, then this is the maximum number of |
74 | entries to remember. */ |
75 | int history_max_entries; |
76 | int max_input_history; /* backwards compatibility */ |
77 | |
78 | /* The current location of the interactive history pointer. Just makes |
79 | life easier for outside callers. */ |
80 | int history_offset; |
81 | |
82 | /* The number of strings currently stored in the history list. */ |
83 | int history_length; |
84 | |
85 | /* The logical `base' of the history array. It defaults to 1. */ |
86 | int history_base = 1; |
87 | |
88 | /* Return the current HISTORY_STATE of the history. */ |
89 | HISTORY_STATE * |
90 | history_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. */ |
107 | void |
108 | history_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. */ |
121 | void |
122 | using_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. */ |
130 | int |
131 | history_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. */ |
143 | int |
144 | where_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. */ |
151 | int |
152 | history_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. */ |
164 | HIST_ENTRY ** |
165 | history_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. */ |
172 | HIST_ENTRY * |
173 | current_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. */ |
183 | HIST_ENTRY * |
184 | previous_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. */ |
192 | HIST_ENTRY * |
193 | next_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. */ |
200 | HIST_ENTRY * |
201 | history_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 | |
212 | HIST_ENTRY * |
213 | alloc_history_entry (string, ts) |
214 | const char *string; |
215 | char *ts; |
216 | { |
217 | HIST_ENTRY *temp; |
218 | |
219 | temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); |
220 | |
221 | temp->line = string ? savestring ((char*) string) : (char*) string; |
222 | temp->data = (char *)NULL; |
223 | temp->timestamp = ts; |
224 | |
225 | return temp; |
226 | } |
227 | |
228 | time_t |
229 | history_get_time (hist) |
230 | HIST_ENTRY *hist; |
231 | { |
232 | char *ts; |
233 | time_t t; |
234 | |
235 | if (hist == 0 || hist->timestamp == 0) |
236 | return 0; |
237 | ts = hist->timestamp; |
238 | if (ts[0] != history_comment_char) |
239 | return 0; |
240 | t = (time_t) atol (ts + 1); /* XXX - should use strtol() here */ |
241 | return t; |
242 | } |
243 | |
244 | static char * |
245 | hist_inittime () |
246 | { |
247 | time_t t; |
248 | char ts[64], *ret; |
249 | |
250 | t = (time_t) time ((time_t *)0); |
251 | #if defined (HAVE_VSNPRINTF) /* assume snprintf if vsnprintf exists */ |
252 | snprintf (ts, sizeof (ts) - 1, "X%lu" , (unsigned long) t); |
253 | #else |
254 | sprintf (ts, "X%lu" , (unsigned long) t); |
255 | #endif |
256 | ret = savestring (ts); |
257 | ret[0] = history_comment_char; |
258 | |
259 | return ret; |
260 | } |
261 | |
262 | /* Place STRING at the end of the history list. The data field |
263 | is set to NULL. */ |
264 | void |
265 | add_history (string) |
266 | const char *string; |
267 | { |
268 | HIST_ENTRY *temp; |
269 | |
270 | if (history_stifled && (history_length == history_max_entries)) |
271 | { |
272 | register int i; |
273 | |
274 | /* If the history is stifled, and history_length is zero, |
275 | and it equals history_max_entries, we don't save items. */ |
276 | if (history_length == 0) |
277 | return; |
278 | |
279 | /* If there is something in the slot, then remove it. */ |
280 | if (the_history[0]) |
281 | (void) free_history_entry (the_history[0]); |
282 | |
283 | /* Copy the rest of the entries, moving down one slot. */ |
284 | for (i = 0; i < history_length; i++) |
285 | the_history[i] = the_history[i + 1]; |
286 | |
287 | history_base++; |
288 | } |
289 | else |
290 | { |
291 | if (history_size == 0) |
292 | { |
293 | history_size = DEFAULT_HISTORY_GROW_SIZE; |
294 | the_history = (HIST_ENTRY **)xmalloc (history_size * sizeof (HIST_ENTRY *)); |
295 | history_length = 1; |
296 | } |
297 | else |
298 | { |
299 | if (history_length == (history_size - 1)) |
300 | { |
301 | history_size += DEFAULT_HISTORY_GROW_SIZE; |
302 | the_history = (HIST_ENTRY **) |
303 | xrealloc (the_history, history_size * sizeof (HIST_ENTRY *)); |
304 | } |
305 | history_length++; |
306 | } |
307 | } |
308 | |
309 | temp = alloc_history_entry ((char*) string, hist_inittime ()); |
310 | |
311 | the_history[history_length] = (HIST_ENTRY *)NULL; |
312 | the_history[history_length - 1] = temp; |
313 | } |
314 | |
315 | /* Change the time stamp of the most recent history entry to STRING. */ |
316 | void |
317 | add_history_time (string) |
318 | const char *string; |
319 | { |
320 | HIST_ENTRY *hs; |
321 | |
322 | hs = the_history[history_length - 1]; |
323 | FREE (hs->timestamp); |
324 | hs->timestamp = savestring (string); |
325 | } |
326 | |
327 | /* Free HIST and return the data so the calling application can free it |
328 | if necessary and desired. */ |
329 | histdata_t |
330 | free_history_entry (hist) |
331 | HIST_ENTRY *hist; |
332 | { |
333 | histdata_t x; |
334 | |
335 | if (hist == 0) |
336 | return ((histdata_t) 0); |
337 | FREE (hist->line); |
338 | FREE (hist->timestamp); |
339 | x = hist->data; |
340 | free (hist); |
341 | return (x); |
342 | } |
343 | |
344 | HIST_ENTRY * |
345 | copy_history_entry (hist) |
346 | HIST_ENTRY *hist; |
347 | { |
348 | HIST_ENTRY *ret; |
349 | char *ts; |
350 | |
351 | if (hist == 0) |
352 | return hist; |
353 | |
354 | ret = alloc_history_entry (hist->line, (char *)NULL); |
355 | |
356 | ts = hist->timestamp ? savestring (hist->timestamp) : hist->timestamp; |
357 | ret->timestamp = ts; |
358 | |
359 | ret->data = hist->data; |
360 | |
361 | return ret; |
362 | } |
363 | |
364 | /* Make the history entry at WHICH have LINE and DATA. This returns |
365 | the old entry so you can dispose of the data. In the case of an |
366 | invalid WHICH, a NULL pointer is returned. */ |
367 | HIST_ENTRY * |
368 | replace_history_entry (which, line, data) |
369 | int which; |
370 | const char *line; |
371 | histdata_t data; |
372 | { |
373 | HIST_ENTRY *temp, *old_value; |
374 | |
375 | if (which < 0 || which >= history_length) |
376 | return ((HIST_ENTRY *)NULL); |
377 | |
378 | temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY)); |
379 | old_value = the_history[which]; |
380 | |
381 | temp->line = savestring (line); |
382 | temp->data = data; |
383 | temp->timestamp = savestring (old_value->timestamp); |
384 | the_history[which] = temp; |
385 | |
386 | return (old_value); |
387 | } |
388 | |
389 | /* Replace the DATA in the specified history entries, replacing OLD with |
390 | NEW. WHICH says which one(s) to replace: WHICH == -1 means to replace |
391 | all of the history entries where entry->data == OLD; WHICH == -2 means |
392 | to replace the `newest' history entry where entry->data == OLD; and |
393 | WHICH >= 0 means to replace that particular history entry's data, as |
394 | long as it matches OLD. */ |
395 | void |
396 | replace_history_data (which,old, new) |
397 | int which; |
398 | histdata_t *old, *new; |
399 | { |
400 | HIST_ENTRY *entry; |
401 | register int i, last; |
402 | |
403 | if (which < -2 || which >= history_length || history_length == 0 || the_history == 0) |
404 | return; |
405 | |
406 | if (which >= 0) |
407 | { |
408 | entry = the_history[which]; |
409 | if (entry && entry->data == old) |
410 | entry->data = new; |
411 | return; |
412 | } |
413 | |
414 | last = -1; |
415 | for (i = 0; i < history_length; i++) |
416 | { |
417 | entry = the_history[i]; |
418 | if (entry == 0) |
419 | continue; |
420 | if (entry->data == old) |
421 | { |
422 | last = i; |
423 | if (which == -1) |
424 | entry->data = new; |
425 | } |
426 | } |
427 | if (which == -2 && last >= 0) |
428 | { |
429 | entry = the_history[last]; |
430 | entry->data = new; /* XXX - we don't check entry->old */ |
431 | } |
432 | } |
433 | |
434 | /* Remove history element WHICH from the history. The removed |
435 | element is returned to you so you can free the line, data, |
436 | and containing structure. */ |
437 | HIST_ENTRY * |
438 | remove_history (which) |
439 | int which; |
440 | { |
441 | HIST_ENTRY *return_value; |
442 | register int i; |
443 | |
444 | if (which < 0 || which >= history_length || history_length == 0 || the_history == 0) |
445 | return ((HIST_ENTRY *)NULL); |
446 | |
447 | return_value = the_history[which]; |
448 | |
449 | for (i = which; i < history_length; i++) |
450 | the_history[i] = the_history[i + 1]; |
451 | |
452 | history_length--; |
453 | |
454 | return (return_value); |
455 | } |
456 | |
457 | /* Stifle the history list, remembering only MAX number of lines. */ |
458 | void |
459 | stifle_history (max) |
460 | int max; |
461 | { |
462 | register int i, j; |
463 | |
464 | if (max < 0) |
465 | max = 0; |
466 | |
467 | if (history_length > max) |
468 | { |
469 | /* This loses because we cannot free the data. */ |
470 | for (i = 0, j = history_length - max; i < j; i++) |
471 | free_history_entry (the_history[i]); |
472 | |
473 | history_base = i; |
474 | for (j = 0, i = history_length - max; j < max; i++, j++) |
475 | the_history[j] = the_history[i]; |
476 | the_history[j] = (HIST_ENTRY *)NULL; |
477 | history_length = j; |
478 | } |
479 | |
480 | history_stifled = 1; |
481 | max_input_history = history_max_entries = max; |
482 | } |
483 | |
484 | /* Stop stifling the history. This returns the previous maximum |
485 | number of history entries. The value is positive if the history |
486 | was stifled, negative if it wasn't. */ |
487 | int |
488 | unstifle_history () |
489 | { |
490 | if (history_stifled) |
491 | { |
492 | history_stifled = 0; |
493 | return (history_max_entries); |
494 | } |
495 | else |
496 | return (-history_max_entries); |
497 | } |
498 | |
499 | int |
500 | history_is_stifled () |
501 | { |
502 | return (history_stifled); |
503 | } |
504 | |
505 | void |
506 | clear_history () |
507 | { |
508 | register int i; |
509 | |
510 | /* This loses because we cannot free the data. */ |
511 | for (i = 0; i < history_length; i++) |
512 | { |
513 | free_history_entry (the_history[i]); |
514 | the_history[i] = (HIST_ENTRY *)NULL; |
515 | } |
516 | |
517 | history_offset = history_length = 0; |
518 | } |
519 | |