xref: /netbsd-src/lib/libedit/history.c (revision 5bbd2a12505d72a8177929a37b5cee489d0a1cfd)
1 /*	$NetBSD: history.c,v 1.46 2011/11/18 20:39:18 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.46 2011/11/18 20:39:18 christos Exp $");
41 #endif
42 #endif /* not lint && not SCCSID */
43 
44 /*
45  * hist.c: TYPE(History) access functions
46  */
47 #include <string.h>
48 #include <stdlib.h>
49 #include <stdarg.h>
50 #include <vis.h>
51 #include <sys/stat.h>
52 
53 static const char hist_cookie[] = "_HiStOrY_V2_\n";
54 
55 #include "histedit.h"
56 #include "chartype.h"
57 
58 typedef int (*history_gfun_t)(void *, TYPE(HistEvent) *);
59 typedef int (*history_efun_t)(void *, TYPE(HistEvent) *, const Char *);
60 typedef void (*history_vfun_t)(void *, TYPE(HistEvent) *);
61 typedef int (*history_sfun_t)(void *, TYPE(HistEvent) *, const int);
62 
63 struct TYPE(history) {
64 	void *h_ref;		/* Argument for history fcns	 */
65 	int h_ent;		/* Last entry point for history	 */
66 	history_gfun_t h_first;	/* Get the first element	 */
67 	history_gfun_t h_next;	/* Get the next element		 */
68 	history_gfun_t h_last;	/* Get the last element		 */
69 	history_gfun_t h_prev;	/* Get the previous element	 */
70 	history_gfun_t h_curr;	/* Get the current element	 */
71 	history_sfun_t h_set;	/* Set the current element	 */
72 	history_sfun_t h_del;	/* Set the given element	 */
73 	history_vfun_t h_clear;	/* Clear the history list	 */
74 	history_efun_t h_enter;	/* Add an element		 */
75 	history_efun_t h_add;	/* Append to an element		 */
76 };
77 
78 #define	HNEXT(h, ev)		(*(h)->h_next)((h)->h_ref, ev)
79 #define	HFIRST(h, ev)		(*(h)->h_first)((h)->h_ref, ev)
80 #define	HPREV(h, ev)		(*(h)->h_prev)((h)->h_ref, ev)
81 #define	HLAST(h, ev)		(*(h)->h_last)((h)->h_ref, ev)
82 #define	HCURR(h, ev)		(*(h)->h_curr)((h)->h_ref, ev)
83 #define	HSET(h, ev, n)		(*(h)->h_set)((h)->h_ref, ev, n)
84 #define	HCLEAR(h, ev)		(*(h)->h_clear)((h)->h_ref, ev)
85 #define	HENTER(h, ev, str)	(*(h)->h_enter)((h)->h_ref, ev, str)
86 #define	HADD(h, ev, str)	(*(h)->h_add)((h)->h_ref, ev, str)
87 #define	HDEL(h, ev, n)		(*(h)->h_del)((h)->h_ref, ev, n)
88 
89 #define	h_strdup(a)	Strdup(a)
90 #define	h_malloc(a)	malloc(a)
91 #define	h_realloc(a, b)	realloc((a), (b))
92 #define	h_free(a)	free(a)
93 
94 typedef struct {
95     int		num;
96     Char	*str;
97 } HistEventPrivate;
98 
99 
100 
101 private int history_setsize(TYPE(History) *, TYPE(HistEvent) *, int);
102 private int history_getsize(TYPE(History) *, TYPE(HistEvent) *);
103 private int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int);
104 private int history_getunique(TYPE(History) *, TYPE(HistEvent) *);
105 private int history_set_fun(TYPE(History) *, TYPE(History) *);
106 private int history_load(TYPE(History) *, const char *);
107 private int history_save(TYPE(History) *, const char *);
108 private int history_prev_event(TYPE(History) *, TYPE(HistEvent) *, int);
109 private int history_next_event(TYPE(History) *, TYPE(HistEvent) *, int);
110 private int history_next_string(TYPE(History) *, TYPE(HistEvent) *, const Char *);
111 private int history_prev_string(TYPE(History) *, TYPE(HistEvent) *, const Char *);
112 
113 
114 /***********************************************************************/
115 
116 /*
117  * Builtin- history implementation
118  */
119 typedef struct hentry_t {
120 	TYPE(HistEvent) ev;		/* What we return		 */
121 	void *data;		/* data				 */
122 	struct hentry_t *next;	/* Next entry			 */
123 	struct hentry_t *prev;	/* Previous entry		 */
124 } hentry_t;
125 
126 typedef struct history_t {
127 	hentry_t list;		/* Fake list header element	*/
128 	hentry_t *cursor;	/* Current element in the list	*/
129 	int max;		/* Maximum number of events	*/
130 	int cur;		/* Current number of events	*/
131 	int eventid;		/* For generation of unique event id	 */
132 	int flags;		/* TYPE(History) flags		*/
133 #define H_UNIQUE	1	/* Store only unique elements	*/
134 } history_t;
135 
136 private int history_def_next(void *, TYPE(HistEvent) *);
137 private int history_def_first(void *, TYPE(HistEvent) *);
138 private int history_def_prev(void *, TYPE(HistEvent) *);
139 private int history_def_last(void *, TYPE(HistEvent) *);
140 private int history_def_curr(void *, TYPE(HistEvent) *);
141 private int history_def_set(void *, TYPE(HistEvent) *, const int);
142 private void history_def_clear(void *, TYPE(HistEvent) *);
143 private int history_def_enter(void *, TYPE(HistEvent) *, const Char *);
144 private int history_def_add(void *, TYPE(HistEvent) *, const Char *);
145 private int history_def_del(void *, TYPE(HistEvent) *, const int);
146 
147 private int history_def_init(void **, TYPE(HistEvent) *, int);
148 private int history_def_insert(history_t *, TYPE(HistEvent) *, const Char *);
149 private void history_def_delete(history_t *, TYPE(HistEvent) *, hentry_t *);
150 
151 private int history_deldata_nth(history_t *, TYPE(HistEvent) *, int, void **);
152 private int history_set_nth(void *, TYPE(HistEvent) *, int);
153 
154 #define	history_def_setsize(p, num)(void) (((history_t *)p)->max = (num))
155 #define	history_def_getsize(p)  (((history_t *)p)->cur)
156 #define	history_def_getunique(p) (((((history_t *)p)->flags) & H_UNIQUE) != 0)
157 #define	history_def_setunique(p, uni) \
158     if (uni) \
159 	(((history_t *)p)->flags) |= H_UNIQUE; \
160     else \
161 	(((history_t *)p)->flags) &= ~H_UNIQUE
162 
163 #define	he_strerror(code)	he_errlist[code]
164 #define	he_seterrev(evp, code)	{\
165 				    evp->num = code;\
166 				    evp->str = he_strerror(code);\
167 				}
168 
169 /* error messages */
170 static const Char *const he_errlist[] = {
171 	STR("OK"),
172 	STR("unknown error"),
173 	STR("malloc() failed"),
174 	STR("first event not found"),
175 	STR("last event not found"),
176 	STR("empty list"),
177 	STR("no next event"),
178 	STR("no previous event"),
179 	STR("current event is invalid"),
180 	STR("event not found"),
181 	STR("can't read history from file"),
182 	STR("can't write history"),
183 	STR("required parameter(s) not supplied"),
184 	STR("history size negative"),
185 	STR("function not allowed with other history-functions-set the default"),
186 	STR("bad parameters")
187 };
188 /* error codes */
189 #define	_HE_OK                   0
190 #define	_HE_UNKNOWN		 1
191 #define	_HE_MALLOC_FAILED        2
192 #define	_HE_FIRST_NOTFOUND       3
193 #define	_HE_LAST_NOTFOUND        4
194 #define	_HE_EMPTY_LIST           5
195 #define	_HE_END_REACHED          6
196 #define	_HE_START_REACHED	 7
197 #define	_HE_CURR_INVALID	 8
198 #define	_HE_NOT_FOUND		 9
199 #define	_HE_HIST_READ		10
200 #define	_HE_HIST_WRITE		11
201 #define	_HE_PARAM_MISSING	12
202 #define	_HE_SIZE_NEGATIVE	13
203 #define	_HE_NOT_ALLOWED		14
204 #define	_HE_BAD_PARAM		15
205 
206 /* history_def_first():
207  *	Default function to return the first event in the history.
208  */
209 private int
210 history_def_first(void *p, TYPE(HistEvent) *ev)
211 {
212 	history_t *h = (history_t *) p;
213 
214 	h->cursor = h->list.next;
215 	if (h->cursor != &h->list)
216 		*ev = h->cursor->ev;
217 	else {
218 		he_seterrev(ev, _HE_FIRST_NOTFOUND);
219 		return -1;
220 	}
221 
222 	return 0;
223 }
224 
225 
226 /* history_def_last():
227  *	Default function to return the last event in the history.
228  */
229 private int
230 history_def_last(void *p, TYPE(HistEvent) *ev)
231 {
232 	history_t *h = (history_t *) p;
233 
234 	h->cursor = h->list.prev;
235 	if (h->cursor != &h->list)
236 		*ev = h->cursor->ev;
237 	else {
238 		he_seterrev(ev, _HE_LAST_NOTFOUND);
239 		return -1;
240 	}
241 
242 	return 0;
243 }
244 
245 
246 /* history_def_next():
247  *	Default function to return the next event in the history.
248  */
249 private int
250 history_def_next(void *p, TYPE(HistEvent) *ev)
251 {
252 	history_t *h = (history_t *) p;
253 
254 	if (h->cursor == &h->list) {
255 		he_seterrev(ev, _HE_EMPTY_LIST);
256 		return -1;
257 	}
258 
259 	if (h->cursor->next == &h->list) {
260 		he_seterrev(ev, _HE_END_REACHED);
261 		return -1;
262 	}
263 
264         h->cursor = h->cursor->next;
265         *ev = h->cursor->ev;
266 
267 	return 0;
268 }
269 
270 
271 /* history_def_prev():
272  *	Default function to return the previous event in the history.
273  */
274 private int
275 history_def_prev(void *p, TYPE(HistEvent) *ev)
276 {
277 	history_t *h = (history_t *) p;
278 
279 	if (h->cursor == &h->list) {
280 		he_seterrev(ev,
281 		    (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
282 		return -1;
283 	}
284 
285 	if (h->cursor->prev == &h->list) {
286 		he_seterrev(ev, _HE_START_REACHED);
287 		return -1;
288 	}
289 
290         h->cursor = h->cursor->prev;
291         *ev = h->cursor->ev;
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(void *p, TYPE(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(void *p, TYPE(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_set_nth():
345  *	Default function to set the current event in the history to the
346  *	n-th one.
347  */
348 private int
349 history_set_nth(void *p, TYPE(HistEvent) *ev, int n)
350 {
351 	history_t *h = (history_t *) p;
352 
353 	if (h->cur == 0) {
354 		he_seterrev(ev, _HE_EMPTY_LIST);
355 		return -1;
356 	}
357 	for (h->cursor = h->list.prev; h->cursor != &h->list;
358 	    h->cursor = h->cursor->prev)
359 		if (n-- <= 0)
360 			break;
361 	if (h->cursor == &h->list) {
362 		he_seterrev(ev, _HE_NOT_FOUND);
363 		return -1;
364 	}
365 	return 0;
366 }
367 
368 
369 /* history_def_add():
370  *	Append string to element
371  */
372 private int
373 history_def_add(void *p, TYPE(HistEvent) *ev, const Char *str)
374 {
375 	history_t *h = (history_t *) p;
376 	size_t len;
377 	Char *s;
378 	HistEventPrivate *evp = (void *)&h->cursor->ev;
379 
380 	if (h->cursor == &h->list)
381 		return history_def_enter(p, ev, str);
382 	len = Strlen(evp->str) + Strlen(str) + 1;
383 	s = h_malloc(len * sizeof(*s));
384 	if (s == NULL) {
385 		he_seterrev(ev, _HE_MALLOC_FAILED);
386 		return -1;
387 	}
388 	(void) Strncpy(s, h->cursor->ev.str, len);
389         s[len - 1] = '\0';
390 	(void) Strncat(s, str, len - Strlen(s) - 1);
391 	h_free(evp->str);
392 	evp->str = s;
393 	*ev = h->cursor->ev;
394 	return 0;
395 }
396 
397 
398 private int
399 history_deldata_nth(history_t *h, TYPE(HistEvent) *ev,
400     int num, void **data)
401 {
402 	if (history_set_nth(h, ev, num) != 0)
403 		return -1;
404 	/* magic value to skip delete (just set to n-th history) */
405 	if (data == (void **)-1)
406 		return 0;
407 	ev->str = Strdup(h->cursor->ev.str);
408 	ev->num = h->cursor->ev.num;
409 	if (data)
410 		*data = h->cursor->data;
411 	history_def_delete(h, ev, h->cursor);
412 	return 0;
413 }
414 
415 
416 /* history_def_del():
417  *	Delete element hp of the h list
418  */
419 /* ARGSUSED */
420 private int
421 history_def_del(void *p, TYPE(HistEvent) *ev __attribute__((__unused__)),
422     const int num)
423 {
424 	history_t *h = (history_t *) p;
425 	if (history_def_set(h, ev, num) != 0)
426 		return -1;
427 	ev->str = Strdup(h->cursor->ev.str);
428 	ev->num = h->cursor->ev.num;
429 	history_def_delete(h, ev, h->cursor);
430 	return 0;
431 }
432 
433 
434 /* history_def_delete():
435  *	Delete element hp of the h list
436  */
437 /* ARGSUSED */
438 private void
439 history_def_delete(history_t *h,
440 		   TYPE(HistEvent) *ev __attribute__((__unused__)), hentry_t *hp)
441 {
442 	HistEventPrivate *evp = (void *)&hp->ev;
443 	if (hp == &h->list)
444 		abort();
445 	if (h->cursor == hp) {
446 		h->cursor = hp->prev;
447 		if (h->cursor == &h->list)
448 			h->cursor = hp->next;
449 	}
450 	hp->prev->next = hp->next;
451 	hp->next->prev = hp->prev;
452 	h_free(evp->str);
453 	h_free(hp);
454 	h->cur--;
455 }
456 
457 
458 /* history_def_insert():
459  *	Insert element with string str in the h list
460  */
461 private int
462 history_def_insert(history_t *h, TYPE(HistEvent) *ev, const Char *str)
463 {
464 	hentry_t *c;
465 
466 	c = h_malloc(sizeof(*c));
467 	if (c == NULL)
468 		goto oomem;
469 	if ((c->ev.str = h_strdup(str)) == NULL) {
470 		h_free(c);
471 		goto oomem;
472 	}
473 	c->data = NULL;
474 	c->ev.num = ++h->eventid;
475 	c->next = h->list.next;
476 	c->prev = &h->list;
477 	h->list.next->prev = c;
478 	h->list.next = c;
479 	h->cur++;
480 	h->cursor = c;
481 
482 	*ev = c->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(void *p, TYPE(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(void **p, TYPE(HistEvent) *ev __attribute__((__unused__)), int n)
522 {
523 	history_t *h = (history_t *) h_malloc(sizeof(*h));
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 = h;
538 	return 0;
539 }
540 
541 
542 /* history_def_clear():
543  *	Default history cleanup function
544  */
545 private void
546 history_def_clear(void *p, TYPE(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->cursor = &h->list;
553 	h->eventid = 0;
554 	h->cur = 0;
555 }
556 
557 
558 
559 
560 /************************************************************************/
561 
562 /* history_init():
563  *	Initialization function.
564  */
565 public TYPE(History) *
566 FUN(history,init)(void)
567 {
568 	TYPE(HistEvent) ev;
569 	TYPE(History) *h = (TYPE(History) *) h_malloc(sizeof(*h));
570 	if (h == NULL)
571 		return NULL;
572 
573 	if (history_def_init(&h->h_ref, &ev, 0) == -1) {
574 		h_free(h);
575 		return NULL;
576 	}
577 	h->h_ent = -1;
578 	h->h_next = history_def_next;
579 	h->h_first = history_def_first;
580 	h->h_last = history_def_last;
581 	h->h_prev = history_def_prev;
582 	h->h_curr = history_def_curr;
583 	h->h_set = history_def_set;
584 	h->h_clear = history_def_clear;
585 	h->h_enter = history_def_enter;
586 	h->h_add = history_def_add;
587 	h->h_del = history_def_del;
588 
589 	return h;
590 }
591 
592 
593 /* history_end():
594  *	clean up history;
595  */
596 public void
597 FUN(history,end)(TYPE(History) *h)
598 {
599 	TYPE(HistEvent) ev;
600 
601 	if (h->h_next == history_def_next)
602 		history_def_clear(h->h_ref, &ev);
603 	h_free(h->h_ref);
604 	h_free(h);
605 }
606 
607 
608 
609 /* history_setsize():
610  *	Set history number of events
611  */
612 private int
613 history_setsize(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
614 {
615 
616 	if (h->h_next != history_def_next) {
617 		he_seterrev(ev, _HE_NOT_ALLOWED);
618 		return -1;
619 	}
620 	if (num < 0) {
621 		he_seterrev(ev, _HE_BAD_PARAM);
622 		return -1;
623 	}
624 	history_def_setsize(h->h_ref, num);
625 	return 0;
626 }
627 
628 
629 /* history_getsize():
630  *      Get number of events currently in history
631  */
632 private int
633 history_getsize(TYPE(History) *h, TYPE(HistEvent) *ev)
634 {
635 	if (h->h_next != history_def_next) {
636 		he_seterrev(ev, _HE_NOT_ALLOWED);
637 		return -1;
638 	}
639 	ev->num = history_def_getsize(h->h_ref);
640 	if (ev->num < -1) {
641 		he_seterrev(ev, _HE_SIZE_NEGATIVE);
642 		return -1;
643 	}
644 	return 0;
645 }
646 
647 
648 /* history_setunique():
649  *	Set if adjacent equal events should not be entered in history.
650  */
651 private int
652 history_setunique(TYPE(History) *h, TYPE(HistEvent) *ev, int uni)
653 {
654 
655 	if (h->h_next != history_def_next) {
656 		he_seterrev(ev, _HE_NOT_ALLOWED);
657 		return -1;
658 	}
659 	history_def_setunique(h->h_ref, uni);
660 	return 0;
661 }
662 
663 
664 /* history_getunique():
665  *	Get if adjacent equal events should not be entered in history.
666  */
667 private int
668 history_getunique(TYPE(History) *h, TYPE(HistEvent) *ev)
669 {
670 	if (h->h_next != history_def_next) {
671 		he_seterrev(ev, _HE_NOT_ALLOWED);
672 		return -1;
673 	}
674 	ev->num = history_def_getunique(h->h_ref);
675 	return 0;
676 }
677 
678 
679 /* history_set_fun():
680  *	Set history functions
681  */
682 private int
683 history_set_fun(TYPE(History) *h, TYPE(History) *nh)
684 {
685 	TYPE(HistEvent) ev;
686 
687 	if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
688 	    nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
689 	    nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
690 	    nh->h_del == NULL || nh->h_ref == NULL) {
691 		if (h->h_next != history_def_next) {
692 			if (history_def_init(&h->h_ref, &ev, 0) == -1)
693 				return -1;
694 			h->h_first = history_def_first;
695 			h->h_next = history_def_next;
696 			h->h_last = history_def_last;
697 			h->h_prev = history_def_prev;
698 			h->h_curr = history_def_curr;
699 			h->h_set = history_def_set;
700 			h->h_clear = history_def_clear;
701 			h->h_enter = history_def_enter;
702 			h->h_add = history_def_add;
703 			h->h_del = history_def_del;
704 		}
705 		return -1;
706 	}
707 	if (h->h_next == history_def_next)
708 		history_def_clear(h->h_ref, &ev);
709 
710 	h->h_ent = -1;
711 	h->h_first = nh->h_first;
712 	h->h_next = nh->h_next;
713 	h->h_last = nh->h_last;
714 	h->h_prev = nh->h_prev;
715 	h->h_curr = nh->h_curr;
716 	h->h_set = nh->h_set;
717 	h->h_clear = nh->h_clear;
718 	h->h_enter = nh->h_enter;
719 	h->h_add = nh->h_add;
720 	h->h_del = nh->h_del;
721 
722 	return 0;
723 }
724 
725 
726 /* history_load():
727  *	TYPE(History) load function
728  */
729 private int
730 history_load(TYPE(History) *h, const char *fname)
731 {
732 	FILE *fp;
733 	char *line;
734 	size_t sz, max_size;
735 	char *ptr;
736 	int i = -1;
737 	TYPE(HistEvent) ev;
738 #ifdef WIDECHAR
739 	static ct_buffer_t conv;
740 #endif
741 
742 	if ((fp = fopen(fname, "r")) == NULL)
743 		return i;
744 
745 	if ((line = fgetln(fp, &sz)) == NULL)
746 		goto done;
747 
748 	if (strncmp(line, hist_cookie, sz) != 0)
749 		goto done;
750 
751 	ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
752 	if (ptr == NULL)
753 		goto done;
754 	for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
755 		char c = line[sz];
756 
757 		if (sz != 0 && line[sz - 1] == '\n')
758 			line[--sz] = '\0';
759 		else
760 			line[sz] = '\0';
761 
762 		if (max_size < sz) {
763 			char *nptr;
764 			max_size = (sz + 1024) & (size_t)~1023;
765 			nptr = h_realloc(ptr, max_size * sizeof(*ptr));
766 			if (nptr == NULL) {
767 				i = -1;
768 				goto oomem;
769 			}
770 			ptr = nptr;
771 		}
772 		(void) strunvis(ptr, line);
773 		line[sz] = c;
774 		if (HENTER(h, &ev, ct_decode_string(ptr, &conv)) == -1) {
775 			i = -1;
776 			goto oomem;
777 		}
778 	}
779 oomem:
780 	h_free(ptr);
781 done:
782 	(void) fclose(fp);
783 	return i;
784 }
785 
786 
787 /* history_save():
788  *	TYPE(History) save function
789  */
790 private int
791 history_save(TYPE(History) *h, const char *fname)
792 {
793 	FILE *fp;
794 	TYPE(HistEvent) ev;
795 	int i = -1, retval;
796 	size_t len, max_size;
797 	char *ptr;
798 	const char *str;
799 #ifdef WIDECHAR
800 	static ct_buffer_t conv;
801 #endif
802 
803 	if ((fp = fopen(fname, "w")) == NULL)
804 		return -1;
805 
806 	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
807 		goto done;
808 	if (fputs(hist_cookie, fp) == EOF)
809 		goto done;
810 	ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
811 	if (ptr == NULL)
812 		goto done;
813 	for (i = 0, retval = HLAST(h, &ev);
814 	    retval != -1;
815 	    retval = HPREV(h, &ev), i++) {
816 		str = ct_encode_string(ev.str, &conv);
817 		len = strlen(str) * 4;
818 		if (len >= max_size) {
819 			char *nptr;
820 			max_size = (len + 1024) & (size_t)~1023;
821 			nptr = h_realloc(ptr, max_size * sizeof(*ptr));
822 			if (nptr == NULL) {
823 				i = -1;
824 				goto oomem;
825 			}
826 			ptr = nptr;
827 		}
828 		(void) strvis(ptr, str, VIS_WHITE);
829 		(void) fprintf(fp, "%s\n", ptr);
830 	}
831 oomem:
832 	h_free(ptr);
833 done:
834 	(void) fclose(fp);
835 	return i;
836 }
837 
838 
839 /* history_prev_event():
840  *	Find the previous event, with number given
841  */
842 private int
843 history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
844 {
845 	int retval;
846 
847 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
848 		if (ev->num == num)
849 			return 0;
850 
851 	he_seterrev(ev, _HE_NOT_FOUND);
852 	return -1;
853 }
854 
855 
856 private int
857 history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d)
858 {
859 	int retval;
860 
861 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
862 		if (ev->num == num) {
863 			if (d)
864 				*d = ((history_t *)h->h_ref)->cursor->data;
865 			return 0;
866 		}
867 
868 	he_seterrev(ev, _HE_NOT_FOUND);
869 	return -1;
870 }
871 
872 
873 /* history_next_event():
874  *	Find the next event, with number given
875  */
876 private int
877 history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
878 {
879 	int retval;
880 
881 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
882 		if (ev->num == num)
883 			return 0;
884 
885 	he_seterrev(ev, _HE_NOT_FOUND);
886 	return -1;
887 }
888 
889 
890 /* history_prev_string():
891  *	Find the previous event beginning with string
892  */
893 private int
894 history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
895 {
896 	size_t len = Strlen(str);
897 	int retval;
898 
899 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
900 		if (Strncmp(str, ev->str, len) == 0)
901 			return 0;
902 
903 	he_seterrev(ev, _HE_NOT_FOUND);
904 	return -1;
905 }
906 
907 
908 /* history_next_string():
909  *	Find the next event beginning with string
910  */
911 private int
912 history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
913 {
914 	size_t len = Strlen(str);
915 	int retval;
916 
917 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
918 		if (Strncmp(str, ev->str, len) == 0)
919 			return 0;
920 
921 	he_seterrev(ev, _HE_NOT_FOUND);
922 	return -1;
923 }
924 
925 
926 /* history():
927  *	User interface to history functions.
928  */
929 int
930 FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...)
931 {
932 	va_list va;
933 	const Char *str;
934 	int retval;
935 
936 	va_start(va, fun);
937 
938 	he_seterrev(ev, _HE_OK);
939 
940 	switch (fun) {
941 	case H_GETSIZE:
942 		retval = history_getsize(h, ev);
943 		break;
944 
945 	case H_SETSIZE:
946 		retval = history_setsize(h, ev, va_arg(va, int));
947 		break;
948 
949 	case H_GETUNIQUE:
950 		retval = history_getunique(h, ev);
951 		break;
952 
953 	case H_SETUNIQUE:
954 		retval = history_setunique(h, ev, va_arg(va, int));
955 		break;
956 
957 	case H_ADD:
958 		str = va_arg(va, const Char *);
959 		retval = HADD(h, ev, str);
960 		break;
961 
962 	case H_DEL:
963 		retval = HDEL(h, ev, va_arg(va, const int));
964 		break;
965 
966 	case H_ENTER:
967 		str = va_arg(va, const Char *);
968 		if ((retval = HENTER(h, ev, str)) != -1)
969 			h->h_ent = ev->num;
970 		break;
971 
972 	case H_APPEND:
973 		str = va_arg(va, const Char *);
974 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
975 			retval = HADD(h, ev, str);
976 		break;
977 
978 	case H_FIRST:
979 		retval = HFIRST(h, ev);
980 		break;
981 
982 	case H_NEXT:
983 		retval = HNEXT(h, ev);
984 		break;
985 
986 	case H_LAST:
987 		retval = HLAST(h, ev);
988 		break;
989 
990 	case H_PREV:
991 		retval = HPREV(h, ev);
992 		break;
993 
994 	case H_CURR:
995 		retval = HCURR(h, ev);
996 		break;
997 
998 	case H_SET:
999 		retval = HSET(h, ev, va_arg(va, const int));
1000 		break;
1001 
1002 	case H_CLEAR:
1003 		HCLEAR(h, ev);
1004 		retval = 0;
1005 		break;
1006 
1007 	case H_LOAD:
1008 		retval = history_load(h, va_arg(va, const char *));
1009 		if (retval == -1)
1010 			he_seterrev(ev, _HE_HIST_READ);
1011 		break;
1012 
1013 	case H_SAVE:
1014 		retval = history_save(h, va_arg(va, const char *));
1015 		if (retval == -1)
1016 			he_seterrev(ev, _HE_HIST_WRITE);
1017 		break;
1018 
1019 	case H_PREV_EVENT:
1020 		retval = history_prev_event(h, ev, va_arg(va, int));
1021 		break;
1022 
1023 	case H_NEXT_EVENT:
1024 		retval = history_next_event(h, ev, va_arg(va, int));
1025 		break;
1026 
1027 	case H_PREV_STR:
1028 		retval = history_prev_string(h, ev, va_arg(va, const Char *));
1029 		break;
1030 
1031 	case H_NEXT_STR:
1032 		retval = history_next_string(h, ev, va_arg(va, const Char *));
1033 		break;
1034 
1035 	case H_FUNC:
1036 	{
1037 		TYPE(History) hf;
1038 
1039 		hf.h_ref = va_arg(va, void *);
1040 		h->h_ent = -1;
1041 		hf.h_first = va_arg(va, history_gfun_t);
1042 		hf.h_next = va_arg(va, history_gfun_t);
1043 		hf.h_last = va_arg(va, history_gfun_t);
1044 		hf.h_prev = va_arg(va, history_gfun_t);
1045 		hf.h_curr = va_arg(va, history_gfun_t);
1046 		hf.h_set = va_arg(va, history_sfun_t);
1047 		hf.h_clear = va_arg(va, history_vfun_t);
1048 		hf.h_enter = va_arg(va, history_efun_t);
1049 		hf.h_add = va_arg(va, history_efun_t);
1050 		hf.h_del = va_arg(va, history_sfun_t);
1051 
1052 		if ((retval = history_set_fun(h, &hf)) == -1)
1053 			he_seterrev(ev, _HE_PARAM_MISSING);
1054 		break;
1055 	}
1056 
1057 	case H_END:
1058 		FUN(history,end)(h);
1059 		retval = 0;
1060 		break;
1061 
1062 	case H_NEXT_EVDATA:
1063 	{
1064 		int num = va_arg(va, int);
1065 		void **d = va_arg(va, void **);
1066 		retval = history_next_evdata(h, ev, num, d);
1067 		break;
1068 	}
1069 
1070 	case H_DELDATA:
1071 	{
1072 		int num = va_arg(va, int);
1073 		void **d = va_arg(va, void **);
1074 		retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d);
1075 		break;
1076 	}
1077 
1078 	case H_REPLACE: /* only use after H_NEXT_EVDATA */
1079 	{
1080 		const Char *line = va_arg(va, const Char *);
1081 		void *d = va_arg(va, void *);
1082 		const Char *s;
1083 		if(!line || !(s = Strdup(line))) {
1084 			retval = -1;
1085 			break;
1086 		}
1087 		((history_t *)h->h_ref)->cursor->ev.str = s;
1088 		((history_t *)h->h_ref)->cursor->data = d;
1089 		retval = 0;
1090 		break;
1091 	}
1092 
1093 	default:
1094 		retval = -1;
1095 		he_seterrev(ev, _HE_UNKNOWN);
1096 		break;
1097 	}
1098 	va_end(va);
1099 	return retval;
1100 }
1101