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