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