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