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