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