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