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