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