2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Christos Zoulas of Cornell University.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * $NetBSD: history.c,v 1.34 2009/09/07 21:24:33 christos Exp $
35 #if !defined(lint) && !defined(SCCSID)
36 static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93";
37 #endif /* not lint && not SCCSID */
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
42 * hist.c: History access functions
52 static const char hist_cookie[] = "_HiStOrY_V2_\n";
56 typedef int (*history_gfun_t)(ptr_t, HistEvent *);
57 typedef int (*history_efun_t)(ptr_t, HistEvent *, const char *);
58 typedef void (*history_vfun_t)(ptr_t, HistEvent *);
59 typedef int (*history_sfun_t)(ptr_t, HistEvent *, const int);
62 ptr_t h_ref; /* Argument for history fcns */
63 int h_ent; /* Last entry point for history */
64 history_gfun_t h_first; /* Get the first element */
65 history_gfun_t h_next; /* Get the next element */
66 history_gfun_t h_last; /* Get the last element */
67 history_gfun_t h_prev; /* Get the previous element */
68 history_gfun_t h_curr; /* Get the current element */
69 history_sfun_t h_set; /* Set the current element */
70 history_sfun_t h_del; /* Set the given element */
71 history_vfun_t h_clear; /* Clear the history list */
72 history_efun_t h_enter; /* Add an element */
73 history_efun_t h_add; /* Append to an element */
76 #define HNEXT(h, ev) (*(h)->h_next)((h)->h_ref, ev)
77 #define HFIRST(h, ev) (*(h)->h_first)((h)->h_ref, ev)
78 #define HPREV(h, ev) (*(h)->h_prev)((h)->h_ref, ev)
79 #define HLAST(h, ev) (*(h)->h_last)((h)->h_ref, ev)
80 #define HCURR(h, ev) (*(h)->h_curr)((h)->h_ref, ev)
81 #define HSET(h, ev, n) (*(h)->h_set)((h)->h_ref, ev, n)
82 #define HCLEAR(h, ev) (*(h)->h_clear)((h)->h_ref, ev)
83 #define HENTER(h, ev, str) (*(h)->h_enter)((h)->h_ref, ev, str)
84 #define HADD(h, ev, str) (*(h)->h_add)((h)->h_ref, ev, str)
85 #define HDEL(h, ev, n) (*(h)->h_del)((h)->h_ref, ev, n)
87 #define h_strdup(a) strdup(a)
88 #define h_malloc(a) malloc(a)
89 #define h_realloc(a, b) realloc((a), (b))
90 #define h_free(a) free(a)
99 private int history_setsize(History *, HistEvent *, int);
100 private int history_getsize(History *, HistEvent *);
101 private int history_setunique(History *, HistEvent *, int);
102 private int history_getunique(History *, HistEvent *);
103 private int history_set_fun(History *, History *);
104 private int history_load(History *, const char *);
105 private int history_save(History *, const char *);
106 private int history_prev_event(History *, HistEvent *, int);
107 private int history_next_event(History *, HistEvent *, int);
108 private int history_next_string(History *, HistEvent *, const char *);
109 private int history_prev_string(History *, HistEvent *, const char *);
112 /***********************************************************************/
115 * Builtin- history implementation
117 typedef struct hentry_t {
118 HistEvent ev; /* What we return */
119 void *data; /* data */
120 struct hentry_t *next; /* Next entry */
121 struct hentry_t *prev; /* Previous entry */
124 typedef struct history_t {
125 hentry_t list; /* Fake list header element */
126 hentry_t *cursor; /* Current element in the list */
127 int max; /* Maximum number of events */
128 int cur; /* Current number of events */
129 int eventid; /* For generation of unique event id */
130 int flags; /* History flags */
131 #define H_UNIQUE 1 /* Store only unique elements */
134 private int history_def_next(ptr_t, HistEvent *);
135 private int history_def_first(ptr_t, HistEvent *);
136 private int history_def_prev(ptr_t, HistEvent *);
137 private int history_def_last(ptr_t, HistEvent *);
138 private int history_def_curr(ptr_t, HistEvent *);
139 private int history_def_set(ptr_t, HistEvent *, const int);
140 private void history_def_clear(ptr_t, HistEvent *);
141 private int history_def_enter(ptr_t, HistEvent *, const char *);
142 private int history_def_add(ptr_t, HistEvent *, const char *);
143 private int history_def_del(ptr_t, HistEvent *, const int);
145 private int history_def_init(ptr_t *, HistEvent *, int);
146 private int history_def_insert(history_t *, HistEvent *, const char *);
147 private void history_def_delete(history_t *, HistEvent *, hentry_t *);
149 private int history_deldata_nth(history_t *, HistEvent *, int, void **);
150 private int history_set_nth(ptr_t, HistEvent *, int);
152 #define history_def_setsize(p, num)(void) (((history_t *)p)->max = (num))
153 #define history_def_getsize(p) (((history_t *)p)->cur)
154 #define history_def_getunique(p) (((((history_t *)p)->flags) & H_UNIQUE) != 0)
155 #define history_def_setunique(p, uni) \
157 (((history_t *)p)->flags) |= H_UNIQUE; \
159 (((history_t *)p)->flags) &= ~H_UNIQUE
161 #define he_strerror(code) he_errlist[code]
162 #define he_seterrev(evp, code) {\
164 evp->str = he_strerror(code);\
168 static const char *const he_errlist[] = {
172 "first event not found",
173 "last event not found",
177 "current event is invalid",
179 "can't read history from file",
180 "can't write history",
181 "required parameter(s) not supplied",
182 "history size negative",
183 "function not allowed with other history-functions-set the default",
188 #define _HE_UNKNOWN 1
189 #define _HE_MALLOC_FAILED 2
190 #define _HE_FIRST_NOTFOUND 3
191 #define _HE_LAST_NOTFOUND 4
192 #define _HE_EMPTY_LIST 5
193 #define _HE_END_REACHED 6
194 #define _HE_START_REACHED 7
195 #define _HE_CURR_INVALID 8
196 #define _HE_NOT_FOUND 9
197 #define _HE_HIST_READ 10
198 #define _HE_HIST_WRITE 11
199 #define _HE_PARAM_MISSING 12
200 #define _HE_SIZE_NEGATIVE 13
201 #define _HE_NOT_ALLOWED 14
202 #define _HE_BAD_PARAM 15
204 /* history_def_first():
205 * Default function to return the first event in the history.
208 history_def_first(ptr_t p, HistEvent *ev)
210 history_t *h = (history_t *) p;
212 h->cursor = h->list.next;
213 if (h->cursor != &h->list)
216 he_seterrev(ev, _HE_FIRST_NOTFOUND);
224 /* history_def_last():
225 * Default function to return the last event in the history.
228 history_def_last(ptr_t p, HistEvent *ev)
230 history_t *h = (history_t *) p;
232 h->cursor = h->list.prev;
233 if (h->cursor != &h->list)
236 he_seterrev(ev, _HE_LAST_NOTFOUND);
244 /* history_def_next():
245 * Default function to return the next event in the history.
248 history_def_next(ptr_t p, HistEvent *ev)
250 history_t *h = (history_t *) p;
252 if (h->cursor == &h->list) {
253 he_seterrev(ev, _HE_EMPTY_LIST);
257 if (h->cursor->next == &h->list) {
258 he_seterrev(ev, _HE_END_REACHED);
262 h->cursor = h->cursor->next;
269 /* history_def_prev():
270 * Default function to return the previous event in the history.
273 history_def_prev(ptr_t p, HistEvent *ev)
275 history_t *h = (history_t *) p;
277 if (h->cursor == &h->list) {
279 (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
283 if (h->cursor->prev == &h->list) {
284 he_seterrev(ev, _HE_START_REACHED);
288 h->cursor = h->cursor->prev;
295 /* history_def_curr():
296 * Default function to return the current event in the history.
299 history_def_curr(ptr_t p, HistEvent *ev)
301 history_t *h = (history_t *) p;
303 if (h->cursor != &h->list)
307 (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
315 /* history_def_set():
316 * Default function to set the current event in the history to the
320 history_def_set(ptr_t p, HistEvent *ev, const int n)
322 history_t *h = (history_t *) p;
325 he_seterrev(ev, _HE_EMPTY_LIST);
328 if (h->cursor == &h->list || h->cursor->ev.num != n) {
329 for (h->cursor = h->list.next; h->cursor != &h->list;
330 h->cursor = h->cursor->next)
331 if (h->cursor->ev.num == n)
334 if (h->cursor == &h->list) {
335 he_seterrev(ev, _HE_NOT_FOUND);
342 /* history_set_nth():
343 * Default function to set the current event in the history to the
347 history_set_nth(ptr_t p, HistEvent *ev, int n)
349 history_t *h = (history_t *) p;
352 he_seterrev(ev, _HE_EMPTY_LIST);
355 for (h->cursor = h->list.prev; h->cursor != &h->list;
356 h->cursor = h->cursor->prev)
359 if (h->cursor == &h->list) {
360 he_seterrev(ev, _HE_NOT_FOUND);
367 /* history_def_add():
368 * Append string to element
371 history_def_add(ptr_t p, HistEvent *ev, const char *str)
373 history_t *h = (history_t *) p;
376 HistEventPrivate *evp = (void *)&h->cursor->ev;
378 if (h->cursor == &h->list)
379 return (history_def_enter(p, ev, str));
380 len = strlen(evp->str) + strlen(str) + 1;
381 s = (char *) h_malloc(len);
383 he_seterrev(ev, _HE_MALLOC_FAILED);
386 (void) strlcpy(s, h->cursor->ev.str, len);
387 (void) strlcat(s, str, len);
388 h_free((ptr_t)evp->str);
396 history_deldata_nth(history_t *h, HistEvent *ev,
397 int num, void **data)
399 if (history_set_nth(h, ev, num) != 0)
401 /* magic value to skip delete (just set to n-th history) */
402 if (data == (void **)-1)
404 ev->str = strdup(h->cursor->ev.str);
405 ev->num = h->cursor->ev.num;
407 *data = h->cursor->data;
408 history_def_delete(h, ev, h->cursor);
413 /* history_def_del():
414 * Delete element hp of the h list
418 history_def_del(ptr_t p, HistEvent *ev __unused,
421 history_t *h = (history_t *) p;
422 if (history_def_set(h, ev, num) != 0)
424 ev->str = strdup(h->cursor->ev.str);
425 ev->num = h->cursor->ev.num;
426 history_def_delete(h, ev, h->cursor);
431 /* history_def_delete():
432 * Delete element hp of the h list
436 history_def_delete(history_t *h,
437 HistEvent *ev __unused, hentry_t *hp)
439 HistEventPrivate *evp = (void *)&hp->ev;
442 if (h->cursor == hp) {
443 h->cursor = hp->prev;
444 if (h->cursor == &h->list)
445 h->cursor = hp->next;
447 hp->prev->next = hp->next;
448 hp->next->prev = hp->prev;
449 h_free((ptr_t) evp->str);
455 /* history_def_insert():
456 * Insert element with string str in the h list
459 history_def_insert(history_t *h, HistEvent *ev, const char *str)
462 h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
463 if (h->cursor == NULL)
465 if ((h->cursor->ev.str = h_strdup(str)) == NULL) {
466 h_free((ptr_t)h->cursor);
469 h->cursor->data = NULL;
470 h->cursor->ev.num = ++h->eventid;
471 h->cursor->next = h->list.next;
472 h->cursor->prev = &h->list;
473 h->list.next->prev = h->cursor;
474 h->list.next = h->cursor;
480 he_seterrev(ev, _HE_MALLOC_FAILED);
485 /* history_def_enter():
486 * Default function to enter an item in the history
489 history_def_enter(ptr_t p, HistEvent *ev, const char *str)
491 history_t *h = (history_t *) p;
493 if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
494 strcmp(h->list.next->ev.str, str) == 0)
497 if (history_def_insert(h, ev, str) == -1)
498 return (-1); /* error, keep error message */
501 * Always keep at least one entry.
502 * This way we don't have to check for the empty list.
504 while (h->cur > h->max && h->cur > 0)
505 history_def_delete(h, ev, h->list.prev);
511 /* history_def_init():
512 * Default history initialization function
516 history_def_init(ptr_t *p, HistEvent *ev __unused, int n)
518 history_t *h = (history_t *) h_malloc(sizeof(history_t));
527 h->list.next = h->list.prev = &h->list;
528 h->list.ev.str = NULL;
530 h->cursor = &h->list;
537 /* history_def_clear():
538 * Default history cleanup function
541 history_def_clear(ptr_t p, HistEvent *ev)
543 history_t *h = (history_t *) p;
545 while (h->list.prev != &h->list)
546 history_def_delete(h, ev, h->list.prev);
554 /************************************************************************/
557 * Initialization function.
563 History *h = (History *) h_malloc(sizeof(History));
567 if (history_def_init(&h->h_ref, &ev, 0) == -1) {
572 h->h_next = history_def_next;
573 h->h_first = history_def_first;
574 h->h_last = history_def_last;
575 h->h_prev = history_def_prev;
576 h->h_curr = history_def_curr;
577 h->h_set = history_def_set;
578 h->h_clear = history_def_clear;
579 h->h_enter = history_def_enter;
580 h->h_add = history_def_add;
581 h->h_del = history_def_del;
591 history_end(History *h)
595 if (h->h_next == history_def_next)
596 history_def_clear(h->h_ref, &ev);
603 /* history_setsize():
604 * Set history number of events
607 history_setsize(History *h, HistEvent *ev, int num)
610 if (h->h_next != history_def_next) {
611 he_seterrev(ev, _HE_NOT_ALLOWED);
615 he_seterrev(ev, _HE_BAD_PARAM);
618 history_def_setsize(h->h_ref, num);
623 /* history_getsize():
624 * Get number of events currently in history
627 history_getsize(History *h, HistEvent *ev)
629 if (h->h_next != history_def_next) {
630 he_seterrev(ev, _HE_NOT_ALLOWED);
633 ev->num = history_def_getsize(h->h_ref);
635 he_seterrev(ev, _HE_SIZE_NEGATIVE);
642 /* history_setunique():
643 * Set if adjacent equal events should not be entered in history.
646 history_setunique(History *h, HistEvent *ev, int uni)
649 if (h->h_next != history_def_next) {
650 he_seterrev(ev, _HE_NOT_ALLOWED);
653 history_def_setunique(h->h_ref, uni);
658 /* history_getunique():
659 * Get if adjacent equal events should not be entered in history.
662 history_getunique(History *h, HistEvent *ev)
664 if (h->h_next != history_def_next) {
665 he_seterrev(ev, _HE_NOT_ALLOWED);
668 ev->num = history_def_getunique(h->h_ref);
673 /* history_set_fun():
674 * Set history functions
677 history_set_fun(History *h, History *nh)
681 if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
682 nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
683 nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
684 nh->h_del == NULL || nh->h_ref == NULL) {
685 if (h->h_next != history_def_next) {
686 history_def_init(&h->h_ref, &ev, 0);
687 h->h_first = history_def_first;
688 h->h_next = history_def_next;
689 h->h_last = history_def_last;
690 h->h_prev = history_def_prev;
691 h->h_curr = history_def_curr;
692 h->h_set = history_def_set;
693 h->h_clear = history_def_clear;
694 h->h_enter = history_def_enter;
695 h->h_add = history_def_add;
696 h->h_del = history_def_del;
700 if (h->h_next == history_def_next)
701 history_def_clear(h->h_ref, &ev);
704 h->h_first = nh->h_first;
705 h->h_next = nh->h_next;
706 h->h_last = nh->h_last;
707 h->h_prev = nh->h_prev;
708 h->h_curr = nh->h_curr;
709 h->h_set = nh->h_set;
710 h->h_clear = nh->h_clear;
711 h->h_enter = nh->h_enter;
712 h->h_add = nh->h_add;
713 h->h_del = nh->h_del;
720 * History load function
723 history_load(History *h, const char *fname)
732 if ((fp = fopen(fname, "r")) == NULL)
735 if ((line = fgetln(fp, &sz)) == NULL)
738 if (strncmp(line, hist_cookie, sz) != 0)
741 ptr = h_malloc(max_size = 1024);
744 for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
747 if (sz != 0 && line[sz - 1] == '\n')
754 max_size = (sz + 1024) & ~1023;
755 nptr = h_realloc(ptr, max_size);
762 (void) strunvis(ptr, line);
764 if (HENTER(h, &ev, ptr) == -1) {
778 * History save function
781 history_save(History *h, const char *fname)
786 size_t len, max_size;
789 if ((fp = fopen(fname, "w")) == NULL)
792 if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
794 if (fputs(hist_cookie, fp) == EOF)
796 ptr = h_malloc(max_size = 1024);
799 for (i = 0, retval = HLAST(h, &ev);
801 retval = HPREV(h, &ev), i++) {
802 len = strlen(ev.str) * 4;
803 if (len >= max_size) {
805 max_size = (len + 1024) & ~1023;
806 nptr = h_realloc(ptr, max_size);
813 (void) strvis(ptr, ev.str, VIS_WHITE);
814 (void) fprintf(fp, "%s\n", ptr);
824 /* history_prev_event():
825 * Find the previous event, with number given
828 history_prev_event(History *h, HistEvent *ev, int num)
832 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
836 he_seterrev(ev, _HE_NOT_FOUND);
842 history_next_evdata(History *h, HistEvent *ev, int num, void **d)
846 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
849 *d = ((history_t *)h->h_ref)->cursor->data;
853 he_seterrev(ev, _HE_NOT_FOUND);
858 /* history_next_event():
859 * Find the next event, with number given
862 history_next_event(History *h, HistEvent *ev, int num)
866 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
870 he_seterrev(ev, _HE_NOT_FOUND);
875 /* history_prev_string():
876 * Find the previous event beginning with string
879 history_prev_string(History *h, HistEvent *ev, const char *str)
881 size_t len = strlen(str);
884 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
885 if (strncmp(str, ev->str, len) == 0)
888 he_seterrev(ev, _HE_NOT_FOUND);
893 /* history_next_string():
894 * Find the next event beginning with string
897 history_next_string(History *h, HistEvent *ev, const char *str)
899 size_t len = strlen(str);
902 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
903 if (strncmp(str, ev->str, len) == 0)
906 he_seterrev(ev, _HE_NOT_FOUND);
912 * User interface to history functions.
915 history(History *h, HistEvent *ev, int fun, ...)
923 he_seterrev(ev, _HE_OK);
927 retval = history_getsize(h, ev);
931 retval = history_setsize(h, ev, va_arg(va, int));
935 retval = history_getunique(h, ev);
939 retval = history_setunique(h, ev, va_arg(va, int));
943 str = va_arg(va, const char *);
944 retval = HADD(h, ev, str);
948 retval = HDEL(h, ev, va_arg(va, const int));
952 str = va_arg(va, const char *);
953 if ((retval = HENTER(h, ev, str)) != -1)
958 str = va_arg(va, const char *);
959 if ((retval = HSET(h, ev, h->h_ent)) != -1)
960 retval = HADD(h, ev, str);
964 retval = HFIRST(h, ev);
968 retval = HNEXT(h, ev);
972 retval = HLAST(h, ev);
976 retval = HPREV(h, ev);
980 retval = HCURR(h, ev);
984 retval = HSET(h, ev, va_arg(va, const int));
993 retval = history_load(h, va_arg(va, const char *));
995 he_seterrev(ev, _HE_HIST_READ);
999 retval = history_save(h, va_arg(va, const char *));
1001 he_seterrev(ev, _HE_HIST_WRITE);
1005 retval = history_prev_event(h, ev, va_arg(va, int));
1009 retval = history_next_event(h, ev, va_arg(va, int));
1013 retval = history_prev_string(h, ev, va_arg(va, const char *));
1017 retval = history_next_string(h, ev, va_arg(va, const char *));
1024 hf.h_ref = va_arg(va, ptr_t);
1026 hf.h_first = va_arg(va, history_gfun_t);
1027 hf.h_next = va_arg(va, history_gfun_t);
1028 hf.h_last = va_arg(va, history_gfun_t);
1029 hf.h_prev = va_arg(va, history_gfun_t);
1030 hf.h_curr = va_arg(va, history_gfun_t);
1031 hf.h_set = va_arg(va, history_sfun_t);
1032 hf.h_clear = va_arg(va, history_vfun_t);
1033 hf.h_enter = va_arg(va, history_efun_t);
1034 hf.h_add = va_arg(va, history_efun_t);
1035 hf.h_del = va_arg(va, history_sfun_t);
1037 if ((retval = history_set_fun(h, &hf)) == -1)
1038 he_seterrev(ev, _HE_PARAM_MISSING);
1049 int num = va_arg(va, int);
1050 void **d = va_arg(va, void **);
1051 retval = history_next_evdata(h, ev, num, d);
1057 int num = va_arg(va, int);
1058 void **d = va_arg(va, void **);
1059 retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d);
1063 case H_REPLACE: /* only use after H_NEXT_EVDATA */
1065 const char *line = va_arg(va, const char *);
1066 void *d = va_arg(va, void *);
1068 if(!line || !(s = strdup(line))) {
1072 ((history_t *)h->h_ref)->cursor->ev.str = s;
1073 ((history_t *)h->h_ref)->cursor->data = d;
1080 he_seterrev(ev, _HE_UNKNOWN);