xref: /netbsd-src/lib/libedit/history.c (revision 8e6ab8837d8d6b9198e67c1c445300b483e2f304)
1 /*	$NetBSD: history.c,v 1.23 2003/06/19 15:55: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. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include "config.h"
40 #if !defined(lint) && !defined(SCCSID)
41 #if 0
42 static char sccsid[] = "@(#)history.c	8.1 (Berkeley) 6/4/93";
43 #else
44 __RCSID("$NetBSD: history.c,v 1.23 2003/06/19 15:55:06 christos Exp $");
45 #endif
46 #endif /* not lint && not SCCSID */
47 
48 /*
49  * hist.c: History access functions
50  */
51 #include <string.h>
52 #include <stdlib.h>
53 #include <stdarg.h>
54 #ifdef HAVE_VIS_H
55 #include <vis.h>
56 #else
57 #include "np/vis.h"
58 #endif
59 #include <sys/stat.h>
60 
61 static const char hist_cookie[] = "_HiStOrY_V2_\n";
62 
63 #include "histedit.h"
64 
65 typedef int (*history_gfun_t)(ptr_t, HistEvent *);
66 typedef int (*history_efun_t)(ptr_t, HistEvent *, const char *);
67 typedef void (*history_vfun_t)(ptr_t, HistEvent *);
68 typedef int (*history_sfun_t)(ptr_t, HistEvent *, const int);
69 
70 struct history {
71 	ptr_t h_ref;		/* Argument for history fcns	 */
72 	int h_ent;		/* Last entry point for history	 */
73 	history_gfun_t h_first;	/* Get the first element	 */
74 	history_gfun_t h_next;	/* Get the next element		 */
75 	history_gfun_t h_last;	/* Get the last element		 */
76 	history_gfun_t h_prev;	/* Get the previous element	 */
77 	history_gfun_t h_curr;	/* Get the current element	 */
78 	history_sfun_t h_set;	/* Set the current element	 */
79 	history_vfun_t h_clear;	/* Clear the history list	 */
80 	history_efun_t h_enter;	/* Add an element		 */
81 	history_efun_t h_add;	/* Append to an element		 */
82 };
83 
84 #define	HNEXT(h, ev)		(*(h)->h_next)((h)->h_ref, ev)
85 #define	HFIRST(h, ev)		(*(h)->h_first)((h)->h_ref, ev)
86 #define	HPREV(h, ev)		(*(h)->h_prev)((h)->h_ref, ev)
87 #define	HLAST(h, ev)		(*(h)->h_last)((h)->h_ref, ev)
88 #define	HCURR(h, ev)		(*(h)->h_curr)((h)->h_ref, ev)
89 #define	HSET(h, ev, n)		(*(h)->h_set)((h)->h_ref, ev, n)
90 #define	HCLEAR(h, ev)		(*(h)->h_clear)((h)->h_ref, ev)
91 #define	HENTER(h, ev, str)	(*(h)->h_enter)((h)->h_ref, ev, str)
92 #define	HADD(h, ev, str)	(*(h)->h_add)((h)->h_ref, ev, str)
93 
94 #define	h_malloc(a)	malloc(a)
95 #define	h_realloc(a, b)	realloc((a), (b))
96 #define	h_free(a)	free(a)
97 
98 typedef struct {
99     int		num;
100     char	*str;
101 } HistEventPrivate;
102 
103 
104 
105 private int history_setsize(History *, HistEvent *, int);
106 private int history_getsize(History *, HistEvent *);
107 private int history_setunique(History *, HistEvent *, int);
108 private int history_getunique(History *, HistEvent *);
109 private int history_set_fun(History *, History *);
110 private int history_load(History *, const char *);
111 private int history_save(History *, const char *);
112 private int history_prev_event(History *, HistEvent *, int);
113 private int history_next_event(History *, HistEvent *, int);
114 private int history_next_string(History *, HistEvent *, const char *);
115 private int history_prev_string(History *, HistEvent *, const char *);
116 
117 
118 /***********************************************************************/
119 
120 /*
121  * Builtin- history implementation
122  */
123 typedef struct hentry_t {
124 	HistEvent ev;		/* What we return		 */
125 	struct hentry_t *next;	/* Next entry			 */
126 	struct hentry_t *prev;	/* Previous entry		 */
127 } hentry_t;
128 
129 typedef struct history_t {
130 	hentry_t list;		/* Fake list header element	*/
131 	hentry_t *cursor;	/* Current element in the list	*/
132 	int max;		/* Maximum number of events	*/
133 	int cur;		/* Current number of events	*/
134 	int eventid;		/* For generation of unique event id	 */
135 	int flags;		/* History flags		*/
136 #define H_UNIQUE	1	/* Store only unique elements	*/
137 } history_t;
138 
139 private int history_def_first(ptr_t, HistEvent *);
140 private int history_def_last(ptr_t, HistEvent *);
141 private int history_def_next(ptr_t, HistEvent *);
142 private int history_def_prev(ptr_t, HistEvent *);
143 private int history_def_curr(ptr_t, HistEvent *);
144 private int history_def_set(ptr_t, HistEvent *, const int n);
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_init(ptr_t *, HistEvent *, int);
148 private void history_def_clear(ptr_t, HistEvent *);
149 private int history_def_insert(history_t *, HistEvent *, const char *);
150 private void history_def_delete(history_t *, HistEvent *, hentry_t *);
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 */
168 static 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  */
207 private int
208 history_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  */
227 private int
228 history_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  */
247 private int
248 history_def_next(ptr_t p, HistEvent *ev)
249 {
250 	history_t *h = (history_t *) p;
251 
252 	if (h->cursor != &h->list)
253 		h->cursor = h->cursor->next;
254 	else {
255 		he_seterrev(ev, _HE_EMPTY_LIST);
256 		return (-1);
257 	}
258 
259 	if (h->cursor != &h->list)
260 		*ev = h->cursor->ev;
261 	else {
262 		he_seterrev(ev, _HE_END_REACHED);
263 		return (-1);
264 	}
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 		h->cursor = h->cursor->prev;
280 	else {
281 		he_seterrev(ev,
282 		    (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
283 		return (-1);
284 	}
285 
286 	if (h->cursor != &h->list)
287 		*ev = h->cursor->ev;
288 	else {
289 		he_seterrev(ev, _HE_START_REACHED);
290 		return (-1);
291 	}
292 
293 	return (0);
294 }
295 
296 
297 /* history_def_curr():
298  *	Default function to return the current event in the history.
299  */
300 private int
301 history_def_curr(ptr_t p, HistEvent *ev)
302 {
303 	history_t *h = (history_t *) p;
304 
305 	if (h->cursor != &h->list)
306 		*ev = h->cursor->ev;
307 	else {
308 		he_seterrev(ev,
309 		    (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
310 		return (-1);
311 	}
312 
313 	return (0);
314 }
315 
316 
317 /* history_def_set():
318  *	Default function to set the current event in the history to the
319  *	given one.
320  */
321 private int
322 history_def_set(ptr_t p, HistEvent *ev, const int n)
323 {
324 	history_t *h = (history_t *) p;
325 
326 	if (h->cur == 0) {
327 		he_seterrev(ev, _HE_EMPTY_LIST);
328 		return (-1);
329 	}
330 	if (h->cursor == &h->list || h->cursor->ev.num != n) {
331 		for (h->cursor = h->list.next; h->cursor != &h->list;
332 		    h->cursor = h->cursor->next)
333 			if (h->cursor->ev.num == n)
334 				break;
335 	}
336 	if (h->cursor == &h->list) {
337 		he_seterrev(ev, _HE_NOT_FOUND);
338 		return (-1);
339 	}
340 	return (0);
341 }
342 
343 
344 /* history_def_add():
345  *	Append string to element
346  */
347 private int
348 history_def_add(ptr_t p, HistEvent *ev, const char *str)
349 {
350 	history_t *h = (history_t *) p;
351 	size_t len;
352 	char *s;
353 	HistEventPrivate *evp = (void *)&h->cursor->ev;
354 
355 	if (h->cursor == &h->list)
356 		return (history_def_enter(p, ev, str));
357 	len = strlen(evp->str) + strlen(str) + 1;
358 	s = (char *) h_malloc(len);
359 	if (s == NULL) {
360 		he_seterrev(ev, _HE_MALLOC_FAILED);
361 		return (-1);
362 	}
363 	(void) strlcpy(s, h->cursor->ev.str, len);
364 	(void) strlcat(s, str, len);
365 	h_free((ptr_t)evp->str);
366 	evp->str = s;
367 	*ev = h->cursor->ev;
368 	return (0);
369 }
370 
371 
372 /* history_def_delete():
373  *	Delete element hp of the h list
374  */
375 /* ARGSUSED */
376 private void
377 history_def_delete(history_t *h,
378 		   HistEvent *ev __attribute__((__unused__)), hentry_t *hp)
379 {
380 	HistEventPrivate *evp = (void *)&hp->ev;
381 	if (hp == &h->list)
382 		abort();
383 	hp->prev->next = hp->next;
384 	hp->next->prev = hp->prev;
385 	h_free((ptr_t) evp->str);
386 	h_free(hp);
387 	h->cur--;
388 }
389 
390 
391 /* history_def_insert():
392  *	Insert element with string str in the h list
393  */
394 private int
395 history_def_insert(history_t *h, HistEvent *ev, const char *str)
396 {
397 
398 	h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
399 	if (h->cursor == NULL)
400 		goto oomem;
401 	if ((h->cursor->ev.str = strdup(str)) == NULL) {
402 		h_free((ptr_t)h->cursor);
403 		goto oomem;
404 	}
405 	h->cursor->ev.num = ++h->eventid;
406 	h->cursor->next = h->list.next;
407 	h->cursor->prev = &h->list;
408 	h->list.next->prev = h->cursor;
409 	h->list.next = h->cursor;
410 	h->cur++;
411 
412 	*ev = h->cursor->ev;
413 	return (0);
414 oomem:
415 	he_seterrev(ev, _HE_MALLOC_FAILED);
416 	return (-1);
417 }
418 
419 
420 /* history_def_enter():
421  *	Default function to enter an item in the history
422  */
423 private int
424 history_def_enter(ptr_t p, HistEvent *ev, const char *str)
425 {
426 	history_t *h = (history_t *) p;
427 
428 	if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
429 	    strcmp(h->list.next->ev.str, str) == 0)
430 	    return (0);
431 
432 	if (history_def_insert(h, ev, str) == -1)
433 		return (-1);	/* error, keep error message */
434 
435 	/*
436          * Always keep at least one entry.
437          * This way we don't have to check for the empty list.
438          */
439 	while (h->cur > h->max && h->cur > 0)
440 		history_def_delete(h, ev, h->list.prev);
441 
442 	return (1);
443 }
444 
445 
446 /* history_def_init():
447  *	Default history initialization function
448  */
449 /* ARGSUSED */
450 private int
451 history_def_init(ptr_t *p, HistEvent *ev __attribute__((__unused__)), int n)
452 {
453 	history_t *h = (history_t *) h_malloc(sizeof(history_t));
454 	if (h == NULL)
455 		return -1;
456 
457 	if (n <= 0)
458 		n = 0;
459 	h->eventid = 0;
460 	h->cur = 0;
461 	h->max = n;
462 	h->list.next = h->list.prev = &h->list;
463 	h->list.ev.str = NULL;
464 	h->list.ev.num = 0;
465 	h->cursor = &h->list;
466 	h->flags = 0;
467 	*p = (ptr_t) h;
468 	return 0;
469 }
470 
471 
472 /* history_def_clear():
473  *	Default history cleanup function
474  */
475 private void
476 history_def_clear(ptr_t p, HistEvent *ev)
477 {
478 	history_t *h = (history_t *) p;
479 
480 	while (h->list.prev != &h->list)
481 		history_def_delete(h, ev, h->list.prev);
482 	h->eventid = 0;
483 	h->cur = 0;
484 }
485 
486 
487 
488 
489 /************************************************************************/
490 
491 /* history_init():
492  *	Initialization function.
493  */
494 public History *
495 history_init(void)
496 {
497 	HistEvent ev;
498 	History *h = (History *) h_malloc(sizeof(History));
499 	if (h == NULL)
500 		return NULL;
501 
502 	if (history_def_init(&h->h_ref, &ev, 0) == -1) {
503 		h_free((ptr_t)h);
504 		return NULL;
505 	}
506 	h->h_ent = -1;
507 	h->h_next = history_def_next;
508 	h->h_first = history_def_first;
509 	h->h_last = history_def_last;
510 	h->h_prev = history_def_prev;
511 	h->h_curr = history_def_curr;
512 	h->h_set = history_def_set;
513 	h->h_clear = history_def_clear;
514 	h->h_enter = history_def_enter;
515 	h->h_add = history_def_add;
516 
517 	return (h);
518 }
519 
520 
521 /* history_end():
522  *	clean up history;
523  */
524 public void
525 history_end(History *h)
526 {
527 	HistEvent ev;
528 
529 	if (h->h_next == history_def_next)
530 		history_def_clear(h->h_ref, &ev);
531 }
532 
533 
534 
535 /* history_setsize():
536  *	Set history number of events
537  */
538 private int
539 history_setsize(History *h, HistEvent *ev, int num)
540 {
541 
542 	if (h->h_next != history_def_next) {
543 		he_seterrev(ev, _HE_NOT_ALLOWED);
544 		return (-1);
545 	}
546 	if (num < 0) {
547 		he_seterrev(ev, _HE_BAD_PARAM);
548 		return (-1);
549 	}
550 	history_def_setsize(h->h_ref, num);
551 	return (0);
552 }
553 
554 
555 /* history_getsize():
556  *      Get number of events currently in history
557  */
558 private int
559 history_getsize(History *h, HistEvent *ev)
560 {
561 	if (h->h_next != history_def_next) {
562 		he_seterrev(ev, _HE_NOT_ALLOWED);
563 		return (-1);
564 	}
565 	ev->num = history_def_getsize(h->h_ref);
566 	if (ev->num < -1) {
567 		he_seterrev(ev, _HE_SIZE_NEGATIVE);
568 		return (-1);
569 	}
570 	return (0);
571 }
572 
573 
574 /* history_setunique():
575  *	Set if adjacent equal events should not be entered in history.
576  */
577 private int
578 history_setunique(History *h, HistEvent *ev, int uni)
579 {
580 
581 	if (h->h_next != history_def_next) {
582 		he_seterrev(ev, _HE_NOT_ALLOWED);
583 		return (-1);
584 	}
585 	history_def_setunique(h->h_ref, uni);
586 	return (0);
587 }
588 
589 
590 /* history_getunique():
591  *	Get if adjacent equal events should not be entered in history.
592  */
593 private int
594 history_getunique(History *h, HistEvent *ev)
595 {
596 	if (h->h_next != history_def_next) {
597 		he_seterrev(ev, _HE_NOT_ALLOWED);
598 		return (-1);
599 	}
600 	ev->num = history_def_getunique(h->h_ref);
601 	return (0);
602 }
603 
604 
605 /* history_set_fun():
606  *	Set history functions
607  */
608 private int
609 history_set_fun(History *h, History *nh)
610 {
611 	HistEvent ev;
612 
613 	if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
614 	    nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
615 	    nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
616 	    nh->h_ref == NULL) {
617 		if (h->h_next != history_def_next) {
618 			history_def_init(&h->h_ref, &ev, 0);
619 			h->h_first = history_def_first;
620 			h->h_next = history_def_next;
621 			h->h_last = history_def_last;
622 			h->h_prev = history_def_prev;
623 			h->h_curr = history_def_curr;
624 			h->h_set = history_def_set;
625 			h->h_clear = history_def_clear;
626 			h->h_enter = history_def_enter;
627 			h->h_add = history_def_add;
628 		}
629 		return (-1);
630 	}
631 	if (h->h_next == history_def_next)
632 		history_def_clear(h->h_ref, &ev);
633 
634 	h->h_ent = -1;
635 	h->h_first = nh->h_first;
636 	h->h_next = nh->h_next;
637 	h->h_last = nh->h_last;
638 	h->h_prev = nh->h_prev;
639 	h->h_curr = nh->h_curr;
640 	h->h_set = nh->h_set;
641 	h->h_clear = nh->h_clear;
642 	h->h_enter = nh->h_enter;
643 	h->h_add = nh->h_add;
644 
645 	return (0);
646 }
647 
648 
649 /* history_load():
650  *	History load function
651  */
652 private int
653 history_load(History *h, const char *fname)
654 {
655 	FILE *fp;
656 	char *line;
657 	size_t sz, max_size;
658 	char *ptr;
659 	int i = -1;
660 	HistEvent ev;
661 
662 	if ((fp = fopen(fname, "r")) == NULL)
663 		return (i);
664 
665 	if ((line = fgetln(fp, &sz)) == NULL)
666 		goto done;
667 
668 	if (strncmp(line, hist_cookie, sz) != 0)
669 		goto done;
670 
671 	ptr = h_malloc(max_size = 1024);
672 	if (ptr == NULL)
673 		goto done;
674 	for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
675 		char c = line[sz];
676 
677 		if (sz != 0 && line[sz - 1] == '\n')
678 			line[--sz] = '\0';
679 		else
680 			line[sz] = '\0';
681 
682 		if (max_size < sz) {
683 			char *nptr;
684 			max_size = (sz + 1023) & ~1023;
685 			nptr = h_realloc(ptr, max_size);
686 			if (nptr == NULL) {
687 				i = -1;
688 				goto oomem;
689 			}
690 			ptr = nptr;
691 		}
692 		(void) strunvis(ptr, line);
693 		line[sz] = c;
694 		if (HENTER(h, &ev, ptr) == -1) {
695 			h_free((ptr_t)ptr);
696 			return -1;
697 		}
698 	}
699 oomem:
700 	h_free((ptr_t)ptr);
701 done:
702 	(void) fclose(fp);
703 	return (i);
704 }
705 
706 
707 /* history_save():
708  *	History save function
709  */
710 private int
711 history_save(History *h, const char *fname)
712 {
713 	FILE *fp;
714 	HistEvent ev;
715 	int i = -1, retval;
716 	size_t len, max_size;
717 	char *ptr;
718 
719 	if ((fp = fopen(fname, "w")) == NULL)
720 		return (-1);
721 
722 	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
723 		goto done;
724 	if (fputs(hist_cookie, fp) == EOF)
725 		goto done;
726 	ptr = h_malloc(max_size = 1024);
727 	if (ptr == NULL)
728 		goto done;
729 	for (i = 0, retval = HLAST(h, &ev);
730 	    retval != -1;
731 	    retval = HPREV(h, &ev), i++) {
732 		len = strlen(ev.str) * 4;
733 		if (len >= max_size) {
734 			char *nptr;
735 			max_size = (len + 1023) & 1023;
736 			nptr = h_realloc(ptr, max_size);
737 			if (nptr == NULL) {
738 				i = -1;
739 				goto oomem;
740 			}
741 			ptr = nptr;
742 		}
743 		(void) strvis(ptr, ev.str, VIS_WHITE);
744 		(void) fprintf(fp, "%s\n", ptr);
745 	}
746 oomem:
747 	h_free((ptr_t)ptr);
748 done:
749 	(void) fclose(fp);
750 	return (i);
751 }
752 
753 
754 /* history_prev_event():
755  *	Find the previous event, with number given
756  */
757 private int
758 history_prev_event(History *h, HistEvent *ev, int num)
759 {
760 	int retval;
761 
762 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
763 		if (ev->num == num)
764 			return (0);
765 
766 	he_seterrev(ev, _HE_NOT_FOUND);
767 	return (-1);
768 }
769 
770 
771 /* history_next_event():
772  *	Find the next event, with number given
773  */
774 private int
775 history_next_event(History *h, HistEvent *ev, int num)
776 {
777 	int retval;
778 
779 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
780 		if (ev->num == num)
781 			return (0);
782 
783 	he_seterrev(ev, _HE_NOT_FOUND);
784 	return (-1);
785 }
786 
787 
788 /* history_prev_string():
789  *	Find the previous event beginning with string
790  */
791 private int
792 history_prev_string(History *h, HistEvent *ev, const char *str)
793 {
794 	size_t len = strlen(str);
795 	int retval;
796 
797 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
798 		if (strncmp(str, ev->str, len) == 0)
799 			return (0);
800 
801 	he_seterrev(ev, _HE_NOT_FOUND);
802 	return (-1);
803 }
804 
805 
806 /* history_next_string():
807  *	Find the next event beginning with string
808  */
809 private int
810 history_next_string(History *h, HistEvent *ev, const char *str)
811 {
812 	size_t len = strlen(str);
813 	int retval;
814 
815 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
816 		if (strncmp(str, ev->str, len) == 0)
817 			return (0);
818 
819 	he_seterrev(ev, _HE_NOT_FOUND);
820 	return (-1);
821 }
822 
823 
824 /* history():
825  *	User interface to history functions.
826  */
827 int
828 history(History *h, HistEvent *ev, int fun, ...)
829 {
830 	va_list va;
831 	const char *str;
832 	int retval;
833 
834 	va_start(va, fun);
835 
836 	he_seterrev(ev, _HE_OK);
837 
838 	switch (fun) {
839 	case H_GETSIZE:
840 		retval = history_getsize(h, ev);
841 		break;
842 
843 	case H_SETSIZE:
844 		retval = history_setsize(h, ev, va_arg(va, int));
845 		break;
846 
847 	case H_GETUNIQUE:
848 		retval = history_getunique(h, ev);
849 		break;
850 
851 	case H_SETUNIQUE:
852 		retval = history_setunique(h, ev, va_arg(va, int));
853 		break;
854 
855 	case H_ADD:
856 		str = va_arg(va, const char *);
857 		retval = HADD(h, ev, str);
858 		break;
859 
860 	case H_ENTER:
861 		str = va_arg(va, const char *);
862 		if ((retval = HENTER(h, ev, str)) != -1)
863 			h->h_ent = ev->num;
864 		break;
865 
866 	case H_APPEND:
867 		str = va_arg(va, const char *);
868 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
869 			retval = HADD(h, ev, str);
870 		break;
871 
872 	case H_FIRST:
873 		retval = HFIRST(h, ev);
874 		break;
875 
876 	case H_NEXT:
877 		retval = HNEXT(h, ev);
878 		break;
879 
880 	case H_LAST:
881 		retval = HLAST(h, ev);
882 		break;
883 
884 	case H_PREV:
885 		retval = HPREV(h, ev);
886 		break;
887 
888 	case H_CURR:
889 		retval = HCURR(h, ev);
890 		break;
891 
892 	case H_SET:
893 		retval = HSET(h, ev, va_arg(va, const int));
894 		break;
895 
896 	case H_CLEAR:
897 		HCLEAR(h, ev);
898 		retval = 0;
899 		break;
900 
901 	case H_LOAD:
902 		retval = history_load(h, va_arg(va, const char *));
903 		if (retval == -1)
904 			he_seterrev(ev, _HE_HIST_READ);
905 		break;
906 
907 	case H_SAVE:
908 		retval = history_save(h, va_arg(va, const char *));
909 		if (retval == -1)
910 			he_seterrev(ev, _HE_HIST_WRITE);
911 		break;
912 
913 	case H_PREV_EVENT:
914 		retval = history_prev_event(h, ev, va_arg(va, int));
915 		break;
916 
917 	case H_NEXT_EVENT:
918 		retval = history_next_event(h, ev, va_arg(va, int));
919 		break;
920 
921 	case H_PREV_STR:
922 		retval = history_prev_string(h, ev, va_arg(va, const char *));
923 		break;
924 
925 	case H_NEXT_STR:
926 		retval = history_next_string(h, ev, va_arg(va, const char *));
927 		break;
928 
929 	case H_FUNC:
930 	{
931 		History hf;
932 
933 		hf.h_ref = va_arg(va, ptr_t);
934 		h->h_ent = -1;
935 		hf.h_first = va_arg(va, history_gfun_t);
936 		hf.h_next = va_arg(va, history_gfun_t);
937 		hf.h_last = va_arg(va, history_gfun_t);
938 		hf.h_prev = va_arg(va, history_gfun_t);
939 		hf.h_curr = va_arg(va, history_gfun_t);
940 		hf.h_set = va_arg(va, history_sfun_t);
941 		hf.h_clear = va_arg(va, history_vfun_t);
942 		hf.h_enter = va_arg(va, history_efun_t);
943 		hf.h_add = va_arg(va, history_efun_t);
944 
945 		if ((retval = history_set_fun(h, &hf)) == -1)
946 			he_seterrev(ev, _HE_PARAM_MISSING);
947 		break;
948 	}
949 
950 	case H_END:
951 		history_end(h);
952 		retval = 0;
953 		break;
954 
955 	default:
956 		retval = -1;
957 		he_seterrev(ev, _HE_UNKNOWN);
958 		break;
959 	}
960 	va_end(va);
961 	return (retval);
962 }
963