xref: /netbsd-src/lib/libedit/history.c (revision 69b6d498973bb4d7230c2d3c12bd9a032738ec8e)
1 /*	$NetBSD: history.c,v 1.30 2005/07/14 15:00:58 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.30 2005/07/14 15:00:58 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 
536 	return (h);
537 }
538 
539 
540 /* history_end():
541  *	clean up history;
542  */
543 public void
544 history_end(History *h)
545 {
546 	HistEvent ev;
547 
548 	if (h->h_next == history_def_next)
549 		history_def_clear(h->h_ref, &ev);
550 	h_free(h);
551 }
552 
553 
554 
555 /* history_setsize():
556  *	Set history number of events
557  */
558 private int
559 history_setsize(History *h, HistEvent *ev, int num)
560 {
561 
562 	if (h->h_next != history_def_next) {
563 		he_seterrev(ev, _HE_NOT_ALLOWED);
564 		return (-1);
565 	}
566 	if (num < 0) {
567 		he_seterrev(ev, _HE_BAD_PARAM);
568 		return (-1);
569 	}
570 	history_def_setsize(h->h_ref, num);
571 	return (0);
572 }
573 
574 
575 /* history_getsize():
576  *      Get number of events currently in history
577  */
578 private int
579 history_getsize(History *h, HistEvent *ev)
580 {
581 	if (h->h_next != history_def_next) {
582 		he_seterrev(ev, _HE_NOT_ALLOWED);
583 		return (-1);
584 	}
585 	ev->num = history_def_getsize(h->h_ref);
586 	if (ev->num < -1) {
587 		he_seterrev(ev, _HE_SIZE_NEGATIVE);
588 		return (-1);
589 	}
590 	return (0);
591 }
592 
593 
594 /* history_setunique():
595  *	Set if adjacent equal events should not be entered in history.
596  */
597 private int
598 history_setunique(History *h, HistEvent *ev, int uni)
599 {
600 
601 	if (h->h_next != history_def_next) {
602 		he_seterrev(ev, _HE_NOT_ALLOWED);
603 		return (-1);
604 	}
605 	history_def_setunique(h->h_ref, uni);
606 	return (0);
607 }
608 
609 
610 /* history_getunique():
611  *	Get if adjacent equal events should not be entered in history.
612  */
613 private int
614 history_getunique(History *h, HistEvent *ev)
615 {
616 	if (h->h_next != history_def_next) {
617 		he_seterrev(ev, _HE_NOT_ALLOWED);
618 		return (-1);
619 	}
620 	ev->num = history_def_getunique(h->h_ref);
621 	return (0);
622 }
623 
624 
625 /* history_set_fun():
626  *	Set history functions
627  */
628 private int
629 history_set_fun(History *h, History *nh)
630 {
631 	HistEvent ev;
632 
633 	if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
634 	    nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
635 	    nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
636 	    nh->h_del == NULL || nh->h_ref == NULL) {
637 		if (h->h_next != history_def_next) {
638 			history_def_init(&h->h_ref, &ev, 0);
639 			h->h_first = history_def_first;
640 			h->h_next = history_def_next;
641 			h->h_last = history_def_last;
642 			h->h_prev = history_def_prev;
643 			h->h_curr = history_def_curr;
644 			h->h_set = history_def_set;
645 			h->h_clear = history_def_clear;
646 			h->h_enter = history_def_enter;
647 			h->h_add = history_def_add;
648 			h->h_del = history_def_del;
649 		}
650 		return (-1);
651 	}
652 	if (h->h_next == history_def_next)
653 		history_def_clear(h->h_ref, &ev);
654 
655 	h->h_ent = -1;
656 	h->h_first = nh->h_first;
657 	h->h_next = nh->h_next;
658 	h->h_last = nh->h_last;
659 	h->h_prev = nh->h_prev;
660 	h->h_curr = nh->h_curr;
661 	h->h_set = nh->h_set;
662 	h->h_clear = nh->h_clear;
663 	h->h_enter = nh->h_enter;
664 	h->h_add = nh->h_add;
665 	h->h_del = nh->h_del;
666 
667 	return (0);
668 }
669 
670 
671 /* history_load():
672  *	History load function
673  */
674 private int
675 history_load(History *h, const char *fname)
676 {
677 	FILE *fp;
678 	char *line;
679 	size_t sz, max_size;
680 	char *ptr;
681 	int i = -1;
682 	HistEvent ev;
683 
684 	if ((fp = fopen(fname, "r")) == NULL)
685 		return (i);
686 
687 	if ((line = fgetln(fp, &sz)) == NULL)
688 		goto done;
689 
690 	if (strncmp(line, hist_cookie, sz) != 0)
691 		goto done;
692 
693 	ptr = h_malloc(max_size = 1024);
694 	if (ptr == NULL)
695 		goto done;
696 	for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
697 		char c = line[sz];
698 
699 		if (sz != 0 && line[sz - 1] == '\n')
700 			line[--sz] = '\0';
701 		else
702 			line[sz] = '\0';
703 
704 		if (max_size < sz) {
705 			char *nptr;
706 			max_size = (sz + 1024) & ~1023;
707 			nptr = h_realloc(ptr, max_size);
708 			if (nptr == NULL) {
709 				i = -1;
710 				goto oomem;
711 			}
712 			ptr = nptr;
713 		}
714 		(void) strunvis(ptr, line);
715 		line[sz] = c;
716 		if (HENTER(h, &ev, ptr) == -1) {
717 			h_free((ptr_t)ptr);
718 			return -1;
719 		}
720 	}
721 oomem:
722 	h_free((ptr_t)ptr);
723 done:
724 	(void) fclose(fp);
725 	return (i);
726 }
727 
728 
729 /* history_save():
730  *	History save function
731  */
732 private int
733 history_save(History *h, const char *fname)
734 {
735 	FILE *fp;
736 	HistEvent ev;
737 	int i = -1, retval;
738 	size_t len, max_size;
739 	char *ptr;
740 
741 	if ((fp = fopen(fname, "w")) == NULL)
742 		return (-1);
743 
744 	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
745 		goto done;
746 	if (fputs(hist_cookie, fp) == EOF)
747 		goto done;
748 	ptr = h_malloc(max_size = 1024);
749 	if (ptr == NULL)
750 		goto done;
751 	for (i = 0, retval = HLAST(h, &ev);
752 	    retval != -1;
753 	    retval = HPREV(h, &ev), i++) {
754 		len = strlen(ev.str) * 4;
755 		if (len >= max_size) {
756 			char *nptr;
757 			max_size = (len + 1024) & ~1023;
758 			nptr = h_realloc(ptr, max_size);
759 			if (nptr == NULL) {
760 				i = -1;
761 				goto oomem;
762 			}
763 			ptr = nptr;
764 		}
765 		(void) strvis(ptr, ev.str, VIS_WHITE);
766 		(void) fprintf(fp, "%s\n", ptr);
767 	}
768 oomem:
769 	h_free((ptr_t)ptr);
770 done:
771 	(void) fclose(fp);
772 	return (i);
773 }
774 
775 
776 /* history_prev_event():
777  *	Find the previous event, with number given
778  */
779 private int
780 history_prev_event(History *h, HistEvent *ev, int num)
781 {
782 	int retval;
783 
784 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
785 		if (ev->num == num)
786 			return (0);
787 
788 	he_seterrev(ev, _HE_NOT_FOUND);
789 	return (-1);
790 }
791 
792 
793 /* history_next_event():
794  *	Find the next event, with number given
795  */
796 private int
797 history_next_event(History *h, HistEvent *ev, int num)
798 {
799 	int retval;
800 
801 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
802 		if (ev->num == num)
803 			return (0);
804 
805 	he_seterrev(ev, _HE_NOT_FOUND);
806 	return (-1);
807 }
808 
809 
810 /* history_prev_string():
811  *	Find the previous event beginning with string
812  */
813 private int
814 history_prev_string(History *h, HistEvent *ev, const char *str)
815 {
816 	size_t len = strlen(str);
817 	int retval;
818 
819 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
820 		if (strncmp(str, ev->str, len) == 0)
821 			return (0);
822 
823 	he_seterrev(ev, _HE_NOT_FOUND);
824 	return (-1);
825 }
826 
827 
828 /* history_next_string():
829  *	Find the next event beginning with string
830  */
831 private int
832 history_next_string(History *h, HistEvent *ev, const char *str)
833 {
834 	size_t len = strlen(str);
835 	int retval;
836 
837 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
838 		if (strncmp(str, ev->str, len) == 0)
839 			return (0);
840 
841 	he_seterrev(ev, _HE_NOT_FOUND);
842 	return (-1);
843 }
844 
845 
846 /* history():
847  *	User interface to history functions.
848  */
849 int
850 history(History *h, HistEvent *ev, int fun, ...)
851 {
852 	va_list va;
853 	const char *str;
854 	int retval;
855 
856 	va_start(va, fun);
857 
858 	he_seterrev(ev, _HE_OK);
859 
860 	switch (fun) {
861 	case H_GETSIZE:
862 		retval = history_getsize(h, ev);
863 		break;
864 
865 	case H_SETSIZE:
866 		retval = history_setsize(h, ev, va_arg(va, int));
867 		break;
868 
869 	case H_GETUNIQUE:
870 		retval = history_getunique(h, ev);
871 		break;
872 
873 	case H_SETUNIQUE:
874 		retval = history_setunique(h, ev, va_arg(va, int));
875 		break;
876 
877 	case H_ADD:
878 		str = va_arg(va, const char *);
879 		retval = HADD(h, ev, str);
880 		break;
881 
882 	case H_DEL:
883 		retval = HDEL(h, ev, va_arg(va, const int));
884 		break;
885 
886 	case H_ENTER:
887 		str = va_arg(va, const char *);
888 		if ((retval = HENTER(h, ev, str)) != -1)
889 			h->h_ent = ev->num;
890 		break;
891 
892 	case H_APPEND:
893 		str = va_arg(va, const char *);
894 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
895 			retval = HADD(h, ev, str);
896 		break;
897 
898 	case H_FIRST:
899 		retval = HFIRST(h, ev);
900 		break;
901 
902 	case H_NEXT:
903 		retval = HNEXT(h, ev);
904 		break;
905 
906 	case H_LAST:
907 		retval = HLAST(h, ev);
908 		break;
909 
910 	case H_PREV:
911 		retval = HPREV(h, ev);
912 		break;
913 
914 	case H_CURR:
915 		retval = HCURR(h, ev);
916 		break;
917 
918 	case H_SET:
919 		retval = HSET(h, ev, va_arg(va, const int));
920 		break;
921 
922 	case H_CLEAR:
923 		HCLEAR(h, ev);
924 		retval = 0;
925 		break;
926 
927 	case H_LOAD:
928 		retval = history_load(h, va_arg(va, const char *));
929 		if (retval == -1)
930 			he_seterrev(ev, _HE_HIST_READ);
931 		break;
932 
933 	case H_SAVE:
934 		retval = history_save(h, va_arg(va, const char *));
935 		if (retval == -1)
936 			he_seterrev(ev, _HE_HIST_WRITE);
937 		break;
938 
939 	case H_PREV_EVENT:
940 		retval = history_prev_event(h, ev, va_arg(va, int));
941 		break;
942 
943 	case H_NEXT_EVENT:
944 		retval = history_next_event(h, ev, va_arg(va, int));
945 		break;
946 
947 	case H_PREV_STR:
948 		retval = history_prev_string(h, ev, va_arg(va, const char *));
949 		break;
950 
951 	case H_NEXT_STR:
952 		retval = history_next_string(h, ev, va_arg(va, const char *));
953 		break;
954 
955 	case H_FUNC:
956 	{
957 		History hf;
958 
959 		hf.h_ref = va_arg(va, ptr_t);
960 		h->h_ent = -1;
961 		hf.h_first = va_arg(va, history_gfun_t);
962 		hf.h_next = va_arg(va, history_gfun_t);
963 		hf.h_last = va_arg(va, history_gfun_t);
964 		hf.h_prev = va_arg(va, history_gfun_t);
965 		hf.h_curr = va_arg(va, history_gfun_t);
966 		hf.h_set = va_arg(va, history_sfun_t);
967 		hf.h_clear = va_arg(va, history_vfun_t);
968 		hf.h_enter = va_arg(va, history_efun_t);
969 		hf.h_add = va_arg(va, history_efun_t);
970 		hf.h_del = va_arg(va, history_sfun_t);
971 
972 		if ((retval = history_set_fun(h, &hf)) == -1)
973 			he_seterrev(ev, _HE_PARAM_MISSING);
974 		break;
975 	}
976 
977 	case H_END:
978 		history_end(h);
979 		retval = 0;
980 		break;
981 
982 	default:
983 		retval = -1;
984 		he_seterrev(ev, _HE_UNKNOWN);
985 		break;
986 	}
987 	va_end(va);
988 	return (retval);
989 }
990