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