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