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