xref: /freebsd/contrib/libedit/history.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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 #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.63 2019/10/08 19:17:57 christos Exp $");
41 #endif
42 #endif /* not lint && not SCCSID */
43 
44 /*
45  * hist.c: TYPE(History) access functions
46  */
47 #include <sys/stat.h>
48 #include <stdarg.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <vis.h>
52 
53 static const char hist_cookie[] = "_HiStOrY_V2_\n";
54 
55 #include "histedit.h"
56 
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, elen, slen;
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 	elen = Strlen(evp->str);
421 	slen = Strlen(str);
422 	len = elen + slen + 1;
423 	s = h_malloc(len * sizeof(*s));
424 	if (s == NULL) {
425 		he_seterrev(ev, _HE_MALLOC_FAILED);
426 		return -1;
427 	}
428 	memcpy(s, evp->str, elen * sizeof(*s));
429 	memcpy(s + elen, str, slen * sizeof(*s));
430         s[len - 1] = '\0';
431 	h_free(evp->str);
432 	evp->str = s;
433 	*ev = h->cursor->ev;
434 	return 0;
435 }
436 
437 
438 static int
439 history_deldata_nth(history_t *h, TYPE(HistEvent) *ev,
440     int num, void **data)
441 {
442 	if (history_set_nth(h, ev, num) != 0)
443 		return -1;
444 	/* magic value to skip delete (just set to n-th history) */
445 	if (data == (void **)-1)
446 		return 0;
447 	ev->str = Strdup(h->cursor->ev.str);
448 	ev->num = h->cursor->ev.num;
449 	if (data)
450 		*data = h->cursor->data;
451 	history_def_delete(h, ev, h->cursor);
452 	return 0;
453 }
454 
455 
456 /* history_def_del():
457  *	Delete element hp of the h list
458  */
459 /* ARGSUSED */
460 static int
461 history_def_del(void *p, TYPE(HistEvent) *ev __attribute__((__unused__)),
462     const int num)
463 {
464 	history_t *h = (history_t *) p;
465 	if (history_def_set(h, ev, num) != 0)
466 		return -1;
467 	ev->str = Strdup(h->cursor->ev.str);
468 	ev->num = h->cursor->ev.num;
469 	history_def_delete(h, ev, h->cursor);
470 	return 0;
471 }
472 
473 
474 /* history_def_delete():
475  *	Delete element hp of the h list
476  */
477 /* ARGSUSED */
478 static void
479 history_def_delete(history_t *h,
480 		   TYPE(HistEvent) *ev __attribute__((__unused__)), hentry_t *hp)
481 {
482 	HistEventPrivate *evp = (void *)&hp->ev;
483 	if (hp == &h->list)
484 		abort();
485 	if (h->cursor == hp) {
486 		h->cursor = hp->prev;
487 		if (h->cursor == &h->list)
488 			h->cursor = hp->next;
489 	}
490 	hp->prev->next = hp->next;
491 	hp->next->prev = hp->prev;
492 	h_free(evp->str);
493 	h_free(hp);
494 	h->cur--;
495 }
496 
497 
498 /* history_def_insert():
499  *	Insert element with string str in the h list
500  */
501 static int
502 history_def_insert(history_t *h, TYPE(HistEvent) *ev, const Char *str)
503 {
504 	hentry_t *c;
505 
506 	c = h_malloc(sizeof(*c));
507 	if (c == NULL)
508 		goto oomem;
509 	if ((c->ev.str = h_strdup(str)) == NULL) {
510 		h_free(c);
511 		goto oomem;
512 	}
513 	c->data = NULL;
514 	c->ev.num = ++h->eventid;
515 	c->next = h->list.next;
516 	c->prev = &h->list;
517 	h->list.next->prev = c;
518 	h->list.next = c;
519 	h->cur++;
520 	h->cursor = c;
521 
522 	*ev = c->ev;
523 	return 0;
524 oomem:
525 	he_seterrev(ev, _HE_MALLOC_FAILED);
526 	return -1;
527 }
528 
529 
530 /* history_def_enter():
531  *	Default function to enter an item in the history
532  */
533 static int
534 history_def_enter(void *p, TYPE(HistEvent) *ev, const Char *str)
535 {
536 	history_t *h = (history_t *) p;
537 
538 	if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
539 	    Strcmp(h->list.next->ev.str, str) == 0)
540 	    return 0;
541 
542 	if (history_def_insert(h, ev, str) == -1)
543 		return -1;	/* error, keep error message */
544 
545 	/*
546          * Always keep at least one entry.
547          * This way we don't have to check for the empty list.
548          */
549 	while (h->cur > h->max && h->cur > 0)
550 		history_def_delete(h, ev, h->list.prev);
551 
552 	return 1;
553 }
554 
555 
556 /* history_def_init():
557  *	Default history initialization function
558  */
559 /* ARGSUSED */
560 static int
561 history_def_init(void **p, TYPE(HistEvent) *ev __attribute__((__unused__)), int n)
562 {
563 	history_t *h = (history_t *) h_malloc(sizeof(*h));
564 	if (h == NULL)
565 		return -1;
566 
567 	if (n <= 0)
568 		n = 0;
569 	h->eventid = 0;
570 	h->cur = 0;
571 	h->max = n;
572 	h->list.next = h->list.prev = &h->list;
573 	h->list.ev.str = NULL;
574 	h->list.ev.num = 0;
575 	h->cursor = &h->list;
576 	h->flags = 0;
577 	*p = h;
578 	return 0;
579 }
580 
581 
582 /* history_def_clear():
583  *	Default history cleanup function
584  */
585 static void
586 history_def_clear(void *p, TYPE(HistEvent) *ev)
587 {
588 	history_t *h = (history_t *) p;
589 
590 	while (h->list.prev != &h->list)
591 		history_def_delete(h, ev, h->list.prev);
592 	h->cursor = &h->list;
593 	h->eventid = 0;
594 	h->cur = 0;
595 }
596 
597 
598 
599 
600 /************************************************************************/
601 
602 /* history_init():
603  *	Initialization function.
604  */
605 TYPE(History) *
606 FUN(history,init)(void)
607 {
608 	TYPE(HistEvent) ev;
609 	TYPE(History) *h = (TYPE(History) *) h_malloc(sizeof(*h));
610 	if (h == NULL)
611 		return NULL;
612 
613 	if (history_def_init(&h->h_ref, &ev, 0) == -1) {
614 		h_free(h);
615 		return NULL;
616 	}
617 	h->h_ent = -1;
618 	h->h_next = history_def_next;
619 	h->h_first = history_def_first;
620 	h->h_last = history_def_last;
621 	h->h_prev = history_def_prev;
622 	h->h_curr = history_def_curr;
623 	h->h_set = history_def_set;
624 	h->h_clear = history_def_clear;
625 	h->h_enter = history_def_enter;
626 	h->h_add = history_def_add;
627 	h->h_del = history_def_del;
628 
629 	return h;
630 }
631 
632 
633 /* history_end():
634  *	clean up history;
635  */
636 void
637 FUN(history,end)(TYPE(History) *h)
638 {
639 	TYPE(HistEvent) ev;
640 
641 	if (h->h_next == history_def_next)
642 		history_def_clear(h->h_ref, &ev);
643 	h_free(h->h_ref);
644 	h_free(h);
645 }
646 
647 
648 
649 /* history_setsize():
650  *	Set history number of events
651  */
652 static int
653 history_setsize(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
654 {
655 
656 	if (h->h_next != history_def_next) {
657 		he_seterrev(ev, _HE_NOT_ALLOWED);
658 		return -1;
659 	}
660 	if (num < 0) {
661 		he_seterrev(ev, _HE_BAD_PARAM);
662 		return -1;
663 	}
664 	history_def_setsize(h->h_ref, num);
665 	return 0;
666 }
667 
668 
669 /* history_getsize():
670  *      Get number of events currently in history
671  */
672 static int
673 history_getsize(TYPE(History) *h, TYPE(HistEvent) *ev)
674 {
675 	if (h->h_next != history_def_next) {
676 		he_seterrev(ev, _HE_NOT_ALLOWED);
677 		return -1;
678 	}
679 	ev->num = history_def_getsize(h->h_ref);
680 	if (ev->num < -1) {
681 		he_seterrev(ev, _HE_SIZE_NEGATIVE);
682 		return -1;
683 	}
684 	return 0;
685 }
686 
687 
688 /* history_setunique():
689  *	Set if adjacent equal events should not be entered in history.
690  */
691 static int
692 history_setunique(TYPE(History) *h, TYPE(HistEvent) *ev, int uni)
693 {
694 
695 	if (h->h_next != history_def_next) {
696 		he_seterrev(ev, _HE_NOT_ALLOWED);
697 		return -1;
698 	}
699 	history_def_setunique(h->h_ref, uni);
700 	return 0;
701 }
702 
703 
704 /* history_getunique():
705  *	Get if adjacent equal events should not be entered in history.
706  */
707 static int
708 history_getunique(TYPE(History) *h, TYPE(HistEvent) *ev)
709 {
710 	if (h->h_next != history_def_next) {
711 		he_seterrev(ev, _HE_NOT_ALLOWED);
712 		return -1;
713 	}
714 	ev->num = history_def_getunique(h->h_ref);
715 	return 0;
716 }
717 
718 
719 /* history_set_fun():
720  *	Set history functions
721  */
722 static int
723 history_set_fun(TYPE(History) *h, TYPE(History) *nh)
724 {
725 	TYPE(HistEvent) ev;
726 
727 	if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
728 	    nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
729 	    nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
730 	    nh->h_del == NULL || nh->h_ref == NULL) {
731 		if (h->h_next != history_def_next) {
732 			if (history_def_init(&h->h_ref, &ev, 0) == -1)
733 				return -1;
734 			h->h_first = history_def_first;
735 			h->h_next = history_def_next;
736 			h->h_last = history_def_last;
737 			h->h_prev = history_def_prev;
738 			h->h_curr = history_def_curr;
739 			h->h_set = history_def_set;
740 			h->h_clear = history_def_clear;
741 			h->h_enter = history_def_enter;
742 			h->h_add = history_def_add;
743 			h->h_del = history_def_del;
744 		}
745 		return -1;
746 	}
747 	if (h->h_next == history_def_next)
748 		history_def_clear(h->h_ref, &ev);
749 
750 	h->h_ent = -1;
751 	h->h_first = nh->h_first;
752 	h->h_next = nh->h_next;
753 	h->h_last = nh->h_last;
754 	h->h_prev = nh->h_prev;
755 	h->h_curr = nh->h_curr;
756 	h->h_set = nh->h_set;
757 	h->h_clear = nh->h_clear;
758 	h->h_enter = nh->h_enter;
759 	h->h_add = nh->h_add;
760 	h->h_del = nh->h_del;
761 
762 	return 0;
763 }
764 
765 
766 /* history_load():
767  *	TYPE(History) load function
768  */
769 static int
770 history_load(TYPE(History) *h, const char *fname)
771 {
772 	FILE *fp;
773 	char *line;
774 	size_t llen;
775 	ssize_t sz;
776 	size_t max_size;
777 	char *ptr;
778 	int i = -1;
779 	TYPE(HistEvent) ev;
780 	Char *decode_result;
781 #ifndef NARROWCHAR
782 	static ct_buffer_t conv;
783 #endif
784 
785 	if ((fp = fopen(fname, "r")) == NULL)
786 		return i;
787 
788 	line = NULL;
789 	llen = 0;
790 	if ((sz = getline(&line, &llen, fp)) == -1)
791 		goto done;
792 
793 	if (strncmp(line, hist_cookie, (size_t)sz) != 0)
794 		goto done;
795 
796 	ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
797 	if (ptr == NULL)
798 		goto done;
799 	for (i = 0; (sz = getline(&line, &llen, fp)) != -1; i++) {
800 		if (sz > 0 && line[sz - 1] == '\n')
801 			line[--sz] = '\0';
802 		if (max_size < (size_t)sz) {
803 			char *nptr;
804 			max_size = ((size_t)sz + 1024) & (size_t)~1023;
805 			nptr = h_realloc(ptr, max_size * sizeof(*ptr));
806 			if (nptr == NULL) {
807 				i = -1;
808 				goto oomem;
809 			}
810 			ptr = nptr;
811 		}
812 		(void) strunvis(ptr, line);
813 		decode_result = ct_decode_string(ptr, &conv);
814 		if (decode_result == NULL)
815 			continue;
816 		if (HENTER(h, &ev, decode_result) == -1) {
817 			i = -1;
818 			goto oomem;
819 		}
820 	}
821 oomem:
822 	h_free(ptr);
823 done:
824 	free(line);
825 	(void) fclose(fp);
826 	return i;
827 }
828 
829 
830 /* history_save_fp():
831  *	TYPE(History) save function
832  */
833 static int
834 history_save_fp(TYPE(History) *h, size_t nelem, FILE *fp)
835 {
836 	TYPE(HistEvent) ev;
837 	int i = -1, retval;
838 	size_t len, max_size;
839 	char *ptr;
840 	const char *str;
841 #ifndef NARROWCHAR
842 	static ct_buffer_t conv;
843 #endif
844 
845 	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
846 		goto done;
847 	if (ftell(fp) == 0 && fputs(hist_cookie, fp) == EOF)
848 		goto done;
849 	ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
850 	if (ptr == NULL)
851 		goto done;
852 	if (nelem != (size_t)-1) {
853 		for (retval = HFIRST(h, &ev); retval != -1 && nelem-- > 0;
854 		    retval = HNEXT(h, &ev))
855 			continue;
856 	} else
857 		retval = -1;
858 
859 	if (retval == -1)
860 		retval = HLAST(h, &ev);
861 
862 	for (i = 0; retval != -1; retval = HPREV(h, &ev), i++) {
863 		str = ct_encode_string(ev.str, &conv);
864 		len = strlen(str) * 4 + 1;
865 		if (len > max_size) {
866 			char *nptr;
867 			max_size = (len + 1024) & (size_t)~1023;
868 			nptr = h_realloc(ptr, max_size * sizeof(*ptr));
869 			if (nptr == NULL) {
870 				i = -1;
871 				goto oomem;
872 			}
873 			ptr = nptr;
874 		}
875 		(void) strvis(ptr, str, VIS_WHITE);
876 		(void) fprintf(fp, "%s\n", ptr);
877 	}
878 oomem:
879 	h_free(ptr);
880 done:
881 	return i;
882 }
883 
884 
885 /* history_save():
886  *    History save function
887  */
888 static int
889 history_save(TYPE(History) *h, const char *fname)
890 {
891     FILE *fp;
892     int i;
893 
894     if ((fp = fopen(fname, "w")) == NULL)
895 	return -1;
896 
897     i = history_save_fp(h, (size_t)-1, fp);
898 
899     (void) fclose(fp);
900     return i;
901 }
902 
903 
904 /* history_prev_event():
905  *	Find the previous event, with number given
906  */
907 static int
908 history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
909 {
910 	int retval;
911 
912 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
913 		if (ev->num == num)
914 			return 0;
915 
916 	he_seterrev(ev, _HE_NOT_FOUND);
917 	return -1;
918 }
919 
920 
921 static int
922 history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d)
923 {
924 	int retval;
925 
926 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
927 		if (ev->num == num) {
928 			if (d)
929 				*d = ((history_t *)h->h_ref)->cursor->data;
930 			return 0;
931 		}
932 
933 	he_seterrev(ev, _HE_NOT_FOUND);
934 	return -1;
935 }
936 
937 
938 /* history_next_event():
939  *	Find the next event, with number given
940  */
941 static int
942 history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
943 {
944 	int retval;
945 
946 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
947 		if (ev->num == num)
948 			return 0;
949 
950 	he_seterrev(ev, _HE_NOT_FOUND);
951 	return -1;
952 }
953 
954 
955 /* history_prev_string():
956  *	Find the previous event beginning with string
957  */
958 static int
959 history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
960 {
961 	size_t len = Strlen(str);
962 	int retval;
963 
964 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
965 		if (Strncmp(str, ev->str, len) == 0)
966 			return 0;
967 
968 	he_seterrev(ev, _HE_NOT_FOUND);
969 	return -1;
970 }
971 
972 
973 /* history_next_string():
974  *	Find the next event beginning with string
975  */
976 static int
977 history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
978 {
979 	size_t len = Strlen(str);
980 	int retval;
981 
982 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
983 		if (Strncmp(str, ev->str, len) == 0)
984 			return 0;
985 
986 	he_seterrev(ev, _HE_NOT_FOUND);
987 	return -1;
988 }
989 
990 
991 /* history():
992  *	User interface to history functions.
993  */
994 int
995 FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...)
996 {
997 	va_list va;
998 	const Char *str;
999 	int retval;
1000 
1001 	va_start(va, fun);
1002 
1003 	he_seterrev(ev, _HE_OK);
1004 
1005 	switch (fun) {
1006 	case H_GETSIZE:
1007 		retval = history_getsize(h, ev);
1008 		break;
1009 
1010 	case H_SETSIZE:
1011 		retval = history_setsize(h, ev, va_arg(va, int));
1012 		break;
1013 
1014 	case H_GETUNIQUE:
1015 		retval = history_getunique(h, ev);
1016 		break;
1017 
1018 	case H_SETUNIQUE:
1019 		retval = history_setunique(h, ev, va_arg(va, int));
1020 		break;
1021 
1022 	case H_ADD:
1023 		str = va_arg(va, const Char *);
1024 		retval = HADD(h, ev, str);
1025 		break;
1026 
1027 	case H_DEL:
1028 		retval = HDEL(h, ev, va_arg(va, const int));
1029 		break;
1030 
1031 	case H_ENTER:
1032 		str = va_arg(va, const Char *);
1033 		if ((retval = HENTER(h, ev, str)) != -1)
1034 			h->h_ent = ev->num;
1035 		break;
1036 
1037 	case H_APPEND:
1038 		str = va_arg(va, const Char *);
1039 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
1040 			retval = HADD(h, ev, str);
1041 		break;
1042 
1043 	case H_FIRST:
1044 		retval = HFIRST(h, ev);
1045 		break;
1046 
1047 	case H_NEXT:
1048 		retval = HNEXT(h, ev);
1049 		break;
1050 
1051 	case H_LAST:
1052 		retval = HLAST(h, ev);
1053 		break;
1054 
1055 	case H_PREV:
1056 		retval = HPREV(h, ev);
1057 		break;
1058 
1059 	case H_CURR:
1060 		retval = HCURR(h, ev);
1061 		break;
1062 
1063 	case H_SET:
1064 		retval = HSET(h, ev, va_arg(va, const int));
1065 		break;
1066 
1067 	case H_CLEAR:
1068 		HCLEAR(h, ev);
1069 		retval = 0;
1070 		break;
1071 
1072 	case H_LOAD:
1073 		retval = history_load(h, va_arg(va, const char *));
1074 		if (retval == -1)
1075 			he_seterrev(ev, _HE_HIST_READ);
1076 		break;
1077 
1078 	case H_SAVE:
1079 		retval = history_save(h, va_arg(va, const char *));
1080 		if (retval == -1)
1081 			he_seterrev(ev, _HE_HIST_WRITE);
1082 		break;
1083 
1084 	case H_SAVE_FP:
1085 		retval = history_save_fp(h, (size_t)-1, va_arg(va, FILE *));
1086 		if (retval == -1)
1087 		    he_seterrev(ev, _HE_HIST_WRITE);
1088 		break;
1089 
1090 	case H_NSAVE_FP:
1091 	{
1092 		size_t sz = va_arg(va, size_t);
1093 		retval = history_save_fp(h, sz, va_arg(va, FILE *));
1094 		if (retval == -1)
1095 		    he_seterrev(ev, _HE_HIST_WRITE);
1096 		break;
1097 	}
1098 
1099 	case H_PREV_EVENT:
1100 		retval = history_prev_event(h, ev, va_arg(va, int));
1101 		break;
1102 
1103 	case H_NEXT_EVENT:
1104 		retval = history_next_event(h, ev, va_arg(va, int));
1105 		break;
1106 
1107 	case H_PREV_STR:
1108 		retval = history_prev_string(h, ev, va_arg(va, const Char *));
1109 		break;
1110 
1111 	case H_NEXT_STR:
1112 		retval = history_next_string(h, ev, va_arg(va, const Char *));
1113 		break;
1114 
1115 	case H_FUNC:
1116 	{
1117 		TYPE(History) hf;
1118 
1119 		hf.h_ref = va_arg(va, void *);
1120 		h->h_ent = -1;
1121 		hf.h_first = va_arg(va, history_gfun_t);
1122 		hf.h_next = va_arg(va, history_gfun_t);
1123 		hf.h_last = va_arg(va, history_gfun_t);
1124 		hf.h_prev = va_arg(va, history_gfun_t);
1125 		hf.h_curr = va_arg(va, history_gfun_t);
1126 		hf.h_set = va_arg(va, history_sfun_t);
1127 		hf.h_clear = va_arg(va, history_vfun_t);
1128 		hf.h_enter = va_arg(va, history_efun_t);
1129 		hf.h_add = va_arg(va, history_efun_t);
1130 		hf.h_del = va_arg(va, history_sfun_t);
1131 
1132 		if ((retval = history_set_fun(h, &hf)) == -1)
1133 			he_seterrev(ev, _HE_PARAM_MISSING);
1134 		break;
1135 	}
1136 
1137 	case H_END:
1138 		FUN(history,end)(h);
1139 		retval = 0;
1140 		break;
1141 
1142 	case H_NEXT_EVDATA:
1143 	{
1144 		int num = va_arg(va, int);
1145 		void **d = va_arg(va, void **);
1146 		retval = history_next_evdata(h, ev, num, d);
1147 		break;
1148 	}
1149 
1150 	case H_DELDATA:
1151 	{
1152 		int num = va_arg(va, int);
1153 		void **d = va_arg(va, void **);
1154 		retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d);
1155 		break;
1156 	}
1157 
1158 	case H_REPLACE: /* only use after H_NEXT_EVDATA */
1159 	{
1160 		const Char *line = va_arg(va, const Char *);
1161 		void *d = va_arg(va, void *);
1162 		const Char *s;
1163 		if(!line || !(s = Strdup(line))) {
1164 			retval = -1;
1165 			break;
1166 		}
1167 		((history_t *)h->h_ref)->cursor->ev.str = s;
1168 		((history_t *)h->h_ref)->cursor->data = d;
1169 		retval = 0;
1170 		break;
1171 	}
1172 
1173 	default:
1174 		retval = -1;
1175 		he_seterrev(ev, _HE_UNKNOWN);
1176 		break;
1177 	}
1178 	va_end(va);
1179 	return retval;
1180 }
1181