1 /* $OpenBSD: history.c,v 1.16 2011/07/07 05:40:42 okan Exp $ */ 2 /* $NetBSD: history.c,v 1.37 2010/01/03 18:27:10 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: TYPE(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 #include "chartype.h" 55 56 typedef int (*history_gfun_t)(ptr_t, TYPE(HistEvent) *); 57 typedef int (*history_efun_t)(ptr_t, TYPE(HistEvent) *, const Char *); 58 typedef void (*history_vfun_t)(ptr_t, TYPE(HistEvent) *); 59 typedef int (*history_sfun_t)(ptr_t, TYPE(HistEvent) *, const int); 60 61 struct TYPE(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 92 typedef struct { 93 int num; 94 Char *str; 95 } HistEventPrivate; 96 97 98 99 private int history_setsize(TYPE(History) *, TYPE(HistEvent) *, int); 100 private int history_getsize(TYPE(History) *, TYPE(HistEvent) *); 101 private int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int); 102 private int history_getunique(TYPE(History) *, TYPE(HistEvent) *); 103 private int history_set_fun(TYPE(History) *, TYPE(History) *); 104 private int history_load(TYPE(History) *, const char *); 105 private int history_save(TYPE(History) *, const char *); 106 private int history_prev_event(TYPE(History) *, TYPE(HistEvent) *, int); 107 private int history_next_event(TYPE(History) *, TYPE(HistEvent) *, int); 108 private int history_next_string(TYPE(History) *, TYPE(HistEvent) *, const Char *); 109 private int history_prev_string(TYPE(History) *, TYPE(HistEvent) *, const Char *); 110 111 112 /***********************************************************************/ 113 114 /* 115 * Builtin- history implementation 116 */ 117 typedef struct hentry_t { 118 TYPE(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 124 typedef 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; /* TYPE(History) flags */ 131 #define H_UNIQUE 1 /* Store only unique elements */ 132 } history_t; 133 134 private int history_def_next(ptr_t, TYPE(HistEvent) *); 135 private int history_def_first(ptr_t, TYPE(HistEvent) *); 136 private int history_def_prev(ptr_t, TYPE(HistEvent) *); 137 private int history_def_last(ptr_t, TYPE(HistEvent) *); 138 private int history_def_curr(ptr_t, TYPE(HistEvent) *); 139 private int history_def_set(ptr_t, TYPE(HistEvent) *, const int); 140 private void history_def_clear(ptr_t, TYPE(HistEvent) *); 141 private int history_def_enter(ptr_t, TYPE(HistEvent) *, const Char *); 142 private int history_def_add(ptr_t, TYPE(HistEvent) *, const Char *); 143 private int history_def_del(ptr_t, TYPE(HistEvent) *, const int); 144 145 private int history_def_init(ptr_t *, TYPE(HistEvent) *, int); 146 private int history_def_insert(history_t *, TYPE(HistEvent) *, const Char *); 147 private void history_def_delete(history_t *, TYPE(HistEvent) *, hentry_t *); 148 149 private int history_deldata_nth(history_t *, TYPE(HistEvent) *, int, void **); 150 private int history_set_nth(ptr_t, TYPE(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 */ 168 static const Char *const he_errlist[] = { 169 STR("OK"), 170 STR("unknown error"), 171 STR("malloc() failed"), 172 STR("first event not found"), 173 STR("last event not found"), 174 STR("empty list"), 175 STR("no next event"), 176 STR("no previous event"), 177 STR("current event is invalid"), 178 STR("event not found"), 179 STR("can't read history from file"), 180 STR("can't write history"), 181 STR("required parameter(s) not supplied"), 182 STR("history size negative"), 183 STR("function not allowed with other history-functions-set the default"), 184 STR("bad parameters") 185 }; 186 /* error codes */ 187 #define _HE_OK 0 188 #define _HE_UNKNOWN 1 189 #define _HE_MALLOC_FAILED 2 190 #define _HE_FIRST_NOTFOUND 3 191 #define _HE_LAST_NOTFOUND 4 192 #define _HE_EMPTY_LIST 5 193 #define _HE_END_REACHED 6 194 #define _HE_START_REACHED 7 195 #define _HE_CURR_INVALID 8 196 #define _HE_NOT_FOUND 9 197 #define _HE_HIST_READ 10 198 #define _HE_HIST_WRITE 11 199 #define _HE_PARAM_MISSING 12 200 #define _HE_SIZE_NEGATIVE 13 201 #define _HE_NOT_ALLOWED 14 202 #define _HE_BAD_PARAM 15 203 204 /* history_def_first(): 205 * Default function to return the first event in the history. 206 */ 207 private int 208 history_def_first(ptr_t p, TYPE(HistEvent) *ev) 209 { 210 history_t *h = (history_t *) p; 211 212 h->cursor = h->list.next; 213 if (h->cursor != &h->list) 214 *ev = h->cursor->ev; 215 else { 216 he_seterrev(ev, _HE_FIRST_NOTFOUND); 217 return (-1); 218 } 219 220 return (0); 221 } 222 223 224 /* history_def_last(): 225 * Default function to return the last event in the history. 226 */ 227 private int 228 history_def_last(ptr_t p, TYPE(HistEvent) *ev) 229 { 230 history_t *h = (history_t *) p; 231 232 h->cursor = h->list.prev; 233 if (h->cursor != &h->list) 234 *ev = h->cursor->ev; 235 else { 236 he_seterrev(ev, _HE_LAST_NOTFOUND); 237 return (-1); 238 } 239 240 return (0); 241 } 242 243 244 /* history_def_next(): 245 * Default function to return the next event in the history. 246 */ 247 private int 248 history_def_next(ptr_t p, TYPE(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 */ 272 private int 273 history_def_prev(ptr_t p, TYPE(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 */ 298 private int 299 history_def_curr(ptr_t p, TYPE(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, TYPE(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 */ 346 private int 347 history_set_nth(ptr_t p, TYPE(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 */ 370 private int 371 history_def_add(ptr_t p, TYPE(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 = h_malloc(len * sizeof(*s)); 382 if (s == NULL) { 383 he_seterrev(ev, _HE_MALLOC_FAILED); 384 return (-1); 385 } 386 (void) Strncpy(s, h->cursor->ev.str, len); 387 s[len - 1] = '\0'; 388 (void) Strncat(s, str, len - Strlen(s) - 1); 389 h_free((ptr_t)evp->str); 390 evp->str = s; 391 *ev = h->cursor->ev; 392 return (0); 393 } 394 395 396 private int 397 history_deldata_nth(history_t *h, TYPE(HistEvent) *ev, 398 int num, void **data) 399 { 400 if (history_set_nth(h, ev, num) != 0) 401 return (-1); 402 /* magic value to skip delete (just set to n-th history) */ 403 if (data == (void **)-1) 404 return (0); 405 ev->str = Strdup(h->cursor->ev.str); 406 ev->num = h->cursor->ev.num; 407 if (data) 408 *data = h->cursor->data; 409 history_def_delete(h, ev, h->cursor); 410 return (0); 411 } 412 413 414 /* history_def_del(): 415 * Delete element hp of the h list 416 */ 417 /* ARGSUSED */ 418 private int 419 history_def_del(ptr_t p, TYPE(HistEvent) *ev __attribute__((__unused__)), 420 const int num) 421 { 422 history_t *h = (history_t *) p; 423 if (history_def_set(h, ev, num) != 0) 424 return (-1); 425 ev->str = Strdup(h->cursor->ev.str); 426 ev->num = h->cursor->ev.num; 427 history_def_delete(h, ev, h->cursor); 428 return (0); 429 } 430 431 432 /* history_def_delete(): 433 * Delete element hp of the h list 434 */ 435 /* ARGSUSED */ 436 private void 437 history_def_delete(history_t *h, 438 TYPE(HistEvent) *ev __attribute__((__unused__)), hentry_t *hp) 439 { 440 HistEventPrivate *evp = (void *)&hp->ev; 441 if (hp == &h->list) 442 abort(); 443 if (h->cursor == hp) { 444 h->cursor = hp->prev; 445 if (h->cursor == &h->list) 446 h->cursor = hp->next; 447 } 448 hp->prev->next = hp->next; 449 hp->next->prev = hp->prev; 450 h_free((ptr_t) evp->str); 451 h_free(hp); 452 h->cur--; 453 } 454 455 456 /* history_def_insert(): 457 * Insert element with string str in the h list 458 */ 459 private int 460 history_def_insert(history_t *h, TYPE(HistEvent) *ev, const Char *str) 461 { 462 463 h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t)); 464 if (h->cursor == NULL) 465 goto oomem; 466 if ((h->cursor->ev.str = h_strdup(str)) == NULL) { 467 h_free((ptr_t)h->cursor); 468 goto oomem; 469 } 470 h->cursor->data = NULL; 471 h->cursor->ev.num = ++h->eventid; 472 h->cursor->next = h->list.next; 473 h->cursor->prev = &h->list; 474 h->list.next->prev = h->cursor; 475 h->list.next = h->cursor; 476 h->cur++; 477 478 *ev = h->cursor->ev; 479 return (0); 480 oomem: 481 he_seterrev(ev, _HE_MALLOC_FAILED); 482 return (-1); 483 } 484 485 486 /* history_def_enter(): 487 * Default function to enter an item in the history 488 */ 489 private int 490 history_def_enter(ptr_t p, TYPE(HistEvent) *ev, const Char *str) 491 { 492 history_t *h = (history_t *) p; 493 494 if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list && 495 Strcmp(h->list.next->ev.str, str) == 0) 496 return (0); 497 498 if (history_def_insert(h, ev, str) == -1) 499 return (-1); /* error, keep error message */ 500 501 /* 502 * Always keep at least one entry. 503 * This way we don't have to check for the empty list. 504 */ 505 while (h->cur > h->max && h->cur > 0) 506 history_def_delete(h, ev, h->list.prev); 507 508 return (1); 509 } 510 511 512 /* history_def_init(): 513 * Default history initialization function 514 */ 515 /* ARGSUSED */ 516 private int 517 history_def_init(ptr_t *p, TYPE(HistEvent) *ev __attribute__((__unused__)), int n) 518 { 519 history_t *h = (history_t *) h_malloc(sizeof(history_t)); 520 if (h == NULL) 521 return -1; 522 523 if (n <= 0) 524 n = 0; 525 h->eventid = 0; 526 h->cur = 0; 527 h->max = n; 528 h->list.next = h->list.prev = &h->list; 529 h->list.ev.str = NULL; 530 h->list.ev.num = 0; 531 h->cursor = &h->list; 532 h->flags = 0; 533 *p = (ptr_t) h; 534 return 0; 535 } 536 537 538 /* history_def_clear(): 539 * Default history cleanup function 540 */ 541 private void 542 history_def_clear(ptr_t p, TYPE(HistEvent) *ev) 543 { 544 history_t *h = (history_t *) p; 545 546 while (h->list.prev != &h->list) 547 history_def_delete(h, ev, h->list.prev); 548 h->eventid = 0; 549 h->cur = 0; 550 } 551 552 553 554 555 /************************************************************************/ 556 557 /* history_init(): 558 * Initialization function. 559 */ 560 public TYPE(History) * 561 FUN(history,init)(void) 562 { 563 TYPE(HistEvent) ev; 564 TYPE(History) *h = (TYPE(History) *) h_malloc(sizeof(TYPE(History))); 565 if (h == NULL) 566 return NULL; 567 568 if (history_def_init(&h->h_ref, &ev, 0) == -1) { 569 h_free((ptr_t)h); 570 return NULL; 571 } 572 h->h_ent = -1; 573 h->h_next = history_def_next; 574 h->h_first = history_def_first; 575 h->h_last = history_def_last; 576 h->h_prev = history_def_prev; 577 h->h_curr = history_def_curr; 578 h->h_set = history_def_set; 579 h->h_clear = history_def_clear; 580 h->h_enter = history_def_enter; 581 h->h_add = history_def_add; 582 h->h_del = history_def_del; 583 584 return (h); 585 } 586 587 588 /* history_end(): 589 * clean up history; 590 */ 591 public void 592 FUN(history,end)(TYPE(History) *h) 593 { 594 TYPE(HistEvent) ev; 595 596 if (h->h_next == history_def_next) 597 history_def_clear(h->h_ref, &ev); 598 h_free(h->h_ref); 599 h_free(h); 600 } 601 602 603 604 /* history_setsize(): 605 * Set history number of events 606 */ 607 private int 608 history_setsize(TYPE(History) *h, TYPE(HistEvent) *ev, int num) 609 { 610 611 if (h->h_next != history_def_next) { 612 he_seterrev(ev, _HE_NOT_ALLOWED); 613 return (-1); 614 } 615 if (num < 0) { 616 he_seterrev(ev, _HE_BAD_PARAM); 617 return (-1); 618 } 619 history_def_setsize(h->h_ref, num); 620 return (0); 621 } 622 623 624 /* history_getsize(): 625 * Get number of events currently in history 626 */ 627 private int 628 history_getsize(TYPE(History) *h, TYPE(HistEvent) *ev) 629 { 630 if (h->h_next != history_def_next) { 631 he_seterrev(ev, _HE_NOT_ALLOWED); 632 return (-1); 633 } 634 ev->num = history_def_getsize(h->h_ref); 635 if (ev->num < -1) { 636 he_seterrev(ev, _HE_SIZE_NEGATIVE); 637 return (-1); 638 } 639 return (0); 640 } 641 642 643 /* history_setunique(): 644 * Set if adjacent equal events should not be entered in history. 645 */ 646 private int 647 history_setunique(TYPE(History) *h, TYPE(HistEvent) *ev, int uni) 648 { 649 650 if (h->h_next != history_def_next) { 651 he_seterrev(ev, _HE_NOT_ALLOWED); 652 return (-1); 653 } 654 history_def_setunique(h->h_ref, uni); 655 return (0); 656 } 657 658 659 /* history_getunique(): 660 * Get if adjacent equal events should not be entered in history. 661 */ 662 private int 663 history_getunique(TYPE(History) *h, TYPE(HistEvent) *ev) 664 { 665 if (h->h_next != history_def_next) { 666 he_seterrev(ev, _HE_NOT_ALLOWED); 667 return (-1); 668 } 669 ev->num = history_def_getunique(h->h_ref); 670 return (0); 671 } 672 673 674 /* history_set_fun(): 675 * Set history functions 676 */ 677 private int 678 history_set_fun(TYPE(History) *h, TYPE(History) *nh) 679 { 680 TYPE(HistEvent) ev; 681 682 if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL || 683 nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL || 684 nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL || 685 nh->h_del == NULL || nh->h_ref == NULL) { 686 if (h->h_next != history_def_next) { 687 history_def_init(&h->h_ref, &ev, 0); 688 h->h_first = history_def_first; 689 h->h_next = history_def_next; 690 h->h_last = history_def_last; 691 h->h_prev = history_def_prev; 692 h->h_curr = history_def_curr; 693 h->h_set = history_def_set; 694 h->h_clear = history_def_clear; 695 h->h_enter = history_def_enter; 696 h->h_add = history_def_add; 697 h->h_del = history_def_del; 698 } 699 return (-1); 700 } 701 if (h->h_next == history_def_next) 702 history_def_clear(h->h_ref, &ev); 703 704 h->h_ent = -1; 705 h->h_first = nh->h_first; 706 h->h_next = nh->h_next; 707 h->h_last = nh->h_last; 708 h->h_prev = nh->h_prev; 709 h->h_curr = nh->h_curr; 710 h->h_set = nh->h_set; 711 h->h_clear = nh->h_clear; 712 h->h_enter = nh->h_enter; 713 h->h_add = nh->h_add; 714 h->h_del = nh->h_del; 715 716 return (0); 717 } 718 719 720 /* history_load(): 721 * TYPE(History) load function 722 */ 723 private int 724 history_load(TYPE(History) *h, const char *fname) 725 { 726 FILE *fp; 727 char *line; 728 size_t sz, max_size; 729 char *ptr; 730 int i = -1; 731 TYPE(HistEvent) ev; 732 #ifdef WIDECHAR 733 static ct_buffer_t conv; 734 #endif 735 736 if ((fp = fopen(fname, "r")) == NULL) 737 return (i); 738 739 if ((line = fgetln(fp, &sz)) == NULL) 740 goto done; 741 742 if (strncmp(line, hist_cookie, sz) != 0) 743 goto done; 744 745 ptr = h_malloc(max_size = 1024); 746 if (ptr == NULL) 747 goto done; 748 for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) { 749 char c = line[sz]; 750 751 if (sz != 0 && line[sz - 1] == '\n') 752 line[--sz] = '\0'; 753 else 754 line[sz] = '\0'; 755 756 if (max_size < sz) { 757 char *nptr; 758 max_size = (sz + 1024) & ~1023; 759 nptr = h_realloc(ptr, max_size); 760 if (nptr == NULL) { 761 i = -1; 762 goto oomem; 763 } 764 ptr = nptr; 765 } 766 (void) strunvis(ptr, line); 767 line[sz] = c; 768 if (HENTER(h, &ev, ct_decode_string(ptr, &conv)) == -1) { 769 i = -1; 770 goto oomem; 771 } 772 } 773 oomem: 774 h_free((ptr_t)ptr); 775 done: 776 (void) fclose(fp); 777 return (i); 778 } 779 780 781 /* history_save(): 782 * TYPE(History) save function 783 */ 784 private int 785 history_save(TYPE(History) *h, const char *fname) 786 { 787 FILE *fp; 788 TYPE(HistEvent) ev; 789 int i = -1, retval; 790 size_t len, max_size; 791 char *ptr; 792 #ifdef WIDECHAR 793 static ct_buffer_t conv; 794 #endif 795 796 if ((fp = fopen(fname, "w")) == NULL) 797 return (-1); 798 799 if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) 800 goto done; 801 if (fputs(hist_cookie, fp) == EOF) 802 goto done; 803 ptr = h_malloc(max_size = 1024); 804 if (ptr == NULL) 805 goto done; 806 for (i = 0, retval = HLAST(h, &ev); 807 retval != -1; 808 retval = HPREV(h, &ev), i++) { 809 len = Strlen(ev.str) * 4; 810 if (len >= max_size) { 811 char *nptr; 812 max_size = (len + 1024) & ~1023; 813 nptr = h_realloc(ptr, max_size); 814 if (nptr == NULL) { 815 i = -1; 816 goto oomem; 817 } 818 ptr = nptr; 819 } 820 (void) strnvis(ptr, ct_encode_string(ev.str, &conv), max_size, 821 VIS_WHITE); 822 (void) fprintf(fp, "%s\n", ptr); 823 } 824 oomem: 825 h_free((ptr_t)ptr); 826 done: 827 (void) fclose(fp); 828 return (i); 829 } 830 831 832 /* history_prev_event(): 833 * Find the previous event, with number given 834 */ 835 private int 836 history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num) 837 { 838 int retval; 839 840 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) 841 if (ev->num == num) 842 return (0); 843 844 he_seterrev(ev, _HE_NOT_FOUND); 845 return (-1); 846 } 847 848 849 private int 850 history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d) 851 { 852 int retval; 853 854 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) 855 if (ev->num == num) { 856 if (d) 857 *d = ((history_t *)h->h_ref)->cursor->data; 858 return (0); 859 } 860 861 he_seterrev(ev, _HE_NOT_FOUND); 862 return (-1); 863 } 864 865 866 /* history_next_event(): 867 * Find the next event, with number given 868 */ 869 private int 870 history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num) 871 { 872 int retval; 873 874 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) 875 if (ev->num == num) 876 return (0); 877 878 he_seterrev(ev, _HE_NOT_FOUND); 879 return (-1); 880 } 881 882 883 /* history_prev_string(): 884 * Find the previous event beginning with string 885 */ 886 private int 887 history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str) 888 { 889 size_t len = Strlen(str); 890 int retval; 891 892 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) 893 if (Strncmp(str, ev->str, len) == 0) 894 return (0); 895 896 he_seterrev(ev, _HE_NOT_FOUND); 897 return (-1); 898 } 899 900 901 /* history_next_string(): 902 * Find the next event beginning with string 903 */ 904 private int 905 history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str) 906 { 907 size_t len = Strlen(str); 908 int retval; 909 910 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) 911 if (Strncmp(str, ev->str, len) == 0) 912 return (0); 913 914 he_seterrev(ev, _HE_NOT_FOUND); 915 return (-1); 916 } 917 918 919 /* history(): 920 * User interface to history functions. 921 */ 922 int 923 FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...) 924 { 925 va_list va; 926 const Char *str; 927 int retval; 928 929 va_start(va, fun); 930 931 he_seterrev(ev, _HE_OK); 932 933 switch (fun) { 934 case H_GETSIZE: 935 retval = history_getsize(h, ev); 936 break; 937 938 case H_SETSIZE: 939 retval = history_setsize(h, ev, va_arg(va, int)); 940 break; 941 942 case H_GETUNIQUE: 943 retval = history_getunique(h, ev); 944 break; 945 946 case H_SETUNIQUE: 947 retval = history_setunique(h, ev, va_arg(va, int)); 948 break; 949 950 case H_ADD: 951 str = va_arg(va, const Char *); 952 retval = HADD(h, ev, str); 953 break; 954 955 case H_DEL: 956 retval = HDEL(h, ev, va_arg(va, const int)); 957 break; 958 959 case H_ENTER: 960 str = va_arg(va, const Char *); 961 if ((retval = HENTER(h, ev, str)) != -1) 962 h->h_ent = ev->num; 963 break; 964 965 case H_APPEND: 966 str = va_arg(va, const Char *); 967 if ((retval = HSET(h, ev, h->h_ent)) != -1) 968 retval = HADD(h, ev, str); 969 break; 970 971 case H_FIRST: 972 retval = HFIRST(h, ev); 973 break; 974 975 case H_NEXT: 976 retval = HNEXT(h, ev); 977 break; 978 979 case H_LAST: 980 retval = HLAST(h, ev); 981 break; 982 983 case H_PREV: 984 retval = HPREV(h, ev); 985 break; 986 987 case H_CURR: 988 retval = HCURR(h, ev); 989 break; 990 991 case H_SET: 992 retval = HSET(h, ev, va_arg(va, const int)); 993 break; 994 995 case H_CLEAR: 996 HCLEAR(h, ev); 997 retval = 0; 998 break; 999 1000 case H_LOAD: 1001 retval = history_load(h, va_arg(va, const char *)); 1002 if (retval == -1) 1003 he_seterrev(ev, _HE_HIST_READ); 1004 break; 1005 1006 case H_SAVE: 1007 retval = history_save(h, va_arg(va, const char *)); 1008 if (retval == -1) 1009 he_seterrev(ev, _HE_HIST_WRITE); 1010 break; 1011 1012 case H_PREV_EVENT: 1013 retval = history_prev_event(h, ev, va_arg(va, int)); 1014 break; 1015 1016 case H_NEXT_EVENT: 1017 retval = history_next_event(h, ev, va_arg(va, int)); 1018 break; 1019 1020 case H_PREV_STR: 1021 retval = history_prev_string(h, ev, va_arg(va, const Char *)); 1022 break; 1023 1024 case H_NEXT_STR: 1025 retval = history_next_string(h, ev, va_arg(va, const Char *)); 1026 break; 1027 1028 case H_FUNC: 1029 { 1030 TYPE(History) hf; 1031 1032 hf.h_ref = va_arg(va, ptr_t); 1033 h->h_ent = -1; 1034 hf.h_first = va_arg(va, history_gfun_t); 1035 hf.h_next = va_arg(va, history_gfun_t); 1036 hf.h_last = va_arg(va, history_gfun_t); 1037 hf.h_prev = va_arg(va, history_gfun_t); 1038 hf.h_curr = va_arg(va, history_gfun_t); 1039 hf.h_set = va_arg(va, history_sfun_t); 1040 hf.h_clear = va_arg(va, history_vfun_t); 1041 hf.h_enter = va_arg(va, history_efun_t); 1042 hf.h_add = va_arg(va, history_efun_t); 1043 hf.h_del = va_arg(va, history_sfun_t); 1044 1045 if ((retval = history_set_fun(h, &hf)) == -1) 1046 he_seterrev(ev, _HE_PARAM_MISSING); 1047 break; 1048 } 1049 1050 case H_END: 1051 FUN(history,end)(h); 1052 retval = 0; 1053 break; 1054 1055 case H_NEXT_EVDATA: 1056 { 1057 int num = va_arg(va, int); 1058 void **d = va_arg(va, void **); 1059 retval = history_next_evdata(h, ev, num, d); 1060 break; 1061 } 1062 1063 case H_DELDATA: 1064 { 1065 int num = va_arg(va, int); 1066 void **d = va_arg(va, void **); 1067 retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d); 1068 break; 1069 } 1070 1071 case H_REPLACE: /* only use after H_NEXT_EVDATA */ 1072 { 1073 const Char *line = va_arg(va, const Char *); 1074 void *d = va_arg(va, void *); 1075 const Char *s; 1076 if(!line || !(s = Strdup(line))) { 1077 retval = -1; 1078 break; 1079 } 1080 ((history_t *)h->h_ref)->cursor->ev.str = s; 1081 ((history_t *)h->h_ref)->cursor->data = d; 1082 retval = 0; 1083 break; 1084 } 1085 1086 default: 1087 retval = -1; 1088 he_seterrev(ev, _HE_UNKNOWN); 1089 break; 1090 } 1091 va_end(va); 1092 return retval; 1093 } 1094