Home | History | Annotate | Line # | Download | only in libedit
history.c revision 1.5
      1 /*	$NetBSD: history.c,v 1.5 1997/04/11 17:52:46 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1992, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Christos Zoulas of Cornell University.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the University of
     21  *	California, Berkeley and its contributors.
     22  * 4. Neither the name of the University nor the names of its contributors
     23  *    may be used to endorse or promote products derived from this software
     24  *    without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     36  * SUCH DAMAGE.
     37  */
     38 
     39 #if !defined(lint) && !defined(SCCSID)
     40 static 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 #ifdef __STDC__
     51 #include <stdarg.h>
     52 #else
     53 #include <varargs.h>
     54 #endif
     55 
     56 static const char hist_cookie[] = "_HiStOrY_V1_\n";
     57 
     58 #include "histedit.h"
     59 
     60 typedef const HistEvent *	(*history_gfun_t) __P((ptr_t));
     61 typedef const HistEvent *	(*history_efun_t) __P((ptr_t, const char *));
     62 typedef void 			(*history_vfun_t) __P((ptr_t));
     63 
     64 struct 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 
     89 private int		 history_set_num	__P((History *, int));
     90 private int		 history_set_fun	__P((History *, History *));
     91 private int 		 history_load		__P((History *, const char *));
     92 private int 		 history_save		__P((History *, const char *));
     93 private const HistEvent *history_prev_event	__P((History *, int));
     94 private const HistEvent *history_next_event	__P((History *, int));
     95 private const HistEvent *history_next_string	__P((History *, const char *));
     96 private const HistEvent *history_prev_string	__P((History *, const char *));
     97 
     98 
     99 /***********************************************************************/
    100 
    101 /*
    102  * Builtin- history implementation
    103  */
    104 typedef 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 
    110 typedef 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 
    118 private const HistEvent *history_def_first  __P((ptr_t));
    119 private const HistEvent *history_def_last   __P((ptr_t));
    120 private const HistEvent *history_def_next   __P((ptr_t));
    121 private const HistEvent *history_def_prev   __P((ptr_t));
    122 private const HistEvent *history_def_curr   __P((ptr_t));
    123 private const HistEvent *history_def_enter  __P((ptr_t, const char *));
    124 private const HistEvent *history_def_add    __P((ptr_t, const char *));
    125 private void             history_def_init   __P((ptr_t *, int));
    126 private void             history_def_clear  __P((ptr_t));
    127 private const HistEvent *history_def_insert __P((history_t *, const char *));
    128 private 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  */
    136 private const HistEvent *
    137 history_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  */
    151 private const HistEvent *
    152 history_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  */
    166 private const HistEvent *
    167 history_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  */
    187 private const HistEvent *
    188 history_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  */
    208 private const HistEvent *
    209 history_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 /* history_def_add():
    221  *	Append string to element
    222  */
    223 private const HistEvent *
    224 history_def_add(p, str)
    225     ptr_t p;
    226     const char *str;
    227 {
    228     history_t *h = (history_t *) p;
    229     size_t len;
    230     char *s;
    231 
    232     if (h->cursor == &h->list)
    233 	return (history_def_enter(p, str));
    234     len = strlen(h->cursor->ev.str) + strlen(str) + 1;
    235     s = (char *) h_malloc(len);
    236     (void)strcpy(s, h->cursor->ev.str);	/* XXX strcpy is safe */
    237     (void)strcat(s, str);			/* XXX strcat is safe */
    238     h_free((ptr_t) h->cursor->ev.str);
    239     h->cursor->ev.str = s;
    240     return &h->cursor->ev;
    241 }
    242 
    243 
    244 /* history_def_delete():
    245  *	Delete element hp of the h list
    246  */
    247 private void
    248 history_def_delete(h, hp)
    249     history_t *h;
    250     hentry_t *hp;
    251 {
    252     if (hp == &h->list)
    253 	abort();
    254     hp->prev->next = hp->next;
    255     hp->next->prev = hp->prev;
    256     h_free((ptr_t) hp->ev.str);
    257     h_free(hp);
    258     h->cur--;
    259 }
    260 
    261 
    262 /* history_def_insert():
    263  *	Insert element with string str in the h list
    264  */
    265 private const HistEvent *
    266 history_def_insert(h, str)
    267     history_t *h;
    268     const char *str;
    269 {
    270     h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
    271     h->cursor->ev.str = strdup(str);
    272     h->cursor->next = h->list.next;
    273     h->cursor->prev = &h->list;
    274     h->list.next->prev = h->cursor;
    275     h->list.next = h->cursor;
    276     h->cur++;
    277 
    278     return &h->cursor->ev;
    279 }
    280 
    281 
    282 /* history_def_enter():
    283  *	Default function to enter an item in the history
    284  */
    285 private const HistEvent *
    286 history_def_enter(p, str)
    287     ptr_t p;
    288     const char *str;
    289 {
    290     history_t *h = (history_t *) p;
    291     const HistEvent *ev;
    292 
    293 
    294     ev = history_def_insert(h, str);
    295     ((HistEvent*) ev)->num = ++h->eventno;
    296 
    297     /*
    298      * Always keep at least one entry.
    299      * This way we don't have to check for the empty list.
    300      */
    301     while (h->cur > h->max + 1)
    302 	history_def_delete(h, h->list.prev);
    303     return ev;
    304 }
    305 
    306 
    307 /* history_def_init():
    308  *	Default history initialization function
    309  */
    310 private void
    311 history_def_init(p, n)
    312     ptr_t *p;
    313     int n;
    314 {
    315     history_t *h = (history_t *) h_malloc(sizeof(history_t));
    316     if (n <= 0)
    317 	n = 0;
    318     h->eventno = 0;
    319     h->cur = 0;
    320     h->max = n;
    321     h->list.next = h->list.prev = &h->list;
    322     h->list.ev.str = NULL;
    323     h->list.ev.num = 0;
    324     h->cursor = &h->list;
    325     *p = (ptr_t) h;
    326 }
    327 
    328 
    329 /* history_def_clear():
    330  *	Default history cleanup function
    331  */
    332 private void
    333 history_def_clear(p)
    334     ptr_t p;
    335 {
    336     history_t *h = (history_t *) p;
    337 
    338     while (h->list.prev != &h->list)
    339 	history_def_delete(h, h->list.prev);
    340     h->eventno = 0;
    341     h->cur = 0;
    342 }
    343 
    344 
    345 
    346 
    347 /************************************************************************/
    348 
    349 /* history_init():
    350  *	Initialization function.
    351  */
    352 public History *
    353 history_init()
    354 {
    355     History *h = (History *) h_malloc(sizeof(History));
    356 
    357     history_def_init(&h->h_ref, 0);
    358 
    359     h->h_next  = history_def_next;
    360     h->h_first = history_def_first;
    361     h->h_last  = history_def_last;
    362     h->h_prev  = history_def_prev;
    363     h->h_curr  = history_def_curr;
    364     h->h_clear = history_def_clear;
    365     h->h_enter = history_def_enter;
    366     h->h_add   = history_def_add;
    367 
    368     return h;
    369 }
    370 
    371 
    372 /* history_end():
    373  *	clean up history;
    374  */
    375 public void
    376 history_end(h)
    377     History *h;
    378 {
    379     if (h->h_next == history_def_next)
    380 	history_def_clear(h->h_ref);
    381 }
    382 
    383 
    384 
    385 /* history_set_num():
    386  *	Set history number of events
    387  */
    388 private int
    389 history_set_num(h, num)
    390     History *h;
    391     int num;
    392 {
    393     if (h->h_next != history_def_next || num < 0)
    394 	return -1;
    395     history_def_set(h->h_ref, num);
    396     return 0;
    397 }
    398 
    399 
    400 /* history_set_fun():
    401  *	Set history functions
    402  */
    403 private int
    404 history_set_fun(h, nh)
    405     History *h, *nh;
    406 {
    407     if (nh->h_first == NULL || nh->h_next == NULL ||
    408         nh->h_last == NULL  || nh->h_prev == NULL || nh->h_curr == NULL ||
    409 	nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
    410 	nh->h_ref == NULL) {
    411 	if (h->h_next != history_def_next) {
    412 	    history_def_init(&h->h_ref, 0);
    413 	    h->h_first = history_def_first;
    414 	    h->h_next  = history_def_next;
    415 	    h->h_last  = history_def_last;
    416 	    h->h_prev  = history_def_prev;
    417 	    h->h_curr  = history_def_curr;
    418 	    h->h_clear = history_def_clear;
    419 	    h->h_enter = history_def_enter;
    420 	    h->h_add   = history_def_add;
    421 	}
    422 	return -1;
    423     }
    424 
    425     if (h->h_next == history_def_next)
    426 	history_def_clear(h->h_ref);
    427 
    428     h->h_first = nh->h_first;
    429     h->h_next  = nh->h_next;
    430     h->h_last  = nh->h_last;
    431     h->h_prev  = nh->h_prev;
    432     h->h_curr  = nh->h_curr;
    433     h->h_clear = nh->h_clear;
    434     h->h_enter = nh->h_enter;
    435     h->h_add   = nh->h_add;
    436 
    437     return 0;
    438 }
    439 
    440 
    441 /* history_load():
    442  *	History load function
    443  */
    444 private int
    445 history_load(h, fname)
    446     History *h;
    447     const char *fname;
    448 {
    449     FILE *fp;
    450     char *line;
    451     size_t sz;
    452     int i = -1;
    453 
    454     if ((fp = fopen(fname, "r")) == NULL)
    455 	return i;
    456 
    457     if ((line = fgetln(fp, &sz)) == NULL)
    458 	goto done;
    459 
    460     if (strncmp(line, hist_cookie, sz) != 0)
    461 	goto done;
    462 
    463     for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
    464 	char c = line[sz];
    465 	line[sz] = '\0';
    466 	HENTER(h, line);
    467 	line[sz] = c;
    468     }
    469 
    470 done:
    471     (void) fclose(fp);
    472     return i;
    473 }
    474 
    475 
    476 /* history_save():
    477  *	History save function
    478  */
    479 private int
    480 history_save(h, fname)
    481     History *h;
    482     const char *fname;
    483 {
    484     FILE *fp;
    485     const HistEvent *ev;
    486     int i = 0;
    487 
    488     if ((fp = fopen(fname, "w")) == NULL)
    489 	return -1;
    490 
    491     (void) fputs(hist_cookie, fp);
    492     for (ev = HLAST(h); ev != NULL; ev = HPREV(h), i++)
    493 	(void) fprintf(fp, "%s", ev->str);
    494     (void) fclose(fp);
    495     return i;
    496 }
    497 
    498 
    499 /* history_prev_event():
    500  *	Find the previous event, with number given
    501  */
    502 private const HistEvent *
    503 history_prev_event(h, num)
    504     History *h;
    505     int num;
    506 {
    507     const HistEvent *ev;
    508     for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
    509 	if (ev->num == num)
    510 	    return ev;
    511     return NULL;
    512 }
    513 
    514 
    515 /* history_next_event():
    516  *	Find the next event, with number given
    517  */
    518 private const HistEvent *
    519 history_next_event(h, num)
    520     History *h;
    521     int num;
    522 {
    523     const HistEvent *ev;
    524     for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
    525 	if (ev->num == num)
    526 	    return ev;
    527     return NULL;
    528 }
    529 
    530 
    531 /* history_prev_string():
    532  *	Find the previous event beginning with string
    533  */
    534 private const HistEvent *
    535 history_prev_string(h, str)
    536     History *h;
    537     const char* str;
    538 {
    539     const HistEvent *ev;
    540     size_t len = strlen(str);
    541 
    542     for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
    543 	if (strncmp(str, ev->str, len) == 0)
    544 	    return ev;
    545     return NULL;
    546 }
    547 
    548 
    549 
    550 
    551 /* history_next_string():
    552  *	Find the next event beginning with string
    553  */
    554 private const HistEvent *
    555 history_next_string(h, str)
    556     History *h;
    557     const char* str;
    558 {
    559     const HistEvent *ev;
    560     size_t len = strlen(str);
    561 
    562     for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
    563 	if (strncmp(str, ev->str, len) == 0)
    564 	    return ev;
    565     return NULL;
    566 }
    567 
    568 
    569 /* history():
    570  *	User interface to history functions.
    571  */
    572 const HistEvent *
    573 #ifdef __STDC__
    574 history(History *h, int fun, ...)
    575 #else
    576 history(va_alist)
    577     va_dcl
    578 #endif
    579 {
    580     va_list va;
    581     const HistEvent *ev = NULL;
    582     const char *str;
    583     static HistEvent sev = { 0, "" };
    584 
    585 #ifdef __STDC__
    586     va_start(va, fun);
    587 #else
    588     History *h;
    589     int fun;
    590     va_start(va);
    591     h = va_arg(va, History *);
    592     fun = va_arg(va, int);
    593 #endif
    594 
    595     switch (fun) {
    596     case H_ADD:
    597 	str = va_arg(va, const char *);
    598 	ev = HADD(h, str);
    599 	break;
    600 
    601     case H_ENTER:
    602 	str = va_arg(va, const char *);
    603 	ev = HENTER(h, str);
    604 	break;
    605 
    606     case H_FIRST:
    607 	ev = HFIRST(h);
    608 	break;
    609 
    610     case H_NEXT:
    611 	ev = HNEXT(h);
    612 	break;
    613 
    614     case H_LAST:
    615 	ev = HLAST(h);
    616 	break;
    617 
    618     case H_PREV:
    619 	ev = HPREV(h);
    620 	break;
    621 
    622     case H_CURR:
    623 	ev = HCURR(h);
    624 	break;
    625 
    626     case H_CLEAR:
    627 	HCLEAR(h);
    628 	break;
    629 
    630     case H_LOAD:
    631 	sev.num = history_load(h, va_arg(va, const char *));
    632 	ev = &sev;
    633 	break;
    634 
    635     case H_SAVE:
    636 	sev.num = history_save(h, va_arg(va, const char *));
    637 	ev = &sev;
    638 	break;
    639 
    640     case H_PREV_EVENT:
    641 	ev = history_prev_event(h, va_arg(va, int));
    642 	break;
    643 
    644     case H_NEXT_EVENT:
    645 	ev = history_next_event(h, va_arg(va, int));
    646 	break;
    647 
    648     case H_PREV_STR:
    649 	ev = history_prev_string(h, va_arg(va, const char*));
    650 	break;
    651 
    652     case H_NEXT_STR:
    653 	ev = history_next_string(h, va_arg(va, const char*));
    654 	break;
    655 
    656     case H_EVENT:
    657 	if (history_set_num(h, va_arg(va, int)) == 0) {
    658 	    sev.num = -1;
    659 	    ev = &sev;
    660 	}
    661 	break;
    662 
    663     case H_FUNC:
    664 	{
    665 	    History hf;
    666 	    hf.h_ref   = va_arg(va, ptr_t);
    667 	    hf.h_first = va_arg(va, history_gfun_t);
    668 	    hf.h_next  = va_arg(va, history_gfun_t);
    669 	    hf.h_last  = va_arg(va, history_gfun_t);
    670 	    hf.h_prev  = va_arg(va, history_gfun_t);
    671 	    hf.h_curr  = va_arg(va, history_gfun_t);
    672 	    hf.h_clear = va_arg(va, history_vfun_t);
    673 	    hf.h_enter = va_arg(va, history_efun_t);
    674 	    hf.h_add   = va_arg(va, history_efun_t);
    675 
    676 	    if (history_set_fun(h, &hf) == 0) {
    677 		sev.num = -1;
    678 		ev = &sev;
    679 	    }
    680 	}
    681 	break;
    682 
    683     case H_END:
    684 	history_end(h);
    685 	break;
    686 
    687     default:
    688 	break;
    689     }
    690     va_end(va);
    691     return ev;
    692 }
    693