xref: /dragonfly/contrib/libedit/src/history.c (revision ae071d8d)
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 #ifndef NARROWCHAR
36 #include "config.h"
37 #endif
38 
39 #if !defined(lint) && !defined(SCCSID)
40 #if 0
41 static char sccsid[] = "@(#)history.c	8.1 (Berkeley) 6/4/93";
42 #else
43 __RCSID("$NetBSD: history.c,v 1.46 2011/11/18 20:39:18 christos Exp $");
44 #endif
45 #endif /* not lint && not SCCSID */
46 
47 /*
48  * hist.c: TYPE(History) access functions
49  */
50 #include <string.h>
51 #include <stdlib.h>
52 #include <stdarg.h>
53 #include <vis.h>
54 #include <sys/stat.h>
55 
56 static const char hist_cookie[] = "_HiStOrY_V2_\n";
57 
58 #include "histedit.h"
59 #include "chartype.h"
60 
61 typedef int (*history_gfun_t)(void *, TYPE(HistEvent) *);
62 typedef int (*history_efun_t)(void *, TYPE(HistEvent) *, const Char *);
63 typedef void (*history_vfun_t)(void *, TYPE(HistEvent) *);
64 typedef int (*history_sfun_t)(void *, TYPE(HistEvent) *, const int);
65 
66 struct TYPE(history) {
67 	void *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(TYPE(History) *, TYPE(HistEvent) *, int);
105 private int history_getsize(TYPE(History) *, TYPE(HistEvent) *);
106 private int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int);
107 private int history_getunique(TYPE(History) *, TYPE(HistEvent) *);
108 private int history_set_fun(TYPE(History) *, TYPE(History) *);
109 private int history_load(TYPE(History) *, const char *);
110 private int history_save(TYPE(History) *, const char *);
111 private int history_prev_event(TYPE(History) *, TYPE(HistEvent) *, int);
112 private int history_next_event(TYPE(History) *, TYPE(HistEvent) *, int);
113 private int history_next_string(TYPE(History) *, TYPE(HistEvent) *, const Char *);
114 private int history_prev_string(TYPE(History) *, TYPE(HistEvent) *, const Char *);
115 
116 
117 /***********************************************************************/
118 
119 /*
120  * Builtin- history implementation
121  */
122 typedef struct hentry_t {
123 	TYPE(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;		/* TYPE(History) flags		*/
136 #define H_UNIQUE	1	/* Store only unique elements	*/
137 } history_t;
138 
139 private int history_def_next(void *, TYPE(HistEvent) *);
140 private int history_def_first(void *, TYPE(HistEvent) *);
141 private int history_def_prev(void *, TYPE(HistEvent) *);
142 private int history_def_last(void *, TYPE(HistEvent) *);
143 private int history_def_curr(void *, TYPE(HistEvent) *);
144 private int history_def_set(void *, TYPE(HistEvent) *, const int);
145 private void history_def_clear(void *, TYPE(HistEvent) *);
146 private int history_def_enter(void *, TYPE(HistEvent) *, const Char *);
147 private int history_def_add(void *, TYPE(HistEvent) *, const Char *);
148 private int history_def_del(void *, TYPE(HistEvent) *, const int);
149 
150 private int history_def_init(void **, TYPE(HistEvent) *, int);
151 private int history_def_insert(history_t *, TYPE(HistEvent) *, const Char *);
152 private void history_def_delete(history_t *, TYPE(HistEvent) *, hentry_t *);
153 
154 private int history_deldata_nth(history_t *, TYPE(HistEvent) *, int, void **);
155 private int history_set_nth(void *, TYPE(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 	STR("OK"),
175 	STR("unknown error"),
176 	STR("malloc() failed"),
177 	STR("first event not found"),
178 	STR("last event not found"),
179 	STR("empty list"),
180 	STR("no next event"),
181 	STR("no previous event"),
182 	STR("current event is invalid"),
183 	STR("event not found"),
184 	STR("can't read history from file"),
185 	STR("can't write history"),
186 	STR("required parameter(s) not supplied"),
187 	STR("history size negative"),
188 	STR("function not allowed with other history-functions-set the default"),
189 	STR("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(void *p, TYPE(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(void *p, TYPE(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(void *p, TYPE(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(void *p, TYPE(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(void *p, TYPE(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(void *p, TYPE(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(void *p, TYPE(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(void *p, TYPE(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 = h_malloc(len * sizeof(*s));
387 	if (s == NULL) {
388 		he_seterrev(ev, _HE_MALLOC_FAILED);
389 		return -1;
390 	}
391 	(void) Strncpy(s, h->cursor->ev.str, len);
392         s[len - 1] = '\0';
393 	(void) Strncat(s, str, len - Strlen(s) - 1);
394 	h_free(evp->str);
395 	evp->str = s;
396 	*ev = h->cursor->ev;
397 	return 0;
398 }
399 
400 
401 private int
402 history_deldata_nth(history_t *h, TYPE(HistEvent) *ev,
403     int num, void **data)
404 {
405 	if (history_set_nth(h, ev, num) != 0)
406 		return -1;
407 	/* magic value to skip delete (just set to n-th history) */
408 	if (data == (void **)-1)
409 		return 0;
410 	ev->str = Strdup(h->cursor->ev.str);
411 	ev->num = h->cursor->ev.num;
412 	if (data)
413 		*data = h->cursor->data;
414 	history_def_delete(h, ev, h->cursor);
415 	return 0;
416 }
417 
418 
419 /* history_def_del():
420  *	Delete element hp of the h list
421  */
422 /* ARGSUSED */
423 private int
424 history_def_del(void *p, TYPE(HistEvent) *ev __attribute__((__unused__)),
425     const int num)
426 {
427 	history_t *h = (history_t *) p;
428 	if (history_def_set(h, ev, num) != 0)
429 		return -1;
430 	ev->str = Strdup(h->cursor->ev.str);
431 	ev->num = h->cursor->ev.num;
432 	history_def_delete(h, ev, h->cursor);
433 	return 0;
434 }
435 
436 
437 /* history_def_delete():
438  *	Delete element hp of the h list
439  */
440 /* ARGSUSED */
441 private void
442 history_def_delete(history_t *h,
443 		   TYPE(HistEvent) *ev __attribute__((__unused__)), hentry_t *hp)
444 {
445 	HistEventPrivate *evp = (void *)&hp->ev;
446 	if (hp == &h->list)
447 		abort();
448 	if (h->cursor == hp) {
449 		h->cursor = hp->prev;
450 		if (h->cursor == &h->list)
451 			h->cursor = hp->next;
452 	}
453 	hp->prev->next = hp->next;
454 	hp->next->prev = hp->prev;
455 	h_free(evp->str);
456 	h_free(hp);
457 	h->cur--;
458 }
459 
460 
461 /* history_def_insert():
462  *	Insert element with string str in the h list
463  */
464 private int
465 history_def_insert(history_t *h, TYPE(HistEvent) *ev, const Char *str)
466 {
467 	hentry_t *c;
468 
469 	c = h_malloc(sizeof(*c));
470 	if (c == NULL)
471 		goto oomem;
472 	if ((c->ev.str = h_strdup(str)) == NULL) {
473 		h_free(c);
474 		goto oomem;
475 	}
476 	c->data = NULL;
477 	c->ev.num = ++h->eventid;
478 	c->next = h->list.next;
479 	c->prev = &h->list;
480 	h->list.next->prev = c;
481 	h->list.next = c;
482 	h->cur++;
483 	h->cursor = c;
484 
485 	*ev = c->ev;
486 	return 0;
487 oomem:
488 	he_seterrev(ev, _HE_MALLOC_FAILED);
489 	return -1;
490 }
491 
492 
493 /* history_def_enter():
494  *	Default function to enter an item in the history
495  */
496 private int
497 history_def_enter(void *p, TYPE(HistEvent) *ev, const Char *str)
498 {
499 	history_t *h = (history_t *) p;
500 
501 	if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
502 	    Strcmp(h->list.next->ev.str, str) == 0)
503 	    return 0;
504 
505 	if (history_def_insert(h, ev, str) == -1)
506 		return -1;	/* error, keep error message */
507 
508 	/*
509          * Always keep at least one entry.
510          * This way we don't have to check for the empty list.
511          */
512 	while (h->cur > h->max && h->cur > 0)
513 		history_def_delete(h, ev, h->list.prev);
514 
515 	return 1;
516 }
517 
518 
519 /* history_def_init():
520  *	Default history initialization function
521  */
522 /* ARGSUSED */
523 private int
524 history_def_init(void **p, TYPE(HistEvent) *ev __attribute__((__unused__)), int n)
525 {
526 	history_t *h = (history_t *) h_malloc(sizeof(*h));
527 	if (h == NULL)
528 		return -1;
529 
530 	if (n <= 0)
531 		n = 0;
532 	h->eventid = 0;
533 	h->cur = 0;
534 	h->max = n;
535 	h->list.next = h->list.prev = &h->list;
536 	h->list.ev.str = NULL;
537 	h->list.ev.num = 0;
538 	h->cursor = &h->list;
539 	h->flags = 0;
540 	*p = h;
541 	return 0;
542 }
543 
544 
545 /* history_def_clear():
546  *	Default history cleanup function
547  */
548 private void
549 history_def_clear(void *p, TYPE(HistEvent) *ev)
550 {
551 	history_t *h = (history_t *) p;
552 
553 	while (h->list.prev != &h->list)
554 		history_def_delete(h, ev, h->list.prev);
555 	h->cursor = &h->list;
556 	h->eventid = 0;
557 	h->cur = 0;
558 }
559 
560 
561 
562 
563 /************************************************************************/
564 
565 /* history_init():
566  *	Initialization function.
567  */
568 public TYPE(History) *
569 FUN(history,init)(void)
570 {
571 	TYPE(HistEvent) ev;
572 	TYPE(History) *h = (TYPE(History) *) h_malloc(sizeof(*h));
573 	if (h == NULL)
574 		return NULL;
575 
576 	if (history_def_init(&h->h_ref, &ev, 0) == -1) {
577 		h_free(h);
578 		return NULL;
579 	}
580 	h->h_ent = -1;
581 	h->h_next = history_def_next;
582 	h->h_first = history_def_first;
583 	h->h_last = history_def_last;
584 	h->h_prev = history_def_prev;
585 	h->h_curr = history_def_curr;
586 	h->h_set = history_def_set;
587 	h->h_clear = history_def_clear;
588 	h->h_enter = history_def_enter;
589 	h->h_add = history_def_add;
590 	h->h_del = history_def_del;
591 
592 	return h;
593 }
594 
595 
596 /* history_end():
597  *	clean up history;
598  */
599 public void
600 FUN(history,end)(TYPE(History) *h)
601 {
602 	TYPE(HistEvent) ev;
603 
604 	if (h->h_next == history_def_next)
605 		history_def_clear(h->h_ref, &ev);
606 	h_free(h->h_ref);
607 	h_free(h);
608 }
609 
610 
611 
612 /* history_setsize():
613  *	Set history number of events
614  */
615 private int
616 history_setsize(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
617 {
618 
619 	if (h->h_next != history_def_next) {
620 		he_seterrev(ev, _HE_NOT_ALLOWED);
621 		return -1;
622 	}
623 	if (num < 0) {
624 		he_seterrev(ev, _HE_BAD_PARAM);
625 		return -1;
626 	}
627 	history_def_setsize(h->h_ref, num);
628 	return 0;
629 }
630 
631 
632 /* history_getsize():
633  *      Get number of events currently in history
634  */
635 private int
636 history_getsize(TYPE(History) *h, TYPE(HistEvent) *ev)
637 {
638 	if (h->h_next != history_def_next) {
639 		he_seterrev(ev, _HE_NOT_ALLOWED);
640 		return -1;
641 	}
642 	ev->num = history_def_getsize(h->h_ref);
643 	if (ev->num < -1) {
644 		he_seterrev(ev, _HE_SIZE_NEGATIVE);
645 		return -1;
646 	}
647 	return 0;
648 }
649 
650 
651 /* history_setunique():
652  *	Set if adjacent equal events should not be entered in history.
653  */
654 private int
655 history_setunique(TYPE(History) *h, TYPE(HistEvent) *ev, int uni)
656 {
657 
658 	if (h->h_next != history_def_next) {
659 		he_seterrev(ev, _HE_NOT_ALLOWED);
660 		return -1;
661 	}
662 	history_def_setunique(h->h_ref, uni);
663 	return 0;
664 }
665 
666 
667 /* history_getunique():
668  *	Get if adjacent equal events should not be entered in history.
669  */
670 private int
671 history_getunique(TYPE(History) *h, TYPE(HistEvent) *ev)
672 {
673 	if (h->h_next != history_def_next) {
674 		he_seterrev(ev, _HE_NOT_ALLOWED);
675 		return -1;
676 	}
677 	ev->num = history_def_getunique(h->h_ref);
678 	return 0;
679 }
680 
681 
682 /* history_set_fun():
683  *	Set history functions
684  */
685 private int
686 history_set_fun(TYPE(History) *h, TYPE(History) *nh)
687 {
688 	TYPE(HistEvent) ev;
689 
690 	if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
691 	    nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
692 	    nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
693 	    nh->h_del == NULL || nh->h_ref == NULL) {
694 		if (h->h_next != history_def_next) {
695 			if (history_def_init(&h->h_ref, &ev, 0) == -1)
696 				return -1;
697 			h->h_first = history_def_first;
698 			h->h_next = history_def_next;
699 			h->h_last = history_def_last;
700 			h->h_prev = history_def_prev;
701 			h->h_curr = history_def_curr;
702 			h->h_set = history_def_set;
703 			h->h_clear = history_def_clear;
704 			h->h_enter = history_def_enter;
705 			h->h_add = history_def_add;
706 			h->h_del = history_def_del;
707 		}
708 		return -1;
709 	}
710 	if (h->h_next == history_def_next)
711 		history_def_clear(h->h_ref, &ev);
712 
713 	h->h_ent = -1;
714 	h->h_first = nh->h_first;
715 	h->h_next = nh->h_next;
716 	h->h_last = nh->h_last;
717 	h->h_prev = nh->h_prev;
718 	h->h_curr = nh->h_curr;
719 	h->h_set = nh->h_set;
720 	h->h_clear = nh->h_clear;
721 	h->h_enter = nh->h_enter;
722 	h->h_add = nh->h_add;
723 	h->h_del = nh->h_del;
724 
725 	return 0;
726 }
727 
728 
729 /* history_load():
730  *	TYPE(History) load function
731  */
732 private int
733 history_load(TYPE(History) *h, const char *fname)
734 {
735 	FILE *fp;
736 	char *line;
737 	size_t sz, max_size;
738 	char *ptr;
739 	int i = -1;
740 	TYPE(HistEvent) ev;
741 #ifdef WIDECHAR
742 	static ct_buffer_t conv;
743 #endif
744 
745 	if ((fp = fopen(fname, "r")) == NULL)
746 		return i;
747 
748 	if ((line = fgetln(fp, &sz)) == NULL)
749 		goto done;
750 
751 	if (strncmp(line, hist_cookie, sz) != 0)
752 		goto done;
753 
754 	ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
755 	if (ptr == NULL)
756 		goto done;
757 	for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
758 		char c = line[sz];
759 
760 		if (sz != 0 && line[sz - 1] == '\n')
761 			line[--sz] = '\0';
762 		else
763 			line[sz] = '\0';
764 
765 		if (max_size < sz) {
766 			char *nptr;
767 			max_size = (sz + 1024) & (size_t)~1023;
768 			nptr = h_realloc(ptr, max_size * sizeof(*ptr));
769 			if (nptr == NULL) {
770 				i = -1;
771 				goto oomem;
772 			}
773 			ptr = nptr;
774 		}
775 		(void) strunvis(ptr, line);
776 		line[sz] = c;
777 		if (HENTER(h, &ev, ct_decode_string(ptr, &conv)) == -1) {
778 			i = -1;
779 			goto oomem;
780 		}
781 	}
782 oomem:
783 	h_free(ptr);
784 done:
785 	(void) fclose(fp);
786 	return i;
787 }
788 
789 
790 /* history_save():
791  *	TYPE(History) save function
792  */
793 private int
794 history_save(TYPE(History) *h, const char *fname)
795 {
796 	FILE *fp;
797 	TYPE(HistEvent) ev;
798 	int i = -1, retval;
799 	size_t len, max_size;
800 	char *ptr;
801 	const char *str;
802 #ifdef WIDECHAR
803 	static ct_buffer_t conv;
804 #endif
805 
806 	if ((fp = fopen(fname, "w")) == NULL)
807 		return -1;
808 
809 	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
810 		goto done;
811 	if (fputs(hist_cookie, fp) == EOF)
812 		goto done;
813 	ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
814 	if (ptr == NULL)
815 		goto done;
816 	for (i = 0, retval = HLAST(h, &ev);
817 	    retval != -1;
818 	    retval = HPREV(h, &ev), i++) {
819 		str = ct_encode_string(ev.str, &conv);
820 		len = strlen(str) * 4;
821 		if (len >= max_size) {
822 			char *nptr;
823 			max_size = (len + 1024) & (size_t)~1023;
824 			nptr = h_realloc(ptr, max_size * sizeof(*ptr));
825 			if (nptr == NULL) {
826 				i = -1;
827 				goto oomem;
828 			}
829 			ptr = nptr;
830 		}
831 		(void) strvis(ptr, str, VIS_WHITE);
832 		(void) fprintf(fp, "%s\n", ptr);
833 	}
834 oomem:
835 	h_free(ptr);
836 done:
837 	(void) fclose(fp);
838 	return i;
839 }
840 
841 
842 /* history_prev_event():
843  *	Find the previous event, with number given
844  */
845 private int
846 history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
847 {
848 	int retval;
849 
850 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
851 		if (ev->num == num)
852 			return 0;
853 
854 	he_seterrev(ev, _HE_NOT_FOUND);
855 	return -1;
856 }
857 
858 
859 private int
860 history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d)
861 {
862 	int retval;
863 
864 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
865 		if (ev->num == num) {
866 			if (d)
867 				*d = ((history_t *)h->h_ref)->cursor->data;
868 			return 0;
869 		}
870 
871 	he_seterrev(ev, _HE_NOT_FOUND);
872 	return -1;
873 }
874 
875 
876 /* history_next_event():
877  *	Find the next event, with number given
878  */
879 private int
880 history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
881 {
882 	int retval;
883 
884 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
885 		if (ev->num == num)
886 			return 0;
887 
888 	he_seterrev(ev, _HE_NOT_FOUND);
889 	return -1;
890 }
891 
892 
893 /* history_prev_string():
894  *	Find the previous event beginning with string
895  */
896 private int
897 history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
898 {
899 	size_t len = Strlen(str);
900 	int retval;
901 
902 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
903 		if (Strncmp(str, ev->str, len) == 0)
904 			return 0;
905 
906 	he_seterrev(ev, _HE_NOT_FOUND);
907 	return -1;
908 }
909 
910 
911 /* history_next_string():
912  *	Find the next event beginning with string
913  */
914 private int
915 history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
916 {
917 	size_t len = Strlen(str);
918 	int retval;
919 
920 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
921 		if (Strncmp(str, ev->str, len) == 0)
922 			return 0;
923 
924 	he_seterrev(ev, _HE_NOT_FOUND);
925 	return -1;
926 }
927 
928 
929 /* history():
930  *	User interface to history functions.
931  */
932 int
933 FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...)
934 {
935 	va_list va;
936 	const Char *str;
937 	int retval;
938 
939 	va_start(va, fun);
940 
941 	he_seterrev(ev, _HE_OK);
942 
943 	switch (fun) {
944 	case H_GETSIZE:
945 		retval = history_getsize(h, ev);
946 		break;
947 
948 	case H_SETSIZE:
949 		retval = history_setsize(h, ev, va_arg(va, int));
950 		break;
951 
952 	case H_GETUNIQUE:
953 		retval = history_getunique(h, ev);
954 		break;
955 
956 	case H_SETUNIQUE:
957 		retval = history_setunique(h, ev, va_arg(va, int));
958 		break;
959 
960 	case H_ADD:
961 		str = va_arg(va, const Char *);
962 		retval = HADD(h, ev, str);
963 		break;
964 
965 	case H_DEL:
966 		retval = HDEL(h, ev, va_arg(va, const int));
967 		break;
968 
969 	case H_ENTER:
970 		str = va_arg(va, const Char *);
971 		if ((retval = HENTER(h, ev, str)) != -1)
972 			h->h_ent = ev->num;
973 		break;
974 
975 	case H_APPEND:
976 		str = va_arg(va, const Char *);
977 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
978 			retval = HADD(h, ev, str);
979 		break;
980 
981 	case H_FIRST:
982 		retval = HFIRST(h, ev);
983 		break;
984 
985 	case H_NEXT:
986 		retval = HNEXT(h, ev);
987 		break;
988 
989 	case H_LAST:
990 		retval = HLAST(h, ev);
991 		break;
992 
993 	case H_PREV:
994 		retval = HPREV(h, ev);
995 		break;
996 
997 	case H_CURR:
998 		retval = HCURR(h, ev);
999 		break;
1000 
1001 	case H_SET:
1002 		retval = HSET(h, ev, va_arg(va, const int));
1003 		break;
1004 
1005 	case H_CLEAR:
1006 		HCLEAR(h, ev);
1007 		retval = 0;
1008 		break;
1009 
1010 	case H_LOAD:
1011 		retval = history_load(h, va_arg(va, const char *));
1012 		if (retval == -1)
1013 			he_seterrev(ev, _HE_HIST_READ);
1014 		break;
1015 
1016 	case H_SAVE:
1017 		retval = history_save(h, va_arg(va, const char *));
1018 		if (retval == -1)
1019 			he_seterrev(ev, _HE_HIST_WRITE);
1020 		break;
1021 
1022 	case H_PREV_EVENT:
1023 		retval = history_prev_event(h, ev, va_arg(va, int));
1024 		break;
1025 
1026 	case H_NEXT_EVENT:
1027 		retval = history_next_event(h, ev, va_arg(va, int));
1028 		break;
1029 
1030 	case H_PREV_STR:
1031 		retval = history_prev_string(h, ev, va_arg(va, const Char *));
1032 		break;
1033 
1034 	case H_NEXT_STR:
1035 		retval = history_next_string(h, ev, va_arg(va, const Char *));
1036 		break;
1037 
1038 	case H_FUNC:
1039 	{
1040 		TYPE(History) hf;
1041 
1042 		hf.h_ref = va_arg(va, void *);
1043 		h->h_ent = -1;
1044 		hf.h_first = va_arg(va, history_gfun_t);
1045 		hf.h_next = va_arg(va, history_gfun_t);
1046 		hf.h_last = va_arg(va, history_gfun_t);
1047 		hf.h_prev = va_arg(va, history_gfun_t);
1048 		hf.h_curr = va_arg(va, history_gfun_t);
1049 		hf.h_set = va_arg(va, history_sfun_t);
1050 		hf.h_clear = va_arg(va, history_vfun_t);
1051 		hf.h_enter = va_arg(va, history_efun_t);
1052 		hf.h_add = va_arg(va, history_efun_t);
1053 		hf.h_del = va_arg(va, history_sfun_t);
1054 
1055 		if ((retval = history_set_fun(h, &hf)) == -1)
1056 			he_seterrev(ev, _HE_PARAM_MISSING);
1057 		break;
1058 	}
1059 
1060 	case H_END:
1061 		FUN(history,end)(h);
1062 		retval = 0;
1063 		break;
1064 
1065 	case H_NEXT_EVDATA:
1066 	{
1067 		int num = va_arg(va, int);
1068 		void **d = va_arg(va, void **);
1069 		retval = history_next_evdata(h, ev, num, d);
1070 		break;
1071 	}
1072 
1073 	case H_DELDATA:
1074 	{
1075 		int num = va_arg(va, int);
1076 		void **d = va_arg(va, void **);
1077 		retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d);
1078 		break;
1079 	}
1080 
1081 	case H_REPLACE: /* only use after H_NEXT_EVDATA */
1082 	{
1083 		const Char *line = va_arg(va, const Char *);
1084 		void *d = va_arg(va, void *);
1085 		const Char *s;
1086 		if(!line || !(s = Strdup(line))) {
1087 			retval = -1;
1088 			break;
1089 		}
1090 		((history_t *)h->h_ref)->cursor->ev.str = s;
1091 		((history_t *)h->h_ref)->cursor->data = d;
1092 		retval = 0;
1093 		break;
1094 	}
1095 
1096 	default:
1097 		retval = -1;
1098 		he_seterrev(ev, _HE_UNKNOWN);
1099 		break;
1100 	}
1101 	va_end(va);
1102 	return retval;
1103 }
1104