gam-resource-manager: adjust to updated proxied call callback signature.
[profile/ivi/murphy.git] / src / breedline / breedline.c
1 /*
2  * Copyright (c) 2012, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *   * Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *   * Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *   * Neither the name of Intel Corporation nor the names of its contributors
14  *     may be used to endorse or promote products derived from this software
15  *     without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <stdarg.h>
34 #include <fcntl.h>
35 #include <termios.h>
36 #include <ctype.h>
37 #include <sys/ioctl.h>
38 #include <sys/poll.h>
39
40 #include "breedline/breedline.h"
41 #include "breedline/mm.h"
42
43 #ifndef TRUE
44 #  define FALSE 0
45 #  define TRUE  (!FALSE)
46 #endif
47
48 #define BRL_UNUSED(var) (void)var
49
50 #ifdef __GNUC__
51 #    define BRL_UNLIKELY(cond) __builtin_expect((cond), 0)
52 #else
53 #    define BRL_UNLIKELY(cond) __builtin_expect((cond), 0)
54 #endif
55
56 #define BRL_CURSOR_START "\x1b[0G"       /* move cursor to start of line */
57 #define BRL_CURSOR_FORW  "\x1b[%dC"      /* move cursor forward by %d */
58 #define BRL_ERASE_RIGHT  "\x1b[0K"       /* erase right of cursor */
59
60
61 /*
62  * breedline modes
63  */
64
65 typedef enum {
66     BRL_MODE_NORMAL,                     /* normal editing mode */
67     BRL_MODE_SEARCH_FORW,                /* searching forward */
68     BRL_MODE_SEARCH_BACK,                /* searching backward */
69 } brl_mode_t;
70
71
72 /*
73  * breedline key types
74  */
75
76 typedef enum {
77     BRL_TYPE_INVALID = 0x000,            /* invalid key */
78     BRL_TYPE_SELF    = 0x100,            /* self-inserting (normal) key */
79     BRL_TYPE_COMMAND = 0x200,            /* editing command key */
80     BRL_TYPE_CSEQ    = 0x400,            /* control sequence indicator */
81     BRL_TYPE_MASK    = 0x700,            /* key type mask */
82 } brl_type_t;
83
84 #define BRL_INPUT_TYPE(in)     ((in)  & BRL_TYPE_MASK)
85 #define BRL_TAG_INPUT(type, c) ((type & BRL_TYPE_MASK) | ((c) & ~BRL_TYPE_MASK))
86 #define BRL_INPUT_DATA(in)     ((in)  & ~BRL_TYPE_MASK)
87
88 /*
89  * breedline commands
90  */
91
92 typedef enum {
93     BRL_CMD_INVALID = 0x00,              /* invalid input */
94     BRL_CMD_SELF    = 0xff,              /* self-inserting input */
95     BRL_CMD_FORWARD = 0x01,              /* cursor forward */
96     BRL_CMD_BACKWARD,                    /* cursor backward */
97     BRL_CMD_PREV_LINE,                   /* previous line from history */
98     BRL_CMD_NEXT_LINE,                   /* next line from history */
99     BRL_CMD_ERASE_BEFORE,                /* erase before cursor */
100     BRL_CMD_ERASE_AT,                    /* erase at cursor */
101     BRL_CMD_LINE_START,                  /* cursor to start of line */
102     BRL_CMD_LINE_END,                    /* cursor to end of line */
103     BRL_CMD_ERASE_REST,                  /* erase till the end of line */
104     BRL_CMD_ERASE_ALL,                   /* erase the whole line */
105     BRL_CMD_YANK,                        /* yank at insertion point */
106     BRL_CMD_PREV_WORD,                   /* cursor to previous word boundary */
107     BRL_CMD_NEXT_WORD,                   /* cursor to next word boundary */
108     BRL_CMD_CANCEL,                      /* cancel special command or mode */
109     BRL_CMD_ENTER,                       /* enter input */
110     BRL_CMD_REDRAW,                      /* redraw input prompt */
111     BRL_CMD_SEARCH_BACK,                 /* search history backward */
112     BRL_CMD_SEARCH_FORW,                 /* search history forward */
113 } brl_cmd_t;
114
115
116 /*
117  * breedline extended control sequences
118  */
119
120 typedef struct {
121     const char *seq;                     /* control sequence */
122     int         len;                     /* sequence length */
123     int         key;                     /* mapped to this key */
124 } extmap_t;
125
126
127 /*
128  * key mapping
129  */
130
131 #undef  CTRL
132 #define CTRL(code) ((code) & 0x1f)
133 #define ESC  0x1b
134 #define DEL  0x7f
135 #define BELL 0x7
136
137 #define MAP(in, out)             [(in)]            = (out)
138 #define MAP_RANGE(min, max, out) [(min) ... (max)] = (out)
139 #define CMD(cmd)                 BRL_TAG_INPUT(BRL_TYPE_COMMAND, BRL_CMD_##cmd)
140
141 static int key_map[256] = {
142     MAP_RANGE(' ' , '~', BRL_TYPE_SELF    ),
143     MAP(CTRL('b')      , CMD(BACKWARD)    ),
144     MAP(CTRL('f')      , CMD(FORWARD)     ),
145     MAP(CTRL('p')      , CMD(PREV_LINE)   ),
146     MAP(CTRL('n')      , CMD(NEXT_LINE)   ),
147     MAP(CTRL('d')      , CMD(ERASE_AT)    ),
148     MAP(    DEL        , CMD(ERASE_BEFORE)),
149     MAP(CTRL('a')      , CMD(LINE_START)  ),
150     MAP(CTRL('e')      , CMD(LINE_END)    ),
151     MAP(CTRL('k')      , CMD(ERASE_REST)  ),
152     MAP(CTRL('u')      , CMD(ERASE_ALL)   ),
153     MAP(CTRL('y')      , CMD(YANK)        ),
154     MAP(CTRL('m')      , CMD(ENTER)       ),
155     MAP(CTRL('l')      , CMD(REDRAW)      ),
156     MAP(CTRL('r')      , CMD(SEARCH_BACK) ),
157     MAP(CTRL('s')      , CMD(SEARCH_FORW) ),
158     MAP(    ESC        , BRL_TYPE_CSEQ    ),
159 };
160
161 static extmap_t ext_map[] = {
162     { "\x1b[A"   , 3, CMD(PREV_LINE)   },
163     { "\x1b[B"   , 3, CMD(NEXT_LINE)   },
164     { "\x1b[C"   , 3, CMD(FORWARD)     },
165     { "\x1b[D"   , 3, CMD(BACKWARD)    },
166     { "\x1b[F"   , 3, CMD(LINE_END)    },
167     { "\x1b[H"   , 3, CMD(LINE_START)  },
168     { "\x1b[1;5A", 6, BRL_TYPE_INVALID },
169     { "\x1b[1;5B", 6, BRL_TYPE_INVALID },
170     { "\x1b[1;5C", 6, CMD(NEXT_WORD)   },
171     { "\x1b[1;5D", 6, CMD(PREV_WORD)   },
172     { NULL       , 0, BRL_TYPE_INVALID }
173 };
174
175
176 /*
177  * ring buffer for saving history
178  */
179
180 typedef struct {
181     char **entries;                      /* ring buffer entries */
182     int    size;                         /* buffer size */
183     int    next;                         /* buffer insertion point */
184     int    srch;                         /* buffer search point */
185     char  *pattern;                      /* search pattern buffer */
186     int    psize;                        /* pattern buffer size */
187     int    plen;                         /* actual pattern length */
188 } ringbuf_t;
189
190
191
192 /*
193  * breedline context
194  */
195
196 struct brl_s {
197     int                 fd;              /* file descriptor to read */
198     struct termios      term_mode;       /* original terminal settings */
199     int                 term_flags;      /* terminal descriptor flags */
200     int                 term_blck;       /* originally in blocking mode */
201     int                 term_ncol;       /* number of columns */
202     void               *ml;              /* mainloop, if any */
203     brl_mainloop_ops_t *ml_ops;          /* mainloop operations, if any */
204     void               *ml_w;            /* I/O watch */
205     brl_line_cb_t       line_cb;         /* input callback */
206     void               *user_data;       /* opaque callback data */
207     char               *prompt;          /* prompt string */
208     int                 hidden;          /* whether prompt is hidden */
209     int                 mode;            /* current mode */
210     unsigned char      *buf;             /* input buffer */
211     int                 size;            /* size of the buffer */
212     int                 data;            /* amount of data in buffer */
213     int                 offs;            /* buffer insertion offset */
214     unsigned char      *yank;            /* yank buffer */
215     int                 yank_size;       /* yank buffer size */
216     int                 yank_data;       /* yank buffer effective length */
217     int                *map;             /* key map */
218     extmap_t           *ext;             /* extended key map */
219     int                 esc;             /* CS being collected */
220     unsigned char       seq[8];          /* control sequence buffer */
221     int                 seq_len;         /* sequence length */
222     ringbuf_t           h;               /* history ring buffer */
223     char               *saved;           /* input buffer saved during search */
224     int                 dump;            /* whether to dump key input */
225     char               *dbg_buf;         /* debug buffer */
226     int                 dbg_size;        /* debug buffer size */
227     int                 dbg_len;         /* debug buffer effective length */
228 };
229
230
231 static int setup_terminal(brl_t *brl);
232 static int cleanup_terminal(brl_t *brl);
233 static int terminal_size(int fd, int *nrow, int *ncol);
234 static int enable_rawmode(brl_t *brl);
235 static int restore_rawmode(brl_t *brl);
236 static int disable_blocking(brl_t *brl);
237 static int restore_blocking(brl_t *brl);
238
239 static void process_input(brl_t *brl);
240 static void reset_input(brl_t *brl);
241 static void dump_input(brl_t *brl);
242 static void redraw_prompt(brl_t *brl);
243
244 static int ringbuf_init(ringbuf_t *rb, int size);
245 static void ringbuf_purge(ringbuf_t *rb);
246
247 static void *_brl_default_alloc(size_t size, const char *file, int line,
248                                 const char *func);
249 static void *_brl_default_realloc(void *ptr, size_t size,
250                                   const char *file, int line, const char *func);
251 static char *_brl_default_strdup(const char *str, const char *file, int line,
252                                  const char *func);
253 static void _brl_default_free(void *ptr,
254                               const char *file, int line, const char *func);
255
256
257
258 brl_t *brl_create(int fd, const char *prompt)
259 {
260     static int  dump = -1, dbg_size = -1;
261     brl_t      *brl;
262
263     if (dump < 0) {
264         const char *val = getenv("__BREEDLINE_DUMP_KEYS");
265         dump = (val != NULL && (*val == 'y' || *val == 'Y'));
266     }
267
268     if (dbg_size < 0) {
269         const char *val = getenv("__BREEDLINE_DEBUG");
270         dbg_size = (val == NULL ? 0 : atoi(val));
271     }
272
273     brl = brl_allocz(sizeof(*brl));
274
275     if (brl != NULL) {
276         brl->fd     = fd;
277         brl->map    = key_map;
278         brl->ext    = ext_map;
279         brl->prompt = brl_strdup(prompt ? prompt : "");
280
281         brl->dump     = dump;
282         brl->dbg_size = dbg_size;
283         if (dbg_size > 0)
284             brl->dbg_buf = brl_allocz(dbg_size);
285
286         brl_limit_history(brl, BRL_DEFAULT_HISTORY);
287
288         if (!setup_terminal(brl) && !terminal_size(fd, NULL, &brl->term_ncol))
289             return brl;
290         else
291             brl_destroy(brl);
292     }
293
294     return NULL;
295 }
296
297
298 void brl_destroy(brl_t *brl)
299 {
300     if (brl != NULL) {
301         brl_hide_prompt(brl);
302         brl_free(brl->prompt);
303         brl_free(brl->buf);
304         brl_free(brl->saved);
305         brl_free(brl->yank);
306         brl_free(brl->dbg_buf);
307         ringbuf_purge(&brl->h);
308         cleanup_terminal(brl);
309
310         brl_free(brl);
311     }
312 }
313
314
315 int brl_set_prompt(brl_t *brl, const char *prompt)
316 {
317     brl_free(brl->prompt);
318     brl->prompt = brl_strdup(prompt);
319
320     return (brl->prompt != NULL || prompt == NULL);
321 }
322
323
324 void brl_hide_prompt(brl_t *brl)
325 {
326     static int warned = 0;
327     char buf[32];
328     int  n, o;
329
330     brl->hidden = TRUE;
331
332     n = snprintf(buf, sizeof(buf), "%s%s", BRL_CURSOR_START, BRL_ERASE_RIGHT);
333     o = write(brl->fd, buf, n);
334     restore_rawmode(brl);
335
336     if (BRL_UNLIKELY(o < 0 && !warned)) {           /* make gcc happy */
337         fprintf(stderr, "write to fd %d failed\n", brl->fd);
338         warned = 1;
339     }
340 }
341
342
343 void brl_show_prompt(brl_t *brl)
344 {
345     brl->hidden = FALSE;
346     enable_rawmode(brl);
347     redraw_prompt(brl);
348 }
349
350
351 static void debug(brl_t *brl, const char *fmt, ...)
352 {
353     va_list ap;
354     int     n;
355
356     va_start(ap, fmt);
357     n = vsnprintf(brl->dbg_buf, brl->dbg_size, fmt, ap);
358     va_end(ap);
359
360     if (n > 0)
361         brl->dbg_len = n < brl->dbg_size ? n : brl->dbg_size;
362     else
363         brl->dbg_len = 0;
364 }
365
366
367 static int ringbuf_init(ringbuf_t *rb, int size)
368 {
369     int i;
370
371     for (i = 0; i < rb->size; i++)
372         brl_free(rb->entries[i]);
373
374     brl_free(rb->entries);
375     rb->entries = NULL;
376     rb->size    = 0;
377     rb->next    = 0;
378     rb->srch    = 0;
379
380     brl_free(rb->pattern);
381     rb->pattern = NULL;
382     rb->psize   = 0;
383     rb->plen    = 0;
384
385     rb->entries = brl_allocz(size * sizeof(rb->entries[0]));
386
387     if (rb->entries == NULL && size != 0)
388         return -1;
389
390     rb->size = size;
391
392     return 0;
393 }
394
395
396 static void ringbuf_purge(ringbuf_t *rb)
397 {
398     ringbuf_init(rb, 0);
399 }
400
401
402 static char **ringbuf_entry(ringbuf_t *rb, int idx)
403 {
404     int i;
405
406     if (rb->entries == NULL) {
407         errno = ENOSPC;
408         return NULL;
409     }
410
411     if (idx >= rb->size) {
412         errno = EOVERFLOW;
413         return NULL;
414     }
415
416     if (idx < 0 && -idx > rb->size) {
417         errno = EOVERFLOW;
418         return NULL;
419     }
420
421     if (idx == 0)
422         return rb->entries + rb->next;
423
424     if (idx < 0) {
425         i = rb->next + idx;
426
427         if (i < 0)
428             i += rb->size;
429
430         return rb->entries + i;
431     }
432     else {
433         i = rb->next - 1 + idx;
434
435         if (i >= rb->size)
436             i -= rb->size;
437
438         return rb->entries + i;
439     }
440 }
441
442
443 static int ringbuf_add(ringbuf_t *rb, const char *str)
444 {
445     char **entry = ringbuf_entry(rb, 0);
446
447     if (entry != NULL) {
448         brl_free(*entry);
449         *entry   = brl_strdup(str);
450         rb->next = (rb->next + 1) % rb->size;
451
452         return 0;
453     }
454     else
455         return -1;
456 }
457
458
459 static void ringbuf_reset_search(ringbuf_t *rb)
460 {
461     rb->srch = 0;
462     rb->plen = 0;
463     memset(rb->pattern, 0, rb->psize);
464 }
465
466
467 static char *ringbuf_search(ringbuf_t *rb, int dir, unsigned char c,
468                             brl_mode_t mode, char *current)
469 {
470     int    i;
471     char **e;
472
473     if (!c && mode == BRL_MODE_NORMAL) {
474         i = rb->srch + (dir < 0 ? -1 : +1);
475
476         if (i > 0)
477             return NULL;
478
479         if (i == 0) {
480             rb->srch = 0;
481             return current;
482         }
483
484         e = ringbuf_entry(rb, i);
485
486         if (e != NULL && *e != NULL) {
487             rb->srch = i;
488             return *e;
489         }
490         else
491             return NULL;
492     }
493     else if (mode == BRL_MODE_SEARCH_BACK) {
494         int total = rb->plen + 1;
495         int found = 0;
496
497         if (c) {
498             if (rb->psize == 0) {
499                 rb->psize = 32;
500                 if (!(rb->pattern = brl_allocz(rb->psize))) {
501                     errno = ENOMEM;
502                     return NULL;
503                 }
504             }
505
506             if (rb->psize < total) {
507                 if (rb->psize * 2 > total)
508                     total = rb->psize * 2;
509                 if (!brl_reallocz(rb->pattern, rb->psize, total)) {
510                     errno = ENOMEM;
511                     return NULL;
512                 }
513                 rb->psize = total;
514             }
515
516             rb->pattern[rb->plen++] = c;
517
518             i = rb->srch;
519         }
520         else {
521             /* keep searching backwards */
522             i = rb->srch - 1;
523         }
524
525         if (!rb->pattern) {
526             errno = EINVAL;
527             return NULL;
528         }
529
530         /* start searching backwards from current search point */
531
532         do {
533             e = ringbuf_entry(rb, i);
534
535             if (e != NULL && *e != NULL) {
536                 /* pattern matching */
537                 if (strstr(*e, rb->pattern)) {
538                     found = 1;
539                     break;
540                 }
541             }
542
543             i--;
544         } while (e != NULL && !found);
545
546         if (found) {
547             /* set the search position while we're in search mode */
548            rb->srch = i;
549            return *e;
550         }
551
552         errno = ENOENT;
553         return NULL;
554     }
555
556     errno = EOPNOTSUPP;
557     return NULL;
558 }
559
560
561 int brl_limit_history(brl_t *brl, size_t size)
562 {
563     return ringbuf_init(&brl->h, size);
564 }
565
566
567 int brl_add_history(brl_t *brl, const char *str)
568 {
569     return ringbuf_add(&brl->h, str);
570 }
571
572
573 static int _brl_process_input(brl_t *brl)
574 {
575     if (brl->dump)
576         dump_input(brl);
577     else
578         process_input(brl);
579
580     return 0;
581 }
582
583
584 int brl_read_line(brl_t *brl, char *buf, size_t size)
585 {
586     if (brl->ml == NULL && brl->ml_ops == NULL) {
587         reset_input(brl);
588         enable_rawmode(brl);
589         brl_show_prompt(brl);
590         redraw_prompt(brl);
591         _brl_process_input(brl);
592
593         if (brl->data > 0)
594             snprintf(buf, size, "%s", brl->buf);
595         else
596             buf[0] = '\0';
597
598         brl_hide_prompt(brl);
599         restore_rawmode(brl);
600
601         return brl->data;
602     }
603     else {
604         errno = EINPROGRESS;
605         return -1;
606     }
607 }
608
609
610 static void _brl_io_cb(int fd, int events, void *user_data)
611 {
612     brl_t *brl = (brl_t *)user_data;
613
614     BRL_UNUSED(fd);
615
616     if (events & POLLIN)
617         _brl_process_input(brl);
618
619     if (events & POLLHUP) {
620         brl->ml_ops->del_watch(brl->ml_w);
621         brl->ml_w = NULL;
622     }
623 }
624
625
626 int brl_use_mainloop(brl_t *brl, void *ml, brl_mainloop_ops_t *ops,
627                      brl_line_cb_t cb, void *user_data)
628 {
629     if (brl != NULL) {
630         if (brl->ml != NULL || brl->ml_ops != NULL) {
631             errno = EBUSY;
632             return -1;
633     }
634
635         brl->line_cb   = cb;
636         brl->user_data = user_data;
637
638         brl->ml     = ml;
639         brl->ml_ops = ops;
640         brl->ml_w   = brl->ml_ops->add_watch(ml, brl->fd, _brl_io_cb, brl);
641
642         if (brl->ml_w != NULL) {
643             disable_blocking(brl);
644             return 0;
645         }
646         else
647             errno = EIO;
648     }
649     else
650         errno = EFAULT;
651
652     return -1;
653 }
654
655
656 static int enable_rawmode(brl_t *brl)
657 {
658     struct termios mode;
659
660     memcpy(&mode, &brl->term_mode, sizeof(mode));
661
662     mode.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
663     mode.c_oflag &= ~OPOST;
664     mode.c_cflag |= CS8;
665     mode.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
666
667     mode.c_cc[VMIN]  = 1;
668     mode.c_cc[VTIME] = 0;
669
670     return tcsetattr(brl->fd, TCSAFLUSH, &mode);
671 }
672
673
674 static int restore_rawmode(brl_t *brl)
675 {
676     return tcsetattr(brl->fd, TCSAFLUSH, &brl->term_mode);
677 }
678
679
680 static int disable_blocking(brl_t *brl)
681 {
682     int flags;
683
684     if (brl->term_blck) {
685         brl->term_flags = fcntl(brl->fd, F_GETFL);
686
687         if (brl->term_flags != -1) {
688             flags = brl->term_flags | O_NONBLOCK;
689
690             if (fcntl(brl->fd, F_SETFL, flags) == 0) {
691                 brl->term_blck = FALSE;
692                 return 0;
693             }
694         }
695
696         return -1;
697     }
698
699     return 0;
700 }
701
702
703 static int restore_blocking(brl_t *brl)
704 {
705     if (!brl->term_blck) {
706         if (fcntl(brl->fd, F_SETFL, brl->term_flags) == 0) {
707             brl->term_blck = FALSE;
708             return 0;
709         }
710         else
711             return -1;
712     }
713
714     return 0;
715 }
716
717
718 static int setup_terminal(brl_t *brl)
719 {
720     if (!isatty(brl->fd)) {
721         errno = ENOTTY;
722         return -1;
723     }
724
725     if (tcgetattr(brl->fd, &brl->term_mode) == -1)
726         return -1;
727
728     if (enable_rawmode(brl) != 0)
729         return -1;
730
731     brl->term_flags = fcntl(brl->fd, F_GETFL);
732     brl->term_blck  = TRUE;
733
734 #if 0
735     if (disable_blocking(brl) == 0)
736         return 0;
737     else
738         return -1;
739 #else
740     return 0;
741 #endif
742 }
743
744
745 static int cleanup_terminal(brl_t *brl)
746 {
747     return (restore_rawmode(brl) | restore_blocking(brl));
748 }
749
750
751 static int terminal_size(int fd, int *nrow, int *ncol)
752 {
753     struct winsize ws;
754     int    col, row;
755
756     if (ioctl(fd, TIOCGWINSZ, &ws) == 0) {
757         row = (ws.ws_row > 0 ? ws.ws_row : 80);
758         col = (ws.ws_col > 0 ? ws.ws_col : 25);
759
760         if (nrow != NULL)
761             *nrow = row;
762         if (ncol != NULL)
763             *ncol = col;
764
765         return 0;
766     }
767     else
768         return -1;
769 }
770
771
772 static void redraw_prompt(brl_t *brl)
773 {
774     static int warned = 0;
775
776     char *prompt, *buf, *p;
777     int   plen, dlen, space, start, trunc;
778     int   l, n, o;
779     char  search_buf[256];
780
781     if (brl->mode == BRL_MODE_SEARCH_BACK) {
782         snprintf(search_buf, 256, "search backwards: '%s'",
783                 brl->h.pattern ? brl->h.pattern : "");
784         prompt = search_buf;
785     }
786     else {
787         prompt = brl->prompt ? brl->prompt : "";
788     }
789
790     plen   = strlen(prompt) + 2;            /* '> ' or '><' */
791
792     if (brl->dbg_len > 0)
793         plen += brl->dbg_len + 2;
794
795     space  = brl->term_ncol - 1 - plen - 1; /* - 1 for potential trailing > */
796
797     /* adjust start if the cursor would be past the right edge */
798     if (brl->offs >= space)
799         start = brl->offs - space;
800     else
801         start = 0;
802
803     /* truncate if there's more data than fits the screen */
804     dlen = brl->data - start;
805
806     if (dlen > space) {
807         dlen = space;
808         trunc = TRUE;
809     }
810     else
811         trunc = FALSE;
812
813     l   = plen + dlen + 64;
814     buf = alloca(l);
815     p   = buf;
816
817 #if 0
818     printf("\n\rplen = %d, dlen = %d, start = %d, buf = '%*.*s'\n\r",
819            plen, dlen, start, brl->data, brl->data, brl->buf);
820     printf("brl->offs = %d, effective offset = %d\n\r", brl->offs,
821            plen + brl->offs - start);
822 #endif
823
824     /* position cursor to beginning of line */
825     n  = snprintf(p, l, "%s", BRL_CURSOR_START);
826     p += n;
827     l -= n;
828
829     /* print prompt + visible portion of buffer */
830     n  = snprintf(p, l, "%s%s%s%s%s%*.*s%s", prompt,
831                   brl->dbg_len ? "[" : "",
832                   brl->dbg_len ? brl->dbg_buf : "",
833                   brl->dbg_len ? "]" : "",
834                   start > 0 ? "><" : "> ",
835                   dlen, dlen, brl->buf + start, trunc ? ">" : "");
836     p += n;
837     l -= n;
838
839     /* erase the rest of the line (ie. make sure there's no trailing junk) */
840     n  = snprintf(p, l, "%s", BRL_ERASE_RIGHT);
841     p += n;
842     l -= n;
843
844     /* okay, perform the actions collected so far */
845     o = write(brl->fd, buf, (p - buf));
846
847     l = plen + dlen + 64;
848     p = buf;
849
850     if (BRL_UNLIKELY(o < 0 && !warned)) {           /* make gcc happy */
851         fprintf(stderr, "write to fd %d failed\n", brl->fd);
852         warned = 1;
853     }
854
855     /* re-position cursor to the current insertion offset */
856     n  = snprintf(p, l, BRL_CURSOR_START""BRL_CURSOR_FORW,
857                   plen + brl->offs - start);
858     p += n;
859     l -= n;
860     o = write(brl->fd, buf, (p - buf));
861
862     if (BRL_UNLIKELY(o < 0 && !warned)) {           /* make gcc happy */
863         fprintf(stderr, "write to fd %d failed\n", brl->fd);
864         warned = 1;
865     }
866 }
867
868
869 /*
870  * input buffer handling
871  */
872
873 static void reset_input(brl_t *brl)
874 {
875     memset(brl->buf, 0, brl->size);
876     brl->data = 0;
877     brl->offs = 0;
878 }
879
880
881 static int insert_input(brl_t *brl, const char *input, int len)
882 {
883     int total;
884
885     total = brl->data + len + 1;
886
887     if (brl->size < total) {
888         if (brl->size * 2 > total)
889             total = brl->size * 2;
890         if (!brl_reallocz(brl->buf, brl->size, total))
891             return -1;
892         brl->size = total;
893     }
894
895     if (brl->offs < brl->data) {
896         memmove(brl->buf + brl->offs + len, brl->buf + brl->offs,
897                 brl->data - brl->offs);
898         memcpy(brl->buf + brl->offs, input, len);
899     }
900     else
901         memcpy(brl->buf + brl->offs, input, len);
902
903     brl->data += len;
904     brl->offs += len;
905     brl->buf[brl->data] = '\0';
906
907     return 0;
908 }
909
910
911 static int erase_input(brl_t *brl, int n)
912 {
913     if (n < 0) {
914         if (-n > brl->offs)
915             n = -brl->offs;
916         if (brl->offs < brl->data)
917             memmove(brl->buf + brl->offs + n, brl->buf + brl->offs,
918                     brl->data - brl->offs);
919         brl->data += n;
920         if (brl->offs > brl->data)
921             brl->offs = brl->data;
922     }
923     else {
924         if (n > brl->data - brl->offs)
925             n = brl->data - brl->offs;
926         memmove(brl->buf + brl->offs, brl->buf + brl->offs + n,
927                 brl->data - (brl->offs + n));
928         brl->data -= n;
929     }
930
931     return 0;
932 }
933
934
935 static void save_input(brl_t *brl)
936 {
937     brl_free(brl->saved);
938     brl->saved = brl_strdup((char *)brl->buf);
939 }
940
941
942 static void save_yank(brl_t *brl, int start, int end)
943 {
944     int size, len;
945
946     if (start < 0 || start >= brl->data || end > brl->data)
947         return;
948
949     len  = end - start + 1;
950     size = len + 1;
951
952     if (brl->yank_size < size) {
953         if (brl->yank_size * 2 > size)
954             size = brl->yank_size * 2;
955         if (!brl_reallocz(brl->yank, brl->yank_size, size))
956             return;
957     }
958
959     brl->yank_size = size;
960
961     memcpy(brl->yank, brl->buf + start, len);
962     brl->yank[len] = '\0';
963     brl->yank_data = len - 1;
964 }
965
966
967 static void restore_input(brl_t *brl)
968 {
969     reset_input(brl);
970
971     if (brl->saved != NULL) {
972         insert_input(brl, brl->saved, strlen(brl->saved));
973         brl_free(brl->saved);
974         brl->saved = NULL;
975     }
976 }
977
978
979 static int input_delimiter(brl_t *brl, int dir)
980 {
981     static const char  delim[] = " ,;:.?!'\"-_/";
982     char              *s, *p;
983
984     if (brl->data == 0)
985         return 0;
986
987     if ((dir < 0 && brl->offs == 0) || (dir >= 0 && brl->offs >= brl->data))
988         return 0;
989
990     s = (char *)brl->buf + brl->offs;
991
992     if (dir < 0) {
993         p = s - 1;
994         if (p > (char *)brl->buf && strchr(delim, *p) != NULL)
995             p--;
996         while (p >= (char *)brl->buf) {
997             if (strchr(delim, *p) != NULL) {
998                 p   += 1;
999                 break;
1000             }
1001             else
1002                 p--;
1003         }
1004         return p - s;
1005     }
1006     else {
1007         p = s;
1008         if (strchr(delim, *p) != NULL && s < (char *)brl->buf + brl->data)
1009             p++;
1010         while (p < (char *)brl->buf + brl->data) {
1011             if (strchr(delim, *p) != NULL)
1012                 break;
1013             else
1014                 p++;
1015         }
1016         return p - s;
1017     }
1018
1019     return 0;
1020 }
1021
1022
1023 static void move_cursor(brl_t *brl, int n)
1024 {
1025     brl->offs += n;
1026
1027     if (brl->offs < 0)
1028         brl->offs = 0;
1029     if (brl->offs > brl->data)
1030         brl->offs = brl->data;
1031 }
1032
1033
1034 static void bell(brl_t *brl)
1035 {
1036     int fd;
1037
1038     if (brl->fd == fileno(stdin))
1039         fd = fileno(stderr);
1040     else
1041         fd = brl->fd;
1042
1043     dprintf(fd, "%c", BELL);
1044 }
1045
1046
1047 /*
1048  * input mapping
1049  */
1050
1051 static int map_input(brl_t *brl, unsigned char c)
1052 {
1053     int mapped;
1054
1055     mapped = brl->map[c];
1056
1057     if (mapped == BRL_TYPE_SELF)
1058         return BRL_TAG_INPUT(BRL_TYPE_SELF, c);
1059     else
1060         return mapped;
1061 }
1062
1063
1064 static int map_esc_sequence(brl_t *brl)
1065 {
1066     BRL_UNUSED(brl);
1067
1068     return BRL_TYPE_INVALID;
1069 }
1070
1071
1072 static int map_ctrl_sequence(brl_t *brl)
1073 {
1074     extmap_t *e;
1075     int       d;
1076
1077     if (brl->ext != NULL) {
1078         for (e = brl->ext; e->seq != NULL; e++) {
1079             if (e->len == brl->seq_len) {
1080                 d = strncmp((char *)brl->seq, e->seq, e->len);
1081
1082                 if (d == 0)
1083                     return e->key;
1084
1085                 if (d < 0)
1086                     break;
1087             }
1088
1089             if (e->len > brl->seq_len)
1090                 break;
1091         }
1092     }
1093
1094     return BRL_TYPE_INVALID;
1095 }
1096
1097
1098 /*
1099  * main input processing
1100  */
1101
1102 static void process_input(brl_t *brl)
1103 {
1104     unsigned char c;
1105     int           mapped, type, in, n, diff;
1106     char          out, *line, *hentry;
1107
1108     while((n = read(brl->fd, &c, sizeof(c))) > 0) {
1109         if (brl->esc) {
1110             if (brl->seq_len < (int)sizeof(brl->seq))
1111                 brl->seq[brl->seq_len++] = c;
1112
1113             if (brl->seq_len == 2) {
1114                 if (c != '[') {
1115                     mapped = map_esc_sequence(brl);
1116                     brl->esc = FALSE;
1117                 }
1118                 else
1119                     continue;
1120             }
1121             else {
1122                 if (0x40 <= c && c <= 0x7e) {
1123                     mapped = map_ctrl_sequence(brl);
1124                     brl->esc = FALSE;
1125                 }
1126                 else {
1127                     if (brl->seq_len == (int)sizeof(brl->seq)) {
1128                         mapped = BRL_TYPE_INVALID;
1129                         brl->esc = FALSE;
1130                     }
1131                     else
1132                         continue;
1133                 }
1134             }
1135         }
1136         else
1137             mapped = map_input(brl, c);
1138
1139         type = BRL_INPUT_TYPE(mapped);
1140         in   = BRL_INPUT_DATA(mapped);
1141
1142         switch (type) {
1143         case BRL_TYPE_SELF:
1144             switch (brl->mode) {
1145                 case BRL_MODE_NORMAL:
1146                     out = (char)(in & 0xff);
1147                     insert_input(brl, &out, 1);
1148                     redraw_prompt(brl);
1149                     break;
1150                 case BRL_MODE_SEARCH_BACK:
1151                     out = (char)(in & 0xff);
1152                     hentry = ringbuf_search(&brl->h, 0, out, BRL_MODE_SEARCH_BACK, NULL);
1153                     if (hentry != NULL) {
1154                         reset_input(brl);
1155                         insert_input(brl, hentry, strlen(hentry));
1156                     }
1157                     else
1158                         bell(brl);
1159                     redraw_prompt(brl);
1160                     break;
1161                 case BRL_MODE_SEARCH_FORW:
1162                     /* TODO */
1163                     break;
1164             }
1165             break;
1166
1167         case BRL_TYPE_COMMAND:
1168             switch (in) {
1169             case BRL_CMD_PREV_LINE:
1170                 if (brl->mode != BRL_MODE_NORMAL) {
1171                     ringbuf_reset_search(&brl->h);
1172                     brl->mode = BRL_MODE_NORMAL;
1173                 }
1174                 if (brl->h.srch == 0)
1175                     save_input(brl);
1176                 hentry = ringbuf_search(&brl->h, -1, 0, BRL_MODE_NORMAL, (char *)brl->saved);
1177                 debug(brl, "s:%d,'%s'", brl->h.srch,
1178                       brl->saved ? brl->saved : "-");
1179                 if (hentry != NULL) {
1180                     reset_input(brl);
1181                     insert_input(brl, hentry, strlen(hentry));
1182                     redraw_prompt(brl);
1183                 }
1184                 else
1185                     bell(brl);
1186                 break;
1187
1188             case BRL_CMD_NEXT_LINE:
1189                 if (brl->mode != BRL_MODE_NORMAL) {
1190                     ringbuf_reset_search(&brl->h);
1191                     brl->mode = BRL_MODE_NORMAL;
1192                 }
1193                 hentry = ringbuf_search(&brl->h, +1, 0, BRL_MODE_NORMAL, (char *)brl->saved);
1194                 debug(brl, "s:%d,'%s'", brl->h.srch,
1195                       brl->saved ? brl->saved : "-");
1196                 if (hentry != NULL) {
1197                     if (hentry == brl->saved)
1198                         restore_input(brl);
1199                     else {
1200                         reset_input(brl);
1201                         insert_input(brl, hentry, strlen(hentry));
1202                     }
1203                     redraw_prompt(brl);
1204                 }
1205                 else
1206                     bell(brl);
1207                 break;
1208
1209             case BRL_CMD_SEARCH_BACK:
1210                 if (brl->mode == BRL_MODE_SEARCH_BACK) {
1211                     /* already in search mode, continue */
1212                     hentry = ringbuf_search(&brl->h, 0, 0, BRL_MODE_SEARCH_BACK, NULL);
1213                     if (hentry != NULL) {
1214                         reset_input(brl);
1215                         insert_input(brl, hentry, strlen(hentry));
1216                     }
1217                     else
1218                         bell(brl);
1219                 }
1220                 else {
1221                     if (brl->h.srch == 0)
1222                         save_input(brl);
1223                     brl->mode = BRL_MODE_SEARCH_BACK;
1224                 }
1225                 redraw_prompt(brl);
1226                 break;
1227
1228             case BRL_CMD_BACKWARD:
1229                 if (brl->mode != BRL_MODE_NORMAL) {
1230                     ringbuf_reset_search(&brl->h);
1231                     brl->mode = BRL_MODE_NORMAL;
1232                 }
1233                 move_cursor(brl, -1);
1234                 redraw_prompt(brl);
1235                 break;
1236             case BRL_CMD_FORWARD:
1237                 if (brl->mode != BRL_MODE_NORMAL) {
1238                     ringbuf_reset_search(&brl->h);
1239                     brl->mode = BRL_MODE_NORMAL;
1240                 }
1241                 move_cursor(brl, +1);
1242                 redraw_prompt(brl);
1243                 break;
1244
1245             case BRL_CMD_LINE_START:
1246                 if (brl->mode != BRL_MODE_NORMAL) {
1247                     ringbuf_reset_search(&brl->h);
1248                     brl->mode = BRL_MODE_NORMAL;
1249                 }
1250                 move_cursor(brl, -brl->offs);
1251                 redraw_prompt(brl);
1252                 break;
1253             case BRL_CMD_LINE_END:
1254                 if (brl->mode != BRL_MODE_NORMAL) {
1255                     ringbuf_reset_search(&brl->h);
1256                     brl->mode = BRL_MODE_NORMAL;
1257                 }
1258                 move_cursor(brl, brl->data - brl->offs);
1259                 redraw_prompt(brl);
1260                 break;
1261
1262             case BRL_CMD_ERASE_BEFORE:
1263                 switch(brl->mode) {
1264                 case BRL_MODE_NORMAL:
1265                     erase_input(brl, -1);
1266                     if (brl->offs < brl->data)
1267                         move_cursor(brl, -1);
1268                     redraw_prompt(brl);
1269                     break;
1270                 case BRL_MODE_SEARCH_BACK:
1271                 case BRL_MODE_SEARCH_FORW:
1272                     if (brl->h.plen > 0) {
1273                         brl->h.pattern[--brl->h.plen] = '\0';
1274                     }
1275                     else {
1276                         ringbuf_reset_search(&brl->h);
1277                         brl->mode = BRL_MODE_NORMAL;
1278                         restore_input(brl);
1279                     }
1280                     redraw_prompt(brl);
1281                     break;
1282                 }
1283                 break;
1284             case BRL_CMD_ERASE_AT:
1285                 if (brl->mode != BRL_MODE_NORMAL) {
1286                     ringbuf_reset_search(&brl->h);
1287                     brl->mode = BRL_MODE_NORMAL;
1288                 }
1289                 erase_input(brl, 1);
1290                 redraw_prompt(brl);
1291                 break;
1292
1293             case BRL_CMD_ERASE_REST:
1294                 if (brl->mode != BRL_MODE_NORMAL) {
1295                     ringbuf_reset_search(&brl->h);
1296                     brl->mode = BRL_MODE_NORMAL;
1297                 }
1298                 save_yank(brl, brl->offs, brl->data);
1299                 erase_input(brl, brl->data - brl->offs);
1300                 redraw_prompt(brl);
1301                 break;
1302             case BRL_CMD_ERASE_ALL:
1303                 if (brl->mode != BRL_MODE_NORMAL) {
1304                     ringbuf_reset_search(&brl->h);
1305                     brl->mode = BRL_MODE_NORMAL;
1306                 }
1307                 save_yank(brl, 0, brl->data);
1308                 reset_input(brl);
1309                 redraw_prompt(brl);
1310                 break;
1311             case BRL_CMD_YANK:
1312                 if (brl->mode != BRL_MODE_NORMAL) {
1313                     ringbuf_reset_search(&brl->h);
1314                     brl->mode = BRL_MODE_NORMAL;
1315                 }
1316                 insert_input(brl, (char *)brl->yank, brl->yank_data);
1317                 redraw_prompt(brl);
1318                 break;
1319
1320             case BRL_CMD_PREV_WORD:
1321                 if (brl->mode != BRL_MODE_NORMAL) {
1322                     ringbuf_reset_search(&brl->h);
1323                     brl->mode = BRL_MODE_NORMAL;
1324                 }
1325                 diff = input_delimiter(brl, -1);
1326                 move_cursor(brl, diff);
1327                 redraw_prompt(brl);
1328                 break;
1329
1330             case BRL_CMD_NEXT_WORD:
1331                 if (brl->mode != BRL_MODE_NORMAL) {
1332                     ringbuf_reset_search(&brl->h);
1333                     brl->mode = BRL_MODE_NORMAL;
1334                 }
1335                 diff = input_delimiter(brl, +1);
1336                 move_cursor(brl, diff);
1337                 redraw_prompt(brl);
1338                 break;
1339
1340             case BRL_CMD_REDRAW:
1341                 redraw_prompt(brl);
1342                 break;
1343
1344             case BRL_CMD_ENTER:
1345                 dprintf(brl->fd, "\n\r");
1346                 if (brl->line_cb != NULL) {
1347                     line = alloca(brl->data + 1);
1348                     strncpy(line, (char *)brl->buf, brl->data);
1349                     line[brl->data] = '\0';
1350                     reset_input(brl);
1351                     restore_rawmode(brl);
1352                     brl->line_cb(brl, line, brl->user_data);
1353                     enable_rawmode(brl);
1354                     ringbuf_reset_search(&brl->h);
1355                     brl->mode = BRL_MODE_NORMAL;
1356                     debug(brl, "");
1357                     redraw_prompt(brl);
1358                 }
1359                 else
1360                     return;
1361                 break;
1362
1363             default:
1364 #if 0
1365                 printf("editing command 0x%x\n\r", in);
1366 #endif
1367                 bell(brl);
1368             }
1369             break;
1370
1371         case BRL_TYPE_CSEQ:
1372             brl->esc     = TRUE;
1373             brl->seq[0]  = c;
1374             brl->seq_len = 1;
1375             break;
1376
1377         case BRL_TYPE_INVALID:
1378         default:
1379             bell(brl);
1380             break;
1381         }
1382     }
1383 }
1384
1385
1386 static void dump_input(brl_t *brl)
1387 {
1388     unsigned char c, seq[64], s[4] = "  \0";
1389     int           i = 0;
1390
1391     printf("got input:");
1392
1393     while (read(brl->fd, &c, 1) == 1) {
1394         printf(" 0x%2.2x", c);
1395         seq[i++] = c;
1396
1397         if (c == 0x3)
1398             exit(0);
1399     }
1400
1401     printf("\n\r");
1402     seq[i] = '\0';
1403
1404     printf("          ");
1405     for (i = 0; seq[i] != 0; i++) {
1406         printf(" %4d", seq[i]);
1407     }
1408     printf("\n\r");
1409
1410     seq[i] = '\0';
1411     printf("          ");
1412     for (i = 0; seq[i] != '\0'; i++) {
1413         s[3] = c = seq[i];
1414         printf(" %s", (isprint(c) && c != '\n' && c != '\r' && c != '\t') ?
1415                (char *)s : (c == ESC ? "ESC" : "."));
1416     }
1417     printf("\n\r");
1418 }
1419
1420
1421 /*
1422  * default passthru allocator
1423  */
1424
1425 static void *_brl_default_alloc(size_t size, const char *file, int line,
1426                                 const char *func)
1427 {
1428     BRL_UNUSED(file);
1429     BRL_UNUSED(line);
1430     BRL_UNUSED(func);
1431
1432     return malloc(size);
1433 }
1434
1435 static void *_brl_default_realloc(void *ptr, size_t size,
1436                                   const char *file, int line, const char *func)
1437 {
1438     BRL_UNUSED(file);
1439     BRL_UNUSED(line);
1440     BRL_UNUSED(func);
1441
1442     return realloc(ptr, size);
1443 }
1444
1445 static char *_brl_default_strdup(const char *str, const char *file, int line,
1446                                  const char *func)
1447 {
1448     BRL_UNUSED(file);
1449     BRL_UNUSED(line);
1450     BRL_UNUSED(func);
1451
1452     return strdup(str);
1453 }
1454
1455 static void _brl_default_free(void *ptr,
1456                               const char *file, int line, const char *func)
1457 {
1458     BRL_UNUSED(file);
1459     BRL_UNUSED(line);
1460     BRL_UNUSED(func);
1461
1462     free(ptr);
1463 }
1464
1465
1466 /* By default we use the libc memory allocator. */
1467 brl_allocator_t __brl_mm = {
1468     .allocfn   = _brl_default_alloc,
1469     .reallocfn = _brl_default_realloc,
1470     .strdupfn  = _brl_default_strdup,
1471     .freefn    = _brl_default_free
1472 };
1473
1474 /* Once an allocation is done, this will block changing the allocator. */
1475 int __brl_mm_busy = FALSE;
1476
1477
1478 int brl_set_allocator(brl_allocator_t *allocator)
1479 {
1480     if (!__brl_mm_busy) {
1481         __brl_mm = *allocator;
1482
1483         return 0;
1484     }
1485     else {
1486         errno = EBUSY;
1487
1488         return -1;
1489     }
1490 }