2 * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the University of California, Berkeley nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include "cmdline_cirbuf.h"
36 #include "cmdline_rdline.h"
38 static void rdline_puts(struct rdline *rdl, const char *buf);
39 static void rdline_miniprintf(struct rdline *rdl,
40 const char *buf, unsigned int val);
42 #ifndef NO_RDLINE_HISTORY
43 static void rdline_remove_old_history_item(struct rdline *rdl);
44 static void rdline_remove_first_history_item(struct rdline *rdl);
45 static unsigned int rdline_get_history_size(struct rdline *rdl);
46 #endif /* !NO_RDLINE_HISTORY */
49 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
60 void rdline_init(struct rdline *rdl,
61 rdline_write_char_t *write_char,
62 rdline_validate_t *validate,
63 rdline_complete_t *complete)
65 memset(rdl, 0, sizeof(*rdl));
66 rdl->validate = validate;
67 rdl->complete = complete;
68 rdl->write_char = write_char;
69 rdl->status = RDLINE_INIT;
70 #ifndef NO_RDLINE_HISTORY
71 cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
72 #endif /* !NO_RDLINE_HISTORY */
76 rdline_newline(struct rdline *rdl, const char *prompt)
80 vt100_init(&rdl->vt100);
81 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
82 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
84 if (prompt != rdl->prompt)
85 memcpy(rdl->prompt, prompt, sizeof(rdl->prompt)-1);
86 rdl->prompt_size = strlen(prompt);
88 for (i=0 ; i<rdl->prompt_size ; i++)
89 rdl->write_char(rdl, rdl->prompt[i]);
90 rdl->status = RDLINE_RUNNING;
92 #ifndef NO_RDLINE_HISTORY
93 rdl->history_cur_line = -1;
94 #endif /* !NO_RDLINE_HISTORY */
98 rdline_stop(struct rdline *rdl)
100 rdl->status = RDLINE_INIT;
104 rdline_restart(struct rdline *rdl)
106 rdl->status = RDLINE_RUNNING;
110 rdline_get_buffer(struct rdline *rdl)
112 unsigned int len_l, len_r;
113 cirbuf_align_left(&rdl->left);
114 cirbuf_align_left(&rdl->right);
116 len_l = CIRBUF_GET_LEN(&rdl->left);
117 len_r = CIRBUF_GET_LEN(&rdl->right);
118 memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
120 rdl->left_buf[len_l + len_r] = '\n';
121 rdl->left_buf[len_l + len_r + 1] = '\0';
122 return rdl->left_buf;
126 display_right_buffer(struct rdline *rdl)
131 rdline_puts(rdl, vt100_clear_right);
132 if (!CIRBUF_IS_EMPTY(&rdl->right)) {
133 CIRBUF_FOREACH(&rdl->right, i, tmp) {
134 rdl->write_char(rdl, tmp);
136 rdline_miniprintf(rdl, vt100_multi_left,
137 CIRBUF_GET_LEN(&rdl->right));
141 void rdline_redisplay(struct rdline *rdl)
146 rdline_puts(rdl, vt100_home);
147 for (i=0 ; i<rdl->prompt_size ; i++)
148 rdl->write_char(rdl, rdl->prompt[i]);
149 CIRBUF_FOREACH(&rdl->left, i, tmp) {
150 rdl->write_char(rdl, tmp);
152 display_right_buffer(rdl);
156 rdline_char_in(struct rdline *rdl, char c)
161 #ifndef NO_RDLINE_HISTORY
165 if (rdl->status != RDLINE_RUNNING)
168 cmd = vt100_parser(&rdl->vt100, c);
174 case CMDLINE_KEY_CTRL_B:
175 case CMDLINE_KEY_LEFT_ARR:
176 if (CIRBUF_IS_EMPTY(&rdl->left))
178 tmp = cirbuf_get_tail(&rdl->left);
179 cirbuf_del_tail(&rdl->left);
180 cirbuf_add_head(&rdl->right, tmp);
181 rdline_puts(rdl, vt100_left_arr);
184 case CMDLINE_KEY_CTRL_F:
185 case CMDLINE_KEY_RIGHT_ARR:
186 if (CIRBUF_IS_EMPTY(&rdl->right))
188 tmp = cirbuf_get_head(&rdl->right);
189 cirbuf_del_head(&rdl->right);
190 cirbuf_add_tail(&rdl->left, tmp);
191 rdline_puts(rdl, vt100_right_arr);
194 case CMDLINE_KEY_WLEFT:
195 while (! CIRBUF_IS_EMPTY(&rdl->left) &&
196 (tmp = cirbuf_get_tail(&rdl->left)) &&
198 rdline_puts(rdl, vt100_left_arr);
199 cirbuf_del_tail(&rdl->left);
200 cirbuf_add_head(&rdl->right, tmp);
202 while (! CIRBUF_IS_EMPTY(&rdl->left) &&
203 (tmp = cirbuf_get_tail(&rdl->left)) &&
205 rdline_puts(rdl, vt100_left_arr);
206 cirbuf_del_tail(&rdl->left);
207 cirbuf_add_head(&rdl->right, tmp);
211 case CMDLINE_KEY_WRIGHT:
212 while (! CIRBUF_IS_EMPTY(&rdl->right) &&
213 (tmp = cirbuf_get_head(&rdl->right)) &&
215 rdline_puts(rdl, vt100_right_arr);
216 cirbuf_del_head(&rdl->right);
217 cirbuf_add_tail(&rdl->left, tmp);
219 while (! CIRBUF_IS_EMPTY(&rdl->right) &&
220 (tmp = cirbuf_get_head(&rdl->right)) &&
222 rdline_puts(rdl, vt100_right_arr);
223 cirbuf_del_head(&rdl->right);
224 cirbuf_add_tail(&rdl->left, tmp);
228 case CMDLINE_KEY_BKSPACE:
229 if(!cirbuf_del_tail_safe(&rdl->left)) {
230 rdline_puts(rdl, vt100_bs);
231 display_right_buffer(rdl);
235 case CMDLINE_KEY_META_BKSPACE:
236 while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
237 rdline_puts(rdl, vt100_bs);
238 cirbuf_del_tail(&rdl->left);
240 while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
241 rdline_puts(rdl, vt100_bs);
242 cirbuf_del_tail(&rdl->left);
244 display_right_buffer(rdl);
247 case CMDLINE_KEY_SUPPR:
248 case CMDLINE_KEY_CTRL_D:
249 if (cmd == CMDLINE_KEY_CTRL_D &&
250 CIRBUF_IS_EMPTY(&rdl->left) &&
251 CIRBUF_IS_EMPTY(&rdl->right)) {
254 if (!cirbuf_del_head_safe(&rdl->right)) {
255 display_right_buffer(rdl);
259 case CMDLINE_KEY_CTRL_A:
260 if (CIRBUF_IS_EMPTY(&rdl->left))
262 rdline_miniprintf(rdl, vt100_multi_left,
263 CIRBUF_GET_LEN(&rdl->left));
264 while (! CIRBUF_IS_EMPTY(&rdl->left)) {
265 tmp = cirbuf_get_tail(&rdl->left);
266 cirbuf_del_tail(&rdl->left);
267 cirbuf_add_head(&rdl->right, tmp);
271 case CMDLINE_KEY_CTRL_E:
272 if (CIRBUF_IS_EMPTY(&rdl->right))
274 rdline_miniprintf(rdl, vt100_multi_right,
275 CIRBUF_GET_LEN(&rdl->right));
276 while (! CIRBUF_IS_EMPTY(&rdl->right)) {
277 tmp = cirbuf_get_head(&rdl->right);
278 cirbuf_del_head(&rdl->right);
279 cirbuf_add_tail(&rdl->left, tmp);
283 #ifndef NO_RDLINE_KILL_BUF
284 case CMDLINE_KEY_CTRL_K:
285 cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
286 rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
287 cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
288 rdline_puts(rdl, vt100_clear_right);
291 case CMDLINE_KEY_CTRL_Y:
293 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
295 i < rdl->kill_size) {
296 cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
297 rdl->write_char(rdl, rdl->kill_buf[i]);
300 display_right_buffer(rdl);
302 #endif /* !NO_RDLINE_KILL_BUF */
304 case CMDLINE_KEY_CTRL_C:
305 rdline_puts(rdl, "\r\n");
306 rdline_newline(rdl, rdl->prompt);
309 case CMDLINE_KEY_CTRL_L:
310 rdline_redisplay(rdl);
313 case CMDLINE_KEY_TAB:
314 case CMDLINE_KEY_HELP:
315 cirbuf_align_left(&rdl->left);
316 rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
318 char tmp_buf[BUFSIZ];
323 if (cmd == CMDLINE_KEY_TAB)
328 /* see in parse.h for help on complete() */
329 ret = rdl->complete(rdl, rdl->left_buf,
330 tmp_buf, sizeof(tmp_buf),
332 /* no completion or error */
337 tmp_size = strlen(tmp_buf);
341 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
344 cirbuf_add_tail(&rdl->left, tmp_buf[i]);
345 rdl->write_char(rdl, tmp_buf[i]);
348 display_right_buffer(rdl);
353 rdline_puts(rdl, "\r\n");
355 rdl->write_char(rdl, ' ');
356 for (i=0 ; tmp_buf[i] ; i++)
357 rdl->write_char(rdl, tmp_buf[i]);
358 rdline_puts(rdl, "\r\n");
359 ret = rdl->complete(rdl, rdl->left_buf,
360 tmp_buf, sizeof(tmp_buf),
364 rdline_redisplay(rdl);
368 case CMDLINE_KEY_RETURN:
369 case CMDLINE_KEY_RETURN2:
370 rdline_get_buffer(rdl);
371 rdl->status = RDLINE_INIT;
372 rdline_puts(rdl, "\r\n");
373 #ifndef NO_RDLINE_HISTORY
374 if (rdl->history_cur_line != -1)
375 rdline_remove_first_history_item(rdl);
379 rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
382 #ifndef NO_RDLINE_HISTORY
383 case CMDLINE_KEY_UP_ARR:
384 if (rdl->history_cur_line == 0) {
385 rdline_remove_first_history_item(rdl);
387 if (rdl->history_cur_line <= 0) {
388 rdline_add_history(rdl, rdline_get_buffer(rdl));
389 rdl->history_cur_line = 0;
392 buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
396 rdl->history_cur_line ++;
397 vt100_init(&rdl->vt100);
398 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
399 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
400 cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
401 rdline_redisplay(rdl);
404 case CMDLINE_KEY_DOWN_ARR:
405 if (rdl->history_cur_line - 1 < 0)
408 rdl->history_cur_line --;
409 buf = rdline_get_history_item(rdl, rdl->history_cur_line);
412 vt100_init(&rdl->vt100);
413 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
414 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
415 cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
416 rdline_redisplay(rdl);
419 #endif /* !NO_RDLINE_HISTORY */
433 if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
436 if (cirbuf_add_tail_safe(&rdl->left, c))
439 rdl->write_char(rdl, c);
440 display_right_buffer(rdl);
448 #ifndef NO_RDLINE_HISTORY
450 rdline_remove_old_history_item(struct rdline * rdl)
454 while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
455 tmp = cirbuf_get_head(&rdl->history);
456 cirbuf_del_head(&rdl->history);
463 rdline_remove_first_history_item(struct rdline * rdl)
467 if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
471 cirbuf_del_tail(&rdl->history);
474 while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
475 tmp = cirbuf_get_tail(&rdl->history);
478 cirbuf_del_tail(&rdl->history);
483 rdline_get_history_size(struct rdline * rdl)
485 unsigned int i, tmp, ret=0;
487 CIRBUF_FOREACH(&rdl->history, i, tmp) {
496 rdline_get_history_item(struct rdline * rdl, unsigned int idx)
498 unsigned int len, i, tmp;
500 len = rdline_get_history_size(rdl);
505 cirbuf_align_left(&rdl->history);
507 CIRBUF_FOREACH(&rdl->history, i, tmp) {
508 if ( idx == len - 1) {
509 return rdl->history_buf + i;
519 rdline_add_history(struct rdline * rdl, const char * buf)
524 for (i=0; i<len ; i++) {
525 if (buf[i] == '\n') {
531 if ( len >= RDLINE_HISTORY_BUF_SIZE )
534 while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
535 rdline_remove_old_history_item(rdl);
538 cirbuf_add_buf_tail(&rdl->history, buf, len);
539 cirbuf_add_tail(&rdl->history, 0);
545 rdline_clear_history(struct rdline * rdl)
547 cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
550 #else /* !NO_RDLINE_HISTORY */
552 int rdline_add_history(struct rdline * rdl, const char * buf) {return -1;}
553 void rdline_clear_history(struct rdline * rdl) {}
554 char * rdline_get_history_item(struct rdline * rdl, unsigned int i) {return NULL;}
557 #endif /* !NO_RDLINE_HISTORY */
560 /* STATIC USEFUL FUNCS */
563 rdline_puts(struct rdline * rdl, const char * buf)
566 while ( (c = *(buf++)) != '\0' ) {
567 rdl->write_char(rdl, c);
571 /* a very very basic printf with one arg and one format 'u' */
573 rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
575 char c, started=0, div=100;
577 while ( (c=*(buf++)) ) {
579 rdl->write_char(rdl, c);
584 rdl->write_char(rdl, '%');
585 rdl->write_char(rdl, c);
588 /* val is never more than 255 */
592 rdl->write_char(rdl, c+'0');