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