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