history.c revision 1.6 1 /* $NetBSD: history.c,v 1.6 1997/07/06 18:25:27 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 #include <sys/cdefs.h>
40 #if !defined(lint) && !defined(SCCSID)
41 #if 0
42 static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93";
43 #else
44 __RCSID("$NetBSD: history.c,v 1.6 1997/07/06 18:25:27 christos Exp $");
45 #endif
46 #endif /* not lint && not SCCSID */
47
48 /*
49 * hist.c: History access functions
50 */
51 #include "sys.h"
52
53 #include <string.h>
54 #include <stdlib.h>
55 #ifdef __STDC__
56 #include <stdarg.h>
57 #else
58 #include <varargs.h>
59 #endif
60
61 static const char hist_cookie[] = "_HiStOrY_V1_\n";
62
63 #include "histedit.h"
64
65 typedef const HistEvent * (*history_gfun_t) __P((ptr_t));
66 typedef const HistEvent * (*history_efun_t) __P((ptr_t, const char *));
67 typedef void (*history_vfun_t) __P((ptr_t));
68
69 struct history {
70 ptr_t h_ref; /* Argument for history fcns */
71 history_gfun_t h_first; /* Get the first element */
72 history_gfun_t h_next; /* Get the next element */
73 history_gfun_t h_last; /* Get the last element */
74 history_gfun_t h_prev; /* Get the previous element */
75 history_gfun_t h_curr; /* Get the current element */
76 history_vfun_t h_clear; /* Clear the history list */
77 history_efun_t h_enter; /* Add an element */
78 history_efun_t h_add; /* Append to an element */
79 };
80
81 #define HNEXT(h) (*(h)->h_next)((h)->h_ref)
82 #define HFIRST(h) (*(h)->h_first)((h)->h_ref)
83 #define HPREV(h) (*(h)->h_prev)((h)->h_ref)
84 #define HLAST(h) (*(h)->h_last)((h)->h_ref)
85 #define HCURR(h) (*(h)->h_curr)((h)->h_ref)
86 #define HCLEAR(h) (*(h)->h_clear)((h)->h_ref)
87 #define HENTER(h, str) (*(h)->h_enter)((h)->h_ref, str)
88 #define HADD(h, str) (*(h)->h_add)((h)->h_ref, str)
89
90 #define h_malloc(a) malloc(a)
91 #define h_free(a) free(a)
92
93
94 private int history_set_num __P((History *, int));
95 private int history_set_fun __P((History *, History *));
96 private int history_load __P((History *, const char *));
97 private int history_save __P((History *, const char *));
98 private const HistEvent *history_prev_event __P((History *, int));
99 private const HistEvent *history_next_event __P((History *, int));
100 private const HistEvent *history_next_string __P((History *, const char *));
101 private const HistEvent *history_prev_string __P((History *, const char *));
102
103
104 /***********************************************************************/
105
106 /*
107 * Builtin- history implementation
108 */
109 typedef struct hentry_t {
110 HistEvent ev; /* What we return */
111 struct hentry_t *next; /* Next entry */
112 struct hentry_t *prev; /* Previous entry */
113 } hentry_t;
114
115 typedef struct history_t {
116 hentry_t list; /* Fake list header element */
117 hentry_t *cursor; /* Current element in the list */
118 int max; /* Maximum number of events */
119 int cur; /* Current number of events */
120 int eventno; /* Current event number */
121 } history_t;
122
123 private const HistEvent *history_def_first __P((ptr_t));
124 private const HistEvent *history_def_last __P((ptr_t));
125 private const HistEvent *history_def_next __P((ptr_t));
126 private const HistEvent *history_def_prev __P((ptr_t));
127 private const HistEvent *history_def_curr __P((ptr_t));
128 private const HistEvent *history_def_enter __P((ptr_t, const char *));
129 private const HistEvent *history_def_add __P((ptr_t, const char *));
130 private void history_def_init __P((ptr_t *, int));
131 private void history_def_clear __P((ptr_t));
132 private const HistEvent *history_def_insert __P((history_t *, const char *));
133 private void history_def_delete __P((history_t *, hentry_t *));
134
135 #define history_def_set(p, num) (void) (((history_t *) p)->max = (num))
136
137
138 /* history_def_first():
139 * Default function to return the first event in the history.
140 */
141 private const HistEvent *
142 history_def_first(p)
143 ptr_t p;
144 {
145 history_t *h = (history_t *) p;
146 h->cursor = h->list.next;
147 if (h->cursor != &h->list)
148 return &h->cursor->ev;
149 else
150 return NULL;
151 }
152
153 /* history_def_last():
154 * Default function to return the last event in the history.
155 */
156 private const HistEvent *
157 history_def_last(p)
158 ptr_t p;
159 {
160 history_t *h = (history_t *) p;
161 h->cursor = h->list.prev;
162 if (h->cursor != &h->list)
163 return &h->cursor->ev;
164 else
165 return NULL;
166 }
167
168 /* history_def_next():
169 * Default function to return the next event in the history.
170 */
171 private const HistEvent *
172 history_def_next(p)
173 ptr_t p;
174 {
175 history_t *h = (history_t *) p;
176
177 if (h->cursor != &h->list)
178 h->cursor = h->cursor->next;
179 else
180 return NULL;
181
182 if (h->cursor != &h->list)
183 return &h->cursor->ev;
184 else
185 return NULL;
186 }
187
188
189 /* history_def_prev():
190 * Default function to return the previous event in the history.
191 */
192 private const HistEvent *
193 history_def_prev(p)
194 ptr_t p;
195 {
196 history_t *h = (history_t *) p;
197
198 if (h->cursor != &h->list)
199 h->cursor = h->cursor->prev;
200 else
201 return NULL;
202
203 if (h->cursor != &h->list)
204 return &h->cursor->ev;
205 else
206 return NULL;
207 }
208
209
210 /* history_def_curr():
211 * Default function to return the current event in the history.
212 */
213 private const HistEvent *
214 history_def_curr(p)
215 ptr_t p;
216 {
217 history_t *h = (history_t *) p;
218
219 if (h->cursor != &h->list)
220 return &h->cursor->ev;
221 else
222 return NULL;
223 }
224
225 /* history_def_add():
226 * Append string to element
227 */
228 private const HistEvent *
229 history_def_add(p, str)
230 ptr_t p;
231 const char *str;
232 {
233 history_t *h = (history_t *) p;
234 size_t len;
235 char *s;
236
237 if (h->cursor == &h->list)
238 return (history_def_enter(p, str));
239 len = strlen(h->cursor->ev.str) + strlen(str) + 1;
240 s = (char *) h_malloc(len);
241 (void)strcpy(s, h->cursor->ev.str); /* XXX strcpy is safe */
242 (void)strcat(s, str); /* XXX strcat is safe */
243 h_free((ptr_t) h->cursor->ev.str);
244 h->cursor->ev.str = s;
245 return &h->cursor->ev;
246 }
247
248
249 /* history_def_delete():
250 * Delete element hp of the h list
251 */
252 private void
253 history_def_delete(h, hp)
254 history_t *h;
255 hentry_t *hp;
256 {
257 if (hp == &h->list)
258 abort();
259 hp->prev->next = hp->next;
260 hp->next->prev = hp->prev;
261 h_free((ptr_t) hp->ev.str);
262 h_free(hp);
263 h->cur--;
264 }
265
266
267 /* history_def_insert():
268 * Insert element with string str in the h list
269 */
270 private const HistEvent *
271 history_def_insert(h, str)
272 history_t *h;
273 const char *str;
274 {
275 h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
276 h->cursor->ev.str = strdup(str);
277 h->cursor->next = h->list.next;
278 h->cursor->prev = &h->list;
279 h->list.next->prev = h->cursor;
280 h->list.next = h->cursor;
281 h->cur++;
282
283 return &h->cursor->ev;
284 }
285
286
287 /* history_def_enter():
288 * Default function to enter an item in the history
289 */
290 private const HistEvent *
291 history_def_enter(p, str)
292 ptr_t p;
293 const char *str;
294 {
295 history_t *h = (history_t *) p;
296 const HistEvent *ev;
297
298
299 ev = history_def_insert(h, str);
300 ((HistEvent*) ev)->num = ++h->eventno;
301
302 /*
303 * Always keep at least one entry.
304 * This way we don't have to check for the empty list.
305 */
306 while (h->cur > h->max + 1)
307 history_def_delete(h, h->list.prev);
308 return ev;
309 }
310
311
312 /* history_def_init():
313 * Default history initialization function
314 */
315 private void
316 history_def_init(p, n)
317 ptr_t *p;
318 int n;
319 {
320 history_t *h = (history_t *) h_malloc(sizeof(history_t));
321 if (n <= 0)
322 n = 0;
323 h->eventno = 0;
324 h->cur = 0;
325 h->max = n;
326 h->list.next = h->list.prev = &h->list;
327 h->list.ev.str = NULL;
328 h->list.ev.num = 0;
329 h->cursor = &h->list;
330 *p = (ptr_t) h;
331 }
332
333
334 /* history_def_clear():
335 * Default history cleanup function
336 */
337 private void
338 history_def_clear(p)
339 ptr_t p;
340 {
341 history_t *h = (history_t *) p;
342
343 while (h->list.prev != &h->list)
344 history_def_delete(h, h->list.prev);
345 h->eventno = 0;
346 h->cur = 0;
347 }
348
349
350
351
352 /************************************************************************/
353
354 /* history_init():
355 * Initialization function.
356 */
357 public History *
358 history_init()
359 {
360 History *h = (History *) h_malloc(sizeof(History));
361
362 history_def_init(&h->h_ref, 0);
363
364 h->h_next = history_def_next;
365 h->h_first = history_def_first;
366 h->h_last = history_def_last;
367 h->h_prev = history_def_prev;
368 h->h_curr = history_def_curr;
369 h->h_clear = history_def_clear;
370 h->h_enter = history_def_enter;
371 h->h_add = history_def_add;
372
373 return h;
374 }
375
376
377 /* history_end():
378 * clean up history;
379 */
380 public void
381 history_end(h)
382 History *h;
383 {
384 if (h->h_next == history_def_next)
385 history_def_clear(h->h_ref);
386 }
387
388
389
390 /* history_set_num():
391 * Set history number of events
392 */
393 private int
394 history_set_num(h, num)
395 History *h;
396 int num;
397 {
398 if (h->h_next != history_def_next || num < 0)
399 return -1;
400 history_def_set(h->h_ref, num);
401 return 0;
402 }
403
404
405 /* history_set_fun():
406 * Set history functions
407 */
408 private int
409 history_set_fun(h, nh)
410 History *h, *nh;
411 {
412 if (nh->h_first == NULL || nh->h_next == NULL ||
413 nh->h_last == NULL || nh->h_prev == NULL || nh->h_curr == NULL ||
414 nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
415 nh->h_ref == NULL) {
416 if (h->h_next != history_def_next) {
417 history_def_init(&h->h_ref, 0);
418 h->h_first = history_def_first;
419 h->h_next = history_def_next;
420 h->h_last = history_def_last;
421 h->h_prev = history_def_prev;
422 h->h_curr = history_def_curr;
423 h->h_clear = history_def_clear;
424 h->h_enter = history_def_enter;
425 h->h_add = history_def_add;
426 }
427 return -1;
428 }
429
430 if (h->h_next == history_def_next)
431 history_def_clear(h->h_ref);
432
433 h->h_first = nh->h_first;
434 h->h_next = nh->h_next;
435 h->h_last = nh->h_last;
436 h->h_prev = nh->h_prev;
437 h->h_curr = nh->h_curr;
438 h->h_clear = nh->h_clear;
439 h->h_enter = nh->h_enter;
440 h->h_add = nh->h_add;
441
442 return 0;
443 }
444
445
446 /* history_load():
447 * History load function
448 */
449 private int
450 history_load(h, fname)
451 History *h;
452 const char *fname;
453 {
454 FILE *fp;
455 char *line;
456 size_t sz;
457 int i = -1;
458
459 if ((fp = fopen(fname, "r")) == NULL)
460 return i;
461
462 if ((line = fgetln(fp, &sz)) == NULL)
463 goto done;
464
465 if (strncmp(line, hist_cookie, sz) != 0)
466 goto done;
467
468 for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
469 char c = line[sz];
470 line[sz] = '\0';
471 HENTER(h, line);
472 line[sz] = c;
473 }
474
475 done:
476 (void) fclose(fp);
477 return i;
478 }
479
480
481 /* history_save():
482 * History save function
483 */
484 private int
485 history_save(h, fname)
486 History *h;
487 const char *fname;
488 {
489 FILE *fp;
490 const HistEvent *ev;
491 int i = 0;
492
493 if ((fp = fopen(fname, "w")) == NULL)
494 return -1;
495
496 (void) fputs(hist_cookie, fp);
497 for (ev = HLAST(h); ev != NULL; ev = HPREV(h), i++)
498 (void) fprintf(fp, "%s", ev->str);
499 (void) fclose(fp);
500 return i;
501 }
502
503
504 /* history_prev_event():
505 * Find the previous event, with number given
506 */
507 private const HistEvent *
508 history_prev_event(h, num)
509 History *h;
510 int num;
511 {
512 const HistEvent *ev;
513 for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
514 if (ev->num == num)
515 return ev;
516 return NULL;
517 }
518
519
520 /* history_next_event():
521 * Find the next event, with number given
522 */
523 private const HistEvent *
524 history_next_event(h, num)
525 History *h;
526 int num;
527 {
528 const HistEvent *ev;
529 for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
530 if (ev->num == num)
531 return ev;
532 return NULL;
533 }
534
535
536 /* history_prev_string():
537 * Find the previous event beginning with string
538 */
539 private const HistEvent *
540 history_prev_string(h, str)
541 History *h;
542 const char* str;
543 {
544 const HistEvent *ev;
545 size_t len = strlen(str);
546
547 for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
548 if (strncmp(str, ev->str, len) == 0)
549 return ev;
550 return NULL;
551 }
552
553
554
555
556 /* history_next_string():
557 * Find the next event beginning with string
558 */
559 private const HistEvent *
560 history_next_string(h, str)
561 History *h;
562 const char* str;
563 {
564 const HistEvent *ev;
565 size_t len = strlen(str);
566
567 for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
568 if (strncmp(str, ev->str, len) == 0)
569 return ev;
570 return NULL;
571 }
572
573
574 /* history():
575 * User interface to history functions.
576 */
577 const HistEvent *
578 #ifdef __STDC__
579 history(History *h, int fun, ...)
580 #else
581 history(va_alist)
582 va_dcl
583 #endif
584 {
585 va_list va;
586 const HistEvent *ev = NULL;
587 const char *str;
588 static HistEvent sev = { 0, "" };
589
590 #ifdef __STDC__
591 va_start(va, fun);
592 #else
593 History *h;
594 int fun;
595 va_start(va);
596 h = va_arg(va, History *);
597 fun = va_arg(va, int);
598 #endif
599
600 switch (fun) {
601 case H_ADD:
602 str = va_arg(va, const char *);
603 ev = HADD(h, str);
604 break;
605
606 case H_ENTER:
607 str = va_arg(va, const char *);
608 ev = HENTER(h, str);
609 break;
610
611 case H_FIRST:
612 ev = HFIRST(h);
613 break;
614
615 case H_NEXT:
616 ev = HNEXT(h);
617 break;
618
619 case H_LAST:
620 ev = HLAST(h);
621 break;
622
623 case H_PREV:
624 ev = HPREV(h);
625 break;
626
627 case H_CURR:
628 ev = HCURR(h);
629 break;
630
631 case H_CLEAR:
632 HCLEAR(h);
633 break;
634
635 case H_LOAD:
636 sev.num = history_load(h, va_arg(va, const char *));
637 ev = &sev;
638 break;
639
640 case H_SAVE:
641 sev.num = history_save(h, va_arg(va, const char *));
642 ev = &sev;
643 break;
644
645 case H_PREV_EVENT:
646 ev = history_prev_event(h, va_arg(va, int));
647 break;
648
649 case H_NEXT_EVENT:
650 ev = history_next_event(h, va_arg(va, int));
651 break;
652
653 case H_PREV_STR:
654 ev = history_prev_string(h, va_arg(va, const char*));
655 break;
656
657 case H_NEXT_STR:
658 ev = history_next_string(h, va_arg(va, const char*));
659 break;
660
661 case H_EVENT:
662 if (history_set_num(h, va_arg(va, int)) == 0) {
663 sev.num = -1;
664 ev = &sev;
665 }
666 break;
667
668 case H_FUNC:
669 {
670 History hf;
671 hf.h_ref = va_arg(va, ptr_t);
672 hf.h_first = va_arg(va, history_gfun_t);
673 hf.h_next = va_arg(va, history_gfun_t);
674 hf.h_last = va_arg(va, history_gfun_t);
675 hf.h_prev = va_arg(va, history_gfun_t);
676 hf.h_curr = va_arg(va, history_gfun_t);
677 hf.h_clear = va_arg(va, history_vfun_t);
678 hf.h_enter = va_arg(va, history_efun_t);
679 hf.h_add = va_arg(va, history_efun_t);
680
681 if (history_set_fun(h, &hf) == 0) {
682 sev.num = -1;
683 ev = &sev;
684 }
685 }
686 break;
687
688 case H_END:
689 history_end(h);
690 break;
691
692 default:
693 break;
694 }
695 va_end(va);
696 return ev;
697 }
698