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