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