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