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