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