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