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