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