history.c revision 1.28 1 /* $NetBSD: history.c,v 1.28 2004/11/27 18:31:45 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. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include "config.h"
36 #if !defined(lint) && !defined(SCCSID)
37 #if 0
38 static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93";
39 #else
40 __RCSID("$NetBSD: history.c,v 1.28 2004/11/27 18:31:45 christos Exp $");
41 #endif
42 #endif /* not lint && not SCCSID */
43
44 /*
45 * hist.c: History access functions
46 */
47 #include <string.h>
48 #include <stdlib.h>
49 #include <stdarg.h>
50 #ifdef HAVE_VIS_H
51 #include <vis.h>
52 #else
53 #include "np/vis.h"
54 #endif
55 #include <sys/stat.h>
56
57 static const char hist_cookie[] = "_HiStOrY_V2_\n";
58
59 #include "histedit.h"
60
61 typedef int (*history_gfun_t)(ptr_t, HistEvent *);
62 typedef int (*history_efun_t)(ptr_t, HistEvent *, const char *);
63 typedef void (*history_vfun_t)(ptr_t, HistEvent *);
64 typedef int (*history_sfun_t)(ptr_t, HistEvent *, const int);
65
66 struct history {
67 ptr_t h_ref; /* Argument for history fcns */
68 int h_ent; /* Last entry point for history */
69 history_gfun_t h_first; /* Get the first element */
70 history_gfun_t h_next; /* Get the next element */
71 history_gfun_t h_last; /* Get the last element */
72 history_gfun_t h_prev; /* Get the previous element */
73 history_gfun_t h_curr; /* Get the current element */
74 history_sfun_t h_set; /* Set the current element */
75 history_vfun_t h_clear; /* Clear the history list */
76 history_efun_t h_enter; /* Add an element */
77 history_efun_t h_add; /* Append to an element */
78 };
79
80 #define HNEXT(h, ev) (*(h)->h_next)((h)->h_ref, ev)
81 #define HFIRST(h, ev) (*(h)->h_first)((h)->h_ref, ev)
82 #define HPREV(h, ev) (*(h)->h_prev)((h)->h_ref, ev)
83 #define HLAST(h, ev) (*(h)->h_last)((h)->h_ref, ev)
84 #define HCURR(h, ev) (*(h)->h_curr)((h)->h_ref, ev)
85 #define HSET(h, ev, n) (*(h)->h_set)((h)->h_ref, ev, n)
86 #define HCLEAR(h, ev) (*(h)->h_clear)((h)->h_ref, ev)
87 #define HENTER(h, ev, str) (*(h)->h_enter)((h)->h_ref, ev, str)
88 #define HADD(h, ev, str) (*(h)->h_add)((h)->h_ref, ev, str)
89
90 #define h_strdup(a) strdup(a)
91 #define h_malloc(a) malloc(a)
92 #define h_realloc(a, b) realloc((a), (b))
93 #define h_free(a) free(a)
94
95 typedef struct {
96 int num;
97 char *str;
98 } HistEventPrivate;
99
100
101
102 private int history_setsize(History *, HistEvent *, int);
103 private int history_getsize(History *, HistEvent *);
104 private int history_setunique(History *, HistEvent *, int);
105 private int history_getunique(History *, HistEvent *);
106 private int history_set_fun(History *, History *);
107 private int history_load(History *, const char *);
108 private int history_save(History *, const char *);
109 private int history_prev_event(History *, HistEvent *, int);
110 private int history_next_event(History *, HistEvent *, int);
111 private int history_next_string(History *, HistEvent *, const char *);
112 private int history_prev_string(History *, HistEvent *, const char *);
113
114
115 /***********************************************************************/
116
117 /*
118 * Builtin- history implementation
119 */
120 typedef struct hentry_t {
121 HistEvent ev; /* What we return */
122 struct hentry_t *next; /* Next entry */
123 struct hentry_t *prev; /* Previous entry */
124 } hentry_t;
125
126 typedef struct history_t {
127 hentry_t list; /* Fake list header element */
128 hentry_t *cursor; /* Current element in the list */
129 int max; /* Maximum number of events */
130 int cur; /* Current number of events */
131 int eventid; /* For generation of unique event id */
132 int flags; /* History flags */
133 #define H_UNIQUE 1 /* Store only unique elements */
134 } history_t;
135
136 private int history_def_first(ptr_t, HistEvent *);
137 private int history_def_last(ptr_t, HistEvent *);
138 private int history_def_next(ptr_t, HistEvent *);
139 private int history_def_prev(ptr_t, HistEvent *);
140 private int history_def_curr(ptr_t, HistEvent *);
141 private int history_def_set(ptr_t, HistEvent *, const int n);
142 private int history_def_enter(ptr_t, HistEvent *, const char *);
143 private int history_def_add(ptr_t, HistEvent *, const char *);
144 private int history_def_init(ptr_t *, HistEvent *, int);
145 private void history_def_clear(ptr_t, HistEvent *);
146 private int history_def_insert(history_t *, HistEvent *, const char *);
147 private void history_def_delete(history_t *, HistEvent *, hentry_t *);
148
149 #define history_def_setsize(p, num)(void) (((history_t *)p)->max = (num))
150 #define history_def_getsize(p) (((history_t *)p)->cur)
151 #define history_def_getunique(p) (((((history_t *)p)->flags) & H_UNIQUE) != 0)
152 #define history_def_setunique(p, uni) \
153 if (uni) \
154 (((history_t *)p)->flags) |= H_UNIQUE; \
155 else \
156 (((history_t *)p)->flags) &= ~H_UNIQUE
157
158 #define he_strerror(code) he_errlist[code]
159 #define he_seterrev(evp, code) {\
160 evp->num = code;\
161 evp->str = he_strerror(code);\
162 }
163
164 /* error messages */
165 static const char *const he_errlist[] = {
166 "OK",
167 "unknown error",
168 "malloc() failed",
169 "first event not found",
170 "last event not found",
171 "empty list",
172 "no next event",
173 "no previous event",
174 "current event is invalid",
175 "event not found",
176 "can't read history from file",
177 "can't write history",
178 "required parameter(s) not supplied",
179 "history size negative",
180 "function not allowed with other history-functions-set the default",
181 "bad parameters"
182 };
183 /* error codes */
184 #define _HE_OK 0
185 #define _HE_UNKNOWN 1
186 #define _HE_MALLOC_FAILED 2
187 #define _HE_FIRST_NOTFOUND 3
188 #define _HE_LAST_NOTFOUND 4
189 #define _HE_EMPTY_LIST 5
190 #define _HE_END_REACHED 6
191 #define _HE_START_REACHED 7
192 #define _HE_CURR_INVALID 8
193 #define _HE_NOT_FOUND 9
194 #define _HE_HIST_READ 10
195 #define _HE_HIST_WRITE 11
196 #define _HE_PARAM_MISSING 12
197 #define _HE_SIZE_NEGATIVE 13
198 #define _HE_NOT_ALLOWED 14
199 #define _HE_BAD_PARAM 15
200
201 /* history_def_first():
202 * Default function to return the first event in the history.
203 */
204 private int
205 history_def_first(ptr_t p, HistEvent *ev)
206 {
207 history_t *h = (history_t *) p;
208
209 h->cursor = h->list.next;
210 if (h->cursor != &h->list)
211 *ev = h->cursor->ev;
212 else {
213 he_seterrev(ev, _HE_FIRST_NOTFOUND);
214 return (-1);
215 }
216
217 return (0);
218 }
219
220
221 /* history_def_last():
222 * Default function to return the last event in the history.
223 */
224 private int
225 history_def_last(ptr_t p, HistEvent *ev)
226 {
227 history_t *h = (history_t *) p;
228
229 h->cursor = h->list.prev;
230 if (h->cursor != &h->list)
231 *ev = h->cursor->ev;
232 else {
233 he_seterrev(ev, _HE_LAST_NOTFOUND);
234 return (-1);
235 }
236
237 return (0);
238 }
239
240
241 /* history_def_next():
242 * Default function to return the next event in the history.
243 */
244 private int
245 history_def_next(ptr_t p, HistEvent *ev)
246 {
247 history_t *h = (history_t *) p;
248
249 if (h->cursor == &h->list) {
250 he_seterrev(ev, _HE_EMPTY_LIST);
251 return (-1);
252 }
253
254 if (h->cursor->next == &h->list) {
255 he_seterrev(ev, _HE_END_REACHED);
256 return (-1);
257 }
258
259 h->cursor = h->cursor->next;
260 *ev = h->cursor->ev;
261
262 return (0);
263 }
264
265
266 /* history_def_prev():
267 * Default function to return the previous event in the history.
268 */
269 private int
270 history_def_prev(ptr_t p, HistEvent *ev)
271 {
272 history_t *h = (history_t *) p;
273
274 if (h->cursor == &h->list) {
275 he_seterrev(ev,
276 (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
277 return (-1);
278 }
279
280 if (h->cursor->prev == &h->list) {
281 he_seterrev(ev, _HE_START_REACHED);
282 return (-1);
283 }
284
285 h->cursor = h->cursor->prev;
286 *ev = h->cursor->ev;
287
288 return (0);
289 }
290
291
292 /* history_def_curr():
293 * Default function to return the current event in the history.
294 */
295 private int
296 history_def_curr(ptr_t p, HistEvent *ev)
297 {
298 history_t *h = (history_t *) p;
299
300 if (h->cursor != &h->list)
301 *ev = h->cursor->ev;
302 else {
303 he_seterrev(ev,
304 (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
305 return (-1);
306 }
307
308 return (0);
309 }
310
311
312 /* history_def_set():
313 * Default function to set the current event in the history to the
314 * given one.
315 */
316 private int
317 history_def_set(ptr_t p, HistEvent *ev, const int n)
318 {
319 history_t *h = (history_t *) p;
320
321 if (h->cur == 0) {
322 he_seterrev(ev, _HE_EMPTY_LIST);
323 return (-1);
324 }
325 if (h->cursor == &h->list || h->cursor->ev.num != n) {
326 for (h->cursor = h->list.next; h->cursor != &h->list;
327 h->cursor = h->cursor->next)
328 if (h->cursor->ev.num == n)
329 break;
330 }
331 if (h->cursor == &h->list) {
332 he_seterrev(ev, _HE_NOT_FOUND);
333 return (-1);
334 }
335 return (0);
336 }
337
338
339 /* history_def_add():
340 * Append string to element
341 */
342 private int
343 history_def_add(ptr_t p, HistEvent *ev, const char *str)
344 {
345 history_t *h = (history_t *) p;
346 size_t len;
347 char *s;
348 HistEventPrivate *evp = (void *)&h->cursor->ev;
349
350 if (h->cursor == &h->list)
351 return (history_def_enter(p, ev, str));
352 len = strlen(evp->str) + strlen(str) + 1;
353 s = (char *) h_malloc(len);
354 if (s == NULL) {
355 he_seterrev(ev, _HE_MALLOC_FAILED);
356 return (-1);
357 }
358 (void) strlcpy(s, h->cursor->ev.str, len);
359 (void) strlcat(s, str, len);
360 h_free((ptr_t)evp->str);
361 evp->str = s;
362 *ev = h->cursor->ev;
363 return (0);
364 }
365
366
367 /* history_def_delete():
368 * Delete element hp of the h list
369 */
370 /* ARGSUSED */
371 private void
372 history_def_delete(history_t *h,
373 HistEvent *ev __attribute__((__unused__)), hentry_t *hp)
374 {
375 HistEventPrivate *evp = (void *)&hp->ev;
376 if (hp == &h->list)
377 abort();
378 hp->prev->next = hp->next;
379 hp->next->prev = hp->prev;
380 h_free((ptr_t) evp->str);
381 h_free(hp);
382 h->cur--;
383 }
384
385
386 /* history_def_insert():
387 * Insert element with string str in the h list
388 */
389 private int
390 history_def_insert(history_t *h, HistEvent *ev, const char *str)
391 {
392
393 h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
394 if (h->cursor == NULL)
395 goto oomem;
396 if ((h->cursor->ev.str = h_strdup(str)) == NULL) {
397 h_free((ptr_t)h->cursor);
398 goto oomem;
399 }
400 h->cursor->ev.num = ++h->eventid;
401 h->cursor->next = h->list.next;
402 h->cursor->prev = &h->list;
403 h->list.next->prev = h->cursor;
404 h->list.next = h->cursor;
405 h->cur++;
406
407 *ev = h->cursor->ev;
408 return (0);
409 oomem:
410 he_seterrev(ev, _HE_MALLOC_FAILED);
411 return (-1);
412 }
413
414
415 /* history_def_enter():
416 * Default function to enter an item in the history
417 */
418 private int
419 history_def_enter(ptr_t p, HistEvent *ev, const char *str)
420 {
421 history_t *h = (history_t *) p;
422
423 if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
424 strcmp(h->list.next->ev.str, str) == 0)
425 return (0);
426
427 if (history_def_insert(h, ev, str) == -1)
428 return (-1); /* error, keep error message */
429
430 /*
431 * Always keep at least one entry.
432 * This way we don't have to check for the empty list.
433 */
434 while (h->cur > h->max && h->cur > 0)
435 history_def_delete(h, ev, h->list.prev);
436
437 return (1);
438 }
439
440
441 /* history_def_init():
442 * Default history initialization function
443 */
444 /* ARGSUSED */
445 private int
446 history_def_init(ptr_t *p, HistEvent *ev __attribute__((__unused__)), int n)
447 {
448 history_t *h = (history_t *) h_malloc(sizeof(history_t));
449 if (h == NULL)
450 return -1;
451
452 if (n <= 0)
453 n = 0;
454 h->eventid = 0;
455 h->cur = 0;
456 h->max = n;
457 h->list.next = h->list.prev = &h->list;
458 h->list.ev.str = NULL;
459 h->list.ev.num = 0;
460 h->cursor = &h->list;
461 h->flags = 0;
462 *p = (ptr_t) h;
463 return 0;
464 }
465
466
467 /* history_def_clear():
468 * Default history cleanup function
469 */
470 private void
471 history_def_clear(ptr_t p, HistEvent *ev)
472 {
473 history_t *h = (history_t *) p;
474
475 while (h->list.prev != &h->list)
476 history_def_delete(h, ev, h->list.prev);
477 h->eventid = 0;
478 h->cur = 0;
479 }
480
481
482
483
484 /************************************************************************/
485
486 /* history_init():
487 * Initialization function.
488 */
489 public History *
490 history_init(void)
491 {
492 HistEvent ev;
493 History *h = (History *) h_malloc(sizeof(History));
494 if (h == NULL)
495 return NULL;
496
497 if (history_def_init(&h->h_ref, &ev, 0) == -1) {
498 h_free((ptr_t)h);
499 return NULL;
500 }
501 h->h_ent = -1;
502 h->h_next = history_def_next;
503 h->h_first = history_def_first;
504 h->h_last = history_def_last;
505 h->h_prev = history_def_prev;
506 h->h_curr = history_def_curr;
507 h->h_set = history_def_set;
508 h->h_clear = history_def_clear;
509 h->h_enter = history_def_enter;
510 h->h_add = history_def_add;
511
512 return (h);
513 }
514
515
516 /* history_end():
517 * clean up history;
518 */
519 public void
520 history_end(History *h)
521 {
522 HistEvent ev;
523
524 if (h->h_next == history_def_next)
525 history_def_clear(h->h_ref, &ev);
526 }
527
528
529
530 /* history_setsize():
531 * Set history number of events
532 */
533 private int
534 history_setsize(History *h, HistEvent *ev, int num)
535 {
536
537 if (h->h_next != history_def_next) {
538 he_seterrev(ev, _HE_NOT_ALLOWED);
539 return (-1);
540 }
541 if (num < 0) {
542 he_seterrev(ev, _HE_BAD_PARAM);
543 return (-1);
544 }
545 history_def_setsize(h->h_ref, num);
546 return (0);
547 }
548
549
550 /* history_getsize():
551 * Get number of events currently in history
552 */
553 private int
554 history_getsize(History *h, HistEvent *ev)
555 {
556 if (h->h_next != history_def_next) {
557 he_seterrev(ev, _HE_NOT_ALLOWED);
558 return (-1);
559 }
560 ev->num = history_def_getsize(h->h_ref);
561 if (ev->num < -1) {
562 he_seterrev(ev, _HE_SIZE_NEGATIVE);
563 return (-1);
564 }
565 return (0);
566 }
567
568
569 /* history_setunique():
570 * Set if adjacent equal events should not be entered in history.
571 */
572 private int
573 history_setunique(History *h, HistEvent *ev, int uni)
574 {
575
576 if (h->h_next != history_def_next) {
577 he_seterrev(ev, _HE_NOT_ALLOWED);
578 return (-1);
579 }
580 history_def_setunique(h->h_ref, uni);
581 return (0);
582 }
583
584
585 /* history_getunique():
586 * Get if adjacent equal events should not be entered in history.
587 */
588 private int
589 history_getunique(History *h, HistEvent *ev)
590 {
591 if (h->h_next != history_def_next) {
592 he_seterrev(ev, _HE_NOT_ALLOWED);
593 return (-1);
594 }
595 ev->num = history_def_getunique(h->h_ref);
596 return (0);
597 }
598
599
600 /* history_set_fun():
601 * Set history functions
602 */
603 private int
604 history_set_fun(History *h, History *nh)
605 {
606 HistEvent ev;
607
608 if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
609 nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
610 nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
611 nh->h_ref == NULL) {
612 if (h->h_next != history_def_next) {
613 history_def_init(&h->h_ref, &ev, 0);
614 h->h_first = history_def_first;
615 h->h_next = history_def_next;
616 h->h_last = history_def_last;
617 h->h_prev = history_def_prev;
618 h->h_curr = history_def_curr;
619 h->h_set = history_def_set;
620 h->h_clear = history_def_clear;
621 h->h_enter = history_def_enter;
622 h->h_add = history_def_add;
623 }
624 return (-1);
625 }
626 if (h->h_next == history_def_next)
627 history_def_clear(h->h_ref, &ev);
628
629 h->h_ent = -1;
630 h->h_first = nh->h_first;
631 h->h_next = nh->h_next;
632 h->h_last = nh->h_last;
633 h->h_prev = nh->h_prev;
634 h->h_curr = nh->h_curr;
635 h->h_set = nh->h_set;
636 h->h_clear = nh->h_clear;
637 h->h_enter = nh->h_enter;
638 h->h_add = nh->h_add;
639
640 return (0);
641 }
642
643
644 /* history_load():
645 * History load function
646 */
647 private int
648 history_load(History *h, const char *fname)
649 {
650 FILE *fp;
651 char *line;
652 size_t sz, max_size;
653 char *ptr;
654 int i = -1;
655 HistEvent ev;
656
657 if ((fp = fopen(fname, "r")) == NULL)
658 return (i);
659
660 if ((line = fgetln(fp, &sz)) == NULL)
661 goto done;
662
663 if (strncmp(line, hist_cookie, sz) != 0)
664 goto done;
665
666 ptr = h_malloc(max_size = 1024);
667 if (ptr == NULL)
668 goto done;
669 for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
670 char c = line[sz];
671
672 if (sz != 0 && line[sz - 1] == '\n')
673 line[--sz] = '\0';
674 else
675 line[sz] = '\0';
676
677 if (max_size < sz) {
678 char *nptr;
679 max_size = (sz + 1024) & ~1023;
680 nptr = h_realloc(ptr, max_size);
681 if (nptr == NULL) {
682 i = -1;
683 goto oomem;
684 }
685 ptr = nptr;
686 }
687 (void) strunvis(ptr, line);
688 line[sz] = c;
689 if (HENTER(h, &ev, ptr) == -1) {
690 h_free((ptr_t)ptr);
691 return -1;
692 }
693 }
694 oomem:
695 h_free((ptr_t)ptr);
696 done:
697 (void) fclose(fp);
698 return (i);
699 }
700
701
702 /* history_save():
703 * History save function
704 */
705 private int
706 history_save(History *h, const char *fname)
707 {
708 FILE *fp;
709 HistEvent ev;
710 int i = -1, retval;
711 size_t len, max_size;
712 char *ptr;
713
714 if ((fp = fopen(fname, "w")) == NULL)
715 return (-1);
716
717 if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
718 goto done;
719 if (fputs(hist_cookie, fp) == EOF)
720 goto done;
721 ptr = h_malloc(max_size = 1024);
722 if (ptr == NULL)
723 goto done;
724 for (i = 0, retval = HLAST(h, &ev);
725 retval != -1;
726 retval = HPREV(h, &ev), i++) {
727 len = strlen(ev.str) * 4;
728 if (len >= max_size) {
729 char *nptr;
730 max_size = (len + 1024) & ~1023;
731 nptr = h_realloc(ptr, max_size);
732 if (nptr == NULL) {
733 i = -1;
734 goto oomem;
735 }
736 ptr = nptr;
737 }
738 (void) strvis(ptr, ev.str, VIS_WHITE);
739 (void) fprintf(fp, "%s\n", ptr);
740 }
741 oomem:
742 h_free((ptr_t)ptr);
743 done:
744 (void) fclose(fp);
745 return (i);
746 }
747
748
749 /* history_prev_event():
750 * Find the previous event, with number given
751 */
752 private int
753 history_prev_event(History *h, HistEvent *ev, int num)
754 {
755 int retval;
756
757 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
758 if (ev->num == num)
759 return (0);
760
761 he_seterrev(ev, _HE_NOT_FOUND);
762 return (-1);
763 }
764
765
766 /* history_next_event():
767 * Find the next event, with number given
768 */
769 private int
770 history_next_event(History *h, HistEvent *ev, int num)
771 {
772 int retval;
773
774 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
775 if (ev->num == num)
776 return (0);
777
778 he_seterrev(ev, _HE_NOT_FOUND);
779 return (-1);
780 }
781
782
783 /* history_prev_string():
784 * Find the previous event beginning with string
785 */
786 private int
787 history_prev_string(History *h, HistEvent *ev, const char *str)
788 {
789 size_t len = strlen(str);
790 int retval;
791
792 for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
793 if (strncmp(str, ev->str, len) == 0)
794 return (0);
795
796 he_seterrev(ev, _HE_NOT_FOUND);
797 return (-1);
798 }
799
800
801 /* history_next_string():
802 * Find the next event beginning with string
803 */
804 private int
805 history_next_string(History *h, HistEvent *ev, const char *str)
806 {
807 size_t len = strlen(str);
808 int retval;
809
810 for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
811 if (strncmp(str, ev->str, len) == 0)
812 return (0);
813
814 he_seterrev(ev, _HE_NOT_FOUND);
815 return (-1);
816 }
817
818
819 /* history():
820 * User interface to history functions.
821 */
822 int
823 history(History *h, HistEvent *ev, int fun, ...)
824 {
825 va_list va;
826 const char *str;
827 int retval;
828
829 va_start(va, fun);
830
831 he_seterrev(ev, _HE_OK);
832
833 switch (fun) {
834 case H_GETSIZE:
835 retval = history_getsize(h, ev);
836 break;
837
838 case H_SETSIZE:
839 retval = history_setsize(h, ev, va_arg(va, int));
840 break;
841
842 case H_GETUNIQUE:
843 retval = history_getunique(h, ev);
844 break;
845
846 case H_SETUNIQUE:
847 retval = history_setunique(h, ev, va_arg(va, int));
848 break;
849
850 case H_ADD:
851 str = va_arg(va, const char *);
852 retval = HADD(h, ev, str);
853 break;
854
855 case H_ENTER:
856 str = va_arg(va, const char *);
857 if ((retval = HENTER(h, ev, str)) != -1)
858 h->h_ent = ev->num;
859 break;
860
861 case H_APPEND:
862 str = va_arg(va, const char *);
863 if ((retval = HSET(h, ev, h->h_ent)) != -1)
864 retval = HADD(h, ev, str);
865 break;
866
867 case H_FIRST:
868 retval = HFIRST(h, ev);
869 break;
870
871 case H_NEXT:
872 retval = HNEXT(h, ev);
873 break;
874
875 case H_LAST:
876 retval = HLAST(h, ev);
877 break;
878
879 case H_PREV:
880 retval = HPREV(h, ev);
881 break;
882
883 case H_CURR:
884 retval = HCURR(h, ev);
885 break;
886
887 case H_SET:
888 retval = HSET(h, ev, va_arg(va, const int));
889 break;
890
891 case H_CLEAR:
892 HCLEAR(h, ev);
893 retval = 0;
894 break;
895
896 case H_LOAD:
897 retval = history_load(h, va_arg(va, const char *));
898 if (retval == -1)
899 he_seterrev(ev, _HE_HIST_READ);
900 break;
901
902 case H_SAVE:
903 retval = history_save(h, va_arg(va, const char *));
904 if (retval == -1)
905 he_seterrev(ev, _HE_HIST_WRITE);
906 break;
907
908 case H_PREV_EVENT:
909 retval = history_prev_event(h, ev, va_arg(va, int));
910 break;
911
912 case H_NEXT_EVENT:
913 retval = history_next_event(h, ev, va_arg(va, int));
914 break;
915
916 case H_PREV_STR:
917 retval = history_prev_string(h, ev, va_arg(va, const char *));
918 break;
919
920 case H_NEXT_STR:
921 retval = history_next_string(h, ev, va_arg(va, const char *));
922 break;
923
924 case H_FUNC:
925 {
926 History hf;
927
928 hf.h_ref = va_arg(va, ptr_t);
929 h->h_ent = -1;
930 hf.h_first = va_arg(va, history_gfun_t);
931 hf.h_next = va_arg(va, history_gfun_t);
932 hf.h_last = va_arg(va, history_gfun_t);
933 hf.h_prev = va_arg(va, history_gfun_t);
934 hf.h_curr = va_arg(va, history_gfun_t);
935 hf.h_set = va_arg(va, history_sfun_t);
936 hf.h_clear = va_arg(va, history_vfun_t);
937 hf.h_enter = va_arg(va, history_efun_t);
938 hf.h_add = va_arg(va, history_efun_t);
939
940 if ((retval = history_set_fun(h, &hf)) == -1)
941 he_seterrev(ev, _HE_PARAM_MISSING);
942 break;
943 }
944
945 case H_END:
946 history_end(h);
947 retval = 0;
948 break;
949
950 default:
951 retval = -1;
952 he_seterrev(ev, _HE_UNKNOWN);
953 break;
954 }
955 va_end(va);
956 return (retval);
957 }
958