history.c revision 148834
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#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 148834 2005-08-07 20:55:59Z 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);
547}
548
549
550
551/* history_setsize():
552 *	Set history number of events
553 */
554private int
555history_setsize(History *h, HistEvent *ev, int num)
556{
557
558	if (h->h_next != history_def_next) {
559		he_seterrev(ev, _HE_NOT_ALLOWED);
560		return (-1);
561	}
562	if (num < 0) {
563		he_seterrev(ev, _HE_BAD_PARAM);
564		return (-1);
565	}
566	history_def_setsize(h->h_ref, num);
567	return (0);
568}
569
570
571/* history_getsize():
572 *      Get number of events currently in history
573 */
574private int
575history_getsize(History *h, HistEvent *ev)
576{
577	if (h->h_next != history_def_next) {
578		he_seterrev(ev, _HE_NOT_ALLOWED);
579		return (-1);
580	}
581	ev->num = history_def_getsize(h->h_ref);
582	if (ev->num < -1) {
583		he_seterrev(ev, _HE_SIZE_NEGATIVE);
584		return (-1);
585	}
586	return (0);
587}
588
589
590/* history_setunique():
591 *	Set if adjacent equal events should not be entered in history.
592 */
593private int
594history_setunique(History *h, HistEvent *ev, int uni)
595{
596
597	if (h->h_next != history_def_next) {
598		he_seterrev(ev, _HE_NOT_ALLOWED);
599		return (-1);
600	}
601	history_def_setunique(h->h_ref, uni);
602	return (0);
603}
604
605
606/* history_getunique():
607 *	Get if adjacent equal events should not be entered in history.
608 */
609private int
610history_getunique(History *h, HistEvent *ev)
611{
612	if (h->h_next != history_def_next) {
613		he_seterrev(ev, _HE_NOT_ALLOWED);
614		return (-1);
615	}
616	ev->num = history_def_getunique(h->h_ref);
617	return (0);
618}
619
620
621/* history_set_fun():
622 *	Set history functions
623 */
624private int
625history_set_fun(History *h, History *nh)
626{
627	HistEvent ev;
628
629	if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
630	    nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
631	    nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
632	    nh->h_del == NULL || nh->h_ref == NULL) {
633		if (h->h_next != history_def_next) {
634			history_def_init(&h->h_ref, &ev, 0);
635			h->h_first = history_def_first;
636			h->h_next = history_def_next;
637			h->h_last = history_def_last;
638			h->h_prev = history_def_prev;
639			h->h_curr = history_def_curr;
640			h->h_set = history_def_set;
641			h->h_clear = history_def_clear;
642			h->h_enter = history_def_enter;
643			h->h_add = history_def_add;
644			h->h_del = history_def_del;
645		}
646		return (-1);
647	}
648	if (h->h_next == history_def_next)
649		history_def_clear(h->h_ref, &ev);
650
651	h->h_ent = -1;
652	h->h_first = nh->h_first;
653	h->h_next = nh->h_next;
654	h->h_last = nh->h_last;
655	h->h_prev = nh->h_prev;
656	h->h_curr = nh->h_curr;
657	h->h_set = nh->h_set;
658	h->h_clear = nh->h_clear;
659	h->h_enter = nh->h_enter;
660	h->h_add = nh->h_add;
661	h->h_del = nh->h_del;
662
663	return (0);
664}
665
666
667/* history_load():
668 *	History load function
669 */
670private int
671history_load(History *h, const char *fname)
672{
673	FILE *fp;
674	char *line;
675	size_t sz, max_size;
676	char *ptr;
677	int i = -1;
678	HistEvent ev;
679
680	if ((fp = fopen(fname, "r")) == NULL)
681		return (i);
682
683	if ((line = fgetln(fp, &sz)) == NULL)
684		goto done;
685
686	if (strncmp(line, hist_cookie, sz) != 0)
687		goto done;
688
689	ptr = h_malloc(max_size = 1024);
690	if (ptr == NULL)
691		goto done;
692	for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
693		char c = line[sz];
694
695		if (sz != 0 && line[sz - 1] == '\n')
696			line[--sz] = '\0';
697		else
698			line[sz] = '\0';
699
700		if (max_size < sz) {
701			char *nptr;
702			max_size = (sz + 1024) & ~1023;
703			nptr = h_realloc(ptr, max_size);
704			if (nptr == NULL) {
705				i = -1;
706				goto oomem;
707			}
708			ptr = nptr;
709		}
710		(void) strunvis(ptr, line);
711		line[sz] = c;
712		if (HENTER(h, &ev, ptr) == -1) {
713			h_free((ptr_t)ptr);
714			return -1;
715		}
716	}
717oomem:
718	h_free((ptr_t)ptr);
719done:
720	(void) fclose(fp);
721	return (i);
722}
723
724
725/* history_save():
726 *	History save function
727 */
728private int
729history_save(History *h, const char *fname)
730{
731	FILE *fp;
732	HistEvent ev;
733	int i = -1, retval;
734	size_t len, max_size;
735	char *ptr;
736
737	if ((fp = fopen(fname, "w")) == NULL)
738		return (-1);
739
740	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
741		goto done;
742	if (fputs(hist_cookie, fp) == EOF)
743		goto done;
744	ptr = h_malloc(max_size = 1024);
745	if (ptr == NULL)
746		goto done;
747	for (i = 0, retval = HLAST(h, &ev);
748	    retval != -1;
749	    retval = HPREV(h, &ev), i++) {
750		len = strlen(ev.str) * 4;
751		if (len >= max_size) {
752			char *nptr;
753			max_size = (len + 1024) & ~1023;
754			nptr = h_realloc(ptr, max_size);
755			if (nptr == NULL) {
756				i = -1;
757				goto oomem;
758			}
759			ptr = nptr;
760		}
761		(void) strvis(ptr, ev.str, VIS_WHITE);
762		(void) fprintf(fp, "%s\n", ptr);
763	}
764oomem:
765	h_free((ptr_t)ptr);
766done:
767	(void) fclose(fp);
768	return (i);
769}
770
771
772/* history_prev_event():
773 *	Find the previous event, with number given
774 */
775private int
776history_prev_event(History *h, HistEvent *ev, int num)
777{
778	int retval;
779
780	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
781		if (ev->num == num)
782			return (0);
783
784	he_seterrev(ev, _HE_NOT_FOUND);
785	return (-1);
786}
787
788
789/* history_next_event():
790 *	Find the next event, with number given
791 */
792private int
793history_next_event(History *h, HistEvent *ev, int num)
794{
795	int retval;
796
797	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
798		if (ev->num == num)
799			return (0);
800
801	he_seterrev(ev, _HE_NOT_FOUND);
802	return (-1);
803}
804
805
806/* history_prev_string():
807 *	Find the previous event beginning with string
808 */
809private int
810history_prev_string(History *h, HistEvent *ev, const char *str)
811{
812	size_t len = strlen(str);
813	int retval;
814
815	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
816		if (strncmp(str, ev->str, len) == 0)
817			return (0);
818
819	he_seterrev(ev, _HE_NOT_FOUND);
820	return (-1);
821}
822
823
824/* history_next_string():
825 *	Find the next event beginning with string
826 */
827private int
828history_next_string(History *h, HistEvent *ev, const char *str)
829{
830	size_t len = strlen(str);
831	int retval;
832
833	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
834		if (strncmp(str, ev->str, len) == 0)
835			return (0);
836
837	he_seterrev(ev, _HE_NOT_FOUND);
838	return (-1);
839}
840
841
842/* history():
843 *	User interface to history functions.
844 */
845int
846history(History *h, HistEvent *ev, int fun, ...)
847{
848	va_list va;
849	const char *str;
850	int retval;
851
852	va_start(va, fun);
853
854	he_seterrev(ev, _HE_OK);
855
856	switch (fun) {
857	case H_GETSIZE:
858		retval = history_getsize(h, ev);
859		break;
860
861	case H_SETSIZE:
862		retval = history_setsize(h, ev, va_arg(va, int));
863		break;
864
865	case H_GETUNIQUE:
866		retval = history_getunique(h, ev);
867		break;
868
869	case H_SETUNIQUE:
870		retval = history_setunique(h, ev, va_arg(va, int));
871		break;
872
873	case H_ADD:
874		str = va_arg(va, const char *);
875		retval = HADD(h, ev, str);
876		break;
877
878	case H_DEL:
879		retval = HDEL(h, ev, va_arg(va, const int));
880		break;
881
882	case H_ENTER:
883		str = va_arg(va, const char *);
884		if ((retval = HENTER(h, ev, str)) != -1)
885			h->h_ent = ev->num;
886		break;
887
888	case H_APPEND:
889		str = va_arg(va, const char *);
890		if ((retval = HSET(h, ev, h->h_ent)) != -1)
891			retval = HADD(h, ev, str);
892		break;
893
894	case H_FIRST:
895		retval = HFIRST(h, ev);
896		break;
897
898	case H_NEXT:
899		retval = HNEXT(h, ev);
900		break;
901
902	case H_LAST:
903		retval = HLAST(h, ev);
904		break;
905
906	case H_PREV:
907		retval = HPREV(h, ev);
908		break;
909
910	case H_CURR:
911		retval = HCURR(h, ev);
912		break;
913
914	case H_SET:
915		retval = HSET(h, ev, va_arg(va, const int));
916		break;
917
918	case H_CLEAR:
919		HCLEAR(h, ev);
920		retval = 0;
921		break;
922
923	case H_LOAD:
924		retval = history_load(h, va_arg(va, const char *));
925		if (retval == -1)
926			he_seterrev(ev, _HE_HIST_READ);
927		break;
928
929	case H_SAVE:
930		retval = history_save(h, va_arg(va, const char *));
931		if (retval == -1)
932			he_seterrev(ev, _HE_HIST_WRITE);
933		break;
934
935	case H_PREV_EVENT:
936		retval = history_prev_event(h, ev, va_arg(va, int));
937		break;
938
939	case H_NEXT_EVENT:
940		retval = history_next_event(h, ev, va_arg(va, int));
941		break;
942
943	case H_PREV_STR:
944		retval = history_prev_string(h, ev, va_arg(va, const char *));
945		break;
946
947	case H_NEXT_STR:
948		retval = history_next_string(h, ev, va_arg(va, const char *));
949		break;
950
951	case H_FUNC:
952	{
953		History hf;
954
955		hf.h_ref = va_arg(va, ptr_t);
956		h->h_ent = -1;
957		hf.h_first = va_arg(va, history_gfun_t);
958		hf.h_next = va_arg(va, history_gfun_t);
959		hf.h_last = va_arg(va, history_gfun_t);
960		hf.h_prev = va_arg(va, history_gfun_t);
961		hf.h_curr = va_arg(va, history_gfun_t);
962		hf.h_set = va_arg(va, history_sfun_t);
963		hf.h_clear = va_arg(va, history_vfun_t);
964		hf.h_enter = va_arg(va, history_efun_t);
965		hf.h_add = va_arg(va, history_efun_t);
966		hf.h_del = va_arg(va, history_sfun_t);
967
968		if ((retval = history_set_fun(h, &hf)) == -1)
969			he_seterrev(ev, _HE_PARAM_MISSING);
970		break;
971	}
972
973	case H_END:
974		history_end(h);
975		retval = 0;
976		break;
977
978	default:
979		retval = -1;
980		he_seterrev(ev, _HE_UNKNOWN);
981		break;
982	}
983	va_end(va);
984	return (retval);
985}
986