]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libedit/history.c
MFV ntp 4.2.8p2 (r281348)
[FreeBSD/FreeBSD.git] / lib / libedit / history.c
1 /*      $NetBSD: history.c,v 1.47 2014/05/11 01:05:17 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.47 2014/05/11 01:05:17 christos Exp $");
41 #endif
42 #endif /* not lint && not SCCSID */
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45
46 /*
47  * hist.c: TYPE(History) access functions
48  */
49 #include <string.h>
50 #include <stdlib.h>
51 #include <stdarg.h>
52 #include <vis.h>
53 #include <sys/stat.h>
54
55 static const char hist_cookie[] = "_HiStOrY_V2_\n";
56
57 #include "histedit.h"
58 #include "chartype.h"
59
60 typedef int (*history_gfun_t)(void *, TYPE(HistEvent) *);
61 typedef int (*history_efun_t)(void *, TYPE(HistEvent) *, const Char *);
62 typedef void (*history_vfun_t)(void *, TYPE(HistEvent) *);
63 typedef int (*history_sfun_t)(void *, TYPE(HistEvent) *, const int);
64
65 struct TYPE(history) {
66         void *h_ref;            /* Argument for history fcns     */
67         int h_ent;              /* Last entry point for history  */
68         history_gfun_t h_first; /* Get the first element         */
69         history_gfun_t h_next;  /* Get the next element          */
70         history_gfun_t h_last;  /* Get the last element          */
71         history_gfun_t h_prev;  /* Get the previous element      */
72         history_gfun_t h_curr;  /* Get the current element       */
73         history_sfun_t h_set;   /* Set the current element       */
74         history_sfun_t h_del;   /* Set the given 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 #define HDEL(h, ev, n)          (*(h)->h_del)((h)->h_ref, ev, n)
90
91 #define h_strdup(a)     Strdup(a)
92 #define h_malloc(a)     malloc(a)
93 #define h_realloc(a, b) realloc((a), (b))
94 #define h_free(a)       free(a)
95
96 typedef struct {
97     int         num;
98     Char        *str;
99 } HistEventPrivate;
100
101
102
103 private int history_setsize(TYPE(History) *, TYPE(HistEvent) *, int);
104 private int history_getsize(TYPE(History) *, TYPE(HistEvent) *);
105 private int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int);
106 private int history_getunique(TYPE(History) *, TYPE(HistEvent) *);
107 private int history_set_fun(TYPE(History) *, TYPE(History) *);
108 private int history_load(TYPE(History) *, const char *);
109 private int history_save(TYPE(History) *, const char *);
110 private int history_save_fp(TYPE(History) *, FILE *);
111 private int history_prev_event(TYPE(History) *, TYPE(HistEvent) *, int);
112 private int history_next_event(TYPE(History) *, TYPE(HistEvent) *, int);
113 private int history_next_string(TYPE(History) *, TYPE(HistEvent) *, const Char *);
114 private int history_prev_string(TYPE(History) *, TYPE(HistEvent) *, const Char *);
115
116
117 /***********************************************************************/
118
119 /*
120  * Builtin- history implementation
121  */
122 typedef struct hentry_t {
123         TYPE(HistEvent) ev;             /* What we return                */
124         void *data;             /* data                          */
125         struct hentry_t *next;  /* Next entry                    */
126         struct hentry_t *prev;  /* Previous entry                */
127 } hentry_t;
128
129 typedef struct history_t {
130         hentry_t list;          /* Fake list header element     */
131         hentry_t *cursor;       /* Current element in the list  */
132         int max;                /* Maximum number of events     */
133         int cur;                /* Current number of events     */
134         int eventid;            /* For generation of unique event id     */
135         int flags;              /* TYPE(History) flags          */
136 #define H_UNIQUE        1       /* Store only unique elements   */
137 } history_t;
138
139 private int history_def_next(void *, TYPE(HistEvent) *);
140 private int history_def_first(void *, TYPE(HistEvent) *);
141 private int history_def_prev(void *, TYPE(HistEvent) *);
142 private int history_def_last(void *, TYPE(HistEvent) *);
143 private int history_def_curr(void *, TYPE(HistEvent) *);
144 private int history_def_set(void *, TYPE(HistEvent) *, const int);
145 private void history_def_clear(void *, TYPE(HistEvent) *);
146 private int history_def_enter(void *, TYPE(HistEvent) *, const Char *);
147 private int history_def_add(void *, TYPE(HistEvent) *, const Char *);
148 private int history_def_del(void *, TYPE(HistEvent) *, const int);
149
150 private int history_def_init(void **, TYPE(HistEvent) *, int);
151 private int history_def_insert(history_t *, TYPE(HistEvent) *, const Char *);
152 private void history_def_delete(history_t *, TYPE(HistEvent) *, hentry_t *);
153
154 private int history_deldata_nth(history_t *, TYPE(HistEvent) *, int, void **);
155 private int history_set_nth(void *, TYPE(HistEvent) *, int);
156
157 #define history_def_setsize(p, num)(void) (((history_t *)p)->max = (num))
158 #define history_def_getsize(p)  (((history_t *)p)->cur)
159 #define history_def_getunique(p) (((((history_t *)p)->flags) & H_UNIQUE) != 0)
160 #define history_def_setunique(p, uni) \
161     if (uni) \
162         (((history_t *)p)->flags) |= H_UNIQUE; \
163     else \
164         (((history_t *)p)->flags) &= ~H_UNIQUE
165
166 #define he_strerror(code)       he_errlist[code]
167 #define he_seterrev(evp, code)  {\
168                                     evp->num = code;\
169                                     evp->str = he_strerror(code);\
170                                 }
171
172 /* error messages */
173 static const Char *const he_errlist[] = {
174         STR("OK"),
175         STR("unknown error"),
176         STR("malloc() failed"),
177         STR("first event not found"),
178         STR("last event not found"),
179         STR("empty list"),
180         STR("no next event"),
181         STR("no previous event"),
182         STR("current event is invalid"),
183         STR("event not found"),
184         STR("can't read history from file"),
185         STR("can't write history"),
186         STR("required parameter(s) not supplied"),
187         STR("history size negative"),
188         STR("function not allowed with other history-functions-set the default"),
189         STR("bad parameters")
190 };
191 /* error codes */
192 #define _HE_OK                   0
193 #define _HE_UNKNOWN              1
194 #define _HE_MALLOC_FAILED        2
195 #define _HE_FIRST_NOTFOUND       3
196 #define _HE_LAST_NOTFOUND        4
197 #define _HE_EMPTY_LIST           5
198 #define _HE_END_REACHED          6
199 #define _HE_START_REACHED        7
200 #define _HE_CURR_INVALID         8
201 #define _HE_NOT_FOUND            9
202 #define _HE_HIST_READ           10
203 #define _HE_HIST_WRITE          11
204 #define _HE_PARAM_MISSING       12
205 #define _HE_SIZE_NEGATIVE       13
206 #define _HE_NOT_ALLOWED         14
207 #define _HE_BAD_PARAM           15
208
209 /* history_def_first():
210  *      Default function to return the first event in the history.
211  */
212 private int
213 history_def_first(void *p, TYPE(HistEvent) *ev)
214 {
215         history_t *h = (history_t *) p;
216
217         h->cursor = h->list.next;
218         if (h->cursor != &h->list)
219                 *ev = h->cursor->ev;
220         else {
221                 he_seterrev(ev, _HE_FIRST_NOTFOUND);
222                 return -1;
223         }
224
225         return 0;
226 }
227
228
229 /* history_def_last():
230  *      Default function to return the last event in the history.
231  */
232 private int
233 history_def_last(void *p, TYPE(HistEvent) *ev)
234 {
235         history_t *h = (history_t *) p;
236
237         h->cursor = h->list.prev;
238         if (h->cursor != &h->list)
239                 *ev = h->cursor->ev;
240         else {
241                 he_seterrev(ev, _HE_LAST_NOTFOUND);
242                 return -1;
243         }
244
245         return 0;
246 }
247
248
249 /* history_def_next():
250  *      Default function to return the next event in the history.
251  */
252 private int
253 history_def_next(void *p, TYPE(HistEvent) *ev)
254 {
255         history_t *h = (history_t *) p;
256
257         if (h->cursor == &h->list) {
258                 he_seterrev(ev, _HE_EMPTY_LIST);
259                 return -1;
260         }
261
262         if (h->cursor->next == &h->list) {
263                 he_seterrev(ev, _HE_END_REACHED);
264                 return -1;
265         }
266
267         h->cursor = h->cursor->next;
268         *ev = h->cursor->ev;
269
270         return 0;
271 }
272
273
274 /* history_def_prev():
275  *      Default function to return the previous event in the history.
276  */
277 private int
278 history_def_prev(void *p, TYPE(HistEvent) *ev)
279 {
280         history_t *h = (history_t *) p;
281
282         if (h->cursor == &h->list) {
283                 he_seterrev(ev,
284                     (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
285                 return -1;
286         }
287
288         if (h->cursor->prev == &h->list) {
289                 he_seterrev(ev, _HE_START_REACHED);
290                 return -1;
291         }
292
293         h->cursor = h->cursor->prev;
294         *ev = h->cursor->ev;
295
296         return 0;
297 }
298
299
300 /* history_def_curr():
301  *      Default function to return the current event in the history.
302  */
303 private int
304 history_def_curr(void *p, TYPE(HistEvent) *ev)
305 {
306         history_t *h = (history_t *) p;
307
308         if (h->cursor != &h->list)
309                 *ev = h->cursor->ev;
310         else {
311                 he_seterrev(ev,
312                     (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
313                 return -1;
314         }
315
316         return 0;
317 }
318
319
320 /* history_def_set():
321  *      Default function to set the current event in the history to the
322  *      given one.
323  */
324 private int
325 history_def_set(void *p, TYPE(HistEvent) *ev, const int n)
326 {
327         history_t *h = (history_t *) p;
328
329         if (h->cur == 0) {
330                 he_seterrev(ev, _HE_EMPTY_LIST);
331                 return -1;
332         }
333         if (h->cursor == &h->list || h->cursor->ev.num != n) {
334                 for (h->cursor = h->list.next; h->cursor != &h->list;
335                     h->cursor = h->cursor->next)
336                         if (h->cursor->ev.num == n)
337                                 break;
338         }
339         if (h->cursor == &h->list) {
340                 he_seterrev(ev, _HE_NOT_FOUND);
341                 return -1;
342         }
343         return 0;
344 }
345
346
347 /* history_set_nth():
348  *      Default function to set the current event in the history to the
349  *      n-th one.
350  */
351 private int
352 history_set_nth(void *p, TYPE(HistEvent) *ev, int n)
353 {
354         history_t *h = (history_t *) p;
355
356         if (h->cur == 0) {
357                 he_seterrev(ev, _HE_EMPTY_LIST);
358                 return -1;
359         }
360         for (h->cursor = h->list.prev; h->cursor != &h->list;
361             h->cursor = h->cursor->prev)
362                 if (n-- <= 0)
363                         break;
364         if (h->cursor == &h->list) {
365                 he_seterrev(ev, _HE_NOT_FOUND);
366                 return -1;
367         }
368         return 0;
369 }
370
371
372 /* history_def_add():
373  *      Append string to element
374  */
375 private int
376 history_def_add(void *p, TYPE(HistEvent) *ev, const Char *str)
377 {
378         history_t *h = (history_t *) p;
379         size_t len;
380         Char *s;
381         HistEventPrivate *evp = (void *)&h->cursor->ev;
382
383         if (h->cursor == &h->list)
384                 return history_def_enter(p, ev, str);
385         len = Strlen(evp->str) + Strlen(str) + 1;
386         s = h_malloc(len * sizeof(*s));
387         if (s == NULL) {
388                 he_seterrev(ev, _HE_MALLOC_FAILED);
389                 return -1;
390         }
391         (void) Strncpy(s, h->cursor->ev.str, len);
392         s[len - 1] = '\0';
393         (void) Strncat(s, str, len - Strlen(s) - 1);
394         h_free(evp->str);
395         evp->str = s;
396         *ev = h->cursor->ev;
397         return 0;
398 }
399
400
401 private int
402 history_deldata_nth(history_t *h, TYPE(HistEvent) *ev,
403     int num, void **data)
404 {
405         if (history_set_nth(h, ev, num) != 0)
406                 return -1;
407         /* magic value to skip delete (just set to n-th history) */
408         if (data == (void **)-1)
409                 return 0;
410         ev->str = Strdup(h->cursor->ev.str);
411         ev->num = h->cursor->ev.num;
412         if (data)
413                 *data = h->cursor->data;
414         history_def_delete(h, ev, h->cursor);
415         return 0;
416 }
417
418
419 /* history_def_del():
420  *      Delete element hp of the h list
421  */
422 /* ARGSUSED */
423 private int
424 history_def_del(void *p, TYPE(HistEvent) *ev __attribute__((__unused__)),
425     const int num)
426 {
427         history_t *h = (history_t *) p;
428         if (history_def_set(h, ev, num) != 0)
429                 return -1;
430         ev->str = Strdup(h->cursor->ev.str);
431         ev->num = h->cursor->ev.num;
432         history_def_delete(h, ev, h->cursor);
433         return 0;
434 }
435
436
437 /* history_def_delete():
438  *      Delete element hp of the h list
439  */
440 /* ARGSUSED */
441 private void
442 history_def_delete(history_t *h, 
443                    TYPE(HistEvent) *ev __attribute__((__unused__)), hentry_t *hp)
444 {
445         HistEventPrivate *evp = (void *)&hp->ev;
446         if (hp == &h->list)
447                 abort();
448         if (h->cursor == hp) {
449                 h->cursor = hp->prev;
450                 if (h->cursor == &h->list)
451                         h->cursor = hp->next;
452         }
453         hp->prev->next = hp->next;
454         hp->next->prev = hp->prev;
455         h_free(evp->str);
456         h_free(hp);
457         h->cur--;
458 }
459
460
461 /* history_def_insert():
462  *      Insert element with string str in the h list
463  */
464 private int
465 history_def_insert(history_t *h, TYPE(HistEvent) *ev, const Char *str)
466 {
467         hentry_t *c;
468
469         c = h_malloc(sizeof(*c));
470         if (c == NULL)
471                 goto oomem;
472         if ((c->ev.str = h_strdup(str)) == NULL) {
473                 h_free(c);
474                 goto oomem;
475         }
476         c->data = NULL;
477         c->ev.num = ++h->eventid;
478         c->next = h->list.next;
479         c->prev = &h->list;
480         h->list.next->prev = c;
481         h->list.next = c;
482         h->cur++;
483         h->cursor = c;
484
485         *ev = c->ev;
486         return 0;
487 oomem:
488         he_seterrev(ev, _HE_MALLOC_FAILED);
489         return -1;
490 }
491
492
493 /* history_def_enter():
494  *      Default function to enter an item in the history
495  */
496 private int
497 history_def_enter(void *p, TYPE(HistEvent) *ev, const Char *str)
498 {
499         history_t *h = (history_t *) p;
500
501         if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
502             Strcmp(h->list.next->ev.str, str) == 0)
503             return 0;
504
505         if (history_def_insert(h, ev, str) == -1)
506                 return -1;      /* error, keep error message */
507
508         /*
509          * Always keep at least one entry.
510          * This way we don't have to check for the empty list.
511          */
512         while (h->cur > h->max && h->cur > 0)
513                 history_def_delete(h, ev, h->list.prev);
514
515         return 1;
516 }
517
518
519 /* history_def_init():
520  *      Default history initialization function
521  */
522 /* ARGSUSED */
523 private int
524 history_def_init(void **p, TYPE(HistEvent) *ev __attribute__((__unused__)), int n)
525 {
526         history_t *h = (history_t *) h_malloc(sizeof(*h));
527         if (h == NULL)
528                 return -1;
529
530         if (n <= 0)
531                 n = 0;
532         h->eventid = 0;
533         h->cur = 0;
534         h->max = n;
535         h->list.next = h->list.prev = &h->list;
536         h->list.ev.str = NULL;
537         h->list.ev.num = 0;
538         h->cursor = &h->list;
539         h->flags = 0;
540         *p = h;
541         return 0;
542 }
543
544
545 /* history_def_clear():
546  *      Default history cleanup function
547  */
548 private void
549 history_def_clear(void *p, TYPE(HistEvent) *ev)
550 {
551         history_t *h = (history_t *) p;
552
553         while (h->list.prev != &h->list)
554                 history_def_delete(h, ev, h->list.prev);
555         h->cursor = &h->list;
556         h->eventid = 0;
557         h->cur = 0;
558 }
559
560
561
562
563 /************************************************************************/
564
565 /* history_init():
566  *      Initialization function.
567  */
568 public TYPE(History) *
569 FUN(history,init)(void)
570 {
571         TYPE(HistEvent) ev;
572         TYPE(History) *h = (TYPE(History) *) h_malloc(sizeof(*h));
573         if (h == NULL)
574                 return NULL;
575
576         if (history_def_init(&h->h_ref, &ev, 0) == -1) {
577                 h_free(h);
578                 return NULL;
579         }
580         h->h_ent = -1;
581         h->h_next = history_def_next;
582         h->h_first = history_def_first;
583         h->h_last = history_def_last;
584         h->h_prev = history_def_prev;
585         h->h_curr = history_def_curr;
586         h->h_set = history_def_set;
587         h->h_clear = history_def_clear;
588         h->h_enter = history_def_enter;
589         h->h_add = history_def_add;
590         h->h_del = history_def_del;
591
592         return h;
593 }
594
595
596 /* history_end():
597  *      clean up history;
598  */
599 public void
600 FUN(history,end)(TYPE(History) *h)
601 {
602         TYPE(HistEvent) ev;
603
604         if (h->h_next == history_def_next)
605                 history_def_clear(h->h_ref, &ev);
606         h_free(h->h_ref);
607         h_free(h);
608 }
609
610
611
612 /* history_setsize():
613  *      Set history number of events
614  */
615 private int
616 history_setsize(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
617 {
618
619         if (h->h_next != history_def_next) {
620                 he_seterrev(ev, _HE_NOT_ALLOWED);
621                 return -1;
622         }
623         if (num < 0) {
624                 he_seterrev(ev, _HE_BAD_PARAM);
625                 return -1;
626         }
627         history_def_setsize(h->h_ref, num);
628         return 0;
629 }
630
631
632 /* history_getsize():
633  *      Get number of events currently in history
634  */
635 private int
636 history_getsize(TYPE(History) *h, TYPE(HistEvent) *ev)
637 {
638         if (h->h_next != history_def_next) {
639                 he_seterrev(ev, _HE_NOT_ALLOWED);
640                 return -1;
641         }
642         ev->num = history_def_getsize(h->h_ref);
643         if (ev->num < -1) {
644                 he_seterrev(ev, _HE_SIZE_NEGATIVE);
645                 return -1;
646         }
647         return 0;
648 }
649
650
651 /* history_setunique():
652  *      Set if adjacent equal events should not be entered in history.
653  */
654 private int
655 history_setunique(TYPE(History) *h, TYPE(HistEvent) *ev, int uni)
656 {
657
658         if (h->h_next != history_def_next) {
659                 he_seterrev(ev, _HE_NOT_ALLOWED);
660                 return -1;
661         }
662         history_def_setunique(h->h_ref, uni);
663         return 0;
664 }
665
666
667 /* history_getunique():
668  *      Get if adjacent equal events should not be entered in history.
669  */
670 private int
671 history_getunique(TYPE(History) *h, TYPE(HistEvent) *ev)
672 {
673         if (h->h_next != history_def_next) {
674                 he_seterrev(ev, _HE_NOT_ALLOWED);
675                 return -1;
676         }
677         ev->num = history_def_getunique(h->h_ref);
678         return 0;
679 }
680
681
682 /* history_set_fun():
683  *      Set history functions
684  */
685 private int
686 history_set_fun(TYPE(History) *h, TYPE(History) *nh)
687 {
688         TYPE(HistEvent) ev;
689
690         if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
691             nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
692             nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
693             nh->h_del == NULL || nh->h_ref == NULL) {
694                 if (h->h_next != history_def_next) {
695                         if (history_def_init(&h->h_ref, &ev, 0) == -1)
696                                 return -1;
697                         h->h_first = history_def_first;
698                         h->h_next = history_def_next;
699                         h->h_last = history_def_last;
700                         h->h_prev = history_def_prev;
701                         h->h_curr = history_def_curr;
702                         h->h_set = history_def_set;
703                         h->h_clear = history_def_clear;
704                         h->h_enter = history_def_enter;
705                         h->h_add = history_def_add;
706                         h->h_del = history_def_del;
707                 }
708                 return -1;
709         }
710         if (h->h_next == history_def_next)
711                 history_def_clear(h->h_ref, &ev);
712
713         h->h_ent = -1;
714         h->h_first = nh->h_first;
715         h->h_next = nh->h_next;
716         h->h_last = nh->h_last;
717         h->h_prev = nh->h_prev;
718         h->h_curr = nh->h_curr;
719         h->h_set = nh->h_set;
720         h->h_clear = nh->h_clear;
721         h->h_enter = nh->h_enter;
722         h->h_add = nh->h_add;
723         h->h_del = nh->h_del;
724
725         return 0;
726 }
727
728
729 /* history_load():
730  *      TYPE(History) load function
731  */
732 private int
733 history_load(TYPE(History) *h, const char *fname)
734 {
735         FILE *fp;
736         char *line;
737         size_t sz, max_size;
738         char *ptr;
739         int i = -1;
740         TYPE(HistEvent) ev;
741 #ifdef WIDECHAR
742         static ct_buffer_t conv;
743 #endif
744
745         if ((fp = fopen(fname, "r")) == NULL)
746                 return i;
747
748         if ((line = fgetln(fp, &sz)) == NULL)
749                 goto done;
750
751         if (strncmp(line, hist_cookie, sz) != 0)
752                 goto done;
753
754         ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
755         if (ptr == NULL)
756                 goto done;
757         for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
758                 char c = line[sz];
759
760                 if (sz != 0 && line[sz - 1] == '\n')
761                         line[--sz] = '\0';
762                 else
763                         line[sz] = '\0';
764
765                 if (max_size < sz) {
766                         char *nptr;
767                         max_size = (sz + 1024) & (size_t)~1023;
768                         nptr = h_realloc(ptr, max_size * sizeof(*ptr));
769                         if (nptr == NULL) {
770                                 i = -1;
771                                 goto oomem;
772                         }
773                         ptr = nptr;
774                 }
775                 (void) strunvis(ptr, line);
776                 line[sz] = c;
777                 if (HENTER(h, &ev, ct_decode_string(ptr, &conv)) == -1) {
778                         i = -1;
779                         goto oomem;
780                 }
781         }
782 oomem:
783         h_free(ptr);
784 done:
785         (void) fclose(fp);
786         return i;
787 }
788
789
790 /* history_save_fp():
791  *      TYPE(History) save function
792  */
793 private int
794 history_save_fp(TYPE(History) *h, FILE *fp)
795 {
796         TYPE(HistEvent) ev;
797         int i = -1, retval;
798         size_t len, max_size;
799         char *ptr;
800         const char *str;
801 #ifdef WIDECHAR
802         static ct_buffer_t conv;
803 #endif
804
805         if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
806                 goto done;
807         if (fputs(hist_cookie, fp) == EOF)
808                 goto done;
809         ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
810         if (ptr == NULL)
811                 goto done;
812         for (i = 0, retval = HLAST(h, &ev);
813             retval != -1;
814             retval = HPREV(h, &ev), i++) {
815                 str = ct_encode_string(ev.str, &conv);
816                 len = strlen(str) * 4;
817                 if (len >= max_size) {
818                         char *nptr;
819                         max_size = (len + 1024) & (size_t)~1023;
820                         nptr = h_realloc(ptr, max_size * sizeof(*ptr));
821                         if (nptr == NULL) {
822                                 i = -1;
823                                 goto oomem;
824                         }
825                         ptr = nptr;
826                 }
827                 (void) strvis(ptr, str, VIS_WHITE);
828                 (void) fprintf(fp, "%s\n", ptr);
829         }
830 oomem:
831         h_free(ptr);
832 done:
833         return i;
834 }
835
836
837 /* history_save():
838  *    History save function
839  */
840 private int
841 history_save(TYPE(History) *h, const char *fname)
842 {
843     FILE *fp;
844     int i;
845
846     if ((fp = fopen(fname, "w")) == NULL)
847         return -1;
848
849     i = history_save_fp(h, fp);
850
851     (void) fclose(fp);
852     return i;
853 }
854
855
856 /* history_prev_event():
857  *      Find the previous event, with number given
858  */
859 private int
860 history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
861 {
862         int retval;
863
864         for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
865                 if (ev->num == num)
866                         return 0;
867
868         he_seterrev(ev, _HE_NOT_FOUND);
869         return -1;
870 }
871
872
873 private int
874 history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d)
875 {
876         int retval;
877
878         for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
879                 if (ev->num == num) {
880                         if (d)
881                                 *d = ((history_t *)h->h_ref)->cursor->data;
882                         return 0;
883                 }
884
885         he_seterrev(ev, _HE_NOT_FOUND);
886         return -1;
887 }
888
889
890 /* history_next_event():
891  *      Find the next event, with number given
892  */
893 private int
894 history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
895 {
896         int retval;
897
898         for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
899                 if (ev->num == num)
900                         return 0;
901
902         he_seterrev(ev, _HE_NOT_FOUND);
903         return -1;
904 }
905
906
907 /* history_prev_string():
908  *      Find the previous event beginning with string
909  */
910 private int
911 history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
912 {
913         size_t len = Strlen(str);
914         int retval;
915
916         for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
917                 if (Strncmp(str, ev->str, len) == 0)
918                         return 0;
919
920         he_seterrev(ev, _HE_NOT_FOUND);
921         return -1;
922 }
923
924
925 /* history_next_string():
926  *      Find the next event beginning with string
927  */
928 private int
929 history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
930 {
931         size_t len = Strlen(str);
932         int retval;
933
934         for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
935                 if (Strncmp(str, ev->str, len) == 0)
936                         return 0;
937
938         he_seterrev(ev, _HE_NOT_FOUND);
939         return -1;
940 }
941
942
943 /* history():
944  *      User interface to history functions.
945  */
946 int
947 FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...)
948 {
949         va_list va;
950         const Char *str;
951         int retval;
952
953         va_start(va, fun);
954
955         he_seterrev(ev, _HE_OK);
956
957         switch (fun) {
958         case H_GETSIZE:
959                 retval = history_getsize(h, ev);
960                 break;
961
962         case H_SETSIZE:
963                 retval = history_setsize(h, ev, va_arg(va, int));
964                 break;
965
966         case H_GETUNIQUE:
967                 retval = history_getunique(h, ev);
968                 break;
969
970         case H_SETUNIQUE:
971                 retval = history_setunique(h, ev, va_arg(va, int));
972                 break;
973
974         case H_ADD:
975                 str = va_arg(va, const Char *);
976                 retval = HADD(h, ev, str);
977                 break;
978
979         case H_DEL:
980                 retval = HDEL(h, ev, va_arg(va, const int));
981                 break;
982
983         case H_ENTER:
984                 str = va_arg(va, const Char *);
985                 if ((retval = HENTER(h, ev, str)) != -1)
986                         h->h_ent = ev->num;
987                 break;
988
989         case H_APPEND:
990                 str = va_arg(va, const Char *);
991                 if ((retval = HSET(h, ev, h->h_ent)) != -1)
992                         retval = HADD(h, ev, str);
993                 break;
994
995         case H_FIRST:
996                 retval = HFIRST(h, ev);
997                 break;
998
999         case H_NEXT:
1000                 retval = HNEXT(h, ev);
1001                 break;
1002
1003         case H_LAST:
1004                 retval = HLAST(h, ev);
1005                 break;
1006
1007         case H_PREV:
1008                 retval = HPREV(h, ev);
1009                 break;
1010
1011         case H_CURR:
1012                 retval = HCURR(h, ev);
1013                 break;
1014
1015         case H_SET:
1016                 retval = HSET(h, ev, va_arg(va, const int));
1017                 break;
1018
1019         case H_CLEAR:
1020                 HCLEAR(h, ev);
1021                 retval = 0;
1022                 break;
1023
1024         case H_LOAD:
1025                 retval = history_load(h, va_arg(va, const char *));
1026                 if (retval == -1)
1027                         he_seterrev(ev, _HE_HIST_READ);
1028                 break;
1029
1030         case H_SAVE:
1031                 retval = history_save(h, va_arg(va, const char *));
1032                 if (retval == -1)
1033                         he_seterrev(ev, _HE_HIST_WRITE);
1034                 break;
1035
1036         case H_SAVE_FP:
1037                 retval = history_save_fp(h, va_arg(va, FILE *));
1038                 if (retval == -1)
1039                     he_seterrev(ev, _HE_HIST_WRITE);
1040                 break;
1041
1042         case H_PREV_EVENT:
1043                 retval = history_prev_event(h, ev, va_arg(va, int));
1044                 break;
1045
1046         case H_NEXT_EVENT:
1047                 retval = history_next_event(h, ev, va_arg(va, int));
1048                 break;
1049
1050         case H_PREV_STR:
1051                 retval = history_prev_string(h, ev, va_arg(va, const Char *));
1052                 break;
1053
1054         case H_NEXT_STR:
1055                 retval = history_next_string(h, ev, va_arg(va, const Char *));
1056                 break;
1057
1058         case H_FUNC:
1059         {
1060                 TYPE(History) hf;
1061
1062                 hf.h_ref = va_arg(va, void *);
1063                 h->h_ent = -1;
1064                 hf.h_first = va_arg(va, history_gfun_t);
1065                 hf.h_next = va_arg(va, history_gfun_t);
1066                 hf.h_last = va_arg(va, history_gfun_t);
1067                 hf.h_prev = va_arg(va, history_gfun_t);
1068                 hf.h_curr = va_arg(va, history_gfun_t);
1069                 hf.h_set = va_arg(va, history_sfun_t);
1070                 hf.h_clear = va_arg(va, history_vfun_t);
1071                 hf.h_enter = va_arg(va, history_efun_t);
1072                 hf.h_add = va_arg(va, history_efun_t);
1073                 hf.h_del = va_arg(va, history_sfun_t);
1074
1075                 if ((retval = history_set_fun(h, &hf)) == -1)
1076                         he_seterrev(ev, _HE_PARAM_MISSING);
1077                 break;
1078         }
1079
1080         case H_END:
1081                 FUN(history,end)(h);
1082                 retval = 0;
1083                 break;
1084
1085         case H_NEXT_EVDATA:
1086         {
1087                 int num = va_arg(va, int);
1088                 void **d = va_arg(va, void **);
1089                 retval = history_next_evdata(h, ev, num, d);
1090                 break;
1091         }
1092
1093         case H_DELDATA:
1094         {
1095                 int num = va_arg(va, int);
1096                 void **d = va_arg(va, void **);
1097                 retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d);
1098                 break;
1099         }
1100
1101         case H_REPLACE: /* only use after H_NEXT_EVDATA */
1102         {
1103                 const Char *line = va_arg(va, const Char *);
1104                 void *d = va_arg(va, void *);
1105                 const Char *s;
1106                 if(!line || !(s = Strdup(line))) {
1107                         retval = -1;
1108                         break;
1109                 }
1110                 ((history_t *)h->h_ref)->cursor->ev.str = s;
1111                 ((history_t *)h->h_ref)->cursor->data = d;
1112                 retval = 0;
1113                 break;
1114         }
1115
1116         default:
1117                 retval = -1;
1118                 he_seterrev(ev, _HE_UNKNOWN);
1119                 break;
1120         }
1121         va_end(va);
1122         return retval;
1123 }