xref: /openbsd-src/lib/libedit/history.c (revision a4afd6dad3fba28f80e70208181c06c482259988)
1 /*-
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Christos Zoulas of Cornell University.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #if !defined(lint) && !defined(SCCSID)
38 static char sccsid[] = "@(#)history.c	8.1 (Berkeley) 6/4/93";
39 #endif /* not lint && not SCCSID */
40 
41 /*
42  * hist.c: History access functions
43  */
44 #include "sys.h"
45 
46 #include <string.h>
47 #include <stdlib.h>
48 #if __STDC__
49 #include <stdarg.h>
50 #else
51 #include <varargs.h>
52 #endif
53 
54 static const char hist_cookie[] = "_HiStOrY_V1_\n";
55 
56 #include "histedit.h"
57 
58 typedef const HistEvent *	(*history_gfun_t) __P((ptr_t));
59 typedef const HistEvent *	(*history_efun_t) __P((ptr_t, const char *));
60 typedef void 			(*history_vfun_t) __P((ptr_t));
61 
62 struct history {
63     ptr_t	   h_ref;		/* Argument for history fcns	*/
64     history_gfun_t h_first;		/* Get the first element	*/
65     history_gfun_t h_next;		/* Get the next element		*/
66     history_gfun_t h_last;		/* Get the last element		*/
67     history_gfun_t h_prev;		/* Get the previous element	*/
68     history_gfun_t h_curr;		/* Get the current element	*/
69     history_vfun_t h_clear;		/* Clear the history list	*/
70     history_efun_t h_enter;		/* Add an element		*/
71     history_efun_t h_add;		/* Append to an element		*/
72 };
73 
74 #define	HNEXT(h)  	(*(h)->h_next)((h)->h_ref)
75 #define	HFIRST(h) 	(*(h)->h_first)((h)->h_ref)
76 #define	HPREV(h)  	(*(h)->h_prev)((h)->h_ref)
77 #define	HLAST(h) 	(*(h)->h_last)((h)->h_ref)
78 #define	HCURR(h) 	(*(h)->h_curr)((h)->h_ref)
79 #define	HCLEAR(h) 	(*(h)->h_clear)((h)->h_ref)
80 #define	HENTER(h, str)	(*(h)->h_enter)((h)->h_ref, str)
81 #define	HADD(h, str)	(*(h)->h_add)((h)->h_ref, str)
82 
83 #define h_malloc(a)	malloc(a)
84 #define h_free(a)	free(a)
85 
86 
87 private int		 history_set_num	__P((History *, int));
88 private int		 history_set_fun	__P((History *, History *));
89 private int 		 history_load		__P((History *, const char *));
90 private int 		 history_save		__P((History *, const char *));
91 private const HistEvent *history_prev_event	__P((History *, int));
92 private const HistEvent *history_next_event	__P((History *, int));
93 private const HistEvent *history_next_string	__P((History *, const char *));
94 private const HistEvent *history_prev_string	__P((History *, const char *));
95 
96 
97 /***********************************************************************/
98 
99 /*
100  * Builtin- history implementation
101  */
102 typedef struct hentry_t {
103     HistEvent ev;		/* What we return		*/
104     struct hentry_t *next;	/* Next entry			*/
105     struct hentry_t *prev;	/* Previous entry		*/
106 } hentry_t;
107 
108 typedef struct history_t {
109     hentry_t  list;		/* Fake list header element	*/
110     hentry_t *cursor;		/* Current element in the list	*/
111     int	max;			/* Maximum number of events	*/
112     int cur;			/* Current number of events	*/
113     int	eventno;		/* Current event number		*/
114 } history_t;
115 
116 private const HistEvent *history_def_first  __P((ptr_t));
117 private const HistEvent *history_def_last   __P((ptr_t));
118 private const HistEvent *history_def_next   __P((ptr_t));
119 private const HistEvent *history_def_prev   __P((ptr_t));
120 private const HistEvent *history_def_curr   __P((ptr_t));
121 private const HistEvent *history_def_enter  __P((ptr_t, const char *));
122 private const HistEvent *history_def_add    __P((ptr_t, const char *));
123 private void             history_def_init   __P((ptr_t *, int));
124 private void             history_def_clear  __P((ptr_t));
125 private const HistEvent *history_def_insert __P((history_t *, const char *));
126 private void             history_def_delete __P((history_t *, hentry_t *));
127 
128 #define history_def_set(p, num)	(void) (((history_t *) p)->max = (num))
129 
130 
131 /* history_def_first():
132  *	Default function to return the first event in the history.
133  */
134 private const HistEvent *
135 history_def_first(p)
136     ptr_t p;
137 {
138     history_t *h = (history_t *) p;
139     h->cursor = h->list.next;
140     if (h->cursor != &h->list)
141 	return &h->cursor->ev;
142     else
143 	return NULL;
144 }
145 
146 /* history_def_last():
147  *	Default function to return the last event in the history.
148  */
149 private const HistEvent *
150 history_def_last(p)
151     ptr_t p;
152 {
153     history_t *h = (history_t *) p;
154     h->cursor = h->list.prev;
155     if (h->cursor != &h->list)
156 	return &h->cursor->ev;
157     else
158 	return NULL;
159 }
160 
161 /* history_def_next():
162  *	Default function to return the next event in the history.
163  */
164 private const HistEvent *
165 history_def_next(p)
166     ptr_t p;
167 {
168     history_t *h = (history_t *) p;
169 
170     if (h->cursor != &h->list)
171 	h->cursor = h->cursor->next;
172     else
173 	return NULL;
174 
175     if (h->cursor != &h->list)
176 	return &h->cursor->ev;
177     else
178 	return NULL;
179 }
180 
181 
182 /* history_def_prev():
183  *	Default function to return the previous event in the history.
184  */
185 private const HistEvent *
186 history_def_prev(p)
187     ptr_t p;
188 {
189     history_t *h = (history_t *) p;
190 
191     if (h->cursor != &h->list)
192 	h->cursor = h->cursor->prev;
193     else
194 	return NULL;
195 
196     if (h->cursor != &h->list)
197 	return &h->cursor->ev;
198     else
199 	return NULL;
200 }
201 
202 
203 /* history_def_curr():
204  *	Default function to return the current event in the history.
205  */
206 private const HistEvent *
207 history_def_curr(p)
208     ptr_t p;
209 {
210     history_t *h = (history_t *) p;
211 
212     if (h->cursor != &h->list)
213 	return &h->cursor->ev;
214     else
215 	return NULL;
216 }
217 
218 /* history_def_add():
219  *	Append string to element
220  */
221 private const HistEvent *
222 history_def_add(p, str)
223     ptr_t p;
224     const char *str;
225 {
226     history_t *h = (history_t *) p;
227     size_t len;
228     char *s;
229 
230     if (h->cursor == &h->list)
231 	return (history_def_enter(p, str));
232     len = strlen(h->cursor->ev.str) + strlen(str) + 1;
233     s = (char *) h_malloc(len);
234     (void) strcpy(s, h->cursor->ev.str);
235     (void) strcat(s, str);
236     h_free((ptr_t) h->cursor->ev.str);
237     h->cursor->ev.str = s;
238     return &h->cursor->ev;
239 }
240 
241 
242 /* history_def_delete():
243  *	Delete element hp of the h list
244  */
245 private void
246 history_def_delete(h, hp)
247     history_t *h;
248     hentry_t *hp;
249 {
250     if (hp == &h->list)
251 	abort();
252     hp->prev->next = hp->next;
253     hp->next->prev = hp->prev;
254     h_free((ptr_t) hp->ev.str);
255     h_free(hp);
256     h->cur--;
257 }
258 
259 
260 /* history_def_insert():
261  *	Insert element with string str in the h list
262  */
263 private const HistEvent *
264 history_def_insert(h, str)
265     history_t *h;
266     const char *str;
267 {
268     h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
269     h->cursor->ev.str = strdup(str);
270     h->cursor->next = h->list.next;
271     h->cursor->prev = &h->list;
272     h->list.next->prev = h->cursor;
273     h->list.next = h->cursor;
274     h->cur++;
275 
276     return &h->cursor->ev;
277 }
278 
279 
280 /* history_def_enter():
281  *	Default function to enter an item in the history
282  */
283 private const HistEvent *
284 history_def_enter(p, str)
285     ptr_t p;
286     const char *str;
287 {
288     history_t *h = (history_t *) p;
289     const HistEvent *ev;
290 
291 
292     ev = history_def_insert(h, str);
293     ((HistEvent*) ev)->num = ++h->eventno;
294 
295     /*
296      * Always keep at least one entry.
297      * This way we don't have to check for the empty list.
298      */
299     while (h->cur > h->max + 1)
300 	history_def_delete(h, h->list.prev);
301     return ev;
302 }
303 
304 
305 /* history_def_init():
306  *	Default history initialization function
307  */
308 private void
309 history_def_init(p, n)
310     ptr_t *p;
311     int n;
312 {
313     history_t *h = (history_t *) h_malloc(sizeof(history_t));
314     if (n <= 0)
315 	n = 0;
316     h->eventno = 0;
317     h->cur = 0;
318     h->max = n;
319     h->list.next = h->list.prev = &h->list;
320     h->list.ev.str = NULL;
321     h->list.ev.num = 0;
322     h->cursor = &h->list;
323     *p = (ptr_t) h;
324 }
325 
326 
327 /* history_def_clear():
328  *	Default history cleanup function
329  */
330 private void
331 history_def_clear(p)
332     ptr_t p;
333 {
334     history_t *h = (history_t *) p;
335 
336     while (h->list.prev != &h->list)
337 	history_def_delete(h, h->list.prev);
338     h->eventno = 0;
339     h->cur = 0;
340 }
341 
342 
343 
344 
345 /************************************************************************/
346 
347 /* history_init():
348  *	Initialization function.
349  */
350 public History *
351 history_init()
352 {
353     History *h = (History *) h_malloc(sizeof(History));
354 
355     history_def_init(&h->h_ref, 0);
356 
357     h->h_next  = history_def_next;
358     h->h_first = history_def_first;
359     h->h_last  = history_def_last;
360     h->h_prev  = history_def_prev;
361     h->h_curr  = history_def_curr;
362     h->h_clear = history_def_clear;
363     h->h_enter = history_def_enter;
364     h->h_add   = history_def_add;
365 
366     return h;
367 }
368 
369 
370 /* history_end():
371  *	clean up history;
372  */
373 public void
374 history_end(h)
375     History *h;
376 {
377     if (h->h_next == history_def_next)
378 	history_def_clear(h->h_ref);
379 }
380 
381 
382 
383 /* history_set_num():
384  *	Set history number of events
385  */
386 private int
387 history_set_num(h, num)
388     History *h;
389     int num;
390 {
391     if (h->h_next != history_def_next || num < 0)
392 	return -1;
393     history_def_set(h->h_ref, num);
394     return 0;
395 }
396 
397 
398 /* history_set_fun():
399  *	Set history functions
400  */
401 private int
402 history_set_fun(h, nh)
403     History *h, *nh;
404 {
405     if (nh->h_first == NULL || nh->h_next == NULL ||
406         nh->h_last == NULL  || nh->h_prev == NULL || nh->h_curr == NULL ||
407 	nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
408 	nh->h_ref == NULL) {
409 	if (h->h_next != history_def_next) {
410 	    history_def_init(&h->h_ref, 0);
411 	    h->h_first = history_def_first;
412 	    h->h_next  = history_def_next;
413 	    h->h_last  = history_def_last;
414 	    h->h_prev  = history_def_prev;
415 	    h->h_curr  = history_def_curr;
416 	    h->h_clear = history_def_clear;
417 	    h->h_enter = history_def_enter;
418 	    h->h_add   = history_def_add;
419 	}
420 	return -1;
421     }
422 
423     if (h->h_next == history_def_next)
424 	history_def_clear(h->h_ref);
425 
426     h->h_first = nh->h_first;
427     h->h_next  = nh->h_next;
428     h->h_last  = nh->h_last;
429     h->h_prev  = nh->h_prev;
430     h->h_curr  = nh->h_curr;
431     h->h_clear = nh->h_clear;
432     h->h_enter = nh->h_enter;
433     h->h_add   = nh->h_add;
434 
435     return 0;
436 }
437 
438 
439 /* history_load():
440  *	History load function
441  */
442 private int
443 history_load(h, fname)
444     History *h;
445     const char *fname;
446 {
447     FILE *fp;
448     char *line;
449     size_t sz;
450     int i = -1;
451 
452     if ((fp = fopen(fname, "r")) == NULL)
453 	return i;
454 
455     if ((line = fgetln(fp, &sz)) == NULL)
456 	goto done;
457 
458     if (strncmp(line, hist_cookie, sz) != 0)
459 	goto done;
460 
461     for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
462 	char c = line[sz];
463 	line[sz] = '\0';
464 	HENTER(h, line);
465 	line[sz] = c;
466     }
467 
468 done:
469     (void) fclose(fp);
470     return i;
471 }
472 
473 
474 /* history_save():
475  *	History save function
476  */
477 private int
478 history_save(h, fname)
479     History *h;
480     const char *fname;
481 {
482     FILE *fp;
483     const HistEvent *ev;
484     int i = 0;
485 
486     if ((fp = fopen(fname, "w")) == NULL)
487 	return -1;
488 
489     (void) fputs(hist_cookie, fp);
490     for (ev = HLAST(h); ev != NULL; ev = HPREV(h), i++)
491 	(void) fprintf(fp, "%s", ev->str);
492     (void) fclose(fp);
493     return i;
494 }
495 
496 
497 /* history_prev_event():
498  *	Find the previous event, with number given
499  */
500 private const HistEvent *
501 history_prev_event(h, num)
502     History *h;
503     int num;
504 {
505     const HistEvent *ev;
506     for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
507 	if (ev->num == num)
508 	    return ev;
509     return NULL;
510 }
511 
512 
513 /* history_next_event():
514  *	Find the next event, with number given
515  */
516 private const HistEvent *
517 history_next_event(h, num)
518     History *h;
519     int num;
520 {
521     const HistEvent *ev;
522     for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
523 	if (ev->num == num)
524 	    return ev;
525     return NULL;
526 }
527 
528 
529 /* history_prev_string():
530  *	Find the previous event beginning with string
531  */
532 private const HistEvent *
533 history_prev_string(h, str)
534     History *h;
535     const char* str;
536 {
537     const HistEvent *ev;
538     size_t len = strlen(str);
539 
540     for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
541 	if (strncmp(str, ev->str, len) == 0)
542 	    return ev;
543     return NULL;
544 }
545 
546 
547 
548 
549 /* history_next_string():
550  *	Find the next event beginning with string
551  */
552 private const HistEvent *
553 history_next_string(h, str)
554     History *h;
555     const char* str;
556 {
557     const HistEvent *ev;
558     size_t len = strlen(str);
559 
560     for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
561 	if (strncmp(str, ev->str, len) == 0)
562 	    return ev;
563     return NULL;
564 }
565 
566 
567 /* history():
568  *	User interface to history functions.
569  */
570 const HistEvent *
571 #if __STDC__
572 history(History *h, int fun, ...)
573 #else
574 history(va_alist)
575     va_dcl
576 #endif
577 {
578     va_list va;
579     const HistEvent *ev = NULL;
580     const char *str;
581     static HistEvent sev = { 0, "" };
582 
583 #if __STDC__
584     va_start(va, fun);
585 #else
586     History *h;
587     int fun;
588     va_start(va);
589     h = va_arg(va, History *);
590     fun = va_arg(va, int);
591 #endif
592 
593     switch (fun) {
594     case H_ADD:
595 	str = va_arg(va, const char *);
596 	ev = HADD(h, str);
597 	break;
598 
599     case H_ENTER:
600 	str = va_arg(va, const char *);
601 	ev = HENTER(h, str);
602 	break;
603 
604     case H_FIRST:
605 	ev = HFIRST(h);
606 	break;
607 
608     case H_NEXT:
609 	ev = HNEXT(h);
610 	break;
611 
612     case H_LAST:
613 	ev = HLAST(h);
614 	break;
615 
616     case H_PREV:
617 	ev = HPREV(h);
618 	break;
619 
620     case H_CURR:
621 	ev = HCURR(h);
622 	break;
623 
624     case H_CLEAR:
625 	HCLEAR(h);
626 	break;
627 
628     case H_LOAD:
629 	sev.num = history_load(h, va_arg(va, const char *));
630 	ev = &sev;
631 	break;
632 
633     case H_SAVE:
634 	sev.num = history_save(h, va_arg(va, const char *));
635 	ev = &sev;
636 	break;
637 
638     case H_PREV_EVENT:
639 	ev = history_prev_event(h, va_arg(va, int));
640 	break;
641 
642     case H_NEXT_EVENT:
643 	ev = history_next_event(h, va_arg(va, int));
644 	break;
645 
646     case H_PREV_STR:
647 	ev = history_prev_string(h, va_arg(va, const char*));
648 	break;
649 
650     case H_NEXT_STR:
651 	ev = history_next_string(h, va_arg(va, const char*));
652 	break;
653 
654     case H_EVENT:
655 	if (history_set_num(h, va_arg(va, int)) == 0) {
656 	    sev.num = -1;
657 	    ev = &sev;
658 	}
659 	break;
660 
661     case H_FUNC:
662 	{
663 	    History hf;
664 	    hf.h_ref   = va_arg(va, ptr_t);
665 	    hf.h_first = va_arg(va, history_gfun_t);
666 	    hf.h_next  = va_arg(va, history_gfun_t);
667 	    hf.h_last  = va_arg(va, history_gfun_t);
668 	    hf.h_prev  = va_arg(va, history_gfun_t);
669 	    hf.h_curr  = va_arg(va, history_gfun_t);
670 	    hf.h_clear = va_arg(va, history_vfun_t);
671 	    hf.h_enter = va_arg(va, history_efun_t);
672 	    hf.h_add   = va_arg(va, history_efun_t);
673 
674 	    if (history_set_fun(h, &hf) == 0) {
675 		sev.num = -1;
676 		ev = &sev;
677 	    }
678 	}
679 	break;
680 
681     case H_END:
682 	history_end(h);
683 	break;
684 
685     default:
686 	break;
687     }
688     va_end(va);
689     return ev;
690 }
691