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