mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-25 14:06:27 +00:00 
			
		
		
		
	- restructured build tree and makefiles to eliminate recursion problems - support for embedded modules - support for static builds - simpler cross-compilation support - simpler module/loader interface (no exported symbols) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@40722 65c4cc65-6c06-0410-ace0-fbb531ad65f3
		
			
				
	
	
		
			876 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			876 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*	$NetBSD: history.c,v 1.19 2002/03/18 16:00:54 christos Exp $	*/
 | |
| 
 | |
| /*-
 | |
|  * Copyright (c) 1992, 1993
 | |
|  *	The Regents of the University of California.  All rights reserved.
 | |
|  *
 | |
|  * This code is derived from software contributed to Berkeley by
 | |
|  * Christos Zoulas of Cornell University.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  * 3. All advertising materials mentioning features or use of this software
 | |
|  *    must display the following acknowledgement:
 | |
|  *	This product includes software developed by the University of
 | |
|  *	California, Berkeley and its contributors.
 | |
|  * 4. Neither the name of the University nor the names of its contributors
 | |
|  *    may be used to endorse or promote products derived from this software
 | |
|  *    without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 | |
|  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 | |
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | |
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | |
|  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | |
|  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | |
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | |
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|  * SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| #if !defined(lint) && !defined(SCCSID)
 | |
| #if 0
 | |
| static char sccsid[] = "@(#)history.c	8.1 (Berkeley) 6/4/93";
 | |
| #else
 | |
| __RCSID("$NetBSD: history.c,v 1.19 2002/03/18 16:00:54 christos Exp $");
 | |
| #endif
 | |
| #endif /* not lint && not SCCSID */
 | |
| 
 | |
| /*
 | |
|  * hist.c: History access functions
 | |
|  */
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdarg.h>
 | |
| #ifdef HAVE_VIS_H
 | |
| #include <vis.h>
 | |
| #else
 | |
| #include "np/vis.h"
 | |
| #endif
 | |
| #include <sys/stat.h>
 | |
| 
 | |
| static const char hist_cookie[] = "_HiStOrY_V2_\n";
 | |
| 
 | |
| #include "histedit.h"
 | |
| 
 | |
| typedef int (*history_gfun_t)(ptr_t, HistEvent *);
 | |
| typedef int (*history_efun_t)(ptr_t, HistEvent *, const char *);
 | |
| typedef void (*history_vfun_t)(ptr_t, HistEvent *);
 | |
| typedef int (*history_sfun_t)(ptr_t, HistEvent *, const int);
 | |
| 
 | |
| struct history {
 | |
| 	ptr_t h_ref;		/* Argument for history fcns	 */
 | |
| 	int h_ent;		/* Last entry point for history	 */
 | |
| 	history_gfun_t h_first;	/* Get the first element	 */
 | |
| 	history_gfun_t h_next;	/* Get the next element		 */
 | |
| 	history_gfun_t h_last;	/* Get the last element		 */
 | |
| 	history_gfun_t h_prev;	/* Get the previous element	 */
 | |
| 	history_gfun_t h_curr;	/* Get the current element	 */
 | |
| 	history_sfun_t h_set;	/* Set the current element	 */
 | |
| 	history_vfun_t h_clear;	/* Clear the history list	 */
 | |
| 	history_efun_t h_enter;	/* Add an element		 */
 | |
| 	history_efun_t h_add;	/* Append to an element		 */
 | |
| };
 | |
| #define	HNEXT(h, ev)		(*(h)->h_next)((h)->h_ref, ev)
 | |
| #define	HFIRST(h, ev)		(*(h)->h_first)((h)->h_ref, ev)
 | |
| #define	HPREV(h, ev)		(*(h)->h_prev)((h)->h_ref, ev)
 | |
| #define	HLAST(h, ev)		(*(h)->h_last)((h)->h_ref, ev)
 | |
| #define	HCURR(h, ev)		(*(h)->h_curr)((h)->h_ref, ev)
 | |
| #define	HSET(h, ev, n)		(*(h)->h_set)((h)->h_ref, ev, n)
 | |
