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