1 /* $NetBSD: history.c,v 1.19 2002/03/18 16:00:54 christos Exp $ */
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Christos Zoulas of Cornell University.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 #if !defined(lint) && !defined(SCCSID)
42 static char sccsid
[] = "@(#)history.c 8.1 (Berkeley) 6/4/93";
44 __RCSID("$NetBSD: history.c,v 1.19 2002/03/18 16:00:54 christos Exp $");
46 #endif /* not lint && not SCCSID */
49 * hist.c: History access functions
61 static const char hist_cookie
[] = "_HiStOrY_V2_\n";
65 typedef int (*history_gfun_t
)(ptr_t
, HistEvent
*);
66 typedef int (*history_efun_t
)(ptr_t
, HistEvent
*, const char *);
67 typedef void (*history_vfun_t
)(ptr_t
, HistEvent
*);
68 typedef int (*history_sfun_t
)(ptr_t
, HistEvent
*, const int);
71 ptr_t h_ref
; /* Argument for history fcns */
72 int h_ent
; /* Last entry point for history */
73 history_gfun_t h_first
; /* Get the first element */
74 history_gfun_t h_next
; /* Get the next element */
75 history_gfun_t h_last
; /* Get the last element */
76 history_gfun_t h_prev
; /* Get the previous element */
77 history_gfun_t h_curr
; /* Get the current element */
78 history_sfun_t h_set
; /* Set the current element */
79 history_vfun_t h_clear
; /* Clear the history list */
80 history_efun_t h_enter
; /* Add an element */
81 history_efun_t h_add
; /* Append to an element */
83 #define HNEXT(h, ev) (*(h)->h_next)((h)->h_ref, ev)
84 #define HFIRST(h, ev) (*(h)->h_first)((h)->h_ref, ev)
85 #define HPREV(h, ev) (*(h)->h_prev)((h)->h_ref, ev)
86 #define HLAST(h, ev) (*(h)->h_last)((h)->h_ref, ev)
87 #define HCURR(h, ev) (*(h)->h_curr)((h)->h_ref, ev)
88 #define HSET(h, ev, n) (*(h)->h_set)((h)->h_ref, ev, n)
89 #define HCLEAR(h, ev) (*(h)->h_clear)((h)->h_ref, ev)
90 #define HENTER(h, ev, str) (*(h)->h_enter)((h)->h_ref, ev, str)
91 #define HADD(h, ev, str) (*(h)->h_add)((h)->h_ref, ev, str)
93 #define h_malloc(a) malloc(a)
94 #define h_realloc(a, b) realloc((a), (b))
95 #define h_free(a) free(a)
104 private int history_setsize(History
*, HistEvent
*, int);
105 private int history_getsize(History
*, HistEvent
*);
106 private int history_set_fun(History
*, History
*);
107 private int history_load(History
*, const char *);
108 private int history_save(History
*, const char *);
109 private int history_prev_event(History
*, HistEvent
*, int);
110 private int history_next_event(History
*, HistEvent
*, int);
111 private int history_next_string(History
*, HistEvent
*, const char *);
112 private int history_prev_string(History
*, HistEvent
*, const char *);
115 /***********************************************************************/
118 * Builtin- history implementation
120 typedef struct hentry_t
{
121 HistEvent ev
; /* What we return */
122 struct hentry_t
*next
; /* Next entry */
123 struct hentry_t
*prev
; /* Previous entry */
126 typedef struct history_t
{
127 hentry_t list
; /* Fake list header element */
128 hentry_t
*cursor
; /* Current element in the list */
129 int max
; /* Maximum number of events */
130 int cur
; /* Current number of events */
131 int eventid
; /* For generation of unique event id */
134 private int history_def_first(ptr_t
, HistEvent
*);
135 private int history_def_last(ptr_t
, HistEvent
*);
136 private int history_def_next(ptr_t
, HistEvent
*);
137 private int history_def_prev(ptr_t
, HistEvent
*);
138 private int history_def_curr(ptr_t
, HistEvent
*);
139 private int history_def_set(ptr_t
, HistEvent
*, const int n
);
140 private int history_def_enter(ptr_t
, HistEvent
*, const char *);
141 private int history_def_add(ptr_t
, HistEvent
*, const char *);
142 private void history_def_init(ptr_t
*, HistEvent
*, int);
143 private void history_def_clear(ptr_t
, HistEvent
*);
144 private int history_def_insert(history_t
*, HistEvent
*, const char *);
145 private void history_def_delete(history_t
*, HistEvent
*, hentry_t
*);
147 #define history_def_setsize(p, num)(void) (((history_t *) p)->max = (num))
148 #define history_def_getsize(p) (((history_t *) p)->cur)
150 #define he_strerror(code) he_errlist[code]
151 #define he_seterrev(evp, code) {\
153 evp->str = he_strerror(code);\
157 static const char *const he_errlist
[] = {
161 "first event not found",
162 "last event not found",
166 "current event is invalid",
168 "can't read history from file",
169 "can't write history",
170 "required parameter(s) not supplied",
171 "history size negative",
172 "function not allowed with other history-functions-set the default",
177 #define _HE_UNKNOWN 1
178 #define _HE_MALLOC_FAILED 2
179 #define _HE_FIRST_NOTFOUND 3
180 #define _HE_LAST_NOTFOUND 4
181 #define _HE_EMPTY_LIST 5
182 #define _HE_END_REACHED 6
183 #define _HE_START_REACHED 7
184 #define _HE_CURR_INVALID 8
185 #define _HE_NOT_FOUND 9
186 #define _HE_HIST_READ 10
187 #define _HE_HIST_WRITE 11
188 #define _HE_PARAM_MISSING 12
189 #define _HE_SIZE_NEGATIVE 13
190 #define _HE_NOT_ALLOWED 14
191 #define _HE_BAD_PARAM 15
193 /* history_def_first():
194 * Default function to return the first event in the history.
197 history_def_first(ptr_t p
, HistEvent
*ev
)
199 history_t
*h
= (history_t
*) p
;
201 h
->cursor
= h
->list
.next
;
202 if (h
->cursor
!= &h
->list
)
205 he_seterrev(ev
, _HE_FIRST_NOTFOUND
);
213 /* history_def_last():
214 * Default function to return the last event in the history.
217 history_def_last(ptr_t p
, HistEvent
*ev
)
219 history_t
*h
= (history_t
*) p
;
221 h
->cursor
= h
->list
.prev
;
222 if (h
->cursor
!= &h
->list
)
225 he_seterrev(ev
, _HE_LAST_NOTFOUND
);
233 /* history_def_next():
234 * Default function to return the next event in the history.
237 history_def_next(ptr_t p
, HistEvent
*ev
)
239 history_t
*h
= (history_t
*) p
;
241 if (h
->cursor
!= &h
->list
)
242 h
->cursor
= h
->cursor
->next
;
244 he_seterrev(ev
, _HE_EMPTY_LIST
);
248 if (h
->cursor
!= &h
->list
)
251 he_seterrev(ev
, _HE_END_REACHED
);
259 /* history_def_prev():
260 * Default function to return the previous event in the history.
263 history_def_prev(ptr_t p
, HistEvent
*ev
)
265 history_t
*h
= (history_t
*) p
;
267 if (h
->cursor
!= &h
->list
)
268 h
->cursor
= h
->cursor
->prev
;
271 (h
->cur
> 0) ? _HE_END_REACHED
: _HE_EMPTY_LIST
);
275 if (h
->cursor
!= &h
->list
)
278 he_seterrev(ev
, _HE_START_REACHED
);
286 /* history_def_curr():
287 * Default function to return the current event in the history.
290 history_def_curr(ptr_t p
, HistEvent
*ev
)
292 history_t
*h
= (history_t
*) p
;
294 if (h
->cursor
!= &h
->list
)
298 (h
->cur
> 0) ? _HE_CURR_INVALID
: _HE_EMPTY_LIST
);
306 /* history_def_set():
307 * Default function to set the current event in the history to the
311 history_def_set(ptr_t p
, HistEvent
*ev
, const int n
)
313 history_t
*h
= (history_t
*) p
;
316 he_seterrev(ev
, _HE_EMPTY_LIST
);
319 if (h
->cursor
== &h
->list
|| h
->cursor
->ev
.num
!= n
) {
320 for (h
->cursor
= h
->list
.next
; h
->cursor
!= &h
->list
;
321 h
->cursor
= h
->cursor
->next
)
322 if (h
->cursor
->ev
.num
== n
)
325 if (h
->cursor
== &h
->list
) {
326 he_seterrev(ev
, _HE_NOT_FOUND
);
333 /* history_def_add():
334 * Append string to element
337 history_def_add(ptr_t p
, HistEvent
*ev
, const char *str
)
339 history_t
*h
= (history_t
*) p
;
342 HistEventPrivate
*evp
= (void *)&h
->cursor
->ev
;
344 if (h
->cursor
== &h
->list
)
345 return (history_def_enter(p
, ev
, str
));
346 len
= strlen(evp
->str
) + strlen(str
) + 1;
347 s
= (char *) h_malloc(len
);
349 he_seterrev(ev
, _HE_MALLOC_FAILED
);
352 (void) strlcpy(s
, h
->cursor
->ev
.str
, len
);
353 (void) strlcat(s
, str
, len
);
361 /* history_def_delete():
362 * Delete element hp of the h list
366 history_def_delete(history_t
*h
, HistEvent
*ev
, hentry_t
*hp
)
368 HistEventPrivate
*evp
= (void *)&hp
->ev
;
371 hp
->prev
->next
= hp
->next
;
372 hp
->next
->prev
= hp
->prev
;
373 h_free((ptr_t
) evp
->str
);
379 /* history_def_insert():
380 * Insert element with string str in the h list
383 history_def_insert(history_t
*h
, HistEvent
*ev
, const char *str
)
386 h
->cursor
= (hentry_t
*) h_malloc(sizeof(hentry_t
));
388 h
->cursor
->ev
.str
= strdup(str
);
389 if (!h
->cursor
|| !h
->cursor
->ev
.str
) {
390 he_seterrev(ev
, _HE_MALLOC_FAILED
);
393 h
->cursor
->ev
.num
= ++h
->eventid
;
394 h
->cursor
->next
= h
->list
.next
;
395 h
->cursor
->prev
= &h
->list
;
396 h
->list
.next
->prev
= h
->cursor
;
397 h
->list
.next
= h
->cursor
;
405 /* history_def_enter():
406 * Default function to enter an item in the history
409 history_def_enter(ptr_t p
, HistEvent
*ev
, const char *str
)
411 history_t
*h
= (history_t
*) p
;
413 if (history_def_insert(h
, ev
, str
) == -1)
414 return (-1); /* error, keep error message */
417 * Always keep at least one entry.
418 * This way we don't have to check for the empty list.
420 while (h
->cur
> h
->max
&& h
->cur
> 0)
421 history_def_delete(h
, ev
, h
->list
.prev
);
427 /* history_def_init():
428 * Default history initialization function
432 history_def_init(ptr_t
*p
, HistEvent
*ev
, int n
)
434 history_t
*h
= (history_t
*) h_malloc(sizeof(history_t
));
441 h
->list
.next
= h
->list
.prev
= &h
->list
;
442 h
->list
.ev
.str
= NULL
;
444 h
->cursor
= &h
->list
;
449 /* history_def_clear():
450 * Default history cleanup function
453 history_def_clear(ptr_t p
, HistEvent
*ev
)
455 history_t
*h
= (history_t
*) p
;
457 while (h
->list
.prev
!= &h
->list
)
458 history_def_delete(h
, ev
, h
->list
.prev
);
466 /************************************************************************/
469 * Initialization function.
474 History
*h
= (History
*) h_malloc(sizeof(History
));
477 history_def_init(&h
->h_ref
, &ev
, 0);
479 h
->h_next
= history_def_next
;
480 h
->h_first
= history_def_first
;
481 h
->h_last
= history_def_last
;
482 h
->h_prev
= history_def_prev
;
483 h
->h_curr
= history_def_curr
;
484 h
->h_set
= history_def_set
;
485 h
->h_clear
= history_def_clear
;
486 h
->h_enter
= history_def_enter
;
487 h
->h_add
= history_def_add
;
497 history_end(History
*h
)
501 if (h
->h_next
== history_def_next
)
502 history_def_clear(h
->h_ref
, &ev
);
507 /* history_setsize():
508 * Set history number of events
511 history_setsize(History
*h
, HistEvent
*ev
, int num
)
514 if (h
->h_next
!= history_def_next
) {
515 he_seterrev(ev
, _HE_NOT_ALLOWED
);
519 he_seterrev(ev
, _HE_BAD_PARAM
);
522 history_def_setsize(h
->h_ref
, num
);
527 /* history_getsize():
528 * Get number of events currently in history
531 history_getsize(History
*h
, HistEvent
*ev
)
535 if (h
->h_next
!= history_def_next
) {
536 he_seterrev(ev
, _HE_NOT_ALLOWED
);
539 retval
= history_def_getsize(h
->h_ref
);
541 he_seterrev(ev
, _HE_SIZE_NEGATIVE
);
549 /* history_set_fun():
550 * Set history functions
553 history_set_fun(History
*h
, History
*nh
)
557 if (nh
->h_first
== NULL
|| nh
->h_next
== NULL
|| nh
->h_last
== NULL
||
558 nh
->h_prev
== NULL
|| nh
->h_curr
== NULL
|| nh
->h_set
== NULL
||
559 nh
->h_enter
== NULL
|| nh
->h_add
== NULL
|| nh
->h_clear
== NULL
||
561 if (h
->h_next
!= history_def_next
) {
562 history_def_init(&h
->h_ref
, &ev
, 0);
563 h
->h_first
= history_def_first
;
564 h
->h_next
= history_def_next
;
565 h
->h_last
= history_def_last
;
566 h
->h_prev
= history_def_prev
;
567 h
->h_curr
= history_def_curr
;
568 h
->h_set
= history_def_set
;
569 h
->h_clear
= history_def_clear
;
570 h
->h_enter
= history_def_enter
;
571 h
->h_add
= history_def_add
;
575 if (h
->h_next
== history_def_next
)
576 history_def_clear(h
->h_ref
, &ev
);
579 h
->h_first
= nh
->h_first
;
580 h
->h_next
= nh
->h_next
;
581 h
->h_last
= nh
->h_last
;
582 h
->h_prev
= nh
->h_prev
;
583 h
->h_curr
= nh
->h_curr
;
584 h
->h_set
= nh
->h_set
;
585 h
->h_clear
= nh
->h_clear
;
586 h
->h_enter
= nh
->h_enter
;
587 h
->h_add
= nh
->h_add
;
594 * History load function
597 history_load(History
*h
, const char *fname
)
606 if ((fp
= fopen(fname
, "r")) == NULL
)
609 if ((line
= fgetln(fp
, &sz
)) == NULL
)
612 if (strncmp(line
, hist_cookie
, sz
) != 0)
615 ptr
= h_malloc(max_size
= 1024);
616 for (i
= 0; (line
= fgetln(fp
, &sz
)) != NULL
; i
++) {
619 if (sz
!= 0 && line
[sz
- 1] == '\n')
625 max_size
= (sz
+ 1023) & ~1023;
626 ptr
= h_realloc(ptr
, max_size
);
628 (void) strunvis(ptr
, line
);
641 * History save function
644 history_save(History
*h
, const char *fname
)
649 size_t len
, max_size
;
652 if ((fp
= fopen(fname
, "w")) == NULL
)
655 (void) fchmod(fileno(fp
), S_IRUSR
|S_IWUSR
);
656 (void) fputs(hist_cookie
, fp
);
657 ptr
= h_malloc(max_size
= 1024);
658 for (retval
= HLAST(h
, &ev
);
660 retval
= HPREV(h
, &ev
), i
++) {
661 len
= strlen(ev
.str
) * 4;
662 if (len
>= max_size
) {
663 max_size
= (len
+ 1023) & 1023;
664 ptr
= h_realloc(ptr
, max_size
);
666 (void) strvis(ptr
, ev
.str
, VIS_WHITE
);
667 (void) fprintf(fp
, "%s\n", ev
.str
);
675 /* history_prev_event():
676 * Find the previous event, with number given
679 history_prev_event(History
*h
, HistEvent
*ev
, int num
)
683 for (retval
= HCURR(h
, ev
); retval
!= -1; retval
= HPREV(h
, ev
))
687 he_seterrev(ev
, _HE_NOT_FOUND
);
692 /* history_next_event():
693 * Find the next event, with number given
696 history_next_event(History
*h
, HistEvent
*ev
, int num
)
700 for (retval
= HCURR(h
, ev
); retval
!= -1; retval
= HNEXT(h
, ev
))
704 he_seterrev(ev
, _HE_NOT_FOUND
);
709 /* history_prev_string():
710 * Find the previous event beginning with string
713 history_prev_string(History
*h
, HistEvent
*ev
, const char *str
)
715 size_t len
= strlen(str
);
718 for (retval
= HCURR(h
, ev
); retval
!= -1; retval
= HNEXT(h
, ev
))
719 if (strncmp(str
, ev
->str
, len
) == 0)
722 he_seterrev(ev
, _HE_NOT_FOUND
);
727 /* history_next_string():
728 * Find the next event beginning with string
731 history_next_string(History
*h
, HistEvent
*ev
, const char *str
)
733 size_t len
= strlen(str
);
736 for (retval
= HCURR(h
, ev
); retval
!= -1; retval
= HPREV(h
, ev
))
737 if (strncmp(str
, ev
->str
, len
) == 0)
740 he_seterrev(ev
, _HE_NOT_FOUND
);
746 * User interface to history functions.
749 history(History
*h
, HistEvent
*ev
, int fun
, ...)
757 he_seterrev(ev
, _HE_OK
);
761 retval
= history_getsize(h
, ev
);
765 retval
= history_setsize(h
, ev
, va_arg(va
, int));
769 str
= va_arg(va
, const char *);
770 retval
= HADD(h
, ev
, str
);
774 str
= va_arg(va
, const char *);
775 if ((retval
= HENTER(h
, ev
, str
)) != -1)
780 str
= va_arg(va
, const char *);
781 if ((retval
= HSET(h
, ev
, h
->h_ent
)) != -1)
782 retval
= HADD(h
, ev
, str
);
786 retval
= HFIRST(h
, ev
);
790 retval
= HNEXT(h
, ev
);
794 retval
= HLAST(h
, ev
);
798 retval
= HPREV(h
, ev
);
802 retval
= HCURR(h
, ev
);
806 retval
= HSET(h
, ev
, va_arg(va
, const int));
815 retval
= history_load(h
, va_arg(va
, const char *));
817 he_seterrev(ev
, _HE_HIST_READ
);
821 retval
= history_save(h
, va_arg(va
, const char *));
823 he_seterrev(ev
, _HE_HIST_WRITE
);
827 retval
= history_prev_event(h
, ev
, va_arg(va
, int));
831 retval
= history_next_event(h
, ev
, va_arg(va
, int));
835 retval
= history_prev_string(h
, ev
, va_arg(va
, const char *));
839 retval
= history_next_string(h
, ev
, va_arg(va
, const char *));
846 hf
.h_ref
= va_arg(va
, ptr_t
);
848 hf
.h_first
= va_arg(va
, history_gfun_t
);
849 hf
.h_next
= va_arg(va
, history_gfun_t
);
850 hf
.h_last
= va_arg(va
, history_gfun_t
);
851 hf
.h_prev
= va_arg(va
, history_gfun_t
);
852 hf
.h_curr
= va_arg(va
, history_gfun_t
);
853 hf
.h_set
= va_arg(va
, history_sfun_t
);
854 hf
.h_clear
= va_arg(va
, history_vfun_t
);
855 hf
.h_enter
= va_arg(va
, history_efun_t
);
856 hf
.h_add
= va_arg(va
, history_efun_t
);
858 if ((retval
= history_set_fun(h
, &hf
)) == -1)
859 he_seterrev(ev
, _HE_PARAM_MISSING
);
870 he_seterrev(ev
, _HE_UNKNOWN
);