4 * Copyright(c) 2010-2013 Intel Corporation. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
37 * All rights reserved.
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions are met:
41 * * Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * * Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * * Neither the name of the University of California, Berkeley nor the
47 * names of its contributors may be used to endorse or promote products
48 * derived from this software without specific prior written permission.
50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
51 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
52 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
54 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
55 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
56 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
57 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
58 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
59 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
70 #include "cmdline_cirbuf.h"
71 #include "cmdline_rdline.h"
73 static void rdline_puts(struct rdline *rdl, const char *buf);
74 static void rdline_miniprintf(struct rdline *rdl,
75 const char *buf, unsigned int val);
77 static void rdline_remove_old_history_item(struct rdline *rdl);
78 static void rdline_remove_first_history_item(struct rdline *rdl);
79 static unsigned int rdline_get_history_size(struct rdline *rdl);
82 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
94 rdline_init(struct rdline *rdl,
95 rdline_write_char_t *write_char,
96 rdline_validate_t *validate,
97 rdline_complete_t *complete)
99 if (!rdl || !write_char || !validate || !complete)
101 memset(rdl, 0, sizeof(*rdl));
102 rdl->validate = validate;
103 rdl->complete = complete;
104 rdl->write_char = write_char;
105 rdl->status = RDLINE_INIT;
106 return cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
110 rdline_newline(struct rdline *rdl, const char *prompt)
117 vt100_init(&rdl->vt100);
118 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
119 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
121 rdl->prompt_size = strnlen(prompt, RDLINE_PROMPT_SIZE-1);
122 if (prompt != rdl->prompt)
123 memcpy(rdl->prompt, prompt, rdl->prompt_size);
124 rdl->prompt[RDLINE_PROMPT_SIZE-1] = '\0';
126 for (i=0 ; i<rdl->prompt_size ; i++)
127 rdl->write_char(rdl, rdl->prompt[i]);
128 rdl->status = RDLINE_RUNNING;
130 rdl->history_cur_line = -1;
134 rdline_stop(struct rdline *rdl)
138 rdl->status = RDLINE_INIT;
142 rdline_quit(struct rdline *rdl)
146 rdl->status = RDLINE_EXITED;
150 rdline_restart(struct rdline *rdl)
154 rdl->status = RDLINE_RUNNING;
158 rdline_reset(struct rdline *rdl)
162 vt100_init(&rdl->vt100);
163 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
164 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
166 rdl->status = RDLINE_RUNNING;
168 rdl->history_cur_line = -1;
172 rdline_get_buffer(struct rdline *rdl)
176 unsigned int len_l, len_r;
177 cirbuf_align_left(&rdl->left);
178 cirbuf_align_left(&rdl->right);
180 len_l = CIRBUF_GET_LEN(&rdl->left);
181 len_r = CIRBUF_GET_LEN(&rdl->right);
182 memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
184 rdl->left_buf[len_l + len_r] = '\n';
185 rdl->left_buf[len_l + len_r + 1] = '\0';
186 return rdl->left_buf;
190 display_right_buffer(struct rdline *rdl, int force)
195 if (!force && CIRBUF_IS_EMPTY(&rdl->right))
198 rdline_puts(rdl, vt100_clear_right);
199 CIRBUF_FOREACH(&rdl->right, i, tmp) {
200 rdl->write_char(rdl, tmp);
202 if (!CIRBUF_IS_EMPTY(&rdl->right))
203 rdline_miniprintf(rdl, vt100_multi_left,
204 CIRBUF_GET_LEN(&rdl->right));
208 rdline_redisplay(struct rdline *rdl)
216 rdline_puts(rdl, vt100_home);
217 for (i=0 ; i<rdl->prompt_size ; i++)
218 rdl->write_char(rdl, rdl->prompt[i]);
219 CIRBUF_FOREACH(&rdl->left, i, tmp) {
220 rdl->write_char(rdl, tmp);
222 display_right_buffer(rdl, 1);
226 rdline_char_in(struct rdline *rdl, char c)
236 if (rdl->status == RDLINE_EXITED)
237 return RDLINE_RES_EXITED;
238 if (rdl->status != RDLINE_RUNNING)
239 return RDLINE_RES_NOT_RUNNING;
241 cmd = vt100_parser(&rdl->vt100, c);
243 return RDLINE_RES_SUCCESS;
247 /* move caret 1 char to the left */
248 case CMDLINE_KEY_CTRL_B:
249 case CMDLINE_KEY_LEFT_ARR:
250 if (CIRBUF_IS_EMPTY(&rdl->left))
252 tmp = cirbuf_get_tail(&rdl->left);
253 cirbuf_del_tail(&rdl->left);
254 cirbuf_add_head(&rdl->right, tmp);
255 rdline_puts(rdl, vt100_left_arr);
258 /* move caret 1 char to the right */
259 case CMDLINE_KEY_CTRL_F:
260 case CMDLINE_KEY_RIGHT_ARR:
261 if (CIRBUF_IS_EMPTY(&rdl->right))
263 tmp = cirbuf_get_head(&rdl->right);
264 cirbuf_del_head(&rdl->right);
265 cirbuf_add_tail(&rdl->left, tmp);
266 rdline_puts(rdl, vt100_right_arr);
269 /* move caret 1 word to the left */
270 /* keyboard equivalent: Alt+B */
271 case CMDLINE_KEY_WLEFT:
272 while (! CIRBUF_IS_EMPTY(&rdl->left) &&
273 (tmp = cirbuf_get_tail(&rdl->left)) &&
275 rdline_puts(rdl, vt100_left_arr);
276 cirbuf_del_tail(&rdl->left);
277 cirbuf_add_head(&rdl->right, tmp);
279 while (! CIRBUF_IS_EMPTY(&rdl->left) &&
280 (tmp = cirbuf_get_tail(&rdl->left)) &&
282 rdline_puts(rdl, vt100_left_arr);
283 cirbuf_del_tail(&rdl->left);
284 cirbuf_add_head(&rdl->right, tmp);
288 /* move caret 1 word to the right */
289 /* keyboard equivalent: Alt+F */
290 case CMDLINE_KEY_WRIGHT:
291 while (! CIRBUF_IS_EMPTY(&rdl->right) &&
292 (tmp = cirbuf_get_head(&rdl->right)) &&
294 rdline_puts(rdl, vt100_right_arr);
295 cirbuf_del_head(&rdl->right);
296 cirbuf_add_tail(&rdl->left, tmp);
298 while (! CIRBUF_IS_EMPTY(&rdl->right) &&
299 (tmp = cirbuf_get_head(&rdl->right)) &&
301 rdline_puts(rdl, vt100_right_arr);
302 cirbuf_del_head(&rdl->right);
303 cirbuf_add_tail(&rdl->left, tmp);
307 /* move caret to the left */
308 case CMDLINE_KEY_CTRL_A:
309 if (CIRBUF_IS_EMPTY(&rdl->left))
311 rdline_miniprintf(rdl, vt100_multi_left,
312 CIRBUF_GET_LEN(&rdl->left));
313 while (! CIRBUF_IS_EMPTY(&rdl->left)) {
314 tmp = cirbuf_get_tail(&rdl->left);
315 cirbuf_del_tail(&rdl->left);
316 cirbuf_add_head(&rdl->right, tmp);
320 /* move caret to the right */
321 case CMDLINE_KEY_CTRL_E:
322 if (CIRBUF_IS_EMPTY(&rdl->right))
324 rdline_miniprintf(rdl, vt100_multi_right,
325 CIRBUF_GET_LEN(&rdl->right));
326 while (! CIRBUF_IS_EMPTY(&rdl->right)) {
327 tmp = cirbuf_get_head(&rdl->right);
328 cirbuf_del_head(&rdl->right);
329 cirbuf_add_tail(&rdl->left, tmp);
333 /* delete 1 char from the left */
334 case CMDLINE_KEY_BKSPACE:
335 if(!cirbuf_del_tail_safe(&rdl->left)) {
336 rdline_puts(rdl, vt100_bs);
337 display_right_buffer(rdl, 1);
341 /* delete 1 char from the right */
342 case CMDLINE_KEY_SUPPR:
343 case CMDLINE_KEY_CTRL_D:
344 if (cmd == CMDLINE_KEY_CTRL_D &&
345 CIRBUF_IS_EMPTY(&rdl->left) &&
346 CIRBUF_IS_EMPTY(&rdl->right)) {
347 return RDLINE_RES_EOF;
349 if (!cirbuf_del_head_safe(&rdl->right)) {
350 display_right_buffer(rdl, 1);
354 /* delete 1 word from the left */
355 case CMDLINE_KEY_META_BKSPACE:
356 case CMDLINE_KEY_CTRL_W:
357 while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
358 rdline_puts(rdl, vt100_bs);
359 cirbuf_del_tail(&rdl->left);
361 while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
362 rdline_puts(rdl, vt100_bs);
363 cirbuf_del_tail(&rdl->left);
365 display_right_buffer(rdl, 1);
368 /* delete 1 word from the right */
369 case CMDLINE_KEY_META_D:
370 while (! CIRBUF_IS_EMPTY(&rdl->right) && isblank2(cirbuf_get_head(&rdl->right)))
371 cirbuf_del_head(&rdl->right);
372 while (! CIRBUF_IS_EMPTY(&rdl->right) && !isblank2(cirbuf_get_head(&rdl->right)))
373 cirbuf_del_head(&rdl->right);
374 display_right_buffer(rdl, 1);
377 /* set kill buffer to contents on the right side of caret */
378 case CMDLINE_KEY_CTRL_K:
379 cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
380 rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
381 cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
382 rdline_puts(rdl, vt100_clear_right);
385 /* paste contents of kill buffer to the left side of caret */
386 case CMDLINE_KEY_CTRL_Y:
388 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
390 i < rdl->kill_size) {
391 cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
392 rdl->write_char(rdl, rdl->kill_buf[i]);
395 display_right_buffer(rdl, 0);
398 /* clear and newline */
399 case CMDLINE_KEY_CTRL_C:
400 rdline_puts(rdl, "\r\n");
401 rdline_newline(rdl, rdl->prompt);
404 /* redisplay (helps when prompt is lost in other output) */
405 case CMDLINE_KEY_CTRL_L:
406 rdline_redisplay(rdl);
410 case CMDLINE_KEY_TAB:
411 case CMDLINE_KEY_HELP:
412 cirbuf_align_left(&rdl->left);
413 rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
415 char tmp_buf[BUFSIZ];
418 unsigned int tmp_size;
420 if (cmd == CMDLINE_KEY_TAB)
425 /* see in parse.h for help on complete() */
426 ret = rdl->complete(rdl, rdl->left_buf,
427 tmp_buf, sizeof(tmp_buf),
429 /* no completion or error */
431 return RDLINE_RES_COMPLETE;
434 tmp_size = strnlen(tmp_buf, sizeof(tmp_buf));
436 if (ret == RDLINE_RES_COMPLETE) {
438 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
441 cirbuf_add_tail(&rdl->left, tmp_buf[i]);
442 rdl->write_char(rdl, tmp_buf[i]);
445 display_right_buffer(rdl, 1);
446 return RDLINE_RES_COMPLETE; /* ?? */
450 rdline_puts(rdl, "\r\n");
452 rdl->write_char(rdl, ' ');
453 for (i=0 ; tmp_buf[i] ; i++)
454 rdl->write_char(rdl, tmp_buf[i]);
455 rdline_puts(rdl, "\r\n");
456 ret = rdl->complete(rdl, rdl->left_buf,
457 tmp_buf, sizeof(tmp_buf),
461 rdline_redisplay(rdl);
463 return RDLINE_RES_COMPLETE;
465 /* complete buffer */
466 case CMDLINE_KEY_RETURN:
467 case CMDLINE_KEY_RETURN2:
468 rdline_get_buffer(rdl);
469 rdl->status = RDLINE_INIT;
470 rdline_puts(rdl, "\r\n");
471 if (rdl->history_cur_line != -1)
472 rdline_remove_first_history_item(rdl);
475 rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
476 /* user may have stopped rdline */
477 if (rdl->status == RDLINE_EXITED)
478 return RDLINE_RES_EXITED;
479 return RDLINE_RES_VALIDATED;
481 /* previous element in history */
482 case CMDLINE_KEY_UP_ARR:
483 case CMDLINE_KEY_CTRL_P:
484 if (rdl->history_cur_line == 0) {
485 rdline_remove_first_history_item(rdl);
487 if (rdl->history_cur_line <= 0) {
488 rdline_add_history(rdl, rdline_get_buffer(rdl));
489 rdl->history_cur_line = 0;
492 buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
496 rdl->history_cur_line ++;
497 vt100_init(&rdl->vt100);
498 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
499 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
500 cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
501 rdline_redisplay(rdl);
504 /* next element in history */
505 case CMDLINE_KEY_DOWN_ARR:
506 case CMDLINE_KEY_CTRL_N:
507 if (rdl->history_cur_line - 1 < 0)
510 rdl->history_cur_line --;
511 buf = rdline_get_history_item(rdl, rdl->history_cur_line);
514 vt100_init(&rdl->vt100);
515 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
516 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
517 cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
518 rdline_redisplay(rdl);
527 return RDLINE_RES_SUCCESS;
530 if (!isprint((int)c))
531 return RDLINE_RES_SUCCESS;
534 if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
535 return RDLINE_RES_SUCCESS;
537 if (cirbuf_add_tail_safe(&rdl->left, c))
538 return RDLINE_RES_SUCCESS;
540 rdl->write_char(rdl, c);
541 display_right_buffer(rdl, 0);
543 return RDLINE_RES_SUCCESS;
550 rdline_remove_old_history_item(struct rdline * rdl)
554 while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
555 tmp = cirbuf_get_head(&rdl->history);
556 cirbuf_del_head(&rdl->history);
563 rdline_remove_first_history_item(struct rdline * rdl)
567 if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
571 cirbuf_del_tail(&rdl->history);
574 while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
575 tmp = cirbuf_get_tail(&rdl->history);
578 cirbuf_del_tail(&rdl->history);
583 rdline_get_history_size(struct rdline * rdl)
585 unsigned int i, tmp, ret=0;
587 CIRBUF_FOREACH(&rdl->history, i, tmp) {
596 rdline_get_history_item(struct rdline * rdl, unsigned int idx)
598 unsigned int len, i, tmp;
603 len = rdline_get_history_size(rdl);
608 cirbuf_align_left(&rdl->history);
610 CIRBUF_FOREACH(&rdl->history, i, tmp) {
611 if ( idx == len - 1) {
612 return rdl->history_buf + i;
622 rdline_add_history(struct rdline * rdl, const char * buf)
629 len = strnlen(buf, RDLINE_BUF_SIZE);
630 for (i=0; i<len ; i++) {
631 if (buf[i] == '\n') {
637 if ( len >= RDLINE_HISTORY_BUF_SIZE )
640 while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
641 rdline_remove_old_history_item(rdl);
644 cirbuf_add_buf_tail(&rdl->history, buf, len);
645 cirbuf_add_tail(&rdl->history, 0);
651 rdline_clear_history(struct rdline * rdl)
655 cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
659 /* STATIC USEFUL FUNCS */
662 rdline_puts(struct rdline * rdl, const char * buf)
665 while ( (c = *(buf++)) != '\0' ) {
666 rdl->write_char(rdl, c);
670 /* a very very basic printf with one arg and one format 'u' */
672 rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
674 char c, started=0, div=100;
676 while ( (c=*(buf++)) ) {
678 rdl->write_char(rdl, c);
683 rdl->write_char(rdl, '%');
684 rdl->write_char(rdl, c);
687 /* val is never more than 255 */
689 c = (char)(val / div);
691 rdl->write_char(rdl, (char)(c+'0'));