1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2014 Intel Corporation.
3 * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
15 #include "cmdline_cirbuf.h"
16 #include "cmdline_private.h"
17 #include "cmdline_rdline.h"
19 static void rdline_puts(struct rdline *rdl, const char *buf);
20 static void rdline_miniprintf(struct rdline *rdl,
21 const char *buf, unsigned int val);
23 static void rdline_remove_old_history_item(struct rdline *rdl);
24 static void rdline_remove_first_history_item(struct rdline *rdl);
25 static unsigned int rdline_get_history_size(struct rdline *rdl);
28 /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
40 rdline_init(struct rdline *rdl,
41 rdline_write_char_t *write_char,
42 rdline_validate_t *validate,
43 rdline_complete_t *complete,
46 if (!rdl || !write_char || !validate || !complete)
48 memset(rdl, 0, sizeof(*rdl));
49 rdl->validate = validate;
50 rdl->complete = complete;
51 rdl->write_char = write_char;
53 rdl->status = RDLINE_INIT;
54 return cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
58 rdline_new(rdline_write_char_t *write_char,
59 rdline_validate_t *validate,
60 rdline_complete_t *complete,
65 rdl = malloc(sizeof(*rdl));
66 if (rdline_init(rdl, write_char, validate, complete, opaque) < 0) {
74 rdline_free(struct rdline *rdl)
80 rdline_newline(struct rdline *rdl, const char *prompt)
87 vt100_init(&rdl->vt100);
88 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
89 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
91 rdl->prompt_size = strnlen(prompt, RDLINE_PROMPT_SIZE-1);
92 if (prompt != rdl->prompt)
93 memcpy(rdl->prompt, prompt, rdl->prompt_size);
94 rdl->prompt[RDLINE_PROMPT_SIZE-1] = '\0';
96 for (i=0 ; i<rdl->prompt_size ; i++)
97 rdl->write_char(rdl, rdl->prompt[i]);
98 rdl->status = RDLINE_RUNNING;
100 rdl->history_cur_line = -1;
104 rdline_stop(struct rdline *rdl)
108 rdl->status = RDLINE_INIT;
112 rdline_quit(struct rdline *rdl)
116 rdl->status = RDLINE_EXITED;
120 rdline_restart(struct rdline *rdl)
124 rdl->status = RDLINE_RUNNING;
128 rdline_reset(struct rdline *rdl)
132 vt100_init(&rdl->vt100);
133 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
134 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
136 rdl->status = RDLINE_RUNNING;
138 rdl->history_cur_line = -1;
142 rdline_get_buffer(struct rdline *rdl)
146 unsigned int len_l, len_r;
147 cirbuf_align_left(&rdl->left);
148 cirbuf_align_left(&rdl->right);
150 len_l = CIRBUF_GET_LEN(&rdl->left);
151 len_r = CIRBUF_GET_LEN(&rdl->right);
152 memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
154 rdl->left_buf[len_l + len_r] = '\n';
155 rdl->left_buf[len_l + len_r + 1] = '\0';
156 return rdl->left_buf;
160 display_right_buffer(struct rdline *rdl, int force)
165 if (!force && CIRBUF_IS_EMPTY(&rdl->right))
168 rdline_puts(rdl, vt100_clear_right);
169 CIRBUF_FOREACH(&rdl->right, i, tmp) {
170 rdl->write_char(rdl, tmp);
172 if (!CIRBUF_IS_EMPTY(&rdl->right))
173 rdline_miniprintf(rdl, vt100_multi_left,
174 CIRBUF_GET_LEN(&rdl->right));
178 rdline_redisplay(struct rdline *rdl)
186 rdline_puts(rdl, vt100_home);
187 for (i=0 ; i<rdl->prompt_size ; i++)
188 rdl->write_char(rdl, rdl->prompt[i]);
189 CIRBUF_FOREACH(&rdl->left, i, tmp) {
190 rdl->write_char(rdl, tmp);
192 display_right_buffer(rdl, 1);
196 rdline_char_in(struct rdline *rdl, char c)
206 if (rdl->status == RDLINE_EXITED)
207 return RDLINE_RES_EXITED;
208 if (rdl->status != RDLINE_RUNNING)
209 return RDLINE_RES_NOT_RUNNING;
211 cmd = vt100_parser(&rdl->vt100, c);
213 return RDLINE_RES_SUCCESS;
217 /* move caret 1 char to the left */
218 case CMDLINE_KEY_CTRL_B:
219 case CMDLINE_KEY_LEFT_ARR:
220 if (CIRBUF_IS_EMPTY(&rdl->left))
222 tmp = cirbuf_get_tail(&rdl->left);
223 cirbuf_del_tail(&rdl->left);
224 cirbuf_add_head(&rdl->right, tmp);
225 rdline_puts(rdl, vt100_left_arr);
228 /* move caret 1 char to the right */
229 case CMDLINE_KEY_CTRL_F:
230 case CMDLINE_KEY_RIGHT_ARR:
231 if (CIRBUF_IS_EMPTY(&rdl->right))
233 tmp = cirbuf_get_head(&rdl->right);
234 cirbuf_del_head(&rdl->right);
235 cirbuf_add_tail(&rdl->left, tmp);
236 rdline_puts(rdl, vt100_right_arr);
239 /* move caret 1 word to the left */
240 /* keyboard equivalent: Alt+B */
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 /* move caret 1 word to the right */
259 /* keyboard equivalent: Alt+F */
260 case CMDLINE_KEY_WRIGHT:
261 while (! CIRBUF_IS_EMPTY(&rdl->right) &&
262 (tmp = cirbuf_get_head(&rdl->right)) &&
264 rdline_puts(rdl, vt100_right_arr);
265 cirbuf_del_head(&rdl->right);
266 cirbuf_add_tail(&rdl->left, tmp);
268 while (! CIRBUF_IS_EMPTY(&rdl->right) &&
269 (tmp = cirbuf_get_head(&rdl->right)) &&
271 rdline_puts(rdl, vt100_right_arr);
272 cirbuf_del_head(&rdl->right);
273 cirbuf_add_tail(&rdl->left, tmp);
277 /* move caret to the left */
278 case CMDLINE_KEY_CTRL_A:
279 if (CIRBUF_IS_EMPTY(&rdl->left))
281 rdline_miniprintf(rdl, vt100_multi_left,
282 CIRBUF_GET_LEN(&rdl->left));
283 while (! CIRBUF_IS_EMPTY(&rdl->left)) {
284 tmp = cirbuf_get_tail(&rdl->left);
285 cirbuf_del_tail(&rdl->left);
286 cirbuf_add_head(&rdl->right, tmp);
290 /* move caret to the right */
291 case CMDLINE_KEY_CTRL_E:
292 if (CIRBUF_IS_EMPTY(&rdl->right))
294 rdline_miniprintf(rdl, vt100_multi_right,
295 CIRBUF_GET_LEN(&rdl->right));
296 while (! CIRBUF_IS_EMPTY(&rdl->right)) {
297 tmp = cirbuf_get_head(&rdl->right);
298 cirbuf_del_head(&rdl->right);
299 cirbuf_add_tail(&rdl->left, tmp);
303 /* delete 1 char from the left */
304 case CMDLINE_KEY_BKSPACE:
305 case CMDLINE_KEY_BKSPACE2:
306 if(!cirbuf_del_tail_safe(&rdl->left)) {
307 rdline_puts(rdl, vt100_bs);
308 display_right_buffer(rdl, 1);
312 /* delete 1 char from the right */
313 case CMDLINE_KEY_SUPPR:
314 case CMDLINE_KEY_CTRL_D:
315 if (cmd == CMDLINE_KEY_CTRL_D &&
316 CIRBUF_IS_EMPTY(&rdl->left) &&
317 CIRBUF_IS_EMPTY(&rdl->right)) {
318 return RDLINE_RES_EOF;
320 if (!cirbuf_del_head_safe(&rdl->right)) {
321 display_right_buffer(rdl, 1);
325 /* delete 1 word from the left */
326 case CMDLINE_KEY_META_BKSPACE:
327 case CMDLINE_KEY_CTRL_W:
328 while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
329 rdline_puts(rdl, vt100_bs);
330 cirbuf_del_tail(&rdl->left);
332 while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
333 rdline_puts(rdl, vt100_bs);
334 cirbuf_del_tail(&rdl->left);
336 display_right_buffer(rdl, 1);
339 /* delete 1 word from the right */
340 case CMDLINE_KEY_META_D:
341 while (! CIRBUF_IS_EMPTY(&rdl->right) && isblank2(cirbuf_get_head(&rdl->right)))
342 cirbuf_del_head(&rdl->right);
343 while (! CIRBUF_IS_EMPTY(&rdl->right) && !isblank2(cirbuf_get_head(&rdl->right)))
344 cirbuf_del_head(&rdl->right);
345 display_right_buffer(rdl, 1);
348 /* set kill buffer to contents on the right side of caret */
349 case CMDLINE_KEY_CTRL_K:
350 cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
351 rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
352 cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
353 rdline_puts(rdl, vt100_clear_right);
356 /* paste contents of kill buffer to the left side of caret */
357 case CMDLINE_KEY_CTRL_Y:
359 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
361 i < rdl->kill_size) {
362 cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
363 rdl->write_char(rdl, rdl->kill_buf[i]);
366 display_right_buffer(rdl, 0);
369 /* clear and newline */
370 case CMDLINE_KEY_CTRL_C:
371 rdline_puts(rdl, "\r\n");
372 rdline_newline(rdl, rdl->prompt);
375 /* redisplay (helps when prompt is lost in other output) */
376 case CMDLINE_KEY_CTRL_L:
377 rdline_redisplay(rdl);
381 case CMDLINE_KEY_TAB:
382 case CMDLINE_KEY_HELP:
383 cirbuf_align_left(&rdl->left);
384 rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
386 char tmp_buf[BUFSIZ];
389 unsigned int tmp_size;
391 if (cmd == CMDLINE_KEY_TAB)
396 /* see in parse.h for help on complete() */
397 ret = rdl->complete(rdl, rdl->left_buf,
398 tmp_buf, sizeof(tmp_buf),
400 /* no completion or error */
402 return RDLINE_RES_COMPLETE;
405 tmp_size = strnlen(tmp_buf, sizeof(tmp_buf));
407 if (ret == RDLINE_RES_COMPLETE) {
409 while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
412 cirbuf_add_tail(&rdl->left, tmp_buf[i]);
413 rdl->write_char(rdl, tmp_buf[i]);
416 display_right_buffer(rdl, 1);
417 return RDLINE_RES_COMPLETE; /* ?? */
421 rdline_puts(rdl, "\r\n");
423 rdl->write_char(rdl, ' ');
424 for (i=0 ; tmp_buf[i] ; i++)
425 rdl->write_char(rdl, tmp_buf[i]);
426 rdline_puts(rdl, "\r\n");
427 ret = rdl->complete(rdl, rdl->left_buf,
428 tmp_buf, sizeof(tmp_buf),
432 rdline_redisplay(rdl);
434 return RDLINE_RES_COMPLETE;
436 /* complete buffer */
437 case CMDLINE_KEY_RETURN:
438 case CMDLINE_KEY_RETURN2:
439 rdline_get_buffer(rdl);
440 rdl->status = RDLINE_INIT;
441 rdline_puts(rdl, "\r\n");
442 if (rdl->history_cur_line != -1)
443 rdline_remove_first_history_item(rdl);
446 rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
447 /* user may have stopped rdline */
448 if (rdl->status == RDLINE_EXITED)
449 return RDLINE_RES_EXITED;
450 return RDLINE_RES_VALIDATED;
452 /* previous element in history */
453 case CMDLINE_KEY_UP_ARR:
454 case CMDLINE_KEY_CTRL_P:
455 if (rdl->history_cur_line == 0) {
456 rdline_remove_first_history_item(rdl);
458 if (rdl->history_cur_line <= 0) {
459 rdline_add_history(rdl, rdline_get_buffer(rdl));
460 rdl->history_cur_line = 0;
463 buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
467 rdl->history_cur_line ++;
468 vt100_init(&rdl->vt100);
469 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
470 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
471 cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
472 rdline_redisplay(rdl);
475 /* next element in history */
476 case CMDLINE_KEY_DOWN_ARR:
477 case CMDLINE_KEY_CTRL_N:
478 if (rdl->history_cur_line - 1 < 0)
481 rdl->history_cur_line --;
482 buf = rdline_get_history_item(rdl, rdl->history_cur_line);
485 vt100_init(&rdl->vt100);
486 cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
487 cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
488 cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
489 rdline_redisplay(rdl);
498 return RDLINE_RES_SUCCESS;
501 if (!isprint((int)c))
502 return RDLINE_RES_SUCCESS;
505 if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
506 return RDLINE_RES_SUCCESS;
508 if (cirbuf_add_tail_safe(&rdl->left, c))
509 return RDLINE_RES_SUCCESS;
511 rdl->write_char(rdl, c);
512 display_right_buffer(rdl, 0);
514 return RDLINE_RES_SUCCESS;
521 rdline_remove_old_history_item(struct rdline * rdl)
525 while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
526 tmp = cirbuf_get_head(&rdl->history);
527 cirbuf_del_head(&rdl->history);
534 rdline_remove_first_history_item(struct rdline * rdl)
538 if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
542 cirbuf_del_tail(&rdl->history);
545 while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
546 tmp = cirbuf_get_tail(&rdl->history);
549 cirbuf_del_tail(&rdl->history);
554 rdline_get_history_size(struct rdline * rdl)
556 unsigned int i, tmp, ret=0;
558 CIRBUF_FOREACH(&rdl->history, i, tmp) {
567 rdline_get_history_item(struct rdline * rdl, unsigned int idx)
569 unsigned int len, i, tmp;
574 len = rdline_get_history_size(rdl);
579 cirbuf_align_left(&rdl->history);
581 CIRBUF_FOREACH(&rdl->history, i, tmp) {
582 if ( idx == len - 1) {
583 return rdl->history_buf + i;
593 rdline_get_history_buffer_size(struct rdline *rdl)
595 return sizeof(rdl->history_buf);
599 rdline_get_opaque(struct rdline *rdl)
601 return rdl != NULL ? rdl->opaque : NULL;
605 rdline_add_history(struct rdline * rdl, const char * buf)
612 len = strnlen(buf, RDLINE_BUF_SIZE);
613 for (i=0; i<len ; i++) {
614 if (buf[i] == '\n') {
620 if ( len >= RDLINE_HISTORY_BUF_SIZE )
623 while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
624 rdline_remove_old_history_item(rdl);
627 cirbuf_add_buf_tail(&rdl->history, buf, len);
628 cirbuf_add_tail(&rdl->history, 0);
634 rdline_clear_history(struct rdline * rdl)
638 cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
642 /* STATIC USEFUL FUNCS */
645 rdline_puts(struct rdline * rdl, const char * buf)
648 while ( (c = *(buf++)) != '\0' ) {
649 rdl->write_char(rdl, c);
653 /* a very very basic printf with one arg and one format 'u' */
655 rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
657 char c, started=0, div=100;
659 while ( (c=*(buf++)) ) {
661 rdl->write_char(rdl, c);
666 rdl->write_char(rdl, '%');
667 rdl->write_char(rdl, c);
670 /* val is never more than 255 */
672 c = (char)(val / div);
674 rdl->write_char(rdl, (char)(c+'0'));