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