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