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)
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