Initial import from FreeBSD RELENG_4:
[games.git] / lib / libedit / history.c
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)
38 static 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 static const char hist_cookie[] = "_HiStOrY_V1_\n";
55
56 #include "histedit.h"
57
58 typedef const HistEvent *       (*history_gfun_t) __P((ptr_t));
59 typedef const HistEvent *       (*history_efun_t) __P((ptr_t, const char *));
60 typedef void                    (*history_vfun_t) __P((ptr_t));
61
62 struct 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
87 private int              history_set_num        __P((History *, int));
88 private int              history_set_fun        __P((History *, History *));
89 private int              history_load           __P((History *, const char *));
90 private int              history_save           __P((History *, const char *));
91 private const HistEvent *history_prev_event     __P((History *, int));
92 private const HistEvent *history_next_event     __P((History *, int));
93 private const HistEvent *history_next_string    __P((History *, const char *));
94 private const HistEvent *history_prev_string    __P((History *, const char *));
95
96
97 /***********************************************************************/
98
99 /*
100  * Builtin- history implementation
101  */
102 typedef 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
108 typedef 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
116 private const HistEvent *history_def_first  __P((ptr_t));
117 private const HistEvent *history_def_last   __P((ptr_t));
118 private const HistEvent *history_def_next   __P((ptr_t));
119 private const HistEvent *history_def_prev   __P((ptr_t));
120 private const HistEvent *history_def_curr   __P((ptr_t));
121 private const HistEvent *history_def_enter  __P((ptr_t, const char *));
122 private const HistEvent *history_def_add    __P((ptr_t, const char *));
123 private void             history_def_init   __P((ptr_t *, int));
124 private void             history_def_clear  __P((ptr_t));
125 private const HistEvent *history_def_insert __P((history_t *, const char *));
126 private 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  */
134 private const HistEvent *
135 history_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  */
149 private const HistEvent *
150 history_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  */
164 private const HistEvent *
165 history_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  */
185 private const HistEvent *
186 history_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  */
206 private const HistEvent *
207 history_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  */
222 private const HistEvent *
223 history_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  */
246 private void
247 history_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  */
264 private const HistEvent *
265 history_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  */
284 private const HistEvent *
285 history_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  */
309 private void
310 history_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  */
331 private void
332 history_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  */
348 public History *
349 history_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  */
371 public void
372 history_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  */
384 private int
385 history_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  */
399 private int
400 history_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  */
439 private int
440 history_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
465 done:
466     (void) fclose(fp);
467     return i;
468 }
469
470
471 /* history_save():
472  *      History save function
473  */
474 private int
475 history_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  */
497 private const HistEvent *
498 history_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  */
513 private const HistEvent *
514 history_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  */
529 private const HistEvent *
530 history_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  */
547 private const HistEvent *
548 history_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  */
565 const HistEvent *
566 #if __STDC__
567 history(History *h, int fun, ...)
568 #else
569 history(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 }