history.c revision 84201
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#include <sys/cdefs.h> 38__FBSDID("$FreeBSD: head/lib/libedit/history.c 84201 2001-09-30 21:21:36Z dillon $"); 39#if !defined(lint) && !defined(SCCSID) 40static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93"; 41#endif /* not lint && not SCCSID */ 42 43/* 44 * hist.c: History access functions 45 */ 46#include "sys.h" 47 48#include <string.h> 49#include <stdlib.h> 50#if __STDC__ 51#include <stdarg.h> 52#else 53#include <varargs.h> 54#endif 55 56static const char hist_cookie[] = "_HiStOrY_V1_\n"; 57 58#include "histedit.h" 59 60typedef const HistEvent * (*history_gfun_t) __P((ptr_t)); 61typedef const HistEvent * (*history_efun_t) __P((ptr_t, const char *)); 62typedef void (*history_vfun_t) __P((ptr_t)); 63 64struct history { 65 ptr_t h_ref; /* Argument for history fcns */ 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_vfun_t h_clear; /* Clear the history list */ 72 history_efun_t h_enter; /* Add an element */ 73 history_efun_t h_add; /* Append to an element */ 74}; 75 76#define HNEXT(h) (*(h)->h_next)((h)->h_ref) 77#define HFIRST(h) (*(h)->h_first)((h)->h_ref) 78#define HPREV(h) (*(h)->h_prev)((h)->h_ref) 79#define HLAST(h) (*(h)->h_last)((h)->h_ref) 80#define HCURR(h) (*(h)->h_curr)((h)->h_ref) 81#define HCLEAR(h) (*(h)->h_clear)((h)->h_ref) 82#define HENTER(h, str) (*(h)->h_enter)((h)->h_ref, str) 83#define HADD(h, str) (*(h)->h_add)((h)->h_ref, str) 84 85#define h_malloc(a) malloc(a) 86#define h_free(a) free(a) 87 88 89private int history_set_num __P((History *, int)); 90private int history_set_fun __P((History *, History *)); 91private int history_load __P((History *, const char *)); 92private int history_save __P((History *, const char *)); 93private const HistEvent *history_prev_event __P((History *, int)); 94private const HistEvent *history_next_event __P((History *, int)); 95private const HistEvent *history_next_string __P((History *, const char *)); 96private const HistEvent *history_prev_string __P((History *, const char *)); 97 98 99/***********************************************************************/ 100 101/* 102 * Builtin- history implementation 103 */ 104typedef struct hentry_t { 105 HistEvent ev; /* What we return */ 106 struct hentry_t *next; /* Next entry */ 107 struct hentry_t *prev; /* Previous entry */ 108} hentry_t; 109 110typedef struct history_t { 111 hentry_t list; /* Fake list header element */ 112 hentry_t *cursor; /* Current element in the list */ 113 int max; /* Maximum number of events */ 114 int cur; /* Current number of events */ 115 int eventno; /* Current event number */ 116} history_t; 117 118private const HistEvent *history_def_first __P((ptr_t)); 119private const HistEvent *history_def_last __P((ptr_t)); 120private const HistEvent *history_def_next __P((ptr_t)); 121private const HistEvent *history_def_prev __P((ptr_t)); 122private const HistEvent *history_def_curr __P((ptr_t)); 123private const HistEvent *history_def_enter __P((ptr_t, const char *)); 124private const HistEvent *history_def_add __P((ptr_t, const char *)); 125private void history_def_init __P((ptr_t *, int)); 126private void history_def_clear __P((ptr_t)); 127private const HistEvent *history_def_insert __P((history_t *, const char *)); 128private void history_def_delete __P((history_t *, hentry_t *)); 129 130#define history_def_set(p, num) (void) (((history_t *) p)->max = (num)) 131 132 133/* history_def_first(): 134 * Default function to return the first event in the history. 135 */ 136private const HistEvent * 137history_def_first(p) 138 ptr_t p; 139{ 140 history_t *h = (history_t *) p; 141 h->cursor = h->list.next; 142 if (h->cursor != &h->list) 143 return &h->cursor->ev; 144 else 145 return NULL; 146} 147 148/* history_def_last(): 149 * Default function to return the last event in the history. 150 */ 151private const HistEvent * 152history_def_last(p) 153 ptr_t p; 154{ 155 history_t *h = (history_t *) p; 156 h->cursor = h->list.prev; 157 if (h->cursor != &h->list) 158 return &h->cursor->ev; 159 else 160 return NULL; 161} 162 163/* history_def_next(): 164 * Default function to return the next event in the history. 165 */ 166private const HistEvent * 167history_def_next(p) 168 ptr_t p; 169{ 170 history_t *h = (history_t *) p; 171 172 if (h->cursor != &h->list) 173 h->cursor = h->cursor->next; 174 else 175 return NULL; 176 177 if (h->cursor != &h->list) 178 return &h->cursor->ev; 179 else 180 return NULL; 181} 182 183 184/* history_def_prev(): 185 * Default function to return the previous event in the history. 186 */ 187private const HistEvent * 188history_def_prev(p) 189 ptr_t p; 190{ 191 history_t *h = (history_t *) p; 192 193 if (h->cursor != &h->list) 194 h->cursor = h->cursor->prev; 195 else 196 return NULL; 197 198 if (h->cursor != &h->list) 199 return &h->cursor->ev; 200 else 201 return NULL; 202} 203 204 205/* history_def_curr(): 206 * Default function to return the current event in the history. 207 */ 208private const HistEvent * 209history_def_curr(p) 210 ptr_t p; 211{ 212 history_t *h = (history_t *) p; 213 214 if (h->cursor != &h->list) 215 return &h->cursor->ev; 216 else 217 return NULL; 218} 219 220 221/* history_def_add(): 222 * Append string to element 223 */ 224private const HistEvent * 225history_def_add(p, str) 226 ptr_t p; 227 const char *str; 228{ 229 history_t *h = (history_t *) p; 230 size_t len; 231 char *s; 232 233 if (h->cursor == &h->list) 234 return (history_def_enter(p, str)); 235 len = strlen(h->cursor->ev.str) + strlen(str) + 1; 236 s = (char *) h_malloc(len); 237 (void)strcpy(s, h->cursor->ev.str); /* XXX strcpy is safe */ 238 (void)strcat(s, str); /* XXX strcat is safe */ 239 h_free((ptr_t) h->cursor->ev.str); 240 h->cursor->ev.str = s; 241 return &h->cursor->ev; 242} 243 244 245/* history_def_delete(): 246 * Delete element hp of the h list 247 */ 248private void 249history_def_delete(h, hp) 250 history_t *h; 251 hentry_t *hp; 252{ 253 if (hp == &h->list) 254 abort(); 255 hp->prev->next = hp->next; 256 hp->next->prev = hp->prev; 257 h_free((ptr_t) hp->ev.str); 258 h_free(hp); 259 h->cur--; 260} 261 262 263/* history_def_insert(): 264 * Insert element with string str in the h list 265 */ 266private const HistEvent * 267history_def_insert(h, str) 268 history_t *h; 269 const char *str; 270{ 271 h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t)); 272 h->cursor->ev.str = strdup(str); 273 h->cursor->next = h->list.next; 274 h->cursor->prev = &h->list; 275 h->list.next->prev = h->cursor; 276 h->list.next = h->cursor; 277 h->cur++; 278 279 return &h->cursor->ev; 280} 281 282 283/* history_def_enter(): 284 * Default function to enter an item in the history 285 */ 286private const HistEvent * 287history_def_enter(p, str) 288 ptr_t p; 289 const char *str; 290{ 291 history_t *h = (history_t *) p; 292 const HistEvent *ev; 293 294 295 ev = history_def_insert(h, str); 296 ((HistEvent*) ev)->num = ++h->eventno; 297 298 /* 299 * Always keep at least one entry. 300 * This way we don't have to check for the empty list. 301 */ 302 while (h->cur > h->max + 1) 303 history_def_delete(h, h->list.prev); 304 return ev; 305} 306 307 308/* history_def_init(): 309 * Default history initialization function 310 */ 311private void 312history_def_init(p, n) 313 ptr_t *p; 314 int n; 315{ 316 history_t *h = (history_t *) h_malloc(sizeof(history_t)); 317 if (n <= 0) 318 n = 0; 319 h->eventno = 0; 320 h->cur = 0; 321 h->max = n; 322 h->list.next = h->list.prev = &h->list; 323 h->list.ev.str = NULL; 324 h->list.ev.num = 0; 325 h->cursor = &h->list; 326 *p = (ptr_t) h; 327} 328 329 330/* history_def_clear(): 331 * Default history cleanup function 332 */ 333private void 334history_def_clear(p) 335 ptr_t p; 336{ 337 history_t *h = (history_t *) p; 338 339 while (h->list.prev != &h->list) 340 history_def_delete(h, h->list.prev); 341 h->eventno = 0; 342 h->cur = 0; 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 return 0; 435} 436 437 438/* history_load(): 439 * History load function 440 */ 441private int 442history_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 467done: 468 (void) fclose(fp); 469 return i; 470} 471 472 473/* history_save(): 474 * History save function 475 */ 476private int 477history_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 */ 499private const HistEvent * 500history_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 */ 515private const HistEvent * 516history_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 */ 531private const HistEvent * 532history_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/* history_next_string(): 547 * Find the next event beginning with string 548 */ 549private const HistEvent * 550history_next_string(h, str) 551 History *h; 552 const char* str; 553{ 554 const HistEvent *ev; 555 size_t len = strlen(str); 556 557 for (ev = HCURR(h); ev != NULL; ev = HPREV(h)) 558 if (strncmp(str, ev->str, len) == 0) 559 return ev; 560 return NULL; 561} 562 563 564/* history(): 565 * User interface to history functions. 566 */ 567const HistEvent * 568#if __STDC__ 569history(History *h, int fun, ...) 570#else 571history(va_alist) 572 va_dcl 573#endif 574{ 575 va_list va; 576 const HistEvent *ev = NULL; 577 const char *str; 578 static HistEvent sev = { 0, "" }; 579 580#if __STDC__ 581 va_start(va, fun); 582#else 583 History *h; 584 int fun; 585 va_start(va); 586 h = va_arg(va, History *); 587 fun = va_arg(va, int); 588#endif 589 590 switch (fun) { 591 case H_ADD: 592 str = va_arg(va, const char *); 593 ev = HADD(h, str); 594 break; 595 596 case H_ENTER: 597 str = va_arg(va, const char *); 598 ev = HENTER(h, str); 599 break; 600 601 case H_FIRST: 602 ev = HFIRST(h); 603 break; 604 605 case H_NEXT: 606 ev = HNEXT(h); 607 break; 608 609 case H_LAST: 610 ev = HLAST(h); 611 break; 612 613 case H_PREV: 614 ev = HPREV(h); 615 break; 616 617 case H_CURR: 618 ev = HCURR(h); 619 break; 620 621 case H_CLEAR: 622 HCLEAR(h); 623 break; 624 625 case H_LOAD: 626 sev.num = history_load(h, va_arg(va, const char *)); 627 ev = &sev; 628 break; 629 630 case H_SAVE: 631 sev.num = history_save(h, va_arg(va, const char *)); 632 ev = &sev; 633 break; 634 635 case H_PREV_EVENT: 636 ev = history_prev_event(h, va_arg(va, int)); 637 break; 638 639 case H_NEXT_EVENT: 640 ev = history_next_event(h, va_arg(va, int)); 641 break; 642 643 case H_PREV_STR: 644 ev = history_prev_string(h, va_arg(va, const char*)); 645 break; 646 647 case H_NEXT_STR: 648 ev = history_next_string(h, va_arg(va, const char*)); 649 break; 650 651 case H_EVENT: 652 if (history_set_num(h, va_arg(va, int)) == 0) { 653 sev.num = -1; 654 ev = &sev; 655 } 656 break; 657 658 case H_FUNC: 659 { 660 History hf; 661 hf.h_ref = va_arg(va, ptr_t); 662 hf.h_first = va_arg(va, history_gfun_t); 663 hf.h_next = va_arg(va, history_gfun_t); 664 hf.h_last = va_arg(va, history_gfun_t); 665 hf.h_prev = va_arg(va, history_gfun_t); 666 hf.h_curr = va_arg(va, history_gfun_t); 667 hf.h_clear = va_arg(va, history_vfun_t); 668 hf.h_enter = va_arg(va, history_efun_t); 669 hf.h_add = va_arg(va, history_efun_t); 670 671 if (history_set_fun(h, &hf) == 0) { 672 sev.num = -1; 673 ev = &sev; 674 } 675 } 676 break; 677 678 case H_END: 679 history_end(h); 680 break; 681 682 default: 683 break; 684 } 685 va_end(va); 686 return ev; 687} 688