history.c revision 1.2
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
54static const char hist_cookie[] = "_HiStOrY_V1_\n";
55
56#include "histedit.h"
57
58typedef const HistEvent *	(*history_gfun_t) __P((ptr_t));
59typedef const HistEvent *	(*history_efun_t) __P((ptr_t, const char *));
60typedef void 			(*history_vfun_t) __P((ptr_t));
61
62struct history {
63    ptr_t	   h_ref;		/* Argument for history fcns	*/
64    history_gfun_t h_first;		/* Get the first element	*/
65    history_gfun_t h_next;		/* Get the next element		*/
66    history_gfun_t h_last;		/* Get the last element		*/
67    history_gfun_t h_prev;		/* Get the previous element	*/
68    history_gfun_t h_curr;		/* Get the current element	*/
69    history_vfun_t h_clear;		/* Clear the history list	*/
70    history_efun_t h_enter;		/* Add an element		*/
71    history_efun_t h_add;		/* Append to an element		*/
72};
73
74#define	HNEXT(h)  	(*(h)->h_next)((h)->h_ref)
75#define	HFIRST(h) 	(*(h)->h_first)((h)->h_ref)
76#define	HPREV(h)  	(*(h)->h_prev)((h)->h_ref)
77#define	HLAST(h) 	(*(h)->h_last)((h)->h_ref)
78#define	HCURR(h) 	(*(h)->h_curr)((h)->h_ref)
79#define	HCLEAR(h) 	(*(h)->h_clear)((h)->h_ref)
80#define	HENTER(h, str)	(*(h)->h_enter)((h)->h_ref, str)
81#define	HADD(h, str)	(*(h)->h_add)((h)->h_ref, str)
82
83#define h_malloc(a)	malloc(a)
84#define h_free(a)	free(a)
85
86
87private int		 history_set_num	__P((History *, int));
88private int		 history_set_fun	__P((History *, History *));
89private int 		 history_load		__P((History *, const char *));
90private int 		 history_save		__P((History *, const char *));
91private const HistEvent *history_prev_event	__P((History *, int));
92private const HistEvent *history_next_event	__P((History *, int));
93private const HistEvent *history_next_string	__P((History *, const char *));
94private const HistEvent *history_prev_string	__P((History *, const char *));
95
96
97/***********************************************************************/
98
99/*
100 * Builtin- history implementation
101 */
102typedef struct hentry_t {
103    HistEvent ev;		/* What we return		*/
104    struct hentry_t *next;	/* Next entry			*/
105    struct hentry_t *prev;	/* Previous entry		*/
106} hentry_t;
107
108typedef struct history_t {
109    hentry_t  list;		/* Fake list header element	*/
110    hentry_t *cursor;		/* Current element in the list	*/
111    int	max;			/* Maximum number of events	*/
112    int cur;			/* Current number of events	*/
113    int	eventno;		/* Current event number		*/
114} history_t;
115
116private const HistEvent *history_def_first  __P((ptr_t));
117private const HistEvent *history_def_last   __P((ptr_t));
118private const HistEvent *history_def_next   __P((ptr_t));
119private const HistEvent *history_def_prev   __P((ptr_t));
120private const HistEvent *history_def_curr   __P((ptr_t));
121private const HistEvent *history_def_enter  __P((ptr_t, const char *));
122private const HistEvent *history_def_add    __P((ptr_t, const char *));
123private void             history_def_init   __P((ptr_t *, int));
124private void             history_def_clear  __P((ptr_t));
125private const HistEvent *history_def_insert __P((history_t *, const char *));
126private void             history_def_delete __P((history_t *, hentry_t *));
127
128#define history_def_set(p, num)	(void) (((history_t *) p)->max = (num))
129
130
131/* history_def_first():
132 *	Default function to return the first event in the history.
133 */
134private const HistEvent *
135history_def_first(p)
136    ptr_t p;
137{
138    history_t *h = (history_t *) p;
139    h->cursor = h->list.next;
140    if (h->cursor != &h->list)
141	return &h->cursor->ev;
142    else
143	return NULL;
144}
145
146/* history_def_last():
147 *	Default function to return the last event in the history.
148 */
149private const HistEvent *
150history_def_last(p)
151    ptr_t p;
152{
153    history_t *h = (history_t *) p;
154    h->cursor = h->list.prev;
155    if (h->cursor != &h->list)
156	return &h->cursor->ev;
157    else
158	return NULL;
159}
160
161/* history_def_next():
162 *	Default function to return the next event in the history.
163 */
164private const HistEvent *
165history_def_next(p)
166    ptr_t p;
167{
168    history_t *h = (history_t *) p;
169
170    if (h->cursor != &h->list)
171	h->cursor = h->cursor->next;
172    else
173	return NULL;
174
175    if (h->cursor != &h->list)
176	return &h->cursor->ev;
177    else
178	return NULL;
179}
180
181
182/* history_def_prev():
183 *	Default function to return the previous event in the history.
184 */
185private const HistEvent *
186history_def_prev(p)
187    ptr_t p;
188{
189    history_t *h = (history_t *) p;
190
191    if (h->cursor != &h->list)
192	h->cursor = h->cursor->prev;
193    else
194	return NULL;
195
196    if (h->cursor != &h->list)
197	return &h->cursor->ev;
198    else
199	return NULL;
200}
201
202
203/* history_def_curr():
204 *	Default function to return the current event in the history.
205 */
206private const HistEvent *
207history_def_curr(p)
208    ptr_t p;
209{
210    history_t *h = (history_t *) p;
211
212    if (h->cursor != &h->list)
213	return &h->cursor->ev;
214    else
215	return NULL;
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_clear():
328 *	Default history cleanup function
329 */
330private void
331history_def_clear(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    h->eventno = 0;
339    h->cur = 0;
340}
341
342
343
344
345/************************************************************************/
346
347/* history_init():
348 *	Initialization function.
349 */
350public History *
351history_init()
352{
353    History *h = (History *) h_malloc(sizeof(History));
354
355    history_def_init(&h->h_ref, 0);
356
357    h->h_next  = history_def_next;
358    h->h_first = history_def_first;
359    h->h_last  = history_def_last;
360    h->h_prev  = history_def_prev;
361    h->h_curr  = history_def_curr;
362    h->h_clear = history_def_clear;
363    h->h_enter = history_def_enter;
364    h->h_add   = history_def_add;
365
366    return h;
367}
368
369
370/* history_end():
371 *	clean up history;
372 */
373public void
374history_end(h)
375    History *h;
376{
377    if (h->h_next == history_def_next)
378	history_def_clear(h->h_ref);
379}
380
381
382
383/* history_set_num():
384 *	Set history number of events
385 */
386private int
387history_set_num(h, num)
388    History *h;
389    int num;
390{
391    if (h->h_next != history_def_next || num < 0)
392	return -1;
393    history_def_set(h->h_ref, num);
394    return 0;
395}
396
397
398/* history_set_fun():
399 *	Set history functions
400 */
401private int
402history_set_fun(h, nh)
403    History *h, *nh;
404{
405    if (nh->h_first == NULL || nh->h_next == NULL ||
406        nh->h_last == NULL  || nh->h_prev == NULL || nh->h_curr == NULL ||
407	nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
408	nh->h_ref == NULL) {
409	if (h->h_next != history_def_next) {
410	    history_def_init(&h->h_ref, 0);
411	    h->h_first = history_def_first;
412	    h->h_next  = history_def_next;
413	    h->h_last  = history_def_last;
414	    h->h_prev  = history_def_prev;
415	    h->h_curr  = history_def_curr;
416	    h->h_clear = history_def_clear;
417	    h->h_enter = history_def_enter;
418	    h->h_add   = history_def_add;
419	}
420	return -1;
421    }
422
423    if (h->h_next == history_def_next)
424	history_def_clear(h->h_ref);
425
426    h->h_first = nh->h_first;
427    h->h_next  = nh->h_next;
428    h->h_last  = nh->h_last;
429    h->h_prev  = nh->h_prev;
430    h->h_curr  = nh->h_curr;
431    h->h_clear = nh->h_clear;
432    h->h_enter = nh->h_enter;
433    h->h_add   = nh->h_add;
434
435    return 0;
436}
437
438
439/* history_load():
440 *	History load function
441 */
442private int
443history_load(h, fname)
444    History *h;
445    const char *fname;
446{
447    FILE *fp;
448    char *line;
449    size_t sz;
450    int i = -1;
451
452    if ((fp = fopen(fname, "r")) == NULL)
453	return i;
454
455    if ((line = fgetln(fp, &sz)) == NULL)
456	goto done;
457
458    if (strncmp(line, hist_cookie, sz) != 0)
459	goto done;
460
461    for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
462	char c = line[sz];
463	line[sz] = '\0';
464	HENTER(h, line);
465	line[sz] = c;
466    }
467
468done:
469    (void) fclose(fp);
470    return i;
471}
472
473
474/* history_save():
475 *	History save function
476 */
477private int
478history_save(h, fname)
479    History *h;
480    const char *fname;
481{
482    FILE *fp;
483    const HistEvent *ev;
484    int i = 0;
485
486    if ((fp = fopen(fname, "w")) == NULL)
487	return -1;
488
489    (void) fputs(hist_cookie, fp);
490    for (ev = HLAST(h); ev != NULL; ev = HPREV(h), i++)
491	(void) fprintf(fp, "%s", ev->str);
492    (void) fclose(fp);
493    return i;
494}
495
496
497/* history_prev_event():
498 *	Find the previous event, with number given
499 */
500private const HistEvent *
501history_prev_event(h, num)
502    History *h;
503    int num;
504{
505    const HistEvent *ev;
506    for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
507	if (ev->num == num)
508	    return ev;
509    return NULL;
510}
511
512
513/* history_next_event():
514 *	Find the next event, with number given
515 */
516private const HistEvent *
517history_next_event(h, num)
518    History *h;
519    int num;
520{
521    const HistEvent *ev;
522    for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
523	if (ev->num == num)
524	    return ev;
525    return NULL;
526}
527
528
529/* history_prev_string():
530 *	Find the previous event beginning with string
531 */
532private const HistEvent *
533history_prev_string(h, str)
534    History *h;
535    const char* str;
536{
537    const HistEvent *ev;
538    size_t len = strlen(str);
539
540    for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
541	if (strncmp(str, ev->str, len) == 0)
542	    return ev;
543    return NULL;
544}
545
546
547
548
549/* history_next_string():
550 *	Find the next event beginning with string
551 */
552private const HistEvent *
553history_next_string(h, str)
554    History *h;
555    const char* str;
556{
557    const HistEvent *ev;
558    size_t len = strlen(str);
559
560    for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
561	if (strncmp(str, ev->str, len) == 0)
562	    return ev;
563    return NULL;
564}
565
566
567/* history():
568 *	User interface to history functions.
569 */
570const HistEvent *
571#if __STDC__
572history(History *h, int fun, ...)
573#else
574history(va_alist)
575    va_dcl
576#endif
577{
578    va_list va;
579    const HistEvent *ev = NULL;
580    const char *str;
581    static HistEvent sev = { 0, "" };
582
583#if __STDC__
584    va_start(va, fun);
585#else
586    History *h;
587    int fun;
588    va_start(va);
589    h = va_arg(va, History *);
590    fun = va_arg(va, int);
591#endif
592
593    switch (fun) {
594    case H_ADD:
595	str = va_arg(va, const char *);
596	ev = HADD(h, str);
597	break;
598
599    case H_ENTER:
600	str = va_arg(va, const char *);
601	ev = HENTER(h, str);
602	break;
603
604    case H_FIRST:
605	ev = HFIRST(h);
606	break;
607
608    case H_NEXT:
609	ev = HNEXT(h);
610	break;
611
612    case H_LAST:
613	ev = HLAST(h);
614	break;
615
616    case H_PREV:
617	ev = HPREV(h);
618	break;
619
620    case H_CURR:
621	ev = HCURR(h);
622	break;
623
624    case H_CLEAR:
625	HCLEAR(h);
626	break;
627
628    case H_LOAD:
629	sev.num = history_load(h, va_arg(va, const char *));
630	ev = &sev;
631	break;
632
633    case H_SAVE:
634	sev.num = history_save(h, va_arg(va, const char *));
635	ev = &sev;
636	break;
637
638    case H_PREV_EVENT:
639	ev = history_prev_event(h, va_arg(va, int));
640	break;
641
642    case H_NEXT_EVENT:
643	ev = history_next_event(h, va_arg(va, int));
644	break;
645
646    case H_PREV_STR:
647	ev = history_prev_string(h, va_arg(va, const char*));
648	break;
649
650    case H_NEXT_STR:
651	ev = history_next_string(h, va_arg(va, const char*));
652	break;
653
654    case H_EVENT:
655	if (history_set_num(h, va_arg(va, int)) == 0) {
656	    sev.num = -1;
657	    ev = &sev;
658	}
659	break;
660
661    case H_FUNC:
662	{
663	    History hf;
664	    hf.h_ref   = va_arg(va, ptr_t);
665	    hf.h_first = va_arg(va, history_gfun_t);
666	    hf.h_next  = va_arg(va, history_gfun_t);
667	    hf.h_last  = va_arg(va, history_gfun_t);
668	    hf.h_prev  = va_arg(va, history_gfun_t);
669	    hf.h_curr  = va_arg(va, history_gfun_t);
670	    hf.h_clear = va_arg(va, history_vfun_t);
671	    hf.h_enter = va_arg(va, history_efun_t);
672	    hf.h_add   = va_arg(va, history_efun_t);
673
674	    if (history_set_fun(h, &hf) == 0) {
675		sev.num = -1;
676		ev = &sev;
677	    }
678	}
679	break;
680
681    case H_END:
682	history_end(h);
683	break;
684
685    default:
686	break;
687    }
688    va_end(va);
689    return ev;
690}
691