history.c revision 26926
1139790Simp/*-
22268Sats * Copyright (c) 1992, 1993
32268Sats *	The Regents of the University of California.  All rights reserved.
42268Sats *
52268Sats * This code is derived from software contributed to Berkeley by
62268Sats * Christos Zoulas of Cornell University.
72268Sats *
82268Sats * Redistribution and use in source and binary forms, with or without
92268Sats * modification, are permitted provided that the following conditions
102268Sats * are met:
112268Sats * 1. Redistributions of source code must retain the above copyright
122268Sats *    notice, this list of conditions and the following disclaimer.
132268Sats * 2. Redistributions in binary form must reproduce the above copyright
142268Sats *    notice, this list of conditions and the following disclaimer in the
152268Sats *    documentation and/or other materials provided with the distribution.
162268Sats * 3. All advertising materials mentioning features or use of this software
172268Sats *    must display the following acknowledgement:
182268Sats *	This product includes software developed by the University of
192268Sats *	California, Berkeley and its contributors.
202268Sats * 4. Neither the name of the University nor the names of its contributors
212268Sats *    may be used to endorse or promote products derived from this software
222268Sats *    without specific prior written permission.
232268Sats *
242268Sats * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
252268Sats * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
262268Sats * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
272268Sats * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
282268Sats * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
292268Sats * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30115703Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31115703Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32115703Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
332268Sats * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
342268Sats * SUCH DAMAGE.
352268Sats */
362268Sats
37112789Smdodd#if !defined(lint) && !defined(SCCSID)
38112789Smdoddstatic char sccsid[] = "@(#)history.c	8.1 (Berkeley) 6/4/93";
39112789Smdodd#endif /* not lint && not SCCSID */
40112789Smdodd
416734Sbde/*
426734Sbde * hist.c: History access functions
436734Sbde */
442268Sats#include "sys.h"
452268Sats
462268Sats#include <string.h>
472268Sats#include <stdlib.h>
482268Sats#if __STDC__
492268Sats#include <stdarg.h>
502268Sats#else
512268Sats#include <varargs.h>
522268Sats#endif
538876Srgrimes
542268Satsstatic const char hist_cookie[] = "_HiStOrY_V1_\n";
552268Sats
562268Sats#include "histedit.h"
572268Sats
582268Satstypedef const HistEvent *	(*history_gfun_t) __P((ptr_t));
592268Satstypedef const HistEvent *	(*history_efun_t) __P((ptr_t, const char *));
60112789Smdoddtypedef void			(*history_vfun_t) __P((ptr_t));
61112789Smdodd
62112789Smdoddstruct history {
63112789Smdodd    ptr_t	   h_ref;		/* Argument for history fcns	*/
642268Sats    history_gfun_t h_first;		/* Get the first element	*/
652268Sats    history_gfun_t h_next;		/* Get the next element		*/
662268Sats    history_gfun_t h_last;		/* Get the last element		*/
672268Sats    history_gfun_t h_prev;		/* Get the previous element	*/
682268Sats    history_gfun_t h_curr;		/* Get the current element	*/
692268Sats    history_vfun_t h_clear;		/* Clear the history list       */
702268Sats    history_efun_t h_enter;		/* Add an element		*/
7128752Sbde    history_efun_t h_add;		/* Append to an element		*/
722268Sats};
732268Sats
742268Sats#define	HNEXT(h)  	(*(h)->h_next)((h)->h_ref)
752268Sats#define	HFIRST(h) 	(*(h)->h_first)((h)->h_ref)
762268Sats#define	HPREV(h)  	(*(h)->h_prev)((h)->h_ref)
772268Sats#define	HLAST(h) 	(*(h)->h_last)((h)->h_ref)
782268Sats#define	HCURR(h) 	(*(h)->h_curr)((h)->h_ref)
792268Sats#define HCLEAR(h)	(*(h)->h_clear)((h)->h_ref)
802268Sats#define	HENTER(h, str)	(*(h)->h_enter)((h)->h_ref, str)
812268Sats#define	HADD(h, str)	(*(h)->h_add)((h)->h_ref, str)
822268Sats
832268Sats#define h_malloc(a)	malloc(a)
842268Sats#define h_free(a)	free(a)
852268Sats
86112789Smdodd
87112789Smdoddprivate int		 history_set_num	__P((History *, int));
88112789Smdoddprivate int		 history_set_fun	__P((History *, History *));
89112789Smdoddprivate int		 history_load		__P((History *, const char *));
90241394Skevloprivate int		 history_save		__P((History *, const char *));
91112789Smdoddprivate const HistEvent *history_prev_event	__P((History *, int));
92112789Smdoddprivate const HistEvent *history_next_event	__P((History *, int));
93112789Smdoddprivate const HistEvent *history_next_string	__P((History *, const char *));
94112789Smdoddprivate 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
219/* history_def_add():
220 *	Append string to element
221 */
222private const HistEvent *
223history_def_add(p, str)
224    ptr_t p;
225    const char *str;
226{
227    history_t *h = (history_t *) p;
228    size_t len;
229    char *s;
230
231    if (h->cursor == &h->list)
232	return (history_def_enter(p, str));
233    len = strlen(h->cursor->ev.str) + strlen(str) + 1;
234    s = (char *) h_malloc(len);
235    (void)strcpy(s, h->cursor->ev.str);	/* XXX strcpy is safe */
236    (void)strcat(s, str);			/* XXX strcat is safe */
237    h_free((ptr_t) h->cursor->ev.str);
238    h->cursor->ev.str = s;
239    return &h->cursor->ev;
240}
241
242
243/* history_def_delete():
244 *	Delete element hp of the h list
245 */
246private void
247history_def_delete(h, hp)
248    history_t *h;
249    hentry_t *hp;
250{
251    if (hp == &h->list)
252	abort();
253    hp->prev->next = hp->next;
254    hp->next->prev = hp->prev;
255    h_free((ptr_t) hp->ev.str);
256    h_free(hp);
257    h->cur--;
258}
259
260
261/* history_def_insert():
262 *	Insert element with string str in the h list
263 */
264private const HistEvent *
265history_def_insert(h, str)
266    history_t *h;
267    const char *str;
268{
269    h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
270    h->cursor->ev.str = strdup(str);
271    h->cursor->next = h->list.next;
272    h->cursor->prev = &h->list;
273    h->list.next->prev = h->cursor;
274    h->list.next = h->cursor;
275    h->cur++;
276
277    return &h->cursor->ev;
278}
279
280
281/* history_def_enter():
282 *	Default function to enter an item in the history
283 */
284private const HistEvent *
285history_def_enter(p, str)
286    ptr_t p;
287    const char *str;
288{
289    history_t *h = (history_t *) p;
290    const HistEvent *ev;
291
292
293    ev = history_def_insert(h, str);
294    ((HistEvent*) ev)->num = ++h->eventno;
295
296    /*
297     * Always keep at least one entry.
298     * This way we don't have to check for the empty list.
299     */
300    while (h->cur > h->max + 1)
301	history_def_delete(h, h->list.prev);
302    return ev;
303}
304
305
306/* history_def_init():
307 *	Default history initialization function
308 */
309private void
310history_def_init(p, n)
311    ptr_t *p;
312    int n;
313{
314    history_t *h = (history_t *) h_malloc(sizeof(history_t));
315    if (n <= 0)
316	n = 0;
317    h->eventno = 0;
318    h->cur = 0;
319    h->max = n;
320    h->list.next = h->list.prev = &h->list;
321    h->list.ev.str = NULL;
322    h->list.ev.num = 0;
323    h->cursor = &h->list;
324    *p = (ptr_t) h;
325}
326
327
328/* history_def_clear():
329 *	Default history cleanup function
330 */
331private void
332history_def_clear(p)
333    ptr_t p;
334{
335    history_t *h = (history_t *) p;
336
337    while (h->list.prev != &h->list)
338	history_def_delete(h, h->list.prev);
339    h->eventno = 0;
340    h->cur = 0;
341}
342
343/************************************************************************/
344
345/* history_init():
346 *	Initialization function.
347 */
348public History *
349history_init()
350{
351    History *h = (History *) h_malloc(sizeof(History));
352
353    history_def_init(&h->h_ref, 0);
354
355    h->h_next  = history_def_next;
356    h->h_first = history_def_first;
357    h->h_last  = history_def_last;
358    h->h_prev  = history_def_prev;
359    h->h_curr  = history_def_curr;
360    h->h_clear = history_def_clear;
361    h->h_enter = history_def_enter;
362    h->h_add   = history_def_add;
363
364    return h;
365}
366
367
368/* history_end():
369 *	clean up history;
370 */
371public void
372history_end(h)
373    History *h;
374{
375    if (h->h_next == history_def_next)
376	history_def_clear(h->h_ref);
377}
378
379
380
381/* history_set_num():
382 *	Set history number of events
383 */
384private int
385history_set_num(h, num)
386    History *h;
387    int num;
388{
389    if (h->h_next != history_def_next || num < 0)
390	return -1;
391    history_def_set(h->h_ref, num);
392    return 0;
393}
394
395
396/* history_set_fun():
397 *	Set history functions
398 */
399private int
400history_set_fun(h, nh)
401    History *h, *nh;
402{
403    if (nh->h_first == NULL || nh->h_next == NULL ||
404	nh->h_last == NULL  || nh->h_prev == NULL || nh->h_curr == NULL ||
405	nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
406	nh->h_ref == NULL) {
407	if (h->h_next != history_def_next) {
408	    history_def_init(&h->h_ref, 0);
409	    h->h_first = history_def_first;
410	    h->h_next  = history_def_next;
411	    h->h_last  = history_def_last;
412	    h->h_prev  = history_def_prev;
413	    h->h_curr  = history_def_curr;
414	    h->h_clear = history_def_clear;
415	    h->h_enter = history_def_enter;
416	    h->h_add   = history_def_add;
417	}
418	return -1;
419    }
420
421    if (h->h_next == history_def_next)
422	history_def_clear(h->h_ref);
423
424    h->h_first = nh->h_first;
425    h->h_next  = nh->h_next;
426    h->h_last  = nh->h_last;
427    h->h_prev  = nh->h_prev;
428    h->h_curr  = nh->h_curr;
429    h->h_clear = nh->h_clear;
430    h->h_enter = nh->h_enter;
431    h->h_add   = nh->h_add;
432    return 0;
433}
434
435
436/* history_load():
437 *	History load function
438 */
439private int
440history_load(h, fname)
441    History *h;
442    const char *fname;
443{
444    FILE *fp;
445    char *line;
446    size_t sz;
447    int i = -1;
448
449    if ((fp = fopen(fname, "r")) == NULL)
450	return i;
451
452    if ((line = fgetln(fp, &sz)) == NULL)
453	goto done;
454
455    if (strncmp(line, hist_cookie, sz) != 0)
456	goto done;
457
458    for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
459	char c = line[sz];
460	line[sz] = '\0';
461	HENTER(h, line);
462	line[sz] = c;
463    }
464
465done:
466    (void) fclose(fp);
467    return i;
468}
469
470
471/* history_save():
472 *	History save function
473 */
474private int
475history_save(h, fname)
476    History *h;
477    const char *fname;
478{
479    FILE *fp;
480    const HistEvent *ev;
481    int i = 0;
482
483    if ((fp = fopen(fname, "w")) == NULL)
484	return -1;
485
486    (void) fputs(hist_cookie, fp);
487    for (ev = HLAST(h); ev != NULL; ev = HPREV(h), i++)
488	(void) fprintf(fp, "%s", ev->str);
489    (void) fclose(fp);
490    return i;
491}
492
493
494/* history_prev_event():
495 *	Find the previous event, with number given
496 */
497private const HistEvent *
498history_prev_event(h, num)
499    History *h;
500    int num;
501{
502    const HistEvent *ev;
503    for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
504	if (ev->num == num)
505	    return ev;
506    return NULL;
507}
508
509
510/* history_next_event():
511 *	Find the next event, with number given
512 */
513private const HistEvent *
514history_next_event(h, num)
515    History *h;
516    int num;
517{
518    const HistEvent *ev;
519    for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
520	if (ev->num == num)
521	    return ev;
522    return NULL;
523}
524
525
526/* history_prev_string():
527 *	Find the previous event beginning with string
528 */
529private const HistEvent *
530history_prev_string(h, str)
531    History *h;
532    const char* str;
533{
534    const HistEvent *ev;
535    size_t len = strlen(str);
536
537    for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
538	if (strncmp(str, ev->str, len) == 0)
539	    return ev;
540    return NULL;
541}
542
543
544/* history_next_string():
545 *	Find the next event beginning with string
546 */
547private const HistEvent *
548history_next_string(h, str)
549    History *h;
550    const char* str;
551{
552    const HistEvent *ev;
553    size_t len = strlen(str);
554
555    for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
556	if (strncmp(str, ev->str, len) == 0)
557	    return ev;
558    return NULL;
559}
560
561
562/* history():
563 *	User interface to history functions.
564 */
565const HistEvent *
566#if __STDC__
567history(History *h, int fun, ...)
568#else
569history(va_alist)
570    va_dcl
571#endif
572{
573    va_list va;
574    const HistEvent *ev = NULL;
575    const char *str;
576    static HistEvent sev = { 0, "" };
577
578#if __STDC__
579    va_start(va, fun);
580#else
581    History *h;
582    int fun;
583    va_start(va);
584    h = va_arg(va, History *);
585    fun = va_arg(va, int);
586#endif
587
588    switch (fun) {
589    case H_ADD:
590	str = va_arg(va, const char *);
591	ev = HADD(h, str);
592	break;
593
594    case H_ENTER:
595	str = va_arg(va, const char *);
596	ev = HENTER(h, str);
597	break;
598
599    case H_FIRST:
600	ev = HFIRST(h);
601	break;
602
603    case H_NEXT:
604	ev = HNEXT(h);
605	break;
606
607    case H_LAST:
608	ev = HLAST(h);
609	break;
610
611    case H_PREV:
612	ev = HPREV(h);
613	break;
614
615    case H_CURR:
616	ev = HCURR(h);
617	break;
618
619    case H_CLEAR:
620	HCLEAR(h);
621	break;
622
623    case H_LOAD:
624	sev.num = history_load(h, va_arg(va, const char *));
625	ev = &sev;
626	break;
627
628    case H_SAVE:
629	sev.num = history_save(h, va_arg(va, const char *));
630	ev = &sev;
631	break;
632
633    case H_PREV_EVENT:
634	ev = history_prev_event(h, va_arg(va, int));
635	break;
636
637    case H_NEXT_EVENT:
638	ev = history_next_event(h, va_arg(va, int));
639	break;
640
641    case H_PREV_STR:
642	ev = history_prev_string(h, va_arg(va, const char*));
643	break;
644
645    case H_NEXT_STR:
646	ev = history_next_string(h, va_arg(va, const char*));
647	break;
648
649    case H_EVENT:
650	if (history_set_num(h, va_arg(va, int)) == 0) {
651	    sev.num = -1;
652	    ev = &sev;
653	}
654	break;
655
656    case H_FUNC:
657	{
658	    History hf;
659	    hf.h_ref   = va_arg(va, ptr_t);
660	    hf.h_first = va_arg(va, history_gfun_t);
661	    hf.h_next  = va_arg(va, history_gfun_t);
662	    hf.h_last  = va_arg(va, history_gfun_t);
663	    hf.h_prev  = va_arg(va, history_gfun_t);
664	    hf.h_curr  = va_arg(va, history_gfun_t);
665	    hf.h_clear = va_arg(va, history_vfun_t);
666	    hf.h_enter = va_arg(va, history_efun_t);
667	    hf.h_add   = va_arg(va, history_efun_t);
668
669	    if (history_set_fun(h, &hf) == 0) {
670		sev.num = -1;
671		ev = &sev;
672	    }
673	}
674	break;
675
676    case H_END:
677	history_end(h);
678	break;
679
680    default:
681	break;
682    }
683    va_end(va);
684    return ev;
685}
686