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