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