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