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