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