1 /* $OpenBSD: history.c,v 1.13 2004/08/23 18:31:25 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.13 2004/08/23 18:31:25 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, *lbuf; 655 size_t sz, max_size; 656 char *ptr; 657 int i = -1; 658 HistEvent ev; 659 660 lbuf = NULL; 661 if ((fp = fopen(fname, "r")) == NULL) 662 return (i); 663 664 if ((line = fgetln(fp, &sz)) == NULL) 665 goto done; 666 667 if (strncmp(line, hist_cookie, sz) != 0) 668 goto done; 669 670 ptr = h_malloc(max_size = 1024); 671 if (ptr == NULL) 672 goto done; 673 for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) { 674 if (line[sz - 1] == '\n') 675 line[sz - 1] = '\0'; 676 else { 677 lbuf = malloc(sz + 1); 678 if (lbuf == NULL) { 679 i = -1; 680 goto oomem; 681 } 682 memcpy(lbuf, line, sz); 683 lbuf[sz++] = '\0'; 684 line = lbuf; 685 } 686 if (sz > max_size) { 687 char *nptr; 688 max_size = (sz + 1023) & ~1023; 689 nptr = h_realloc(ptr, max_size); 690 if (nptr == NULL) { 691 i = -1; 692 goto oomem; 693 } 694 ptr = nptr; 695 } 696 (void) strunvis(ptr, line); 697 if (HENTER(h, &ev, ptr) == -1) { 698 h_free((ptr_t)ptr); 699 return -1; 700 } 701 } 702 oomem: 703 h_free((ptr_t)ptr); 704 done: 705 h_free(lbuf); 706 (void) fclose(fp); 707 return (i); 708 } 709 710 711 /* history_save(): 712 * History save function 713 */ 714 private int 715 history_save(History *h, const char *fname) 716 { 717 FILE *fp; 718 HistEvent ev; 719 int i = -1, retval; 720 size_t len, max_size; 721 char *ptr; 722 723 if ((fp = fopen(fname, "w")) == NULL) 724 return (-1); 725 726 if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) 727 goto done; 728 if (fputs(hist_cookie, fp) == EOF) 729 goto done; 730 ptr = h_malloc(max_size = 1024); 731 if (ptr == NULL) 732 goto done; 733 for (i = 0, retval = HLAST(h, &ev); 734 retval != -1; 735 retval = HPREV(h, &ev), i++) { 736 len = strlen(ev.str) * 4 + 1; 737 if (len > max_size) { 738 char *nptr; 739 max_size = (len + 1023) & ~1023; 740 nptr = h_realloc(ptr, max_size); 741 if (nptr == NULL) { 742 i = -1; 743 goto oomem; 744 } 745 ptr = nptr; 746 } 747 (void) strnvis(ptr, ev.str, max_size, VIS_WHITE); 748 (void) fprintf(fp, "%s\n", ptr); 749 } 750 oomem: 751 h_free((ptr_t)ptr); 752 done: 753 (void) fclose(fp); 754 return (i); 755 } 756 757 758 /* history_prev_event(): 759 * Find the previous event, with number given 760 */ 761 private int 762 history_prev_event(History *h, HistEvent *ev, int num) 763 { 764 int retval; 765 766 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) 767 if (ev->num == num) 768 return (0); 769 770 he_seterrev(ev, _HE_NOT_FOUND); 771 return (-1); 772 } 773 774 775 /* history_next_event(): 776 * Find the next event, with number given 777 */ 778 private int 779 history_next_event(History *h, HistEvent *ev, int num) 780 { 781 int retval; 782 783 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) 784 if (ev->num == num) 785 return (0); 786 787 he_seterrev(ev, _HE_NOT_FOUND); 788 return (-1); 789 } 790 791 792 /* history_prev_string(): 793 * Find the previous event beginning with string 794 */ 795 private int 796 history_prev_string(History *h, HistEvent *ev, const char *str) 797 { 798 size_t len = strlen(str); 799 int retval; 800 801 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) 802 if (strncmp(str, ev->str, len) == 0) 803 return (0); 804 805 he_seterrev(ev, _HE_NOT_FOUND); 806 return (-1); 807 } 808 809 810 /* history_next_string(): 811 * Find the next event beginning with string 812 */ 813 private int 814 history_next_string(History *h, HistEvent *ev, const char *str) 815 { 816 size_t len = strlen(str); 817 int retval; 818 819 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) 820 if (strncmp(str, ev->str, len) == 0) 821 return (0); 822 823 he_seterrev(ev, _HE_NOT_FOUND); 824 return (-1); 825 } 826 827 828 /* history(): 829 * User interface to history functions. 830 */ 831 int 832 history(History *h, HistEvent *ev, int fun, ...) 833 { 834 va_list va; 835 const char *str; 836 int retval; 837 838 va_start(va, fun); 839 840 he_seterrev(ev, _HE_OK); 841 842 switch (fun) { 843 case H_GETSIZE: 844 retval = history_getsize(h, ev); 845 break; 846 847 case H_SETSIZE: 848 retval = history_setsize(h, ev, va_arg(va, int)); 849 break; 850 851 case H_GETUNIQUE: 852 retval = history_getunique(h, ev); 853 break; 854 855 case H_SETUNIQUE: 856 retval = history_setunique(h, ev, va_arg(va, int)); 857 break; 858 859 case H_ADD: 860 str = va_arg(va, const char *); 861 retval = HADD(h, ev, str); 862 break; 863 864 case H_ENTER: 865 str = va_arg(va, const char *); 866 if ((retval = HENTER(h, ev, str)) != -1) 867 h->h_ent = ev->num; 868 break; 869 870 case H_APPEND: 871 str = va_arg(va, const char *); 872 if ((retval = HSET(h, ev, h->h_ent)) != -1) 873 retval = HADD(h, ev, str); 874 break; 875 876 case H_FIRST: 877 retval = HFIRST(h, ev); 878 break; 879 880 case H_NEXT: 881 retval = HNEXT(h, ev); 882 break; 883 884 case H_LAST: 885 retval = HLAST(h, ev); 886 break; 887 888 case H_PREV: 889 retval = HPREV(h, ev); 890 break; 891 892 case H_CURR: 893 retval = HCURR(h, ev); 894 break; 895 896 case H_SET: 897 retval = HSET(h, ev, va_arg(va, const int)); 898 break; 899 900 case H_CLEAR: 901 HCLEAR(h, ev); 902 retval = 0; 903 break; 904 905 case H_LOAD: 906 retval = history_load(h, va_arg(va, const char *)); 907 if (retval == -1) 908 he_seterrev(ev, _HE_HIST_READ); 909 break; 910 911 case H_SAVE: 912 retval = history_save(h, va_arg(va, const char *)); 913 if (retval == -1) 914 he_seterrev(ev, _HE_HIST_WRITE); 915 break; 916 917 case H_PREV_EVENT: 918 retval = history_prev_event(h, ev, va_arg(va, int)); 919 break; 920 921 case H_NEXT_EVENT: 922 retval = history_next_event(h, ev, va_arg(va, int)); 923 break; 924 925 case H_PREV_STR: 926 retval = history_prev_string(h, ev, va_arg(va, const char *)); 927 break; 928 929 case H_NEXT_STR: 930 retval = history_next_string(h, ev, va_arg(va, const char *)); 931 break; 932 933 case H_FUNC: 934 { 935 History hf; 936 937 hf.h_ref = va_arg(va, ptr_t); 938 h->h_ent = -1; 939 hf.h_first = va_arg(va, history_gfun_t); 940 hf.h_next = va_arg(va, history_gfun_t); 941 hf.h_last = va_arg(va, history_gfun_t); 942 hf.h_prev = va_arg(va, history_gfun_t); 943 hf.h_curr = va_arg(va, history_gfun_t); 944 hf.h_set = va_arg(va, history_sfun_t); 945 hf.h_clear = va_arg(va, history_vfun_t); 946 hf.h_enter = va_arg(va, history_efun_t); 947 hf.h_add = va_arg(va, history_efun_t); 948 949 if ((retval = history_set_fun(h, &hf)) == -1) 950 he_seterrev(ev, _HE_PARAM_MISSING); 951 break; 952 } 953 954 case H_END: 955 history_end(h); 956 retval = 0; 957 break; 958 959 default: 960 retval = -1; 961 he_seterrev(ev, _HE_UNKNOWN); 962 break; 963 } 964 va_end(va); 965 return (retval); 966 } 967