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