Home | History | Annotate | Line # | Download | only in libedit
history.c revision 1.34
      1 /*	$NetBSD: history.c,v 1.34 2009/09/07 21:24:33 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.34 2009/09/07 21:24:33 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 	void *data;		/* data				 */
    125 	struct hentry_t *next;	/* Next entry			 */
    126 	struct hentry_t *prev;	/* Previous entry		 */
    127 } hentry_t;
    128 
    129 typedef struct history_t {
    130 	hentry_t list;		/* Fake list header element	*/
    131 	hentry_t *cursor;	/* Current element in the list	*/
    132 	int max;		/* Maximum number of events	*/
    133 	int cur;		/* Current number of events	*/
    134 	int eventid;		/* For generation of unique event id	 */
    135 	int flags;		/* History flags		*/
    136 #define H_UNIQUE	1	/* Store only unique elements	*/
    137 } history_t;
    138 
    139 private int history_def_next(ptr_t, HistEvent *);
    140 private int history_def_first(ptr_t, HistEvent *);
    141 private int history_def_prev(ptr_t, HistEvent *);
    142 private int history_def_last(ptr_t, HistEvent *);
    143 private int history_def_curr(ptr_t, HistEvent *);
    144 private int history_def_set(ptr_t, HistEvent *, const int);
    145 private void history_def_clear(ptr_t, HistEvent *);
    146 private int history_def_enter(ptr_t, HistEvent *, const char *);
    147 private int history_def_add(ptr_t, HistEvent *, const char *);
    148 private int history_def_del(ptr_t, HistEvent *, const int);
    149 
    150 private int history_def_init(ptr_t *, HistEvent *, int);
    151 private int history_def_insert(history_t *, HistEvent *, const char *);
    152 private void history_def_delete(history_t *, HistEvent *, hentry_t *);
    153 
    154 private int history_deldata_nth(history_t *, HistEvent *, int, void **);
    155 private int history_set_nth(ptr_t, HistEvent *, int);
    156 
    157 #define	history_def_setsize(p, num)(void) (((history_t *)p)->max = (num))
    158 #define	history_def_getsize(p)  (((history_t *)p)->cur)
    159 #define	history_def_getunique(p) (((((history_t *)p)->flags) & H_UNIQUE) != 0)
    160 #define	history_def_setunique(p, uni) \
    161     if (uni) \
    162 	(((history_t *)p)->flags) |= H_UNIQUE; \
    163     else \
    164 	(((history_t *)p)->flags) &= ~H_UNIQUE
    165 
    166 #define	he_strerror(code)	he_errlist[code]
    167 #define	he_seterrev(evp, code)	{\
    168 				    evp->num = code;\
    169 				    evp->str = he_strerror(code);\
    170 				}
    171 
    172 /* error messages */
    173 static const char *const he_errlist[] = {
    174 	"OK",
    175 	"unknown error",
    176 	"malloc() failed",
    177 	"first event not found",
    178 	"last event not found",
    179 	"empty list",
    180 	"no next event",
    181 	"no previous event",
    182 	"current event is invalid",
    183 	"event not found",
    184 	"can't read history from file",
    185 	"can't write history",
    186 	"required parameter(s) not supplied",
    187 	"history size negative",
    188 	"function not allowed with other history-functions-set the default",
    189 	"bad parameters"
    190 };
    191 /* error codes */
    192 #define	_HE_OK                   0
    193 #define	_HE_UNKNOWN		 1
    194 #define	_HE_MALLOC_FAILED        2
    195 #define	_HE_FIRST_NOTFOUND       3
    196 #define	_HE_LAST_NOTFOUND        4
    197 #define	_HE_EMPTY_LIST           5
    198 #define	_HE_END_REACHED          6
    199 #define	_HE_START_REACHED	 7
    200 #define	_HE_CURR_INVALID	 8
    201 #define	_HE_NOT_FOUND		 9
    202 #define	_HE_HIST_READ		10
    203 #define	_HE_HIST_WRITE		11
    204 #define	_HE_PARAM_MISSING	12
    205 #define	_HE_SIZE_NEGATIVE	13
    206 #define	_HE_NOT_ALLOWED		14
    207 #define	_HE_BAD_PARAM		15
    208 
    209 /* history_def_first():
    210  *	Default function to return the first event in the history.
    211  */
    212 private int
    213 history_def_first(ptr_t p, HistEvent *ev)
    214 {
    215 	history_t *h = (history_t *) p;
    216 
    217 	h->cursor = h->list.next;
    218 	if (h->cursor != &h->list)
    219 		*ev = h->cursor->ev;
    220 	else {
    221 		he_seterrev(ev, _HE_FIRST_NOTFOUND);
    222 		return (-1);
    223 	}
    224 
    225 	return (0);
    226 }
    227 
    228 
    229 /* history_def_last():
    230  *	Default function to return the last event in the history.
    231  */
    232 private int
    233 history_def_last(ptr_t p, HistEvent *ev)
    234 {
    235 	history_t *h = (history_t *) p;
    236 
    237 	h->cursor = h->list.prev;
    238 	if (h->cursor != &h->list)
    239 		*ev = h->cursor->ev;
    240 	else {
    241 		he_seterrev(ev, _HE_LAST_NOTFOUND);
    242 		return (-1);
    243 	}
    244 
    245 	return (0);
    246 }
    247 
    248 
    249 /* history_def_next():
    250  *	Default function to return the next event in the history.
    251  */
    252 private int
    253 history_def_next(ptr_t p, HistEvent *ev)
    254 {
    255 	history_t *h = (history_t *) p;
    256 
    257 	if (h->cursor == &h->list) {
    258 		he_seterrev(ev, _HE_EMPTY_LIST);
    259 		return (-1);
    260 	}
    261 
    262 	if (h->cursor->next == &h->list) {
    263 		he_seterrev(ev, _HE_END_REACHED);
    264 		return (-1);
    265 	}
    266 
    267         h->cursor = h->cursor->next;
    268         *ev = h->cursor->ev;
    269 
    270 	return (0);
    271 }
    272 
    273 
    274 /* history_def_prev():
    275  *	Default function to return the previous event in the history.
    276  */
    277 private int
    278 history_def_prev(ptr_t p, HistEvent *ev)
    279 {
    280 	history_t *h = (history_t *) p;
    281 
    282 	if (h->cursor == &h->list) {
    283 		he_seterrev(ev,
    284 		    (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
    285 		return (-1);
    286 	}
    287 
    288 	if (h->cursor->prev == &h->list) {
    289 		he_seterrev(ev, _HE_START_REACHED);
    290 		return (-1);
    291 	}
    292 
    293         h->cursor = h->cursor->prev;
    294         *ev = h->cursor->ev;
    295 
    296 	return (0);
    297 }
    298 
    299 
    300 /* history_def_curr():
    301  *	Default function to return the current event in the history.
    302  */
    303 private int
    304 history_def_curr(ptr_t p, HistEvent *ev)
    305 {
    306 	history_t *h = (history_t *) p;
    307 
    308 	if (h->cursor != &h->list)
    309 		*ev = h->cursor->ev;
    310 	else {
    311 		he_seterrev(ev,
    312 		    (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
    313 		return (-1);
    314 	}
    315 
    316 	return (0);
    317 }
    318 
    319 
    320 /* history_def_set():
    321  *	Default function to set the current event in the history to the
    322  *	given one.
    323  */
    324 private int
    325 history_def_set(ptr_t p, HistEvent *ev, const int n)
    326 {
    327 	history_t *h = (history_t *) p;
    328 
    329 	if (h->cur == 0) {
    330 		he_seterrev(ev, _HE_EMPTY_LIST);
    331 		return (-1);
    332 	}
    333 	if (h->cursor == &h->list || h->cursor->ev.num != n) {
    334 		for (h->cursor = h->list.next; h->cursor != &h->list;
    335 		    h->cursor = h->cursor->next)
    336 			if (h->cursor->ev.num == n)
    337 				break;
    338 	}
    339 	if (h->cursor == &h->list) {
    340 		he_seterrev(ev, _HE_NOT_FOUND);
    341 		return (-1);
    342 	}
    343 	return (0);
    344 }
    345 
    346 
    347 /* history_set_nth():
    348  *	Default function to set the current event in the history to the
    349  *	n-th one.
    350  */
    351 private int
    352 history_set_nth(ptr_t p, HistEvent *ev, int n)
    353 {
    354 	history_t *h = (history_t *) p;
    355 
    356 	if (h->cur == 0) {
    357 		he_seterrev(ev, _HE_EMPTY_LIST);
    358 		return (-1);
    359 	}
    360 	for (h->cursor = h->list.prev; h->cursor != &h->list;
    361 	    h->cursor = h->cursor->prev)
    362 		if (n-- <= 0)
    363 			break;
    364 	if (h->cursor == &h->list) {
    365 		he_seterrev(ev, _HE_NOT_FOUND);
    366 		return (-1);
    367 	}
    368 	return (0);
    369 }
    370 
    371 
    372 /* history_def_add():
    373  *	Append string to element
    374  */
    375 private int
    376 history_def_add(ptr_t p, HistEvent *ev, const char *str)
    377 {
    378 	history_t *h = (history_t *) p;
    379 	size_t len;
    380 	char *s;
    381 	HistEventPrivate *evp = (void *)&h->cursor->ev;
    382 
    383 	if (h->cursor == &h->list)
    384 		return (history_def_enter(p, ev, str));
    385 	len = strlen(evp->str) + strlen(str) + 1;
    386 	s = (char *) h_malloc(len);
    387 	if (s == NULL) {
    388 		he_seterrev(ev, _HE_MALLOC_FAILED);
    389 		return (-1);
    390 	}
    391 	(void) strlcpy(s, h->cursor->ev.str, len);
    392 	(void) strlcat(s, str, len);
    393 	h_free((ptr_t)evp->str);
    394 	evp->str = s;
    395 	*ev = h->cursor->ev;
    396 	return (0);
    397 }
    398 
    399 
    400 private int
    401 history_deldata_nth(history_t *h, HistEvent *ev,
    402     int num, void **data)
    403 {
    404 	if (history_set_nth(h, ev, num) != 0)
    405 		return (-1);
    406 	/* magic value to skip delete (just set to n-th history) */
    407 	if (data == (void **)-1)
    408 		return (0);
    409 	ev->str = strdup(h->cursor->ev.str);
    410 	ev->num = h->cursor->ev.num;
    411 	if (data)
    412 		*data = h->cursor->data;
    413 	history_def_delete(h, ev, h->cursor);
    414 	return (0);
    415 }
    416 
    417 
    418 /* history_def_del():
    419  *	Delete element hp of the h list
    420  */
    421 /* ARGSUSED */
    422 private int
    423 history_def_del(ptr_t p, HistEvent *ev __attribute__((__unused__)),
    424     const int num)
    425 {
    426 	history_t *h = (history_t *) p;
    427 	if (history_def_set(h, ev, num) != 0)
    428 		return (-1);
    429 	ev->str = strdup(h->cursor->ev.str);
    430 	ev->num = h->cursor->ev.num;
    431 	history_def_delete(h, ev, h->cursor);
    432 	return (0);
    433 }
    434 
    435 
    436 /* history_def_delete():
    437  *	Delete element hp of the h list
    438  */
    439 /* ARGSUSED */
    440 private void
    441 history_def_delete(history_t *h,
    442 		   HistEvent *ev __attribute__((__unused__)), hentry_t *hp)
    443 {
    444 	HistEventPrivate *evp = (void *)&hp->ev;
    445 	if (hp == &h->list)
    446 		abort();
    447 	if (h->cursor == hp) {
    448 		h->cursor = hp->prev;
    449 		if (h->cursor == &h->list)
    450 			h->cursor = hp->next;
    451 	}
    452 	hp->prev->next = hp->next;
    453 	hp->next->prev = hp->prev;
    454 	h_free((ptr_t) evp->str);
    455 	h_free(hp);
    456 	h->cur--;
    457 }
    458 
    459 
    460 /* history_def_insert():
    461  *	Insert element with string str in the h list
    462  */
    463 private int
    464 history_def_insert(history_t *h, HistEvent *ev, const char *str)
    465 {
    466 
    467 	h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
    468 	if (h->cursor == NULL)
    469 		goto oomem;
    470 	if ((h->cursor->ev.str = h_strdup(str)) == NULL) {
    471 		h_free((ptr_t)h->cursor);
    472 		goto oomem;
    473 	}
    474 	h->cursor->data = NULL;
    475 	h->cursor->ev.num = ++h->eventid;
    476 	h->cursor->next = h->list.next;
    477 	h->cursor->prev = &h->list;
    478 	h->list.next->prev = h->cursor;
    479 	h->list.next = h->cursor;
    480 	h->cur++;
    481 
    482 	*ev = h->cursor->ev;
    483 	return (0);
    484 oomem:
    485 	he_seterrev(ev, _HE_MALLOC_FAILED);
    486 	return (-1);
    487 }
    488 
    489 
    490 /* history_def_enter():
    491  *	Default function to enter an item in the history
    492  */
    493 private int
    494 history_def_enter(ptr_t p, HistEvent *ev, const char *str)
    495 {
    496 	history_t *h = (history_t *) p;
    497 
    498 	if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
    499 	    strcmp(h->list.next->ev.str, str) == 0)
    500 	    return (0);
    501 
    502 	if (history_def_insert(h, ev, str) == -1)
    503 		return (-1);	/* error, keep error message */
    504 
    505 	/*
    506          * Always keep at least one entry.
    507          * This way we don't have to check for the empty list.
    508          */
    509 	while (h->cur > h->max && h->cur > 0)
    510 		history_def_delete(h, ev, h->list.prev);
    511 
    512 	return (1);
    513 }
    514 
    515 
    516 /* history_def_init():
    517  *	Default history initialization function
    518  */
    519 /* ARGSUSED */
    520 private int
    521 history_def_init(ptr_t *p, HistEvent *ev __attribute__((__unused__)), int n)
    522 {
    523 	history_t *h = (history_t *) h_malloc(sizeof(history_t));
    524 	if (h == NULL)
    525 		return -1;
    526 
    527 	if (n <= 0)
    528 		n = 0;
    529 	h->eventid = 0;
    530 	h->cur = 0;
    531 	h->max = n;
    532 	h->list.next = h->list.prev = &h->list;
    533 	h->list.ev.str = NULL;
    534 	h->list.ev.num = 0;
    535 	h->cursor = &h->list;
    536 	h->flags = 0;
    537 	*p = (ptr_t) h;
    538 	return 0;
    539 }
    540 
    541 
    542 /* history_def_clear():
    543  *	Default history cleanup function
    544  */
    545 private void
    546 history_def_clear(ptr_t p, HistEvent *ev)
    547 {
    548 	history_t *h = (history_t *) p;
    549 
    550 	while (h->list.prev != &h->list)
    551 		history_def_delete(h, ev, h->list.prev);
    552 	h->eventid = 0;
    553 	h->cur = 0;
    554 }
    555 
    556 
    557 
    558 
    559 /************************************************************************/
    560 
    561 /* history_init():
    562  *	Initialization function.
    563  */
    564 public History *
    565 history_init(void)
    566 {
    567 	HistEvent ev;
    568 	History *h = (History *) h_malloc(sizeof(History));
    569 	if (h == NULL)
    570 		return NULL;
    571 
    572 	if (history_def_init(&h->h_ref, &ev, 0) == -1) {
    573 		h_free((ptr_t)h);
    574 		return NULL;
    575 	}
    576 	h->h_ent = -1;
    577 	h->h_next = history_def_next;
    578 	h->h_first = history_def_first;
    579 	h->h_last = history_def_last;
    580 	h->h_prev = history_def_prev;
    581 	h->h_curr = history_def_curr;
    582 	h->h_set = history_def_set;
    583 	h->h_clear = history_def_clear;
    584 	h->h_enter = history_def_enter;
    585 	h->h_add = history_def_add;
    586 	h->h_del = history_def_del;
    587 
    588 	return (h);
    589 }
    590 
    591 
    592 /* history_end():
    593  *	clean up history;
    594  */
    595 public void
    596 history_end(History *h)
    597 {
    598 	HistEvent ev;
    599 
    600 	if (h->h_next == history_def_next)
    601 		history_def_clear(h->h_ref, &ev);
    602 	h_free(h->h_ref);
    603 	h_free(h);
    604 }
    605 
    606 
    607 
    608 /* history_setsize():
    609  *	Set history number of events
    610  */
    611 private int
    612 history_setsize(History *h, HistEvent *ev, int num)
    613 {
    614 
    615 	if (h->h_next != history_def_next) {
    616 		he_seterrev(ev, _HE_NOT_ALLOWED);
    617 		return (-1);
    618 	}
    619 	if (num < 0) {
    620 		he_seterrev(ev, _HE_BAD_PARAM);
    621 		return (-1);
    622 	}
    623 	history_def_setsize(h->h_ref, num);
    624 	return (0);
    625 }
    626 
    627 
    628 /* history_getsize():
    629  *      Get number of events currently in history
    630  */
    631 private int
    632 history_getsize(History *h, HistEvent *ev)
    633 {
    634 	if (h->h_next != history_def_next) {
    635 		he_seterrev(ev, _HE_NOT_ALLOWED);
    636 		return (-1);
    637 	}
    638 	ev->num = history_def_getsize(h->h_ref);
    639 	if (ev->num < -1) {
    640 		he_seterrev(ev, _HE_SIZE_NEGATIVE);
    641 		return (-1);
    642 	}
    643 	return (0);
    644 }
    645 
    646 
    647 /* history_setunique():
    648  *	Set if adjacent equal events should not be entered in history.
    649  */
    650 private int
    651 history_setunique(History *h, HistEvent *ev, int uni)
    652 {
    653 
    654 	if (h->h_next != history_def_next) {
    655 		he_seterrev(ev, _HE_NOT_ALLOWED);
    656 		return (-1);
    657 	}
    658 	history_def_setunique(h->h_ref, uni);
    659 	return (0);
    660 }
    661 
    662 
    663 /* history_getunique():
    664  *	Get if adjacent equal events should not be entered in history.
    665  */
    666 private int
    667 history_getunique(History *h, HistEvent *ev)
    668 {
    669 	if (h->h_next != history_def_next) {
    670 		he_seterrev(ev, _HE_NOT_ALLOWED);
    671 		return (-1);
    672 	}
    673 	ev->num = history_def_getunique(h->h_ref);
    674 	return (0);
    675 }
    676 
    677 
    678 /* history_set_fun():
    679  *	Set history functions
    680  */
    681 private int
    682 history_set_fun(History *h, History *nh)
    683 {
    684 	HistEvent ev;
    685 
    686 	if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
    687 	    nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
    688 	    nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
    689 	    nh->h_del == NULL || nh->h_ref == NULL) {
    690 		if (h->h_next != history_def_next) {
    691 			history_def_init(&h->h_ref, &ev, 0);
    692 			h->h_first = history_def_first;
    693 			h->h_next = history_def_next;
    694 			h->h_last = history_def_last;
    695 			h->h_prev = history_def_prev;
    696 			h->h_curr = history_def_curr;
    697 			h->h_set = history_def_set;
    698 			h->h_clear = history_def_clear;
    699 			h->h_enter = history_def_enter;
    700 			h->h_add = history_def_add;
    701 			h->h_del = history_def_del;
    702 		}
    703 		return (-1);
    704 	}
    705 	if (h->h_next == history_def_next)
    706 		history_def_clear(h->h_ref, &ev);
    707 
    708 	h->h_ent = -1;
    709 	h->h_first = nh->h_first;
    710 	h->h_next = nh->h_next;
    711 	h->h_last = nh->h_last;
    712 	h->h_prev = nh->h_prev;
    713 	h->h_curr = nh->h_curr;
    714 	h->h_set = nh->h_set;
    715 	h->h_clear = nh->h_clear;
    716 	h->h_enter = nh->h_enter;
    717 	h->h_add = nh->h_add;
    718 	h->h_del = nh->h_del;
    719 
    720 	return (0);
    721 }
    722 
    723 
    724 /* history_load():
    725  *	History load function
    726  */
    727 private int
    728 history_load(History *h, const char *fname)
    729 {
    730 	FILE *fp;
    731 	char *line;
    732 	size_t sz, max_size;
    733 	char *ptr;
    734 	int i = -1;
    735 	HistEvent ev;
    736 
    737 	if ((fp = fopen(fname, "r")) == NULL)
    738 		return (i);
    739 
    740 	if ((line = fgetln(fp, &sz)) == NULL)
    741 		goto done;
    742 
    743 	if (strncmp(line, hist_cookie, sz) != 0)
    744 		goto done;
    745 
    746 	ptr = h_malloc(max_size = 1024);
    747 	if (ptr == NULL)
    748 		goto done;
    749 	for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
    750 		char c = line[sz];
    751 
    752 		if (sz != 0 && line[sz - 1] == '\n')
    753 			line[--sz] = '\0';
    754 		else
    755 			line[sz] = '\0';
    756 
    757 		if (max_size < sz) {
    758 			char *nptr;
    759 			max_size = (sz + 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) strunvis(ptr, line);
    768 		line[sz] = c;
    769 		if (HENTER(h, &ev, ptr) == -1) {
    770 			i = -1;
    771 			goto oomem;
    772 		}
    773 	}
    774 oomem:
    775 	h_free((ptr_t)ptr);
    776 done:
    777 	(void) fclose(fp);
    778 	return (i);
    779 }
    780 
    781 
    782 /* history_save():
    783  *	History save function
    784  */
    785 private int
    786 history_save(History *h, const char *fname)
    787 {
    788 	FILE *fp;
    789 	HistEvent ev;
    790 	int i = -1, retval;
    791 	size_t len, max_size;
    792 	char *ptr;
    793 
    794 	if ((fp = fopen(fname, "w")) == NULL)
    795 		return (-1);
    796 
    797 	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
    798 		goto done;
    799 	if (fputs(hist_cookie, fp) == EOF)
    800 		goto done;
    801 	ptr = h_malloc(max_size = 1024);
    802 	if (ptr == NULL)
    803 		goto done;
    804 	for (i = 0, retval = HLAST(h, &ev);
    805 	    retval != -1;
    806 	    retval = HPREV(h, &ev), i++) {
    807 		len = strlen(ev.str) * 4;
    808 		if (len >= max_size) {
    809 			char *nptr;
    810 			max_size = (len + 1024) & ~1023;
    811 			nptr = h_realloc(ptr, max_size);
    812 			if (nptr == NULL) {
    813 				i = -1;
    814 				goto oomem;
    815 			}
    816 			ptr = nptr;
    817 		}
    818 		(void) strvis(ptr, ev.str, VIS_WHITE);
    819 		(void) fprintf(fp, "%s\n", ptr);
    820 	}
    821 oomem:
    822 	h_free((ptr_t)ptr);
    823 done:
    824 	(void) fclose(fp);
    825 	return (i);
    826 }
    827 
    828 
    829 /* history_prev_event():
    830  *	Find the previous event, with number given
    831  */
    832 private int
    833 history_prev_event(History *h, HistEvent *ev, int num)
    834 {
    835 	int retval;
    836 
    837 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
    838 		if (ev->num == num)
    839 			return (0);
    840 
    841 	he_seterrev(ev, _HE_NOT_FOUND);
    842 	return (-1);
    843 }
    844 
    845 
    846 private int
    847 history_next_evdata(History *h, HistEvent *ev, int num, void **d)
    848 {
    849 	int retval;
    850 
    851 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
    852 		if (num-- <= 0) {
    853 			if (d)
    854 				*d = ((history_t *)h->h_ref)->cursor->data;
    855 			return (0);
    856 		}
    857 
    858 	he_seterrev(ev, _HE_NOT_FOUND);
    859 	return (-1);
    860 }
    861 
    862 
    863 /* history_next_event():
    864  *	Find the next event, with number given
    865  */
    866 private int
    867 history_next_event(History *h, HistEvent *ev, int num)
    868 {
    869 	int retval;
    870 
    871 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
    872 		if (ev->num == num)
    873 			return (0);
    874 
    875 	he_seterrev(ev, _HE_NOT_FOUND);
    876 	return (-1);
    877 }
    878 
    879 
    880 /* history_prev_string():
    881  *	Find the previous event beginning with string
    882  */
    883 private int
    884 history_prev_string(History *h, HistEvent *ev, const char *str)
    885 {
    886 	size_t len = strlen(str);
    887 	int retval;
    888 
    889 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
    890 		if (strncmp(str, ev->str, len) == 0)
    891 			return (0);
    892 
    893 	he_seterrev(ev, _HE_NOT_FOUND);
    894 	return (-1);
    895 }
    896 
    897 
    898 /* history_next_string():
    899  *	Find the next event beginning with string
    900  */
    901 private int
    902 history_next_string(History *h, HistEvent *ev, const char *str)
    903 {
    904 	size_t len = strlen(str);
    905 	int retval;
    906 
    907 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
    908 		if (strncmp(str, ev->str, len) == 0)
    909 			return (0);
    910 
    911 	he_seterrev(ev, _HE_NOT_FOUND);
    912 	return (-1);
    913 }
    914 
    915 
    916 /* history():
    917  *	User interface to history functions.
    918  */
    919 int
    920 history(History *h, HistEvent *ev, int fun, ...)
    921 {
    922 	va_list va;
    923 	const char *str;
    924 	int retval;
    925 
    926 	va_start(va, fun);
    927 
    928 	he_seterrev(ev, _HE_OK);
    929 
    930 	switch (fun) {
    931 	case H_GETSIZE:
    932 		retval = history_getsize(h, ev);
    933 		break;
    934 
    935 	case H_SETSIZE:
    936 		retval = history_setsize(h, ev, va_arg(va, int));
    937 		break;
    938 
    939 	case H_GETUNIQUE:
    940 		retval = history_getunique(h, ev);
    941 		break;
    942 
    943 	case H_SETUNIQUE:
    944 		retval = history_setunique(h, ev, va_arg(va, int));
    945 		break;
    946 
    947 	case H_ADD:
    948 		str = va_arg(va, const char *);
    949 		retval = HADD(h, ev, str);
    950 		break;
    951 
    952 	case H_DEL:
    953 		retval = HDEL(h, ev, va_arg(va, const int));
    954 		break;
    955 
    956 	case H_ENTER:
    957 		str = va_arg(va, const char *);
    958 		if ((retval = HENTER(h, ev, str)) != -1)
    959 			h->h_ent = ev->num;
    960 		break;
    961 
    962 	case H_APPEND:
    963 		str = va_arg(va, const char *);
    964 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
    965 			retval = HADD(h, ev, str);
    966 		break;
    967 
    968 	case H_FIRST:
    969 		retval = HFIRST(h, ev);
    970 		break;
    971 
    972 	case H_NEXT:
    973 		retval = HNEXT(h, ev);
    974 		break;
    975 
    976 	case H_LAST:
    977 		retval = HLAST(h, ev);
    978 		break;
    979 
    980 	case H_PREV:
    981 		retval = HPREV(h, ev);
    982 		break;
    983 
    984 	case H_CURR:
    985 		retval = HCURR(h, ev);
    986 		break;
    987 
    988 	case H_SET:
    989 		retval = HSET(h, ev, va_arg(va, const int));
    990 		break;
    991 
    992 	case H_CLEAR:
    993 		HCLEAR(h, ev);
    994 		retval = 0;
    995 		break;
    996 
    997 	case H_LOAD:
    998 		retval = history_load(h, va_arg(va, const char *));
    999 		if (retval == -1)
   1000 			he_seterrev(ev, _HE_HIST_READ);
   1001 		break;
   1002 
   1003 	case H_SAVE:
   1004 		retval = history_save(h, va_arg(va, const char *));
   1005 		if (retval == -1)
   1006 			he_seterrev(ev, _HE_HIST_WRITE);
   1007 		break;
   1008 
   1009 	case H_PREV_EVENT:
   1010 		retval = history_prev_event(h, ev, va_arg(va, int));
   1011 		break;
   1012 
   1013 	case H_NEXT_EVENT:
   1014 		retval = history_next_event(h, ev, va_arg(va, int));
   1015 		break;
   1016 
   1017 	case H_PREV_STR:
   1018 		retval = history_prev_string(h, ev, va_arg(va, const char *));
   1019 		break;
   1020 
   1021 	case H_NEXT_STR:
   1022 		retval = history_next_string(h, ev, va_arg(va, const char *));
   1023 		break;
   1024 
   1025 	case H_FUNC:
   1026 	{
   1027 		History hf;
   1028 
   1029 		hf.h_ref = va_arg(va, ptr_t);
   1030 		h->h_ent = -1;
   1031 		hf.h_first = va_arg(va, history_gfun_t);
   1032 		hf.h_next = va_arg(va, history_gfun_t);
   1033 		hf.h_last = va_arg(va, history_gfun_t);
   1034 		hf.h_prev = va_arg(va, history_gfun_t);
   1035 		hf.h_curr = va_arg(va, history_gfun_t);
   1036 		hf.h_set = va_arg(va, history_sfun_t);
   1037 		hf.h_clear = va_arg(va, history_vfun_t);
   1038 		hf.h_enter = va_arg(va, history_efun_t);
   1039 		hf.h_add = va_arg(va, history_efun_t);
   1040 		hf.h_del = va_arg(va, history_sfun_t);
   1041 
   1042 		if ((retval = history_set_fun(h, &hf)) == -1)
   1043 			he_seterrev(ev, _HE_PARAM_MISSING);
   1044 		break;
   1045 	}
   1046 
   1047 	case H_END:
   1048 		history_end(h);
   1049 		retval = 0;
   1050 		break;
   1051 
   1052 	case H_NEXT_EVDATA:
   1053 	{
   1054 		int num = va_arg(va, int);
   1055 		void **d = va_arg(va, void **);
   1056 		retval = history_next_evdata(h, ev, num, d);
   1057 		break;
   1058 	}
   1059 
   1060 	case H_DELDATA:
   1061 	{
   1062 		int num = va_arg(va, int);
   1063 		void **d = va_arg(va, void **);
   1064 		retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d);
   1065 		break;
   1066 	}
   1067 
   1068 	case H_REPLACE: /* only use after H_NEXT_EVDATA */
   1069 	{
   1070 		const char *line = va_arg(va, const char *);
   1071 		void *d = va_arg(va, void *);
   1072 		const char *s;
   1073 		if(!line || !(s = strdup(line))) {
   1074 			retval = -1;
   1075 			break;
   1076 		}
   1077 		((history_t *)h->h_ref)->cursor->ev.str = s;
   1078 		((history_t *)h->h_ref)->cursor->data = d;
   1079 		retval = 0;
   1080 		break;
   1081 	}
   1082 
   1083 	default:
   1084 		retval = -1;
   1085 		he_seterrev(ev, _HE_UNKNOWN);
   1086 		break;
   1087 	}
   1088 	va_end(va);
   1089 	return retval;
   1090 }
   1091