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