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