history.c revision 237448
1/*- 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Christos Zoulas of Cornell University. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $NetBSD: history.c,v 1.34 2009/09/07 21:24:33 christos Exp $ 33 */ 34 35#if !defined(lint) && !defined(SCCSID) 36static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93"; 37#endif /* not lint && not SCCSID */ 38#include <sys/cdefs.h> 39__FBSDID("$FreeBSD: head/lib/libedit/history.c 237448 2012-06-22 18:01:22Z pfg $"); 40 41/* 42 * hist.c: History access functions 43 */ 44#include "sys.h" 45 46#include <string.h> 47#include <stdlib.h> 48#include <stdarg.h> 49#include <vis.h> 50#include <sys/stat.h> 51 52static const char hist_cookie[] = "_HiStOrY_V2_\n"; 53 54#include "histedit.h" 55 56typedef int (*history_gfun_t)(ptr_t, HistEvent *); 57typedef int (*history_efun_t)(ptr_t, HistEvent *, const char *); 58typedef void (*history_vfun_t)(ptr_t, HistEvent *); 59typedef int (*history_sfun_t)(ptr_t, HistEvent *, const int); 60 61struct history { 62 ptr_t h_ref; /* Argument for history fcns */ 63 int h_ent; /* Last entry point for history */ 64 history_gfun_t h_first; /* Get the first element */ 65 history_gfun_t h_next; /* Get the next element */ 66 history_gfun_t h_last; /* Get the last element */ 67 history_gfun_t h_prev; /* Get the previous element */ 68 history_gfun_t h_curr; /* Get the current element */ 69 history_sfun_t h_set; /* Set the current element */ 70 history_sfun_t h_del; /* Set the given element */ 71 history_vfun_t h_clear; /* Clear the history list */ 72 history_efun_t h_enter; /* Add an element */ 73 history_efun_t h_add; /* Append to an element */ 74}; 75 76#define HNEXT(h, ev) (*(h)->h_next)((h)->h_ref, ev) 77#define HFIRST(h, ev) (*(h)->h_first)((h)->h_ref, ev) 78#define HPREV(h, ev) (*(h)->h_prev)((h)->h_ref, ev) 79#define HLAST(h, ev) (*(h)->h_last)((h)->h_ref, ev) 80#define HCURR(h, ev) (*(h)->h_curr)((h)->h_ref, ev) 81#define HSET(h, ev, n) (*(h)->h_set)((h)->h_ref, ev, n) 82#define HCLEAR(h, ev) (*(h)->h_clear)((h)->h_ref, ev) 83#define HENTER(h, ev, str) (*(h)->h_enter)((h)->h_ref, ev, str) 84#define HADD(h, ev, str) (*(h)->h_add)((h)->h_ref, ev, str) 85#define HDEL(h, ev, n) (*(h)->h_del)((h)->h_ref, ev, n) 86 87#define h_strdup(a) strdup(a) 88#define h_malloc(a) malloc(a) 89#define h_realloc(a, b) realloc((a), (b)) 90#define h_free(a) free(a) 91 92typedef struct { 93 int num; 94 char *str; 95} HistEventPrivate; 96 97 98 99private int history_setsize(History *, HistEvent *, int); 100private int history_getsize(History *, HistEvent *); 101private int history_setunique(History *, HistEvent *, int); 102private int history_getunique(History *, HistEvent *); 103private int history_set_fun(History *, History *); 104private int history_load(History *, const char *); 105private int history_save(History *, const char *); 106private int history_prev_event(History *, HistEvent *, int); 107private int history_next_event(History *, HistEvent *, int); 108private int history_next_string(History *, HistEvent *, const char *); 109private int history_prev_string(History *, HistEvent *, const char *); 110 111 112/***********************************************************************/ 113 114/* 115 * Builtin- history implementation 116 */ 117typedef struct hentry_t { 118 HistEvent ev; /* What we return */ 119 void *data; /* data */ 120 struct hentry_t *next; /* Next entry */ 121 struct hentry_t *prev; /* Previous entry */ 122} hentry_t; 123 124typedef struct history_t { 125 hentry_t list; /* Fake list header element */ 126 hentry_t *cursor; /* Current element in the list */ 127 int max; /* Maximum number of events */ 128 int cur; /* Current number of events */ 129 int eventid; /* For generation of unique event id */ 130 int flags; /* History flags */ 131#define H_UNIQUE 1 /* Store only unique elements */ 132} history_t; 133 134private int history_def_next(ptr_t, HistEvent *); 135private int history_def_first(ptr_t, HistEvent *); 136private int history_def_prev(ptr_t, HistEvent *); 137private int history_def_last(ptr_t, HistEvent *); 138private int history_def_curr(ptr_t, HistEvent *); 139private int history_def_set(ptr_t, HistEvent *, const int); 140private void history_def_clear(ptr_t, HistEvent *); 141private int history_def_enter(ptr_t, HistEvent *, const char *); 142private int history_def_add(ptr_t, HistEvent *, const char *); 143private int history_def_del(ptr_t, HistEvent *, const int); 144 145private int history_def_init(ptr_t *, HistEvent *, int); 146private int history_def_insert(history_t *, HistEvent *, const char *); 147private void history_def_delete(history_t *, HistEvent *, hentry_t *); 148 149private int history_deldata_nth(history_t *, HistEvent *, int, void **); 150private int history_set_nth(ptr_t, HistEvent *, int); 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 */ 168static 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 */ 207private int 208history_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 */ 227private int 228history_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 */ 247private int 248history_def_next(ptr_t p, HistEvent *ev) 249{ 250 history_t *h = (history_t *) p; 251 252 if (h->cursor == &h->list) { 253 he_seterrev(ev, _HE_EMPTY_LIST); 254 return (-1); 255 } 256 257 if (h->cursor->next == &h->list) { 258 he_seterrev(ev, _HE_END_REACHED); 259 return (-1); 260 } 261 262 h->cursor = h->cursor->next; 263 *ev = h->cursor->ev; 264 265 return (0); 266} 267 268 269/* history_def_prev(): 270 * Default function to return the previous event in the history. 271 */ 272private int 273history_def_prev(ptr_t p, HistEvent *ev) 274{ 275 history_t *h = (history_t *) p; 276 277 if (h->cursor == &h->list) { 278 he_seterrev(ev, 279 (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST); 280 return (-1); 281 } 282 283 if (h->cursor->prev == &h->list) { 284 he_seterrev(ev, _HE_START_REACHED); 285 return (-1); 286 } 287 288 h->cursor = h->cursor->prev; 289 *ev = h->cursor->ev; 290 291 return (0); 292} 293 294 295/* history_def_curr(): 296 * Default function to return the current event in the history. 297 */ 298private int 299history_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 */ 319private int 320history_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_set_nth(): 343 * Default function to set the current event in the history to the 344 * n-th one. 345 */ 346private int 347history_set_nth(ptr_t p, HistEvent *ev, int n) 348{ 349 history_t *h = (history_t *) p; 350 351 if (h->cur == 0) { 352 he_seterrev(ev, _HE_EMPTY_LIST); 353 return (-1); 354 } 355 for (h->cursor = h->list.prev; h->cursor != &h->list; 356 h->cursor = h->cursor->prev) 357 if (n-- <= 0) 358 break; 359 if (h->cursor == &h->list) { 360 he_seterrev(ev, _HE_NOT_FOUND); 361 return (-1); 362 } 363 return (0); 364} 365 366 367/* history_def_add(): 368 * Append string to element 369 */ 370private int 371history_def_add(ptr_t p, HistEvent *ev, const char *str) 372{ 373 history_t *h = (history_t *) p; 374 size_t len; 375 char *s; 376 HistEventPrivate *evp = (void *)&h->cursor->ev; 377 378 if (h->cursor == &h->list) 379 return (history_def_enter(p, ev, str)); 380 len = strlen(evp->str) + strlen(str) + 1; 381 s = (char *) h_malloc(len); 382 if (s == NULL) { 383 he_seterrev(ev, _HE_MALLOC_FAILED); 384 return (-1); 385 } 386 (void) strlcpy(s, h->cursor->ev.str, len); 387 (void) strlcat(s, str, len); 388 h_free((ptr_t)evp->str); 389 evp->str = s; 390 *ev = h->cursor->ev; 391 return (0); 392} 393 394 395private int 396history_deldata_nth(history_t *h, HistEvent *ev, 397 int num, void **data) 398{ 399 if (history_set_nth(h, ev, num) != 0) 400 return (-1); 401 /* magic value to skip delete (just set to n-th history) */ 402 if (data == (void **)-1) 403 return (0); 404 ev->str = strdup(h->cursor->ev.str); 405 ev->num = h->cursor->ev.num; 406 if (data) 407 *data = h->cursor->data; 408 history_def_delete(h, ev, h->cursor); 409 return (0); 410} 411 412 413/* history_def_del(): 414 * Delete element hp of the h list 415 */ 416/* ARGSUSED */ 417private int 418history_def_del(ptr_t p, HistEvent *ev __unused, 419 const int num) 420{ 421 history_t *h = (history_t *) p; 422 if (history_def_set(h, ev, num) != 0) 423 return (-1); 424 ev->str = strdup(h->cursor->ev.str); 425 ev->num = h->cursor->ev.num; 426 history_def_delete(h, ev, h->cursor); 427 return (0); 428} 429 430 431/* history_def_delete(): 432 * Delete element hp of the h list 433 */ 434/* ARGSUSED */ 435private void 436history_def_delete(history_t *h, 437 HistEvent *ev __unused, hentry_t *hp) 438{ 439 HistEventPrivate *evp = (void *)&hp->ev; 440 if (hp == &h->list) 441 abort(); 442 if (h->cursor == hp) { 443 h->cursor = hp->prev; 444 if (h->cursor == &h->list) 445 h->cursor = hp->next; 446 } 447 hp->prev->next = hp->next; 448 hp->next->prev = hp->prev; 449 h_free((ptr_t) evp->str); 450 h_free(hp); 451 h->cur--; 452} 453 454 455/* history_def_insert(): 456 * Insert element with string str in the h list 457 */ 458private int 459history_def_insert(history_t *h, HistEvent *ev, const char *str) 460{ 461 462 h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t)); 463 if (h->cursor == NULL) 464 goto oomem; 465 if ((h->cursor->ev.str = h_strdup(str)) == NULL) { 466 h_free((ptr_t)h->cursor); 467 goto oomem; 468 } 469 h->cursor->data = NULL; 470 h->cursor->ev.num = ++h->eventid; 471 h->cursor->next = h->list.next; 472 h->cursor->prev = &h->list; 473 h->list.next->prev = h->cursor; 474 h->list.next = h->cursor; 475 h->cur++; 476 477 *ev = h->cursor->ev; 478 return (0); 479oomem: 480 he_seterrev(ev, _HE_MALLOC_FAILED); 481 return (-1); 482} 483 484 485/* history_def_enter(): 486 * Default function to enter an item in the history 487 */ 488private int 489history_def_enter(ptr_t p, HistEvent *ev, const char *str) 490{ 491 history_t *h = (history_t *) p; 492 493 if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list && 494 strcmp(h->list.next->ev.str, str) == 0) 495 return (0); 496 497 if (history_def_insert(h, ev, str) == -1) 498 return (-1); /* error, keep error message */ 499 500 /* 501 * Always keep at least one entry. 502 * This way we don't have to check for the empty list. 503 */ 504 while (h->cur > h->max && h->cur > 0) 505 history_def_delete(h, ev, h->list.prev); 506 507 return (1); 508} 509 510 511/* history_def_init(): 512 * Default history initialization function 513 */ 514/* ARGSUSED */ 515private int 516history_def_init(ptr_t *p, HistEvent *ev __unused, int n) 517{ 518 history_t *h = (history_t *) h_malloc(sizeof(history_t)); 519 if (h == NULL) 520 return -1; 521 522 if (n <= 0) 523 n = 0; 524 h->eventid = 0; 525 h->cur = 0; 526 h->max = n; 527 h->list.next = h->list.prev = &h->list; 528 h->list.ev.str = NULL; 529 h->list.ev.num = 0; 530 h->cursor = &h->list; 531 h->flags = 0; 532 *p = (ptr_t) h; 533 return 0; 534} 535 536 537/* history_def_clear(): 538 * Default history cleanup function 539 */ 540private void 541history_def_clear(ptr_t p, HistEvent *ev) 542{ 543 history_t *h = (history_t *) p; 544 545 while (h->list.prev != &h->list) 546 history_def_delete(h, ev, h->list.prev); 547 h->eventid = 0; 548 h->cur = 0; 549} 550 551 552 553 554/************************************************************************/ 555 556/* history_init(): 557 * Initialization function. 558 */ 559public History * 560history_init(void) 561{ 562 HistEvent ev; 563 History *h = (History *) h_malloc(sizeof(History)); 564 if (h == NULL) 565 return NULL; 566 567 if (history_def_init(&h->h_ref, &ev, 0) == -1) { 568 h_free((ptr_t)h); 569 return NULL; 570 } 571 h->h_ent = -1; 572 h->h_next = history_def_next; 573 h->h_first = history_def_first; 574 h->h_last = history_def_last; 575 h->h_prev = history_def_prev; 576 h->h_curr = history_def_curr; 577 h->h_set = history_def_set; 578 h->h_clear = history_def_clear; 579 h->h_enter = history_def_enter; 580 h->h_add = history_def_add; 581 h->h_del = history_def_del; 582 583 return (h); 584} 585 586 587/* history_end(): 588 * clean up history; 589 */ 590public void 591history_end(History *h) 592{ 593 HistEvent ev; 594 595 if (h->h_next == history_def_next) 596 history_def_clear(h->h_ref, &ev); 597 h_free(h->h_ref); 598 h_free(h); 599} 600 601 602 603/* history_setsize(): 604 * Set history number of events 605 */ 606private int 607history_setsize(History *h, HistEvent *ev, int num) 608{ 609 610 if (h->h_next != history_def_next) { 611 he_seterrev(ev, _HE_NOT_ALLOWED); 612 return (-1); 613 } 614 if (num < 0) { 615 he_seterrev(ev, _HE_BAD_PARAM); 616 return (-1); 617 } 618 history_def_setsize(h->h_ref, num); 619 return (0); 620} 621 622 623/* history_getsize(): 624 * Get number of events currently in history 625 */ 626private int 627history_getsize(History *h, HistEvent *ev) 628{ 629 if (h->h_next != history_def_next) { 630 he_seterrev(ev, _HE_NOT_ALLOWED); 631 return (-1); 632 } 633 ev->num = history_def_getsize(h->h_ref); 634 if (ev->num < -1) { 635 he_seterrev(ev, _HE_SIZE_NEGATIVE); 636 return (-1); 637 } 638 return (0); 639} 640 641 642/* history_setunique(): 643 * Set if adjacent equal events should not be entered in history. 644 */ 645private int 646history_setunique(History *h, HistEvent *ev, int uni) 647{ 648 649 if (h->h_next != history_def_next) { 650 he_seterrev(ev, _HE_NOT_ALLOWED); 651 return (-1); 652 } 653 history_def_setunique(h->h_ref, uni); 654 return (0); 655} 656 657 658/* history_getunique(): 659 * Get if adjacent equal events should not be entered in history. 660 */ 661private int 662history_getunique(History *h, HistEvent *ev) 663{ 664 if (h->h_next != history_def_next) { 665 he_seterrev(ev, _HE_NOT_ALLOWED); 666 return (-1); 667 } 668 ev->num = history_def_getunique(h->h_ref); 669 return (0); 670} 671 672 673/* history_set_fun(): 674 * Set history functions 675 */ 676private int 677history_set_fun(History *h, History *nh) 678{ 679 HistEvent ev; 680 681 if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL || 682 nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL || 683 nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL || 684 nh->h_del == NULL || nh->h_ref == NULL) { 685 if (h->h_next != history_def_next) { 686 history_def_init(&h->h_ref, &ev, 0); 687 h->h_first = history_def_first; 688 h->h_next = history_def_next; 689 h->h_last = history_def_last; 690 h->h_prev = history_def_prev; 691 h->h_curr = history_def_curr; 692 h->h_set = history_def_set; 693 h->h_clear = history_def_clear; 694 h->h_enter = history_def_enter; 695 h->h_add = history_def_add; 696 h->h_del = history_def_del; 697 } 698 return (-1); 699 } 700 if (h->h_next == history_def_next) 701 history_def_clear(h->h_ref, &ev); 702 703 h->h_ent = -1; 704 h->h_first = nh->h_first; 705 h->h_next = nh->h_next; 706 h->h_last = nh->h_last; 707 h->h_prev = nh->h_prev; 708 h->h_curr = nh->h_curr; 709 h->h_set = nh->h_set; 710 h->h_clear = nh->h_clear; 711 h->h_enter = nh->h_enter; 712 h->h_add = nh->h_add; 713 h->h_del = nh->h_del; 714 715 return (0); 716} 717 718 719/* history_load(): 720 * History load function 721 */ 722private int 723history_load(History *h, const char *fname) 724{ 725 FILE *fp; 726 char *line; 727 size_t sz, max_size; 728 char *ptr; 729 int i = -1; 730 HistEvent ev; 731 732 if ((fp = fopen(fname, "r")) == NULL) 733 return (i); 734 735 if ((line = fgetln(fp, &sz)) == NULL) 736 goto done; 737 738 if (strncmp(line, hist_cookie, sz) != 0) 739 goto done; 740 741 ptr = h_malloc(max_size = 1024); 742 if (ptr == NULL) 743 goto done; 744 for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) { 745 char c = line[sz]; 746 747 if (sz != 0 && line[sz - 1] == '\n') 748 line[--sz] = '\0'; 749 else 750 line[sz] = '\0'; 751 752 if (max_size < sz) { 753 char *nptr; 754 max_size = (sz + 1024) & ~1023; 755 nptr = h_realloc(ptr, max_size); 756 if (nptr == NULL) { 757 i = -1; 758 goto oomem; 759 } 760 ptr = nptr; 761 } 762 (void) strunvis(ptr, line); 763 line[sz] = c; 764 if (HENTER(h, &ev, ptr) == -1) { 765 i = -1; 766 goto oomem; 767 } 768 } 769oomem: 770 h_free((ptr_t)ptr); 771done: 772 (void) fclose(fp); 773 return (i); 774} 775 776 777/* history_save(): 778 * History save function 779 */ 780private int 781history_save(History *h, const char *fname) 782{ 783 FILE *fp; 784 HistEvent ev; 785 int i = -1, retval; 786 size_t len, max_size; 787 char *ptr; 788 789 if ((fp = fopen(fname, "w")) == NULL) 790 return (-1); 791 792 if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) 793 goto done; 794 if (fputs(hist_cookie, fp) == EOF) 795 goto done; 796 ptr = h_malloc(max_size = 1024); 797 if (ptr == NULL) 798 goto done; 799 for (i = 0, retval = HLAST(h, &ev); 800 retval != -1; 801 retval = HPREV(h, &ev), i++) { 802 len = strlen(ev.str) * 4; 803 if (len >= max_size) { 804 char *nptr; 805 max_size = (len + 1024) & ~1023; 806 nptr = h_realloc(ptr, max_size); 807 if (nptr == NULL) { 808 i = -1; 809 goto oomem; 810 } 811 ptr = nptr; 812 } 813 (void) strvis(ptr, ev.str, VIS_WHITE); 814 (void) fprintf(fp, "%s\n", ptr); 815 } 816oomem: 817 h_free((ptr_t)ptr); 818done: 819 (void) fclose(fp); 820 return (i); 821} 822 823 824/* history_prev_event(): 825 * Find the previous event, with number given 826 */ 827private int 828history_prev_event(History *h, HistEvent *ev, int num) 829{ 830 int retval; 831 832 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) 833 if (ev->num == num) 834 return (0); 835 836 he_seterrev(ev, _HE_NOT_FOUND); 837 return (-1); 838} 839 840 841private int 842history_next_evdata(History *h, HistEvent *ev, int num, void **d) 843{ 844 int retval; 845 846 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) 847 if (num-- <= 0) { 848 if (d) 849 *d = ((history_t *)h->h_ref)->cursor->data; 850 return (0); 851 } 852 853 he_seterrev(ev, _HE_NOT_FOUND); 854 return (-1); 855} 856 857 858/* history_next_event(): 859 * Find the next event, with number given 860 */ 861private int 862history_next_event(History *h, HistEvent *ev, int num) 863{ 864 int retval; 865 866 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) 867 if (ev->num == num) 868 return (0); 869 870 he_seterrev(ev, _HE_NOT_FOUND); 871 return (-1); 872} 873 874 875/* history_prev_string(): 876 * Find the previous event beginning with string 877 */ 878private int 879history_prev_string(History *h, HistEvent *ev, const char *str) 880{ 881 size_t len = strlen(str); 882 int retval; 883 884 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) 885 if (strncmp(str, ev->str, len) == 0) 886 return (0); 887 888 he_seterrev(ev, _HE_NOT_FOUND); 889 return (-1); 890} 891 892 893/* history_next_string(): 894 * Find the next event beginning with string 895 */ 896private int 897history_next_string(History *h, HistEvent *ev, const char *str) 898{ 899 size_t len = strlen(str); 900 int retval; 901 902 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) 903 if (strncmp(str, ev->str, len) == 0) 904 return (0); 905 906 he_seterrev(ev, _HE_NOT_FOUND); 907 return (-1); 908} 909 910 911/* history(): 912 * User interface to history functions. 913 */ 914int 915history(History *h, HistEvent *ev, int fun, ...) 916{ 917 va_list va; 918 const char *str; 919 int retval; 920 921 va_start(va, fun); 922 923 he_seterrev(ev, _HE_OK); 924 925 switch (fun) { 926 case H_GETSIZE: 927 retval = history_getsize(h, ev); 928 break; 929 930 case H_SETSIZE: 931 retval = history_setsize(h, ev, va_arg(va, int)); 932 break; 933 934 case H_GETUNIQUE: 935 retval = history_getunique(h, ev); 936 break; 937 938 case H_SETUNIQUE: 939 retval = history_setunique(h, ev, va_arg(va, int)); 940 break; 941 942 case H_ADD: 943 str = va_arg(va, const char *); 944 retval = HADD(h, ev, str); 945 break; 946 947 case H_DEL: 948 retval = HDEL(h, ev, va_arg(va, const int)); 949 break; 950 951 case H_ENTER: 952 str = va_arg(va, const char *); 953 if ((retval = HENTER(h, ev, str)) != -1) 954 h->h_ent = ev->num; 955 break; 956 957 case H_APPEND: 958 str = va_arg(va, const char *); 959 if ((retval = HSET(h, ev, h->h_ent)) != -1) 960 retval = HADD(h, ev, str); 961 break; 962 963 case H_FIRST: 964 retval = HFIRST(h, ev); 965 break; 966 967 case H_NEXT: 968 retval = HNEXT(h, ev); 969 break; 970 971 case H_LAST: 972 retval = HLAST(h, ev); 973 break; 974 975 case H_PREV: 976 retval = HPREV(h, ev); 977 break; 978 979 case H_CURR: 980 retval = HCURR(h, ev); 981 break; 982 983 case H_SET: 984 retval = HSET(h, ev, va_arg(va, const int)); 985 break; 986 987 case H_CLEAR: 988 HCLEAR(h, ev); 989 retval = 0; 990 break; 991 992 case H_LOAD: 993 retval = history_load(h, va_arg(va, const char *)); 994 if (retval == -1) 995 he_seterrev(ev, _HE_HIST_READ); 996 break; 997 998 case H_SAVE: 999 retval = history_save(h, va_arg(va, const char *)); 1000 if (retval == -1) 1001 he_seterrev(ev, _HE_HIST_WRITE); 1002 break; 1003 1004 case H_PREV_EVENT: 1005 retval = history_prev_event(h, ev, va_arg(va, int)); 1006 break; 1007 1008 case H_NEXT_EVENT: 1009 retval = history_next_event(h, ev, va_arg(va, int)); 1010 break; 1011 1012 case H_PREV_STR: 1013 retval = history_prev_string(h, ev, va_arg(va, const char *)); 1014 break; 1015 1016 case H_NEXT_STR: 1017 retval = history_next_string(h, ev, va_arg(va, const char *)); 1018 break; 1019 1020 case H_FUNC: 1021 { 1022 History hf; 1023 1024 hf.h_ref = va_arg(va, ptr_t); 1025 h->h_ent = -1; 1026 hf.h_first = va_arg(va, history_gfun_t); 1027 hf.h_next = va_arg(va, history_gfun_t); 1028 hf.h_last = va_arg(va, history_gfun_t); 1029 hf.h_prev = va_arg(va, history_gfun_t); 1030 hf.h_curr = va_arg(va, history_gfun_t); 1031 hf.h_set = va_arg(va, history_sfun_t); 1032 hf.h_clear = va_arg(va, history_vfun_t); 1033 hf.h_enter = va_arg(va, history_efun_t); 1034 hf.h_add = va_arg(va, history_efun_t); 1035 hf.h_del = va_arg(va, history_sfun_t); 1036 1037 if ((retval = history_set_fun(h, &hf)) == -1) 1038 he_seterrev(ev, _HE_PARAM_MISSING); 1039 break; 1040 } 1041 1042 case H_END: 1043 history_end(h); 1044 retval = 0; 1045 break; 1046 1047 case H_NEXT_EVDATA: 1048 { 1049 int num = va_arg(va, int); 1050 void **d = va_arg(va, void **); 1051 retval = history_next_evdata(h, ev, num, d); 1052 break; 1053 } 1054 1055 case H_DELDATA: 1056 { 1057 int num = va_arg(va, int); 1058 void **d = va_arg(va, void **); 1059 retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d); 1060 break; 1061 } 1062 1063 case H_REPLACE: /* only use after H_NEXT_EVDATA */ 1064 { 1065 const char *line = va_arg(va, const char *); 1066 void *d = va_arg(va, void *); 1067 const char *s; 1068 if(!line || !(s = strdup(line))) { 1069 retval = -1; 1070 break; 1071 } 1072 ((history_t *)h->h_ref)->cursor->ev.str = s; 1073 ((history_t *)h->h_ref)->cursor->data = d; 1074 retval = 0; 1075 break; 1076 } 1077 1078 default: 1079 retval = -1; 1080 he_seterrev(ev, _HE_UNKNOWN); 1081 break; 1082 } 1083 va_end(va); 1084 return retval; 1085} 1086