xref: /netbsd-src/lib/libedit/history.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: history.c,v 1.32 2006/09/28 13:52: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 #include "config.h"
36 #if !defined(lint) && !defined(SCCSID)
37 #if 0
38 static char sccsid[] = "@(#)history.c	8.1 (Berkeley) 6/4/93";
39 #else
40 __RCSID("$NetBSD: history.c,v 1.32 2006/09/28 13:52:51 christos Exp $");
41 #endif
42 #endif /* not lint && not SCCSID */
43 
44 /*
45  * hist.c: 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 
57 static const char hist_cookie[] = "_HiStOrY_V2_\n";
58 
59 #include "histedit.h"
60 
61 typedef int (*history_gfun_t)(ptr_t, HistEvent *);
62 typedef int (*history_efun_t)(ptr_t, HistEvent *, const char *);
63 typedef void (*history_vfun_t)(ptr_t, HistEvent *);
64 typedef int (*history_sfun_t)(ptr_t, HistEvent *, const int);
65 
66 struct 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 
97 typedef struct {
98     int		num;
99     char	*str;
100 } HistEventPrivate;
101 
102 
103 
104 private int history_setsize(History *, HistEvent *, int);
105 private int history_getsize(History *, HistEvent *);
106 private int history_setunique(History *, HistEvent *, int);
107 private int history_getunique(History *, HistEvent *);
108 private int history_set_fun(History *, History *);
109 private int history_load(History *, const char *);
110 private int history_save(History *, const char *);
111 private int history_prev_event(History *, HistEvent *, int);
112 private int history_next_event(History *, HistEvent *, int);
113 private int history_next_string(History *, HistEvent *, const char *);
114 private int history_prev_string(History *, HistEvent *, const char *);
115 
116 
117 /***********************************************************************/
118 
119 /*
120  * Builtin- history implementation
121  */
122 typedef struct hentry_t {
123 	HistEvent ev;		/* What we return		 */
124 	struct hentry_t *next;	/* Next entry			 */
125 	struct hentry_t *prev;	/* Previous entry		 */
126 } hentry_t;
127 
128 typedef struct history_t {
129 	hentry_t list;		/* Fake list header element	*/
130 	hentry_t *cursor;	/* Current element in the list	*/
131 	int max;		/* Maximum number of events	*/
132 	int cur;		/* Current number of events	*/
133 	int eventid;		/* For generation of unique event id	 */
134 	int flags;		/* History flags		*/
135 #define H_UNIQUE	1	/* Store only unique elements	*/
136 } history_t;
137 
138 private int history_def_next(ptr_t, HistEvent *);
139 private int history_def_first(ptr_t, HistEvent *);
140 private int history_def_prev(ptr_t, HistEvent *);
141 private int history_def_last(ptr_t, HistEvent *);
142 private int history_def_curr(ptr_t, HistEvent *);
143 private int history_def_set(ptr_t, HistEvent *, const int);
144 private void history_def_clear(ptr_t, HistEvent *);
145 private int history_def_enter(ptr_t, HistEvent *, const char *);
146 private int history_def_add(ptr_t, HistEvent *, const char *);
147 private int history_def_del(ptr_t, HistEvent *, const int);
148 
149 private int history_def_init(ptr_t *, HistEvent *, int);
150 private int history_def_insert(history_t *, HistEvent *, const char *);
151 private void history_def_delete(history_t *, HistEvent *, hentry_t *);
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 */
169 static 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  */
208 private int
209 history_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  */
228 private int
229 history_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  */
248 private int
249 history_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  */
273 private int
274 history_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  */
299 private int
300 history_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  */
320 private int
321 history_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_def_add():
344  *	Append string to element
345  */
346 private int
347 history_def_add(ptr_t p, HistEvent *ev, const char *str)
348 {
349 	history_t *h = (history_t *) p;
350 	size_t len;
351 	char *s;
352 	HistEventPrivate *evp = (void *)&h->cursor->ev;
353 
354 	if (h->cursor == &h->list)
355 		return (history_def_enter(p, ev, str));
356 	len = strlen(evp->str) + strlen(str) + 1;
357 	s = (char *) h_malloc(len);
358 	if (s == NULL) {
359 		he_seterrev(ev, _HE_MALLOC_FAILED);
360 		return (-1);
361 	}
362 	(void) strlcpy(s, h->cursor->ev.str, len);
363 	(void) strlcat(s, str, len);
364 	h_free((ptr_t)evp->str);
365 	evp->str = s;
366 	*ev = h->cursor->ev;
367 	return (0);
368 }
369 
370 
371 /* history_def_del():
372  *	Delete element hp of the h list
373  */
374 /* ARGSUSED */
375 private int
376 history_def_del(ptr_t p, HistEvent *ev __attribute__((__unused__)),
377     const int num)
378 {
379 	history_t *h = (history_t *) p;
380 	if (history_def_set(h, ev, num) != 0)
381 		return (-1);
382 	ev->str = strdup(h->cursor->ev.str);
383 	ev->num = h->cursor->ev.num;
384 	history_def_delete(h, ev, h->cursor);
385 	return (0);
386 }
387 
388 
389 /* history_def_delete():
390  *	Delete element hp of the h list
391  */
392 /* ARGSUSED */
393 private void
394 history_def_delete(history_t *h,
395 		   HistEvent *ev __attribute__((__unused__)), hentry_t *hp)
396 {
397 	HistEventPrivate *evp = (void *)&hp->ev;
398 	if (hp == &h->list)
399 		abort();
400 	if (h->cursor == hp)
401 		h->cursor = hp->prev;
402 	hp->prev->next = hp->next;
403 	hp->next->prev = hp->prev;
404 	h_free((ptr_t) evp->str);
405 	h_free(hp);
406 	h->cur--;
407 }
408 
409 
410 /* history_def_insert():
411  *	Insert element with string str in the h list
412  */
413 private int
414 history_def_insert(history_t *h, HistEvent *ev, const char *str)
415 {
416 
417 	h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
418 	if (h->cursor == NULL)
419 		goto oomem;
420 	if ((h->cursor->ev.str = h_strdup(str)) == NULL) {
421 		h_free((ptr_t)h->cursor);
422 		goto oomem;
423 	}
424 	h->cursor->ev.num = ++h->eventid;
425 	h->cursor->next = h->list.next;
426 	h->cursor->prev = &h->list;
427 	h->list.next->prev = h->cursor;
428 	h->list.next = h->cursor;
429 	h->cur++;
430 
431 	*ev = h->cursor->ev;
432 	return (0);
433 oomem:
434 	he_seterrev(ev, _HE_MALLOC_FAILED);
435 	return (-1);
436 }
437 
438 
439 /* history_def_enter():
440  *	Default function to enter an item in the history
441  */
442 private int
443 history_def_enter(ptr_t p, HistEvent *ev, const char *str)
444 {
445 	history_t *h = (history_t *) p;
446 
447 	if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
448 	    strcmp(h->list.next->ev.str, str) == 0)
449 	    return (0);
450 
451 	if (history_def_insert(h, ev, str) == -1)
452 		return (-1);	/* error, keep error message */
453 
454 	/*
455          * Always keep at least one entry.
456          * This way we don't have to check for the empty list.
457          */
458 	while (h->cur > h->max && h->cur > 0)
459 		history_def_delete(h, ev, h->list.prev);
460 
461 	return (1);
462 }
463 
464 
465 /* history_def_init():
466  *	Default history initialization function
467  */
468 /* ARGSUSED */
469 private int
470 history_def_init(ptr_t *p, HistEvent *ev __attribute__((__unused__)), int n)
471 {
472 	history_t *h = (history_t *) h_malloc(sizeof(history_t));
473 	if (h == NULL)
474 		return -1;
475 
476 	if (n <= 0)
477 		n = 0;
478 	h->eventid = 0;
479 	h->cur = 0;
480 	h->max = n;
481 	h->list.next = h->list.prev = &h->list;
482 	h->list.ev.str = NULL;
483 	h->list.ev.num = 0;
484 	h->cursor = &h->list;
485 	h->flags = 0;
486 	*p = (ptr_t) h;
487 	return 0;
488 }
489 
490 
491 /* history_def_clear():
492  *	Default history cleanup function
493  */
494 private void
495 history_def_clear(ptr_t p, HistEvent *ev)
496 {
497 	history_t *h = (history_t *) p;
498 
499 	while (h->list.prev != &h->list)
500 		history_def_delete(h, ev, h->list.prev);
501 	h->eventid = 0;
502 	h->cur = 0;
503 }
504 
505 
506 
507 
508 /************************************************************************/
509 
510 /* history_init():
511  *	Initialization function.
512  */
513 public History *
514 history_init(void)
515 {
516 	HistEvent ev;
517 	History *h = (History *) h_malloc(sizeof(History));
518 	if (h == NULL)
519 		return NULL;
520 
521 	if (history_def_init(&h->h_ref, &ev, 0) == -1) {
522 		h_free((ptr_t)h);
523 		return NULL;
524 	}
525 	h->h_ent = -1;
526 	h->h_next = history_def_next;
527 	h->h_first = history_def_first;
528 	h->h_last = history_def_last;
529 	h->h_prev = history_def_prev;
530 	h->h_curr = history_def_curr;
531 	h->h_set = history_def_set;
532 	h->h_clear = history_def_clear;
533 	h->h_enter = history_def_enter;
534 	h->h_add = history_def_add;
535 	h->h_del = history_def_del;
536 
537 	return (h);
538 }
539 
540 
541 /* history_end():
542  *	clean up history;
543  */
544 public void
545 history_end(History *h)
546 {
547 	HistEvent ev;
548 
549 	if (h->h_next == history_def_next)
550 		history_def_clear(h->h_ref, &ev);
551 	h_free(h->h_ref);
552 	h_free(h);
553 }
554 
555 
556 
557 /* history_setsize():
558  *	Set history number of events
559  */
560 private int
561 history_setsize(History *h, HistEvent *ev, int num)
562 {
563 
564 	if (h->h_next != history_def_next) {
565 		he_seterrev(ev, _HE_NOT_ALLOWED);
566 		return (-1);
567 	}
568 	if (num < 0) {
569 		he_seterrev(ev, _HE_BAD_PARAM);
570 		return (-1);
571 	}
572 	history_def_setsize(h->h_ref, num);
573 	return (0);
574 }
575 
576 
577 /* history_getsize():
578  *      Get number of events currently in history
579  */
580 private int
581 history_getsize(History *h, HistEvent *ev)
582 {
583 	if (h->h_next != history_def_next) {
584 		he_seterrev(ev, _HE_NOT_ALLOWED);
585 		return (-1);
586 	}
587 	ev->num = history_def_getsize(h->h_ref);
588 	if (ev->num < -1) {
589 		he_seterrev(ev, _HE_SIZE_NEGATIVE);
590 		return (-1);
591 	}
592 	return (0);
593 }
594 
595 
596 /* history_setunique():
597  *	Set if adjacent equal events should not be entered in history.
598  */
599 private int
600 history_setunique(History *h, HistEvent *ev, int uni)
601 {
602 
603 	if (h->h_next != history_def_next) {
604 		he_seterrev(ev, _HE_NOT_ALLOWED);
605 		return (-1);
606 	}
607 	history_def_setunique(h->h_ref, uni);
608 	return (0);
609 }
610 
611 
612 /* history_getunique():
613  *	Get if adjacent equal events should not be entered in history.
614  */
615 private int
616 history_getunique(History *h, HistEvent *ev)
617 {
618 	if (h->h_next != history_def_next) {
619 		he_seterrev(ev, _HE_NOT_ALLOWED);
620 		return (-1);
621 	}
622 	ev->num = history_def_getunique(h->h_ref);
623 	return (0);
624 }
625 
626 
627 /* history_set_fun():
628  *	Set history functions
629  */
630 private int
631 history_set_fun(History *h, History *nh)
632 {
633 	HistEvent ev;
634 
635 	if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
636 	    nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
637 	    nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
638 	    nh->h_del == NULL || nh->h_ref == NULL) {
639 		if (h->h_next != history_def_next) {
640 			history_def_init(&h->h_ref, &ev, 0);
641 			h->h_first = history_def_first;
642 			h->h_next = history_def_next;
643 			h->h_last = history_def_last;
644 			h->h_prev = history_def_prev;
645 			h->h_curr = history_def_curr;
646 			h->h_set = history_def_set;
647 			h->h_clear = history_def_clear;
648 			h->h_enter = history_def_enter;
649 			h->h_add = history_def_add;
650 			h->h_del = history_def_del;
651 		}
652 		return (-1);
653 	}
654 	if (h->h_next == history_def_next)
655 		history_def_clear(h->h_ref, &ev);
656 
657 	h->h_ent = -1;
658 	h->h_first = nh->h_first;
659 	h->h_next = nh->h_next;
660 	h->h_last = nh->h_last;
661 	h->h_prev = nh->h_prev;
662 	h->h_curr = nh->h_curr;
663 	h->h_set = nh->h_set;
664 	h->h_clear = nh->h_clear;
665 	h->h_enter = nh->h_enter;
666 	h->h_add = nh->h_add;
667 	h->h_del = nh->h_del;
668 
669 	return (0);
670 }
671 
672 
673 /* history_load():
674  *	History load function
675  */
676 private int
677 history_load(History *h, const char *fname)
678 {
679 	FILE *fp;
680 	char *line;
681 	size_t sz, max_size;
682 	char *ptr;
683 	int i = -1;
684 	HistEvent ev;
685 
686 	if ((fp = fopen(fname, "r")) == NULL)
687 		return (i);
688 
689 	if ((line = fgetln(fp, &sz)) == NULL)
690 		goto done;
691 
692 	if (strncmp(line, hist_cookie, sz) != 0)
693 		goto done;
694 
695 	ptr = h_malloc(max_size = 1024);
696 	if (ptr == NULL)
697 		goto done;
698 	for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
699 		char c = line[sz];
700 
701 		if (sz != 0 && line[sz - 1] == '\n')
702 			line[--sz] = '\0';
703 		else
704 			line[sz] = '\0';
705 
706 		if (max_size < sz) {
707 			char *nptr;
708 			max_size = (sz + 1024) & ~1023;
709 			nptr = h_realloc(ptr, max_size);
710 			if (nptr == NULL) {
711 				i = -1;
712 				goto oomem;
713 			}
714 			ptr = nptr;
715 		}
716 		(void) strunvis(ptr, line);
717 		line[sz] = c;
718 		if (HENTER(h, &ev, ptr) == -1) {
719 			h_free((ptr_t)ptr);
720 			return -1;
721 		}
722 	}
723 oomem:
724 	h_free((ptr_t)ptr);
725 done:
726 	(void) fclose(fp);
727 	return (i);
728 }
729 
730 
731 /* history_save():
732  *	History save function
733  */
734 private int
735 history_save(History *h, const char *fname)
736 {
737 	FILE *fp;
738 	HistEvent ev;
739 	int i = -1, retval;
740 	size_t len, max_size;
741 	char *ptr;
742 
743 	if ((fp = fopen(fname, "w")) == NULL)
744 		return (-1);
745 
746 	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
747 		goto done;
748 	if (fputs(hist_cookie, fp) == EOF)
749 		goto done;
750 	ptr = h_malloc(max_size = 1024);
751 	if (ptr == NULL)
752 		goto done;
753 	for (i = 0, retval = HLAST(h, &ev);
754 	    retval != -1;
755 	    retval = HPREV(h, &ev), i++) {
756 		len = strlen(ev.str) * 4;
757 		if (len >= max_size) {
758 			char *nptr;
759 			max_size = (len + 1024) & ~1023;
760 			nptr = h_realloc(ptr, max_size);
761 			if (nptr == NULL) {
762 				i = -1;
763 				goto oomem;
764 			}
765 			ptr = nptr;
766 		}
767 		(void) strvis(ptr, ev.str, VIS_WHITE);
768 		(void) fprintf(fp, "%s\n", ptr);
769 	}
770 oomem:
771 	h_free((ptr_t)ptr);
772 done:
773 	(void) fclose(fp);
774 	return (i);
775 }
776 
777 
778 /* history_prev_event():
779  *	Find the previous event, with number given
780  */
781 private int
782 history_prev_event(History *h, HistEvent *ev, int num)
783 {
784 	int retval;
785 
786 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
787 		if (ev->num == num)
788 			return (0);
789 
790 	he_seterrev(ev, _HE_NOT_FOUND);
791 	return (-1);
792 }
793 
794 
795 /* history_next_event():
796  *	Find the next event, with number given
797  */
798 private int
799 history_next_event(History *h, HistEvent *ev, int num)
800 {
801 	int retval;
802 
803 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
804 		if (ev->num == num)
805 			return (0);
806 
807 	he_seterrev(ev, _HE_NOT_FOUND);
808 	return (-1);
809 }
810 
811 
812 /* history_prev_string():
813  *	Find the previous event beginning with string
814  */
815 private int
816 history_prev_string(History *h, HistEvent *ev, const char *str)
817 {
818 	size_t len = strlen(str);
819 	int retval;
820 
821 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
822 		if (strncmp(str, ev->str, len) == 0)
823 			return (0);
824 
825 	he_seterrev(ev, _HE_NOT_FOUND);
826 	return (-1);
827 }
828 
829 
830 /* history_next_string():
831  *	Find the next event beginning with string
832  */
833 private int
834 history_next_string(History *h, HistEvent *ev, const char *str)
835 {
836 	size_t len = strlen(str);
837 	int retval;
838 
839 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
840 		if (strncmp(str, ev->str, len) == 0)
841 			return (0);
842 
843 	he_seterrev(ev, _HE_NOT_FOUND);
844 	return (-1);
845 }
846 
847 
848 /* history():
849  *	User interface to history functions.
850  */
851 int
852 history(History *h, HistEvent *ev, int fun, ...)
853 {
854 	va_list va;
855 	const char *str;
856 	int retval;
857 
858 	va_start(va, fun);
859 
860 	he_seterrev(ev, _HE_OK);
861 
862 	switch (fun) {
863 	case H_GETSIZE:
864 		retval = history_getsize(h, ev);
865 		break;
866 
867 	case H_SETSIZE:
868 		retval = history_setsize(h, ev, va_arg(va, int));
869 		break;
870 
871 	case H_GETUNIQUE:
872 		retval = history_getunique(h, ev);
873 		break;
874 
875 	case H_SETUNIQUE:
876 		retval = history_setunique(h, ev, va_arg(va, int));
877 		break;
878 
879 	case H_ADD:
880 		str = va_arg(va, const char *);
881 		retval = HADD(h, ev, str);
882 		break;
883 
884 	case H_DEL:
885 		retval = HDEL(h, ev, va_arg(va, const int));
886 		break;
887 
888 	case H_ENTER:
889 		str = va_arg(va, const char *);
890 		if ((retval = HENTER(h, ev, str)) != -1)
891 			h->h_ent = ev->num;
892 		break;
893 
894 	case H_APPEND:
895 		str = va_arg(va, const char *);
896 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
897 			retval = HADD(h, ev, str);
898 		break;
899 
900 	case H_FIRST:
901 		retval = HFIRST(h, ev);
902 		break;
903 
904 	case H_NEXT:
905 		retval = HNEXT(h, ev);
906 		break;
907 
908 	case H_LAST:
909 		retval = HLAST(h, ev);
910 		break;
911 
912 	case H_PREV:
913 		retval = HPREV(h, ev);
914 		break;
915 
916 	case H_CURR:
917 		retval = HCURR(h, ev);
918 		break;
919 
920 	case H_SET:
921 		retval = HSET(h, ev, va_arg(va, const int));
922 		break;
923 
924 	case H_CLEAR:
925 		HCLEAR(h, ev);
926 		retval = 0;
927 		break;
928 
929 	case H_LOAD:
930 		retval = history_load(h, va_arg(va, const char *));
931 		if (retval == -1)
932 			he_seterrev(ev, _HE_HIST_READ);
933 		break;
934 
935 	case H_SAVE:
936 		retval = history_save(h, va_arg(va, const char *));
937 		if (retval == -1)
938 			he_seterrev(ev, _HE_HIST_WRITE);
939 		break;
940 
941 	case H_PREV_EVENT:
942 		retval = history_prev_event(h, ev, va_arg(va, int));
943 		break;
944 
945 	case H_NEXT_EVENT:
946 		retval = history_next_event(h, ev, va_arg(va, int));
947 		break;
948 
949 	case H_PREV_STR:
950 		retval = history_prev_string(h, ev, va_arg(va, const char *));
951 		break;
952 
953 	case H_NEXT_STR:
954 		retval = history_next_string(h, ev, va_arg(va, const char *));
955 		break;
956 
957 	case H_FUNC:
958 	{
959 		History hf;
960 
961 		hf.h_ref = va_arg(va, ptr_t);
962 		h->h_ent = -1;
963 		hf.h_first = va_arg(va, history_gfun_t);
964 		hf.h_next = va_arg(va, history_gfun_t);
965 		hf.h_last = va_arg(va, history_gfun_t);
966 		hf.h_prev = va_arg(va, history_gfun_t);
967 		hf.h_curr = va_arg(va, history_gfun_t);
968 		hf.h_set = va_arg(va, history_sfun_t);
969 		hf.h_clear = va_arg(va, history_vfun_t);
970 		hf.h_enter = va_arg(va, history_efun_t);
971 		hf.h_add = va_arg(va, history_efun_t);
972 		hf.h_del = va_arg(va, history_sfun_t);
973 
974 		if ((retval = history_set_fun(h, &hf)) == -1)
975 			he_seterrev(ev, _HE_PARAM_MISSING);
976 		break;
977 	}
978 
979 	case H_END:
980 		history_end(h);
981 		retval = 0;
982 		break;
983 
984 	default:
985 		retval = -1;
986 		he_seterrev(ev, _HE_UNKNOWN);
987 		break;
988 	}
989 	va_end(va);
990 	return (retval);
991 }
992