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