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