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