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