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