xref: /netbsd-src/lib/libedit/history.c (revision dc306354b0b29af51801a7632f1e95265a68cd81)
1 /*	$NetBSD: history.c,v 1.11 1998/12/12 19:52:51 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Christos Zoulas of Cornell University.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
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.
25  *
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
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #if !defined(lint) && !defined(SCCSID)
41 #if 0
42 static char sccsid[] = "@(#)history.c	8.1 (Berkeley) 6/4/93";
43 #else
44 __RCSID("$NetBSD: history.c,v 1.11 1998/12/12 19:52:51 christos Exp $");
45 #endif
46 #endif /* not lint && not SCCSID */
47 
48 /*
49  * hist.c: History access functions
50  */
51 #include "sys.h"
52 
53 #include <string.h>
54 #include <stdlib.h>
55 #ifdef __STDC__
56 #include <stdarg.h>
57 #else
58 #include <varargs.h>
59 #endif
60 
61 static const char hist_cookie[] = "_HiStOrY_V1_\n";
62 
63 #include "histedit.h"
64 
65 typedef int	(*history_gfun_t) __P((ptr_t, HistEvent *));
66 typedef int	(*history_efun_t) __P((ptr_t, HistEvent *, const char *));
67 typedef void 	(*history_vfun_t) __P((ptr_t, HistEvent *));
68 typedef int	(*history_sfun_t) __P((ptr_t, HistEvent *, const int));
69 
70 struct history {
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		*/
82 };
83 
84 #define	HNEXT(h, ev)  		(*(h)->h_next)((h)->h_ref, ev)
85 #define	HFIRST(h, ev) 		(*(h)->h_first)((h)->h_ref, ev)
86 #define	HPREV(h, ev)  		(*(h)->h_prev)((h)->h_ref, ev)
87 #define	HLAST(h, ev) 		(*(h)->h_last)((h)->h_ref, ev)
88 #define	HCURR(h, ev) 		(*(h)->h_curr)((h)->h_ref, ev)
89 #define	HSET(h, ev, n) 		(*(h)->h_set)((h)->h_ref, ev, n)
90 #define	HCLEAR(h, ev) 		(*(h)->h_clear)((h)->h_ref, ev)
91 #define	HENTER(h, ev, str)	(*(h)->h_enter)((h)->h_ref, ev, str)
92 #define	HADD(h, ev, str)	(*(h)->h_add)((h)->h_ref, ev, str)
93 
94 #define h_malloc(a)	malloc(a)
95 #define h_free(a)	free(a)
96 
97 
98 private int	history_set_num		__P((History *, HistEvent *, int));
99 private int	history_get_size	__P((History *, HistEvent *));
100 private int	history_set_fun		__P((History *, History *));
101 private int 	history_load		__P((History *, const char *));
102 private int 	history_save		__P((History *, const char *));
103 private int	history_prev_event	__P((History *, HistEvent *, int));
104 private int	history_next_event	__P((History *, HistEvent *, int));
105 private int	history_next_string	__P((History *, HistEvent *, const char *));
106 private int	history_prev_string	__P((History *, HistEvent *, const char *));
107 
108 
109 /***********************************************************************/
110 
111 /*
112  * Builtin- history implementation
113  */
114 typedef struct hentry_t {
115     HistEvent ev;		/* What we return		*/
116     struct hentry_t *next;	/* Next entry			*/
117     struct hentry_t *prev;	/* Previous entry		*/
118 } hentry_t;
119 
120 typedef struct history_t {
121     hentry_t  list;		/* Fake list header element	*/
122     hentry_t *cursor;		/* Current element in the list	*/
123     int	max;			/* Maximum number of events	*/
124     int cur;			/* Current number of events	*/
125     int	eventid;		/* For generation of unique event id	*/
126 } history_t;
127 
128 private int	history_def_first  __P((ptr_t, HistEvent *));
129 private int	history_def_last   __P((ptr_t, HistEvent *));
130 private int	history_def_next   __P((ptr_t, HistEvent *));
131 private int	history_def_prev   __P((ptr_t, HistEvent *));
132 private int	history_def_curr   __P((ptr_t, HistEvent *));
133 private int	history_def_set    __P((ptr_t, HistEvent *, const int n));
134 private int	history_def_enter  __P((ptr_t, HistEvent *, const char *));
135 private int	history_def_add    __P((ptr_t, HistEvent *, const char *));
136 private void	history_def_init   __P((ptr_t *, HistEvent *, int));
137 private void	history_def_clear  __P((ptr_t, HistEvent *));
138 private int	history_def_insert __P((history_t *, HistEvent *,const char *));
139 private void	history_def_delete __P((history_t *, HistEvent *, hentry_t *));
140 
141 #define history_def_setsize(p, num)(void) (((history_t *) p)->max = (num))
142 #define history_def_getsize(p)  (((history_t *) p)->cur)
143 
144 #define he_strerror(code)	he_errlist[code]
145 #define he_seterrev(evp, code)	{\
146 				    evp->num = code;\
147 				    evp->str = he_strerror(code);\
148 				}
149 
150 /* error messages */
151 static const char *const he_errlist[] = {
152     "OK",
153     "unknown error"
154     "malloc() failed",
155     "first event not found",
156     "last event not found",
157     "empty list",
158     "no next event",
159     "no previous event",
160     "current event is invalid",
161     "event not found",
162     "can't read history from file",
163     "can't write history",
164     "required parameter(s) not supplied",
165     "history size negative",
166     "function not allowed with other history-functions-set the default",
167     "bad parameters"
168 };
169 
170 /* error codes */
171 #define _HE_OK                   0
172 #define _HE_UNKNOWN		 1
173 #define _HE_MALLOC_FAILED        2
174 #define _HE_FIRST_NOTFOUND       3
175 #define _HE_LAST_NOTFOUND        4
176 #define _HE_EMPTY_LIST           5
177 #define _HE_END_REACHED          6
178 #define _HE_START_REACHED	 7
179 #define _HE_CURR_INVALID	 8
180 #define _HE_NOT_FOUND		 9
181 #define _HE_HIST_READ		10
182 #define _HE_HIST_WRITE		11
183 #define _HE_PARAM_MISSING	12
184 #define _HE_SIZE_NEGATIVE	13
185 #define _HE_NOT_ALLOWED		14
186 #define _HE_BAD_PARAM		15
187 
188 /* history_def_first():
189  *	Default function to return the first event in the history.
190  */
191 private int
192 history_def_first(p, ev)
193     ptr_t p;
194     HistEvent *ev;
195 {
196     history_t *h = (history_t *) p;
197 
198     h->cursor = h->list.next;
199     if (h->cursor != &h->list)
200 	*ev = h->cursor->ev;
201     else {
202 	he_seterrev(ev, _HE_FIRST_NOTFOUND);
203 	return -1;
204     }
205 
206     return 0;
207 }
208 
209 
210 /* history_def_last():
211  *	Default function to return the last event in the history.
212  */
213 private int
214 history_def_last(p, ev)
215     ptr_t p;
216     HistEvent *ev;
217 {
218     history_t *h = (history_t *) p;
219 
220     h->cursor = h->list.prev;
221     if (h->cursor != &h->list)
222 	*ev =  h->cursor->ev;
223     else {
224 	he_seterrev(ev, _HE_LAST_NOTFOUND);
225 	return -1;
226     }
227 
228     return 0;
229 }
230 
231 
232 /* history_def_next():
233  *	Default function to return the next event in the history.
234  */
235 private int
236 history_def_next(p, ev)
237     ptr_t p;
238     HistEvent *ev;
239 {
240     history_t *h = (history_t *) p;
241 
242     if (h->cursor != &h->list)
243 	h->cursor = h->cursor->next;
244     else {
245 	he_seterrev(ev, _HE_EMPTY_LIST);
246 	return -1;
247     }
248 
249     if (h->cursor != &h->list)
250 	*ev = h->cursor->ev;
251     else {
252 	he_seterrev(ev, _HE_END_REACHED);
253 	return -1;
254     }
255 
256     return 0;
257 }
258 
259 
260 /* history_def_prev():
261  *	Default function to return the previous event in the history.
262  */
263 private int
264 history_def_prev(p, ev)
265     ptr_t p;
266     HistEvent *ev;
267 {
268     history_t *h = (history_t *) p;
269 
270     if (h->cursor != &h->list)
271 	h->cursor = h->cursor->prev;
272     else {
273 	he_seterrev(ev, (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
274 	return -1;
275    }
276 
277     if (h->cursor != &h->list)
278 	*ev = h->cursor->ev;
279     else {
280 	he_seterrev(ev, _HE_START_REACHED);
281 	return -1;
282     }
283 
284     return 0;
285 }
286 
287 
288 /* history_def_curr():
289  *	Default function to return the current event in the history.
290  */
291 private int
292 history_def_curr(p, ev)
293     ptr_t p;
294     HistEvent *ev;
295 {
296     history_t *h = (history_t *) p;
297 
298     if (h->cursor != &h->list)
299 	*ev = h->cursor->ev;
300     else {
301 	he_seterrev(ev, (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
302 	return -1;
303    }
304 
305     return 0;
306 }
307 
308 
309 /* history_def_set():
310  *	Default function to set the current event in the history to the
311  *	given one.
312  */
313 private int
314 history_def_set(p, ev, n)
315     ptr_t p;
316     HistEvent *ev;
317     int n;
318 {
319     history_t *h = (history_t *) p;
320 
321     if (h->cur == 0) {
322 	he_seterrev(ev, _HE_EMPTY_LIST);
323 	return -1;
324     }
325 
326     if (h->cursor == &h->list || h->cursor->ev.num != n) {
327 	for (h->cursor = h->list.next; h->cursor != &h->list;
328 	     h->cursor = h->cursor->next)
329 	    if (h->cursor->ev.num == n)
330 		break;
331     }
332 
333     if (h->cursor == &h->list) {
334 	he_seterrev(ev, _HE_NOT_FOUND);
335 	return -1;
336     }
337 
338     return 0;
339 }
340 
341 
342 /* history_def_add():
343  *	Append string to element
344  */
345 private int
346 history_def_add(p, ev, str)
347     ptr_t p;
348     HistEvent *ev;
349     const char *str;
350 {
351     history_t *h = (history_t *) p;
352     size_t len;
353     char *s;
354 
355     if (h->cursor == &h->list)
356 	return (history_def_enter(p, ev, str));
357     len = strlen(h->cursor->ev.str) + strlen(str) + 1;
358     s = (char *) h_malloc(len);
359     if (!s) {
360 	he_seterrev(ev, _HE_MALLOC_FAILED);
361 	return -1;
362     }
363     (void)strcpy(s, h->cursor->ev.str);		/* XXX strcpy is safe */
364     (void)strcat(s, str);			/* XXX strcat is safe */
365     /* LINTED const cast */
366     h_free((ptr_t) h->cursor->ev.str);
367     h->cursor->ev.str = s;
368     *ev = h->cursor->ev;
369     return 0;
370 }
371 
372 
373 /* history_def_delete():
374  *	Delete element hp of the h list
375  */
376 /* ARGSUSED */
377 private void
378 history_def_delete(h, ev, hp)
379     history_t *h;
380     HistEvent *ev;
381     hentry_t *hp;
382 {
383     if (hp == &h->list)
384 	abort();
385     hp->prev->next = hp->next;
386     hp->next->prev = hp->prev;
387     /* LINTED const cast */
388     h_free((ptr_t) hp->ev.str);
389     h_free(hp);
390     h->cur--;
391 }
392 
393 
394 /* history_def_insert():
395  *	Insert element with string str in the h list
396  */
397 private int
398 history_def_insert(h, ev, str)
399     history_t *h;
400     HistEvent *ev;
401     const char *str;
402 {
403     h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
404     if (h->cursor)
405     	h->cursor->ev.str = strdup(str);
406     if (!h->cursor || !h->cursor->ev.str) {
407 	he_seterrev(ev, _HE_MALLOC_FAILED);
408 	return -1;
409     }
410     h->cursor->ev.num = ++h->eventid;
411     h->cursor->next = h->list.next;
412     h->cursor->prev = &h->list;
413     h->list.next->prev = h->cursor;
414     h->list.next = h->cursor;
415     h->cur++;
416 
417     *ev = h->cursor->ev;
418     return 0;
419 }
420 
421 
422 /* history_def_enter():
423  *	Default function to enter an item in the history
424  */
425 private int
426 history_def_enter(p, ev, str)
427     ptr_t p;
428     HistEvent *ev;
429     const char *str;
430 {
431     history_t *h = (history_t *) p;
432 
433     if (history_def_insert(h, ev, str) == -1)
434 	return -1; /* error, keep error message */
435 
436     /*
437      * Always keep at least one entry.
438      * This way we don't have to check for the empty list.
439      */
440     while (h->cur - 1 > h->max)
441 	history_def_delete(h, ev, h->list.prev);
442 
443     return 0;
444 }
445 
446 
447 /* history_def_init():
448  *	Default history initialization function
449  */
450 /* ARGSUSED */
451 private void
452 history_def_init(p, ev, n)
453     ptr_t *p;
454     HistEvent *ev;
455     int n;
456 {
457     history_t *h = (history_t *) h_malloc(sizeof(history_t));
458     if (n <= 0)
459 	n = 0;
460     h->eventid = 0;
461     h->cur = 0;
462     h->max = n;
463     h->list.next = h->list.prev = &h->list;
464     h->list.ev.str = NULL;
465     h->list.ev.num = 0;
466     h->cursor = &h->list;
467     *p = (ptr_t) h;
468 }
469 
470 
471 /* history_def_clear():
472  *	Default history cleanup function
473  */
474 private void
475 history_def_clear(p, ev)
476     ptr_t p;
477     HistEvent *ev;
478 {
479     history_t *h = (history_t *) p;
480 
481     while (h->list.prev != &h->list)
482 	history_def_delete(h, ev, h->list.prev);
483     h->eventid = 0;
484     h->cur = 0;
485 }
486 
487 
488 
489 
490 /************************************************************************/
491 
492 /* history_init():
493  *	Initialization function.
494  */
495 public History *
496 history_init()
497 {
498     History *h = (History *) h_malloc(sizeof(History));
499     HistEvent ev;
500 
501     history_def_init(&h->h_ref, &ev, 0);
502     h->h_ent   = -1;
503     h->h_next  = history_def_next;
504     h->h_first = history_def_first;
505     h->h_last  = history_def_last;
506     h->h_prev  = history_def_prev;
507     h->h_curr  = history_def_curr;
508     h->h_set   = history_def_set;
509     h->h_clear = history_def_clear;
510     h->h_enter = history_def_enter;
511     h->h_add   = history_def_add;
512 
513     return h;
514 }
515 
516 
517 /* history_end():
518  *	clean up history;
519  */
520 public void
521 history_end(h)
522     History *h;
523 {
524     HistEvent ev;
525     if (h->h_next == history_def_next)
526 	history_def_clear(h->h_ref, &ev);
527 }
528 
529 
530 
531 /* history_set_num():
532  *	Set history number of events
533  */
534 private int
535 history_set_num(h, ev, num)
536     History *h;
537     HistEvent *ev;
538     int num;
539 {
540     if (h->h_next != history_def_next) {
541 	he_seterrev(ev, _HE_NOT_ALLOWED);
542 	return -1;
543     }
544 
545     if (num < 0) {
546 	he_seterrev(ev, _HE_BAD_PARAM);
547 	return -1;
548     }
549 
550     history_def_setsize(h->h_ref, num);
551     return 0;
552 }
553 
554 /* history_get_size():
555  *      Get number of events currently in history
556  */
557 private int
558 history_get_size(h, ev)
559     History *h;
560     HistEvent *ev;
561 {
562     int retval=0;
563 
564     if (h->h_next != history_def_next) {
565 	he_seterrev(ev, _HE_NOT_ALLOWED);
566 	return -1;
567     }
568     retval = history_def_getsize(h->h_ref);
569     if (retval < -1) {
570 	he_seterrev(ev, _HE_SIZE_NEGATIVE);
571 	return -1;
572     }
573 
574     ev->num = retval;
575     return 0;
576 }
577 
578 /* history_set_fun():
579  *	Set history functions
580  */
581 private int
582 history_set_fun(h, nh)
583     History *h;
584     History *nh;
585 {
586     HistEvent ev;
587 
588     if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
589 	nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
590 	nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
591 	nh->h_ref == NULL) {
592 	if (h->h_next != history_def_next) {
593 	    history_def_init(&h->h_ref, &ev, 0);
594 	    h->h_first = history_def_first;
595 	    h->h_next  = history_def_next;
596 	    h->h_last  = history_def_last;
597 	    h->h_prev  = history_def_prev;
598 	    h->h_curr  = history_def_curr;
599 	    h->h_set   = history_def_set;
600 	    h->h_clear = history_def_clear;
601 	    h->h_enter = history_def_enter;
602 	    h->h_add   = history_def_add;
603 	}
604 	return -1;
605     }
606 
607     if (h->h_next == history_def_next)
608 	history_def_clear(h->h_ref, &ev);
609 
610     h->h_ent   = -1;
611     h->h_first = nh->h_first;
612     h->h_next  = nh->h_next;
613     h->h_last  = nh->h_last;
614     h->h_prev  = nh->h_prev;
615     h->h_curr  = nh->h_curr;
616     h->h_set   = nh->h_set;
617     h->h_clear = nh->h_clear;
618     h->h_enter = nh->h_enter;
619     h->h_add   = nh->h_add;
620 
621     return 0;
622 }
623 
624 
625 /* history_load():
626  *	History load function
627  */
628 private int
629 history_load(h, fname)
630     History *h;
631     const char *fname;
632 {
633     FILE *fp;
634     char *line;
635     size_t sz;
636     int i = -1;
637     HistEvent ev;
638 
639     if ((fp = fopen(fname, "r")) == NULL)
640 	return i;
641 
642     if ((line = fgetln(fp, &sz)) == NULL)
643 	goto done;
644 
645     if (strncmp(line, hist_cookie, sz) != 0)
646 	goto done;
647 
648     for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
649 	char c = line[sz];
650 	line[sz] = '\0';
651 	HENTER(h, &ev, line);
652 	line[sz] = c;
653     }
654 
655 done:
656     (void) fclose(fp);
657     return i;
658 }
659 
660 
661 /* history_save():
662  *	History save function
663  */
664 private int
665 history_save(h, fname)
666     History *h;
667     const char *fname;
668 {
669     FILE *fp;
670     HistEvent ev;
671     int i = 0, retval;
672 
673     if ((fp = fopen(fname, "w")) == NULL)
674 	return -1;
675 
676     (void) fputs(hist_cookie, fp);
677     for (retval = HLAST(h, &ev); retval != -1; retval = HPREV(h, &ev), i++)
678 	(void) fprintf(fp, "%s", ev.str);
679     (void) fclose(fp);
680     return i;
681 }
682 
683 
684 /* history_prev_event():
685  *	Find the previous event, with number given
686  */
687 private int
688 history_prev_event(h, ev, num)
689     History *h;
690     HistEvent *ev;
691     int num;
692 {
693     int retval;
694     for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
695 	if (ev->num == num)
696 	    return 0;
697 
698     he_seterrev(ev, _HE_NOT_FOUND);
699     return -1;
700 }
701 
702 
703 /* history_next_event():
704  *	Find the next event, with number given
705  */
706 private int
707 history_next_event(h, ev, num)
708     History *h;
709     HistEvent *ev;
710     int num;
711 {
712     int retval;
713     for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
714 	if (ev->num == num)
715 	    return 0;
716 
717     he_seterrev(ev, _HE_NOT_FOUND);
718     return -1;
719 }
720 
721 
722 /* history_prev_string():
723  *	Find the previous event beginning with string
724  */
725 private int
726 history_prev_string(h, ev, str)
727     History *h;
728     HistEvent *ev;
729     const char* str;
730 {
731     size_t len = strlen(str);
732     int retval;
733 
734     for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
735 	if (strncmp(str, ev->str, len) == 0)
736 	    return 0;
737 
738     he_seterrev(ev, _HE_NOT_FOUND);
739     return -1;
740 }
741 
742 
743 
744 
745 /* history_next_string():
746  *	Find the next event beginning with string
747  */
748 private int
749 history_next_string(h, ev, str)
750     History *h;
751     HistEvent *ev;
752     const char* str;
753 {
754     size_t len = strlen(str);
755     int retval;
756 
757     for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
758 	if (strncmp(str, ev->str, len) == 0)
759 	    return 0;
760 
761     he_seterrev(ev, _HE_NOT_FOUND);
762     return -1;
763 }
764 
765 
766 /* history():
767  *	User interface to history functions.
768  */
769 int
770 #ifdef __STDC__
771 history(History *h, HistEvent *ev, int fun, ...)
772 #else
773 history(va_alist)
774     va_dcl
775 #endif
776 {
777     va_list va;
778     const char *str;
779     int retval;
780 
781 #ifdef __STDC__
782     va_start(va, fun);
783 #else
784     History *h;
785     HistEvent *ev;
786     int fun;
787     va_start(va);
788     h = va_arg(va, History *);
789     ev = va_arg(va, HistEvent *);
790     fun = va_arg(va, int);
791 #endif
792 
793     he_seterrev(ev, _HE_OK);
794 
795     switch (fun) {
796     case H_GETSIZE:
797 	retval = history_get_size(h, ev);
798 	break;
799 
800     case H_SETSIZE:
801 	retval = history_set_num(h, ev, va_arg(va, int));
802 	break;
803 
804     case H_ADD:
805 	str = va_arg(va, const char *);
806 	retval = HADD(h, ev, str);
807 	break;
808 
809     case H_ENTER:
810 	str = va_arg(va, const char *);
811 	if ((retval = HENTER(h, ev, str)) != -1)
812 	    h->h_ent = ev->num;
813 	break;
814 
815     case H_APPEND:
816 	str = va_arg(va, const char *);
817 	if ((retval = HSET(h, ev, h->h_ent)) != -1)
818 	    retval = HADD(h, ev, str);
819 	break;
820 
821     case H_FIRST:
822 	retval = HFIRST(h, ev);
823 	break;
824 
825     case H_NEXT:
826 	retval = HNEXT(h, ev);
827 	break;
828 
829     case H_LAST:
830 	retval = HLAST(h, ev);
831 	break;
832 
833     case H_PREV:
834 	retval = HPREV(h, ev);
835 	break;
836 
837     case H_CURR:
838 	retval = HCURR(h, ev);
839 	break;
840 
841     case H_SET:
842 	retval = HSET(h, ev, va_arg(va, const int));
843 	break;
844 
845     case H_CLEAR:
846 	HCLEAR(h, ev);
847 	retval = 0;
848 	break;
849 
850     case H_LOAD:
851 	retval = history_load(h, va_arg(va, const char *));
852 	if (retval == -1)
853 	    he_seterrev(ev, _HE_HIST_READ);
854 	break;
855 
856     case H_SAVE:
857 	retval = history_save(h, va_arg(va, const char *));
858 	if (retval == -1)
859 	    he_seterrev(ev, _HE_HIST_WRITE);
860 	break;
861 
862     case H_PREV_EVENT:
863 	retval = history_prev_event(h, ev, va_arg(va, int));
864 	break;
865 
866     case H_NEXT_EVENT:
867 	retval = history_next_event(h, ev, va_arg(va, int));
868 	break;
869 
870     case H_PREV_STR:
871 	retval = history_prev_string(h, ev, va_arg(va, const char*));
872 	break;
873 
874     case H_NEXT_STR:
875 	retval = history_next_string(h, ev, va_arg(va, const char*));
876 	break;
877 
878     case H_FUNC:
879 	{
880 	    History hf;
881 
882 	    hf.h_ref   = va_arg(va, ptr_t);
883 	    h->h_ent   = -1;
884 	    hf.h_first = va_arg(va, history_gfun_t);
885 	    hf.h_next  = va_arg(va, history_gfun_t);
886 	    hf.h_last  = va_arg(va, history_gfun_t);
887 	    hf.h_prev  = va_arg(va, history_gfun_t);
888 	    hf.h_curr  = va_arg(va, history_gfun_t);
889 	    hf.h_set   = va_arg(va, history_sfun_t);
890 	    hf.h_clear = va_arg(va, history_vfun_t);
891 	    hf.h_enter = va_arg(va, history_efun_t);
892 	    hf.h_add   = va_arg(va, history_efun_t);
893 
894 	    if ((retval = history_set_fun(h, &hf)) == -1)
895 		he_seterrev(ev, _HE_PARAM_MISSING);
896 	}
897 	break;
898 
899     case H_END:
900 	history_end(h);
901 	retval = 0;
902 	break;
903 
904     default:
905 	retval = -1;
906 	he_seterrev(ev, _HE_UNKNOWN);
907 	break;
908     }
909     va_end(va);
910     return retval;
911 }
912