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