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