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