Annotation of src/lib/libedit/history.c, Revision 1.57
1.57 ! christos 1: /* $NetBSD: history.c,v 1.56 2016/04/11 16:06:52 christos Exp $ */
1.3 lukem 2:
1.1 cgd 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.
1.24 agc 18: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 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:
1.19 christos 35: #include "config.h"
1.1 cgd 36: #if !defined(lint) && !defined(SCCSID)
1.6 christos 37: #if 0
1.1 cgd 38: static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93";
1.6 christos 39: #else
1.57 ! christos 40: __RCSID("$NetBSD: history.c,v 1.56 2016/04/11 16:06:52 christos Exp $");
1.6 christos 41: #endif
1.1 cgd 42: #endif /* not lint && not SCCSID */
43:
44: /*
1.35 christos 45: * hist.c: TYPE(History) access functions
1.1 cgd 46: */
1.52 christos 47: #include <sys/stat.h>
48: #include <stdarg.h>
49: #include <stdlib.h>
1.1 cgd 50: #include <string.h>
1.12 christos 51: #include <vis.h>
1.1 cgd 52:
1.12 christos 53: static const char hist_cookie[] = "_HiStOrY_V2_\n";
1.2 christos 54:
1.1 cgd 55: #include "histedit.h"
56:
1.54 christos 57:
58: #ifdef NARROWCHAR
59:
1.55 christos 60: #define Char char
1.54 christos 61: #define FUN(prefix, rest) prefix ## _ ## rest
62: #define FUNW(type) type
63: #define TYPE(type) type
64: #define STR(x) x
65:
66: #define Strlen(s) strlen(s)
67: #define Strdup(s) strdup(s)
68: #define Strcmp(d, s) strcmp(d, s)
69: #define Strncmp(d, s, n) strncmp(d, s, n)
70: #define Strncpy(d, s, n) strncpy(d, s, n)
71: #define Strncat(d, s, n) strncat(d, s, n)
1.56 christos 72: #define ct_decode_string(s, b) (s)
73: #define ct_encode_string(s, b) (s)
1.54 christos 74:
75: #else
1.56 christos 76: #include "chartype.h"
1.54 christos 77:
1.55 christos 78: #define Char wchar_t
1.54 christos 79: #define FUN(prefix, rest) prefix ## _w ## rest
80: #define FUNW(type) type ## _w
81: #define TYPE(type) type ## W
82: #define STR(x) L ## x
83:
84: #define Strlen(s) wcslen(s)
85: #define Strdup(s) wcsdup(s)
86: #define Strcmp(d, s) wcscmp(d, s)
87: #define Strncmp(d, s, n) wcsncmp(d, s, n)
88: #define Strncpy(d, s, n) wcsncpy(d, s, n)
89: #define Strncat(d, s, n) wcsncat(d, s, n)
90:
91: #endif
92:
93:
1.42 christos 94: typedef int (*history_gfun_t)(void *, TYPE(HistEvent) *);
95: typedef int (*history_efun_t)(void *, TYPE(HistEvent) *, const Char *);
96: typedef void (*history_vfun_t)(void *, TYPE(HistEvent) *);
97: typedef int (*history_sfun_t)(void *, TYPE(HistEvent) *, const int);
1.1 cgd 98:
1.37 christos 99: struct TYPE(history) {
1.42 christos 100: void *h_ref; /* Argument for history fcns */
1.16 lukem 101: int h_ent; /* Last entry point for history */
102: history_gfun_t h_first; /* Get the first element */
103: history_gfun_t h_next; /* Get the next element */
104: history_gfun_t h_last; /* Get the last element */
105: history_gfun_t h_prev; /* Get the previous element */
106: history_gfun_t h_curr; /* Get the current element */
107: history_sfun_t h_set; /* Set the current element */
1.30 christos 108: history_sfun_t h_del; /* Set the given element */
1.16 lukem 109: history_vfun_t h_clear; /* Clear the history list */
110: history_efun_t h_enter; /* Add an element */
111: history_efun_t h_add; /* Append to an element */
1.1 cgd 112: };
1.22 christos 113:
1.16 lukem 114: #define HNEXT(h, ev) (*(h)->h_next)((h)->h_ref, ev)
115: #define HFIRST(h, ev) (*(h)->h_first)((h)->h_ref, ev)
116: #define HPREV(h, ev) (*(h)->h_prev)((h)->h_ref, ev)
117: #define HLAST(h, ev) (*(h)->h_last)((h)->h_ref, ev)
118: #define HCURR(h, ev) (*(h)->h_curr)((h)->h_ref, ev)
119: #define HSET(h, ev, n) (*(h)->h_set)((h)->h_ref, ev, n)
120: #define HCLEAR(h, ev) (*(h)->h_clear)((h)->h_ref, ev)
1.7 christos 121: #define HENTER(h, ev, str) (*(h)->h_enter)((h)->h_ref, ev, str)
122: #define HADD(h, ev, str) (*(h)->h_add)((h)->h_ref, ev, str)
1.30 christos 123: #define HDEL(h, ev, n) (*(h)->h_del)((h)->h_ref, ev, n)
1.1 cgd 124:
1.35 christos 125: #define h_strdup(a) Strdup(a)
1.16 lukem 126: #define h_malloc(a) malloc(a)
127: #define h_realloc(a, b) realloc((a), (b))
128: #define h_free(a) free(a)
1.1 cgd 129:
1.19 christos 130: typedef struct {
131: int num;
1.35 christos 132: Char *str;
1.19 christos 133: } HistEventPrivate;
134:
135:
1.57 ! christos 136: static int history_setsize(TYPE(History) *, TYPE(HistEvent) *, int);
! 137: static int history_getsize(TYPE(History) *, TYPE(HistEvent) *);
! 138: static int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int);
! 139: static int history_getunique(TYPE(History) *, TYPE(HistEvent) *);
! 140: static int history_set_fun(TYPE(History) *, TYPE(History) *);
! 141: static int history_load(TYPE(History) *, const char *);
! 142: static int history_save(TYPE(History) *, const char *);
! 143: static int history_save_fp(TYPE(History) *, FILE *);
! 144: static int history_prev_event(TYPE(History) *, TYPE(HistEvent) *, int);
! 145: static int history_next_event(TYPE(History) *, TYPE(HistEvent) *, int);
! 146: static int history_next_string(TYPE(History) *, TYPE(HistEvent) *,
! 147: const Char *);
! 148: static int history_prev_string(TYPE(History) *, TYPE(HistEvent) *,
! 149: const Char *);
1.1 cgd 150:
151:
152: /***********************************************************************/
153:
154: /*
155: * Builtin- history implementation
156: */
157: typedef struct hentry_t {
1.35 christos 158: TYPE(HistEvent) ev; /* What we return */
1.34 christos 159: void *data; /* data */
1.16 lukem 160: struct hentry_t *next; /* Next entry */
161: struct hentry_t *prev; /* Previous entry */
1.22 christos 162: } hentry_t;
1.1 cgd 163:
164: typedef struct history_t {
1.22 christos 165: hentry_t list; /* Fake list header element */
166: hentry_t *cursor; /* Current element in the list */
167: int max; /* Maximum number of events */
168: int cur; /* Current number of events */
1.16 lukem 169: int eventid; /* For generation of unique event id */
1.35 christos 170: int flags; /* TYPE(History) flags */
1.22 christos 171: #define H_UNIQUE 1 /* Store only unique elements */
172: } history_t;
1.16 lukem 173:
1.57 ! christos 174: static int history_def_next(void *, TYPE(HistEvent) *);
! 175: static int history_def_first(void *, TYPE(HistEvent) *);
! 176: static int history_def_prev(void *, TYPE(HistEvent) *);
! 177: static int history_def_last(void *, TYPE(HistEvent) *);
! 178: static int history_def_curr(void *, TYPE(HistEvent) *);
! 179: static int history_def_set(void *, TYPE(HistEvent) *, const int);
! 180: static void history_def_clear(void *, TYPE(HistEvent) *);
! 181: static int history_def_enter(void *, TYPE(HistEvent) *, const Char *);
! 182: static int history_def_add(void *, TYPE(HistEvent) *, const Char *);
! 183: static int history_def_del(void *, TYPE(HistEvent) *, const int);
! 184:
! 185: static int history_def_init(void **, TYPE(HistEvent) *, int);
! 186: static int history_def_insert(history_t *, TYPE(HistEvent) *, const Char *);
! 187: static void history_def_delete(history_t *, TYPE(HistEvent) *, hentry_t *);
1.1 cgd 188:
1.57 ! christos 189: static int history_deldata_nth(history_t *, TYPE(HistEvent) *, int, void **);
! 190: static int history_set_nth(void *, TYPE(HistEvent) *, int);
1.34 christos 191:
1.22 christos 192: #define history_def_setsize(p, num)(void) (((history_t *)p)->max = (num))
193: #define history_def_getsize(p) (((history_t *)p)->cur)
194: #define history_def_getunique(p) (((((history_t *)p)->flags) & H_UNIQUE) != 0)
195: #define history_def_setunique(p, uni) \
196: if (uni) \
197: (((history_t *)p)->flags) |= H_UNIQUE; \
198: else \
199: (((history_t *)p)->flags) &= ~H_UNIQUE
1.1 cgd 200:
1.16 lukem 201: #define he_strerror(code) he_errlist[code]
202: #define he_seterrev(evp, code) {\
1.7 christos 203: evp->num = code;\
204: evp->str = he_strerror(code);\
205: }
1.14 simonb 206:
1.7 christos 207: /* error messages */
1.35 christos 208: static const Char *const he_errlist[] = {
209: STR("OK"),
210: STR("unknown error"),
211: STR("malloc() failed"),
212: STR("first event not found"),
213: STR("last event not found"),
214: STR("empty list"),
215: STR("no next event"),
216: STR("no previous event"),
217: STR("current event is invalid"),
218: STR("event not found"),
219: STR("can't read history from file"),
220: STR("can't write history"),
221: STR("required parameter(s) not supplied"),
222: STR("history size negative"),
223: STR("function not allowed with other history-functions-set the default"),
224: STR("bad parameters")
1.7 christos 225: };
226: /* error codes */
1.16 lukem 227: #define _HE_OK 0
228: #define _HE_UNKNOWN 1
229: #define _HE_MALLOC_FAILED 2
230: #define _HE_FIRST_NOTFOUND 3
231: #define _HE_LAST_NOTFOUND 4
232: #define _HE_EMPTY_LIST 5
233: #define _HE_END_REACHED 6
234: #define _HE_START_REACHED 7
235: #define _HE_CURR_INVALID 8
236: #define _HE_NOT_FOUND 9
237: #define _HE_HIST_READ 10
238: #define _HE_HIST_WRITE 11
239: #define _HE_PARAM_MISSING 12
240: #define _HE_SIZE_NEGATIVE 13
241: #define _HE_NOT_ALLOWED 14
242: #define _HE_BAD_PARAM 15
1.1 cgd 243:
244: /* history_def_first():
245: * Default function to return the first event in the history.
246: */
1.57 ! christos 247: static int
1.42 christos 248: history_def_first(void *p, TYPE(HistEvent) *ev)
1.1 cgd 249: {
1.16 lukem 250: history_t *h = (history_t *) p;
1.7 christos 251:
1.16 lukem 252: h->cursor = h->list.next;
253: if (h->cursor != &h->list)
254: *ev = h->cursor->ev;
255: else {
256: he_seterrev(ev, _HE_FIRST_NOTFOUND);
1.43 christos 257: return -1;
1.16 lukem 258: }
1.7 christos 259:
1.43 christos 260: return 0;
1.1 cgd 261: }
262:
1.8 christos 263:
1.1 cgd 264: /* history_def_last():
265: * Default function to return the last event in the history.
266: */
1.57 ! christos 267: static int
1.42 christos 268: history_def_last(void *p, TYPE(HistEvent) *ev)
1.16 lukem 269: {
270: history_t *h = (history_t *) p;
271:
272: h->cursor = h->list.prev;
273: if (h->cursor != &h->list)
274: *ev = h->cursor->ev;
275: else {
276: he_seterrev(ev, _HE_LAST_NOTFOUND);
1.43 christos 277: return -1;
1.16 lukem 278: }
1.7 christos 279:
1.43 christos 280: return 0;
1.1 cgd 281: }
282:
1.8 christos 283:
1.1 cgd 284: /* history_def_next():
285: * Default function to return the next event in the history.
286: */
1.57 ! christos 287: static int
1.42 christos 288: history_def_next(void *p, TYPE(HistEvent) *ev)
1.16 lukem 289: {
290: history_t *h = (history_t *) p;
291:
1.28 christos 292: if (h->cursor == &h->list) {
1.16 lukem 293: he_seterrev(ev, _HE_EMPTY_LIST);
1.43 christos 294: return -1;
1.16 lukem 295: }
1.1 cgd 296:
1.28 christos 297: if (h->cursor->next == &h->list) {
1.16 lukem 298: he_seterrev(ev, _HE_END_REACHED);
1.43 christos 299: return -1;
1.16 lukem 300: }
1.7 christos 301:
1.28 christos 302: h->cursor = h->cursor->next;
303: *ev = h->cursor->ev;
304:
1.43 christos 305: return 0;
1.1 cgd 306: }
307:
308:
309: /* history_def_prev():
310: * Default function to return the previous event in the history.
311: */
1.57 ! christos 312: static int
1.42 christos 313: history_def_prev(void *p, TYPE(HistEvent) *ev)
1.16 lukem 314: {
315: history_t *h = (history_t *) p;
316:
1.28 christos 317: if (h->cursor == &h->list) {
1.16 lukem 318: he_seterrev(ev,
319: (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
1.43 christos 320: return -1;
1.16 lukem 321: }
1.1 cgd 322:
1.28 christos 323: if (h->cursor->prev == &h->list) {
1.16 lukem 324: he_seterrev(ev, _HE_START_REACHED);
1.43 christos 325: return -1;
1.16 lukem 326: }
1.7 christos 327:
1.28 christos 328: h->cursor = h->cursor->prev;
329: *ev = h->cursor->ev;
330:
1.43 christos 331: return 0;
1.1 cgd 332: }
333:
334:
335: /* history_def_curr():
336: * Default function to return the current event in the history.
337: */
1.57 ! christos 338: static int
1.42 christos 339: history_def_curr(void *p, TYPE(HistEvent) *ev)
1.1 cgd 340: {
1.16 lukem 341: history_t *h = (history_t *) p;
1.1 cgd 342:
1.16 lukem 343: if (h->cursor != &h->list)
344: *ev = h->cursor->ev;
345: else {
346: he_seterrev(ev,
347: (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
1.43 christos 348: return -1;
1.16 lukem 349: }
1.7 christos 350:
1.43 christos 351: return 0;
1.1 cgd 352: }
353:
1.8 christos 354:
355: /* history_def_set():
356: * Default function to set the current event in the history to the
357: * given one.
358: */
1.57 ! christos 359: static int
1.42 christos 360: history_def_set(void *p, TYPE(HistEvent) *ev, const int n)
1.16 lukem 361: {
362: history_t *h = (history_t *) p;
1.8 christos 363:
1.16 lukem 364: if (h->cur == 0) {
365: he_seterrev(ev, _HE_EMPTY_LIST);
1.43 christos 366: return -1;
1.16 lukem 367: }
368: if (h->cursor == &h->list || h->cursor->ev.num != n) {
369: for (h->cursor = h->list.next; h->cursor != &h->list;
370: h->cursor = h->cursor->next)
371: if (h->cursor->ev.num == n)
372: break;
373: }
374: if (h->cursor == &h->list) {
375: he_seterrev(ev, _HE_NOT_FOUND);
1.43 christos 376: return -1;
1.16 lukem 377: }
1.43 christos 378: return 0;
1.8 christos 379: }
380:
381:
1.34 christos 382: /* history_set_nth():
383: * Default function to set the current event in the history to the
384: * n-th one.
385: */
1.57 ! christos 386: static int
1.42 christos 387: history_set_nth(void *p, TYPE(HistEvent) *ev, int n)
1.34 christos 388: {
389: history_t *h = (history_t *) p;
390:
391: if (h->cur == 0) {
392: he_seterrev(ev, _HE_EMPTY_LIST);
1.43 christos 393: return -1;
1.34 christos 394: }
395: for (h->cursor = h->list.prev; h->cursor != &h->list;
396: h->cursor = h->cursor->prev)
397: if (n-- <= 0)
398: break;
399: if (h->cursor == &h->list) {
400: he_seterrev(ev, _HE_NOT_FOUND);
1.43 christos 401: return -1;
1.34 christos 402: }
1.43 christos 403: return 0;
1.34 christos 404: }
405:
406:
1.1 cgd 407: /* history_def_add():
408: * Append string to element
409: */
1.57 ! christos 410: static int
1.42 christos 411: history_def_add(void *p, TYPE(HistEvent) *ev, const Char *str)
1.16 lukem 412: {
413: history_t *h = (history_t *) p;
414: size_t len;
1.35 christos 415: Char *s;
1.19 christos 416: HistEventPrivate *evp = (void *)&h->cursor->ev;
1.16 lukem 417:
418: if (h->cursor == &h->list)
1.43 christos 419: return history_def_enter(p, ev, str);
1.35 christos 420: len = Strlen(evp->str) + Strlen(str) + 1;
421: s = h_malloc(len * sizeof(*s));
1.21 christos 422: if (s == NULL) {
1.16 lukem 423: he_seterrev(ev, _HE_MALLOC_FAILED);
1.43 christos 424: return -1;
1.16 lukem 425: }
1.35 christos 426: (void) Strncpy(s, h->cursor->ev.str, len);
427: s[len - 1] = '\0';
428: (void) Strncat(s, str, len - Strlen(s) - 1);
1.42 christos 429: h_free(evp->str);
1.19 christos 430: evp->str = s;
1.16 lukem 431: *ev = h->cursor->ev;
1.43 christos 432: return 0;
1.1 cgd 433: }
434:
435:
1.57 ! christos 436: static int
1.35 christos 437: history_deldata_nth(history_t *h, TYPE(HistEvent) *ev,
1.34 christos 438: int num, void **data)
439: {
440: if (history_set_nth(h, ev, num) != 0)
1.43 christos 441: return -1;
1.34 christos 442: /* magic value to skip delete (just set to n-th history) */
443: if (data == (void **)-1)
1.43 christos 444: return 0;
1.35 christos 445: ev->str = Strdup(h->cursor->ev.str);
1.34 christos 446: ev->num = h->cursor->ev.num;
447: if (data)
448: *data = h->cursor->data;
449: history_def_delete(h, ev, h->cursor);
1.43 christos 450: return 0;
1.34 christos 451: }
452:
453:
1.30 christos 454: /* history_def_del():
455: * Delete element hp of the h list
456: */
457: /* ARGSUSED */
1.57 ! christos 458: static int
1.42 christos 459: history_def_del(void *p, TYPE(HistEvent) *ev __attribute__((__unused__)),
1.30 christos 460: const int num)
461: {
462: history_t *h = (history_t *) p;
463: if (history_def_set(h, ev, num) != 0)
1.43 christos 464: return -1;
1.35 christos 465: ev->str = Strdup(h->cursor->ev.str);
1.30 christos 466: ev->num = h->cursor->ev.num;
467: history_def_delete(h, ev, h->cursor);
1.43 christos 468: return 0;
1.30 christos 469: }
470:
471:
1.1 cgd 472: /* history_def_delete():
473: * Delete element hp of the h list
474: */
1.11 christos 475: /* ARGSUSED */
1.57 ! christos 476: static void
1.52 christos 477: history_def_delete(history_t *h,
1.35 christos 478: TYPE(HistEvent) *ev __attribute__((__unused__)), hentry_t *hp)
1.16 lukem 479: {
1.19 christos 480: HistEventPrivate *evp = (void *)&hp->ev;
1.16 lukem 481: if (hp == &h->list)
482: abort();
1.34 christos 483: if (h->cursor == hp) {
1.30 christos 484: h->cursor = hp->prev;
1.34 christos 485: if (h->cursor == &h->list)
486: h->cursor = hp->next;
487: }
1.16 lukem 488: hp->prev->next = hp->next;
489: hp->next->prev = hp->prev;
1.42 christos 490: h_free(evp->str);
1.16 lukem 491: h_free(hp);
492: h->cur--;
1.1 cgd 493: }
494:
495:
496: /* history_def_insert():
497: * Insert element with string str in the h list
498: */
1.57 ! christos 499: static int
1.35 christos 500: history_def_insert(history_t *h, TYPE(HistEvent) *ev, const Char *str)
1.16 lukem 501: {
1.39 christos 502: hentry_t *c;
1.16 lukem 503:
1.39 christos 504: c = h_malloc(sizeof(*c));
505: if (c == NULL)
1.21 christos 506: goto oomem;
1.39 christos 507: if ((c->ev.str = h_strdup(str)) == NULL) {
1.42 christos 508: h_free(c);
1.21 christos 509: goto oomem;
1.16 lukem 510: }
1.39 christos 511: c->data = NULL;
512: c->ev.num = ++h->eventid;
513: c->next = h->list.next;
514: c->prev = &h->list;
515: h->list.next->prev = c;
516: h->list.next = c;
1.16 lukem 517: h->cur++;
1.39 christos 518: h->cursor = c;
1.1 cgd 519:
1.39 christos 520: *ev = c->ev;
1.43 christos 521: return 0;
1.21 christos 522: oomem:
523: he_seterrev(ev, _HE_MALLOC_FAILED);
1.43 christos 524: return -1;
1.1 cgd 525: }
526:
527:
528: /* history_def_enter():
529: * Default function to enter an item in the history
530: */
1.57 ! christos 531: static int
1.42 christos 532: history_def_enter(void *p, TYPE(HistEvent) *ev, const Char *str)
1.16 lukem 533: {
534: history_t *h = (history_t *) p;
535:
1.22 christos 536: if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
1.35 christos 537: Strcmp(h->list.next->ev.str, str) == 0)
1.43 christos 538: return 0;
1.22 christos 539:
1.16 lukem 540: if (history_def_insert(h, ev, str) == -1)
1.43 christos 541: return -1; /* error, keep error message */
1.16 lukem 542:
543: /*
544: * Always keep at least one entry.
545: * This way we don't have to check for the empty list.
546: */
1.18 jdolecek 547: while (h->cur > h->max && h->cur > 0)
1.16 lukem 548: history_def_delete(h, ev, h->list.prev);
1.7 christos 549:
1.43 christos 550: return 1;
1.1 cgd 551: }
552:
553:
554: /* history_def_init():
555: * Default history initialization function
556: */
1.11 christos 557: /* ARGSUSED */
1.57 ! christos 558: static int
1.42 christos 559: history_def_init(void **p, TYPE(HistEvent) *ev __attribute__((__unused__)), int n)
1.16 lukem 560: {
1.42 christos 561: history_t *h = (history_t *) h_malloc(sizeof(*h));
1.21 christos 562: if (h == NULL)
563: return -1;
1.16 lukem 564:
565: if (n <= 0)
566: n = 0;
567: h->eventid = 0;
568: h->cur = 0;
569: h->max = n;
570: h->list.next = h->list.prev = &h->list;
571: h->list.ev.str = NULL;
572: h->list.ev.num = 0;
573: h->cursor = &h->list;
1.22 christos 574: h->flags = 0;
1.42 christos 575: *p = h;
1.21 christos 576: return 0;
1.1 cgd 577: }
578:
579:
1.2 christos 580: /* history_def_clear():
1.1 cgd 581: * Default history cleanup function
582: */
1.57 ! christos 583: static void
1.42 christos 584: history_def_clear(void *p, TYPE(HistEvent) *ev)
1.16 lukem 585: {
586: history_t *h = (history_t *) p;
587:
588: while (h->list.prev != &h->list)
589: history_def_delete(h, ev, h->list.prev);
1.39 christos 590: h->cursor = &h->list;
1.16 lukem 591: h->eventid = 0;
592: h->cur = 0;
1.1 cgd 593: }
594:
1.2 christos 595:
596:
597:
1.1 cgd 598: /************************************************************************/
599:
600: /* history_init():
601: * Initialization function.
602: */
1.57 ! christos 603: TYPE(History) *
1.35 christos 604: FUN(history,init)(void)
1.1 cgd 605: {
1.35 christos 606: TYPE(HistEvent) ev;
1.42 christos 607: TYPE(History) *h = (TYPE(History) *) h_malloc(sizeof(*h));
1.21 christos 608: if (h == NULL)
609: return NULL;
1.1 cgd 610:
1.21 christos 611: if (history_def_init(&h->h_ref, &ev, 0) == -1) {
1.42 christos 612: h_free(h);
1.21 christos 613: return NULL;
614: }
1.16 lukem 615: h->h_ent = -1;
616: h->h_next = history_def_next;
617: h->h_first = history_def_first;
618: h->h_last = history_def_last;
619: h->h_prev = history_def_prev;
620: h->h_curr = history_def_curr;
621: h->h_set = history_def_set;
622: h->h_clear = history_def_clear;
623: h->h_enter = history_def_enter;
624: h->h_add = history_def_add;
1.31 christos 625: h->h_del = history_def_del;
1.1 cgd 626:
1.43 christos 627: return h;
1.1 cgd 628: }
629:
630:
631: /* history_end():
632: * clean up history;
633: */
1.57 ! christos 634: void
1.35 christos 635: FUN(history,end)(TYPE(History) *h)
1.1 cgd 636: {
1.35 christos 637: TYPE(HistEvent) ev;
1.16 lukem 638:
639: if (h->h_next == history_def_next)
640: history_def_clear(h->h_ref, &ev);
1.32 christos 641: h_free(h->h_ref);
1.29 christos 642: h_free(h);
1.1 cgd 643: }
644:
645:
646:
1.12 christos 647: /* history_setsize():
1.1 cgd 648: * Set history number of events
649: */
1.57 ! christos 650: static int
1.35 christos 651: history_setsize(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
1.16 lukem 652: {
1.7 christos 653:
1.16 lukem 654: if (h->h_next != history_def_next) {
655: he_seterrev(ev, _HE_NOT_ALLOWED);
1.43 christos 656: return -1;
1.16 lukem 657: }
658: if (num < 0) {
659: he_seterrev(ev, _HE_BAD_PARAM);
1.43 christos 660: return -1;
1.16 lukem 661: }
662: history_def_setsize(h->h_ref, num);
1.43 christos 663: return 0;
1.1 cgd 664: }
665:
1.16 lukem 666:
1.12 christos 667: /* history_getsize():
1.7 christos 668: * Get number of events currently in history
669: */
1.57 ! christos 670: static int
1.35 christos 671: history_getsize(TYPE(History) *h, TYPE(HistEvent) *ev)
1.16 lukem 672: {
1.22 christos 673: if (h->h_next != history_def_next) {
674: he_seterrev(ev, _HE_NOT_ALLOWED);
1.43 christos 675: return -1;
1.22 christos 676: }
677: ev->num = history_def_getsize(h->h_ref);
678: if (ev->num < -1) {
679: he_seterrev(ev, _HE_SIZE_NEGATIVE);
1.43 christos 680: return -1;
1.22 christos 681: }
1.43 christos 682: return 0;
1.22 christos 683: }
684:
685:
686: /* history_setunique():
687: * Set if adjacent equal events should not be entered in history.
688: */
1.57 ! christos 689: static int
1.35 christos 690: history_setunique(TYPE(History) *h, TYPE(HistEvent) *ev, int uni)
1.22 christos 691: {
1.7 christos 692:
1.16 lukem 693: if (h->h_next != history_def_next) {
694: he_seterrev(ev, _HE_NOT_ALLOWED);
1.43 christos 695: return -1;
1.16 lukem 696: }
1.22 christos 697: history_def_setunique(h->h_ref, uni);
1.43 christos 698: return 0;
1.22 christos 699: }
700:
701:
702: /* history_getunique():
703: * Get if adjacent equal events should not be entered in history.
704: */
1.57 ! christos 705: static int
1.35 christos 706: history_getunique(TYPE(History) *h, TYPE(HistEvent) *ev)
1.22 christos 707: {
708: if (h->h_next != history_def_next) {
709: he_seterrev(ev, _HE_NOT_ALLOWED);
1.43 christos 710: return -1;
1.16 lukem 711: }
1.22 christos 712: ev->num = history_def_getunique(h->h_ref);
1.43 christos 713: return 0;
1.7 christos 714: }
1.1 cgd 715:
1.16 lukem 716:
1.1 cgd 717: /* history_set_fun():
718: * Set history functions
719: */
1.57 ! christos 720: static int
1.35 christos 721: history_set_fun(TYPE(History) *h, TYPE(History) *nh)
1.16 lukem 722: {
1.35 christos 723: TYPE(HistEvent) ev;
1.16 lukem 724:
725: if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
726: nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
727: nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
1.30 christos 728: nh->h_del == NULL || nh->h_ref == NULL) {
1.16 lukem 729: if (h->h_next != history_def_next) {
1.46 christos 730: if (history_def_init(&h->h_ref, &ev, 0) == -1)
731: return -1;
1.16 lukem 732: h->h_first = history_def_first;
733: h->h_next = history_def_next;
734: h->h_last = history_def_last;
735: h->h_prev = history_def_prev;
736: h->h_curr = history_def_curr;
737: h->h_set = history_def_set;
738: h->h_clear = history_def_clear;
739: h->h_enter = history_def_enter;
740: h->h_add = history_def_add;
1.30 christos 741: h->h_del = history_def_del;
1.16 lukem 742: }
1.43 christos 743: return -1;
1.16 lukem 744: }
745: if (h->h_next == history_def_next)
746: history_def_clear(h->h_ref, &ev);
747:
748: h->h_ent = -1;
749: h->h_first = nh->h_first;
750: h->h_next = nh->h_next;
751: h->h_last = nh->h_last;
752: h->h_prev = nh->h_prev;
753: h->h_curr = nh->h_curr;
754: h->h_set = nh->h_set;
755: h->h_clear = nh->h_clear;
756: h->h_enter = nh->h_enter;
757: h->h_add = nh->h_add;
1.30 christos 758: h->h_del = nh->h_del;
1.1 cgd 759:
1.43 christos 760: return 0;
1.1 cgd 761: }
762:
763:
1.2 christos 764: /* history_load():
1.35 christos 765: * TYPE(History) load function
1.2 christos 766: */
1.57 ! christos 767: static int
1.35 christos 768: history_load(TYPE(History) *h, const char *fname)
1.16 lukem 769: {
770: FILE *fp;
771: char *line;
1.50 christos 772: size_t llen;
773: ssize_t sz;
774: size_t max_size;
1.16 lukem 775: char *ptr;
776: int i = -1;
1.35 christos 777: TYPE(HistEvent) ev;
1.53 christos 778: #ifndef NARROWCHAR
1.36 christos 779: static ct_buffer_t conv;
1.35 christos 780: #endif
1.16 lukem 781:
782: if ((fp = fopen(fname, "r")) == NULL)
1.43 christos 783: return i;
1.16 lukem 784:
1.50 christos 785: line = NULL;
786: llen = 0;
787: if ((sz = getline(&line, &llen, fp)) == -1)
1.16 lukem 788: goto done;
789:
1.50 christos 790: if (strncmp(line, hist_cookie, (size_t)sz) != 0)
1.16 lukem 791: goto done;
792:
1.42 christos 793: ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
1.21 christos 794: if (ptr == NULL)
795: goto done;
1.50 christos 796: for (i = 0; (sz = getline(&line, &llen, fp)) != -1; i++) {
797: if (sz > 0 && line[sz - 1] == '\n')
798: line[--sz] = '\0';
799: if (max_size < (size_t)sz) {
1.21 christos 800: char *nptr;
1.50 christos 801: max_size = ((size_t)sz + 1024) & (size_t)~1023;
1.42 christos 802: nptr = h_realloc(ptr, max_size * sizeof(*ptr));
1.21 christos 803: if (nptr == NULL) {
804: i = -1;
805: goto oomem;
806: }
807: ptr = nptr;
1.16 lukem 808: }
809: (void) strunvis(ptr, line);
1.35 christos 810: if (HENTER(h, &ev, ct_decode_string(ptr, &conv)) == -1) {
1.33 sketch 811: i = -1;
812: goto oomem;
1.21 christos 813: }
1.16 lukem 814: }
1.21 christos 815: oomem:
1.42 christos 816: h_free(ptr);
1.2 christos 817: done:
1.48 christos 818: free(line);
1.16 lukem 819: (void) fclose(fp);
1.43 christos 820: return i;
1.2 christos 821: }
822:
823:
1.47 christos 824: /* history_save_fp():
1.35 christos 825: * TYPE(History) save function
1.2 christos 826: */
1.57 ! christos 827: static int
1.47 christos 828: history_save_fp(TYPE(History) *h, FILE *fp)
1.16 lukem 829: {
1.35 christos 830: TYPE(HistEvent) ev;
1.21 christos 831: int i = -1, retval;
1.16 lukem 832: size_t len, max_size;
1.40 christos 833: char *ptr;
834: const char *str;
1.53 christos 835: #ifndef NARROWCHAR
1.36 christos 836: static ct_buffer_t conv;
1.35 christos 837: #endif
1.16 lukem 838:
1.21 christos 839: if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
840: goto done;
841: if (fputs(hist_cookie, fp) == EOF)
842: goto done;
1.42 christos 843: ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
1.21 christos 844: if (ptr == NULL)
845: goto done;
846: for (i = 0, retval = HLAST(h, &ev);
1.16 lukem 847: retval != -1;
848: retval = HPREV(h, &ev), i++) {
1.39 christos 849: str = ct_encode_string(ev.str, &conv);
1.49 christos 850: len = strlen(str) * 4 + 1;
851: if (len > max_size) {
1.21 christos 852: char *nptr;
1.45 christos 853: max_size = (len + 1024) & (size_t)~1023;
1.42 christos 854: nptr = h_realloc(ptr, max_size * sizeof(*ptr));
1.21 christos 855: if (nptr == NULL) {
856: i = -1;
857: goto oomem;
858: }
859: ptr = nptr;
1.16 lukem 860: }
1.39 christos 861: (void) strvis(ptr, str, VIS_WHITE);
1.20 christos 862: (void) fprintf(fp, "%s\n", ptr);
1.16 lukem 863: }
1.21 christos 864: oomem:
1.42 christos 865: h_free(ptr);
1.21 christos 866: done:
1.43 christos 867: return i;
1.2 christos 868: }
869:
870:
1.47 christos 871: /* history_save():
872: * History save function
873: */
1.57 ! christos 874: static int
1.47 christos 875: history_save(TYPE(History) *h, const char *fname)
876: {
877: FILE *fp;
878: int i;
879:
880: if ((fp = fopen(fname, "w")) == NULL)
881: return -1;
882:
883: i = history_save_fp(h, fp);
884:
885: (void) fclose(fp);
886: return i;
887: }
888:
889:
1.1 cgd 890: /* history_prev_event():
891: * Find the previous event, with number given
892: */
1.57 ! christos 893: static int
1.35 christos 894: history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
1.16 lukem 895: {
896: int retval;
1.7 christos 897:
1.16 lukem 898: for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
899: if (ev->num == num)
1.43 christos 900: return 0;
1.16 lukem 901:
902: he_seterrev(ev, _HE_NOT_FOUND);
1.43 christos 903: return -1;
1.1 cgd 904: }
905:
906:
1.57 ! christos 907: static int
1.35 christos 908: history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d)
1.34 christos 909: {
910: int retval;
911:
912: for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
1.38 christos 913: if (ev->num == num) {
1.34 christos 914: if (d)
915: *d = ((history_t *)h->h_ref)->cursor->data;
1.43 christos 916: return 0;
1.34 christos 917: }
918:
919: he_seterrev(ev, _HE_NOT_FOUND);
1.43 christos 920: return -1;
1.34 christos 921: }
922:
923:
1.1 cgd 924: /* history_next_event():
925: * Find the next event, with number given
926: */
1.57 ! christos 927: static int
1.35 christos 928: history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
1.16 lukem 929: {
930: int retval;
931:
932: for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
933: if (ev->num == num)
1.43 christos 934: return 0;
1.7 christos 935:
1.16 lukem 936: he_seterrev(ev, _HE_NOT_FOUND);
1.43 christos 937: return -1;
1.1 cgd 938: }
939:
940:
941: /* history_prev_string():
942: * Find the previous event beginning with string
943: */
1.57 ! christos 944: static int
1.35 christos 945: history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
1.16 lukem 946: {
1.35 christos 947: size_t len = Strlen(str);
1.16 lukem 948: int retval;
1.7 christos 949:
1.16 lukem 950: for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
1.35 christos 951: if (Strncmp(str, ev->str, len) == 0)
1.43 christos 952: return 0;
1.16 lukem 953:
954: he_seterrev(ev, _HE_NOT_FOUND);
1.43 christos 955: return -1;
1.1 cgd 956: }
957:
958:
959: /* history_next_string():
960: * Find the next event beginning with string
961: */
1.57 ! christos 962: static int
1.35 christos 963: history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
1.16 lukem 964: {
1.35 christos 965: size_t len = Strlen(str);
1.16 lukem 966: int retval;
967:
968: for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
1.35 christos 969: if (Strncmp(str, ev->str, len) == 0)
1.43 christos 970: return 0;
1.7 christos 971:
1.16 lukem 972: he_seterrev(ev, _HE_NOT_FOUND);
1.43 christos 973: return -1;
1.1 cgd 974: }
975:
976:
977: /* history():
978: * User interface to history functions.
979: */
1.7 christos 980: int
1.35 christos 981: FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...)
1.1 cgd 982: {
1.16 lukem 983: va_list va;
1.35 christos 984: const Char *str;
1.16 lukem 985: int retval;
986:
987: va_start(va, fun);
988:
989: he_seterrev(ev, _HE_OK);
990:
991: switch (fun) {
992: case H_GETSIZE:
993: retval = history_getsize(h, ev);
994: break;
995:
996: case H_SETSIZE:
997: retval = history_setsize(h, ev, va_arg(va, int));
1.22 christos 998: break;
999:
1000: case H_GETUNIQUE:
1001: retval = history_getunique(h, ev);
1002: break;
1003:
1004: case H_SETUNIQUE:
1005: retval = history_setunique(h, ev, va_arg(va, int));
1.16 lukem 1006: break;
1007:
1008: case H_ADD:
1.35 christos 1009: str = va_arg(va, const Char *);
1.16 lukem 1010: retval = HADD(h, ev, str);
1011: break;
1012:
1.30 christos 1013: case H_DEL:
1014: retval = HDEL(h, ev, va_arg(va, const int));
1015: break;
1016:
1.16 lukem 1017: case H_ENTER:
1.35 christos 1018: str = va_arg(va, const Char *);
1.16 lukem 1019: if ((retval = HENTER(h, ev, str)) != -1)
1020: h->h_ent = ev->num;
1021: break;
1022:
1023: case H_APPEND:
1.35 christos 1024: str = va_arg(va, const Char *);
1.16 lukem 1025: if ((retval = HSET(h, ev, h->h_ent)) != -1)
1026: retval = HADD(h, ev, str);
1027: break;
1028:
1029: case H_FIRST:
1030: retval = HFIRST(h, ev);
1031: break;
1.1 cgd 1032:
1.16 lukem 1033: case H_NEXT:
1034: retval = HNEXT(h, ev);
1035: break;
1036:
1037: case H_LAST:
1038: retval = HLAST(h, ev);
1039: break;
1040:
1041: case H_PREV:
1042: retval = HPREV(h, ev);
1043: break;
1044:
1045: case H_CURR:
1046: retval = HCURR(h, ev);
1047: break;
1048:
1049: case H_SET:
1050: retval = HSET(h, ev, va_arg(va, const int));
1051: break;
1052:
1053: case H_CLEAR:
1054: HCLEAR(h, ev);
1055: retval = 0;
1056: break;
1057:
1058: case H_LOAD:
1059: retval = history_load(h, va_arg(va, const char *));
1060: if (retval == -1)
1061: he_seterrev(ev, _HE_HIST_READ);
1062: break;
1063:
1064: case H_SAVE:
1065: retval = history_save(h, va_arg(va, const char *));
1066: if (retval == -1)
1067: he_seterrev(ev, _HE_HIST_WRITE);
1068: break;
1069:
1.47 christos 1070: case H_SAVE_FP:
1071: retval = history_save_fp(h, va_arg(va, FILE *));
1072: if (retval == -1)
1073: he_seterrev(ev, _HE_HIST_WRITE);
1074: break;
1075:
1.16 lukem 1076: case H_PREV_EVENT:
1077: retval = history_prev_event(h, ev, va_arg(va, int));
1078: break;
1079:
1080: case H_NEXT_EVENT:
1081: retval = history_next_event(h, ev, va_arg(va, int));
1082: break;
1.1 cgd 1083:
1.16 lukem 1084: case H_PREV_STR:
1.35 christos 1085: retval = history_prev_string(h, ev, va_arg(va, const Char *));
1.16 lukem 1086: break;
1.7 christos 1087:
1.16 lukem 1088: case H_NEXT_STR:
1.35 christos 1089: retval = history_next_string(h, ev, va_arg(va, const Char *));
1.16 lukem 1090: break;
1.1 cgd 1091:
1.16 lukem 1092: case H_FUNC:
1.1 cgd 1093: {
1.35 christos 1094: TYPE(History) hf;
1.16 lukem 1095:
1.42 christos 1096: hf.h_ref = va_arg(va, void *);
1.16 lukem 1097: h->h_ent = -1;
1098: hf.h_first = va_arg(va, history_gfun_t);
1099: hf.h_next = va_arg(va, history_gfun_t);
1100: hf.h_last = va_arg(va, history_gfun_t);
1101: hf.h_prev = va_arg(va, history_gfun_t);
1102: hf.h_curr = va_arg(va, history_gfun_t);
1103: hf.h_set = va_arg(va, history_sfun_t);
1104: hf.h_clear = va_arg(va, history_vfun_t);
1105: hf.h_enter = va_arg(va, history_efun_t);
1106: hf.h_add = va_arg(va, history_efun_t);
1.30 christos 1107: hf.h_del = va_arg(va, history_sfun_t);
1.8 christos 1108:
1.16 lukem 1109: if ((retval = history_set_fun(h, &hf)) == -1)
1110: he_seterrev(ev, _HE_PARAM_MISSING);
1111: break;
1112: }
1113:
1114: case H_END:
1.36 christos 1115: FUN(history,end)(h);
1.16 lukem 1116: retval = 0;
1117: break;
1118:
1.34 christos 1119: case H_NEXT_EVDATA:
1120: {
1121: int num = va_arg(va, int);
1122: void **d = va_arg(va, void **);
1123: retval = history_next_evdata(h, ev, num, d);
1124: break;
1125: }
1126:
1127: case H_DELDATA:
1128: {
1129: int num = va_arg(va, int);
1130: void **d = va_arg(va, void **);
1131: retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d);
1132: break;
1133: }
1134:
1135: case H_REPLACE: /* only use after H_NEXT_EVDATA */
1136: {
1.35 christos 1137: const Char *line = va_arg(va, const Char *);
1.34 christos 1138: void *d = va_arg(va, void *);
1.35 christos 1139: const Char *s;
1140: if(!line || !(s = Strdup(line))) {
1.34 christos 1141: retval = -1;
1142: break;
1143: }
1144: ((history_t *)h->h_ref)->cursor->ev.str = s;
1145: ((history_t *)h->h_ref)->cursor->data = d;
1146: retval = 0;
1147: break;
1148: }
1149:
1.16 lukem 1150: default:
1151: retval = -1;
1152: he_seterrev(ev, _HE_UNKNOWN);
1153: break;
1154: }
1155: va_end(va);
1.34 christos 1156: return retval;
1.1 cgd 1157: }
CVSweb <webmaster@jp.NetBSD.org>