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