1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2014 Intel Corporation.
3 * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
13 #include "cmdline_cirbuf.h"
14 #include "cmdline_private.h"
15 #include "cmdline_rdline.h"
17 static void rdline_puts(struct rdline *rdl, const char *buf);
18 static void rdline_miniprintf(struct rdline *rdl,
19 const char *buf, unsigned int val);
21 static void rdline_remove_old_history_item(struct rdline *rdl);
22 static void rdline_remove_first_history_item(struct rdline *rdl);
23 static unsigned int rdline_get_history_size(struct rdline *rdl);
26 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
38 rdline_init(struct rdline *rdl,
39 rdline_write_char_t *write_char,
40 rdline_validate_t *validate,
41 rdline_complete_t *complete,
44 if (!rdl || !write_char || !validate || !complete)
46 memset(rdl, 0, sizeof(*rdl));
47 rdl->validate = validate;
48 rdl->complete = complete;
49 rdl->write_char = write_char;
51 rdl->status = RDLINE_INIT;
52 return cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
56 rdline_new(rdline_write_char_t *write_char,
57 rdline_validate_t *validate,
58 rdline_complete_t *complete,
63 rdl = malloc(sizeof(*rdl));
64 if (rdline_init(rdl, write_char, validate, complete, opaque) < 0) {
72 rdline_free(struct rdline *rdl)
78 rdline_newline(struct rdline *rdl, const char *prompt)
85 vt100_init(&rdl->vt100);
86 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
87 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
89 rdl->prompt_size = strnlen(prompt, RDLINE_PROMPT_SIZE-1);
90 if (prompt != rdl->prompt)
91 memcpy(rdl->prompt, prompt, rdl->prompt_size);
92 rdl->prompt[RDLINE_PROMPT_SIZE-1] = '\0';
94 for (i=0 ; i<rdl->prompt_size ; i++)
95 rdl->write_char(rdl, rdl->prompt[i]);
96 rdl->status = RDLINE_RUNNING;
98 rdl->history_cur_line = -1;
102 rdline_stop(struct rdline *rdl)
106 rdl->status = RDLINE_INIT;
110 rdline_quit(struct rdline *rdl)
114 rdl->status = RDLINE_EXITED;
118 rdline_restart(struct rdline *rdl)
122 rdl->status = RDLINE_RUNNING;
126 rdline_reset(struct rdline *rdl)
130 vt100_init(&rdl->vt100);
131 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
132 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
134 rdl->status = RDLINE_RUNNING;
136 rdl->history_cur_line = -1;
140 rdline_get_buffer(struct rdline *rdl)
144 unsigned int len_l, len_r;
145 cirbuf_align_left(&rdl->left);
146 cirbuf_align_left(&rdl->right);
148 len_l = CIRBUF_GET_LEN(&rdl->left);
149 len_r = CIRBUF_GET_LEN(&rdl->right);
150 memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
152 rdl->left_buf[len_l + len_r] = '\n';
153 rdl->left_buf[len_l + len_r + 1] = '\0';
154 return rdl->left_buf;
158 display_right_buffer(struct rdline *rdl, int force)
163 if (!force && CIRBUF_IS_EMPTY(&rdl->right))
166 rdline_puts(rdl, vt100_clear_right);
167 CIRBUF_FOREACH(&rdl->right, i, tmp) {
168 rdl->write_char(rdl, tmp);
170 if (!CIRBUF_IS_EMPTY(&rdl->right))
171 rdline_miniprintf(rdl, vt100_multi_left,
172 CIRBUF_GET_LEN(&rdl->right));
176 rdline_redisplay(struct rdline *rdl)
184 rdline_puts(rdl, vt100_home);
185 for (i=0 ; i<rdl->prompt_size ; i++)
186 rdl->write_char(rdl, rdl->prompt[i]);
187 CIRBUF_FOREACH(&rdl->left, i, tmp) {
188 rdl->write_char(rdl, tmp);
190 display_right_buffer(rdl, 1);
194 rdline_char_in(struct rdline *rdl, char c)
204 if (rdl->status == RDLINE_EXITED)
205 return RDLINE_RES_EXITED;
206 if (rdl->status != RDLINE_RUNNING)
207 return RDLINE_RES_NOT_RUNNING;
209 cmd = vt100_parser(&rdl->vt100, c);
211 return RDLINE_RES_SUCCESS;
215 /* move caret 1 char to the left */
216 case CMDLINE_KEY_CTRL_B:
217 case CMDLINE_KEY_LEFT_ARR:
218 if (CIRBUF_IS_EMPTY(&rdl->left))
220 tmp = cirbuf_get_tail(&rdl->left);
221 cirbuf_del_tail(&rdl->left);
222 cirbuf_add_head(&rdl->right, tmp);
223 rdline_puts(rdl, vt100_left_arr);
226 /* move caret 1 char to the right */
227 case CMDLINE_KEY_CTRL_F:
228 case CMDLINE_KEY_RIGHT_ARR:
229 if (CIRBUF_IS_EMPTY(&rdl->right))
231 tmp = cirbuf_get_head(&rdl->right);
232 cirbuf_del_head(&rdl->right);
233 cirbuf_add_tail(&rdl->left, tmp);
234 rdline_puts(rdl, vt100_right_arr);
237 /* move caret 1 word to the left */
238 /* keyboard equivalent: Alt+B */
239 case CMDLINE_KEY_WLEFT:
240 while (! CIRBUF_IS_EMPTY(&rdl->left) &&
241 (tmp = cirbuf_get_tail(&rdl->left)) &&
243 rdline_puts(rdl, vt100_left_arr);
244 cirbuf_del_tail(&rdl->left);
245 cirbuf_add_head(&rdl->right, tmp);
247 while (! CIRBUF_IS_EMPTY(&rdl->left) &&
248 (tmp = cirbuf_get_tail(&rdl->left)) &&
250 rdline_puts(rdl, vt100_left_arr);
251 cirbuf_del_tail(&rdl->left);
252 cirbuf_add_head(&rdl->right, tmp);
256 /* move caret 1 word to the right */
257 /* keyboard equivalent: Alt+F */
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 /* move caret to the left */
276 case CMDLINE_KEY_CTRL_A:
277 if (CIRBUF_IS_EMPTY(&rdl->left))
279 rdline_miniprintf(rdl, vt100_multi_left,
280 CIRBUF_GET_LEN(&rdl->left));
281 while (! CIRBUF_IS_EMPTY(&rdl->left)) {
282 tmp = cirbuf_get_tail(&rdl->left);
283 cirbuf_del_tail(&rdl->left);
284 cirbuf_add_head(&rdl->right, tmp);
288 /* move caret to the right */
289 case CMDLINE_KEY_CTRL_E:
290 if (CIRBUF_IS_EMPTY(&rdl->right))
292 rdline_miniprintf(rdl, vt100_multi_right,
293 CIRBUF_GET_LEN(&rdl->right));
294 while (! CIRBUF_IS_EMPTY(&rdl->right)) {
295 tmp = cirbuf_get_head(&rdl->right);
296 cirbuf_del_head(&rdl->right);
297 cirbuf_add_tail(&rdl->left, tmp);
301 /* delete 1 char from the left */
302 case CMDLINE_KEY_BKSPACE:
303 case CMDLINE_KEY_BKSPACE2:
304 if(!cirbuf_del_tail_safe(&rdl->left)) {
305 rdline_puts(rdl, vt100_bs);
306 display_right_buffer(rdl, 1);
310 /* delete 1 char from the right */
311 case CMDLINE_KEY_SUPPR:
312 case CMDLINE_KEY_CTRL_D:
313 if (cmd == CMDLINE_KEY_CTRL_D &&
314 CIRBUF_IS_EMPTY(&rdl->left) &&
315 CIRBUF_IS_EMPTY(&rdl->right)) {
316 return RDLINE_RES_EOF;
318 if (!cirbuf_del_head_safe(&rdl->right)) {
319 display_right_buffer(rdl, 1);
323 /* delete 1 word from the left */
324 case CMDLINE_KEY_META_BKSPACE:
325 case CMDLINE_KEY_CTRL_W:
326 while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
327 rdline_puts(rdl, vt100_bs);
328 cirbuf_del_tail(&rdl->left);
330 while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
331 rdline_puts(rdl, vt100_bs);
332 cirbuf_del_tail(&rdl->left);
334 display_right_buffer(rdl, 1);
337 /* delete 1 word from the right */
338 case CMDLINE_KEY_META_D:
339 while (! CIRBUF_IS_EMPTY(&rdl->right) && isblank2(cirbuf_get_head(&rdl->right)))
340 cirbuf_del_head(&rdl->right);
341 while (! CIRBUF_IS_EMPTY(&rdl->right) && !isblank2(cirbuf_get_head(&rdl->right)))
342 cirbuf_del_head(&rdl->right);
343 display_right_buffer(rdl, 1);
346 /* set kill buffer to contents on the right side of caret */
347 case CMDLINE_KEY_CTRL_K:
348 cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
349 rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
350 cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
351 rdline_puts(rdl, vt100_clear_right);
354 /* paste contents of kill buffer to the left side of caret */
355 case CMDLINE_KEY_CTRL_Y:
357 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
359 i < rdl->kill_size) {
360 cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
361 rdl->write_char(rdl, rdl->kill_buf[i]);
364 display_right_buffer(rdl, 0);
367 /* clear and newline */
368 case CMDLINE_KEY_CTRL_C:
369 rdline_puts(rdl, "\r\n");
370 rdline_newline(rdl, rdl->prompt);
373 /* redisplay (helps when prompt is lost in other output) */
374 case CMDLINE_KEY_CTRL_L:
375 rdline_redisplay(rdl);
379 case CMDLINE_KEY_TAB:
380 case CMDLINE_KEY_HELP:
381 cirbuf_align_left(&rdl->left);
382 rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
384 char tmp_buf[BUFSIZ];
387 unsigned int tmp_size;
389 if (cmd == CMDLINE_KEY_TAB)
394 /* see in parse.h for help on complete() */
395 ret = rdl->complete(rdl, rdl->left_buf,
396 tmp_buf, sizeof(tmp_buf),
398 /* no completion or error */
400 return RDLINE_RES_COMPLETE;
403 tmp_size = strnlen(tmp_buf, sizeof(tmp_buf));
405 if (ret == RDLINE_RES_COMPLETE) {
407 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
410 cirbuf_add_tail(&rdl->left, tmp_buf[i]);
411 rdl->write_char(rdl, tmp_buf[i]);
414 display_right_buffer(rdl, 1);
415 return RDLINE_RES_COMPLETE; /* ?? */
419 rdline_puts(rdl, "\r\n");
421 rdl->write_char(rdl, ' ');
422 for (i=0 ; tmp_buf[i] ; i++)
423 rdl->write_char(rdl, tmp_buf[i]);
424 rdline_puts(rdl, "\r\n");
425 ret = rdl->complete(rdl, rdl->left_buf,
426 tmp_buf, sizeof(tmp_buf),
430 rdline_redisplay(rdl);
432 return RDLINE_RES_COMPLETE;
434 /* complete buffer */
435 case CMDLINE_KEY_RETURN:
436 case CMDLINE_KEY_RETURN2:
437 rdline_get_buffer(rdl);
438 rdl->status = RDLINE_INIT;
439 rdline_puts(rdl, "\r\n");
440 if (rdl->history_cur_line != -1)
441 rdline_remove_first_history_item(rdl);
444 rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
445 /* user may have stopped rdline */
446 if (rdl->status == RDLINE_EXITED)
447 return RDLINE_RES_EXITED;
448 return RDLINE_RES_VALIDATED;
450 /* previous element in history */
451 case CMDLINE_KEY_UP_ARR:
452 case CMDLINE_KEY_CTRL_P:
453 if (rdl->history_cur_line == 0) {
454 rdline_remove_first_history_item(rdl);
456 if (rdl->history_cur_line <= 0) {
457 rdline_add_history(rdl, rdline_get_buffer(rdl));
458 rdl->history_cur_line = 0;
461 buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
465 rdl->history_cur_line ++;
466 vt100_init(&rdl->vt100);
467 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
468 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
469 cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
470 rdline_redisplay(rdl);
473 /* next element in history */
474 case CMDLINE_KEY_DOWN_ARR:
475 case CMDLINE_KEY_CTRL_N:
476 if (rdl->history_cur_line - 1 < 0)
479 rdl->history_cur_line --;
480 buf = rdline_get_history_item(rdl, rdl->history_cur_line);
483 vt100_init(&rdl->vt100);
484 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
485 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
486 cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
487 rdline_redisplay(rdl);
496 return RDLINE_RES_SUCCESS;
499 if (!isprint((int)c))
500 return RDLINE_RES_SUCCESS;
503 if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
504 return RDLINE_RES_SUCCESS;
506 if (cirbuf_add_tail_safe(&rdl->left, c))
507 return RDLINE_RES_SUCCESS;
509 rdl->write_char(rdl, c);
510 display_right_buffer(rdl, 0);
512 return RDLINE_RES_SUCCESS;
519 rdline_remove_old_history_item(struct rdline * rdl)
523 while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
524 tmp = cirbuf_get_head(&rdl->history);
525 cirbuf_del_head(&rdl->history);
532 rdline_remove_first_history_item(struct rdline * rdl)
536 if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
540 cirbuf_del_tail(&rdl->history);
543 while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
544 tmp = cirbuf_get_tail(&rdl->history);
547 cirbuf_del_tail(&rdl->history);
552 rdline_get_history_size(struct rdline * rdl)
554 unsigned int i, tmp, ret=0;
556 CIRBUF_FOREACH(&rdl->history, i, tmp) {
565 rdline_get_history_item(struct rdline * rdl, unsigned int idx)
567 unsigned int len, i, tmp;
572 len = rdline_get_history_size(rdl);
577 cirbuf_align_left(&rdl->history);
579 CIRBUF_FOREACH(&rdl->history, i, tmp) {
580 if ( idx == len - 1) {
581 return rdl->history_buf + i;
591 rdline_get_history_buffer_size(struct rdline *rdl)
593 return sizeof(rdl->history_buf);
597 rdline_get_opaque(struct rdline *rdl)
599 return rdl != NULL ? rdl->opaque : NULL;
603 rdline_add_history(struct rdline * rdl, const char * buf)
610 len = strnlen(buf, RDLINE_BUF_SIZE);
611 for (i=0; i<len ; i++) {
612 if (buf[i] == '\n') {
618 if ( len >= RDLINE_HISTORY_BUF_SIZE )
621 while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
622 rdline_remove_old_history_item(rdl);
625 cirbuf_add_buf_tail(&rdl->history, buf, len);
626 cirbuf_add_tail(&rdl->history, 0);
632 rdline_clear_history(struct rdline * rdl)
636 cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
640 /* STATIC USEFUL FUNCS */
643 rdline_puts(struct rdline * rdl, const char * buf)
646 while ( (c = *(buf++)) != '\0' ) {
647 rdl->write_char(rdl, c);
651 /* a very very basic printf with one arg and one format 'u' */
653 rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
655 char c, started=0, div=100;
657 while ( (c=*(buf++)) ) {
659 rdl->write_char(rdl, c);
664 rdl->write_char(rdl, '%');
665 rdl->write_char(rdl, c);
668 /* val is never more than 255 */
670 c = (char)(val / div);
672 rdl->write_char(rdl, (char)(c+'0'));