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