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