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
38static 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
53static 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
94typedef int (*history_gfun_t)(void *, TYPE(HistEvent) *);
95typedef int (*history_efun_t)(void *, TYPE(HistEvent) *, const Char *);
96typedef void (*history_vfun_t)(void *, TYPE(HistEvent) *);
97typedef int (*history_sfun_t)(void *, TYPE(HistEvent) *, const int);
98
99struct 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
130typedef struct {
131    int		num;
132    Char	*str;
133} HistEventPrivate;
134
135
136static int history_setsize(TYPE(History) *, TYPE(HistEvent) *, int);
137static int history_getsize(TYPE(History) *, TYPE(HistEvent) *);
138static int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int);
139static int history_getunique(TYPE(History) *, TYPE(HistEvent) *);
140static int history_set_fun(TYPE(History) *, TYPE(History) *);
141static int history_load(TYPE(History) *, const char *);
142static int history_save(TYPE(History) *, const char *);
143static int history_save_fp(TYPE(History) *, size_t, FILE *);
144static int history_prev_event(TYPE(History) *, TYPE(HistEvent) *, int);
145static int history_next_event(TYPE(History) *, TYPE(HistEvent) *, int);
146static int history_next_string(TYPE(History) *, TYPE(HistEvent) *,
147    const Char *);
148static int history_prev_string(TYPE(History) *, TYPE(HistEvent) *,
149    const Char *);
150
151
152/***********************************************************************/
153
154/*
155 * Builtin- history implementation
156 */
157typedef 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
164typedef 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
174static int history_def_next(void *, TYPE(HistEvent) *);
175static int history_def_first(void *, TYPE(HistEvent) *);
176static int history_def_prev(void *, TYPE(HistEvent) *);
177static int history_def_last(void *, TYPE(HistEvent) *);
178static int history_def_curr(void *, TYPE(HistEvent) *);
179static int history_def_set(void *, TYPE(HistEvent) *, const int);
180static void history_def_clear(void *, TYPE(HistEvent) *);
181static int history_def_enter(void *, TYPE(HistEvent) *, const Char *);
182static int history_def_add(void *, TYPE(HistEvent) *, const Char *);
183static int history_def_del(void *, TYPE(HistEvent) *, const int);
184
185static int history_def_init(void **, TYPE(HistEvent) *, int);
186static int history_def_insert(history_t *, TYPE(HistEvent) *, const Char *);
187static void history_def_delete(history_t *, TYPE(HistEvent) *, hentry_t *);
188
189static int history_deldata_nth(history_t *, TYPE(HistEvent) *, int, void **);
190static 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 */
208static 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 */
247static int
248history_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 */
267static int
268history_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 */
287static int
288history_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 */
312static int
313history_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 */
338static int
339history_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 */
359static int
360history_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 */
386static int
387history_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 */
410static int
411history_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
438static int
439history_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 */
460static int
461history_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 */
478static void
479history_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 */
501static int
502history_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;
524oomem:
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 */
533static int
534history_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 */
560static int
561history_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 */
585static void
586history_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 */
605TYPE(History) *
606FUN(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 */
636void
637FUN(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 */
652static int
653history_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 */
672static int
673history_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 */
691static int
692history_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 */
707static int
708history_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 */
722static int
723history_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 */
769static int
770history_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	}
821oomem:
822	h_free(ptr);
823done:
824	free(line);
825	(void) fclose(fp);
826	return i;
827}
828
829
830/* history_save_fp():
831 *	TYPE(History) save function
832 */
833static int
834history_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	}
878oomem:
879	h_free(ptr);
880done:
881	return i;
882}
883
884
885/* history_save():
886 *    History save function
887 */
888static int
889history_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 */
907static int
908history_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
921static int
922history_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 */
941static int
942history_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 */
958static int
959history_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 */
976static int
977history_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 */
994int
995FUNW(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