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