1 /* $OpenBSD: history.c,v 1.10 2003/06/02 20:18:40 millert Exp $ */ 2 /* $NetBSD: history.c,v 1.5 1997/04/11 17:52:46 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 #if !defined(lint) && !defined(SCCSID) 37 #if 0 38 static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93"; 39 #else 40 static const char rcsid[] = "$OpenBSD: history.c,v 1.10 2003/06/02 20:18:40 millert Exp $"; 41 #endif 42 #endif /* not lint && not SCCSID */ 43 44 /* 45 * hist.c: History access functions 46 */ 47 #include "sys.h" 48 49 #include <string.h> 50 #include <stdlib.h> 51 #include <stdarg.h> 52 53 static const char hist_cookie[] = "_HiStOrY_V1_\n"; 54 55 #include "histedit.h" 56 57 typedef const HistEvent * (*history_gfun_t)(ptr_t); 58 typedef const HistEvent * (*history_efun_t)(ptr_t, const char *); 59 typedef void (*history_vfun_t)(ptr_t); 60 61 struct history { 62 ptr_t h_ref; /* Argument for history fcns */ 63 history_gfun_t h_first; /* Get the first element */ 64 history_gfun_t h_next; /* Get the next element */ 65 history_gfun_t h_last; /* Get the last element */ 66 history_gfun_t h_prev; /* Get the previous element */ 67 history_gfun_t h_curr; /* Get the current element */ 68 history_vfun_t h_clear; /* Clear the history list */ 69 history_efun_t h_enter; /* Add an element */ 70 history_efun_t h_add; /* Append to an element */ 71 }; 72 73 #define HNEXT(h) (*(h)->h_next)((h)->h_ref) 74 #define HFIRST(h) (*(h)->h_first)((h)->h_ref) 75 #define HPREV(h) (*(h)->h_prev)((h)->h_ref) 76 #define HLAST(h) (*(h)->h_last)((h)->h_ref) 77 #define HCURR(h) (*(h)->h_curr)((h)->h_ref) 78 #define HCLEAR(h) (*(h)->h_clear)((h)->h_ref) 79 #define HENTER(h, str) (*(h)->h_enter)((h)->h_ref, str) 80 #define HADD(h, str) (*(h)->h_add)((h)->h_ref, str) 81 82 #define h_malloc(a) malloc(a) 83 #define h_free(a) free(a) 84 85 86 private int history_set_num(History *, int); 87 private int history_set_fun(History *, History *); 88 private int history_load(History *, const char *); 89 private int history_save(History *, const char *); 90 private const HistEvent *history_prev_event(History *, int); 91 private const HistEvent *history_next_event(History *, int); 92 private const HistEvent *history_next_string(History *, const char *); 93 private const HistEvent *history_prev_string(History *, const char *); 94 95 96 /***********************************************************************/ 97 98 /* 99 * Builtin- history implementation 100 */ 101 typedef struct hentry_t { 102 HistEvent ev; /* What we return */ 103 struct hentry_t *next; /* Next entry */ 104 struct hentry_t *prev; /* Previous entry */ 105 } hentry_t; 106 107 typedef struct history_t { 108 hentry_t list; /* Fake list header element */ 109 hentry_t *cursor; /* Current element in the list */ 110 int max; /* Maximum number of events */ 111 int cur; /* Current number of events */ 112 int eventno; /* Current event number */ 113 } history_t; 114 115 private const HistEvent *history_def_first(ptr_t); 116 private const HistEvent *history_def_last(ptr_t); 117 private const HistEvent *history_def_next(ptr_t); 118 private const HistEvent *history_def_prev(ptr_t); 119 private const HistEvent *history_def_curr(ptr_t); 120 private const HistEvent *history_def_enter(ptr_t, const char *); 121 private const HistEvent *history_def_add(ptr_t, const char *); 122 private void history_def_init(ptr_t *, int); 123 private void history_def_clear(ptr_t); 124 private const HistEvent *history_def_insert(history_t *, const char *); 125 private void history_def_delete(history_t *, hentry_t *); 126 127 #define history_def_set(p, num) (void)(((history_t *) p)->max = (num)) 128 129 130 /* history_def_first(): 131 * Default function to return the first event in the history. 132 */ 133 private const HistEvent * 134 history_def_first(p) 135 ptr_t p; 136 { 137 history_t *h = (history_t *) p; 138 h->cursor = h->list.next; 139 if (h->cursor != &h->list) 140 return &h->cursor->ev; 141 else 142 return NULL; 143 } 144 145 /* history_def_last(): 146 * Default function to return the last event in the history. 147 */ 148 private const HistEvent * 149 history_def_last(p) 150 ptr_t p; 151 { 152 history_t *h = (history_t *) p; 153 h->cursor = h->list.prev; 154 if (h->cursor != &h->list) 155 return &h->cursor->ev; 156 else 157 return NULL; 158 } 159 160 /* history_def_next(): 161 * Default function to return the next event in the history. 162 */ 163 private const HistEvent * 164 history_def_next(p) 165 ptr_t p; 166 { 167 history_t *h = (history_t *) p; 168 169 if (h->cursor != &h->list) 170 h->cursor = h->cursor->next; 171 else 172 return NULL; 173 174 if (h->cursor != &h->list) 175 return &h->cursor->ev; 176 else 177 return NULL; 178 } 179 180 181 /* history_def_prev(): 182 * Default function to return the previous event in the history. 183 */ 184 private const HistEvent * 185 history_def_prev(p) 186 ptr_t p; 187 { 188 history_t *h = (history_t *) p; 189 190 if (h->cursor != &h->list) 191 h->cursor = h->cursor->prev; 192 else 193 return NULL; 194 195 if (h->cursor != &h->list) 196 return &h->cursor->ev; 197 else 198 return NULL; 199 } 200 201 202 /* history_def_curr(): 203 * Default function to return the current event in the history. 204 */ 205 private const HistEvent * 206 history_def_curr(p) 207 ptr_t p; 208 { 209 history_t *h = (history_t *) p; 210 211 if (h->cursor != &h->list) 212 return &h->cursor->ev; 213 else 214 return NULL; 215 } 216 217 /* history_def_add(): 218 * Append string to element 219 */ 220 private const HistEvent * 221 history_def_add(p, str) 222 ptr_t p; 223 const char *str; 224 { 225 history_t *h = (history_t *) p; 226 size_t len; 227 char *s; 228 229 if (h->cursor == &h->list) 230 return (history_def_enter(p, str)); 231 len = strlen(h->cursor->ev.str) + strlen(str) + 1; 232 s = (char *) h_malloc(len); 233 (void)strlcpy(s, h->cursor->ev.str, len); 234 (void)strlcat(s, str, len); 235 h_free((ptr_t) h->cursor->ev.str); 236 h->cursor->ev.str = s; 237 return &h->cursor->ev; 238 } 239 240 241 /* history_def_delete(): 242 * Delete element hp of the h list 243 */ 244 private void 245 history_def_delete(h, hp) 246 history_t *h; 247 hentry_t *hp; 248 { 249 if (hp == &h->list) 250 abort(); 251 hp->prev->next = hp->next; 252 hp->next->prev = hp->prev; 253 h_free((ptr_t) hp->ev.str); 254 h_free(hp); 255 h->cur--; 256 } 257 258 259 /* history_def_insert(): 260 * Insert element with string str in the h list 261 */ 262 private const HistEvent * 263 history_def_insert(h, str) 264 history_t *h; 265 const char *str; 266 { 267 h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t)); 268 h->cursor->ev.str = strdup(str); 269 h->cursor->next = h->list.next; 270 h->cursor->prev = &h->list; 271 h->list.next->prev = h->cursor; 272 h->list.next = h->cursor; 273 h->cur++; 274 275 return &h->cursor->ev; 276 } 277 278 279 /* history_def_enter(): 280 * Default function to enter an item in the history 281 */ 282 private const HistEvent * 283 history_def_enter(p, str) 284 ptr_t p; 285 const char *str; 286 { 287 history_t *h = (history_t *) p; 288 const HistEvent *ev; 289 290 291 ev = history_def_insert(h, str); 292 ((HistEvent*) ev)->num = ++h->eventno; 293 294 /* 295 * Always keep at least one entry. 296 * This way we don't have to check for the empty list. 297 */ 298 while (h->cur > h->max + 1) 299 history_def_delete(h, h->list.prev); 300 return ev; 301 } 302 303 304 /* history_def_init(): 305 * Default history initialization function 306 */ 307 private void 308 history_def_init(p, n) 309 ptr_t *p; 310 int n; 311 { 312 history_t *h = (history_t *) h_malloc(sizeof(history_t)); 313 if (n <= 0) 314 n = 0; 315 h->eventno = 0; 316 h->cur = 0; 317 h->max = n; 318 h->list.next = h->list.prev = &h->list; 319 h->list.ev.str = NULL; 320 h->list.ev.num = 0; 321 h->cursor = &h->list; 322 *p = (ptr_t) h; 323 } 324 325 326 /* history_def_clear(): 327 * Default history cleanup function 328 */ 329 private void 330 history_def_clear(p) 331 ptr_t p; 332 { 333 history_t *h = (history_t *) p; 334 335 while (h->list.prev != &h->list) 336 history_def_delete(h, h->list.prev); 337 h->eventno = 0; 338 h->cur = 0; 339 } 340 341 342 343 344 /************************************************************************/ 345 346 /* history_init(): 347 * Initialization function. 348 */ 349 public History * 350 history_init() 351 { 352 History *h = (History *) h_malloc(sizeof(History)); 353 354 history_def_init(&h->h_ref, 0); 355 356 h->h_next = history_def_next; 357 h->h_first = history_def_first; 358 h->h_last = history_def_last; 359 h->h_prev = history_def_prev; 360 h->h_curr = history_def_curr; 361 h->h_clear = history_def_clear; 362 h->h_enter = history_def_enter; 363 h->h_add = history_def_add; 364 365 return h; 366 } 367 368 369 /* history_end(): 370 * clean up history; 371 */ 372 public void 373 history_end(h) 374 History *h; 375 { 376 if (h->h_next == history_def_next) 377 history_def_clear(h->h_ref); 378 } 379 380 381 382 /* history_set_num(): 383 * Set history number of events 384 */ 385 private int 386 history_set_num(h, num) 387 History *h; 388 int num; 389 { 390 if (h->h_next != history_def_next || num < 0) 391 return -1; 392 history_def_set(h->h_ref, num); 393 return 0; 394 } 395 396 397 /* history_set_fun(): 398 * Set history functions 399 */ 400 private int 401 history_set_fun(h, nh) 402 History *h, *nh; 403 { 404 if (nh->h_first == NULL || nh->h_next == NULL || 405 nh->h_last == NULL || nh->h_prev == NULL || nh->h_curr == NULL || 406 nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL || 407 nh->h_ref == NULL) { 408 if (h->h_next != history_def_next) { 409 history_def_init(&h->h_ref, 0); 410 h->h_first = history_def_first; 411 h->h_next = history_def_next; 412 h->h_last = history_def_last; 413 h->h_prev = history_def_prev; 414 h->h_curr = history_def_curr; 415 h->h_clear = history_def_clear; 416 h->h_enter = history_def_enter; 417 h->h_add = history_def_add; 418 } 419 return -1; 420 } 421 422 if (h->h_next == history_def_next) 423 history_def_clear(h->h_ref); 424 425 h->h_first = nh->h_first; 426 h->h_next = nh->h_next; 427 h->h_last = nh->h_last; 428 h->h_prev = nh->h_prev; 429 h->h_curr = nh->h_curr; 430 h->h_clear = nh->h_clear; 431 h->h_enter = nh->h_enter; 432 h->h_add = nh->h_add; 433 434 return 0; 435 } 436 437 438 /* history_load(): 439 * History load function 440 */ 441 private int 442 history_load(h, fname) 443 History *h; 444 const char *fname; 445 { 446 FILE *fp; 447 char *line; 448 size_t sz; 449 int i = -1; 450 451 if ((fp = fopen(fname, "r")) == NULL) 452 return i; 453 454 if ((line = fgetln(fp, &sz)) == NULL) 455 goto done; 456 457 if (strncmp(line, hist_cookie, sz) != 0) 458 goto done; 459 460 for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) { 461 char c = line[sz]; 462 line[sz] = '\0'; 463 HENTER(h, line); 464 line[sz] = c; 465 } 466 467 done: 468 (void)fclose(fp); 469 return i; 470 } 471 472 473 /* history_save(): 474 * History save function 475 */ 476 private int 477 history_save(h, fname) 478 History *h; 479 const char *fname; 480 { 481 FILE *fp; 482 const HistEvent *ev; 483 int i = 0; 484 485 if ((fp = fopen(fname, "w")) == NULL) 486 return -1; 487 488 (void)fputs(hist_cookie, fp); 489 for (ev = HLAST(h); ev != NULL; ev = HPREV(h), i++) 490 (void)fprintf(fp, "%s", ev->str); 491 (void)fclose(fp); 492 return i; 493 } 494 495 496 /* history_prev_event(): 497 * Find the previous event, with number given 498 */ 499 private const HistEvent * 500 history_prev_event(h, num) 501 History *h; 502 int num; 503 { 504 const HistEvent *ev; 505 for (ev = HCURR(h); ev != NULL; ev = HPREV(h)) 506 if (ev->num == num) 507 return ev; 508 return NULL; 509 } 510 511 512 /* history_next_event(): 513 * Find the next event, with number given 514 */ 515 private const HistEvent * 516 history_next_event(h, num) 517 History *h; 518 int num; 519 { 520 const HistEvent *ev; 521 for (ev = HCURR(h); ev != NULL; ev = HNEXT(h)) 522 if (ev->num == num) 523 return ev; 524 return NULL; 525 } 526 527 528 /* history_prev_string(): 529 * Find the previous event beginning with string 530 */ 531 private const HistEvent * 532 history_prev_string(h, str) 533 History *h; 534 const char* str; 535 { 536 const HistEvent *ev; 537 size_t len = strlen(str); 538 539 for (ev = HCURR(h); ev != NULL; ev = HNEXT(h)) 540 if (strncmp(str, ev->str, len) == 0) 541 return ev; 542 return NULL; 543 } 544 545 546 547 548 /* history_next_string(): 549 * Find the next event beginning with string 550 */ 551 private const HistEvent * 552 history_next_string(h, str) 553 History *h; 554 const char* str; 555 { 556 const HistEvent *ev; 557 size_t len = strlen(str); 558 559 for (ev = HCURR(h); ev != NULL; ev = HPREV(h)) 560 if (strncmp(str, ev->str, len) == 0) 561 return ev; 562 return NULL; 563 } 564 565 566 /* history(): 567 * User interface to history functions. 568 */ 569 const HistEvent * 570 history(History *h, int fun, ...) 571 { 572 va_list va; 573 const HistEvent *ev = NULL; 574 const char *str; 575 static HistEvent sev = { 0, "" }; 576 577 va_start(va, fun); 578 579 switch (fun) { 580 case H_ADD: 581 str = va_arg(va, const char *); 582 ev = HADD(h, str); 583 break; 584 585 case H_ENTER: 586 str = va_arg(va, const char *); 587 ev = HENTER(h, str); 588 break; 589 590 case H_FIRST: 591 ev = HFIRST(h); 592 break; 593 594 case H_NEXT: 595 ev = HNEXT(h); 596 break; 597 598 case H_LAST: 599 ev = HLAST(h); 600 break; 601 602 case H_PREV: 603 ev = HPREV(h); 604 break; 605 606 case H_CURR: 607 ev = HCURR(h); 608 break; 609 610 case H_CLEAR: 611 HCLEAR(h); 612 break; 613 614 case H_LOAD: 615 sev.num = history_load(h, va_arg(va, const char *)); 616 ev = &sev; 617 break; 618 619 case H_SAVE: 620 sev.num = history_save(h, va_arg(va, const char *)); 621 ev = &sev; 622 break; 623 624 case H_PREV_EVENT: 625 ev = history_prev_event(h, va_arg(va, int)); 626 break; 627 628 case H_NEXT_EVENT: 629 ev = history_next_event(h, va_arg(va, int)); 630 break; 631 632 case H_PREV_STR: 633 ev = history_prev_string(h, va_arg(va, const char*)); 634 break; 635 636 case H_NEXT_STR: 637 ev = history_next_string(h, va_arg(va, const char*)); 638 break; 639 640 case H_EVENT: 641 if (history_set_num(h, va_arg(va, int)) == 0) { 642 sev.num = -1; 643 ev = &sev; 644 } 645 break; 646 647 case H_FUNC: 648 { 649 History hf; 650 hf.h_ref = va_arg(va, ptr_t); 651 hf.h_first = va_arg(va, history_gfun_t); 652 hf.h_next = va_arg(va, history_gfun_t); 653 hf.h_last = va_arg(va, history_gfun_t); 654 hf.h_prev = va_arg(va, history_gfun_t); 655 hf.h_curr = va_arg(va, history_gfun_t); 656 hf.h_clear = va_arg(va, history_vfun_t); 657 hf.h_enter = va_arg(va, history_efun_t); 658 hf.h_add = va_arg(va, history_efun_t); 659 660 if (history_set_fun(h, &hf) == 0) { 661 sev.num = -1; 662 ev = &sev; 663 } 664 } 665 break; 666 667 case H_END: 668 history_end(h); 669 break; 670 671 default: 672 break; 673 } 674 va_end(va); 675 return ev; 676 } 677