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.31 2005/08/01 14:34:06 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 struct hentry_t *next; /* Next entry */
120 struct hentry_t *prev; /* Previous entry */
123 typedef struct history_t {
124 hentry_t list; /* Fake list header element */
125 hentry_t *cursor; /* Current element in the list */
126 int max; /* Maximum number of events */
127 int cur; /* Current number of events */
128 int eventid; /* For generation of unique event id */
129 int flags; /* History flags */
130 #define H_UNIQUE 1 /* Store only unique elements */
133 private int history_def_next(ptr_t, HistEvent *);
134 private int history_def_first(ptr_t, HistEvent *);
135 private int history_def_prev(ptr_t, HistEvent *);
136 private int history_def_last(ptr_t, HistEvent *);
137 private int history_def_curr(ptr_t, HistEvent *);
138 private int history_def_set(ptr_t, HistEvent *, const int);
139 private void history_def_clear(ptr_t, HistEvent *);
140 private int history_def_enter(ptr_t, HistEvent *, const char *);
141 private int history_def_add(ptr_t, HistEvent *, const char *);
142 private int history_def_del(ptr_t, HistEvent *, const int);
144 private int history_def_init(ptr_t *, HistEvent *, int);
145 private int history_def_insert(history_t *, HistEvent *, const char *);
146 private void history_def_delete(history_t *, HistEvent *, hentry_t *);
148 #define history_def_setsize(p, num)(void) (((history_t *)p)->max = (num))
149 #define history_def_getsize(p) (((history_t *)p)->cur)
150 #define history_def_getunique(p) (((((history_t *)p)->flags) & H_UNIQUE) != 0)
151 #define history_def_setunique(p, uni) \
153 (((history_t *)p)->flags) |= H_UNIQUE; \
155 (((history_t *)p)->flags) &= ~H_UNIQUE
157 #define he_strerror(code) he_errlist[code]
158 #define he_seterrev(evp, code) {\
160 evp->str = he_strerror(code);\
164 static const char *const he_errlist[] = {
168 "first event not found",
169 "last event not found",
173 "current event is invalid",
175 "can't read history from file",
176 "can't write history",
177 "required parameter(s) not supplied",
178 "history size negative",
179 "function not allowed with other history-functions-set the default",
184 #define _HE_UNKNOWN 1
185 #define _HE_MALLOC_FAILED 2
186 #define _HE_FIRST_NOTFOUND 3
187 #define _HE_LAST_NOTFOUND 4
188 #define _HE_EMPTY_LIST 5
189 #define _HE_END_REACHED 6
190 #define _HE_START_REACHED 7
191 #define _HE_CURR_INVALID 8
192 #define _HE_NOT_FOUND 9
193 #define _HE_HIST_READ 10
194 #define _HE_HIST_WRITE 11
195 #define _HE_PARAM_MISSING 12
196 #define _HE_SIZE_NEGATIVE 13
197 #define _HE_NOT_ALLOWED 14
198 #define _HE_BAD_PARAM 15
200 /* history_def_first():
201 * Default function to return the first event in the history.
204 history_def_first(ptr_t p, HistEvent *ev)
206 history_t *h = (history_t *) p;
208 h->cursor = h->list.next;
209 if (h->cursor != &h->list)
212 he_seterrev(ev, _HE_FIRST_NOTFOUND);
220 /* history_def_last():
221 * Default function to return the last event in the history.
224 history_def_last(ptr_t p, HistEvent *ev)
226 history_t *h = (history_t *) p;
228 h->cursor = h->list.prev;
229 if (h->cursor != &h->list)
232 he_seterrev(ev, _HE_LAST_NOTFOUND);
240 /* history_def_next():
241 * Default function to return the next event in the history.
244 history_def_next(ptr_t p, HistEvent *ev)
246 history_t *h = (history_t *) p;
248 if (h->cursor == &h->list) {
249 he_seterrev(ev, _HE_EMPTY_LIST);
253 if (h->cursor->next == &h->list) {
254 he_seterrev(ev, _HE_END_REACHED);
258 h->cursor = h->cursor->next;
265 /* history_def_prev():
266 * Default function to return the previous event in the history.
269 history_def_prev(ptr_t p, HistEvent *ev)
271 history_t *h = (history_t *) p;
273 if (h->cursor == &h->list) {
275 (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
279 if (h->cursor->prev == &h->list) {
280 he_seterrev(ev, _HE_START_REACHED);
284 h->cursor = h->cursor->prev;
291 /* history_def_curr():
292 * Default function to return the current event in the history.
295 history_def_curr(ptr_t p, HistEvent *ev)
297 history_t *h = (history_t *) p;
299 if (h->cursor != &h->list)
303 (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
311 /* history_def_set():
312 * Default function to set the current event in the history to the
316 history_def_set(ptr_t p, HistEvent *ev, const int n)
318 history_t *h = (history_t *) p;
321 he_seterrev(ev, _HE_EMPTY_LIST);
324 if (h->cursor == &h->list || h->cursor->ev.num != n) {
325 for (h->cursor = h->list.next; h->cursor != &h->list;
326 h->cursor = h->cursor->next)
327 if (h->cursor->ev.num == n)
330 if (h->cursor == &h->list) {
331 he_seterrev(ev, _HE_NOT_FOUND);
338 /* history_def_add():
339 * Append string to element
342 history_def_add(ptr_t p, HistEvent *ev, const char *str)
344 history_t *h = (history_t *) p;
347 HistEventPrivate *evp = (void *)&h->cursor->ev;
349 if (h->cursor == &h->list)
350 return (history_def_enter(p, ev, str));
351 len = strlen(evp->str) + strlen(str) + 1;
352 s = (char *) h_malloc(len);
354 he_seterrev(ev, _HE_MALLOC_FAILED);
357 (void) strlcpy(s, h->cursor->ev.str, len);
358 (void) strlcat(s, str, len);
359 h_free((ptr_t)evp->str);
366 /* history_def_del():
367 * Delete element hp of the h list
371 history_def_del(ptr_t p, HistEvent *ev __unused,
374 history_t *h = (history_t *) p;
375 if (history_def_set(h, ev, num) != 0)
377 ev->str = strdup(h->cursor->ev.str);
378 ev->num = h->cursor->ev.num;
379 history_def_delete(h, ev, h->cursor);
384 /* history_def_delete():
385 * Delete element hp of the h list
389 history_def_delete(history_t *h,
390 HistEvent *ev __unused, hentry_t *hp)
392 HistEventPrivate *evp = (void *)&hp->ev;
396 h->cursor = hp->prev;
397 hp->prev->next = hp->next;
398 hp->next->prev = hp->prev;
399 h_free((ptr_t) evp->str);
405 /* history_def_insert():
406 * Insert element with string str in the h list
409 history_def_insert(history_t *h, HistEvent *ev, const char *str)
412 h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
413 if (h->cursor == NULL)
415 if ((h->cursor->ev.str = h_strdup(str)) == NULL) {
416 h_free((ptr_t)h->cursor);
419 h->cursor->ev.num = ++h->eventid;
420 h->cursor->next = h->list.next;
421 h->cursor->prev = &h->list;
422 h->list.next->prev = h->cursor;
423 h->list.next = h->cursor;
429 he_seterrev(ev, _HE_MALLOC_FAILED);
434 /* history_def_enter():
435 * Default function to enter an item in the history
438 history_def_enter(ptr_t p, HistEvent *ev, const char *str)
440 history_t *h = (history_t *) p;
442 if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
443 strcmp(h->list.next->ev.str, str) == 0)
446 if (history_def_insert(h, ev, str) == -1)
447 return (-1); /* error, keep error message */
450 * Always keep at least one entry.
451 * This way we don't have to check for the empty list.
453 while (h->cur > h->max && h->cur > 0)
454 history_def_delete(h, ev, h->list.prev);
460 /* history_def_init():
461 * Default history initialization function
465 history_def_init(ptr_t *p, HistEvent *ev __unused, int n)
467 history_t *h = (history_t *) h_malloc(sizeof(history_t));
476 h->list.next = h->list.prev = &h->list;
477 h->list.ev.str = NULL;
479 h->cursor = &h->list;
486 /* history_def_clear():
487 * Default history cleanup function
490 history_def_clear(ptr_t p, HistEvent *ev)
492 history_t *h = (history_t *) p;
494 while (h->list.prev != &h->list)
495 history_def_delete(h, ev, h->list.prev);
503 /************************************************************************/
506 * Initialization function.
512 History *h = (History *) h_malloc(sizeof(History));
516 if (history_def_init(&h->h_ref, &ev, 0) == -1) {
521 h->h_next = history_def_next;
522 h->h_first = history_def_first;
523 h->h_last = history_def_last;
524 h->h_prev = history_def_prev;
525 h->h_curr = history_def_curr;
526 h->h_set = history_def_set;
527 h->h_clear = history_def_clear;
528 h->h_enter = history_def_enter;
529 h->h_add = history_def_add;
530 h->h_del = history_def_del;
540 history_end(History *h)
544 if (h->h_next == history_def_next)
545 history_def_clear(h->h_ref, &ev);
551 /* history_setsize():
552 * Set history number of events
555 history_setsize(History *h, HistEvent *ev, int num)
558 if (h->h_next != history_def_next) {
559 he_seterrev(ev, _HE_NOT_ALLOWED);
563 he_seterrev(ev, _HE_BAD_PARAM);
566 history_def_setsize(h->h_ref, num);
571 /* history_getsize():
572 * Get number of events currently in history
575 history_getsize(History *h, HistEvent *ev)
577 if (h->h_next != history_def_next) {
578 he_seterrev(ev, _HE_NOT_ALLOWED);
581 ev->num = history_def_getsize(h->h_ref);
583 he_seterrev(ev, _HE_SIZE_NEGATIVE);
590 /* history_setunique():
591 * Set if adjacent equal events should not be entered in history.
594 history_setunique(History *h, HistEvent *ev, int uni)
597 if (h->h_next != history_def_next) {
598 he_seterrev(ev, _HE_NOT_ALLOWED);
601 history_def_setunique(h->h_ref, uni);
606 /* history_getunique():
607 * Get if adjacent equal events should not be entered in history.
610 history_getunique(History *h, HistEvent *ev)
612 if (h->h_next != history_def_next) {
613 he_seterrev(ev, _HE_NOT_ALLOWED);
616 ev->num = history_def_getunique(h->h_ref);
621 /* history_set_fun():
622 * Set history functions
625 history_set_fun(History *h, History *nh)
629 if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
630 nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
631 nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
632 nh->h_del == NULL || nh->h_ref == NULL) {
633 if (h->h_next != history_def_next) {
634 history_def_init(&h->h_ref, &ev, 0);
635 h->h_first = history_def_first;
636 h->h_next = history_def_next;
637 h->h_last = history_def_last;
638 h->h_prev = history_def_prev;
639 h->h_curr = history_def_curr;
640 h->h_set = history_def_set;
641 h->h_clear = history_def_clear;
642 h->h_enter = history_def_enter;
643 h->h_add = history_def_add;
644 h->h_del = history_def_del;
648 if (h->h_next == history_def_next)
649 history_def_clear(h->h_ref, &ev);
652 h->h_first = nh->h_first;
653 h->h_next = nh->h_next;
654 h->h_last = nh->h_last;
655 h->h_prev = nh->h_prev;
656 h->h_curr = nh->h_curr;
657 h->h_set = nh->h_set;
658 h->h_clear = nh->h_clear;
659 h->h_enter = nh->h_enter;
660 h->h_add = nh->h_add;
661 h->h_del = nh->h_del;
668 * History load function
671 history_load(History *h, const char *fname)
680 if ((fp = fopen(fname, "r")) == NULL)
683 if ((line = fgetln(fp, &sz)) == NULL)
686 if (strncmp(line, hist_cookie, sz) != 0)
689 ptr = h_malloc(max_size = 1024);
692 for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
695 if (sz != 0 && line[sz - 1] == '\n')
702 max_size = (sz + 1024) & ~1023;
703 nptr = h_realloc(ptr, max_size);
710 (void) strunvis(ptr, line);
712 if (HENTER(h, &ev, ptr) == -1) {
726 * History save function
729 history_save(History *h, const char *fname)
734 size_t len, max_size;
737 if ((fp = fopen(fname, "w")) == NULL)
740 if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
742 if (fputs(hist_cookie, fp) == EOF)
744 ptr = h_malloc(max_size = 1024);
747 for (i = 0, retval = HLAST(h, &ev);
749 retval = HPREV(h, &ev), i++) {
750 len = strlen(ev.str) * 4;
751 if (len >= max_size) {
753 max_size = (len + 1024) & ~1023;
754 nptr = h_realloc(ptr, max_size);
761 (void) strvis(ptr, ev.str, VIS_WHITE);
762 (void) fprintf(fp, "%s\n", ptr);
772 /* history_prev_event():
773 * Find the previous event, with number given
776 history_prev_event(History *h, HistEvent *ev, int num)
780 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
784 he_seterrev(ev, _HE_NOT_FOUND);
789 /* history_next_event():
790 * Find the next event, with number given
793 history_next_event(History *h, HistEvent *ev, int num)
797 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
801 he_seterrev(ev, _HE_NOT_FOUND);
806 /* history_prev_string():
807 * Find the previous event beginning with string
810 history_prev_string(History *h, HistEvent *ev, const char *str)
812 size_t len = strlen(str);
815 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
816 if (strncmp(str, ev->str, len) == 0)
819 he_seterrev(ev, _HE_NOT_FOUND);
824 /* history_next_string():
825 * Find the next event beginning with string
828 history_next_string(History *h, HistEvent *ev, const char *str)
830 size_t len = strlen(str);
833 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
834 if (strncmp(str, ev->str, len) == 0)
837 he_seterrev(ev, _HE_NOT_FOUND);
843 * User interface to history functions.
846 history(History *h, HistEvent *ev, int fun, ...)
854 he_seterrev(ev, _HE_OK);
858 retval = history_getsize(h, ev);
862 retval = history_setsize(h, ev, va_arg(va, int));
866 retval = history_getunique(h, ev);
870 retval = history_setunique(h, ev, va_arg(va, int));
874 str = va_arg(va, const char *);
875 retval = HADD(h, ev, str);
879 retval = HDEL(h, ev, va_arg(va, const int));
883 str = va_arg(va, const char *);
884 if ((retval = HENTER(h, ev, str)) != -1)
889 str = va_arg(va, const char *);
890 if ((retval = HSET(h, ev, h->h_ent)) != -1)
891 retval = HADD(h, ev, str);
895 retval = HFIRST(h, ev);
899 retval = HNEXT(h, ev);
903 retval = HLAST(h, ev);
907 retval = HPREV(h, ev);
911 retval = HCURR(h, ev);
915 retval = HSET(h, ev, va_arg(va, const int));
924 retval = history_load(h, va_arg(va, const char *));
926 he_seterrev(ev, _HE_HIST_READ);
930 retval = history_save(h, va_arg(va, const char *));
932 he_seterrev(ev, _HE_HIST_WRITE);
936 retval = history_prev_event(h, ev, va_arg(va, int));
940 retval = history_next_event(h, ev, va_arg(va, int));
944 retval = history_prev_string(h, ev, va_arg(va, const char *));
948 retval = history_next_string(h, ev, va_arg(va, const char *));
955 hf.h_ref = va_arg(va, ptr_t);
957 hf.h_first = va_arg(va, history_gfun_t);
958 hf.h_next = va_arg(va, history_gfun_t);
959 hf.h_last = va_arg(va, history_gfun_t);
960 hf.h_prev = va_arg(va, history_gfun_t);
961 hf.h_curr = va_arg(va, history_gfun_t);
962 hf.h_set = va_arg(va, history_sfun_t);
963 hf.h_clear = va_arg(va, history_vfun_t);
964 hf.h_enter = va_arg(va, history_efun_t);
965 hf.h_add = va_arg(va, history_efun_t);
966 hf.h_del = va_arg(va, history_sfun_t);
968 if ((retval = history_set_fun(h, &hf)) == -1)
969 he_seterrev(ev, _HE_PARAM_MISSING);
980 he_seterrev(ev, _HE_UNKNOWN);