2 * Copyright (c) 2012, Intel Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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.
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.
37 #include <sys/ioctl.h>
40 #include "breedline/breedline.h"
41 #include "breedline/mm.h"
45 # define TRUE (!FALSE)
48 #define BRL_UNUSED(var) (void)var
51 # define BRL_UNLIKELY(cond) __builtin_expect((cond), 0)
53 # define BRL_UNLIKELY(cond) __builtin_expect((cond), 0)
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 */
66 BRL_MODE_NORMAL, /* normal editing mode */
67 BRL_MODE_SEARCH_FORW, /* searching forward */
68 BRL_MODE_SEARCH_BACK, /* searching backward */
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 */
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)
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 */
117 * breedline extended control sequences
121 const char *seq; /* control sequence */
122 int len; /* sequence length */
123 int key; /* mapped to this key */
132 #define CTRL(code) ((code) & 0x1f)
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)
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 ),
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 }
177 * ring buffer for saving history
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 */
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 */
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);
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);
244 static int ringbuf_init(ringbuf_t *rb, int size);
245 static void ringbuf_purge(ringbuf_t *rb);
247 static void *_brl_default_alloc(size_t size, const char *file, int line,
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,
253 static void _brl_default_free(void *ptr,
254 const char *file, int line, const char *func);
258 brl_t *brl_create(int fd, const char *prompt)
260 static int dump = -1, dbg_size = -1;
264 const char *val = getenv("__BREEDLINE_DUMP_KEYS");
265 dump = (val != NULL && (*val == 'y' || *val == 'Y'));
269 const char *val = getenv("__BREEDLINE_DEBUG");
270 dbg_size = (val == NULL ? 0 : atoi(val));
273 brl = brl_allocz(sizeof(*brl));
279 brl->prompt = brl_strdup(prompt ? prompt : "");
282 brl->dbg_size = dbg_size;
284 brl->dbg_buf = brl_allocz(dbg_size);
286 brl_limit_history(brl, BRL_DEFAULT_HISTORY);
288 if (!setup_terminal(brl) && !terminal_size(fd, NULL, &brl->term_ncol))
298 void brl_destroy(brl_t *brl)
301 brl_hide_prompt(brl);
302 brl_free(brl->prompt);
304 brl_free(brl->saved);
306 brl_free(brl->dbg_buf);
307 ringbuf_purge(&brl->h);
308 cleanup_terminal(brl);
315 int brl_set_prompt(brl_t *brl, const char *prompt)
317 brl_free(brl->prompt);
318 brl->prompt = brl_strdup(prompt);
320 return (brl->prompt != NULL || prompt == NULL);
324 void brl_hide_prompt(brl_t *brl)
326 static int warned = 0;
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);
336 if (BRL_UNLIKELY(o < 0 && !warned)) { /* make gcc happy */
337 fprintf(stderr, "write to fd %d failed\n", brl->fd);
343 void brl_show_prompt(brl_t *brl)
351 static void debug(brl_t *brl, const char *fmt, ...)
357 n = vsnprintf(brl->dbg_buf, brl->dbg_size, fmt, ap);
361 brl->dbg_len = n < brl->dbg_size ? n : brl->dbg_size;
367 static int ringbuf_init(ringbuf_t *rb, int size)
371 for (i = 0; i < rb->size; i++)
372 brl_free(rb->entries[i]);
374 brl_free(rb->entries);
380 brl_free(rb->pattern);
385 rb->entries = brl_allocz(size * sizeof(rb->entries[0]));
387 if (rb->entries == NULL && size != 0)
396 static void ringbuf_purge(ringbuf_t *rb)
402 static char **ringbuf_entry(ringbuf_t *rb, int idx)
406 if (rb->entries == NULL) {
411 if (idx >= rb->size) {
416 if (idx < 0 && -idx > rb->size) {
422 return rb->entries + rb->next;
430 return rb->entries + i;
433 i = rb->next - 1 + idx;
438 return rb->entries + i;
443 static int ringbuf_add(ringbuf_t *rb, const char *str)
445 char **entry = ringbuf_entry(rb, 0);
449 *entry = brl_strdup(str);
450 rb->next = (rb->next + 1) % rb->size;
459 static void ringbuf_reset_search(ringbuf_t *rb)
463 memset(rb->pattern, 0, rb->psize);
467 static char *ringbuf_search(ringbuf_t *rb, int dir, unsigned char c,
468 brl_mode_t mode, char *current)
473 if (!c && mode == BRL_MODE_NORMAL) {
474 i = rb->srch + (dir < 0 ? -1 : +1);
484 e = ringbuf_entry(rb, i);
486 if (e != NULL && *e != NULL) {
493 else if (mode == BRL_MODE_SEARCH_BACK) {
494 int total = rb->plen + 1;
498 if (rb->psize == 0) {
500 if (!(rb->pattern = brl_allocz(rb->psize))) {
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)) {
516 rb->pattern[rb->plen++] = c;
521 /* keep searching backwards */
530 /* start searching backwards from current search point */
533 e = ringbuf_entry(rb, i);
535 if (e != NULL && *e != NULL) {
536 /* pattern matching */
537 if (strstr(*e, rb->pattern)) {
544 } while (e != NULL && !found);
547 /* set the search position while we're in search mode */
561 int brl_limit_history(brl_t *brl, size_t size)
563 return ringbuf_init(&brl->h, size);
567 int brl_add_history(brl_t *brl, const char *str)
569 return ringbuf_add(&brl->h, str);
573 static int _brl_process_input(brl_t *brl)
584 int brl_read_line(brl_t *brl, char *buf, size_t size)
586 if (brl->ml == NULL && brl->ml_ops == NULL) {
589 brl_show_prompt(brl);
591 _brl_process_input(brl);
594 snprintf(buf, size, "%s", brl->buf);
598 brl_hide_prompt(brl);
599 restore_rawmode(brl);
610 static void _brl_io_cb(int fd, int events, void *user_data)
612 brl_t *brl = (brl_t *)user_data;
617 _brl_process_input(brl);
619 if (events & POLLHUP) {
620 brl->ml_ops->del_watch(brl->ml_w);
626 int brl_use_mainloop(brl_t *brl, void *ml, brl_mainloop_ops_t *ops,
627 brl_line_cb_t cb, void *user_data)
630 if (brl->ml != NULL || brl->ml_ops != NULL) {
636 brl->user_data = user_data;
640 brl->ml_w = brl->ml_ops->add_watch(ml, brl->fd, _brl_io_cb, brl);
642 if (brl->ml_w != NULL) {
643 disable_blocking(brl);
656 static int enable_rawmode(brl_t *brl)
660 memcpy(&mode, &brl->term_mode, sizeof(mode));
662 mode.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
663 mode.c_oflag &= ~OPOST;
665 mode.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
668 mode.c_cc[VTIME] = 0;
670 return tcsetattr(brl->fd, TCSAFLUSH, &mode);
674 static int restore_rawmode(brl_t *brl)
676 return tcsetattr(brl->fd, TCSAFLUSH, &brl->term_mode);
680 static int disable_blocking(brl_t *brl)
684 if (brl->term_blck) {
685 brl->term_flags = fcntl(brl->fd, F_GETFL);
687 if (brl->term_flags != -1) {
688 flags = brl->term_flags | O_NONBLOCK;
690 if (fcntl(brl->fd, F_SETFL, flags) == 0) {
691 brl->term_blck = FALSE;
703 static int restore_blocking(brl_t *brl)
705 if (!brl->term_blck) {
706 if (fcntl(brl->fd, F_SETFL, brl->term_flags) == 0) {
707 brl->term_blck = FALSE;
718 static int setup_terminal(brl_t *brl)
720 if (!isatty(brl->fd)) {
725 if (tcgetattr(brl->fd, &brl->term_mode) == -1)
728 if (enable_rawmode(brl) != 0)
731 brl->term_flags = fcntl(brl->fd, F_GETFL);
732 brl->term_blck = TRUE;
735 if (disable_blocking(brl) == 0)
745 static int cleanup_terminal(brl_t *brl)
747 return (restore_rawmode(brl) | restore_blocking(brl));
751 static int terminal_size(int fd, int *nrow, int *ncol)
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);
772 static void redraw_prompt(brl_t *brl)
774 static int warned = 0;
776 char *prompt, *buf, *p;
777 int plen, dlen, space, start, trunc;
779 char search_buf[256];
781 if (brl->mode == BRL_MODE_SEARCH_BACK) {
782 snprintf(search_buf, 256, "search backwards: '%s'",
783 brl->h.pattern ? brl->h.pattern : "");
787 prompt = brl->prompt ? brl->prompt : "";
790 plen = strlen(prompt) + 2; /* '> ' or '><' */
792 if (brl->dbg_len > 0)
793 plen += brl->dbg_len + 2;
795 space = brl->term_ncol - 1 - plen - 1; /* - 1 for potential trailing > */
797 /* adjust start if the cursor would be past the right edge */
798 if (brl->offs >= space)
799 start = brl->offs - space;
803 /* truncate if there's more data than fits the screen */
804 dlen = brl->data - start;
813 l = plen + dlen + 64;
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);
824 /* position cursor to beginning of line */
825 n = snprintf(p, l, "%s", BRL_CURSOR_START);
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 ? ">" : "");
839 /* erase the rest of the line (ie. make sure there's no trailing junk) */
840 n = snprintf(p, l, "%s", BRL_ERASE_RIGHT);
844 /* okay, perform the actions collected so far */
845 o = write(brl->fd, buf, (p - buf));
847 l = plen + dlen + 64;
850 if (BRL_UNLIKELY(o < 0 && !warned)) { /* make gcc happy */
851 fprintf(stderr, "write to fd %d failed\n", brl->fd);
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);
860 o = write(brl->fd, buf, (p - buf));
862 if (BRL_UNLIKELY(o < 0 && !warned)) { /* make gcc happy */
863 fprintf(stderr, "write to fd %d failed\n", brl->fd);
870 * input buffer handling
873 static void reset_input(brl_t *brl)
875 memset(brl->buf, 0, brl->size);
881 static int insert_input(brl_t *brl, const char *input, int len)
885 total = brl->data + len + 1;
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))
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);
901 memcpy(brl->buf + brl->offs, input, len);
905 brl->buf[brl->data] = '\0';
911 static int erase_input(brl_t *brl, int n)
916 if (brl->offs < brl->data)
917 memmove(brl->buf + brl->offs + n, brl->buf + brl->offs,
918 brl->data - brl->offs);
920 if (brl->offs > brl->data)
921 brl->offs = brl->data;
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));
935 static void save_input(brl_t *brl)
937 brl_free(brl->saved);
938 brl->saved = brl_strdup((char *)brl->buf);
942 static void save_yank(brl_t *brl, int start, int end)
946 if (start < 0 || start >= brl->data || end > brl->data)
949 len = end - start + 1;
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))
959 brl->yank_size = size;
961 memcpy(brl->yank, brl->buf + start, len);
962 brl->yank[len] = '\0';
963 brl->yank_data = len - 1;
967 static void restore_input(brl_t *brl)
971 if (brl->saved != NULL) {
972 insert_input(brl, brl->saved, strlen(brl->saved));
973 brl_free(brl->saved);
979 static int input_delimiter(brl_t *brl, int dir)
981 static const char delim[] = " ,;:.?!'\"-_/";
987 if ((dir < 0 && brl->offs == 0) || (dir >= 0 && brl->offs >= brl->data))
990 s = (char *)brl->buf + brl->offs;
994 if (p > (char *)brl->buf && strchr(delim, *p) != NULL)
996 while (p >= (char *)brl->buf) {
997 if (strchr(delim, *p) != NULL) {
1008 if (strchr(delim, *p) != NULL && s < (char *)brl->buf + brl->data)
1010 while (p < (char *)brl->buf + brl->data) {
1011 if (strchr(delim, *p) != NULL)
1023 static void move_cursor(brl_t *brl, int n)
1029 if (brl->offs > brl->data)
1030 brl->offs = brl->data;
1034 static void bell(brl_t *brl)
1038 if (brl->fd == fileno(stdin))
1039 fd = fileno(stderr);
1043 dprintf(fd, "%c", BELL);
1051 static int map_input(brl_t *brl, unsigned char c)
1055 mapped = brl->map[c];
1057 if (mapped == BRL_TYPE_SELF)
1058 return BRL_TAG_INPUT(BRL_TYPE_SELF, c);
1064 static int map_esc_sequence(brl_t *brl)
1068 return BRL_TYPE_INVALID;
1072 static int map_ctrl_sequence(brl_t *brl)
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);
1089 if (e->len > brl->seq_len)
1094 return BRL_TYPE_INVALID;
1099 * main input processing
1102 static void process_input(brl_t *brl)
1105 int mapped, type, in, n, diff;
1106 char out, *line, *hentry;
1108 while((n = read(brl->fd, &c, sizeof(c))) > 0) {
1110 if (brl->seq_len < (int)sizeof(brl->seq))
1111 brl->seq[brl->seq_len++] = c;
1113 if (brl->seq_len == 2) {
1115 mapped = map_esc_sequence(brl);
1122 if (0x40 <= c && c <= 0x7e) {
1123 mapped = map_ctrl_sequence(brl);
1127 if (brl->seq_len == (int)sizeof(brl->seq)) {
1128 mapped = BRL_TYPE_INVALID;
1137 mapped = map_input(brl, c);
1139 type = BRL_INPUT_TYPE(mapped);
1140 in = BRL_INPUT_DATA(mapped);
1144 switch (brl->mode) {
1145 case BRL_MODE_NORMAL:
1146 out = (char)(in & 0xff);
1147 insert_input(brl, &out, 1);
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) {
1155 insert_input(brl, hentry, strlen(hentry));
1161 case BRL_MODE_SEARCH_FORW:
1167 case BRL_TYPE_COMMAND:
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;
1174 if (brl->h.srch == 0)
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) {
1181 insert_input(brl, hentry, strlen(hentry));
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;
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)
1201 insert_input(brl, hentry, strlen(hentry));
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) {
1215 insert_input(brl, hentry, strlen(hentry));
1221 if (brl->h.srch == 0)
1223 brl->mode = BRL_MODE_SEARCH_BACK;
1228 case BRL_CMD_BACKWARD:
1229 if (brl->mode != BRL_MODE_NORMAL) {
1230 ringbuf_reset_search(&brl->h);
1231 brl->mode = BRL_MODE_NORMAL;
1233 move_cursor(brl, -1);
1236 case BRL_CMD_FORWARD:
1237 if (brl->mode != BRL_MODE_NORMAL) {
1238 ringbuf_reset_search(&brl->h);
1239 brl->mode = BRL_MODE_NORMAL;
1241 move_cursor(brl, +1);
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;
1250 move_cursor(brl, -brl->offs);
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;
1258 move_cursor(brl, brl->data - brl->offs);
1262 case BRL_CMD_ERASE_BEFORE:
1264 case BRL_MODE_NORMAL:
1265 erase_input(brl, -1);
1266 if (brl->offs < brl->data)
1267 move_cursor(brl, -1);
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';
1276 ringbuf_reset_search(&brl->h);
1277 brl->mode = BRL_MODE_NORMAL;
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;
1289 erase_input(brl, 1);
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;
1298 save_yank(brl, brl->offs, brl->data);
1299 erase_input(brl, brl->data - brl->offs);
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;
1307 save_yank(brl, 0, brl->data);
1312 if (brl->mode != BRL_MODE_NORMAL) {
1313 ringbuf_reset_search(&brl->h);
1314 brl->mode = BRL_MODE_NORMAL;
1316 insert_input(brl, (char *)brl->yank, brl->yank_data);
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;
1325 diff = input_delimiter(brl, -1);
1326 move_cursor(brl, diff);
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;
1335 diff = input_delimiter(brl, +1);
1336 move_cursor(brl, diff);
1340 case BRL_CMD_REDRAW:
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';
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;
1365 printf("editing command 0x%x\n\r", in);
1377 case BRL_TYPE_INVALID:
1386 static void dump_input(brl_t *brl)
1388 unsigned char c, seq[64], s[4] = " \0";
1391 printf("got input:");
1393 while (read(brl->fd, &c, 1) == 1) {
1394 printf(" 0x%2.2x", c);
1405 for (i = 0; seq[i] != 0; i++) {
1406 printf(" %4d", seq[i]);
1412 for (i = 0; seq[i] != '\0'; i++) {
1414 printf(" %s", (isprint(c) && c != '\n' && c != '\r' && c != '\t') ?
1415 (char *)s : (c == ESC ? "ESC" : "."));
1422 * default passthru allocator
1425 static void *_brl_default_alloc(size_t size, const char *file, int line,
1432 return malloc(size);
1435 static void *_brl_default_realloc(void *ptr, size_t size,
1436 const char *file, int line, const char *func)
1442 return realloc(ptr, size);
1445 static char *_brl_default_strdup(const char *str, const char *file, int line,
1455 static void _brl_default_free(void *ptr,
1456 const char *file, int line, const char *func)
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
1474 /* Once an allocation is done, this will block changing the allocator. */
1475 int __brl_mm_busy = FALSE;
1478 int brl_set_allocator(brl_allocator_t *allocator)
1480 if (!__brl_mm_busy) {
1481 __brl_mm = *allocator;