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