| #define	HCLEAR(h, ev)		(*(h)->h_clear)((h)->h_ref, ev)
 | |
| #define	HENTER(h, ev, str)	(*(h)->h_enter)((h)->h_ref, ev, str)
 | |
| #define	HADD(h, ev, str)	(*(h)->h_add)((h)->h_ref, ev, str)
 | |
| 
 | |
| #define	h_malloc(a)	malloc(a)
 | |
| #define	h_realloc(a, b)	realloc((a), (b))
 | |
| #define	h_free(a)	free(a)
 | |
| 
 | |
| typedef struct {
 | |
|     int		num;
 | |
|     char	*str;
 | |
| } HistEventPrivate;
 | |
| 
 | |
| 
 | |
| 
 | |
| private int history_setsize(History *, HistEvent *, int);
 | |
| private int history_getsize(History *, HistEvent *);
 | |
| private int history_set_fun(History *, History *);
 | |
| private int history_load(History *, const char *);
 | |
| private int history_save(History *, const char *);
 | |
| private int history_prev_event(History *, HistEvent *, int);
 | |
| private int history_next_event(History *, HistEvent *, int);
 | |
| private int history_next_string(History *, HistEvent *, const char *);
 | |
| private int history_prev_string(History *, HistEvent *, const char *);
 | |
| 
 | |
| 
 | |
| /***********************************************************************/
 | |
| 
 | |
| /*
 | |
|  * Builtin- history implementation
 | |
|  */
 | |
| typedef struct hentry_t {
 | |
| 	HistEvent ev;		/* What we return		 */
 | |
| 	struct hentry_t *next;	/* Next entry			 */
 | |
| 	struct hentry_t *prev;	/* Previous entry		 */
 | |
| }        hentry_t;
 | |
| 
 | |
| typedef struct history_t {
 | |
| 	hentry_t list;		/* Fake list header element	 */
 | |
| 	hentry_t *cursor;	/* Current element in the list	 */
 | |
| 	int max;		/* Maximum number of events	 */
 | |
| 	int cur;		/* Current number of events	 */
 | |
| 	int eventid;		/* For generation of unique event id	 */
 | |
| }         history_t;
 | |
| 
 | |
| private int history_def_first(ptr_t, HistEvent *);
 | |
| private int history_def_last(ptr_t, HistEvent *);
 | |
| private int history_def_next(ptr_t, HistEvent *);
 | |
| private int history_def_prev(ptr_t, HistEvent *);
 | |
| private int history_def_curr(ptr_t, HistEvent *);
 | |
| private int history_def_set(ptr_t, HistEvent *, const int n);
 | |
| private int history_def_enter(ptr_t, HistEvent *, const char *);
 | |
| private int history_def_add(ptr_t, HistEvent *, const char *);
 | |
| private void history_def_init(ptr_t *, HistEvent *, int);
 | |
| private void history_def_clear(ptr_t, HistEvent *);
 | |
| private int history_def_insert(history_t *, HistEvent *, const char *);
 | |
| private void history_def_delete(history_t *, HistEvent *, hentry_t *);
 | |
| 
 | |
| #define	history_def_setsize(p, num)(void) (((history_t *) p)->max = (num))
 | |
| #define	history_def_getsize(p)  (((history_t *) p)->cur)
 | |
| 
 | |
| #define	he_strerror(code)	he_errlist[code]
 | |
| #define	he_seterrev(evp, code)	{\
 | |
| 				    evp->num = code;\
 | |
| 				    evp->str = he_strerror(code);\
 | |
| 				}
 | |
| 
 | |
| /* error messages */
 | |
| static const char *const he_errlist[] = {
 | |
| 	"OK",
 | |
| 	"unknown error",
 | |
| 	"malloc() failed",
 | |
| 	"first event not found",
 | |
| 	"last event not found",
 | |
| 	"empty list",
 | |
| 	"no next event",
 | |
| 	"no previous event",
 | |
| 	"current event is invalid",
 | |
| 	"event not found",
 | |
| 	"can't read history from file",
 | |
| 	"can't write history",
 | |
| 	"required parameter(s) not supplied",
 | |
| 	"history size negative",
 | |
| 	"function not allowed with other history-functions-set the default",
 | |
| 	"bad parameters"
 | |
| };
 | |
