xref: /openbsd-src/lib/libedit/history.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: history.c,v 1.13 2004/08/23 18:31:25 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.13 2004/08/23 18:31:25 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, *lbuf;
655 	size_t sz, max_size;
656 	char *ptr;
657 	int i = -1;
658 	HistEvent ev;
659 
660 	lbuf = NULL;
661 	if ((fp = fopen(fname, "r")) == NULL)
662 		return (i);
663 
664 	if ((line = fgetln(fp, &sz)) == NULL)
665 		goto done;
666 
667 	if (strncmp(line, hist_cookie, sz) != 0)
668 		goto done;
669 
670 	ptr = h_malloc(max_size = 1024);
671 	if (ptr == NULL)
672 		goto done;
673 	for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
674 		if (line[sz - 1] == '\n')
675 			line[sz - 1] = '\0';
676 		else {
677 			lbuf = malloc(sz + 1);
678 			if (lbuf == NULL) {
679 				i = -1;
680 				goto oomem;
681 			}
682 			memcpy(lbuf, line, sz);
683 			lbuf[sz++] = '\0';
684 			line = lbuf;
685 		}
686 		if (sz > max_size) {
687 			char *nptr;
688 			max_size = (sz + 1023) & ~1023;
689 			nptr = h_realloc(ptr, max_size);
690 			if (nptr == NULL) {
691 				i = -1;
692 				goto oomem;
693 			}
694 			ptr = nptr;
695 		}
696 		(void) strunvis(ptr, line);
697 		if (HENTER(h, &ev, ptr) == -1) {
698 			h_free((ptr_t)ptr);
699 			return -1;
700 		}
701 	}
702 oomem:
703 	h_free((ptr_t)ptr);
704 done:
705 	h_free(lbuf);
706 	(void) fclose(fp);
707 	return (i);
708 }
709 
710 
711 /* history_save():
712  *	History save function
713  */
714 private int
715 history_save(History *h, const char *fname)
716 {
717 	FILE *fp;
718 	HistEvent ev;
719 	int i = -1, retval;
720 	size_t len, max_size;
721 	char *ptr;
722 
723 	if ((fp = fopen(fname, "w")) == NULL)
724 		return (-1);
725 
726 	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
727 		goto done;
728 	if (fputs(hist_cookie, fp) == EOF)
729 		goto done;
730 	ptr = h_malloc(max_size = 1024);
731 	if (ptr == NULL)
732 		goto done;
733 	for (i = 0, retval = HLAST(h, &ev);
734 	    retval != -1;
735 	    retval = HPREV(h, &ev), i++) {
736 		len = strlen(ev.str) * 4 + 1;
737 		if (len > max_size) {
738 			char *nptr;
739 			max_size = (len + 1023) & ~1023;
740 			nptr = h_realloc(ptr, max_size);
741 			if (nptr == NULL) {
742 				i = -1;
743 				goto oomem;
744 			}
745 			ptr = nptr;
746 		}
747 		(void) strnvis(ptr, ev.str, max_size, VIS_WHITE);
748 		(void) fprintf(fp, "%s\n", ptr);
749 	}
750 oomem:
751 	h_free((ptr_t)ptr);
752 done:
753 	(void) fclose(fp);
754 	return (i);
755 }
756 
757 
758 /* history_prev_event():
759  *	Find the previous event, with number given
760  */
761 private int
762 history_prev_event(History *h, HistEvent *ev, int num)
763 {
764 	int retval;
765 
766 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
767 		if (ev->num == num)
768 			return (0);
769 
770 	he_seterrev(ev, _HE_NOT_FOUND);
771 	return (-1);
772 }
773 
774 
775 /* history_next_event():
776  *	Find the next event, with number given
777  */
778 private int
779 history_next_event(History *h, HistEvent *ev, int num)
780 {
781 	int retval;
782 
783 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
784 		if (ev->num == num)
785 			return (0);
786 
787 	he_seterrev(ev, _HE_NOT_FOUND);
788 	return (-1);
789 }
790 
791 
792 /* history_prev_string():
793  *	Find the previous event beginning with string
794  */
795 private int
796 history_prev_string(History *h, HistEvent *ev, const char *str)
797 {
798 	size_t len = strlen(str);
799 	int retval;
800 
801 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
802 		if (strncmp(str, ev->str, len) == 0)
803 			return (0);
804 
805 	he_seterrev(ev, _HE_NOT_FOUND);
806 	return (-1);
807 }
808 
809 
810 /* history_next_string():
811  *	Find the next event beginning with string
812  */
813 private int
814 history_next_string(History *h, HistEvent *ev, const char *str)
815 {
816 	size_t len = strlen(str);
817 	int retval;
818 
819 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
820 		if (strncmp(str, ev->str, len) == 0)
821 			return (0);
822 
823 	he_seterrev(ev, _HE_NOT_FOUND);
824 	return (-1);
825 }
826 
827 
828 /* history():
829  *	User interface to history functions.
830  */
831 int
832 history(History *h, HistEvent *ev, int fun, ...)
833 {
834 	va_list va;
835 	const char *str;
836 	int retval;
837 
838 	va_start(va, fun);
839 
840 	he_seterrev(ev, _HE_OK);
841 
842 	switch (fun) {
843 	case H_GETSIZE:
844 		retval = history_getsize(h, ev);
845 		break;
846 
847 	case H_SETSIZE:
848 		retval = history_setsize(h, ev, va_arg(va, int));
849 		break;
850 
851 	case H_GETUNIQUE:
852 		retval = history_getunique(h, ev);
853 		break;
854 
855 	case H_SETUNIQUE:
856 		retval = history_setunique(h, ev, va_arg(va, int));
857 		break;
858 
859 	case H_ADD:
860 		str = va_arg(va, const char *);
861 		retval = HADD(h, ev, str);
862 		break;
863 
864 	case H_ENTER:
865 		str = va_arg(va, const char *);
866 		if ((retval = HENTER(h, ev, str)) != -1)
867 			h->h_ent = ev->num;
868 		break;
869 
870 	case H_APPEND:
871 		str = va_arg(va, const char *);
872 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
873 			retval = HADD(h, ev, str);
874 		break;
875 
876 	case H_FIRST:
877 		retval = HFIRST(h, ev);
878 		break;
879 
880 	case H_NEXT:
881 		retval = HNEXT(h, ev);
882 		break;
883 
884 	case H_LAST:
885 		retval = HLAST(h, ev);
886 		break;
887 
888 	case H_PREV:
889 		retval = HPREV(h, ev);
890 		break;
891 
892 	case H_CURR:
893 		retval = HCURR(h, ev);
894 		break;
895 
896 	case H_SET:
897 		retval = HSET(h, ev, va_arg(va, const int));
898 		break;
899 
900 	case H_CLEAR:
901 		HCLEAR(h, ev);
902 		retval = 0;
903 		break;
904 
905 	case H_LOAD:
906 		retval = history_load(h, va_arg(va, const char *));
907 		if (retval == -1)
908 			he_seterrev(ev, _HE_HIST_READ);
909 		break;
910 
911 	case H_SAVE:
912 		retval = history_save(h, va_arg(va, const char *));
913 		if (retval == -1)
914 			he_seterrev(ev, _HE_HIST_WRITE);
915 		break;
916 
917 	case H_PREV_EVENT:
918 		retval = history_prev_event(h, ev, va_arg(va, int));
919 		break;
920 
921 	case H_NEXT_EVENT:
922 		retval = history_next_event(h, ev, va_arg(va, int));
923 		break;
924 
925 	case H_PREV_STR:
926 		retval = history_prev_string(h, ev, va_arg(va, const char *));
927 		break;
928 
929 	case H_NEXT_STR:
930 		retval = history_next_string(h, ev, va_arg(va, const char *));
931 		break;
932 
933 	case H_FUNC:
934 	{
935 		History hf;
936 
937 		hf.h_ref = va_arg(va, ptr_t);
938 		h->h_ent = -1;
939 		hf.h_first = va_arg(va, history_gfun_t);
940 		hf.h_next = va_arg(va, history_gfun_t);
941 		hf.h_last = va_arg(va, history_gfun_t);
942 		hf.h_prev = va_arg(va, history_gfun_t);
943 		hf.h_curr = va_arg(va, history_gfun_t);
944 		hf.h_set = va_arg(va, history_sfun_t);
945 		hf.h_clear = va_arg(va, history_vfun_t);
946 		hf.h_enter = va_arg(va, history_efun_t);
947 		hf.h_add = va_arg(va, history_efun_t);
948 
949 		if ((retval = history_set_fun(h, &hf)) == -1)
950 			he_seterrev(ev, _HE_PARAM_MISSING);
951 		break;
952 	}
953 
954 	case H_END:
955 		history_end(h);
956 		retval = 0;
957 		break;
958 
959 	default:
960 		retval = -1;
961 		he_seterrev(ev, _HE_UNKNOWN);
962 		break;
963 	}
964 	va_end(va);
965 	return (retval);
966 }
967