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