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