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