| /* error codes */
 | |
| #define	_HE_OK                   0
 | |
| #define	_HE_UNKNOWN		 1
 | |
| #define	_HE_MALLOC_FAILED        2
 | |
| #define	_HE_FIRST_NOTFOUND       3
 | |
| #define	_HE_LAST_NOTFOUND        4
 | |
| #define	_HE_EMPTY_LIST           5
 | |
| #define	_HE_END_REACHED          6
 | |
| #define	_HE_START_REACHED	 7
 | |
| #define	_HE_CURR_INVALID	 8
 | |
| #define	_HE_NOT_FOUND		 9
 | |
| #define	_HE_HIST_READ		10
 | |
| #define	_HE_HIST_WRITE		11
 | |
| #define	_HE_PARAM_MISSING	12
 | |
| #define	_HE_SIZE_NEGATIVE	13
 | |
| #define	_HE_NOT_ALLOWED		14
 | |
| #define	_HE_BAD_PARAM		15
 | |
| 
 | |
| /* history_def_first():
 | |
|  *	Default function to return the first event in the history.
 | |
|  */
 | |
| private int
 | |
| history_def_first(ptr_t p, HistEvent *ev)
 | |
| {
 | |
| 	history_t *h = (history_t *) p;
 | |
| 
 | |
| 	h->cursor = h->list.next;
 | |
| 	if (h->cursor != &h->list)
 | |
| 		*ev = h->cursor->ev;
 | |
| 	else {
 | |
| 		he_seterrev(ev, _HE_FIRST_NOTFOUND);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_def_last():
 | |
|  *	Default function to return the last event in the history.
 | |
|  */
 | |
| private int
 | |
| history_def_last(ptr_t p, HistEvent *ev)
 | |
| {
 | |
| 	history_t *h = (history_t *) p;
 | |
| 
 | |
| 	h->cursor = h->list.prev;
 | |
| 	if (h->cursor != &h->list)
 | |
| 		*ev = h->cursor->ev;
 | |
| 	else {
 | |
| 		he_seterrev(ev, _HE_LAST_NOTFOUND);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_def_next():
 | |
|  *	Default function to return the next event in the history.
 | |
|  */
 | |
| private int
 | |
| history_def_next(ptr_t p, HistEvent *ev)
 | |
| {
 | |
| 	history_t *h = (history_t *) p;
 | |
| 
 | |
| 	if (h->cursor != &h->list)
 | |
| 		h->cursor = h->cursor->next;
 | |
| 	else {
 | |
| 		he_seterrev(ev, _HE_EMPTY_LIST);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 
 | |
| 	if (h->cursor != &h->list)
 | |
| 		*ev = h->cursor->ev;
 | |
| 	else {
 | |
| 		he_seterrev(ev, _HE_END_REACHED);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_def_prev():
 | |
|  *	Default function to return the previous event in the history.
 | |
|  */
 | |
| private int
 | |
| history_def_prev(ptr_t p, HistEvent *ev)
 | |
| {
 | |
| 	history_t *h = (history_t *) p;
 | |
| 
 | |
| 	if (h->cursor != &h->list)
 | |
| 		h->cursor = h->cursor->prev;
 | |
| 	else {
 | |
| 		he_seterrev(ev,
 | |
| 		    (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 
 | |
| 	if (h->cursor != &h->list)
 | |
| 		*ev = h->cursor->ev;
 | |
| 	else {
 | |
| 		he_seterrev(ev, _HE_START_REACHED);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_def_curr():
 | |
|  *	Default function to return the current event in the history.
 | |
|  */
 | |
| private int
 | |
| history_def_curr(ptr_t p, HistEvent *ev)
 | |
| {
 | |
| 	history_t *h = (history_t *) p;
 | |
| 
 | |
| 	if (h->cursor != &h->list)
 | |
| 		*ev = h->cursor->ev;
 | |
| 	else {
 | |
| 		he_seterrev(ev,
 | |
| 		    (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_def_set():
 | |
|  *	Default function to set the current event in the history to the
 | |
|  *	given one.
 | |
|  */
 | |
| private int
 | |
| history_def_set(ptr_t p, HistEvent *ev, const int n)
 | |
| {
 | |
| 	history_t *h = (history_t *) p;
 | |
| 
 | |
| 	if (h->cur == 0) {
 | |
| 		he_seterrev(ev, _HE_EMPTY_LIST);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 	if (h->cursor == &h->list || h->cursor->ev.num != n) {
 | |
| 		for (h->cursor = h->list.next; h->cursor != &h->list;
 | |
| 		    h->cursor = h->cursor->next)
 | |
| 			if (h->cursor->ev.num == n)
 | |
| 				break;
 | |
| 	}
 | |
| 	if (h->cursor == &h->list) {
 | |
| 		he_seterrev(ev, _HE_NOT_FOUND);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_def_add():
 | |
|  *	Append string to element
 | |
|  */
 | |
| private int
 | |
| history_def_add(ptr_t p, HistEvent *ev, const char *str)
 | |
| {
 | |
| 	history_t *h = (history_t *) p;
 | |
| 	size_t len;
 | |
| 	char *s;
 | |
| 	HistEventPrivate *evp = (void *)&h->cursor->ev;
 | |
| 
 | |
| 	if (h->cursor == &h->list)
 | |
| 		return (history_def_enter(p, ev, str));
 | |
| 	len = strlen(evp->str) + strlen(str) + 1;
 | |
| 	s = (char *) h_malloc(len);
 | |
| 	if (!s) {
 | |
| 		he_seterrev(ev, _HE_MALLOC_FAILED);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 	(void) strlcpy(s, h->cursor->ev.str, len);
 | |
| 	(void) strlcat(s, str, len);
 | |
| 	h_free(evp->str);
 | |
| 	evp->str = s;
 | |
| 	*ev = h->cursor->ev;
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_def_delete():
 | |
|  *	Delete element hp of the h list
 | |
|  */
 | |
| /* ARGSUSED */
 | |
| private void
 | |
| history_def_delete(history_t *h, HistEvent *ev, hentry_t *hp)
 | |
| {
 | |
| 	HistEventPrivate *evp = (void *)&hp->ev;
 | |
| 	if (hp == &h->list)
 | |
| 		abort();
 | |
| 	hp->prev->next = hp->next;
 | |
| 	hp->next->prev = hp->prev;
 | |
| 	h_free((ptr_t) evp->str);
 | |
| 	h_free(hp);
 | |
| 	h->cur--;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_def_insert():
 | |
|  *	Insert element with string str in the h list
 | |
|  */
 | |
| private int
 | |
| history_def_insert(history_t *h, HistEvent *ev, const char *str)
 | |
| {
 | |
| 
 | |
| 	h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
 | |
| 	if (h->cursor)
 | |
| 		h->cursor->ev.str = strdup(str);
 | |
| 	if (!h->cursor || !h->cursor->ev.str) {
 | |
| 		he_seterrev(ev, _HE_MALLOC_FAILED);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 	h->cursor->ev.num = ++h->eventid;
 | |
| 	h->cursor->next = h->list.next;
 | |
| 	h->cursor->prev = &h->list;
 | |
| 	h->list.next->prev = h->cursor;
 | |
| 	h->list.next = h->cursor;
 | |
| 	h->cur++;
 | |
| 
 | |
| 	*ev = h->cursor->ev;
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_def_enter():
 | |
|  *	Default function to enter an item in the history
 | |
|  */
 | |
| private int
 | |
| history_def_enter(ptr_t p, HistEvent *ev, const char *str)
 | |
| {
 | |
| 	history_t *h = (history_t *) p;
 | |
| 
 | |
| 	if (history_def_insert(h, ev, str) == -1)
 | |
| 		return (-1);	/* error, keep error message */
 | |
| 
 | |
| 	/*
 | |
|          * Always keep at least one entry.
 | |
|          * This way we don't have to check for the empty list.
 | |
|          */
 | |
| 	while (h->cur > h->max && h->cur > 0)
 | |
| 		history_def_delete(h, ev, h->list.prev);
 | |
| 
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_def_init():
 | |
|  *	Default history initialization function
 | |
|  */
 | |
| /* ARGSUSED */
 | |
| private void
 | |
| history_def_init(ptr_t *p, HistEvent *ev, int n)
 | |
| {
 | |
| 	history_t *h = (history_t *) h_malloc(sizeof(history_t));
 | |
| 
 | |
| 	if (n <= 0)
 | |
| 		n = 0;
 | |
| 	h->eventid = 0;
 | |
| 	h->cur = 0;
 | |
| 	h->max = n;
 | |
| 	h->list.next = h->list.prev = &h->list;
 | |
| 	h->list.ev.str = NULL;
 | |
| 	h->list.ev.num = 0;
 | |
| 	h->cursor = &h->list;
 | |
| 	*p = (ptr_t) h;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_def_clear():
 | |
|  *	Default history cleanup function
 | |
|  */
 | |
| private void
 | |
| history_def_clear(ptr_t p, HistEvent *ev)
 | |
| {
 | |
| 	history_t *h = (history_t *) p;
 | |
| 
 | |
| 	while (h->list.prev != &h->list)
 | |
| 		history_def_delete(h, ev, h->list.prev);
 | |
| 	h->eventid = 0;
 | |
| 	h->cur = 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /************************************************************************/
 | |
| 
 | |
| /* history_init():
 | |
|  *	Initialization function.
 | |
|  */
 | |
| public History *
 | |
| history_init(void)
 | |
| {
 | |
| 	History *h = (History *) h_malloc(sizeof(History));
 | |
| 	HistEvent ev;
 | |
| 
 | |
| 	history_def_init(&h->h_ref, &ev, 0);
 | |
| 	h->h_ent = -1;
 | |
| 	h->h_next = history_def_next;
 | |
| 	h->h_first = history_def_first;
 | |
| 	h->h_last = history_def_last;
 | |
| 	h->h_prev = history_def_prev;
 | |
| 	h->h_curr = history_def_curr;
 | |
| 	h->h_set = history_def_set;
 | |
| 	h->h_clear = history_def_clear;
 | |
| 	h->h_enter = history_def_enter;
 | |
| 	h->h_add = history_def_add;
 | |
| 
 | |
| 	return (h);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_end():
 | |
|  *	clean up history;
 | |
|  */
 | |
| public void
 | |
| history_end(History *h)
 | |
| {
 | |
| 	HistEvent ev;
 | |
| 
 | |
| 	if (h->h_next == history_def_next)
 | |
| 		history_def_clear(h->h_ref, &ev);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* history_setsize():
 | |
|  *	Set history number of events
 | |
|  */
 | |
| private int
 | |
| history_setsize(History *h, HistEvent *ev, int num)
 | |
| {
 | |
| 
 | |
| 	if (h->h_next != history_def_next) {
 | |
| 		he_seterrev(ev, _HE_NOT_ALLOWED);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 	if (num < 0) {
 | |
| 		he_seterrev(ev, _HE_BAD_PARAM);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 	history_def_setsize(h->h_ref, num);
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_getsize():
 | |
|  *      Get number of events currently in history
 | |
|  */
 | |
| private int
 | |
| history_getsize(History *h, HistEvent *ev)
 | |
| {
 | |
| 	int retval = 0;
 | |
| 
 | |
| 	if (h->h_next != history_def_next) {
 | |
| 		he_seterrev(ev, _HE_NOT_ALLOWED);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 	retval = history_def_getsize(h->h_ref);
 | |
| 	if (retval < -1) {
 | |
| 		he_seterrev(ev, _HE_SIZE_NEGATIVE);
 | |
| 		return (-1);
 | |
| 	}
 | |
| 	ev->num = retval;
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_set_fun():
 | |
|  *	Set history functions
 | |
|  */
 | |
| private int
 | |
| history_set_fun(History *h, History *nh)
 | |
| {
 | |
| 	HistEvent ev;
 | |
| 
 | |
| 	if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
 | |
| 	    nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
 | |
| 	    nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
 | |
| 	    nh->h_ref == NULL) {
 | |
| 		if (h->h_next != history_def_next) {
 | |
| 			history_def_init(&h->h_ref, &ev, 0);
 | |
| 			h->h_first = history_def_first;
 | |
| 			h->h_next = history_def_next;
 | |
| 			h->h_last = history_def_last;
 | |
| 			h->h_prev = history_def_prev;
 | |
| 			h->h_curr = history_def_curr;
 | |
| 			h->h_set = history_def_set;
 | |
| 			h->h_clear = history_def_clear;
 | |
| 			h->h_enter = history_def_enter;
 | |
| 			h->h_add = history_def_add;
 | |
| 		}
 | |
| 		return (-1);
 | |
| 	}
 | |
| 	if (h->h_next == history_def_next)
 | |
| 		history_def_clear(h->h_ref, &ev);
 | |
| 
 | |
| 	h->h_ent = -1;
 | |
| 	h->h_first = nh->h_first;
 | |
| 	h->h_next = nh->h_next;
 | |
| 	h->h_last = nh->h_last;
 | |
| 	h->h_prev = nh->h_prev;
 | |
| 	h->h_curr = nh->h_curr;
 | |
| 	h->h_set = nh->h_set;
 | |
| 	h->h_clear = nh->h_clear;
 | |
| 	h->h_enter = nh->h_enter;
 | |
| 	h->h_add = nh->h_add;
 | |
| 
 | |
| 	return (0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_load():
 | |
|  *	History load function
 | |
|  */
 | |
| private int
 | |
| history_load(History *h, const char *fname)
 | |
| {
 | |
| 	FILE *fp;
 | |
| 	char *line;
 | |
| 	size_t sz, max_size;
 | |
| 	char *ptr;
 | |
| 	int i = -1;
 | |
| 	HistEvent ev;
 | |
| 
 | |
| 	if ((fp = fopen(fname, "r")) == NULL)
 | |
| 		return (i);
 | |
| 
 | |
| 	if ((line = fgetln(fp, &sz)) == NULL)
 | |
| 		goto done;
 | |
| 
 | |
| 	if (strncmp(line, hist_cookie, sz) != 0)
 | |
| 		goto done;
 | |
| 
 | |
| 	ptr = h_malloc(max_size = 1024);
 | |
| 	for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
 | |
| 		char c = line[sz];
 | |
| 
 | |
| 		if (sz != 0 && line[sz - 1] == '\n')
 | |
| 			line[--sz] = '\0';
 | |
| 		else
 | |
| 			line[sz] = '\0';
 | |
| 
 | |
| 		if (max_size < sz) {
 | |
| 			max_size = (sz + 1023) & ~1023;
 | |
| 			ptr = h_realloc(ptr, max_size);
 | |
| 		}
 | |
| 		(void) strunvis(ptr, line);
 | |
| 		line[sz] = c;
 | |
| 		HENTER(h, &ev, ptr);
 | |
| 	}
 | |
| 	h_free(ptr);
 | |
| 
 | |
| done:
 | |
| 	(void) fclose(fp);
 | |
| 	return (i);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_save():
 | |
|  *	History save function
 | |
|  */
 | |
| private int
 | |
| history_save(History *h, const char *fname)
 | |
| {
 | |
| 	FILE *fp;
 | |
| 	HistEvent ev;
 | |
| 	int i = 0, retval;
 | |
| 	size_t len, max_size;
 | |
| 	char *ptr;
 | |
| 
 | |
| 	if ((fp = fopen(fname, "w")) == NULL)
 | |
| 		return (-1);
 | |
| 
 | |
| 	(void) fchmod(fileno(fp), S_IRUSR|S_IWUSR);
 | |
| 	(void) fputs(hist_cookie, fp);
 | |
| 	ptr = h_malloc(max_size = 1024);
 | |
| 	for (retval = HLAST(h, &ev);
 | |
| 	    retval != -1;
 | |
| 	    retval = HPREV(h, &ev), i++) {
 | |
| 		len = strlen(ev.str) * 4;
 | |
| 		if (len >= max_size) {
 | |
| 			max_size = (len + 1023) & 1023;
 | |
| 			ptr = h_realloc(ptr, max_size);
 | |
| 		}
 | |
| 		(void) strvis(ptr, ev.str, VIS_WHITE);
 | |
| 		(void) fprintf(fp, "%s\n", ev.str);
 | |
| 	}
 | |
| 	h_free(ptr);
 | |
| 	(void) fclose(fp);
 | |
| 	return (i);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_prev_event():
 | |
|  *	Find the previous event, with number given
 | |
|  */
 | |
| private int
 | |
| history_prev_event(History *h, HistEvent *ev, int num)
 | |
| {
 | |
| 	int retval;
 | |
| 
 | |
| 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
 | |
| 		if (ev->num == num)
 | |
| 			return (0);
 | |
| 
 | |
| 	he_seterrev(ev, _HE_NOT_FOUND);
 | |
| 	return (-1);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_next_event():
 | |
|  *	Find the next event, with number given
 | |
|  */
 | |
| private int
 | |
| history_next_event(History *h, HistEvent *ev, int num)
 | |
| {
 | |
| 	int retval;
 | |
| 
 | |
| 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
 | |
| 		if (ev->num == num)
 | |
| 			return (0);
 | |
| 
 | |
| 	he_seterrev(ev, _HE_NOT_FOUND);
 | |
| 	return (-1);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_prev_string():
 | |
|  *	Find the previous event beginning with string
 | |
|  */
 | |
| private int
 | |
| history_prev_string(History *h, HistEvent *ev, const char *str)
 | |
| {
 | |
| 	size_t len = strlen(str);
 | |
| 	int retval;
 | |
| 
 | |
| 	for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
 | |
| 		if (strncmp(str, ev->str, len) == 0)
 | |
| 			return (0);
 | |
| 
 | |
| 	he_seterrev(ev, _HE_NOT_FOUND);
 | |
| 	return (-1);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history_next_string():
 | |
|  *	Find the next event beginning with string
 | |
|  */
 | |
| private int
 | |
| history_next_string(History *h, HistEvent *ev, const char *str)
 | |
| {
 | |
| 	size_t len = strlen(str);
 | |
| 	int retval;
 | |
| 
 | |
| 	for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
 | |
| 		if (strncmp(str, ev->str, len) == 0)
 | |
| 			return (0);
 | |
| 
 | |
| 	he_seterrev(ev, _HE_NOT_FOUND);
 | |
| 	return (-1);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* history():
 | |
|  *	User interface to history functions.
 | |
|  */
 | |
| int
 | |
| history(History *h, HistEvent *ev, int fun, ...)
 | |
| {
 | |
| 	va_list va;
 | |
| 	const char *str;
 | |
| 	int retval;
 | |
| 
 | |
| 	va_start(va, fun);
 | |
| 
 | |
| 	he_seterrev(ev, _HE_OK);
 | |
| 
 | |
| 	switch (fun) {
 | |
| 	case H_GETSIZE:
 | |
| 		retval = history_getsize(h, ev);
 | |
| 		break;
 | |
| 
 | |
| 	case H_SETSIZE:
 | |
| 		retval = history_setsize(h, ev, va_arg(va, int));
 | |
| 		break;
 | |
| 
 | |
| 	case H_ADD:
 | |
| 		str = va_arg(va, const char *);
 | |
| 		retval = HADD(h, ev, str);
 | |
| 		break;
 | |
| 
 | |
| 	case H_ENTER:
 | |
| 		str = va_arg(va, const char *);
 | |
| 		if ((retval = HENTER(h, ev, str)) != -1)
 | |
| 			h->h_ent = ev->num;
 | |
| 		break;
 | |
| 
 | |
| 	case H_APPEND:
 | |
| 		str = va_arg(va, const char *);
 | |
| 		if ((retval = HSET(h, ev, h->h_ent)) != -1)
 | |
| 			retval = HADD(h, ev, str);
 | |
| 		break;
 | |
| 
 | |
| 	case H_FIRST:
 | |
| 		retval = HFIRST(h, ev);
 | |
| 		break;
 | |
| 
 | |
| 	case H_NEXT:
 | |
| 		retval = HNEXT(h, ev);
 | |
| 		break;
 | |
| 
 | |
| 	case H_LAST:
 | |
| 		retval = HLAST(h, ev);
 | |
| 		break;
 | |
| 
 | |
| 	case H_PREV:
 | |
| 		retval = HPREV(h, ev);
 | |
| 		break;
 | |
| 
 | |
| 	case H_CURR:
 | |
| 		retval = HCURR(h, ev);
 | |
| 		break;
 | |
| 
 | |
| 	case H_SET:
 | |
| 		retval = HSET(h, ev, va_arg(va, const int));
 | |
| 		break;
 | |
| 
 | |
| 	case H_CLEAR:
 | |
| 		HCLEAR(h, ev);
 | |
| 		retval = 0;
 | |
| 		break;
 | |
| 
 | |
| 	case H_LOAD:
 | |
| 		retval = history_load(h, va_arg(va, const char *));
 | |
| 		if (retval == -1)
 | |
| 			he_seterrev(ev, _HE_HIST_READ);
 | |
| 		break;
 | |
| 
 | |
| 	case H_SAVE:
 | |
| 		retval = history_save(h, va_arg(va, const char *));
 | |
| 		if (retval == -1)
 | |
| 			he_seterrev(ev, _HE_HIST_WRITE);
 | |
| 		break;
 | |
| 
 | |
| 	case H_PREV_EVENT:
 | |
| 		retval = history_prev_event(h, ev, va_arg(va, int));
 | |
| 		break;
 | |
| 
 | |
| 	case H_NEXT_EVENT:
 | |
| 		retval = history_next_event(h, ev, va_arg(va, int));
 | |
| 		break;
 | |
| 
 | |
| 	case H_PREV_STR:
 | |
| 		retval = history_prev_string(h, ev, va_arg(va, const char *));
 | |
| 		break;
 | |
| 
 | |
| 	case H_NEXT_STR:
 | |
| 		retval = history_next_string(h, ev, va_arg(va, const char *));
 | |
| 		break;
 | |
| 
 | |
| 	case H_FUNC:
 | |
| 	{
 | |
| 		History hf;
 | |
| 
 | |
| 		hf.h_ref = va_arg(va, ptr_t);
 | |
| 		h->h_ent = -1;
 | |
| 		hf.h_first = va_arg(va, history_gfun_t);
 | |
| 		hf.h_next = va_arg(va, history_gfun_t);
 | |
| 		hf.h_last = va_arg(va, history_gfun_t);
 | |
| 		hf.h_prev = va_arg(va, history_gfun_t);
 | |
| 		hf.h_curr = va_arg(va, history_gfun_t);
 | |
| 		hf.h_set = va_arg(va, history_sfun_t);
 | |
| 		hf.h_clear = va_arg(va, history_vfun_t);
 | |
| 		hf.h_enter = va_arg(va, history_efun_t);
 | |
| 		hf.h_add = va_arg(va, history_efun_t);
 | |
| 
 | |
| 		if ((retval = history_set_fun(h, &hf)) == -1)
 | |
| 			he_seterrev(ev, _HE_PARAM_MISSING);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	case H_END:
 | |
| 		history_end(h);
 | |
| 		retval = 0;
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		retval = -1;
 | |
| 		he_seterrev(ev, _HE_UNKNOWN);
 | |
| 		break;
 | |
| 	}
 | |
| 	va_end(va);
 | |
| 	return (retval);
 | |
| }
 |