history.c revision 1573
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)
38static 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
56typedef const HistEvent *	(*history_gfun_t) __P((ptr_t));
57typedef const HistEvent *	(*history_efun_t) __P((ptr_t, const char *));
58
59struct 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
82private int		 history_set_num	__P((History *, int));
83private 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));
90private const HistEvent *history_prev_event	__P((History *, int));
91private const HistEvent *history_next_event	__P((History *, int));
92private const HistEvent *history_next_string	__P((History *, const char *));
93private const HistEvent *history_prev_string	__P((History *, const char *));
94
95
96/***********************************************************************/
97
98/*
99 * Builtin- history implementation
100 */
101typedef 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
107typedef 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
115private const HistEvent *history_def_first  __P((ptr_t));
116private const HistEvent *history_def_last   __P((ptr_t));
117private const HistEvent *history_def_next   __P((ptr_t));
118private const HistEvent *history_def_prev   __P((ptr_t));
119private const HistEvent *history_def_curr   __P((ptr_t));
120private const HistEvent *history_def_enter  __P((ptr_t, const char *));
121private const HistEvent *history_def_add    __P((ptr_t, const char *));
122private void             history_def_init   __P((ptr_t *, int));
123private void             history_def_end    __P((ptr_t));
124private const HistEvent *history_def_insert __P((history_t *, const char *));
125private 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 */
133private const HistEvent *
134history_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 */
148private const HistEvent *
149history_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 */
163private const HistEvent *
164history_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 */
184private const HistEvent *
185history_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 */
205private const HistEvent *
206history_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 */
221private const HistEvent *
222history_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 */
245private void
246history_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 */
263private const HistEvent *
264history_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 */
283private const HistEvent *
284history_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 */
308private void
309history_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 */
330private void
331history_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 */
345public History *
346history_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 */
367public void
368history_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 */
380private int
381history_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 */
395private int
396history_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 */
433private const HistEvent *
434history_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 */
449private const HistEvent *
450history_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 */
465private const HistEvent *
466history_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 */
483private const HistEvent *
484history_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 */
501const HistEvent *
502#if __STDC__
503history(History *h, int fun, ...)
504#else
505history(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