Home | History | Annotate | Line # | Download | only in libedit
history.c revision 1.33
      1 /*	$NetBSD: history.c,v 1.33 2009/02/06 14:40:32 sketch 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.33 2009/02/06 14:40:32 sketch 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 	h->h_del = history_def_del;
    536 
    537 	return (h);
    538 }
    539 
    540 
    541 /* history_end():
    542  *	clean up history;
    543  */
    544 public void
    545 history_end(History *h)
    546 {
    547 	HistEvent ev;
    548 
    549 	if (h->h_next == history_def_next)
    550 		history_def_clear(h->h_ref, &ev);
    551 	h_free(h->h_ref);
    552 	h_free(h);
    553 }
    554 
    555 
    556 
    557 /* history_setsize():
    558  *	Set history number of events
    559  */
    560 private int
    561 history_setsize(History *h, HistEvent *ev, int num)
    562 {
    563 
    564 	if (h->h_next != history_def_next) {
    565 		he_seterrev(ev, _HE_NOT_ALLOWED);
    566 		return (-1);
    567 	}
    568 	if (num < 0) {
    569 		he_seterrev(ev, _HE_BAD_PARAM);
    570 		return (-1);
    571 	}
    572 	history_def_setsize(h->h_ref, num);
    573 	return (0);
    574 }
    575 
    576 
    577 /* history_getsize():
    578  *      Get number of events currently in history
    579  */
    580 private int
    581 history_getsize(History *h, HistEvent *ev)
    582 {
    583 	if (h->h_next != history_def_next) {
    584 		he_seterrev(ev, _HE_NOT_ALLOWED);
    585 		return (-1);
    586 	}
    587 	ev->num = history_def_getsize(h->h_ref);
    588 	if (ev->num < -1) {
    589 		he_seterrev(ev, _HE_SIZE_NEGATIVE);
    590 		return (-1);
    591 	}
    592 	return (0);
    593 }
    594 
    595 
    596 /* history_setunique():
    597  *	Set if adjacent equal events should not be entered in history.
    598  */
    599 private int
    600 history_setunique(History *h, HistEvent *ev, int uni)
    601 {
    602 
    603 	if (h->h_next != history_def_next) {
    604 		he_seterrev(ev, _HE_NOT_ALLOWED);
    605 		return (-1);
    606 	}
    607 	history_def_setunique(h->h_ref, uni);
    608 	return (0);
    609 }
    610 
    611 
    612 /* history_getunique():
    613  *	Get if adjacent equal events should not be entered in history.
    614  */
    615 private int
    616 history_getunique(History *h, HistEvent *ev)
    617 {
    618 	if (h->h_next != history_def_next) {
    619 		he_seterrev(ev, _HE_NOT_ALLOWED);
    620 		return (-1);
    621 	}
    622 	ev->num = history_def_getunique(h->h_ref);
    623 	return (0);
    624 }
    625 
    626 
    627 /* history_set_fun():
    628  *	Set history functions
    629  */
    630 private int
    631 history_set_fun(History *h, History *nh)
    632 {
    633 	HistEvent ev;
    634 
    635 	if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
    636 	    nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
    637 	    nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
    638 	    nh->h_del == NULL || nh->h_ref == NULL) {
    639 		if (h->h_next != history_def_next) {
    640 			history_def_init(&h->h_ref, &ev, 0);
    641 			h->h_first = history_def_first;
    642 			h->h_next = history_def_next;
    643 			h->h_last = history_def_last;
    644 			h->h_prev = history_def_prev;
    645 			h->h_curr = history_def_curr;
    646 			h->h_set = history_def_set;
    647 			h->h_clear = history_def_clear;
    648 			h->h_enter = history_def_enter;
    649 			h->h_add = history_def_add;
    650 			h->h_del = history_def_del;
    651 		}
    652 		return (-1);
    653 	}
    654 	if (h->h_next == history_def_next)
    655 		history_def_clear(h->h_ref, &ev);
    656 
    657 	h->h_ent = -1;
    658 	h->h_first = nh->h_first;
    659 	h->h_next = nh->h_next;
    660 	h->h_last = nh->h_last;
    661 	h->h_prev = nh->h_prev;
    662 	h->h_curr = nh->h_curr;
    663 	h->h_set = nh->h_set;
    664 	h->h_clear = nh->h_clear;
    665 	h->h_enter = nh->h_enter;
    666 	h->h_add = nh->h_add;
    667 	h->h_del = nh->h_del;
    668 
    669 	return (0);
    670 }
    671 
    672 
    673 /* history_load():
    674  *	History load function
    675  */
    676 private int
    677 history_load(History *h, const char *fname)
    678 {
    679 	FILE *fp;
    680 	char *line;
    681 	size_t sz, max_size;
    682 	char *ptr;
    683 	int i = -1;
    684 	HistEvent ev;
    685 
    686 	if ((fp = fopen(fname, "r")) == NULL)
    687 		return (i);
    688 
    689 	if ((line = fgetln(fp, &sz)) == NULL)
    690 		goto done;
    691 
    692 	if (strncmp(line, hist_cookie, sz) != 0)
    693 		goto done;
    694 
    695 	ptr = h_malloc(max_size = 1024);
    696 	if (ptr == NULL)
    697 		goto done;
    698 	for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
    699 		char c = line[sz];
    700 
    701 		if (sz != 0 && line[sz - 1] == '\n')
    702 			line[--sz] = '\0';
    703 		else
    704 			line[sz] = '\0';
    705 
    706 		if (max_size < sz) {
    707 			char *nptr;
    708 			max_size = (sz + 1024) & ~1023;
    709 			nptr = h_realloc(ptr, max_size);
    710 			if (nptr == NULL) {
    711 				i = -1;
    712 				goto oomem;
    713 			}
    714 			ptr = nptr;
    715 		}
    716 		(void) strunvis(ptr, line);
    717 		line[sz] = c;
    718 		if (HENTER(h, &ev, ptr) == -1) {
    719 			i = -1;
    720 			goto oomem;
    721 		}
    722 	}
    723 oomem:
    724 	h_free((ptr_t)ptr);
    725 done:
    726 	(void) fclose(fp);
    727 	return (i);
    728 }
    729 
    730 
    731 /* history_save():
    732  *	History save function
    733  */
    734 private int
    735 history_save(History *h, const char *fname)
    736 {
    737 	FILE *fp;
    738 	HistEvent ev;
    739 	int i = -1, retval;
    740 	size_t len, max_size;
    741 	char *ptr;
    742 
    743 	if ((fp = fopen(fname, "w")) == NULL)
    744 		return (-1);
    745 
    746 	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
    747 		goto done;
    748 	if (fputs(hist_cookie, fp) == EOF)
    749 		goto done;
    750 	ptr = h_malloc(max_size = 1024);
    751 	if (ptr == NULL)
    752 		goto done;
    753 	for (i = 0, retval = HLAST(h, &ev);
    754 	    retval != -1;
    755 	    retval = HPREV(h, &ev), i++) {
    756 		len = strlen(ev.str) * 4;
    757 		if (len >= max_size) {
    758 			char *nptr;
    759 			max_size = (len + 1024) & ~1023;
    760 			nptr = h_realloc(ptr, max_size);
    761 			if (nptr == NULL) {
    762 				i = -1;
    763 				goto oomem;
    764 			}
    765 			ptr = nptr;
    766 		}
    767 		(void) strvis(ptr, ev.str, VIS_WHITE);
    768 		(void) fprintf(fp, "%s\n", ptr);
    769 	}
    770 oomem:
    771 	h_free((ptr_t)ptr);
    772 done:
    773 	(void) fclose(fp);
    774 	return (i);
    775 }
    776 
    777 
    778 /* history_prev_event():
    779  *	Find the previous event, with number given
    780  */
    781 private int
    782 history_prev_event(History *h, HistEvent *ev, int num)
    783 {
    784 	int retval;
    785 
    786 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
    787 		if (ev->num == num)
    788 			return (0);
    789 
    790 	he_seterrev(ev, _HE_NOT_FOUND);
    791 	return (-1);
    792 }
    793 
    794 
    795 /* history_next_event():
    796  *	Find the next event, with number given
    797  */
    798 private int
    799 history_next_event(History *h, HistEvent *ev, int num)
    800 {
    801 	int retval;
    802 
    803 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
    804 		if (ev->num == num)
    805 			return (0);
    806 
    807 	he_seterrev(ev, _HE_NOT_FOUND);
    808 	return (-1);
    809 }
    810 
    811 
    812 /* history_prev_string():
    813  *	Find the previous event beginning with string
    814  */
    815 private int
    816 history_prev_string(History *h, HistEvent *ev, const char *str)
    817 {
    818 	size_t len = strlen(str);
    819 	int retval;
    820 
    821 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
    822 		if (strncmp(str, ev->str, len) == 0)
    823 			return (0);
    824 
    825 	he_seterrev(ev, _HE_NOT_FOUND);
    826 	return (-1);
    827 }
    828 
    829 
    830 /* history_next_string():
    831  *	Find the next event beginning with string
    832  */
    833 private int
    834 history_next_string(History *h, HistEvent *ev, const char *str)
    835 {
    836 	size_t len = strlen(str);
    837 	int retval;
    838 
    839 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
    840 		if (strncmp(str, ev->str, len) == 0)
    841 			return (0);
    842 
    843 	he_seterrev(ev, _HE_NOT_FOUND);
    844 	return (-1);
    845 }
    846 
    847 
    848 /* history():
    849  *	User interface to history functions.
    850  */
    851 int
    852 history(History *h, HistEvent *ev, int fun, ...)
    853 {
    854 	va_list va;
    855 	const char *str;
    856 	int retval;
    857 
    858 	va_start(va, fun);
    859 
    860 	he_seterrev(ev, _HE_OK);
    861 
    862 	switch (fun) {
    863 	case H_GETSIZE:
    864 		retval = history_getsize(h, ev);
    865 		break;
    866 
    867 	case H_SETSIZE:
    868 		retval = history_setsize(h, ev, va_arg(va, int));
    869 		break;
    870 
    871 	case H_GETUNIQUE:
    872 		retval = history_getunique(h, ev);
    873 		break;
    874 
    875 	case H_SETUNIQUE:
    876 		retval = history_setunique(h, ev, va_arg(va, int));
    877 		break;
    878 
    879 	case H_ADD:
    880 		str = va_arg(va, const char *);
    881 		retval = HADD(h, ev, str);
    882 		break;
    883 
    884 	case H_DEL:
    885 		retval = HDEL(h, ev, va_arg(va, const int));
    886 		break;
    887 
    888 	case H_ENTER:
    889 		str = va_arg(va, const char *);
    890 		if ((retval = HENTER(h, ev, str)) != -1)
    891 			h->h_ent = ev->num;
    892 		break;
    893 
    894 	case H_APPEND:
    895 		str = va_arg(va, const char *);
    896 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
    897 			retval = HADD(h, ev, str);
    898 		break;
    899 
    900 	case H_FIRST:
    901 		retval = HFIRST(h, ev);
    902 		break;
    903 
    904 	case H_NEXT:
    905 		retval = HNEXT(h, ev);
    906 		break;
    907 
    908 	case H_LAST:
    909 		retval = HLAST(h, ev);
    910 		break;
    911 
    912 	case H_PREV:
    913 		retval = HPREV(h, ev);
    914 		break;
    915 
    916 	case H_CURR:
    917 		retval = HCURR(h, ev);
    918 		break;
    919 
    920 	case H_SET:
    921 		retval = HSET(h, ev, va_arg(va, const int));
    922 		break;
    923 
    924 	case H_CLEAR:
    925 		HCLEAR(h, ev);
    926 		retval = 0;
    927 		break;
    928 
    929 	case H_LOAD:
    930 		retval = history_load(h, va_arg(va, const char *));
    931 		if (retval == -1)
    932 			he_seterrev(ev, _HE_HIST_READ);
    933 		break;
    934 
    935 	case H_SAVE:
    936 		retval = history_save(h, va_arg(va, const char *));
    937 		if (retval == -1)
    938 			he_seterrev(ev, _HE_HIST_WRITE);
    939 		break;
    940 
    941 	case H_PREV_EVENT:
    942 		retval = history_prev_event(h, ev, va_arg(va, int));
    943 		break;
    944 
    945 	case H_NEXT_EVENT:
    946 		retval = history_next_event(h, ev, va_arg(va, int));
    947 		break;
    948 
    949 	case H_PREV_STR:
    950 		retval = history_prev_string(h, ev, va_arg(va, const char *));
    951 		break;
    952 
    953 	case H_NEXT_STR:
    954 		retval = history_next_string(h, ev, va_arg(va, const char *));
    955 		break;
    956 
    957 	case H_FUNC:
    958 	{
    959 		History hf;
    960 
    961 		hf.h_ref = va_arg(va, ptr_t);
    962 		h->h_ent = -1;
    963 		hf.h_first = va_arg(va, history_gfun_t);
    964 		hf.h_next = va_arg(va, history_gfun_t);
    965 		hf.h_last = va_arg(va, history_gfun_t);
    966 		hf.h_prev = va_arg(va, history_gfun_t);
    967 		hf.h_curr = va_arg(va, history_gfun_t);
    968 		hf.h_set = va_arg(va, history_sfun_t);
    969 		hf.h_clear = va_arg(va, history_vfun_t);
    970 		hf.h_enter = va_arg(va, history_efun_t);
    971 		hf.h_add = va_arg(va, history_efun_t);
    972 		hf.h_del = va_arg(va, history_sfun_t);
    973 
    974 		if ((retval = history_set_fun(h, &hf)) == -1)
    975 			he_seterrev(ev, _HE_PARAM_MISSING);
    976 		break;
    977 	}
    978 
    979 	case H_END:
    980 		history_end(h);
    981 		retval = 0;
    982 		break;
    983 
    984 	default:
    985 		retval = -1;
    986 		he_seterrev(ev, _HE_UNKNOWN);
    987 		break;
    988 	}
    989 	va_end(va);
    990 	return (retval);
    991 }
    992