history.c revision 84201
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#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/lib/libedit/history.c 84201 2001-09-30 21:21:36Z dillon $");
39#if !defined(lint) && !defined(SCCSID)
40static char sccsid[] = "@(#)history.c	8.1 (Berkeley) 6/4/93";
41#endif /* not lint && not SCCSID */
42
43/*
44 * hist.c: History access functions
45 */
46#include "sys.h"
47
48#include <string.h>
49#include <stdlib.h>
50#if __STDC__
51#include <stdarg.h>
52#else
53#include <varargs.h>
54#endif
55
56static const char hist_cookie[] = "_HiStOrY_V1_\n";
57
58#include "histedit.h"
59
60typedef const HistEvent *	(*history_gfun_t) __P((ptr_t));
61typedef const HistEvent *	(*history_efun_t) __P((ptr_t, const char *));
62typedef void			(*history_vfun_t) __P((ptr_t));
63
64struct history {
65    ptr_t	   h_ref;		/* Argument for history fcns	*/
66    history_gfun_t h_first;		/* Get the first element	*/
67    history_gfun_t h_next;		/* Get the next element		*/
68    history_gfun_t h_last;		/* Get the last element		*/
69    history_gfun_t h_prev;		/* Get the previous element	*/
70    history_gfun_t h_curr;		/* Get the current element	*/
71    history_vfun_t h_clear;		/* Clear the history list       */
72    history_efun_t h_enter;		/* Add an element		*/
73    history_efun_t h_add;		/* Append to an element		*/
74};
75
76#define	HNEXT(h)  	(*(h)->h_next)((h)->h_ref)
77#define	HFIRST(h) 	(*(h)->h_first)((h)->h_ref)
78#define	HPREV(h)  	(*(h)->h_prev)((h)->h_ref)
79#define	HLAST(h) 	(*(h)->h_last)((h)->h_ref)
80#define	HCURR(h) 	(*(h)->h_curr)((h)->h_ref)
81#define HCLEAR(h)	(*(h)->h_clear)((h)->h_ref)
82#define	HENTER(h, str)	(*(h)->h_enter)((h)->h_ref, str)
83#define	HADD(h, str)	(*(h)->h_add)((h)->h_ref, str)
84
85#define h_malloc(a)	malloc(a)
86#define h_free(a)	free(a)
87
88
89private int		 history_set_num	__P((History *, int));
90private int		 history_set_fun	__P((History *, History *));
91private int		 history_load		__P((History *, const char *));
92private int		 history_save		__P((History *, const char *));
93private const HistEvent *history_prev_event	__P((History *, int));
94private const HistEvent *history_next_event	__P((History *, int));
95private const HistEvent *history_next_string	__P((History *, const char *));
96private const HistEvent *history_prev_string	__P((History *, const char *));
97
98
99/***********************************************************************/
100
101/*
102 * Builtin- history implementation
103 */
104typedef struct hentry_t {
105    HistEvent ev;		/* What we return		*/
106    struct hentry_t *next;	/* Next entry			*/
107    struct hentry_t *prev;	/* Previous entry		*/
108} hentry_t;
109
110typedef struct history_t {
111    hentry_t  list;		/* Fake list header element	*/
112    hentry_t *cursor;		/* Current element in the list	*/
113    int	max;			/* Maximum number of events	*/
114    int cur;			/* Current number of events	*/
115    int	eventno;		/* Current event number		*/
116} history_t;
117
118private const HistEvent *history_def_first  __P((ptr_t));
119private const HistEvent *history_def_last   __P((ptr_t));
120private const HistEvent *history_def_next   __P((ptr_t));
121private const HistEvent *history_def_prev   __P((ptr_t));
122private const HistEvent *history_def_curr   __P((ptr_t));
123private const HistEvent *history_def_enter  __P((ptr_t, const char *));
124private const HistEvent *history_def_add    __P((ptr_t, const char *));
125private void             history_def_init   __P((ptr_t *, int));
126private void             history_def_clear  __P((ptr_t));
127private const HistEvent *history_def_insert __P((history_t *, const char *));
128private void             history_def_delete __P((history_t *, hentry_t *));
129
130#define history_def_set(p, num)	(void) (((history_t *) p)->max = (num))
131
132
133/* history_def_first():
134 *	Default function to return the first event in the history.
135 */
136private const HistEvent *
137history_def_first(p)
138    ptr_t p;
139{
140    history_t *h = (history_t *) p;
141    h->cursor = h->list.next;
142    if (h->cursor != &h->list)
143	return &h->cursor->ev;
144    else
145	return NULL;
146}
147
148/* history_def_last():
149 *	Default function to return the last event in the history.
150 */
151private const HistEvent *
152history_def_last(p)
153    ptr_t p;
154{
155    history_t *h = (history_t *) p;
156    h->cursor = h->list.prev;
157    if (h->cursor != &h->list)
158	return &h->cursor->ev;
159    else
160	return NULL;
161}
162
163/* history_def_next():
164 *	Default function to return the next event in the history.
165 */
166private const HistEvent *
167history_def_next(p)
168    ptr_t p;
169{
170    history_t *h = (history_t *) p;
171
172    if (h->cursor != &h->list)
173	h->cursor = h->cursor->next;
174    else
175	return NULL;
176
177    if (h->cursor != &h->list)
178	return &h->cursor->ev;
179    else
180	return NULL;
181}
182
183
184/* history_def_prev():
185 *	Default function to return the previous event in the history.
186 */
187private const HistEvent *
188history_def_prev(p)
189    ptr_t p;
190{
191    history_t *h = (history_t *) p;
192
193    if (h->cursor != &h->list)
194	h->cursor = h->cursor->prev;
195    else
196	return NULL;
197
198    if (h->cursor != &h->list)
199	return &h->cursor->ev;
200    else
201	return NULL;
202}
203
204
205/* history_def_curr():
206 *	Default function to return the current event in the history.
207 */
208private const HistEvent *
209history_def_curr(p)
210    ptr_t p;
211{
212    history_t *h = (history_t *) p;
213
214    if (h->cursor != &h->list)
215	return &h->cursor->ev;
216    else
217	return NULL;
218}
219
220
221/* history_def_add():
222 *	Append string to element
223 */
224private const HistEvent *
225history_def_add(p, str)
226    ptr_t p;
227    const char *str;
228{
229    history_t *h = (history_t *) p;
230    size_t len;
231    char *s;
232
233    if (h->cursor == &h->list)
234	return (history_def_enter(p, str));
235    len = strlen(h->cursor->ev.str) + strlen(str) + 1;
236    s = (char *) h_malloc(len);
237    (void)strcpy(s, h->cursor->ev.str);	/* XXX strcpy is safe */
238    (void)strcat(s, str);			/* XXX strcat is safe */
239    h_free((ptr_t) h->cursor->ev.str);
240    h->cursor->ev.str = s;
241    return &h->cursor->ev;
242}
243
244
245/* history_def_delete():
246 *	Delete element hp of the h list
247 */
248private void
249history_def_delete(h, hp)
250    history_t *h;
251    hentry_t *hp;
252{
253    if (hp == &h->list)
254	abort();
255    hp->prev->next = hp->next;
256    hp->next->prev = hp->prev;
257    h_free((ptr_t) hp->ev.str);
258    h_free(hp);
259    h->cur--;
260}
261
262
263/* history_def_insert():
264 *	Insert element with string str in the h list
265 */
266private const HistEvent *
267history_def_insert(h, str)
268    history_t *h;
269    const char *str;
270{
271    h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
272    h->cursor->ev.str = strdup(str);
273    h->cursor->next = h->list.next;
274    h->cursor->prev = &h->list;
275    h->list.next->prev = h->cursor;
276    h->list.next = h->cursor;
277    h->cur++;
278
279    return &h->cursor->ev;
280}
281
282
283/* history_def_enter():
284 *	Default function to enter an item in the history
285 */
286private const HistEvent *
287history_def_enter(p, str)
288    ptr_t p;
289    const char *str;
290{
291    history_t *h = (history_t *) p;
292    const HistEvent *ev;
293
294
295    ev = history_def_insert(h, str);
296    ((HistEvent*) ev)->num = ++h->eventno;
297
298    /*
299     * Always keep at least one entry.
300     * This way we don't have to check for the empty list.
301     */
302    while (h->cur > h->max + 1)
303	history_def_delete(h, h->list.prev);
304    return ev;
305}
306
307
308/* history_def_init():
309 *	Default history initialization function
310 */
311private void
312history_def_init(p, n)
313    ptr_t *p;
314    int n;
315{
316    history_t *h = (history_t *) h_malloc(sizeof(history_t));
317    if (n <= 0)
318	n = 0;
319    h->eventno = 0;
320    h->cur = 0;
321    h->max = n;
322    h->list.next = h->list.prev = &h->list;
323    h->list.ev.str = NULL;
324    h->list.ev.num = 0;
325    h->cursor = &h->list;
326    *p = (ptr_t) h;
327}
328
329
330/* history_def_clear():
331 *	Default history cleanup function
332 */
333private void
334history_def_clear(p)
335    ptr_t p;
336{
337    history_t *h = (history_t *) p;
338
339    while (h->list.prev != &h->list)
340	history_def_delete(h, h->list.prev);
341    h->eventno = 0;
342    h->cur = 0;
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    return 0;
435}
436
437
438/* history_load():
439 *	History load function
440 */
441private int
442history_load(h, fname)
443    History *h;
444    const char *fname;
445{
446    FILE *fp;
447    char *line;
448    size_t sz;
449    int i = -1;
450
451    if ((fp = fopen(fname, "r")) == NULL)
452	return i;
453
454    if ((line = fgetln(fp, &sz)) == NULL)
455	goto done;
456
457    if (strncmp(line, hist_cookie, sz) != 0)
458	goto done;
459
460    for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
461	char c = line[sz];
462	line[sz] = '\0';
463	HENTER(h, line);
464	line[sz] = c;
465    }
466
467done:
468    (void) fclose(fp);
469    return i;
470}
471
472
473/* history_save():
474 *	History save function
475 */
476private int
477history_save(h, fname)
478    History *h;
479    const char *fname;
480{
481    FILE *fp;
482    const HistEvent *ev;
483    int i = 0;
484
485    if ((fp = fopen(fname, "w")) == NULL)
486	return -1;
487
488    (void) fputs(hist_cookie, fp);
489    for (ev = HLAST(h); ev != NULL; ev = HPREV(h), i++)
490	(void) fprintf(fp, "%s", ev->str);
491    (void) fclose(fp);
492    return i;
493}
494
495
496/* history_prev_event():
497 *	Find the previous event, with number given
498 */
499private const HistEvent *
500history_prev_event(h, num)
501    History *h;
502    int num;
503{
504    const HistEvent *ev;
505    for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
506	if (ev->num == num)
507	    return ev;
508    return NULL;
509}
510
511
512/* history_next_event():
513 *	Find the next event, with number given
514 */
515private const HistEvent *
516history_next_event(h, num)
517    History *h;
518    int num;
519{
520    const HistEvent *ev;
521    for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
522	if (ev->num == num)
523	    return ev;
524    return NULL;
525}
526
527
528/* history_prev_string():
529 *	Find the previous event beginning with string
530 */
531private const HistEvent *
532history_prev_string(h, str)
533    History *h;
534    const char* str;
535{
536    const HistEvent *ev;
537    size_t len = strlen(str);
538
539    for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
540	if (strncmp(str, ev->str, len) == 0)
541	    return ev;
542    return NULL;
543}
544
545
546/* history_next_string():
547 *	Find the next event beginning with string
548 */
549private const HistEvent *
550history_next_string(h, str)
551    History *h;
552    const char* str;
553{
554    const HistEvent *ev;
555    size_t len = strlen(str);
556
557    for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
558	if (strncmp(str, ev->str, len) == 0)
559	    return ev;
560    return NULL;
561}
562
563
564/* history():
565 *	User interface to history functions.
566 */
567const HistEvent *
568#if __STDC__
569history(History *h, int fun, ...)
570#else
571history(va_alist)
572    va_dcl
573#endif
574{
575    va_list va;
576    const HistEvent *ev = NULL;
577    const char *str;
578    static HistEvent sev = { 0, "" };
579
580#if __STDC__
581    va_start(va, fun);
582#else
583    History *h;
584    int fun;
585    va_start(va);
586    h = va_arg(va, History *);
587    fun = va_arg(va, int);
588#endif
589
590    switch (fun) {
591    case H_ADD:
592	str = va_arg(va, const char *);
593	ev = HADD(h, str);
594	break;
595
596    case H_ENTER:
597	str = va_arg(va, const char *);
598	ev = HENTER(h, str);
599	break;
600
601    case H_FIRST:
602	ev = HFIRST(h);
603	break;
604
605    case H_NEXT:
606	ev = HNEXT(h);
607	break;
608
609    case H_LAST:
610	ev = HLAST(h);
611	break;
612
613    case H_PREV:
614	ev = HPREV(h);
615	break;
616
617    case H_CURR:
618	ev = HCURR(h);
619	break;
620
621    case H_CLEAR:
622	HCLEAR(h);
623	break;
624
625    case H_LOAD:
626	sev.num = history_load(h, va_arg(va, const char *));
627	ev = &sev;
628	break;
629
630    case H_SAVE:
631	sev.num = history_save(h, va_arg(va, const char *));
632	ev = &sev;
633	break;
634
635    case H_PREV_EVENT:
636	ev = history_prev_event(h, va_arg(va, int));
637	break;
638
639    case H_NEXT_EVENT:
640	ev = history_next_event(h, va_arg(va, int));
641	break;
642
643    case H_PREV_STR:
644	ev = history_prev_string(h, va_arg(va, const char*));
645	break;
646
647    case H_NEXT_STR:
648	ev = history_next_string(h, va_arg(va, const char*));
649	break;
650
651    case H_EVENT:
652	if (history_set_num(h, va_arg(va, int)) == 0) {
653	    sev.num = -1;
654	    ev = &sev;
655	}
656	break;
657
658    case H_FUNC:
659	{
660	    History hf;
661	    hf.h_ref   = va_arg(va, ptr_t);
662	    hf.h_first = va_arg(va, history_gfun_t);
663	    hf.h_next  = va_arg(va, history_gfun_t);
664	    hf.h_last  = va_arg(va, history_gfun_t);
665	    hf.h_prev  = va_arg(va, history_gfun_t);
666	    hf.h_curr  = va_arg(va, history_gfun_t);
667	    hf.h_clear = va_arg(va, history_vfun_t);
668	    hf.h_enter = va_arg(va, history_efun_t);
669	    hf.h_add   = va_arg(va, history_efun_t);
670
671	    if (history_set_fun(h, &hf) == 0) {
672		sev.num = -1;
673		ev = &sev;
674	    }
675	}
676	break;
677
678    case H_END:
679	history_end(h);
680	break;
681
682    default:
683	break;
684    }
685    va_end(va);
686    return ev;
687}
688