2 * Copyright (c) <2010>, Intel Corporation
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * 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
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
32 * 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.
69 #include "cmdline_cirbuf.h"
70 #include "cmdline_rdline.h"
71 #include "cmdline_parse.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 #ifndef NO_RDLINE_HISTORY
78 static void rdline_remove_old_history_item(struct rdline *rdl);
79 static void rdline_remove_first_history_item(struct rdline *rdl);
80 static unsigned int rdline_get_history_size(struct rdline *rdl);
81 #endif /* !NO_RDLINE_HISTORY */
84 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
96 rdline_init(struct rdline *rdl,
97 rdline_write_char_t *write_char,
98 rdline_validate_t *validate,
99 rdline_complete_t *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 #ifndef NO_RDLINE_HISTORY
107 cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
108 #endif /* !NO_RDLINE_HISTORY */
112 rdline_newline(struct rdline *rdl, const char *prompt)
116 vt100_init(&rdl->vt100);
117 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
118 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
120 if (prompt != rdl->prompt)
121 memcpy(rdl->prompt, prompt, sizeof(rdl->prompt)-1);
122 rdl->prompt_size = strlen(prompt);
124 for (i=0 ; i<rdl->prompt_size ; i++)
125 rdl->write_char(rdl, rdl->prompt[i]);
126 rdl->status = RDLINE_RUNNING;
128 #ifndef NO_RDLINE_HISTORY
129 rdl->history_cur_line = -1;
130 #endif /* !NO_RDLINE_HISTORY */
134 rdline_stop(struct rdline *rdl)
136 rdl->status = RDLINE_INIT;
140 rdline_quit(struct rdline *rdl)
142 rdl->status = RDLINE_EXITED;
146 rdline_restart(struct rdline *rdl)
148 rdl->status = RDLINE_RUNNING;
152 rdline_get_buffer(struct rdline *rdl)
154 unsigned int len_l, len_r;
155 cirbuf_align_left(&rdl->left);
156 cirbuf_align_left(&rdl->right);
158 len_l = CIRBUF_GET_LEN(&rdl->left);
159 len_r = CIRBUF_GET_LEN(&rdl->right);
160 memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
162 rdl->left_buf[len_l + len_r] = '\n';
163 rdl->left_buf[len_l + len_r + 1] = '\0';
164 return rdl->left_buf;
168 display_right_buffer(struct rdline *rdl, int force)
173 if (!force && CIRBUF_IS_EMPTY(&rdl->right))
176 rdline_puts(rdl, vt100_clear_right);
177 CIRBUF_FOREACH(&rdl->right, i, tmp) {
178 rdl->write_char(rdl, tmp);
180 if (!CIRBUF_IS_EMPTY(&rdl->right))
181 rdline_miniprintf(rdl, vt100_multi_left,
182 CIRBUF_GET_LEN(&rdl->right));
186 rdline_redisplay(struct rdline *rdl)
191 rdline_puts(rdl, vt100_home);
192 for (i=0 ; i<rdl->prompt_size ; i++)
193 rdl->write_char(rdl, rdl->prompt[i]);
194 CIRBUF_FOREACH(&rdl->left, i, tmp) {
195 rdl->write_char(rdl, tmp);
197 display_right_buffer(rdl, 1);
201 rdline_char_in(struct rdline *rdl, char c)
206 #ifndef NO_RDLINE_HISTORY
210 if (rdl->status == RDLINE_EXITED)
211 return RDLINE_RES_EXITED;
212 if (rdl->status != RDLINE_RUNNING)
213 return RDLINE_RES_NOT_RUNNING;
215 cmd = vt100_parser(&rdl->vt100, c);
216 if (cmd == VT100_NOT_COMPLETE)
217 return RDLINE_RES_SUCCESS;
219 if (cmd != VT100_STD_CHAR) {
221 case CMDLINE_KEY_CTRL_B:
222 case CMDLINE_KEY_LEFT_ARR:
223 if (CIRBUF_IS_EMPTY(&rdl->left))
225 tmp = cirbuf_get_tail(&rdl->left);
226 cirbuf_del_tail(&rdl->left);
227 cirbuf_add_head(&rdl->right, tmp);
228 rdline_puts(rdl, vt100_left_arr);
231 case CMDLINE_KEY_CTRL_F:
232 case CMDLINE_KEY_RIGHT_ARR:
233 if (CIRBUF_IS_EMPTY(&rdl->right))
235 tmp = cirbuf_get_head(&rdl->right);
236 cirbuf_del_head(&rdl->right);
237 cirbuf_add_tail(&rdl->left, tmp);
238 rdline_puts(rdl, vt100_right_arr);
241 case CMDLINE_KEY_WLEFT:
242 while (! CIRBUF_IS_EMPTY(&rdl->left) &&
243 (tmp = cirbuf_get_tail(&rdl->left)) &&
245 rdline_puts(rdl, vt100_left_arr);
246 cirbuf_del_tail(&rdl->left);
247 cirbuf_add_head(&rdl->right, tmp);
249 while (! CIRBUF_IS_EMPTY(&rdl->left) &&
250 (tmp = cirbuf_get_tail(&rdl->left)) &&
252 rdline_puts(rdl, vt100_left_arr);
253 cirbuf_del_tail(&rdl->left);
254 cirbuf_add_head(&rdl->right, tmp);
258 case CMDLINE_KEY_WRIGHT:
259 while (! CIRBUF_IS_EMPTY(&rdl->right) &&
260 (tmp = cirbuf_get_head(&rdl->right)) &&
262 rdline_puts(rdl, vt100_right_arr);
263 cirbuf_del_head(&rdl->right);
264 cirbuf_add_tail(&rdl->left, tmp);
266 while (! CIRBUF_IS_EMPTY(&rdl->right) &&
267 (tmp = cirbuf_get_head(&rdl->right)) &&
269 rdline_puts(rdl, vt100_right_arr);
270 cirbuf_del_head(&rdl->right);
271 cirbuf_add_tail(&rdl->left, tmp);
275 case CMDLINE_KEY_BKSPACE:
276 if(!cirbuf_del_tail_safe(&rdl->left)) {
277 rdline_puts(rdl, vt100_bs);
278 display_right_buffer(rdl, 1);
282 case CMDLINE_KEY_META_BKSPACE:
283 case CMDLINE_KEY_CTRL_W:
284 while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
285 rdline_puts(rdl, vt100_bs);
286 cirbuf_del_tail(&rdl->left);
288 while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
289 rdline_puts(rdl, vt100_bs);
290 cirbuf_del_tail(&rdl->left);
292 display_right_buffer(rdl, 1);
295 case CMDLINE_KEY_META_D:
296 while (! CIRBUF_IS_EMPTY(&rdl->right) && isblank2(cirbuf_get_head(&rdl->right)))
297 cirbuf_del_head(&rdl->right);
298 while (! CIRBUF_IS_EMPTY(&rdl->right) && !isblank2(cirbuf_get_head(&rdl->right)))
299 cirbuf_del_head(&rdl->right);
300 display_right_buffer(rdl, 1);
303 case CMDLINE_KEY_SUPPR:
304 case CMDLINE_KEY_CTRL_D:
305 if (cmd == CMDLINE_KEY_CTRL_D &&
306 CIRBUF_IS_EMPTY(&rdl->left) &&
307 CIRBUF_IS_EMPTY(&rdl->right)) {
308 return RDLINE_RES_EOF;
310 if (!cirbuf_del_head_safe(&rdl->right)) {
311 display_right_buffer(rdl, 1);
315 case CMDLINE_KEY_CTRL_A:
316 if (CIRBUF_IS_EMPTY(&rdl->left))
318 rdline_miniprintf(rdl, vt100_multi_left,
319 CIRBUF_GET_LEN(&rdl->left));
320 while (! CIRBUF_IS_EMPTY(&rdl->left)) {
321 tmp = cirbuf_get_tail(&rdl->left);
322 cirbuf_del_tail(&rdl->left);
323 cirbuf_add_head(&rdl->right, tmp);
327 case CMDLINE_KEY_CTRL_E:
328 if (CIRBUF_IS_EMPTY(&rdl->right))
330 rdline_miniprintf(rdl, vt100_multi_right,
331 CIRBUF_GET_LEN(&rdl->right));
332 while (! CIRBUF_IS_EMPTY(&rdl->right)) {
333 tmp = cirbuf_get_head(&rdl->right);
334 cirbuf_del_head(&rdl->right);
335 cirbuf_add_tail(&rdl->left, tmp);
339 #ifndef NO_RDLINE_KILL_BUF
340 case CMDLINE_KEY_CTRL_K:
341 cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
342 rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
343 cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
344 rdline_puts(rdl, vt100_clear_right);
347 case CMDLINE_KEY_CTRL_Y:
349 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
351 i < rdl->kill_size) {
352 cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
353 rdl->write_char(rdl, rdl->kill_buf[i]);
356 display_right_buffer(rdl, 0);
358 #endif /* !NO_RDLINE_KILL_BUF */
360 case CMDLINE_KEY_CTRL_C:
361 rdline_puts(rdl, "\r\n");
362 rdline_newline(rdl, rdl->prompt);
365 case CMDLINE_KEY_CTRL_L:
366 rdline_redisplay(rdl);
369 case CMDLINE_KEY_TAB:
370 case CMDLINE_KEY_HELP:
371 cirbuf_align_left(&rdl->left);
372 rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
374 char tmp_buf[CMDLINE_MAX_TOKEN_SIZE];
377 unsigned int tmp_size;
379 if (cmd == CMDLINE_KEY_TAB)
384 /* see in parse.h for help on complete() */
385 ret = rdl->complete(rdl, rdl->left_buf,
386 tmp_buf, sizeof(tmp_buf),
388 /* no completion or error */
390 return RDLINE_RES_COMPLETE;
393 tmp_size = strlen(tmp_buf);
395 if (ret == RDLINE_RES_COMPLETE) {
397 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
400 cirbuf_add_tail(&rdl->left, tmp_buf[i]);
401 rdl->write_char(rdl, tmp_buf[i]);
404 display_right_buffer(rdl, 1);
405 return RDLINE_RES_COMPLETE; /* ?? */
409 rdline_puts(rdl, "\r\n");
411 rdl->write_char(rdl, ' ');
412 for (i=0 ; tmp_buf[i] ; i++)
413 rdl->write_char(rdl, tmp_buf[i]);
414 rdline_puts(rdl, "\r\n");
415 ret = rdl->complete(rdl, rdl->left_buf,
416 tmp_buf, sizeof(tmp_buf),
420 rdline_redisplay(rdl);
422 return RDLINE_RES_COMPLETE;
424 case CMDLINE_KEY_RETURN:
425 case CMDLINE_KEY_RETURN2:
426 rdline_get_buffer(rdl);
427 rdl->status = RDLINE_INIT;
428 rdline_puts(rdl, "\r\n");
429 #ifndef NO_RDLINE_HISTORY
430 if (rdl->history_cur_line != -1)
431 rdline_remove_first_history_item(rdl);
435 rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
436 /* user may have stopped rdline */
437 if (rdl->status == RDLINE_EXITED)
438 return RDLINE_RES_EXITED;
439 return RDLINE_RES_VALIDATED;
441 #ifndef NO_RDLINE_HISTORY
442 case CMDLINE_KEY_UP_ARR:
443 case CMDLINE_KEY_CTRL_P:
444 if (rdl->history_cur_line == 0) {
445 rdline_remove_first_history_item(rdl);
447 if (rdl->history_cur_line <= 0) {
448 rdline_add_history(rdl, rdline_get_buffer(rdl));
449 rdl->history_cur_line = 0;
452 buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
456 rdl->history_cur_line ++;
457 vt100_init(&rdl->vt100);
458 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
459 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
460 cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
461 rdline_redisplay(rdl);
464 case CMDLINE_KEY_DOWN_ARR:
465 case CMDLINE_KEY_CTRL_N:
466 if (rdl->history_cur_line - 1 < 0)
469 rdl->history_cur_line --;
470 buf = rdline_get_history_item(rdl, rdl->history_cur_line);
473 vt100_init(&rdl->vt100);
474 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
475 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
476 cirbuf_add_buf_tail(&rdl->left, buf, strlen(buf));
477 rdline_redisplay(rdl);
480 #endif /* !NO_RDLINE_HISTORY */
487 return RDLINE_RES_SUCCESS;
490 if (!isprint((int)c))
491 return RDLINE_RES_SUCCESS;
494 if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
495 return RDLINE_RES_SUCCESS;
497 if (cirbuf_add_tail_safe(&rdl->left, c))
498 return RDLINE_RES_SUCCESS;
500 rdl->write_char(rdl, c);
501 display_right_buffer(rdl, 0);
503 return RDLINE_RES_SUCCESS;
509 #ifndef NO_RDLINE_HISTORY
511 rdline_remove_old_history_item(struct rdline * rdl)
515 while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
516 tmp = cirbuf_get_head(&rdl->history);
517 cirbuf_del_head(&rdl->history);
524 rdline_remove_first_history_item(struct rdline * rdl)
528 if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
532 cirbuf_del_tail(&rdl->history);
535 while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
536 tmp = cirbuf_get_tail(&rdl->history);
539 cirbuf_del_tail(&rdl->history);
544 rdline_get_history_size(struct rdline * rdl)
546 unsigned int i, tmp, ret=0;
548 CIRBUF_FOREACH(&rdl->history, i, tmp) {
557 rdline_get_history_item(struct rdline * rdl, unsigned int idx)
559 unsigned int len, i, tmp;
561 len = rdline_get_history_size(rdl);
566 cirbuf_align_left(&rdl->history);
568 CIRBUF_FOREACH(&rdl->history, i, tmp) {
569 if ( idx == len - 1) {
570 return rdl->history_buf + i;
580 rdline_add_history(struct rdline * rdl, const char * buf)
585 for (i=0; i<len ; i++) {
586 if (buf[i] == '\n') {
592 if ( len >= RDLINE_HISTORY_BUF_SIZE )
595 while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
596 rdline_remove_old_history_item(rdl);
599 cirbuf_add_buf_tail(&rdl->history, buf, len);
600 cirbuf_add_tail(&rdl->history, 0);
606 rdline_clear_history(struct rdline * rdl)
608 cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
611 #else /* !NO_RDLINE_HISTORY */
613 int rdline_add_history(struct rdline * rdl, const char * buf) {return -1;}
614 void rdline_clear_history(struct rdline * rdl) {}
615 char * rdline_get_history_item(struct rdline * rdl, unsigned int i) {return NULL;}
618 #endif /* !NO_RDLINE_HISTORY */
621 /* STATIC USEFUL FUNCS */
624 rdline_puts(struct rdline * rdl, const char * buf)
627 while ( (c = *(buf++)) != '\0' ) {
628 rdl->write_char(rdl, c);
632 /* a very very basic printf with one arg and one format 'u' */
634 rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
636 char c, started=0, div=100;
638 while ( (c=*(buf++)) ) {
640 rdl->write_char(rdl, c);
645 rdl->write_char(rdl, '%');
646 rdl->write_char(rdl, c);
649 /* val is never more than 255 */
651 c = (char)(val / div);
653 rdl->write_char(rdl, (char)(c+'0'));