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