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