xref: /freebsd-12.1/lib/libedit/history.c (revision 0700a396)
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 #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.47 2014/05/11 01:05:17 christos Exp $");
41 #endif
42 #endif /* not lint && not SCCSID */
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45 
46 /*
47  * hist.c: TYPE(History) access functions
48  */
49 #include <string.h>
50 #include <stdlib.h>
51 #include <stdarg.h>
52 #include <vis.h>
53 #include <sys/stat.h>
54 
55 static const char hist_cookie[] = "_HiStOrY_V2_\n";
56 
57 #include "histedit.h"
58 #include "chartype.h"
59 
60 typedef int (*history_gfun_t)(void *, TYPE(HistEvent) *);
61 typedef int (*history_efun_t)(void *, TYPE(HistEvent) *, const Char *);
62 typedef void (*history_vfun_t)(void *, TYPE(HistEvent) *);
63 typedef int (*history_sfun_t)(void *, TYPE(HistEvent) *, const int);
64 
65 struct TYPE(history) {
66 	void *h_ref;		/* Argument for history fcns	 */
67 	int h_ent;		/* Last entry point for history	 */
68 	history_gfun_t h_first;	/* Get the first element	 */
69 	history_gfun_t h_next;	/* Get the next element		 */
70 	history_gfun_t h_last;	/* Get the last element		 */
71 	history_gfun_t h_prev;	/* Get the previous element	 */
72 	history_gfun_t h_curr;	/* Get the current element	 */
73 	history_sfun_t h_set;	/* Set the current element	 */
74 	history_sfun_t h_del;	/* Set the given element	 */
75 	history_vfun_t h_clear;	/* Clear the history list	 */
76 	history_efun_t h_enter;	/* Add an element		 */
77 	history_efun_t h_add;	/* Append to an element		 */
78 };
79 
80 #define	HNEXT(h, ev)		(*(h)->h_next)((h)->h_ref, ev)
81 #define	HFIRST(h, ev)		(*(h)->h_first)((h)->h_ref, ev)
82 #define	HPREV(h, ev)		(*(h)->h_prev)((h)->h_ref, ev)
83 #define	HLAST(h, ev)		(*(h)->h_last)((h)->h_ref, ev)
84 #define	HCURR(h, ev)		(*(h)->h_curr)((h)->h_ref, ev)
85 #define	HSET(h, ev, n)		(*(h)->h_set)((h)->h_ref, ev, n)
86 #define	HCLEAR(h, ev)		(*(h)->h_clear)((h)->h_ref, ev)
87 #define	HENTER(h, ev, str)	(*(h)->h_enter)((h)->h_ref, ev, str)
88 #define	HADD(h, ev, str)	(*(h)->h_add)((h)->h_ref, ev, str)
89 #define	HDEL(h, ev, n)		(*(h)->h_del)((h)->h_ref, ev, n)
90 
91 #define	h_strdup(a)	Strdup(a)
92 #define	h_malloc(a)	malloc(a)
93 #define	h_realloc(a, b)	realloc((a), (b))
94 #define	h_free(a)	free(a)
95 
96 typedef struct {
97     int		num;
98     Char	*str;
99 } HistEventPrivate;
100 
101 
102 
103 private int history_setsize(TYPE(History) *, TYPE(HistEvent) *, int);
104 private int history_getsize(TYPE(History) *, TYPE(HistEvent) *);
105 private int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int);
106 private int history_getunique(TYPE(History) *, TYPE(HistEvent) *);
107 private int history_set_fun(TYPE(History) *, TYPE(History) *);
108 private int history_load(TYPE(History) *, const char *);
109 private int history_save(TYPE(History) *, const char *);
110 private int history_save_fp(TYPE(History) *, FILE *);
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_fp():
791  *	TYPE(History) save function
792  */
793 private int
794 history_save_fp(TYPE(History) *h, FILE *fp)
795 {
796 	TYPE(HistEvent) ev;
797 	int i = -1, retval;
798 	size_t len, max_size;
799 	char *ptr;
800 	const char *str;
801 #ifdef WIDECHAR
802 	static ct_buffer_t conv;
803 #endif
804 
805 	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
806 		goto done;
807 	if (fputs(hist_cookie, fp) == EOF)
808 		goto done;
809 	ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
810 	if (ptr == NULL)
811 		goto done;
812 	for (i = 0, retval = HLAST(h, &ev);
813 	    retval != -1;
814 	    retval = HPREV(h, &ev), i++) {
815 		str = ct_encode_string(ev.str, &conv);
816 		len = strlen(str) * 4;
817 		if (len >= max_size) {
818 			char *nptr;
819 			max_size = (len + 1024) & (size_t)~1023;
820 			nptr = h_realloc(ptr, max_size * sizeof(*ptr));
821 			if (nptr == NULL) {
822 				i = -1;
823 				goto oomem;
824 			}
825 			ptr = nptr;
826 		}
827 		(void) strvis(ptr, str, VIS_WHITE);
828 		(void) fprintf(fp, "%s\n", ptr);
829 	}
830 oomem:
831 	h_free(ptr);
832 done:
833 	return i;
834 }
835 
836 
837 /* history_save():
838  *    History save function
839  */
840 private int
841 history_save(TYPE(History) *h, const char *fname)
842 {
843     FILE *fp;
844     int i;
845 
846     if ((fp = fopen(fname, "w")) == NULL)
847 	return -1;
848 
849     i = history_save_fp(h, fp);
850 
851     (void) fclose(fp);
852     return i;
853 }
854 
855 
856 /* history_prev_event():
857  *	Find the previous event, with number given
858  */
859 private int
860 history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
861 {
862 	int retval;
863 
864 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
865 		if (ev->num == num)
866 			return 0;
867 
868 	he_seterrev(ev, _HE_NOT_FOUND);
869 	return -1;
870 }
871 
872 
873 private int
874 history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d)
875 {
876 	int retval;
877 
878 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
879 		if (ev->num == num) {
880 			if (d)
881 				*d = ((history_t *)h->h_ref)->cursor->data;
882 			return 0;
883 		}
884 
885 	he_seterrev(ev, _HE_NOT_FOUND);
886 	return -1;
887 }
888 
889 
890 /* history_next_event():
891  *	Find the next event, with number given
892  */
893 private int
894 history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
895 {
896 	int retval;
897 
898 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
899 		if (ev->num == num)
900 			return 0;
901 
902 	he_seterrev(ev, _HE_NOT_FOUND);
903 	return -1;
904 }
905 
906 
907 /* history_prev_string():
908  *	Find the previous event beginning with string
909  */
910 private int
911 history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
912 {
913 	size_t len = Strlen(str);
914 	int retval;
915 
916 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
917 		if (Strncmp(str, ev->str, len) == 0)
918 			return 0;
919 
920 	he_seterrev(ev, _HE_NOT_FOUND);
921 	return -1;
922 }
923 
924 
925 /* history_next_string():
926  *	Find the next event beginning with string
927  */
928 private int
929 history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
930 {
931 	size_t len = Strlen(str);
932 	int retval;
933 
934 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
935 		if (Strncmp(str, ev->str, len) == 0)
936 			return 0;
937 
938 	he_seterrev(ev, _HE_NOT_FOUND);
939 	return -1;
940 }
941 
942 
943 /* history():
944  *	User interface to history functions.
945  */
946 int
947 FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...)
948 {
949 	va_list va;
950 	const Char *str;
951 	int retval;
952 
953 	va_start(va, fun);
954 
955 	he_seterrev(ev, _HE_OK);
956 
957 	switch (fun) {
958 	case H_GETSIZE:
959 		retval = history_getsize(h, ev);
960 		break;
961 
962 	case H_SETSIZE:
963 		retval = history_setsize(h, ev, va_arg(va, int));
964 		break;
965 
966 	case H_GETUNIQUE:
967 		retval = history_getunique(h, ev);
968 		break;
969 
970 	case H_SETUNIQUE:
971 		retval = history_setunique(h, ev, va_arg(va, int));
972 		break;
973 
974 	case H_ADD:
975 		str = va_arg(va, const Char *);
976 		retval = HADD(h, ev, str);
977 		break;
978 
979 	case H_DEL:
980 		retval = HDEL(h, ev, va_arg(va, const int));
981 		break;
982 
983 	case H_ENTER:
984 		str = va_arg(va, const Char *);
985 		if ((retval = HENTER(h, ev, str)) != -1)
986 			h->h_ent = ev->num;
987 		break;
988 
989 	case H_APPEND:
990 		str = va_arg(va, const Char *);
991 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
992 			retval = HADD(h, ev, str);
993 		break;
994 
995 	case H_FIRST:
996 		retval = HFIRST(h, ev);
997 		break;
998 
999 	case H_NEXT:
1000 		retval = HNEXT(h, ev);
1001 		break;
1002 
1003 	case H_LAST:
1004 		retval = HLAST(h, ev);
1005 		break;
1006 
1007 	case H_PREV:
1008 		retval = HPREV(h, ev);
1009 		break;
1010 
1011 	case H_CURR:
1012 		retval = HCURR(h, ev);
1013 		break;
1014 
1015 	case H_SET:
1016 		retval = HSET(h, ev, va_arg(va, const int));
1017 		break;
1018 
1019 	case H_CLEAR:
1020 		HCLEAR(h, ev);
1021 		retval = 0;
1022 		break;
1023 
1024 	case H_LOAD:
1025 		retval = history_load(h, va_arg(va, const char *));
1026 		if (retval == -1)
1027 			he_seterrev(ev, _HE_HIST_READ);
1028 		break;
1029 
1030 	case H_SAVE:
1031 		retval = history_save(h, va_arg(va, const char *));
1032 		if (retval == -1)
1033 			he_seterrev(ev, _HE_HIST_WRITE);
1034 		break;
1035 
1036 	case H_SAVE_FP:
1037 		retval = history_save_fp(h, va_arg(va, FILE *));
1038 		if (retval == -1)
1039 		    he_seterrev(ev, _HE_HIST_WRITE);
1040 		break;
1041 
1042 	case H_PREV_EVENT:
1043 		retval = history_prev_event(h, ev, va_arg(va, int));
1044 		break;
1045 
1046 	case H_NEXT_EVENT:
1047 		retval = history_next_event(h, ev, va_arg(va, int));
1048 		break;
1049 
1050 	case H_PREV_STR:
1051 		retval = history_prev_string(h, ev, va_arg(va, const Char *));
1052 		break;
1053 
1054 	case H_NEXT_STR:
1055 		retval = history_next_string(h, ev, va_arg(va, const Char *));
1056 		break;
1057 
1058 	case H_FUNC:
1059 	{
1060 		TYPE(History) hf;
1061 
1062 		hf.h_ref = va_arg(va, void *);
1063 		h->h_ent = -1;
1064 		hf.h_first = va_arg(va, history_gfun_t);
1065 		hf.h_next = va_arg(va, history_gfun_t);
1066 		hf.h_last = va_arg(va, history_gfun_t);
1067 		hf.h_prev = va_arg(va, history_gfun_t);
1068 		hf.h_curr = va_arg(va, history_gfun_t);
1069 		hf.h_set = va_arg(va, history_sfun_t);
1070 		hf.h_clear = va_arg(va, history_vfun_t);
1071 		hf.h_enter = va_arg(va, history_efun_t);
1072 		hf.h_add = va_arg(va, history_efun_t);
1073 		hf.h_del = va_arg(va, history_sfun_t);
1074 
1075 		if ((retval = history_set_fun(h, &hf)) == -1)
1076 			he_seterrev(ev, _HE_PARAM_MISSING);
1077 		break;
1078 	}
1079 
1080 	case H_END:
1081 		FUN(history,end)(h);
1082 		retval = 0;
1083 		break;
1084 
1085 	case H_NEXT_EVDATA:
1086 	{
1087 		int num = va_arg(va, int);
1088 		void **d = va_arg(va, void **);
1089 		retval = history_next_evdata(h, ev, num, d);
1090 		break;
1091 	}
1092 
1093 	case H_DELDATA:
1094 	{
1095 		int num = va_arg(va, int);
1096 		void **d = va_arg(va, void **);
1097 		retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d);
1098 		break;
1099 	}
1100 
1101 	case H_REPLACE: /* only use after H_NEXT_EVDATA */
1102 	{
1103 		const Char *line = va_arg(va, const Char *);
1104 		void *d = va_arg(va, void *);
1105 		const Char *s;
1106 		if(!line || !(s = Strdup(line))) {
1107 			retval = -1;
1108 			break;
1109 		}
1110 		((history_t *)h->h_ref)->cursor->ev.str = s;
1111 		((history_t *)h->h_ref)->cursor->data = d;
1112 		retval = 0;
1113 		break;
1114 	}
1115 
1116 	default:
1117 		retval = -1;
1118 		he_seterrev(ev, _HE_UNKNOWN);
1119 		break;
1120 	}
1121 	va_end(va);
1122 	return retval;
1123 }
1124