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