Home | History | Annotate | Line # | Download | only in libedit
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)
     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 /* history_def_add():
    219  *	Append string to element
    220  */
    221 private const HistEvent *
    222 history_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  */
    245 private void
    246 history_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  */
    263 private const HistEvent *
    264 history_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  */
    283 private const HistEvent *
    284 history_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  */
    308 private void
    309 history_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  */
    330 private void
    331 history_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  */
    350 public History *
    351 history_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  */
    373 public void
    374 history_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  */
    386 private int
    387 history_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  */
    401 private int
    402 history_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  */
    442 private int
    443 history_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 
    468 done:
    469     (void) fclose(fp);
    470     return i;
    471 }
    472 
    473 
    474 /* history_save():
    475  *	History save function
    476  */
    477 private int
    478 history_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  */
    500 private const HistEvent *
    501 history_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  */
    516 private const HistEvent *
    517 history_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  */
    532 private const HistEvent *
    533 history_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  */
    552 private const HistEvent *
    553 history_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  */
    570 const HistEvent *
    571 #if __STDC__
    572 history(History *h, int fun, ...)
    573 #else
    574 history(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