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