xref: /netbsd-src/lib/libedit/history.c (revision 4d36c519fb2dea03797ab4f8073e5de98a5c090e)
1*4d36c519Skre /*	$NetBSD: history.c,v 1.64 2024/07/11 05:41:24 kre Exp $	*/
22543e3e6Slukem 
36dc2f1dbScgd /*-
46dc2f1dbScgd  * Copyright (c) 1992, 1993
56dc2f1dbScgd  *	The Regents of the University of California.  All rights reserved.
66dc2f1dbScgd  *
76dc2f1dbScgd  * This code is derived from software contributed to Berkeley by
86dc2f1dbScgd  * Christos Zoulas of Cornell University.
96dc2f1dbScgd  *
106dc2f1dbScgd  * Redistribution and use in source and binary forms, with or without
116dc2f1dbScgd  * modification, are permitted provided that the following conditions
126dc2f1dbScgd  * are met:
136dc2f1dbScgd  * 1. Redistributions of source code must retain the above copyright
146dc2f1dbScgd  *    notice, this list of conditions and the following disclaimer.
156dc2f1dbScgd  * 2. Redistributions in binary form must reproduce the above copyright
166dc2f1dbScgd  *    notice, this list of conditions and the following disclaimer in the
176dc2f1dbScgd  *    documentation and/or other materials provided with the distribution.
18eb7c1594Sagc  * 3. Neither the name of the University nor the names of its contributors
196dc2f1dbScgd  *    may be used to endorse or promote products derived from this software
206dc2f1dbScgd  *    without specific prior written permission.
216dc2f1dbScgd  *
226dc2f1dbScgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
236dc2f1dbScgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
246dc2f1dbScgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
256dc2f1dbScgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
266dc2f1dbScgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
276dc2f1dbScgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
286dc2f1dbScgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
296dc2f1dbScgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
306dc2f1dbScgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
316dc2f1dbScgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
326dc2f1dbScgd  * SUCH DAMAGE.
336dc2f1dbScgd  */
346dc2f1dbScgd 
350e0ac6b7Schristos #include "config.h"
366dc2f1dbScgd #if !defined(lint) && !defined(SCCSID)
3754fac685Schristos #if 0
386dc2f1dbScgd static char sccsid[] = "@(#)history.c	8.1 (Berkeley) 6/4/93";
3954fac685Schristos #else
40*4d36c519Skre __RCSID("$NetBSD: history.c,v 1.64 2024/07/11 05:41:24 kre Exp $");
4154fac685Schristos #endif
426dc2f1dbScgd #endif /* not lint && not SCCSID */
436dc2f1dbScgd 
446dc2f1dbScgd /*
4534e53048Schristos  * hist.c: TYPE(History) access functions
466dc2f1dbScgd  */
47e125f8f1Schristos #include <sys/stat.h>
48*4d36c519Skre #include <fcntl.h>
4922383670Schristos #include <stdarg.h>
5022383670Schristos #include <stdlib.h>
5122383670Schristos #include <string.h>
5222383670Schristos #include <vis.h>
536dc2f1dbScgd 
5484ee55f1Schristos static const char hist_cookie[] = "_HiStOrY_V2_\n";
5595b0e87bSchristos 
566dc2f1dbScgd #include "histedit.h"
576dc2f1dbScgd 
580aefc7f9Schristos 
590aefc7f9Schristos #ifdef NARROWCHAR
600aefc7f9Schristos 
610594af80Schristos #define	Char			char
620aefc7f9Schristos #define	FUN(prefix, rest)	prefix ## _ ## rest
630aefc7f9Schristos #define	FUNW(type)		type
640aefc7f9Schristos #define	TYPE(type)		type
650aefc7f9Schristos #define	STR(x)			x
660aefc7f9Schristos 
670aefc7f9Schristos #define	Strlen(s)		strlen(s)
680aefc7f9Schristos #define	Strdup(s)		strdup(s)
690aefc7f9Schristos #define	Strcmp(d, s)		strcmp(d, s)
700aefc7f9Schristos #define	Strncmp(d, s, n)	strncmp(d, s, n)
710aefc7f9Schristos #define	Strncpy(d, s, n)	strncpy(d, s, n)
720aefc7f9Schristos #define	Strncat(d, s, n)	strncat(d, s, n)
73a75ea7b9Schristos #define	ct_decode_string(s, b)	(s)
74a75ea7b9Schristos #define	ct_encode_string(s, b)	(s)
750aefc7f9Schristos 
760aefc7f9Schristos #else
77a75ea7b9Schristos #include "chartype.h"
780aefc7f9Schristos 
790594af80Schristos #define	Char			wchar_t
800aefc7f9Schristos #define	FUN(prefix, rest)	prefix ## _w ## rest
810aefc7f9Schristos #define	FUNW(type)		type ## _w
820aefc7f9Schristos #define	TYPE(type)		type ## W
830aefc7f9Schristos #define	STR(x)			L ## x
840aefc7f9Schristos 
850aefc7f9Schristos #define	Strlen(s)		wcslen(s)
860aefc7f9Schristos #define	Strdup(s)		wcsdup(s)
870aefc7f9Schristos #define	Strcmp(d, s)		wcscmp(d, s)
880aefc7f9Schristos #define	Strncmp(d, s, n)	wcsncmp(d, s, n)
890aefc7f9Schristos #define	Strncpy(d, s, n)	wcsncpy(d, s, n)
900aefc7f9Schristos #define	Strncat(d, s, n)	wcsncat(d, s, n)
910aefc7f9Schristos 
920aefc7f9Schristos #endif
930aefc7f9Schristos 
940aefc7f9Schristos 
95a13cd756Schristos typedef int (*history_gfun_t)(void *, TYPE(HistEvent) *);
96a13cd756Schristos typedef int (*history_efun_t)(void *, TYPE(HistEvent) *, const Char *);
97a13cd756Schristos typedef void (*history_vfun_t)(void *, TYPE(HistEvent) *);
98a13cd756Schristos typedef int (*history_sfun_t)(void *, TYPE(HistEvent) *, const int);
996dc2f1dbScgd 
TYPE(history)10073eda9feSchristos struct TYPE(history) {
101a13cd756Schristos 	void *h_ref;		/* Argument for history fcns	 */
102eac8b136Schristos 	int h_ent;		/* Last entry point for history	 */
1036dc2f1dbScgd 	history_gfun_t h_first;	/* Get the first element	 */
1046dc2f1dbScgd 	history_gfun_t h_next;	/* Get the next element		 */
1056dc2f1dbScgd 	history_gfun_t h_last;	/* Get the last element		 */
1066dc2f1dbScgd 	history_gfun_t h_prev;	/* Get the previous element	 */
1076dc2f1dbScgd 	history_gfun_t h_curr;	/* Get the current element	 */
108ccac6ba8Schristos 	history_sfun_t h_set;	/* Set the current element	 */
10945542456Schristos 	history_sfun_t h_del;	/* Set the given element	 */
11095b0e87bSchristos 	history_vfun_t h_clear;	/* Clear the history list	 */
1116dc2f1dbScgd 	history_efun_t h_enter;	/* Add an element		 */
1126dc2f1dbScgd 	history_efun_t h_add;	/* Append to an element		 */
1136dc2f1dbScgd };
114f24857bfSchristos 
115a7db9a79Schristos #define	HNEXT(h, ev)		(*(h)->h_next)((h)->h_ref, ev)
116a7db9a79Schristos #define	HFIRST(h, ev)		(*(h)->h_first)((h)->h_ref, ev)
117a7db9a79Schristos #define	HPREV(h, ev)		(*(h)->h_prev)((h)->h_ref, ev)
118a7db9a79Schristos #define	HLAST(h, ev)		(*(h)->h_last)((h)->h_ref, ev)
119a7db9a79Schristos #define	HCURR(h, ev)		(*(h)->h_curr)((h)->h_ref, ev)
120ccac6ba8Schristos #define	HSET(h, ev, n)		(*(h)->h_set)((h)->h_ref, ev, n)
121a7db9a79Schristos #define	HCLEAR(h, ev)		(*(h)->h_clear)((h)->h_ref, ev)
122a7db9a79Schristos #define	HENTER(h, ev, str)	(*(h)->h_enter)((h)->h_ref, ev, str)
123a7db9a79Schristos #define	HADD(h, ev, str)	(*(h)->h_add)((h)->h_ref, ev, str)
12445542456Schristos #define	HDEL(h, ev, n)		(*(h)->h_del)((h)->h_ref, ev, n)
1256dc2f1dbScgd 
12634e53048Schristos #define	h_strdup(a)	Strdup(a)
1276dc2f1dbScgd #define	h_malloc(a)	malloc(a)
12884ee55f1Schristos #define	h_realloc(a, b)	realloc((a), (b))
1296dc2f1dbScgd #define	h_free(a)	free(a)
1306dc2f1dbScgd 
1310e0ac6b7Schristos typedef struct {
1320e0ac6b7Schristos     int		num;
13334e53048Schristos     Char	*str;
1340e0ac6b7Schristos } HistEventPrivate;
1350e0ac6b7Schristos 
1360e0ac6b7Schristos 
137469d44f8Schristos static int history_setsize(TYPE(History) *, TYPE(HistEvent) *, int);
138469d44f8Schristos static int history_getsize(TYPE(History) *, TYPE(HistEvent) *);
139469d44f8Schristos static int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int);
140469d44f8Schristos static int history_getunique(TYPE(History) *, TYPE(HistEvent) *);
141469d44f8Schristos static int history_set_fun(TYPE(History) *, TYPE(History) *);
142469d44f8Schristos static int history_load(TYPE(History) *, const char *);
143469d44f8Schristos static int history_save(TYPE(History) *, const char *);
14427916d7cSchristos static int history_save_fp(TYPE(History) *, size_t, FILE *);
145469d44f8Schristos static int history_prev_event(TYPE(History) *, TYPE(HistEvent) *, int);
146469d44f8Schristos static int history_next_event(TYPE(History) *, TYPE(HistEvent) *, int);
147469d44f8Schristos static int history_next_string(TYPE(History) *, TYPE(HistEvent) *,
148469d44f8Schristos     const Char *);
149469d44f8Schristos static int history_prev_string(TYPE(History) *, TYPE(HistEvent) *,
150469d44f8Schristos     const Char *);
1516dc2f1dbScgd 
1526dc2f1dbScgd 
1536dc2f1dbScgd /***********************************************************************/
1546dc2f1dbScgd 
1556dc2f1dbScgd /*
1566dc2f1dbScgd  * Builtin- history implementation
1576dc2f1dbScgd  */
1586dc2f1dbScgd typedef struct hentry_t {
15934e53048Schristos 	TYPE(HistEvent) ev;		/* What we return		 */
160ea3813edSchristos 	void *data;		/* data				 */
1616dc2f1dbScgd 	struct hentry_t *next;	/* Next entry			 */
1626dc2f1dbScgd 	struct hentry_t *prev;	/* Previous entry		 */
1636dc2f1dbScgd } hentry_t;
1646dc2f1dbScgd 
1656dc2f1dbScgd typedef struct history_t {
1666dc2f1dbScgd 	hentry_t list;		/* Fake list header element	*/
1676dc2f1dbScgd 	hentry_t *cursor;	/* Current element in the list	*/
1686dc2f1dbScgd 	int max;		/* Maximum number of events	*/
1696dc2f1dbScgd 	int cur;		/* Current number of events	*/
170a7db9a79Schristos 	int eventid;		/* For generation of unique event id	 */
17134e53048Schristos 	int flags;		/* TYPE(History) flags		*/
172f24857bfSchristos #define H_UNIQUE	1	/* Store only unique elements	*/
1736dc2f1dbScgd } history_t;
1746dc2f1dbScgd 
175469d44f8Schristos static int history_def_next(void *, TYPE(HistEvent) *);
176469d44f8Schristos static int history_def_first(void *, TYPE(HistEvent) *);
177469d44f8Schristos static int history_def_prev(void *, TYPE(HistEvent) *);
178469d44f8Schristos static int history_def_last(void *, TYPE(HistEvent) *);
179469d44f8Schristos static int history_def_curr(void *, TYPE(HistEvent) *);
180469d44f8Schristos static int history_def_set(void *, TYPE(HistEvent) *, const int);
181469d44f8Schristos static void history_def_clear(void *, TYPE(HistEvent) *);
182469d44f8Schristos static int history_def_enter(void *, TYPE(HistEvent) *, const Char *);
183469d44f8Schristos static int history_def_add(void *, TYPE(HistEvent) *, const Char *);
184469d44f8Schristos static int history_def_del(void *, TYPE(HistEvent) *, const int);
18545542456Schristos 
186469d44f8Schristos static int history_def_init(void **, TYPE(HistEvent) *, int);
187469d44f8Schristos static int history_def_insert(history_t *, TYPE(HistEvent) *, const Char *);
188469d44f8Schristos static void history_def_delete(history_t *, TYPE(HistEvent) *, hentry_t *);
1896dc2f1dbScgd 
190469d44f8Schristos static int history_deldata_nth(history_t *, TYPE(HistEvent) *, int, void **);
191469d44f8Schristos static int history_set_nth(void *, TYPE(HistEvent) *, int);
192ea3813edSchristos 
193ccac6ba8Schristos #define	history_def_setsize(p, num)(void) (((history_t *)p)->max = (num))
194a7db9a79Schristos #define	history_def_getsize(p)  (((history_t *)p)->cur)
195f24857bfSchristos #define	history_def_getunique(p) (((((history_t *)p)->flags) & H_UNIQUE) != 0)
196f24857bfSchristos #define	history_def_setunique(p, uni) \
197f24857bfSchristos     if (uni) \
198f24857bfSchristos 	(((history_t *)p)->flags) |= H_UNIQUE; \
199f24857bfSchristos     else \
200f24857bfSchristos 	(((history_t *)p)->flags) &= ~H_UNIQUE
2016dc2f1dbScgd 
202a7db9a79Schristos #define	he_strerror(code)	he_errlist[code]
203a7db9a79Schristos #define	he_seterrev(evp, code)	{\
204a7db9a79Schristos 				    evp->num = code;\
205a7db9a79Schristos 				    evp->str = he_strerror(code);\
206a7db9a79Schristos 				}
207a7db9a79Schristos 
208a7db9a79Schristos /* error messages */
20934e53048Schristos static const Char *const he_errlist[] = {
21034e53048Schristos 	STR("OK"),
21134e53048Schristos 	STR("unknown error"),
21234e53048Schristos 	STR("malloc() failed"),
21334e53048Schristos 	STR("first event not found"),
21434e53048Schristos 	STR("last event not found"),
21534e53048Schristos 	STR("empty list"),
21634e53048Schristos 	STR("no next event"),
21734e53048Schristos 	STR("no previous event"),
21834e53048Schristos 	STR("current event is invalid"),
21934e53048Schristos 	STR("event not found"),
22034e53048Schristos 	STR("can't read history from file"),
22134e53048Schristos 	STR("can't write history"),
22234e53048Schristos 	STR("required parameter(s) not supplied"),
22334e53048Schristos 	STR("history size negative"),
22434e53048Schristos 	STR("function not allowed with other history-functions-set the default"),
22534e53048Schristos 	STR("bad parameters")
226a7db9a79Schristos };
227a7db9a79Schristos /* error codes */
228a7db9a79Schristos #define	_HE_OK                   0
229a7db9a79Schristos #define	_HE_UNKNOWN		 1
230a7db9a79Schristos #define	_HE_MALLOC_FAILED        2
231a7db9a79Schristos #define	_HE_FIRST_NOTFOUND       3
232a7db9a79Schristos #define	_HE_LAST_NOTFOUND        4
233a7db9a79Schristos #define	_HE_EMPTY_LIST           5
234a7db9a79Schristos #define	_HE_END_REACHED          6
235a7db9a79Schristos #define	_HE_START_REACHED	 7
236a7db9a79Schristos #define	_HE_CURR_INVALID	 8
237a7db9a79Schristos #define	_HE_NOT_FOUND		 9
238a7db9a79Schristos #define	_HE_HIST_READ		10
239a7db9a79Schristos #define	_HE_HIST_WRITE		11
240a7db9a79Schristos #define	_HE_PARAM_MISSING	12
241a7db9a79Schristos #define	_HE_SIZE_NEGATIVE	13
242a7db9a79Schristos #define	_HE_NOT_ALLOWED		14
243a7db9a79Schristos #define	_HE_BAD_PARAM		15
2446dc2f1dbScgd 
2456dc2f1dbScgd /* history_def_first():
2466dc2f1dbScgd  *	Default function to return the first event in the history.
2476dc2f1dbScgd  */
248469d44f8Schristos static int
history_def_first(void * p,TYPE (HistEvent)* ev)249a13cd756Schristos history_def_first(void *p, TYPE(HistEvent) *ev)
2506dc2f1dbScgd {
2516dc2f1dbScgd 	history_t *h = (history_t *) p;
252a7db9a79Schristos 
2536dc2f1dbScgd 	h->cursor = h->list.next;
2546dc2f1dbScgd 	if (h->cursor != &h->list)
255a7db9a79Schristos 		*ev = h->cursor->ev;
256a7db9a79Schristos 	else {
257a7db9a79Schristos 		he_seterrev(ev, _HE_FIRST_NOTFOUND);
258b71bed95Schristos 		return -1;
259a7db9a79Schristos 	}
260a7db9a79Schristos 
261b71bed95Schristos 	return 0;
2626dc2f1dbScgd }
2636dc2f1dbScgd 
264ccac6ba8Schristos 
2656dc2f1dbScgd /* history_def_last():
2666dc2f1dbScgd  *	Default function to return the last event in the history.
2676dc2f1dbScgd  */
268469d44f8Schristos static int
history_def_last(void * p,TYPE (HistEvent)* ev)269a13cd756Schristos history_def_last(void *p, TYPE(HistEvent) *ev)
2706dc2f1dbScgd {
2716dc2f1dbScgd 	history_t *h = (history_t *) p;
272a7db9a79Schristos 
2736dc2f1dbScgd 	h->cursor = h->list.prev;
2746dc2f1dbScgd 	if (h->cursor != &h->list)
275a7db9a79Schristos 		*ev = h->cursor->ev;
276a7db9a79Schristos 	else {
277a7db9a79Schristos 		he_seterrev(ev, _HE_LAST_NOTFOUND);
278b71bed95Schristos 		return -1;
279a7db9a79Schristos 	}
280a7db9a79Schristos 
281b71bed95Schristos 	return 0;
2826dc2f1dbScgd }
2836dc2f1dbScgd 
284ccac6ba8Schristos 
2856dc2f1dbScgd /* history_def_next():
2866dc2f1dbScgd  *	Default function to return the next event in the history.
2876dc2f1dbScgd  */
288469d44f8Schristos static int
history_def_next(void * p,TYPE (HistEvent)* ev)289a13cd756Schristos history_def_next(void *p, TYPE(HistEvent) *ev)
2906dc2f1dbScgd {
2916dc2f1dbScgd 	history_t *h = (history_t *) p;
2926dc2f1dbScgd 
2933bca82ecSchristos 	if (h->cursor == &h->list) {
294a7db9a79Schristos 		he_seterrev(ev, _HE_EMPTY_LIST);
295b71bed95Schristos 		return -1;
296a7db9a79Schristos 	}
2976dc2f1dbScgd 
2983bca82ecSchristos 	if (h->cursor->next == &h->list) {
299a7db9a79Schristos 		he_seterrev(ev, _HE_END_REACHED);
300b71bed95Schristos 		return -1;
301a7db9a79Schristos 	}
302a7db9a79Schristos 
3033bca82ecSchristos         h->cursor = h->cursor->next;
3043bca82ecSchristos         *ev = h->cursor->ev;
3053bca82ecSchristos 
306b71bed95Schristos 	return 0;
3076dc2f1dbScgd }
3086dc2f1dbScgd 
3096dc2f1dbScgd 
3106dc2f1dbScgd /* history_def_prev():
3116dc2f1dbScgd  *	Default function to return the previous event in the history.
3126dc2f1dbScgd  */
313469d44f8Schristos static int
history_def_prev(void * p,TYPE (HistEvent)* ev)314a13cd756Schristos history_def_prev(void *p, TYPE(HistEvent) *ev)
3156dc2f1dbScgd {
3166dc2f1dbScgd 	history_t *h = (history_t *) p;
3176dc2f1dbScgd 
3183bca82ecSchristos 	if (h->cursor == &h->list) {
319d30d584aSlukem 		he_seterrev(ev,
320d30d584aSlukem 		    (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
321b71bed95Schristos 		return -1;
322a7db9a79Schristos 	}
3236dc2f1dbScgd 
3243bca82ecSchristos 	if (h->cursor->prev == &h->list) {
325a7db9a79Schristos 		he_seterrev(ev, _HE_START_REACHED);
326b71bed95Schristos 		return -1;
327a7db9a79Schristos 	}
328a7db9a79Schristos 
3293bca82ecSchristos         h->cursor = h->cursor->prev;
3303bca82ecSchristos         *ev = h->cursor->ev;
3313bca82ecSchristos 
332b71bed95Schristos 	return 0;
3336dc2f1dbScgd }
3346dc2f1dbScgd 
3356dc2f1dbScgd 
3366dc2f1dbScgd /* history_def_curr():
3376dc2f1dbScgd  *	Default function to return the current event in the history.
3386dc2f1dbScgd  */
339469d44f8Schristos static int
history_def_curr(void * p,TYPE (HistEvent)* ev)340a13cd756Schristos history_def_curr(void *p, TYPE(HistEvent) *ev)
3416dc2f1dbScgd {
3426dc2f1dbScgd 	history_t *h = (history_t *) p;
3436dc2f1dbScgd 
3446dc2f1dbScgd 	if (h->cursor != &h->list)
345a7db9a79Schristos 		*ev = h->cursor->ev;
346a7db9a79Schristos 	else {
347d30d584aSlukem 		he_seterrev(ev,
348d30d584aSlukem 		    (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
349b71bed95Schristos 		return -1;
350a7db9a79Schristos 	}
351a7db9a79Schristos 
352b71bed95Schristos 	return 0;
3536dc2f1dbScgd }
3546dc2f1dbScgd 
355ccac6ba8Schristos 
356ccac6ba8Schristos /* history_def_set():
357ccac6ba8Schristos  *	Default function to set the current event in the history to the
358ccac6ba8Schristos  *	given one.
359ccac6ba8Schristos  */
360469d44f8Schristos static int
history_def_set(void * p,TYPE (HistEvent)* ev,const int n)361a13cd756Schristos history_def_set(void *p, TYPE(HistEvent) *ev, const int n)
362ccac6ba8Schristos {
363ccac6ba8Schristos 	history_t *h = (history_t *) p;
364ccac6ba8Schristos 
365ccac6ba8Schristos 	if (h->cur == 0) {
366ccac6ba8Schristos 		he_seterrev(ev, _HE_EMPTY_LIST);
367b71bed95Schristos 		return -1;
368ccac6ba8Schristos 	}
369ccac6ba8Schristos 	if (h->cursor == &h->list || h->cursor->ev.num != n) {
370ccac6ba8Schristos 		for (h->cursor = h->list.next; h->cursor != &h->list;
371ccac6ba8Schristos 		    h->cursor = h->cursor->next)
372ccac6ba8Schristos 			if (h->cursor->ev.num == n)
373ccac6ba8Schristos 				break;
374ccac6ba8Schristos 	}
375ccac6ba8Schristos 	if (h->cursor == &h->list) {
376ccac6ba8Schristos 		he_seterrev(ev, _HE_NOT_FOUND);
377b71bed95Schristos 		return -1;
378ccac6ba8Schristos 	}
379b71bed95Schristos 	return 0;
380ccac6ba8Schristos }
381ccac6ba8Schristos 
382ccac6ba8Schristos 
383ea3813edSchristos /* history_set_nth():
384ea3813edSchristos  *	Default function to set the current event in the history to the
385ea3813edSchristos  *	n-th one.
386ea3813edSchristos  */
387469d44f8Schristos static int
history_set_nth(void * p,TYPE (HistEvent)* ev,int n)388a13cd756Schristos history_set_nth(void *p, TYPE(HistEvent) *ev, int n)
389ea3813edSchristos {
390ea3813edSchristos 	history_t *h = (history_t *) p;
391ea3813edSchristos 
392ea3813edSchristos 	if (h->cur == 0) {
393ea3813edSchristos 		he_seterrev(ev, _HE_EMPTY_LIST);
394b71bed95Schristos 		return -1;
395ea3813edSchristos 	}
396ea3813edSchristos 	for (h->cursor = h->list.prev; h->cursor != &h->list;
397ea3813edSchristos 	    h->cursor = h->cursor->prev)
398ea3813edSchristos 		if (n-- <= 0)
399ea3813edSchristos 			break;
400ea3813edSchristos 	if (h->cursor == &h->list) {
401ea3813edSchristos 		he_seterrev(ev, _HE_NOT_FOUND);
402b71bed95Schristos 		return -1;
403ea3813edSchristos 	}
404b71bed95Schristos 	return 0;
405ea3813edSchristos }
406ea3813edSchristos 
407ea3813edSchristos 
4086dc2f1dbScgd /* history_def_add():
4096dc2f1dbScgd  *	Append string to element
4106dc2f1dbScgd  */
411469d44f8Schristos static int
history_def_add(void * p,TYPE (HistEvent)* ev,const Char * str)412a13cd756Schristos history_def_add(void *p, TYPE(HistEvent) *ev, const Char *str)
4136dc2f1dbScgd {
4146dc2f1dbScgd 	history_t *h = (history_t *) p;
415991f6216Schristos 	size_t len, elen, slen;
41634e53048Schristos 	Char *s;
4170e0ac6b7Schristos 	HistEventPrivate *evp = (void *)&h->cursor->ev;
4186dc2f1dbScgd 
4196dc2f1dbScgd 	if (h->cursor == &h->list)
420b71bed95Schristos 		return history_def_enter(p, ev, str);
421991f6216Schristos 	elen = Strlen(evp->str);
422991f6216Schristos 	slen = Strlen(str);
423991f6216Schristos 	len = elen + slen + 1;
42434e53048Schristos 	s = h_malloc(len * sizeof(*s));
425e6ee0301Schristos 	if (s == NULL) {
426a7db9a79Schristos 		he_seterrev(ev, _HE_MALLOC_FAILED);
427b71bed95Schristos 		return -1;
428a7db9a79Schristos 	}
429991f6216Schristos 	memcpy(s, evp->str, elen * sizeof(*s));
430991f6216Schristos 	memcpy(s + elen, str, slen * sizeof(*s));
43134e53048Schristos         s[len - 1] = '\0';
432a13cd756Schristos 	h_free(evp->str);
4330e0ac6b7Schristos 	evp->str = s;
434a7db9a79Schristos 	*ev = h->cursor->ev;
435b71bed95Schristos 	return 0;
4366dc2f1dbScgd }
4376dc2f1dbScgd 
4386dc2f1dbScgd 
439469d44f8Schristos static int
history_deldata_nth(history_t * h,TYPE (HistEvent)* ev,int num,void ** data)44034e53048Schristos history_deldata_nth(history_t *h, TYPE(HistEvent) *ev,
441ea3813edSchristos     int num, void **data)
442ea3813edSchristos {
443ea3813edSchristos 	if (history_set_nth(h, ev, num) != 0)
444b71bed95Schristos 		return -1;
445ea3813edSchristos 	/* magic value to skip delete (just set to n-th history) */
446ea3813edSchristos 	if (data == (void **)-1)
447b71bed95Schristos 		return 0;
44834e53048Schristos 	ev->str = Strdup(h->cursor->ev.str);
449ea3813edSchristos 	ev->num = h->cursor->ev.num;
450ea3813edSchristos 	if (data)
451ea3813edSchristos 		*data = h->cursor->data;
452ea3813edSchristos 	history_def_delete(h, ev, h->cursor);
453b71bed95Schristos 	return 0;
454ea3813edSchristos }
455ea3813edSchristos 
456ea3813edSchristos 
45745542456Schristos /* history_def_del():
45845542456Schristos  *	Delete element hp of the h list
45945542456Schristos  */
46045542456Schristos /* ARGSUSED */
461469d44f8Schristos static int
history_def_del(void * p,TYPE (HistEvent)* ev,const int num)462a13cd756Schristos history_def_del(void *p, TYPE(HistEvent) *ev __attribute__((__unused__)),
46345542456Schristos     const int num)
46445542456Schristos {
46545542456Schristos 	history_t *h = (history_t *) p;
46645542456Schristos 	if (history_def_set(h, ev, num) != 0)
467b71bed95Schristos 		return -1;
46834e53048Schristos 	ev->str = Strdup(h->cursor->ev.str);
46945542456Schristos 	ev->num = h->cursor->ev.num;
47045542456Schristos 	history_def_delete(h, ev, h->cursor);
471b71bed95Schristos 	return 0;
47245542456Schristos }
47345542456Schristos 
47445542456Schristos 
4756dc2f1dbScgd /* history_def_delete():
4766dc2f1dbScgd  *	Delete element hp of the h list
4776dc2f1dbScgd  */
47861df62e6Schristos /* ARGSUSED */
479469d44f8Schristos static void
history_def_delete(history_t * h,TYPE (HistEvent)* ev,hentry_t * hp)480839ca00bSchristos history_def_delete(history_t *h,
48134e53048Schristos 		   TYPE(HistEvent) *ev __attribute__((__unused__)), hentry_t *hp)
4826dc2f1dbScgd {
4830e0ac6b7Schristos 	HistEventPrivate *evp = (void *)&hp->ev;
4846dc2f1dbScgd 	if (hp == &h->list)
4856dc2f1dbScgd 		abort();
486ea3813edSchristos 	if (h->cursor == hp) {
48745542456Schristos 		h->cursor = hp->prev;
488ea3813edSchristos 		if (h->cursor == &h->list)
489ea3813edSchristos 			h->cursor = hp->next;
490ea3813edSchristos 	}
4916dc2f1dbScgd 	hp->prev->next = hp->next;
4926dc2f1dbScgd 	hp->next->prev = hp->prev;
493a13cd756Schristos 	h_free(evp->str);
4946dc2f1dbScgd 	h_free(hp);
4956dc2f1dbScgd 	h->cur--;
4966dc2f1dbScgd }
4976dc2f1dbScgd 
4986dc2f1dbScgd 
4996dc2f1dbScgd /* history_def_insert():
5006dc2f1dbScgd  *	Insert element with string str in the h list
5016dc2f1dbScgd  */
502469d44f8Schristos static int
history_def_insert(history_t * h,TYPE (HistEvent)* ev,const Char * str)50334e53048Schristos history_def_insert(history_t *h, TYPE(HistEvent) *ev, const Char *str)
5046dc2f1dbScgd {
50532d7653dSchristos 	hentry_t *c;
506d30d584aSlukem 
50732d7653dSchristos 	c = h_malloc(sizeof(*c));
50832d7653dSchristos 	if (c == NULL)
509e6ee0301Schristos 		goto oomem;
51032d7653dSchristos 	if ((c->ev.str = h_strdup(str)) == NULL) {
511a13cd756Schristos 		h_free(c);
512e6ee0301Schristos 		goto oomem;
513a7db9a79Schristos 	}
51432d7653dSchristos 	c->data = NULL;
51532d7653dSchristos 	c->ev.num = ++h->eventid;
51632d7653dSchristos 	c->next = h->list.next;
51732d7653dSchristos 	c->prev = &h->list;
51832d7653dSchristos 	h->list.next->prev = c;
51932d7653dSchristos 	h->list.next = c;
5206dc2f1dbScgd 	h->cur++;
52132d7653dSchristos 	h->cursor = c;
5226dc2f1dbScgd 
52332d7653dSchristos 	*ev = c->ev;
524b71bed95Schristos 	return 0;
525e6ee0301Schristos oomem:
526e6ee0301Schristos 	he_seterrev(ev, _HE_MALLOC_FAILED);
527b71bed95Schristos 	return -1;
5286dc2f1dbScgd }
5296dc2f1dbScgd 
5306dc2f1dbScgd 
5316dc2f1dbScgd /* history_def_enter():
5326dc2f1dbScgd  *	Default function to enter an item in the history
5336dc2f1dbScgd  */
534469d44f8Schristos static int
history_def_enter(void * p,TYPE (HistEvent)* ev,const Char * str)535a13cd756Schristos history_def_enter(void *p, TYPE(HistEvent) *ev, const Char *str)
5366dc2f1dbScgd {
5376dc2f1dbScgd 	history_t *h = (history_t *) p;
5386dc2f1dbScgd 
539f24857bfSchristos 	if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
54034e53048Schristos 	    Strcmp(h->list.next->ev.str, str) == 0)
541b71bed95Schristos 	    return 0;
542f24857bfSchristos 
543a7db9a79Schristos 	if (history_def_insert(h, ev, str) == -1)
544b71bed95Schristos 		return -1;	/* error, keep error message */
5456dc2f1dbScgd 
5466dc2f1dbScgd 	/*
5476dc2f1dbScgd          * Always keep at least one entry.
5486dc2f1dbScgd          * This way we don't have to check for the empty list.
5496dc2f1dbScgd          */
550e7e71e37Sjdolecek 	while (h->cur > h->max && h->cur > 0)
551a7db9a79Schristos 		history_def_delete(h, ev, h->list.prev);
552a7db9a79Schristos 
553b71bed95Schristos 	return 1;
5546dc2f1dbScgd }
5556dc2f1dbScgd 
5566dc2f1dbScgd 
5576dc2f1dbScgd /* history_def_init():
5586dc2f1dbScgd  *	Default history initialization function
5596dc2f1dbScgd  */
56061df62e6Schristos /* ARGSUSED */
561469d44f8Schristos static int
history_def_init(void ** p,TYPE (HistEvent)* ev,int n)562a13cd756Schristos history_def_init(void **p, TYPE(HistEvent) *ev __attribute__((__unused__)), int n)
5636dc2f1dbScgd {
564a13cd756Schristos 	history_t *h = (history_t *) h_malloc(sizeof(*h));
565e6ee0301Schristos 	if (h == NULL)
566e6ee0301Schristos 		return -1;
567d30d584aSlukem 
5686dc2f1dbScgd 	if (n <= 0)
5696dc2f1dbScgd 		n = 0;
570a7db9a79Schristos 	h->eventid = 0;
5716dc2f1dbScgd 	h->cur = 0;
5726dc2f1dbScgd 	h->max = n;
5736dc2f1dbScgd 	h->list.next = h->list.prev = &h->list;
5746dc2f1dbScgd 	h->list.ev.str = NULL;
5756dc2f1dbScgd 	h->list.ev.num = 0;
5766dc2f1dbScgd 	h->cursor = &h->list;
577f24857bfSchristos 	h->flags = 0;
578a13cd756Schristos 	*p = h;
579e6ee0301Schristos 	return 0;
5806dc2f1dbScgd }
5816dc2f1dbScgd 
5826dc2f1dbScgd 
58395b0e87bSchristos /* history_def_clear():
5846dc2f1dbScgd  *	Default history cleanup function
5856dc2f1dbScgd  */
586469d44f8Schristos static void
history_def_clear(void * p,TYPE (HistEvent)* ev)587a13cd756Schristos history_def_clear(void *p, TYPE(HistEvent) *ev)
5886dc2f1dbScgd {
5896dc2f1dbScgd 	history_t *h = (history_t *) p;
5906dc2f1dbScgd 
5916dc2f1dbScgd 	while (h->list.prev != &h->list)
592a7db9a79Schristos 		history_def_delete(h, ev, h->list.prev);
59332d7653dSchristos 	h->cursor = &h->list;
594a7db9a79Schristos 	h->eventid = 0;
59595b0e87bSchristos 	h->cur = 0;
5966dc2f1dbScgd }
5976dc2f1dbScgd 
59895b0e87bSchristos 
59995b0e87bSchristos 
60095b0e87bSchristos 
6016dc2f1dbScgd /************************************************************************/
6026dc2f1dbScgd 
6036dc2f1dbScgd /* history_init():
6046dc2f1dbScgd  *	Initialization function.
6056dc2f1dbScgd  */
TYPE(History)606469d44f8Schristos TYPE(History) *
60734e53048Schristos FUN(history,init)(void)
6086dc2f1dbScgd {
60934e53048Schristos 	TYPE(HistEvent) ev;
610a13cd756Schristos 	TYPE(History) *h = (TYPE(History) *) h_malloc(sizeof(*h));
611e6ee0301Schristos 	if (h == NULL)
612e6ee0301Schristos 		return NULL;
6136dc2f1dbScgd 
614e6ee0301Schristos 	if (history_def_init(&h->h_ref, &ev, 0) == -1) {
615a13cd756Schristos 		h_free(h);
616e6ee0301Schristos 		return NULL;
617e6ee0301Schristos 	}
618eac8b136Schristos 	h->h_ent = -1;
6196dc2f1dbScgd 	h->h_next = history_def_next;
6206dc2f1dbScgd 	h->h_first = history_def_first;
6216dc2f1dbScgd 	h->h_last = history_def_last;
6226dc2f1dbScgd 	h->h_prev = history_def_prev;
6236dc2f1dbScgd 	h->h_curr = history_def_curr;
624ccac6ba8Schristos 	h->h_set = history_def_set;
62595b0e87bSchristos 	h->h_clear = history_def_clear;
6266dc2f1dbScgd 	h->h_enter = history_def_enter;
6276dc2f1dbScgd 	h->h_add = history_def_add;
628ee399edcSchristos 	h->h_del = history_def_del;
6296dc2f1dbScgd 
630b71bed95Schristos 	return h;
6316dc2f1dbScgd }
6326dc2f1dbScgd 
6336dc2f1dbScgd 
6346dc2f1dbScgd /* history_end():
6356dc2f1dbScgd  *	clean up history;
6366dc2f1dbScgd  */
637469d44f8Schristos void
FUN(history,end)63834e53048Schristos FUN(history,end)(TYPE(History) *h)
6396dc2f1dbScgd {
64034e53048Schristos 	TYPE(HistEvent) ev;
641d30d584aSlukem 
6426dc2f1dbScgd 	if (h->h_next == history_def_next)
643a7db9a79Schristos 		history_def_clear(h->h_ref, &ev);
644d9590bcaSchristos 	h_free(h->h_ref);
64532bff6afSchristos 	h_free(h);
6466dc2f1dbScgd }
6476dc2f1dbScgd 
6486dc2f1dbScgd 
6496dc2f1dbScgd 
65084ee55f1Schristos /* history_setsize():
6516dc2f1dbScgd  *	Set history number of events
6526dc2f1dbScgd  */
653469d44f8Schristos static int
history_setsize(TYPE (History)* h,TYPE (HistEvent)* ev,int num)65434e53048Schristos history_setsize(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
6556dc2f1dbScgd {
656d30d584aSlukem 
657a7db9a79Schristos 	if (h->h_next != history_def_next) {
658a7db9a79Schristos 		he_seterrev(ev, _HE_NOT_ALLOWED);
659b71bed95Schristos 		return -1;
660a7db9a79Schristos 	}
661a7db9a79Schristos 	if (num < 0) {
662a7db9a79Schristos 		he_seterrev(ev, _HE_BAD_PARAM);
663b71bed95Schristos 		return -1;
664d30d584aSlukem 	}
665d30d584aSlukem 	history_def_setsize(h->h_ref, num);
666b71bed95Schristos 	return 0;
667a7db9a79Schristos }
668a7db9a79Schristos 
6696dc2f1dbScgd 
67084ee55f1Schristos /* history_getsize():
671a7db9a79Schristos  *      Get number of events currently in history
672a7db9a79Schristos  */
673469d44f8Schristos static int
history_getsize(TYPE (History)* h,TYPE (HistEvent)* ev)67434e53048Schristos history_getsize(TYPE(History) *h, TYPE(HistEvent) *ev)
675a7db9a79Schristos {
676f24857bfSchristos 	if (h->h_next != history_def_next) {
677f24857bfSchristos 		he_seterrev(ev, _HE_NOT_ALLOWED);
678b71bed95Schristos 		return -1;
679f24857bfSchristos 	}
680f24857bfSchristos 	ev->num = history_def_getsize(h->h_ref);
681f24857bfSchristos 	if (ev->num < -1) {
682f24857bfSchristos 		he_seterrev(ev, _HE_SIZE_NEGATIVE);
683b71bed95Schristos 		return -1;
684f24857bfSchristos 	}
685b71bed95Schristos 	return 0;
686f24857bfSchristos }
687f24857bfSchristos 
688f24857bfSchristos 
689f24857bfSchristos /* history_setunique():
690f24857bfSchristos  *	Set if adjacent equal events should not be entered in history.
691f24857bfSchristos  */
692469d44f8Schristos static int
history_setunique(TYPE (History)* h,TYPE (HistEvent)* ev,int uni)69334e53048Schristos history_setunique(TYPE(History) *h, TYPE(HistEvent) *ev, int uni)
694f24857bfSchristos {
695a7db9a79Schristos 
696a7db9a79Schristos 	if (h->h_next != history_def_next) {
697a7db9a79Schristos 		he_seterrev(ev, _HE_NOT_ALLOWED);
698b71bed95Schristos 		return -1;
699a7db9a79Schristos 	}
700f24857bfSchristos 	history_def_setunique(h->h_ref, uni);
701b71bed95Schristos 	return 0;
702f24857bfSchristos }
703f24857bfSchristos 
704f24857bfSchristos 
705f24857bfSchristos /* history_getunique():
706f24857bfSchristos  *	Get if adjacent equal events should not be entered in history.
707f24857bfSchristos  */
708469d44f8Schristos static int
history_getunique(TYPE (History)* h,TYPE (HistEvent)* ev)70934e53048Schristos history_getunique(TYPE(History) *h, TYPE(HistEvent) *ev)
710f24857bfSchristos {
711f24857bfSchristos 	if (h->h_next != history_def_next) {
712f24857bfSchristos 		he_seterrev(ev, _HE_NOT_ALLOWED);
713b71bed95Schristos 		return -1;
714d30d584aSlukem 	}
715f24857bfSchristos 	ev->num = history_def_getunique(h->h_ref);
716b71bed95Schristos 	return 0;
717a7db9a79Schristos }
718a7db9a79Schristos 
7196dc2f1dbScgd 
7206dc2f1dbScgd /* history_set_fun():
7216dc2f1dbScgd  *	Set history functions
7226dc2f1dbScgd  */
723469d44f8Schristos static int
history_set_fun(TYPE (History)* h,TYPE (History)* nh)72434e53048Schristos history_set_fun(TYPE(History) *h, TYPE(History) *nh)
7256dc2f1dbScgd {
72634e53048Schristos 	TYPE(HistEvent) ev;
727a7db9a79Schristos 
728ccac6ba8Schristos 	if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
729ccac6ba8Schristos 	    nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
73095b0e87bSchristos 	    nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
73145542456Schristos 	    nh->h_del == NULL || nh->h_ref == NULL) {
7326dc2f1dbScgd 		if (h->h_next != history_def_next) {
733ae5295eeSchristos 			if (history_def_init(&h->h_ref, &ev, 0) == -1)
734ae5295eeSchristos 				return -1;
7356dc2f1dbScgd 			h->h_first = history_def_first;
7366dc2f1dbScgd 			h->h_next = history_def_next;
7376dc2f1dbScgd 			h->h_last = history_def_last;
7386dc2f1dbScgd 			h->h_prev = history_def_prev;
7396dc2f1dbScgd 			h->h_curr = history_def_curr;
740ccac6ba8Schristos 			h->h_set = history_def_set;
74195b0e87bSchristos 			h->h_clear = history_def_clear;
7426dc2f1dbScgd 			h->h_enter = history_def_enter;
7436dc2f1dbScgd 			h->h_add = history_def_add;
74445542456Schristos 			h->h_del = history_def_del;
7456dc2f1dbScgd 		}
746b71bed95Schristos 		return -1;
7476dc2f1dbScgd 	}
7486dc2f1dbScgd 	if (h->h_next == history_def_next)
749a7db9a79Schristos 		history_def_clear(h->h_ref, &ev);
7506dc2f1dbScgd 
751eac8b136Schristos 	h->h_ent = -1;
75295b0e87bSchristos 	h->h_first = nh->h_first;
75395b0e87bSchristos 	h->h_next = nh->h_next;
75495b0e87bSchristos 	h->h_last = nh->h_last;
75595b0e87bSchristos 	h->h_prev = nh->h_prev;
75695b0e87bSchristos 	h->h_curr = nh->h_curr;
757ccac6ba8Schristos 	h->h_set = nh->h_set;
75895b0e87bSchristos 	h->h_clear = nh->h_clear;
75995b0e87bSchristos 	h->h_enter = nh->h_enter;
76095b0e87bSchristos 	h->h_add = nh->h_add;
76145542456Schristos 	h->h_del = nh->h_del;
76295b0e87bSchristos 
763b71bed95Schristos 	return 0;
7646dc2f1dbScgd }
7656dc2f1dbScgd 
7666dc2f1dbScgd 
76795b0e87bSchristos /* history_load():
76834e53048Schristos  *	TYPE(History) load function
76995b0e87bSchristos  */
770469d44f8Schristos static int
history_load(TYPE (History)* h,const char * fname)77134e53048Schristos history_load(TYPE(History) *h, const char *fname)
77295b0e87bSchristos {
77395b0e87bSchristos 	FILE *fp;
77495b0e87bSchristos 	char *line;
775a7ab79fbSchristos 	size_t llen;
776a7ab79fbSchristos 	ssize_t sz;
777a7ab79fbSchristos 	size_t max_size;
77884ee55f1Schristos 	char *ptr;
77995b0e87bSchristos 	int i = -1;
78034e53048Schristos 	TYPE(HistEvent) ev;
7816da79ee0Schristos 	Char *decode_result;
7824e541d85Schristos #ifndef NARROWCHAR
7830b9ae3fdSchristos 	static ct_buffer_t conv;
78434e53048Schristos #endif
78595b0e87bSchristos 
78695b0e87bSchristos 	if ((fp = fopen(fname, "r")) == NULL)
787b71bed95Schristos 		return i;
78895b0e87bSchristos 
789a7ab79fbSchristos 	line = NULL;
790a7ab79fbSchristos 	llen = 0;
791a7ab79fbSchristos 	if ((sz = getline(&line, &llen, fp)) == -1)
79295b0e87bSchristos 		goto done;
79395b0e87bSchristos 
794a7ab79fbSchristos 	if (strncmp(line, hist_cookie, (size_t)sz) != 0)
79595b0e87bSchristos 		goto done;
79695b0e87bSchristos 
797a13cd756Schristos 	ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
798e6ee0301Schristos 	if (ptr == NULL)
799e6ee0301Schristos 		goto done;
800a7ab79fbSchristos 	for (i = 0; (sz = getline(&line, &llen, fp)) != -1; i++) {
801a7ab79fbSchristos 		if (sz > 0 && line[sz - 1] == '\n')
802a7ab79fbSchristos 			line[--sz] = '\0';
803a7ab79fbSchristos 		if (max_size < (size_t)sz) {
804e6ee0301Schristos 			char *nptr;
805a7ab79fbSchristos 			max_size = ((size_t)sz + 1024) & (size_t)~1023;
806a13cd756Schristos 			nptr = h_realloc(ptr, max_size * sizeof(*ptr));
807e6ee0301Schristos 			if (nptr == NULL) {
808e6ee0301Schristos 				i = -1;
809e6ee0301Schristos 				goto oomem;
810e6ee0301Schristos 			}
811e6ee0301Schristos 			ptr = nptr;
81295b0e87bSchristos 		}
81384ee55f1Schristos 		(void) strunvis(ptr, line);
8146da79ee0Schristos 		decode_result = ct_decode_string(ptr, &conv);
8156da79ee0Schristos 		if (decode_result == NULL)
8166da79ee0Schristos 			continue;
817b8b0289dSkre 		if (HENTER(h, &ev, decode_result) == -1) {
818fe96b18cSsketch 			i = -1;
819fe96b18cSsketch 			goto oomem;
82084ee55f1Schristos 		}
821e6ee0301Schristos 	}
822e6ee0301Schristos oomem:
823a13cd756Schristos 	h_free(ptr);
82495b0e87bSchristos done:
82587240809Schristos 	free(line);
82695b0e87bSchristos 	(void) fclose(fp);
827b71bed95Schristos 	return i;
82895b0e87bSchristos }
82995b0e87bSchristos 
83095b0e87bSchristos 
83158ced3d7Schristos /* history_save_fp():
83234e53048Schristos  *	TYPE(History) save function
83395b0e87bSchristos  */
834469d44f8Schristos static int
history_save_fp(TYPE (History)* h,size_t nelem,FILE * fp)83527916d7cSchristos history_save_fp(TYPE(History) *h, size_t nelem, FILE *fp)
83695b0e87bSchristos {
83734e53048Schristos 	TYPE(HistEvent) ev;
838e6ee0301Schristos 	int i = -1, retval;
83984ee55f1Schristos 	size_t len, max_size;
8405bf2e3dbSchristos 	char *ptr;
8415bf2e3dbSchristos 	const char *str;
8424e541d85Schristos #ifndef NARROWCHAR
8430b9ae3fdSchristos 	static ct_buffer_t conv;
84434e53048Schristos #endif
84584ee55f1Schristos 
84627916d7cSchristos 	if (ftell(fp) == 0 && fputs(hist_cookie, fp) == EOF)
847e6ee0301Schristos 		goto done;
848a13cd756Schristos 	ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
849e6ee0301Schristos 	if (ptr == NULL)
850e6ee0301Schristos 		goto done;
85127916d7cSchristos 	if (nelem != (size_t)-1) {
85227916d7cSchristos 		for (retval = HFIRST(h, &ev); retval != -1 && nelem-- > 0;
85327916d7cSchristos 		    retval = HNEXT(h, &ev))
85427916d7cSchristos 			continue;
85527916d7cSchristos 	} else
85627916d7cSchristos 		retval = -1;
85727916d7cSchristos 
85827916d7cSchristos 	if (retval == -1)
85927916d7cSchristos 		retval = HLAST(h, &ev);
86027916d7cSchristos 
86127916d7cSchristos 	for (i = 0; retval != -1; retval = HPREV(h, &ev), i++) {
86232d7653dSchristos 		str = ct_encode_string(ev.str, &conv);
863c8255363Schristos 		len = strlen(str) * 4 + 1;
864c8255363Schristos 		if (len > max_size) {
865e6ee0301Schristos 			char *nptr;
866c11bd863Schristos 			max_size = (len + 1024) & (size_t)~1023;
867a13cd756Schristos 			nptr = h_realloc(ptr, max_size * sizeof(*ptr));
868e6ee0301Schristos 			if (nptr == NULL) {
869e6ee0301Schristos 				i = -1;
870e6ee0301Schristos 				goto oomem;
871e6ee0301Schristos 			}
872e6ee0301Schristos 			ptr = nptr;
87384ee55f1Schristos 		}
87432d7653dSchristos 		(void) strvis(ptr, str, VIS_WHITE);
8754a964eafSchristos 		(void) fprintf(fp, "%s\n", ptr);
87684ee55f1Schristos 	}
877e6ee0301Schristos oomem:
878a13cd756Schristos 	h_free(ptr);
879e6ee0301Schristos done:
88058ced3d7Schristos 	return i;
88158ced3d7Schristos }
88258ced3d7Schristos 
88358ced3d7Schristos 
88458ced3d7Schristos /* history_save():
88558ced3d7Schristos  *    History save function
88658ced3d7Schristos  */
887469d44f8Schristos static int
history_save(TYPE (History)* h,const char * fname)88858ced3d7Schristos history_save(TYPE(History) *h, const char *fname)
88958ced3d7Schristos {
89058ced3d7Schristos     FILE *fp;
89158ced3d7Schristos     int i;
89258ced3d7Schristos 
893*4d36c519Skre     if ((i = open(fname, O_WRONLY|O_CREAT|O_TRUNC,
894*4d36c519Skre 		S_IRUSR|S_IWUSR)) == -1)
895*4d36c519Skre 	return -1;
896*4d36c519Skre 
897*4d36c519Skre     if ((fp = fdopen(i, "w")) == NULL)
89858ced3d7Schristos 	return -1;
89958ced3d7Schristos 
90027916d7cSchristos     i = history_save_fp(h, (size_t)-1, fp);
90158ced3d7Schristos 
90295b0e87bSchristos     (void) fclose(fp);
903b71bed95Schristos     return i;
90495b0e87bSchristos }
90595b0e87bSchristos 
90695b0e87bSchristos 
9076dc2f1dbScgd /* history_prev_event():
9086dc2f1dbScgd  *	Find the previous event, with number given
9096dc2f1dbScgd  */
910469d44f8Schristos static int
history_prev_event(TYPE (History)* h,TYPE (HistEvent)* ev,int num)91134e53048Schristos history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
9126dc2f1dbScgd {
913a7db9a79Schristos 	int retval;
914d30d584aSlukem 
915a7db9a79Schristos 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
9166dc2f1dbScgd 		if (ev->num == num)
917b71bed95Schristos 			return 0;
918a7db9a79Schristos 
919a7db9a79Schristos 	he_seterrev(ev, _HE_NOT_FOUND);
920b71bed95Schristos 	return -1;
9216dc2f1dbScgd }
9226dc2f1dbScgd 
9236dc2f1dbScgd 
924469d44f8Schristos static int
history_next_evdata(TYPE (History)* h,TYPE (HistEvent)* ev,int num,void ** d)92534e53048Schristos history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d)
926ea3813edSchristos {
927ea3813edSchristos 	int retval;
928ea3813edSchristos 
929ea3813edSchristos 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
9307350622eSchristos 		if (ev->num == num) {
931ea3813edSchristos 			if (d)
932ea3813edSchristos 				*d = ((history_t *)h->h_ref)->cursor->data;
933b71bed95Schristos 			return 0;
934ea3813edSchristos 		}
935ea3813edSchristos 
936ea3813edSchristos 	he_seterrev(ev, _HE_NOT_FOUND);
937b71bed95Schristos 	return -1;
938ea3813edSchristos }
939ea3813edSchristos 
940ea3813edSchristos 
9416dc2f1dbScgd /* history_next_event():
9426dc2f1dbScgd  *	Find the next event, with number given
9436dc2f1dbScgd  */
944469d44f8Schristos static int
history_next_event(TYPE (History)* h,TYPE (HistEvent)* ev,int num)94534e53048Schristos history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
9466dc2f1dbScgd {
947a7db9a79Schristos 	int retval;
948d30d584aSlukem 
949a7db9a79Schristos 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
9506dc2f1dbScgd 		if (ev->num == num)
951b71bed95Schristos 			return 0;
952a7db9a79Schristos 
953a7db9a79Schristos 	he_seterrev(ev, _HE_NOT_FOUND);
954b71bed95Schristos 	return -1;
9556dc2f1dbScgd }
9566dc2f1dbScgd 
9576dc2f1dbScgd 
9586dc2f1dbScgd /* history_prev_string():
9596dc2f1dbScgd  *	Find the previous event beginning with string
9606dc2f1dbScgd  */
961469d44f8Schristos static int
history_prev_string(TYPE (History)* h,TYPE (HistEvent)* ev,const Char * str)96234e53048Schristos history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
9636dc2f1dbScgd {
96434e53048Schristos 	size_t len = Strlen(str);
965a7db9a79Schristos 	int retval;
9666dc2f1dbScgd 
967a7db9a79Schristos 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
96834e53048Schristos 		if (Strncmp(str, ev->str, len) == 0)
969b71bed95Schristos 			return 0;
970a7db9a79Schristos 
971a7db9a79Schristos 	he_seterrev(ev, _HE_NOT_FOUND);
972b71bed95Schristos 	return -1;
9736dc2f1dbScgd }
9746dc2f1dbScgd 
9756dc2f1dbScgd 
9766dc2f1dbScgd /* history_next_string():
9776dc2f1dbScgd  *	Find the next event beginning with string
9786dc2f1dbScgd  */
979469d44f8Schristos static int
history_next_string(TYPE (History)* h,TYPE (HistEvent)* ev,const Char * str)98034e53048Schristos history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
9816dc2f1dbScgd {
98234e53048Schristos 	size_t len = Strlen(str);
983a7db9a79Schristos 	int retval;
9846dc2f1dbScgd 
985a7db9a79Schristos 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
98634e53048Schristos 		if (Strncmp(str, ev->str, len) == 0)
987b71bed95Schristos 			return 0;
988a7db9a79Schristos 
989a7db9a79Schristos 	he_seterrev(ev, _HE_NOT_FOUND);
990b71bed95Schristos 	return -1;
9916dc2f1dbScgd }
9926dc2f1dbScgd 
9936dc2f1dbScgd 
9946dc2f1dbScgd /* history():
9956dc2f1dbScgd  *	User interface to history functions.
9966dc2f1dbScgd  */
997a7db9a79Schristos int
FUNW(history)99834e53048Schristos FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...)
9996dc2f1dbScgd {
10006dc2f1dbScgd 	va_list va;
100134e53048Schristos 	const Char *str;
1002a7db9a79Schristos 	int retval;
10036dc2f1dbScgd 
10046dc2f1dbScgd 	va_start(va, fun);
10056dc2f1dbScgd 
1006a7db9a79Schristos 	he_seterrev(ev, _HE_OK);
1007a7db9a79Schristos 
10086dc2f1dbScgd 	switch (fun) {
1009ccac6ba8Schristos 	case H_GETSIZE:
101084ee55f1Schristos 		retval = history_getsize(h, ev);
1011ccac6ba8Schristos 		break;
1012ccac6ba8Schristos 
1013ccac6ba8Schristos 	case H_SETSIZE:
101484ee55f1Schristos 		retval = history_setsize(h, ev, va_arg(va, int));
1015ccac6ba8Schristos 		break;
1016ccac6ba8Schristos 
1017f24857bfSchristos 	case H_GETUNIQUE:
1018f24857bfSchristos 		retval = history_getunique(h, ev);
1019f24857bfSchristos 		break;
1020f24857bfSchristos 
1021f24857bfSchristos 	case H_SETUNIQUE:
1022f24857bfSchristos 		retval = history_setunique(h, ev, va_arg(va, int));
1023f24857bfSchristos 		break;
1024f24857bfSchristos 
10256dc2f1dbScgd 	case H_ADD:
102634e53048Schristos 		str = va_arg(va, const Char *);
1027a7db9a79Schristos 		retval = HADD(h, ev, str);
10286dc2f1dbScgd 		break;
10296dc2f1dbScgd 
103045542456Schristos 	case H_DEL:
103145542456Schristos 		retval = HDEL(h, ev, va_arg(va, const int));
103245542456Schristos 		break;
103345542456Schristos 
10346dc2f1dbScgd 	case H_ENTER:
103534e53048Schristos 		str = va_arg(va, const Char *);
1036eac8b136Schristos 		if ((retval = HENTER(h, ev, str)) != -1)
1037eac8b136Schristos 			h->h_ent = ev->num;
1038eac8b136Schristos 		break;
1039eac8b136Schristos 
1040eac8b136Schristos 	case H_APPEND:
104134e53048Schristos 		str = va_arg(va, const Char *);
1042eac8b136Schristos 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
1043eac8b136Schristos 			retval = HADD(h, ev, str);
10446dc2f1dbScgd 		break;
10456dc2f1dbScgd 
10466dc2f1dbScgd 	case H_FIRST:
1047a7db9a79Schristos 		retval = HFIRST(h, ev);
10486dc2f1dbScgd 		break;
10496dc2f1dbScgd 
10506dc2f1dbScgd 	case H_NEXT:
1051a7db9a79Schristos 		retval = HNEXT(h, ev);
10526dc2f1dbScgd 		break;
10536dc2f1dbScgd 
10546dc2f1dbScgd 	case H_LAST:
1055a7db9a79Schristos 		retval = HLAST(h, ev);
10566dc2f1dbScgd 		break;
10576dc2f1dbScgd 
10586dc2f1dbScgd 	case H_PREV:
1059a7db9a79Schristos 		retval = HPREV(h, ev);
10606dc2f1dbScgd 		break;
10616dc2f1dbScgd 
10626dc2f1dbScgd 	case H_CURR:
1063a7db9a79Schristos 		retval = HCURR(h, ev);
10646dc2f1dbScgd 		break;
10656dc2f1dbScgd 
1066ccac6ba8Schristos 	case H_SET:
1067ccac6ba8Schristos 		retval = HSET(h, ev, va_arg(va, const int));
1068ccac6ba8Schristos 		break;
1069ccac6ba8Schristos 
107095b0e87bSchristos 	case H_CLEAR:
1071a7db9a79Schristos 		HCLEAR(h, ev);
1072a7db9a79Schristos 		retval = 0;
107395b0e87bSchristos 		break;
107495b0e87bSchristos 
107595b0e87bSchristos 	case H_LOAD:
1076a7db9a79Schristos 		retval = history_load(h, va_arg(va, const char *));
1077a7db9a79Schristos 		if (retval == -1)
1078a7db9a79Schristos 			he_seterrev(ev, _HE_HIST_READ);
107995b0e87bSchristos 		break;
108095b0e87bSchristos 
108195b0e87bSchristos 	case H_SAVE:
1082a7db9a79Schristos 		retval = history_save(h, va_arg(va, const char *));
1083a7db9a79Schristos 		if (retval == -1)
1084a7db9a79Schristos 			he_seterrev(ev, _HE_HIST_WRITE);
108595b0e87bSchristos 		break;
108695b0e87bSchristos 
108758ced3d7Schristos 	case H_SAVE_FP:
108827916d7cSchristos 		retval = history_save_fp(h, (size_t)-1, va_arg(va, FILE *));
108927916d7cSchristos 		if (retval == -1)
109027916d7cSchristos 		    he_seterrev(ev, _HE_HIST_WRITE);
109127916d7cSchristos 		break;
109227916d7cSchristos 
109327916d7cSchristos 	case H_NSAVE_FP:
1094ec59efb2Suwe 	{
1095ec59efb2Suwe 		size_t sz = va_arg(va, size_t);
1096ec59efb2Suwe 		retval = history_save_fp(h, sz, va_arg(va, FILE *));
109758ced3d7Schristos 		if (retval == -1)
109858ced3d7Schristos 		    he_seterrev(ev, _HE_HIST_WRITE);
109958ced3d7Schristos 		break;
1100ec59efb2Suwe 	}
110158ced3d7Schristos 
11026dc2f1dbScgd 	case H_PREV_EVENT:
1103a7db9a79Schristos 		retval = history_prev_event(h, ev, va_arg(va, int));
11046dc2f1dbScgd 		break;
11056dc2f1dbScgd 
11066dc2f1dbScgd 	case H_NEXT_EVENT:
1107a7db9a79Schristos 		retval = history_next_event(h, ev, va_arg(va, int));
11086dc2f1dbScgd 		break;
11096dc2f1dbScgd 
11106dc2f1dbScgd 	case H_PREV_STR:
111134e53048Schristos 		retval = history_prev_string(h, ev, va_arg(va, const Char *));
11126dc2f1dbScgd 		break;
11136dc2f1dbScgd 
11146dc2f1dbScgd 	case H_NEXT_STR:
111534e53048Schristos 		retval = history_next_string(h, ev, va_arg(va, const Char *));
11166dc2f1dbScgd 		break;
11176dc2f1dbScgd 
11186dc2f1dbScgd 	case H_FUNC:
11196dc2f1dbScgd 	{
112034e53048Schristos 		TYPE(History) hf;
1121ccac6ba8Schristos 
1122a13cd756Schristos 		hf.h_ref = va_arg(va, void *);
1123eac8b136Schristos 		h->h_ent = -1;
112495b0e87bSchristos 		hf.h_first = va_arg(va, history_gfun_t);
112595b0e87bSchristos 		hf.h_next = va_arg(va, history_gfun_t);
112695b0e87bSchristos 		hf.h_last = va_arg(va, history_gfun_t);
112795b0e87bSchristos 		hf.h_prev = va_arg(va, history_gfun_t);
112895b0e87bSchristos 		hf.h_curr = va_arg(va, history_gfun_t);
1129ccac6ba8Schristos 		hf.h_set = va_arg(va, history_sfun_t);
113095b0e87bSchristos 		hf.h_clear = va_arg(va, history_vfun_t);
113195b0e87bSchristos 		hf.h_enter = va_arg(va, history_efun_t);
113295b0e87bSchristos 		hf.h_add = va_arg(va, history_efun_t);
113345542456Schristos 		hf.h_del = va_arg(va, history_sfun_t);
11346dc2f1dbScgd 
1135a7db9a79Schristos 		if ((retval = history_set_fun(h, &hf)) == -1)
1136a7db9a79Schristos 			he_seterrev(ev, _HE_PARAM_MISSING);
11376dc2f1dbScgd 		break;
1138d30d584aSlukem 	}
11396dc2f1dbScgd 
11406dc2f1dbScgd 	case H_END:
11410b9ae3fdSchristos 		FUN(history,end)(h);
1142a7db9a79Schristos 		retval = 0;
1143a7db9a79Schristos 		break;
1144a7db9a79Schristos 
1145ea3813edSchristos 	case H_NEXT_EVDATA:
1146ea3813edSchristos 	{
1147ea3813edSchristos 		int num = va_arg(va, int);
1148ea3813edSchristos 		void **d = va_arg(va, void **);
1149ea3813edSchristos 		retval = history_next_evdata(h, ev, num, d);
1150ea3813edSchristos 		break;
1151ea3813edSchristos 	}
1152ea3813edSchristos 
1153ea3813edSchristos 	case H_DELDATA:
1154ea3813edSchristos 	{
1155ea3813edSchristos 		int num = va_arg(va, int);
1156ea3813edSchristos 		void **d = va_arg(va, void **);
1157ea3813edSchristos 		retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d);
1158ea3813edSchristos 		break;
1159ea3813edSchristos 	}
1160ea3813edSchristos 
1161ea3813edSchristos 	case H_REPLACE: /* only use after H_NEXT_EVDATA */
1162ea3813edSchristos 	{
116334e53048Schristos 		const Char *line = va_arg(va, const Char *);
1164ea3813edSchristos 		void *d = va_arg(va, void *);
116534e53048Schristos 		const Char *s;
116634e53048Schristos 		if(!line || !(s = Strdup(line))) {
1167ea3813edSchristos 			retval = -1;
1168ea3813edSchristos 			break;
1169ea3813edSchristos 		}
1170ea3813edSchristos 		((history_t *)h->h_ref)->cursor->ev.str = s;
1171ea3813edSchristos 		((history_t *)h->h_ref)->cursor->data = d;
1172ea3813edSchristos 		retval = 0;
1173ea3813edSchristos 		break;
1174ea3813edSchristos 	}
1175ea3813edSchristos 
11766dc2f1dbScgd 	default:
1177a7db9a79Schristos 		retval = -1;
1178a7db9a79Schristos 		he_seterrev(ev, _HE_UNKNOWN);
11796dc2f1dbScgd 		break;
11806dc2f1dbScgd 	}
11816dc2f1dbScgd 	va_end(va);
1182ea3813edSchristos 	return retval;
11836dc2f1dbScgd }
1184