wlan - Rip out all wlan locks part 1/2
[dragonfly.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  * @(#)history.c        8.1 (Berkeley) 6/4/93
33  * $NetBSD: history.c,v 1.32 2006/09/28 13:52:51 christos Exp $
34  * $DragonFly: src/lib/libedit/history.c,v 1.7 2007/05/05 00:27:39 pavalos Exp $
35  */
36
37 #include "config.h"
38
39 /*
40  * hist.c: History access functions
41  */
42 #include <string.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #ifdef HAVE_VIS_H
46 #include <vis.h>
47 #else
48 #include "np/vis.h"
49 #endif
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 __attribute__((__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 __attribute__((__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 __attribute__((__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->h_ref);
547         h_free(h);
548 }
549
550
551
552 /* history_setsize():
553  *      Set history number of events
554  */
555 private int
556 history_setsize(History *h, HistEvent *ev, int num)
557 {
558
559         if (h->h_next != history_def_next) {
560                 he_seterrev(ev, _HE_NOT_ALLOWED);
561                 return (-1);
562         }
563         if (num < 0) {
564                 he_seterrev(ev, _HE_BAD_PARAM);
565                 return (-1);
566         }
567         history_def_setsize(h->h_ref, num);
568         return (0);
569 }
570
571
572 /* history_getsize():
573  *      Get number of events currently in history
574  */
575 private int
576 history_getsize(History *h, HistEvent *ev)
577 {
578         if (h->h_next != history_def_next) {
579                 he_seterrev(ev, _HE_NOT_ALLOWED);
580                 return (-1);
581         }
582         ev->num = history_def_getsize(h->h_ref);
583         if (ev->num < -1) {
584                 he_seterrev(ev, _HE_SIZE_NEGATIVE);
585                 return (-1);
586         }
587         return (0);
588 }
589
590
591 /* history_setunique():
592  *      Set if adjacent equal events should not be entered in history.
593  */
594 private int
595 history_setunique(History *h, HistEvent *ev, int uni)
596 {
597
598         if (h->h_next != history_def_next) {
599                 he_seterrev(ev, _HE_NOT_ALLOWED);
600                 return (-1);
601         }
602         history_def_setunique(h->h_ref, uni);
603         return (0);
604 }
605
606
607 /* history_getunique():
608  *      Get if adjacent equal events should not be entered in history.
609  */
610 private int
611 history_getunique(History *h, HistEvent *ev)
612 {
613         if (h->h_next != history_def_next) {
614                 he_seterrev(ev, _HE_NOT_ALLOWED);
615                 return (-1);
616         }
617         ev->num = history_def_getunique(h->h_ref);
618         return (0);
619 }
620
621
622 /* history_set_fun():
623  *      Set history functions
624  */
625 private int
626 history_set_fun(History *h, History *nh)
627 {
628         HistEvent ev;
629
630         if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
631             nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
632             nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
633             nh->h_del == NULL || nh->h_ref == NULL) {
634                 if (h->h_next != history_def_next) {
635                         history_def_init(&h->h_ref, &ev, 0);
636                         h->h_first = history_def_first;
637                         h->h_next = history_def_next;
638                         h->h_last = history_def_last;
639                         h->h_prev = history_def_prev;
640                         h->h_curr = history_def_curr;
641                         h->h_set = history_def_set;
642                         h->h_clear = history_def_clear;
643                         h->h_enter = history_def_enter;
644                         h->h_add = history_def_add;
645                         h->h_del = history_def_del;
646                 }
647                 return (-1);
648         }
649         if (h->h_next == history_def_next)
650                 history_def_clear(h->h_ref, &ev);
651
652         h->h_ent = -1;
653         h->h_first = nh->h_first;
654         h->h_next = nh->h_next;
655         h->h_last = nh->h_last;
656         h->h_prev = nh->h_prev;
657         h->h_curr = nh->h_curr;
658         h->h_set = nh->h_set;
659         h->h_clear = nh->h_clear;
660         h->h_enter = nh->h_enter;
661         h->h_add = nh->h_add;
662         h->h_del = nh->h_del;
663
664         return (0);
665 }
666
667
668 /* history_load():
669  *      History load function
670  */
671 private int
672 history_load(History *h, const char *fname)
673 {
674         FILE *fp;
675         char *line;
676         size_t sz, max_size;
677         char *ptr;
678         int i = -1;
679         HistEvent ev;
680
681         if ((fp = fopen(fname, "r")) == NULL)
682                 return (i);
683
684         if ((line = fgetln(fp, &sz)) == NULL)
685                 goto done;
686
687         if (strncmp(line, hist_cookie, sz) != 0)
688                 goto done;
689
690         ptr = h_malloc(max_size = 1024);
691         if (ptr == NULL)
692                 goto done;
693         for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
694                 char c = line[sz];
695
696                 if (sz != 0 && line[sz - 1] == '\n')
697                         line[--sz] = '\0';
698                 else
699                         line[sz] = '\0';
700
701                 if (max_size < sz) {
702                         char *nptr;
703                         max_size = (sz + 1024) & ~1023;
704                         nptr = h_realloc(ptr, max_size);
705                         if (nptr == NULL) {
706                                 i = -1;
707                                 goto oomem;
708                         }
709                         ptr = nptr;
710                 }
711                 (void) strunvis(ptr, line);
712                 line[sz] = c;
713                 if (HENTER(h, &ev, ptr) == -1) {
714                         h_free((ptr_t)ptr);
715                         return -1;
716                 }
717         }
718 oomem:
719         h_free((ptr_t)ptr);
720 done:
721         (void) fclose(fp);
722         return (i);
723 }
724
725
726 /* history_save():
727  *      History save function
728  */
729 private int
730 history_save(History *h, const char *fname)
731 {
732         FILE *fp;
733         HistEvent ev;
734         int i = -1, retval;
735         size_t len, max_size;
736         char *ptr;
737
738         if ((fp = fopen(fname, "w")) == NULL)
739                 return (-1);
740
741         if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
742                 goto done;
743         if (fputs(hist_cookie, fp) == EOF)
744                 goto done;
745         ptr = h_malloc(max_size = 1024);
746         if (ptr == NULL)
747                 goto done;
748         for (i = 0, retval = HLAST(h, &ev);
749             retval != -1;
750             retval = HPREV(h, &ev), i++) {
751                 len = strlen(ev.str) * 4;
752                 if (len >= max_size) {
753                         char *nptr;
754                         max_size = (len + 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) strvis(ptr, ev.str, VIS_WHITE);
763                 (void) fprintf(fp, "%s\n", ptr);
764         }
765 oomem:
766         h_free((ptr_t)ptr);
767 done:
768         (void) fclose(fp);
769         return (i);
770 }
771
772
773 /* history_prev_event():
774  *      Find the previous event, with number given
775  */
776 private int
777 history_prev_event(History *h, HistEvent *ev, int num)
778 {
779         int retval;
780
781         for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
782                 if (ev->num == num)
783                         return (0);
784
785         he_seterrev(ev, _HE_NOT_FOUND);
786         return (-1);
787 }
788
789
790 /* history_next_event():
791  *      Find the next event, with number given
792  */
793 private int
794 history_next_event(History *h, HistEvent *ev, int num)
795 {
796         int retval;
797
798         for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
799                 if (ev->num == num)
800                         return (0);
801
802         he_seterrev(ev, _HE_NOT_FOUND);
803         return (-1);
804 }
805
806
807 /* history_prev_string():
808  *      Find the previous event beginning with string
809  */
810 private int
811 history_prev_string(History *h, HistEvent *ev, const char *str)
812 {
813         size_t len = strlen(str);
814         int retval;
815
816         for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
817                 if (strncmp(str, ev->str, len) == 0)
818                         return (0);
819
820         he_seterrev(ev, _HE_NOT_FOUND);
821         return (-1);
822 }
823
824
825 /* history_next_string():
826  *      Find the next event beginning with string
827  */
828 private int
829 history_next_string(History *h, HistEvent *ev, const char *str)
830 {
831         size_t len = strlen(str);
832         int retval;
833
834         for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
835                 if (strncmp(str, ev->str, len) == 0)
836                         return (0);
837
838         he_seterrev(ev, _HE_NOT_FOUND);
839         return (-1);
840 }
841
842
843 /* history():
844  *      User interface to history functions.
845  */
846 int
847 history(History *h, HistEvent *ev, int fun, ...)
848 {
849         va_list va;
850         const char *str;
851         int retval;
852
853         va_start(va, fun);
854
855         he_seterrev(ev, _HE_OK);
856
857         switch (fun) {
858         case H_GETSIZE:
859                 retval = history_getsize(h, ev);
860                 break;
861
862         case H_SETSIZE:
863                 retval = history_setsize(h, ev, va_arg(va, int));
864                 break;
865
866         case H_GETUNIQUE:
867                 retval = history_getunique(h, ev);
868                 break;
869
870         case H_SETUNIQUE:
871                 retval = history_setunique(h, ev, va_arg(va, int));
872                 break;
873
874         case H_ADD:
875                 str = va_arg(va, const char *);
876                 retval = HADD(h, ev, str);
877                 break;
878
879         case H_DEL:
880                 retval = HDEL(h, ev, va_arg(va, const int));
881                 break;
882
883         case H_ENTER:
884                 str = va_arg(va, const char *);
885                 if ((retval = HENTER(h, ev, str)) != -1)
886                         h->h_ent = ev->num;
887                 break;
888
889         case H_APPEND:
890                 str = va_arg(va, const char *);
891                 if ((retval = HSET(h, ev, h->h_ent)) != -1)
892                         retval = HADD(h, ev, str);
893                 break;
894
895         case H_FIRST:
896                 retval = HFIRST(h, ev);
897                 break;
898
899         case H_NEXT:
900                 retval = HNEXT(h, ev);
901                 break;
902
903         case H_LAST:
904                 retval = HLAST(h, ev);
905                 break;
906
907         case H_PREV:
908                 retval = HPREV(h, ev);
909                 break;
910
911         case H_CURR:
912                 retval = HCURR(h, ev);
913                 break;
914
915         case H_SET:
916                 retval = HSET(h, ev, va_arg(va, const int));
917                 break;
918
919         case H_CLEAR:
920                 HCLEAR(h, ev);
921                 retval = 0;
922                 break;
923
924         case H_LOAD:
925                 retval = history_load(h, va_arg(va, const char *));
926                 if (retval == -1)
927                         he_seterrev(ev, _HE_HIST_READ);
928                 break;
929
930         case H_SAVE:
931                 retval = history_save(h, va_arg(va, const char *));
932                 if (retval == -1)
933                         he_seterrev(ev, _HE_HIST_WRITE);
934                 break;
935
936         case H_PREV_EVENT:
937                 retval = history_prev_event(h, ev, va_arg(va, int));
938                 break;
939
940         case H_NEXT_EVENT:
941                 retval = history_next_event(h, ev, va_arg(va, int));
942                 break;
943
944         case H_PREV_STR:
945                 retval = history_prev_string(h, ev, va_arg(va, const char *));
946                 break;
947
948         case H_NEXT_STR:
949                 retval = history_next_string(h, ev, va_arg(va, const char *));
950                 break;
951
952         case H_FUNC:
953         {
954                 History hf;
955
956                 hf.h_ref = va_arg(va, ptr_t);
957                 h->h_ent = -1;
958                 hf.h_first = va_arg(va, history_gfun_t);
959                 hf.h_next = va_arg(va, history_gfun_t);
960                 hf.h_last = va_arg(va, history_gfun_t);
961                 hf.h_prev = va_arg(va, history_gfun_t);
962                 hf.h_curr = va_arg(va, history_gfun_t);
963                 hf.h_set = va_arg(va, history_sfun_t);
964                 hf.h_clear = va_arg(va, history_vfun_t);
965                 hf.h_enter = va_arg(va, history_efun_t);
966                 hf.h_add = va_arg(va, history_efun_t);
967                 hf.h_del = va_arg(va, history_sfun_t);
968
969                 if ((retval = history_set_fun(h, &hf)) == -1)
970                         he_seterrev(ev, _HE_PARAM_MISSING);
971                 break;
972         }
973
974         case H_END:
975                 history_end(h);
976                 retval = 0;
977                 break;
978
979         default:
980                 retval = -1;
981                 he_seterrev(ev, _HE_UNKNOWN);
982                 break;
983         }
984         va_end(va);
985         return (retval);
986 }