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