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