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