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