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