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