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