xref: /netbsd-src/lib/libedit/history.c (revision ce0bb6e8d2e560ecacbe865a848624f94498063b)
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 #include "histedit.h"
55 
56 typedef const HistEvent *	(*history_gfun_t) __P((ptr_t));
57 typedef const HistEvent *	(*history_efun_t) __P((ptr_t, const char *));
58 
59 struct history {
60     ptr_t	   h_ref;		/* Argument for history fcns	*/
61     history_gfun_t h_first;		/* Get the first element	*/
62     history_gfun_t h_next;		/* Get the next element		*/
63     history_gfun_t h_last;		/* Get the last element		*/
64     history_gfun_t h_prev;		/* Get the previous element	*/
65     history_gfun_t h_curr;		/* Get the current element	*/
66     history_efun_t h_enter;		/* Add an element		*/
67     history_efun_t h_add;		/* Append to an element		*/
68 };
69 
70 #define	HNEXT(h)  	(*(h)->h_next)((h)->h_ref)
71 #define	HFIRST(h) 	(*(h)->h_first)((h)->h_ref)
72 #define	HPREV(h)  	(*(h)->h_prev)((h)->h_ref)
73 #define	HLAST(h) 	(*(h)->h_last)((h)->h_ref)
74 #define	HCURR(h) 	(*(h)->h_curr)((h)->h_ref)
75 #define	HENTER(h, str)	(*(h)->h_enter)((h)->h_ref, str)
76 #define	HADD(h, str)	(*(h)->h_add)((h)->h_ref, str)
77 
78 #define h_malloc(a)	malloc(a)
79 #define h_free(a)	free(a)
80 
81 
82 private int		 history_set_num	__P((History *, int));
83 private int		 history_set_fun	__P((History *, history_gfun_t,
84 						     history_gfun_t,
85 						     history_gfun_t,
86 						     history_gfun_t,
87 						     history_gfun_t,
88 						     history_efun_t,
89 						     history_efun_t, ptr_t));
90 private const HistEvent *history_prev_event	__P((History *, int));
91 private const HistEvent *history_next_event	__P((History *, int));
92 private const HistEvent *history_next_string	__P((History *, const char *));
93 private const HistEvent *history_prev_string	__P((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  __P((ptr_t));
116 private const HistEvent *history_def_last   __P((ptr_t));
117 private const HistEvent *history_def_next   __P((ptr_t));
118 private const HistEvent *history_def_prev   __P((ptr_t));
119 private const HistEvent *history_def_curr   __P((ptr_t));
120 private const HistEvent *history_def_enter  __P((ptr_t, const char *));
121 private const HistEvent *history_def_add    __P((ptr_t, const char *));
122 private void             history_def_init   __P((ptr_t *, int));
123 private void             history_def_end    __P((ptr_t));
124 private const HistEvent *history_def_insert __P((history_t *, const char *));
125 private void             history_def_delete __P((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 
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_end():
328  *	Default history cleanup function
329  */
330 private void
331 history_def_end(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 }
339 
340 /************************************************************************/
341 
342 /* history_init():
343  *	Initialization function.
344  */
345 public History *
346 history_init()
347 {
348     History *h = (History *) h_malloc(sizeof(History));
349 
350     history_def_init(&h->h_ref, 0);
351 
352     h->h_next  = history_def_next;
353     h->h_first = history_def_first;
354     h->h_last  = history_def_last;
355     h->h_prev  = history_def_prev;
356     h->h_curr  = history_def_curr;
357     h->h_enter = history_def_enter;
358     h->h_add   = history_def_add;
359 
360     return h;
361 }
362 
363 
364 /* history_end():
365  *	clean up history;
366  */
367 public void
368 history_end(h)
369     History *h;
370 {
371     if (h->h_next == history_def_next)
372 	history_def_end(h->h_ref);
373 }
374 
375 
376 
377 /* history_set_num():
378  *	Set history number of events
379  */
380 private int
381 history_set_num(h, num)
382     History *h;
383     int num;
384 {
385     if (h->h_next != history_def_next || num < 0)
386 	return -1;
387     history_def_set(h->h_ref, num);
388     return 0;
389 }
390 
391 
392 /* history_set_fun():
393  *	Set history functions
394  */
395 private int
396 history_set_fun(h, first, next, last, prev, curr, enter, add, ptr)
397     History *h;
398     history_gfun_t first, next, last, prev, curr;
399     history_efun_t enter, add;
400     ptr_t ptr;
401 {
402     if (first == NULL || next == NULL ||
403         last == NULL  || prev == NULL || curr == NULL ||
404 	enter == NULL || add == NULL ||
405 	ptr == NULL ) {
406 	if (h->h_next != history_def_next) {
407 	    history_def_init(&h->h_ref, 0);
408 	    h->h_first = history_def_first;
409 	    h->h_next  = history_def_next;
410 	    h->h_last  = history_def_last;
411 	    h->h_prev  = history_def_prev;
412 	    h->h_curr  = history_def_curr;
413 	    h->h_enter = history_def_enter;
414 	    h->h_add   = history_def_add;
415 	}
416 	return -1;
417     }
418 
419     if (h->h_next == history_def_next)
420 	history_def_end(h->h_ref);
421 
422     h->h_next  = next;
423     h->h_first = first;
424     h->h_enter = enter;
425     h->h_add   = add;
426     return 0;
427 }
428 
429 
430 /* history_prev_event():
431  *	Find the previous event, with number given
432  */
433 private const HistEvent *
434 history_prev_event(h, num)
435     History *h;
436     int num;
437 {
438     const HistEvent *ev;
439     for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
440 	if (ev->num == num)
441 	    return ev;
442     return NULL;
443 }
444 
445 
446 /* history_next_event():
447  *	Find the next event, with number given
448  */
449 private const HistEvent *
450 history_next_event(h, num)
451     History *h;
452     int num;
453 {
454     const HistEvent *ev;
455     for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
456 	if (ev->num == num)
457 	    return ev;
458     return NULL;
459 }
460 
461 
462 /* history_prev_string():
463  *	Find the previous event beginning with string
464  */
465 private const HistEvent *
466 history_prev_string(h, str)
467     History *h;
468     const char* str;
469 {
470     const HistEvent *ev;
471     size_t len = strlen(str);
472 
473     for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
474 	if (strncmp(str, ev->str, len) == 0)
475 	    return ev;
476     return NULL;
477 }
478 
479 
480 /* history_next_string():
481  *	Find the next event beginning with string
482  */
483 private const HistEvent *
484 history_next_string(h, str)
485     History *h;
486     const char* str;
487 {
488     const HistEvent *ev;
489     size_t len = strlen(str);
490 
491     for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
492 	if (strncmp(str, ev->str, len) == 0)
493 	    return ev;
494     return NULL;
495 }
496 
497 
498 /* history():
499  *	User interface to history functions.
500  */
501 const HistEvent *
502 #if __STDC__
503 history(History *h, int fun, ...)
504 #else
505 history(va_alist)
506     va_dcl
507 #endif
508 {
509     va_list va;
510     const HistEvent *ev = NULL;
511     const char *str;
512     static const HistEvent sev = { 0, "" };
513 
514 #if __STDC__
515     va_start(va, fun);
516 #else
517     History *h;
518     int fun;
519     va_start(va);
520     h = va_arg(va, History *);
521     fun = va_arg(va, int);
522 #endif
523 
524     switch (fun) {
525     case H_ADD:
526 	str = va_arg(va, const char *);
527 	ev = HADD(h, str);
528 	break;
529 
530     case H_ENTER:
531 	str = va_arg(va, const char *);
532 	ev = HENTER(h, str);
533 	break;
534 
535     case H_FIRST:
536 	ev = HFIRST(h);
537 	break;
538 
539     case H_NEXT:
540 	ev = HNEXT(h);
541 	break;
542 
543     case H_LAST:
544 	ev = HLAST(h);
545 	break;
546 
547     case H_PREV:
548 	ev = HPREV(h);
549 	break;
550 
551     case H_CURR:
552 	ev = HCURR(h);
553 	break;
554 
555     case H_PREV_EVENT:
556 	ev = history_prev_event(h, va_arg(va, int));
557 	break;
558 
559     case H_NEXT_EVENT:
560 	ev = history_next_event(h, va_arg(va, int));
561 	break;
562 
563     case H_PREV_STR:
564 	ev = history_prev_string(h, va_arg(va, const char*));
565 	break;
566 
567     case H_NEXT_STR:
568 	ev = history_next_string(h, va_arg(va, const char*));
569 	break;
570 
571     case H_EVENT:
572 	if (history_set_num(h, va_arg(va, int)) == 0)
573 	    ev = &sev;
574 	break;
575 
576     case H_FUNC:
577 	{
578 	    history_gfun_t	first = va_arg(va, history_gfun_t);
579 	    history_gfun_t	next  = va_arg(va, history_gfun_t);
580 	    history_gfun_t	last  = va_arg(va, history_gfun_t);
581 	    history_gfun_t	prev  = va_arg(va, history_gfun_t);
582 	    history_gfun_t	curr  = va_arg(va, history_gfun_t);
583 	    history_efun_t	enter = va_arg(va, history_efun_t);
584 	    history_efun_t	add   = va_arg(va, history_efun_t);
585 	    ptr_t		ptr   = va_arg(va, ptr_t);
586 
587 	    if (history_set_fun(h, first, next, last, prev,
588 				   curr, enter, add, ptr) == 0)
589 		ev = &sev;
590 	}
591 	break;
592 
593     case H_END:
594 	history_end(h);
595 	break;
596 
597     default:
598 	break;
599     }
600     va_end(va);
601     return ev;
602 }
603