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