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