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