]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libedit/history.c
This commit was generated by cvs2svn to compensate for changes in r165670,
[FreeBSD/FreeBSD.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.31 2005/08/01 14:34:06 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         struct hentry_t *next;  /* Next entry                    */
120         struct hentry_t *prev;  /* Previous entry                */
121 } hentry_t;
122
123 typedef 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
133 private int history_def_next(ptr_t, HistEvent *);
134 private int history_def_first(ptr_t, HistEvent *);
135 private int history_def_prev(ptr_t, HistEvent *);
136 private int history_def_last(ptr_t, HistEvent *);
137 private int history_def_curr(ptr_t, HistEvent *);
138 private int history_def_set(ptr_t, HistEvent *, const int);
139 private void history_def_clear(ptr_t, HistEvent *);
140 private int history_def_enter(ptr_t, HistEvent *, const char *);
141 private int history_def_add(ptr_t, HistEvent *, const char *);
142 private int history_def_del(ptr_t, HistEvent *, const int);
143
144 private int history_def_init(ptr_t *, HistEvent *, int);
145 private int history_def_insert(history_t *, HistEvent *, const char *);
146 private 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 */
164 static 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  */
203 private int
204 history_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  */
223 private int
224 history_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  */
243 private int
244 history_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  */
268 private int
269 history_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  */
294 private int
295 history_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  */
315 private int
316 history_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  */
341 private int
342 history_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 */
370 private int
371 history_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 */
388 private void
389 history_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  */
408 private int
409 history_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);
428 oomem:
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  */
437 private int
438 history_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 */
464 private int
465 history_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  */
489 private void
490 history_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  */
508 public History *
509 history_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  */
539 public void
540 history_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  */
554 private int
555 history_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  */
574 private int
575 history_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  */
593 private int
594 history_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  */
609 private int
610 history_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  */
624 private int
625 history_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  */
670 private int
671 history_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         }
717 oomem:
718         h_free((ptr_t)ptr);
719 done:
720         (void) fclose(fp);
721         return (i);
722 }
723
724
725 /* history_save():
726  *      History save function
727  */
728 private int
729 history_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         }
764 oomem:
765         h_free((ptr_t)ptr);
766 done:
767         (void) fclose(fp);
768         return (i);
769 }
770
771
772 /* history_prev_event():
773  *      Find the previous event, with number given
774  */
775 private int
776 history_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  */
792 private int
793 history_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  */
809 private int
810 history_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  */
827 private int
828 history_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  */
845 int
846 history(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 }