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