Home | History | Annotate | Line # | Download | only in libedit
history.c revision 1.11
      1 /*	$NetBSD: history.c,v 1.11 1998/12/12 19:52:51 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.11 1998/12/12 19:52:51 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 int	(*history_gfun_t) __P((ptr_t, HistEvent *));
     66 typedef int	(*history_efun_t) __P((ptr_t, HistEvent *, const char *));
     67 typedef void 	(*history_vfun_t) __P((ptr_t, HistEvent *));
     68 typedef int	(*history_sfun_t) __P((ptr_t, HistEvent *, const int));
     69 
     70 struct history {
     71     ptr_t	   h_ref;		/* Argument for history fcns	*/
     72     int 	   h_ent;		/* Last entry point for history	*/
     73     history_gfun_t h_first;		/* Get the first element	*/
     74     history_gfun_t h_next;		/* Get the next element		*/
     75     history_gfun_t h_last;		/* Get the last element		*/
     76     history_gfun_t h_prev;		/* Get the previous element	*/
     77     history_gfun_t h_curr;		/* Get the current element	*/
     78     history_sfun_t h_set;		/* Set the current element	*/
     79     history_vfun_t h_clear;		/* Clear the history list	*/
     80     history_efun_t h_enter;		/* Add an element		*/
     81     history_efun_t h_add;		/* Append to an element		*/
     82 };
     83 
     84 #define	HNEXT(h, ev)  		(*(h)->h_next)((h)->h_ref, ev)
     85 #define	HFIRST(h, ev) 		(*(h)->h_first)((h)->h_ref, ev)
     86 #define	HPREV(h, ev)  		(*(h)->h_prev)((h)->h_ref, ev)
     87 #define	HLAST(h, ev) 		(*(h)->h_last)((h)->h_ref, ev)
     88 #define	HCURR(h, ev) 		(*(h)->h_curr)((h)->h_ref, ev)
     89 #define	HSET(h, ev, n) 		(*(h)->h_set)((h)->h_ref, ev, n)
     90 #define	HCLEAR(h, ev) 		(*(h)->h_clear)((h)->h_ref, ev)
     91 #define	HENTER(h, ev, str)	(*(h)->h_enter)((h)->h_ref, ev, str)
     92 #define	HADD(h, ev, str)	(*(h)->h_add)((h)->h_ref, ev, str)
     93 
     94 #define h_malloc(a)	malloc(a)
     95 #define h_free(a)	free(a)
     96 
     97 
     98 private int	history_set_num		__P((History *, HistEvent *, int));
     99 private int	history_get_size	__P((History *, HistEvent *));
    100 private int	history_set_fun		__P((History *, History *));
    101 private int 	history_load		__P((History *, const char *));
    102 private int 	history_save		__P((History *, const char *));
    103 private int	history_prev_event	__P((History *, HistEvent *, int));
    104 private int	history_next_event	__P((History *, HistEvent *, int));
    105 private int	history_next_string	__P((History *, HistEvent *, const char *));
    106 private int	history_prev_string	__P((History *, HistEvent *, const char *));
    107 
    108 
    109 /***********************************************************************/
    110 
    111 /*
    112  * Builtin- history implementation
    113  */
    114 typedef struct hentry_t {
    115     HistEvent ev;		/* What we return		*/
    116     struct hentry_t *next;	/* Next entry			*/
    117     struct hentry_t *prev;	/* Previous entry		*/
    118 } hentry_t;
    119 
    120 typedef struct history_t {
    121     hentry_t  list;		/* Fake list header element	*/
    122     hentry_t *cursor;		/* Current element in the list	*/
    123     int	max;			/* Maximum number of events	*/
    124     int cur;			/* Current number of events	*/
    125     int	eventid;		/* For generation of unique event id	*/
    126 } history_t;
    127 
    128 private int	history_def_first  __P((ptr_t, HistEvent *));
    129 private int	history_def_last   __P((ptr_t, HistEvent *));
    130 private int	history_def_next   __P((ptr_t, HistEvent *));
    131 private int	history_def_prev   __P((ptr_t, HistEvent *));
    132 private int	history_def_curr   __P((ptr_t, HistEvent *));
    133 private int	history_def_set    __P((ptr_t, HistEvent *, const int n));
    134 private int	history_def_enter  __P((ptr_t, HistEvent *, const char *));
    135 private int	history_def_add    __P((ptr_t, HistEvent *, const char *));
    136 private void	history_def_init   __P((ptr_t *, HistEvent *, int));
    137 private void	history_def_clear  __P((ptr_t, HistEvent *));
    138 private int	history_def_insert __P((history_t *, HistEvent *,const char *));
    139 private void	history_def_delete __P((history_t *, HistEvent *, hentry_t *));
    140 
    141 #define history_def_setsize(p, num)(void) (((history_t *) p)->max = (num))
    142 #define history_def_getsize(p)  (((history_t *) p)->cur)
    143 
    144 #define he_strerror(code)	he_errlist[code]
    145 #define he_seterrev(evp, code)	{\
    146 				    evp->num = code;\
    147 				    evp->str = he_strerror(code);\
    148 				}
    149 
    150 /* error messages */
    151 static const char *const he_errlist[] = {
    152     "OK",
    153     "unknown error"
    154     "malloc() failed",
    155     "first event not found",
    156     "last event not found",
    157     "empty list",
    158     "no next event",
    159     "no previous event",
    160     "current event is invalid",
    161     "event not found",
    162     "can't read history from file",
    163     "can't write history",
    164     "required parameter(s) not supplied",
    165     "history size negative",
    166     "function not allowed with other history-functions-set the default",
    167     "bad parameters"
    168 };
    169 
    170 /* error codes */
    171 #define _HE_OK                   0
    172 #define _HE_UNKNOWN		 1
    173 #define _HE_MALLOC_FAILED        2
    174 #define _HE_FIRST_NOTFOUND       3
    175 #define _HE_LAST_NOTFOUND        4
    176 #define _HE_EMPTY_LIST           5
    177 #define _HE_END_REACHED          6
    178 #define _HE_START_REACHED	 7
    179 #define _HE_CURR_INVALID	 8
    180 #define _HE_NOT_FOUND		 9
    181 #define _HE_HIST_READ		10
    182 #define _HE_HIST_WRITE		11
    183 #define _HE_PARAM_MISSING	12
    184 #define _HE_SIZE_NEGATIVE	13
    185 #define _HE_NOT_ALLOWED		14
    186 #define _HE_BAD_PARAM		15
    187 
    188 /* history_def_first():
    189  *	Default function to return the first event in the history.
    190  */
    191 private int
    192 history_def_first(p, ev)
    193     ptr_t p;
    194     HistEvent *ev;
    195 {
    196     history_t *h = (history_t *) p;
    197 
    198     h->cursor = h->list.next;
    199     if (h->cursor != &h->list)
    200 	*ev = h->cursor->ev;
    201     else {
    202 	he_seterrev(ev, _HE_FIRST_NOTFOUND);
    203 	return -1;
    204     }
    205 
    206     return 0;
    207 }
    208 
    209 
    210 /* history_def_last():
    211  *	Default function to return the last event in the history.
    212  */
    213 private int
    214 history_def_last(p, ev)
    215     ptr_t p;
    216     HistEvent *ev;
    217 {
    218     history_t *h = (history_t *) p;
    219 
    220     h->cursor = h->list.prev;
    221     if (h->cursor != &h->list)
    222 	*ev =  h->cursor->ev;
    223     else {
    224 	he_seterrev(ev, _HE_LAST_NOTFOUND);
    225 	return -1;
    226     }
    227 
    228     return 0;
    229 }
    230 
    231 
    232 /* history_def_next():
    233  *	Default function to return the next event in the history.
    234  */
    235 private int
    236 history_def_next(p, ev)
    237     ptr_t p;
    238     HistEvent *ev;
    239 {
    240     history_t *h = (history_t *) p;
    241 
    242     if (h->cursor != &h->list)
    243 	h->cursor = h->cursor->next;
    244     else {
    245 	he_seterrev(ev, _HE_EMPTY_LIST);
    246 	return -1;
    247     }
    248 
    249     if (h->cursor != &h->list)
    250 	*ev = h->cursor->ev;
    251     else {
    252 	he_seterrev(ev, _HE_END_REACHED);
    253 	return -1;
    254     }
    255 
    256     return 0;
    257 }
    258 
    259 
    260 /* history_def_prev():
    261  *	Default function to return the previous event in the history.
    262  */
    263 private int
    264 history_def_prev(p, ev)
    265     ptr_t p;
    266     HistEvent *ev;
    267 {
    268     history_t *h = (history_t *) p;
    269 
    270     if (h->cursor != &h->list)
    271 	h->cursor = h->cursor->prev;
    272     else {
    273 	he_seterrev(ev, (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
    274 	return -1;
    275    }
    276 
    277     if (h->cursor != &h->list)
    278 	*ev = h->cursor->ev;
    279     else {
    280 	he_seterrev(ev, _HE_START_REACHED);
    281 	return -1;
    282     }
    283 
    284     return 0;
    285 }
    286 
    287 
    288 /* history_def_curr():
    289  *	Default function to return the current event in the history.
    290  */
    291 private int
    292 history_def_curr(p, ev)
    293     ptr_t p;
    294     HistEvent *ev;
    295 {
    296     history_t *h = (history_t *) p;
    297 
    298     if (h->cursor != &h->list)
    299 	*ev = h->cursor->ev;
    300     else {
    301 	he_seterrev(ev, (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
    302 	return -1;
    303    }
    304 
    305     return 0;
    306 }
    307 
    308 
    309 /* history_def_set():
    310  *	Default function to set the current event in the history to the
    311  *	given one.
    312  */
    313 private int
    314 history_def_set(p, ev, n)
    315     ptr_t p;
    316     HistEvent *ev;
    317     int n;
    318 {
    319     history_t *h = (history_t *) p;
    320 
    321     if (h->cur == 0) {
    322 	he_seterrev(ev, _HE_EMPTY_LIST);
    323 	return -1;
    324     }
    325 
    326     if (h->cursor == &h->list || h->cursor->ev.num != n) {
    327 	for (h->cursor = h->list.next; h->cursor != &h->list;
    328 	     h->cursor = h->cursor->next)
    329 	    if (h->cursor->ev.num == n)
    330 		break;
    331     }
    332 
    333     if (h->cursor == &h->list) {
    334 	he_seterrev(ev, _HE_NOT_FOUND);
    335 	return -1;
    336     }
    337 
    338     return 0;
    339 }
    340 
    341 
    342 /* history_def_add():
    343  *	Append string to element
    344  */
    345 private int
    346 history_def_add(p, ev, str)
    347     ptr_t p;
    348     HistEvent *ev;
    349     const char *str;
    350 {
    351     history_t *h = (history_t *) p;
    352     size_t len;
    353     char *s;
    354 
    355     if (h->cursor == &h->list)
    356 	return (history_def_enter(p, ev, str));
    357     len = strlen(h->cursor->ev.str) + strlen(str) + 1;
    358     s = (char *) h_malloc(len);
    359     if (!s) {
    360 	he_seterrev(ev, _HE_MALLOC_FAILED);
    361 	return -1;
    362     }
    363     (void)strcpy(s, h->cursor->ev.str);		/* XXX strcpy is safe */
    364     (void)strcat(s, str);			/* XXX strcat is safe */
    365     /* LINTED const cast */
    366     h_free((ptr_t) h->cursor->ev.str);
    367     h->cursor->ev.str = s;
    368     *ev = h->cursor->ev;
    369     return 0;
    370 }
    371 
    372 
    373 /* history_def_delete():
    374  *	Delete element hp of the h list
    375  */
    376 /* ARGSUSED */
    377 private void
    378 history_def_delete(h, ev, hp)
    379     history_t *h;
    380     HistEvent *ev;
    381     hentry_t *hp;
    382 {
    383     if (hp == &h->list)
    384 	abort();
    385     hp->prev->next = hp->next;
    386     hp->next->prev = hp->prev;
    387     /* LINTED const cast */
    388     h_free((ptr_t) hp->ev.str);
    389     h_free(hp);
    390     h->cur--;
    391 }
    392 
    393 
    394 /* history_def_insert():
    395  *	Insert element with string str in the h list
    396  */
    397 private int
    398 history_def_insert(h, ev, str)
    399     history_t *h;
    400     HistEvent *ev;
    401     const char *str;
    402 {
    403     h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
    404     if (h->cursor)
    405     	h->cursor->ev.str = strdup(str);
    406     if (!h->cursor || !h->cursor->ev.str) {
    407 	he_seterrev(ev, _HE_MALLOC_FAILED);
    408 	return -1;
    409     }
    410     h->cursor->ev.num = ++h->eventid;
    411     h->cursor->next = h->list.next;
    412     h->cursor->prev = &h->list;
    413     h->list.next->prev = h->cursor;
    414     h->list.next = h->cursor;
    415     h->cur++;
    416 
    417     *ev = h->cursor->ev;
    418     return 0;
    419 }
    420 
    421 
    422 /* history_def_enter():
    423  *	Default function to enter an item in the history
    424  */
    425 private int
    426 history_def_enter(p, ev, str)
    427     ptr_t p;
    428     HistEvent *ev;
    429     const char *str;
    430 {
    431     history_t *h = (history_t *) p;
    432 
    433     if (history_def_insert(h, ev, str) == -1)
    434 	return -1; /* error, keep error message */
    435 
    436     /*
    437      * Always keep at least one entry.
    438      * This way we don't have to check for the empty list.
    439      */
    440     while (h->cur - 1 > h->max)
    441 	history_def_delete(h, ev, h->list.prev);
    442 
    443     return 0;
    444 }
    445 
    446 
    447 /* history_def_init():
    448  *	Default history initialization function
    449  */
    450 /* ARGSUSED */
    451 private void
    452 history_def_init(p, ev, n)
    453     ptr_t *p;
    454     HistEvent *ev;
    455     int n;
    456 {
    457     history_t *h = (history_t *) h_malloc(sizeof(history_t));
    458     if (n <= 0)
    459 	n = 0;
    460     h->eventid = 0;
    461     h->cur = 0;
    462     h->max = n;
    463     h->list.next = h->list.prev = &h->list;
    464     h->list.ev.str = NULL;
    465     h->list.ev.num = 0;
    466     h->cursor = &h->list;
    467     *p = (ptr_t) h;
    468 }
    469 
    470 
    471 /* history_def_clear():
    472  *	Default history cleanup function
    473  */
    474 private void
    475 history_def_clear(p, ev)
    476     ptr_t p;
    477     HistEvent *ev;
    478 {
    479     history_t *h = (history_t *) p;
    480 
    481     while (h->list.prev != &h->list)
    482 	history_def_delete(h, ev, h->list.prev);
    483     h->eventid = 0;
    484     h->cur = 0;
    485 }
    486 
    487 
    488 
    489 
    490 /************************************************************************/
    491 
    492 /* history_init():
    493  *	Initialization function.
    494  */
    495 public History *
    496 history_init()
    497 {
    498     History *h = (History *) h_malloc(sizeof(History));
    499     HistEvent ev;
    500 
    501     history_def_init(&h->h_ref, &ev, 0);
    502     h->h_ent   = -1;
    503     h->h_next  = history_def_next;
    504     h->h_first = history_def_first;
    505     h->h_last  = history_def_last;
    506     h->h_prev  = history_def_prev;
    507     h->h_curr  = history_def_curr;
    508     h->h_set   = history_def_set;
    509     h->h_clear = history_def_clear;
    510     h->h_enter = history_def_enter;
    511     h->h_add   = history_def_add;
    512 
    513     return h;
    514 }
    515 
    516 
    517 /* history_end():
    518  *	clean up history;
    519  */
    520 public void
    521 history_end(h)
    522     History *h;
    523 {
    524     HistEvent ev;
    525     if (h->h_next == history_def_next)
    526 	history_def_clear(h->h_ref, &ev);
    527 }
    528 
    529 
    530 
    531 /* history_set_num():
    532  *	Set history number of events
    533  */
    534 private int
    535 history_set_num(h, ev, num)
    536     History *h;
    537     HistEvent *ev;
    538     int num;
    539 {
    540     if (h->h_next != history_def_next) {
    541 	he_seterrev(ev, _HE_NOT_ALLOWED);
    542 	return -1;
    543     }
    544 
    545     if (num < 0) {
    546 	he_seterrev(ev, _HE_BAD_PARAM);
    547 	return -1;
    548     }
    549 
    550     history_def_setsize(h->h_ref, num);
    551     return 0;
    552 }
    553 
    554 /* history_get_size():
    555  *      Get number of events currently in history
    556  */
    557 private int
    558 history_get_size(h, ev)
    559     History *h;
    560     HistEvent *ev;
    561 {
    562     int retval=0;
    563 
    564     if (h->h_next != history_def_next) {
    565 	he_seterrev(ev, _HE_NOT_ALLOWED);
    566 	return -1;
    567     }
    568     retval = history_def_getsize(h->h_ref);
    569     if (retval < -1) {
    570 	he_seterrev(ev, _HE_SIZE_NEGATIVE);
    571 	return -1;
    572     }
    573 
    574     ev->num = retval;
    575     return 0;
    576 }
    577 
    578 /* history_set_fun():
    579  *	Set history functions
    580  */
    581 private int
    582 history_set_fun(h, nh)
    583     History *h;
    584     History *nh;
    585 {
    586     HistEvent ev;
    587 
    588     if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
    589 	nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
    590 	nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
    591 	nh->h_ref == NULL) {
    592 	if (h->h_next != history_def_next) {
    593 	    history_def_init(&h->h_ref, &ev, 0);
    594 	    h->h_first = history_def_first;
    595 	    h->h_next  = history_def_next;
    596 	    h->h_last  = history_def_last;
    597 	    h->h_prev  = history_def_prev;
    598 	    h->h_curr  = history_def_curr;
    599 	    h->h_set   = history_def_set;
    600 	    h->h_clear = history_def_clear;
    601 	    h->h_enter = history_def_enter;
    602 	    h->h_add   = history_def_add;
    603 	}
    604 	return -1;
    605     }
    606 
    607     if (h->h_next == history_def_next)
    608 	history_def_clear(h->h_ref, &ev);
    609 
    610     h->h_ent   = -1;
    611     h->h_first = nh->h_first;
    612     h->h_next  = nh->h_next;
    613     h->h_last  = nh->h_last;
    614     h->h_prev  = nh->h_prev;
    615     h->h_curr  = nh->h_curr;
    616     h->h_set   = nh->h_set;
    617     h->h_clear = nh->h_clear;
    618     h->h_enter = nh->h_enter;
    619     h->h_add   = nh->h_add;
    620 
    621     return 0;
    622 }
    623 
    624 
    625 /* history_load():
    626  *	History load function
    627  */
    628 private int
    629 history_load(h, fname)
    630     History *h;
    631     const char *fname;
    632 {
    633     FILE *fp;
    634     char *line;
    635     size_t sz;
    636     int i = -1;
    637     HistEvent ev;
    638 
    639     if ((fp = fopen(fname, "r")) == NULL)
    640 	return i;
    641 
    642     if ((line = fgetln(fp, &sz)) == NULL)
    643 	goto done;
    644 
    645     if (strncmp(line, hist_cookie, sz) != 0)
    646 	goto done;
    647 
    648     for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
    649 	char c = line[sz];
    650 	line[sz] = '\0';
    651 	HENTER(h, &ev, line);
    652 	line[sz] = c;
    653     }
    654 
    655 done:
    656     (void) fclose(fp);
    657     return i;
    658 }
    659 
    660 
    661 /* history_save():
    662  *	History save function
    663  */
    664 private int
    665 history_save(h, fname)
    666     History *h;
    667     const char *fname;
    668 {
    669     FILE *fp;
    670     HistEvent ev;
    671     int i = 0, retval;
    672 
    673     if ((fp = fopen(fname, "w")) == NULL)
    674 	return -1;
    675 
    676     (void) fputs(hist_cookie, fp);
    677     for (retval = HLAST(h, &ev); retval != -1; retval = HPREV(h, &ev), i++)
    678 	(void) fprintf(fp, "%s", ev.str);
    679     (void) fclose(fp);
    680     return i;
    681 }
    682 
    683 
    684 /* history_prev_event():
    685  *	Find the previous event, with number given
    686  */
    687 private int
    688 history_prev_event(h, ev, num)
    689     History *h;
    690     HistEvent *ev;
    691     int num;
    692 {
    693     int retval;
    694     for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
    695 	if (ev->num == num)
    696 	    return 0;
    697 
    698     he_seterrev(ev, _HE_NOT_FOUND);
    699     return -1;
    700 }
    701 
    702 
    703 /* history_next_event():
    704  *	Find the next event, with number given
    705  */
    706 private int
    707 history_next_event(h, ev, num)
    708     History *h;
    709     HistEvent *ev;
    710     int num;
    711 {
    712     int retval;
    713     for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
    714 	if (ev->num == num)
    715 	    return 0;
    716 
    717     he_seterrev(ev, _HE_NOT_FOUND);
    718     return -1;
    719 }
    720 
    721 
    722 /* history_prev_string():
    723  *	Find the previous event beginning with string
    724  */
    725 private int
    726 history_prev_string(h, ev, str)
    727     History *h;
    728     HistEvent *ev;
    729     const char* str;
    730 {
    731     size_t len = strlen(str);
    732     int retval;
    733 
    734     for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
    735 	if (strncmp(str, ev->str, len) == 0)
    736 	    return 0;
    737 
    738     he_seterrev(ev, _HE_NOT_FOUND);
    739     return -1;
    740 }
    741 
    742 
    743 
    744 
    745 /* history_next_string():
    746  *	Find the next event beginning with string
    747  */
    748 private int
    749 history_next_string(h, ev, str)
    750     History *h;
    751     HistEvent *ev;
    752     const char* str;
    753 {
    754     size_t len = strlen(str);
    755     int retval;
    756 
    757     for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
    758 	if (strncmp(str, ev->str, len) == 0)
    759 	    return 0;
    760 
    761     he_seterrev(ev, _HE_NOT_FOUND);
    762     return -1;
    763 }
    764 
    765 
    766 /* history():
    767  *	User interface to history functions.
    768  */
    769 int
    770 #ifdef __STDC__
    771 history(History *h, HistEvent *ev, int fun, ...)
    772 #else
    773 history(va_alist)
    774     va_dcl
    775 #endif
    776 {
    777     va_list va;
    778     const char *str;
    779     int retval;
    780 
    781 #ifdef __STDC__
    782     va_start(va, fun);
    783 #else
    784     History *h;
    785     HistEvent *ev;
    786     int fun;
    787     va_start(va);
    788     h = va_arg(va, History *);
    789     ev = va_arg(va, HistEvent *);
    790     fun = va_arg(va, int);
    791 #endif
    792 
    793     he_seterrev(ev, _HE_OK);
    794 
    795     switch (fun) {
    796     case H_GETSIZE:
    797 	retval = history_get_size(h, ev);
    798 	break;
    799 
    800     case H_SETSIZE:
    801 	retval = history_set_num(h, ev, va_arg(va, int));
    802 	break;
    803 
    804     case H_ADD:
    805 	str = va_arg(va, const char *);
    806 	retval = HADD(h, ev, str);
    807 	break;
    808 
    809     case H_ENTER:
    810 	str = va_arg(va, const char *);
    811 	if ((retval = HENTER(h, ev, str)) != -1)
    812 	    h->h_ent = ev->num;
    813 	break;
    814 
    815     case H_APPEND:
    816 	str = va_arg(va, const char *);
    817 	if ((retval = HSET(h, ev, h->h_ent)) != -1)
    818 	    retval = HADD(h, ev, str);
    819 	break;
    820 
    821     case H_FIRST:
    822 	retval = HFIRST(h, ev);
    823 	break;
    824 
    825     case H_NEXT:
    826 	retval = HNEXT(h, ev);
    827 	break;
    828 
    829     case H_LAST:
    830 	retval = HLAST(h, ev);
    831 	break;
    832 
    833     case H_PREV:
    834 	retval = HPREV(h, ev);
    835 	break;
    836 
    837     case H_CURR:
    838 	retval = HCURR(h, ev);
    839 	break;
    840 
    841     case H_SET:
    842 	retval = HSET(h, ev, va_arg(va, const int));
    843 	break;
    844 
    845     case H_CLEAR:
    846 	HCLEAR(h, ev);
    847 	retval = 0;
    848 	break;
    849 
    850     case H_LOAD:
    851 	retval = history_load(h, va_arg(va, const char *));
    852 	if (retval == -1)
    853 	    he_seterrev(ev, _HE_HIST_READ);
    854 	break;
    855 
    856     case H_SAVE:
    857 	retval = history_save(h, va_arg(va, const char *));
    858 	if (retval == -1)
    859 	    he_seterrev(ev, _HE_HIST_WRITE);
    860 	break;
    861 
    862     case H_PREV_EVENT:
    863 	retval = history_prev_event(h, ev, va_arg(va, int));
    864 	break;
    865 
    866     case H_NEXT_EVENT:
    867 	retval = history_next_event(h, ev, va_arg(va, int));
    868 	break;
    869 
    870     case H_PREV_STR:
    871 	retval = history_prev_string(h, ev, va_arg(va, const char*));
    872 	break;
    873 
    874     case H_NEXT_STR:
    875 	retval = history_next_string(h, ev, va_arg(va, const char*));
    876 	break;
    877 
    878     case H_FUNC:
    879 	{
    880 	    History hf;
    881 
    882 	    hf.h_ref   = va_arg(va, ptr_t);
    883 	    h->h_ent   = -1;
    884 	    hf.h_first = va_arg(va, history_gfun_t);
    885 	    hf.h_next  = va_arg(va, history_gfun_t);
    886 	    hf.h_last  = va_arg(va, history_gfun_t);
    887 	    hf.h_prev  = va_arg(va, history_gfun_t);
    888 	    hf.h_curr  = va_arg(va, history_gfun_t);
    889 	    hf.h_set   = va_arg(va, history_sfun_t);
    890 	    hf.h_clear = va_arg(va, history_vfun_t);
    891 	    hf.h_enter = va_arg(va, history_efun_t);
    892 	    hf.h_add   = va_arg(va, history_efun_t);
    893 
    894 	    if ((retval = history_set_fun(h, &hf)) == -1)
    895 		he_seterrev(ev, _HE_PARAM_MISSING);
    896 	}
    897 	break;
    898 
    899     case H_END:
    900 	history_end(h);
    901 	retval = 0;
    902 	break;
    903 
    904     default:
    905 	retval = -1;
    906 	he_seterrev(ev, _HE_UNKNOWN);
    907 	break;
    908     }
    909     va_end(va);
    910     return retval;
    911 }
    912