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