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