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