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