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