Home | History | Annotate | Line # | Download | only in libedit
history.c revision 1.50
      1 /*	$NetBSD: history.c,v 1.50 2016/02/15 15:53:45 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.50 2016/02/15 15:53:45 christos 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 llen;
    736 	ssize_t sz;
    737 	size_t max_size;
    738 	char *ptr;
    739 	int i = -1;
    740 	TYPE(HistEvent) ev;
    741 #ifdef WIDECHAR
    742 	static ct_buffer_t conv;
    743 #endif
    744 
    745 	if ((fp = fopen(fname, "r")) == NULL)
    746 		return i;
    747 
    748 	line = NULL;
    749 	llen = 0;
    750 	if ((sz = getline(&line, &llen, fp)) == -1)
    751 		goto done;
    752 
    753 	if (strncmp(line, hist_cookie, (size_t)sz) != 0)
    754 		goto done;
    755 
    756 	ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
    757 	if (ptr == NULL)
    758 		goto done;
    759 	free(line);
    760 	for (i = 0; (sz = getline(&line, &llen, fp)) != -1; i++) {
    761 		if (sz > 0 && line[sz - 1] == '\n')
    762 			line[--sz] = '\0';
    763 		if (max_size < (size_t)sz) {
    764 			char *nptr;
    765 			max_size = ((size_t)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 		if (HENTER(h, &ev, ct_decode_string(ptr, &conv)) == -1) {
    775 			i = -1;
    776 			goto oomem;
    777 		}
    778 		free(line);
    779 	}
    780 oomem:
    781 	h_free(ptr);
    782 done:
    783 	free(line);
    784 	(void) fclose(fp);
    785 	return i;
    786 }
    787 
    788 
    789 /* history_save_fp():
    790  *	TYPE(History) save function
    791  */
    792 private int
    793 history_save_fp(TYPE(History) *h, FILE *fp)
    794 {
    795 	TYPE(HistEvent) ev;
    796 	int i = -1, retval;
    797 	size_t len, max_size;
    798 	char *ptr;
    799 	const char *str;
    800 #ifdef WIDECHAR
    801 	static ct_buffer_t conv;
    802 #endif
    803 
    804 	if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
    805 		goto done;
    806 	if (fputs(hist_cookie, fp) == EOF)
    807 		goto done;
    808 	ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
    809 	if (ptr == NULL)
    810 		goto done;
    811 	for (i = 0, retval = HLAST(h, &ev);
    812 	    retval != -1;
    813 	    retval = HPREV(h, &ev), i++) {
    814 		str = ct_encode_string(ev.str, &conv);
    815 		len = strlen(str) * 4 + 1;
    816 		if (len > max_size) {
    817 			char *nptr;
    818 			max_size = (len + 1024) & (size_t)~1023;
    819 			nptr = h_realloc(ptr, max_size * sizeof(*ptr));
    820 			if (nptr == NULL) {
    821 				i = -1;
    822 				goto oomem;
    823 			}
    824 			ptr = nptr;
    825 		}
    826 		(void) strvis(ptr, str, VIS_WHITE);
    827 		(void) fprintf(fp, "%s\n", ptr);
    828 	}
    829 oomem:
    830 	h_free(ptr);
    831 done:
    832 	return i;
    833 }
    834 
    835 
    836 /* history_save():
    837  *    History save function
    838  */
    839 private int
    840 history_save(TYPE(History) *h, const char *fname)
    841 {
    842     FILE *fp;
    843     int i;
    844 
    845     if ((fp = fopen(fname, "w")) == NULL)
    846 	return -1;
    847 
    848     i = history_save_fp(h, fp);
    849 
    850     (void) fclose(fp);
    851     return i;
    852 }
    853 
    854 
    855 /* history_prev_event():
    856  *	Find the previous event, with number given
    857  */
    858 private int
    859 history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
    860 {
    861 	int retval;
    862 
    863 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
    864 		if (ev->num == num)
    865 			return 0;
    866 
    867 	he_seterrev(ev, _HE_NOT_FOUND);
    868 	return -1;
    869 }
    870 
    871 
    872 private int
    873 history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d)
    874 {
    875 	int retval;
    876 
    877 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
    878 		if (ev->num == num) {
    879 			if (d)
    880 				*d = ((history_t *)h->h_ref)->cursor->data;
    881 			return 0;
    882 		}
    883 
    884 	he_seterrev(ev, _HE_NOT_FOUND);
    885 	return -1;
    886 }
    887 
    888 
    889 /* history_next_event():
    890  *	Find the next event, with number given
    891  */
    892 private int
    893 history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
    894 {
    895 	int retval;
    896 
    897 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
    898 		if (ev->num == num)
    899 			return 0;
    900 
    901 	he_seterrev(ev, _HE_NOT_FOUND);
    902 	return -1;
    903 }
    904 
    905 
    906 /* history_prev_string():
    907  *	Find the previous event beginning with string
    908  */
    909 private int
    910 history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
    911 {
    912 	size_t len = Strlen(str);
    913 	int retval;
    914 
    915 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
    916 		if (Strncmp(str, ev->str, len) == 0)
    917 			return 0;
    918 
    919 	he_seterrev(ev, _HE_NOT_FOUND);
    920 	return -1;
    921 }
    922 
    923 
    924 /* history_next_string():
    925  *	Find the next event beginning with string
    926  */
    927 private int
    928 history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
    929 {
    930 	size_t len = Strlen(str);
    931 	int retval;
    932 
    933 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
    934 		if (Strncmp(str, ev->str, len) == 0)
    935 			return 0;
    936 
    937 	he_seterrev(ev, _HE_NOT_FOUND);
    938 	return -1;
    939 }
    940 
    941 
    942 /* history():
    943  *	User interface to history functions.
    944  */
    945 int
    946 FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...)
    947 {
    948 	va_list va;
    949 	const Char *str;
    950 	int retval;
    951 
    952 	va_start(va, fun);
    953 
    954 	he_seterrev(ev, _HE_OK);
    955 
    956 	switch (fun) {
    957 	case H_GETSIZE:
    958 		retval = history_getsize(h, ev);
    959 		break;
    960 
    961 	case H_SETSIZE:
    962 		retval = history_setsize(h, ev, va_arg(va, int));
    963 		break;
    964 
    965 	case H_GETUNIQUE:
    966 		retval = history_getunique(h, ev);
    967 		break;
    968 
    969 	case H_SETUNIQUE:
    970 		retval = history_setunique(h, ev, va_arg(va, int));
    971 		break;
    972 
    973 	case H_ADD:
    974 		str = va_arg(va, const Char *);
    975 		retval = HADD(h, ev, str);
    976 		break;
    977 
    978 	case H_DEL:
    979 		retval = HDEL(h, ev, va_arg(va, const int));
    980 		break;
    981 
    982 	case H_ENTER:
    983 		str = va_arg(va, const Char *);
    984 		if ((retval = HENTER(h, ev, str)) != -1)
    985 			h->h_ent = ev->num;
    986 		break;
    987 
    988 	case H_APPEND:
    989 		str = va_arg(va, const Char *);
    990 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
    991 			retval = HADD(h, ev, str);
    992 		break;
    993 
    994 	case H_FIRST:
    995 		retval = HFIRST(h, ev);
    996 		break;
    997 
    998 	case H_NEXT:
    999 		retval = HNEXT(h, ev);
   1000 		break;
   1001 
   1002 	case H_LAST:
   1003 		retval = HLAST(h, ev);
   1004 		break;
   1005 
   1006 	case H_PREV:
   1007 		retval = HPREV(h, ev);
   1008 		break;
   1009 
   1010 	case H_CURR:
   1011 		retval = HCURR(h, ev);
   1012 		break;
   1013 
   1014 	case H_SET:
   1015 		retval = HSET(h, ev, va_arg(va, const int));
   1016 		break;
   1017 
   1018 	case H_CLEAR:
   1019 		HCLEAR(h, ev);
   1020 		retval = 0;
   1021 		break;
   1022 
   1023 	case H_LOAD:
   1024 		retval = history_load(h, va_arg(va, const char *));
   1025 		if (retval == -1)
   1026 			he_seterrev(ev, _HE_HIST_READ);
   1027 		break;
   1028 
   1029 	case H_SAVE:
   1030 		retval = history_save(h, va_arg(va, const char *));
   1031 		if (retval == -1)
   1032 			he_seterrev(ev, _HE_HIST_WRITE);
   1033 		break;
   1034 
   1035 	case H_SAVE_FP:
   1036 		retval = history_save_fp(h, va_arg(va, FILE *));
   1037 		if (retval == -1)
   1038 		    he_seterrev(ev, _HE_HIST_WRITE);
   1039 		break;
   1040 
   1041 	case H_PREV_EVENT:
   1042 		retval = history_prev_event(h, ev, va_arg(va, int));
   1043 		break;
   1044 
   1045 	case H_NEXT_EVENT:
   1046 		retval = history_next_event(h, ev, va_arg(va, int));
   1047 		break;
   1048 
   1049 	case H_PREV_STR:
   1050 		retval = history_prev_string(h, ev, va_arg(va, const Char *));
   1051 		break;
   1052 
   1053 	case H_NEXT_STR:
   1054 		retval = history_next_string(h, ev, va_arg(va, const Char *));
   1055 		break;
   1056 
   1057 	case H_FUNC:
   1058 	{
   1059 		TYPE(History) hf;
   1060 
   1061 		hf.h_ref = va_arg(va, void *);
   1062 		h->h_ent = -1;
   1063 		hf.h_first = va_arg(va, history_gfun_t);
   1064 		hf.h_next = va_arg(va, history_gfun_t);
   1065 		hf.h_last = va_arg(va, history_gfun_t);
   1066 		hf.h_prev = va_arg(va, history_gfun_t);
   1067 		hf.h_curr = va_arg(va, history_gfun_t);
   1068 		hf.h_set = va_arg(va, history_sfun_t);
   1069 		hf.h_clear = va_arg(va, history_vfun_t);
   1070 		hf.h_enter = va_arg(va, history_efun_t);
   1071 		hf.h_add = va_arg(va, history_efun_t);
   1072 		hf.h_del = va_arg(va, history_sfun_t);
   1073 
   1074 		if ((retval = history_set_fun(h, &hf)) == -1)
   1075 			he_seterrev(ev, _HE_PARAM_MISSING);
   1076 		break;
   1077 	}
   1078 
   1079 	case H_END:
   1080 		FUN(history,end)(h);
   1081 		retval = 0;
   1082 		break;
   1083 
   1084 	case H_NEXT_EVDATA:
   1085 	{
   1086 		int num = va_arg(va, int);
   1087 		void **d = va_arg(va, void **);
   1088 		retval = history_next_evdata(h, ev, num, d);
   1089 		break;
   1090 	}
   1091 
   1092 	case H_DELDATA:
   1093 	{
   1094 		int num = va_arg(va, int);
   1095 		void **d = va_arg(va, void **);
   1096 		retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d);
   1097 		break;
   1098 	}
   1099 
   1100 	case H_REPLACE: /* only use after H_NEXT_EVDATA */
   1101 	{
   1102 		const Char *line = va_arg(va, const Char *);
   1103 		void *d = va_arg(va, void *);
   1104 		const Char *s;
   1105 		if(!line || !(s = Strdup(line))) {
   1106 			retval = -1;
   1107 			break;
   1108 		}
   1109 		((history_t *)h->h_ref)->cursor->ev.str = s;
   1110 		((history_t *)h->h_ref)->cursor->data = d;
   1111 		retval = 0;
   1112 		break;
   1113 	}
   1114 
   1115 	default:
   1116 		retval = -1;
   1117 		he_seterrev(ev, _HE_UNKNOWN);
   1118 		break;
   1119 	}
   1120 	va_end(va);
   1121 	return retval;
   1122 }
   1123