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