history.c revision 1.2
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) 38static 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 54static const char hist_cookie[] = "_HiStOrY_V1_\n"; 55 56#include "histedit.h" 57 58typedef const HistEvent * (*history_gfun_t) __P((ptr_t)); 59typedef const HistEvent * (*history_efun_t) __P((ptr_t, const char *)); 60typedef void (*history_vfun_t) __P((ptr_t)); 61 62struct 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 87private int history_set_num __P((History *, int)); 88private int history_set_fun __P((History *, History *)); 89private int history_load __P((History *, const char *)); 90private int history_save __P((History *, const char *)); 91private const HistEvent *history_prev_event __P((History *, int)); 92private const HistEvent *history_next_event __P((History *, int)); 93private const HistEvent *history_next_string __P((History *, const char *)); 94private const HistEvent *history_prev_string __P((History *, const char *)); 95 96 97/***********************************************************************/ 98 99/* 100 * Builtin- history implementation 101 */ 102typedef 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 108typedef 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 116private const HistEvent *history_def_first __P((ptr_t)); 117private const HistEvent *history_def_last __P((ptr_t)); 118private const HistEvent *history_def_next __P((ptr_t)); 119private const HistEvent *history_def_prev __P((ptr_t)); 120private const HistEvent *history_def_curr __P((ptr_t)); 121private const HistEvent *history_def_enter __P((ptr_t, const char *)); 122private const HistEvent *history_def_add __P((ptr_t, const char *)); 123private void history_def_init __P((ptr_t *, int)); 124private void history_def_clear __P((ptr_t)); 125private const HistEvent *history_def_insert __P((history_t *, const char *)); 126private 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 */ 134private const HistEvent * 135history_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 */ 149private const HistEvent * 150history_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 */ 164private const HistEvent * 165history_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 */ 185private const HistEvent * 186history_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 */ 206private const HistEvent * 207history_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 */ 221private const HistEvent * 222history_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 */ 245private void 246history_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 */ 263private const HistEvent * 264history_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 */ 283private const HistEvent * 284history_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 */ 308private void 309history_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 */ 330private void 331history_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 */ 350public History * 351history_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 */ 373public void 374history_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 */ 386private int 387history_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 */ 401private int 402history_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 */ 442private int 443history_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 468done: 469 (void) fclose(fp); 470 return i; 471} 472 473 474/* history_save(): 475 * History save function 476 */ 477private int 478history_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 */ 500private const HistEvent * 501history_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 */ 516private const HistEvent * 517history_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 */ 532private const HistEvent * 533history_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 */ 552private const HistEvent * 553history_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 */ 570const HistEvent * 571#if __STDC__ 572history(History *h, int fun, ...) 573#else 574history(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