/*- * 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. */ #if !defined(lint) && !defined(SCCSID) static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93"; #endif /* not lint && not SCCSID */ /* * hist.c: History access functions */ #include "sys.h" #include #include #if __STDC__ #include #else #include #endif #ifdef linux #include "unix/histedit.h" #else #include "histedit.h" #endif typedef const HistEvent * (*history_gfun_t) __P((ptr_t)); typedef const HistEvent * (*history_efun_t) __P((ptr_t, const char *)); struct history { ptr_t h_ref; /* Argument for history fcns */ 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_efun_t h_enter; /* Add an element */ history_efun_t h_add; /* Append to an element */ }; #define HNEXT(h) (*(h)->h_next)((h)->h_ref) #define HFIRST(h) (*(h)->h_first)((h)->h_ref) #define HPREV(h) (*(h)->h_prev)((h)->h_ref) #define HLAST(h) (*(h)->h_last)((h)->h_ref) #define HCURR(h) (*(h)->h_curr)((h)->h_ref) #define HENTER(h, str) (*(h)->h_enter)((h)->h_ref, str) #define HADD(h, str) (*(h)->h_add)((h)->h_ref, str) #define h_malloc(a) malloc(a) #define h_free(a) free(a) private int history_set_num __P((History *, int)); private int history_set_fun __P((History *, history_gfun_t, history_gfun_t, history_gfun_t, history_gfun_t, history_gfun_t, history_efun_t, history_efun_t, ptr_t)); private const HistEvent *history_prev_event __P((History *, int)); private const HistEvent *history_next_event __P((History *, int)); private const HistEvent *history_next_string __P((History *, const char *)); private const HistEvent *history_prev_string __P((History *, 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 eventno; /* Current event number */ } history_t; private const HistEvent *history_def_first __P((ptr_t)); private const HistEvent *history_def_last __P((ptr_t)); private const HistEvent *history_def_next __P((ptr_t)); private const HistEvent *history_def_prev __P((ptr_t)); private const HistEvent *history_def_curr __P((ptr_t)); private const HistEvent *history_def_enter __P((ptr_t, const char *)); private const HistEvent *history_def_add __P((ptr_t, const char *)); private void history_def_init __P((ptr_t *, int)); private void history_def_end __P((ptr_t)); private const HistEvent *history_def_insert __P((history_t *, const char *)); private void history_def_delete __P((history_t *, hentry_t *)); #define history_def_set(p, num) (void) (((history_t *) p)->max = (num)) /* history_def_first(): * Default function to return the first event in the history. */ private const HistEvent * history_def_first(p) ptr_t p; { history_t *h = (history_t *) p; h->cursor = h->list.next; if (h->cursor != &h->list) return &h->cursor->ev; else return NULL; } /* history_def_last(): * Default function to return the last event in the history. */ private const HistEvent * history_def_last(p) ptr_t p; { history_t *h = (history_t *) p; h->cursor = h->list.prev; if (h->cursor != &h->list) return &h->cursor->ev; else return NULL; } /* history_def_next(): * Default function to return the next event in the history. */ private const HistEvent * history_def_next(p) ptr_t p; { history_t *h = (history_t *) p; if (h->cursor != &h->list) h->cursor = h->cursor->next; else return NULL; if (h->cursor != &h->list) return &h->cursor->ev; else return NULL; } /* history_def_prev(): * Default function to return the previous event in the history. */ private const HistEvent * history_def_prev(p) ptr_t p; { history_t *h = (history_t *) p; if (h->cursor != &h->list) h->cursor = h->cursor->prev; else return NULL; if (h->cursor != &h->list) return &h->cursor->ev; else return NULL; } /* history_def_curr(): * Default function to return the current event in the history. */ private const HistEvent * history_def_curr(p) ptr_t p; { history_t *h = (history_t *) p; if (h->cursor != &h->list) return &h->cursor->ev; else return NULL; } /* history_def_add(): * Append string to element */ private const HistEvent * history_def_add(p, str) ptr_t p; const char *str; { history_t *h = (history_t *) p; size_t len; char *s; if (h->cursor == &h->list) return (history_def_enter(p, str)); len = strlen(h->cursor->ev.str) + strlen(str) + 1; s = (char *) h_malloc(len); (void) strcpy(s, h->cursor->ev.str); (void) strcat(s, str); h_free((ptr_t) h->cursor->ev.str); h->cursor->ev.str = s; return &h->cursor->ev; } /* history_def_delete(): * Delete element hp of the h list */ private void history_def_delete(h, hp) history_t *h; hentry_t *hp; { if (hp == &h->list) abort(); hp->prev->next = hp->next; hp->next->prev = hp->prev; h_free((ptr_t) hp->ev.str); h_free(hp); h->cur--; } /* history_def_insert(): * Insert element with string str in the h list */ private const HistEvent * history_def_insert(h, str) history_t *h; const char *str; { h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t)); h->cursor->ev.str = strdup(str); h->cursor->next = h->list.next; h->cursor->prev = &h->list; h->list.next->prev = h->cursor; h->list.next = h->cursor; h->cur++; return &h->cursor->ev; } /* history_def_enter(): * Default function to enter an item in the history */ private const HistEvent * history_def_enter(p, str) ptr_t p; const char *str; { history_t *h = (history_t *) p; const HistEvent *ev; ev = history_def_insert(h, str); ((HistEvent*) ev)->num = ++h->eventno; /* * Always keep at least one entry. * This way we don't have to check for the empty list. */ while (h->cur > h->max + 1) history_def_delete(h, h->list.prev); return ev; } /* history_def_init(): * Default history initialization function */ private void history_def_init(p, n) ptr_t *p; int n; { history_t *h = (history_t *) h_malloc(sizeof(history_t)); if (n <= 0) n = 0; h->eventno = 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_end(): * Default history cleanup function */ private void history_def_end(p) ptr_t p; { history_t *h = (history_t *) p; while (h->list.prev != &h->list) history_def_delete(h, h->list.prev); } /************************************************************************/ /* history_init(): * Initialization function. */ public History * history_init() { History *h = (History *) h_malloc(sizeof(History)); history_def_init(&h->h_ref, 0); 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_enter = history_def_enter; h->h_add = history_def_add; return h; } /* history_end(): * clean up history; */ public void history_end(h) History *h; { if (h->h_next == history_def_next) history_def_end(h->h_ref); } /* history_set_num(): * Set history number of events */ private int history_set_num(h, num) History *h; int num; { if (h->h_next != history_def_next || num < 0) return -1; history_def_set(h->h_ref, num); return 0; } /* history_set_fun(): * Set history functions */ private int history_set_fun(h, first, next, last, prev, curr, enter, add, ptr) History *h; history_gfun_t first, next, last, prev, curr; history_efun_t enter, add; ptr_t ptr; { if (first == NULL || next == NULL || last == NULL || prev == NULL || curr == NULL || enter == NULL || add == NULL || ptr == NULL ) { if (h->h_next != history_def_next) { history_def_init(&h->h_ref, 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_enter = history_def_enter; h->h_add = history_def_add; } return -1; } if (h->h_next == history_def_next) history_def_end(h->h_ref); h->h_next = next; h->h_first = first; h->h_enter = enter; h->h_add = add; return 0; } /* history_prev_event(): * Find the previous event, with number given */ private const HistEvent * history_prev_event(h, num) History *h; int num; { const HistEvent *ev; for (ev = HCURR(h); ev != NULL; ev = HPREV(h)) if (ev->num == num) return ev; return NULL; } /* history_next_event(): * Find the next event, with number given */ private const HistEvent * history_next_event(h, num) History *h; int num; { const HistEvent *ev; for (ev = HCURR(h); ev != NULL; ev = HNEXT(h)) if (ev->num == num) return ev; return NULL; } /* history_prev_string(): * Find the previous event beginning with string */ private const HistEvent * history_prev_string(h, str) History *h; const char* str; { const HistEvent *ev; size_t len = strlen(str); for (ev = HCURR(h); ev != NULL; ev = HNEXT(h)) if (strncmp(str, ev->str, len) == 0) return ev; return NULL; } /* history_next_string(): * Find the next event beginning with string */ private const HistEvent * history_next_string(h, str) History *h; const char* str; { const HistEvent *ev; size_t len = strlen(str); for (ev = HCURR(h); ev != NULL; ev = HPREV(h)) if (strncmp(str, ev->str, len) == 0) return ev; return NULL; } /* history(): * User interface to history functions. */ const HistEvent * #if __STDC__ history(History *h, int fun, ...) #else history(va_alist) va_dcl #endif { va_list va; const HistEvent *ev = NULL; const char *str; static const HistEvent sev = { 0, "" }; #if __STDC__ va_start(va, fun); #else History *h; int fun; va_start(va); h = va_arg(va, History *); fun = va_arg(va, int); #endif switch (fun) { case H_ADD: str = va_arg(va, const char *); ev = HADD(h, str); break; case H_ENTER: str = va_arg(va, const char *); ev = HENTER(h, str); break; case H_FIRST: ev = HFIRST(h); break; case H_NEXT: ev = HNEXT(h); break; case H_LAST: ev = HLAST(h); break; case H_PREV: ev = HPREV(h); break; case H_CURR: ev = HCURR(h); break; case H_PREV_EVENT: ev = history_prev_event(h, va_arg(va, int)); break; case H_NEXT_EVENT: ev = history_next_event(h, va_arg(va, int)); break; case H_PREV_STR: ev = history_prev_string(h, va_arg(va, const char*)); break; case H_NEXT_STR: ev = history_next_string(h, va_arg(va, const char*)); break; case H_EVENT: if (history_set_num(h, va_arg(va, int)) == 0) ev = &sev; break; case H_FUNC: { history_gfun_t first = va_arg(va, history_gfun_t); history_gfun_t next = va_arg(va, history_gfun_t); history_gfun_t last = va_arg(va, history_gfun_t); history_gfun_t prev = va_arg(va, history_gfun_t); history_gfun_t curr = va_arg(va, history_gfun_t); history_efun_t enter = va_arg(va, history_efun_t); history_efun_t add = va_arg(va, history_efun_t); ptr_t ptr = va_arg(va, ptr_t); if (history_set_fun(h, first, next, last, prev, curr, enter, add, ptr) == 0) ev = &sev; } break; case H_END: history_end(h); break; default: break; } va_end(va); return ev; }