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