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