1 /*
2 * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3 */
4
5 #include "aos/cli.h"
6 #include "aos/kernel.h"
7 #include "cli_adapt.h"
8 #include "cli_api.h"
9 #include "cli_console.h"
10 #include <stdarg.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #if CLI_IOBOX_ENABLE
17 #include "path_helper.h"
18 #include <dirent.h>
19 #include <errno.h>
20 #include <stdbool.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 #endif
25
26 #if AOS_COMP_DEBUG
27 #include "aos/debug.h"
28 #endif
29
30 #define RET_CHAR '\n'
31 #define END_CHAR '\r'
32 #define PROMPT "# "
33 #define EXIT_MSG "exit"
34
35 typedef struct {
36 int32_t argc;
37 char *argv[CLI_MAX_ARG_NUM];
38 } _cmd_arg_t;
39
40 typedef struct {
41 _cmd_arg_t arg[CLI_MAX_ONCECMD_NUM];
42 } cmd_arg_t;
43
44 typedef enum {
45 CLI_WAIT_NORMAL,
46 CLI_WAIT_SPEC_KEY,
47 CLI_WAIT_FUNC_KEY,
48 } cli_input_stat;
49
50 struct cli_status {
51 int32_t inited;
52 uint32_t num;
53 int32_t echo_disabled;
54
55 char inbuf[CLI_INBUF_SIZE];
56
57 const struct cli_command *cmds[CLI_MAX_COMMANDS];
58
59 uint32_t his_idx;
60 uint32_t his_cur;
61 char history[CLI_INBUF_SIZE];
62
63 uint32_t cmd_cur_pos;
64 uint32_t cmd_end_pos;
65
66 cli_input_stat input_status;
67 };
68
69 static struct cli_status *g_cli = NULL;
70 extern cli_console cli_uart_console;
71 extern void hal_reboot(void);
72 #if CLI_IOBOX_ENABLE
73 extern uint32_t vfs_get_match_dev_node(const char *name, char *match_name);
74 #endif
75 int32_t cli_va_printf(const char *fmt, va_list va);
76 int32_t cli_printf(const char *fmt, ...);
77
cli_prefix_print(void)78 static inline void cli_prefix_print(void)
79 {
80 #if CLI_IOBOX_ENABLE
81 char _buf[PATH_MAX] = {0};
82 cli_printf("(%s:%s)"PROMPT, cli_task_get_console_name(),
83 getcwd(_buf, sizeof(_buf)));
84 #else
85 cli_printf("(%s)"PROMPT, cli_task_get_console_name());
86 #endif
87 }
88
lookup_command(char * name,int32_t len)89 static const struct cli_command *lookup_command(char *name, int32_t len)
90 {
91 int32_t i = 0;
92 int32_t n = 0;
93
94 while (i < CLI_MAX_COMMANDS && n < g_cli->num) {
95 if (g_cli->cmds[i]->name == NULL) {
96 i++;
97 continue;
98 }
99 /* See if partial or full match is expected */
100 if (len != 0) {
101 if (!strncmp(g_cli->cmds[i]->name, name, len)) {
102 return g_cli->cmds[i];
103 }
104 } else {
105 if (!strcmp(g_cli->cmds[i]->name, name)) {
106 return g_cli->cmds[i];
107 }
108 }
109
110 i++;
111 n++;
112 }
113
114 return NULL;
115 }
116
proc_onecmd(int32_t argc,char * argv[])117 int32_t proc_onecmd(int32_t argc, char *argv[])
118 {
119 int32_t i = 0;
120 int32_t ret = 0;
121 uint8_t tmp = 0;
122 const char *p = NULL;
123
124 const struct cli_command *command = NULL;
125
126 if (argc < 1) {
127 ret = CLI_ERR_INVALID;
128 goto cmd_err;
129 }
130
131 if (!g_cli->echo_disabled) {
132 tmp = cli_console_get_tag_len(NULL);
133 cli_console_set_tag_len(0, NULL);
134 cli_printf("\r\n");
135
136 cli_console_set_tag_len(tmp, NULL);
137 }
138
139 /*
140 * Some comamands can allow extensions like foo.a, foo.b and hence
141 * compare commands before first dot.
142 */
143 i = ((p = strchr(argv[0], '.')) == NULL) ? 0 : (p - argv[0]);
144
145 command = lookup_command(argv[0], i);
146 if (command == NULL) {
147 ret = CLI_ERR_CMDNOTEXIST;
148 goto cmd_err;
149 }
150
151 command->function(NULL, CLI_OUTBUF_SIZE, argc, argv);
152
153 ret = 0;
154
155 cmd_err:
156 return ret;
157 }
158
cli_handle_input(char * inbuf)159 int32_t cli_handle_input(char *inbuf)
160 {
161 struct {
162 unsigned inArg:1;
163 unsigned inQuote:1;
164 unsigned done:1;
165 } stat;
166
167 cmd_arg_t *cmd_arg_all = (cmd_arg_t *)aos_zalloc(sizeof(cmd_arg_t));
168 if (!cmd_arg_all) {
169 printf("%s malloc fail\r\n", __func__);
170 return -1;
171 }
172
173 int32_t cmdnum = 0;
174 int32_t *pargc = &(cmd_arg_all->arg[0].argc);
175 int32_t i = 0;
176 int32_t ret = 0;
177
178 memset(&stat, 0, sizeof(stat));
179
180 do {
181 switch (inbuf[i]) {
182 case '\0':
183 if (stat.inQuote) {
184 ret = CLI_ERR_SYNTAX;
185 goto exit;
186 }
187 stat.done = 1;
188 break;
189
190 case '"':
191 if (i > 0 && inbuf[i - 1] == '\\' && stat.inArg) {
192 memcpy(&inbuf[i - 1], &inbuf[i],
193 strlen((const char *)(&inbuf[i]) + 1));
194 --i;
195 break;
196 }
197 if (!stat.inQuote && stat.inArg) {
198 break;
199 }
200 if (stat.inQuote && !stat.inArg) {
201 ret = CLI_ERR_SYNTAX;
202 goto exit;
203 }
204
205 if (!stat.inQuote && !stat.inArg) {
206 stat.inArg = 1;
207 stat.inQuote = 1;
208 (*pargc)++;
209 (cmd_arg_all->arg[cmdnum]).argv[(*pargc) - 1] =
210 &inbuf[i + 1];
211 } else if (stat.inQuote && stat.inArg) {
212 stat.inArg = 0;
213 stat.inQuote = 0;
214 inbuf[i] = '\0';
215 }
216 break;
217
218 case ' ':
219 if (i > 0 && inbuf[i - 1] == '\\' && stat.inArg) {
220 memcpy(&inbuf[i - 1], &inbuf[i],
221 strlen((const char *)(&inbuf[i]) + 1));
222 --i;
223 break;
224 }
225 if (!stat.inQuote && stat.inArg) {
226 stat.inArg = 0;
227 inbuf[i] = '\0';
228 }
229 break;
230
231 case ';':
232 if (i > 0 && inbuf[i - 1] == '\\' && stat.inArg) {
233 memcpy(&inbuf[i - 1], &inbuf[i],
234 strlen((const char *)(&inbuf[i]) + 1));
235 --i;
236 break;
237 }
238 if (stat.inQuote) {
239 ret = CLI_ERR_SYNTAX;
240 goto exit;
241 }
242 if (!stat.inQuote && stat.inArg) {
243 stat.inArg = 0;
244 inbuf[i] = '\0';
245
246 if (*pargc) {
247 if (++cmdnum < CLI_MAX_ONCECMD_NUM) {
248 pargc = &((cmd_arg_all->arg[cmdnum]).argc);
249 }
250 }
251 }
252
253 break;
254
255 default:
256 if (!stat.inArg) {
257 stat.inArg = 1;
258 (*pargc)++;
259 (cmd_arg_all->arg[cmdnum]).argv[(*pargc) - 1] = &inbuf[i];
260 }
261 break;
262 }
263 } while (!stat.done && ++i < CLI_INBUF_SIZE &&
264 cmdnum < CLI_MAX_ONCECMD_NUM && (*pargc) < CLI_MAX_ARG_NUM);
265
266 if (stat.inQuote) {
267 ret = CLI_ERR_SYNTAX;
268 goto exit;
269 }
270
271 for (i = 0; i <= cmdnum && i < CLI_MAX_ONCECMD_NUM; i++) {
272 ret |=
273 proc_onecmd((cmd_arg_all->arg[i]).argc, (cmd_arg_all->arg[i]).argv);
274 }
275
276 exit:
277 if (cmd_arg_all) {
278 free(cmd_arg_all);
279 }
280 return ret;
281 }
282
283 /**
284 * @brief Perform basic tab-completion on the input buffer
285 *
286 * @param[in] inbuf the input buffer
287 * @param[in] bp the current buffer pointer
288 *
289 * @return none
290 *
291 */
cli_tab_complete(char * inbuf,uint32_t * idx)292 static void cli_tab_complete(char *inbuf, uint32_t *idx)
293 {
294 int32_t i, n, m;
295
296 const char *fm = NULL;
297
298 i = n = m = 0;
299
300 /* show matching commands */
301 for (i = 0; i < CLI_MAX_COMMANDS && n < g_cli->num; i++) {
302 if (g_cli->cmds[i]->name != NULL) {
303 if (!strncmp(inbuf, g_cli->cmds[i]->name, *idx)) {
304 m++;
305 if (m == 1) {
306 fm = g_cli->cmds[i]->name;
307 } else if (m == 2) {
308 cli_printf("\r\n%-24s %-24s", fm, g_cli->cmds[i]->name);
309 } else if (m > 2 && m % 4 == 1) {
310 cli_printf("\r\n%-24s", g_cli->cmds[i]->name);
311 } else {
312 cli_printf(" %-24s", g_cli->cmds[i]->name);
313 }
314 }
315 n++;
316 }
317 }
318
319 /* there's only one match, so complete the line */
320 if (m == 1 && fm) {
321 n = strlen((const char *)fm) - *idx;
322 if (*idx + n < CLI_INBUF_SIZE) {
323 memcpy(inbuf + *idx, fm + *idx, n);
324 *idx += n;
325 inbuf[*idx] = '\0';
326 }
327 }
328 if (m >= 2) {
329 cli_printf("\r\n");
330 cli_prefix_print();
331 }
332
333 /* just redraw input line */
334 cli_printf("%s", inbuf);
335 }
336
337 #if CLI_IOBOX_ENABLE
find_last(char * inbuf,uint32_t * idx)338 static char *find_last(char *inbuf, uint32_t *idx)
339 {
340 char *ret = inbuf + (--(*idx));
341
342 if (*ret == ' ')
343 return NULL;
344
345 while ((*ret != ' ') && (*idx > 0)) {
346 ret--;
347 (*idx)--;
348 }
349
350 if (*ret == ' ') {
351 ret++;
352 (*idx)++;
353 } else {
354 ret = NULL;
355 }
356
357 return ret;
358 }
359
refine_dir(char * dir,char ** last_name)360 static void refine_dir(char *dir, char **last_name)
361 {
362 int slash_cnt = 0;
363 char *p = dir, *last_slash = dir;
364
365 while (*p != '\0') {
366 if (*p == '/') {
367 last_slash = p;
368 slash_cnt++;
369 }
370
371 p++;
372 }
373
374 *last_name = last_slash + 1;
375 if (slash_cnt < 2) {
376 (*last_name)[strlen((const char *)(*last_name)) + 1] = '\0';
377 memmove((*last_name) + 1, *last_name, strlen(*last_name));
378 **last_name = '\0';
379 (*last_name)++;
380 } else {
381 *last_slash = '\0';
382 }
383 }
384
385 #define CLI_AUTO_PATH_DEBUG(...) /* cli_printf(__VA_ARGS__) */
386
cli_tab_complete_path(char * inbuf,uint32_t * idx)387 static void cli_tab_complete_path(char *inbuf, uint32_t *idx)
388 {
389 int32_t n, m;
390 bool match_all = false;
391 struct stat s;
392 uint32_t last_idx = *idx;
393 char *last_str = NULL, *last_name = NULL;
394 char *dir = NULL, abspath[256] = {0};
395 DIR *pdir = NULL;
396 struct dirent *entry = NULL;
397 char fpath[256], fm[256] = {0};
398 int len, fm_type = 0;
399 char tmpdir[256] = {0};
400
401 CLI_AUTO_PATH_DEBUG("%s %d, inbuf: %s, idx: %d\r\n", __func__, __LINE__,
402 inbuf, *idx);
403 last_str = find_last(inbuf, &last_idx);
404 CLI_AUTO_PATH_DEBUG("%s %d, last: %s, last_idx: %d\r\n", __func__, __LINE__,
405 last_str, last_idx);
406
407 if (!last_str) {
408 // tab after string, match all entries
409 match_all = true;
410 dir = getcwd(abspath, sizeof(abspath));
411 CLI_AUTO_PATH_DEBUG("%s %d, dir: %s\r\n", __func__, __LINE__, dir);
412 } else {
413 // tab after string
414 // get realp path
415 if (last_str[strlen((const char *)last_str) - 1] == '/')
416 match_all = true;
417
418 CLI_AUTO_PATH_DEBUG("%s %d, match_all: %d\r\n", __func__, __LINE__,
419 match_all);
420 // get real path, without tailing '/' for dir!
421 dir = get_realpath(last_str, abspath, sizeof(abspath));
422 if (!dir) {
423 cli_printf("Failed to get real path!\r\n");
424 return;
425 }
426
427 strncpy(tmpdir, dir, strlen(dir));
428 CLI_AUTO_PATH_DEBUG(
429 "%s %d, inbuf: %s, idx: %d, last_str:%s, tmpdir:%s, dir:%s\r\n",
430 __func__, __LINE__, inbuf, *idx, last_str, tmpdir, dir);
431
432 if (!match_all) {
433 // get base name and last name, dir string will be reformed!
434 refine_dir(dir, &last_name);
435 CLI_AUTO_PATH_DEBUG("%s %d, dir: %s, last_name: %s\r\n", __func__,
436 __LINE__, dir, last_name);
437 }
438
439 // update inbuf with only base name
440 *idx -= strlen((const char *)tmpdir) - strlen((const char *)dir);
441 // strcpy(last_str, dir);
442
443 if (inbuf[*idx] == ' ')
444 /*skip to replace white space.*/
445 (*idx)++;
446 inbuf[*idx] = '\0';
447 // *idx = strlen(inbuf);
448 CLI_AUTO_PATH_DEBUG("%s %d, inbuf: %s, idx: %d\r\n", __func__, __LINE__,
449 inbuf, *idx);
450 }
451
452 n = m = 0;
453
454 if (stat(dir, &s)) {
455 cli_printf("%s not existed\r\n", dir);
456 return;
457 }
458
459 pdir = opendir(dir);
460 if (!pdir) {
461 cli_printf("Failed to open dir %s\r\n", dir);
462 return;
463 }
464
465 if (!S_ISDIR(s.st_mode)) {
466 closedir(pdir);
467 cli_printf("%s is not a valid dir\r\n", dir);
468 return;
469 }
470
471 /* device fs process */
472 if (!strncmp(dir, "/dev", strlen("/dev"))) {
473 m = vfs_get_match_dev_node(last_name, fm);
474 } else {
475 uint32_t match_count = 0;
476 while ((entry = readdir(pdir))) {
477 /*skip "./" and "../" directory.*/
478 if ((!strncmp(entry->d_name, ".", 1)) ||
479 (!strncmp(entry->d_name, "..", 2)))
480 continue;
481
482 memset(&s, 0, sizeof(s));
483 memset(fpath, 0, 128);
484 snprintf(fpath, 128, "%s%s%s", dir,
485 dir[strlen((const char *)dir) - 1] == '/' ? "" : "/",
486 entry->d_name);
487 CLI_AUTO_PATH_DEBUG("%s %d, fpath: %s, m: %d, n: %d\r\n", __func__,
488 __LINE__, fpath, m, n);
489
490 if (stat(fpath, &s)) {
491 cli_printf("stat %s failed - %s\n", fpath, strerror(errno));
492 continue;
493 }
494
495 char dname[256] = {0};
496 if (S_ISDIR(s.st_mode))
497 snprintf(dname, 256, "%s%s", entry->d_name, "/");
498 else
499 snprintf(dname, 256, "%s", entry->d_name);
500
501 if (match_all) {
502 match_count++;
503 /*print all file or directory under current dir.*/
504 if (match_count % 4 == 1) {
505 cli_printf("\r\n%-24s", dname);
506 } else {
507 cli_printf(" %-24s", dname);
508 }
509 continue;
510 } else {
511 CLI_AUTO_PATH_DEBUG("%s %d, last_name: %s, entryname: %s\r\n",
512 __func__, __LINE__, last_name,
513 entry->d_name);
514 if (!strncmp(last_name, entry->d_name,
515 strlen((const char *)last_name))) {
516 m++;
517
518 if (m == 1) {
519 // fm = entry->d_name;
520 snprintf(fm, sizeof(fm), "%s", entry->d_name);
521 fm_type = S_ISDIR(s.st_mode) ? 1 : 0;
522 CLI_AUTO_PATH_DEBUG("%s %d, fm: %s, fm_type: %d\r\n",
523 __func__, __LINE__, fm, fm_type);
524 } else if (m == 2) {
525 cli_printf("\r\n%-24s %-24s", fm, dname);
526 } else if (m > 2 && m % 4 == 1) {
527 cli_printf("\r\n%-24s", dname);
528 } else {
529 cli_printf(" %-24s", dname);
530 }
531 }
532 n++;
533 }
534 }
535 }
536
537 closedir(pdir);
538
539 // if match all case, work done here.
540 if (match_all) {
541 cli_printf("\r\n");
542 goto redraw;
543 }
544
545 CLI_AUTO_PATH_DEBUG("%s %d, inbuf: %s, idx: %d, m: %d, n: %d\r\n", __func__,
546 __LINE__, inbuf, *idx, m, n);
547 /* there's only one match, so complete the line */
548 if (m == 1 && fm[0] != 0) {
549 n = strlen((const char *)fm) + ((fm_type == 1) ? 2 : 1);
550 if ((*idx + n + 1) < CLI_INBUF_SIZE) {
551 // memcpy(inbuf + *idx, fm + *idx, n);
552 snprintf(inbuf + *idx, CLI_INBUF_SIZE - *idx, "%s%s%s",
553 (inbuf[(*idx) - 1] == '/' || inbuf[(*idx) - 1] == ' ') ?
554 "" :
555 "/",
556 fm, fm_type == 1 ? "/" : "");
557 *idx += n;
558 inbuf[*idx] = '\0';
559 }
560 }
561
562 if (m >= 2 || m < 1) {
563 if (m >= 2) {
564 cli_printf("\r\n");
565 }
566 // strncpy(inbuf + *idx, last_name, CLI_INBUF_SIZE - *idx);
567 snprintf(inbuf + *idx, CLI_INBUF_SIZE - *idx, "%s%s",
568 (inbuf[(*idx) - 1] == '/' || inbuf[(*idx) - 1] == ' ') ? "" :
569 "/",
570 last_name);
571 *idx = strlen((const char *)inbuf);
572 }
573
574 CLI_AUTO_PATH_DEBUG("%s %d, inbuf: %s, idx: %d\r\n", __func__, __LINE__,
575 inbuf, *idx);
576
577 redraw:
578 /* just redraw input line */
579 if (match_all) {
580 len = strlen(inbuf);
581 if (inbuf[len - 1] != '/')
582 inbuf[len++] = '/';
583 inbuf[len] = '\0';
584 }
585
586 if (m >= 2 || match_all) {
587 cli_prefix_print();
588 }
589 cli_printf("%s", inbuf);
590 }
591 #endif
592
cli_history_input(char * cli_console_inbuf)593 static void cli_history_input(char *cli_console_inbuf)
594 {
595 CPSR_ALLOC();
596 RHINO_CPU_INTRPT_DISABLE();
597 char *inbuf = cli_console_inbuf;
598 int32_t charnum = strlen((const char *)cli_console_inbuf) + 1;
599 int32_t his_cur = g_cli->his_cur;
600 int32_t left_num = CLI_INBUF_SIZE - his_cur;
601
602 char lastchar;
603 int32_t tmp_idx;
604
605 g_cli->his_idx = his_cur;
606
607 if (left_num >= charnum) {
608 tmp_idx = his_cur + charnum - 1;
609 lastchar = g_cli->history[tmp_idx];
610 strncpy(&(g_cli->history[his_cur]), inbuf, charnum);
611
612 } else {
613 tmp_idx = (his_cur + charnum - 1) % CLI_INBUF_SIZE;
614 lastchar = g_cli->history[tmp_idx];
615
616 strncpy(&(g_cli->history[his_cur]), inbuf, left_num);
617 strncpy(&(g_cli->history[0]), inbuf + left_num, charnum - left_num);
618 }
619 tmp_idx = (tmp_idx + 1) % CLI_INBUF_SIZE;
620
621 g_cli->his_cur = tmp_idx;
622
623 /*overwrite*/
624 if ('\0' != lastchar) {
625 while (g_cli->history[tmp_idx] != '\0') {
626 g_cli->history[tmp_idx] = '\0';
627
628 tmp_idx = (tmp_idx + 1) % CLI_INBUF_SIZE;
629 }
630 }
631 RHINO_CPU_INTRPT_ENABLE();
632 }
633
cli_up_history(char * inaddr)634 static void cli_up_history(char *inaddr)
635 {
636 uint32_t index, detectindex, lastindex;
637
638 CPSR_ALLOC();
639 RHINO_CPU_INTRPT_DISABLE();
640
641 lastindex = g_cli->his_idx;
642 index = (g_cli->his_idx - 1 + CLI_INBUF_SIZE) % CLI_INBUF_SIZE;
643 detectindex = (index - 1 + CLI_INBUF_SIZE) % CLI_INBUF_SIZE;
644
645 while ((g_cli->history[index] == '\0') &&
646 g_cli->history[detectindex] != '\0' &&
647 (g_cli->his_idx != g_cli->his_cur ||
648 g_cli->history[g_cli->his_idx] == '\0') &&
649 (index != g_cli->his_idx)) {
650 index = (index - 1 + CLI_INBUF_SIZE) % CLI_INBUF_SIZE;
651 }
652 if (index != g_cli->his_idx) {
653 while (g_cli->history[index] != '\0' && index != g_cli->his_cur) {
654 index = (index - 1 + CLI_INBUF_SIZE) % CLI_INBUF_SIZE;
655 }
656 index = (index + 1) % CLI_INBUF_SIZE;
657 }
658 g_cli->his_idx = index;
659
660 while (g_cli->history[lastindex] != '\0') {
661 *inaddr++ = g_cli->history[lastindex];
662 lastindex = (lastindex + 1) % CLI_INBUF_SIZE;
663 }
664 *inaddr = '\0';
665
666 RHINO_CPU_INTRPT_ENABLE();
667 return;
668 }
669
cli_down_history(char * inaddr)670 static void cli_down_history(char *inaddr)
671 {
672 uint32_t index, lastindex;
673
674 CPSR_ALLOC();
675 RHINO_CPU_INTRPT_DISABLE();
676 lastindex = g_cli->his_idx;
677 index = g_cli->his_idx;
678
679 while ((g_cli->history[index] != '\0')) {
680 index = (index + 1) % CLI_INBUF_SIZE;
681 }
682 if (index != g_cli->his_idx) {
683 while (g_cli->history[index] == '\0' && index != g_cli->his_cur) {
684 index = (index + 1) % CLI_INBUF_SIZE;
685 }
686 }
687 g_cli->his_idx = index;
688
689 while (g_cli->history[lastindex] != '\0') {
690 *inaddr++ = g_cli->history[lastindex];
691 lastindex = (lastindex + 1) % CLI_INBUF_SIZE;
692 }
693
694 *inaddr = '\0';
695
696 RHINO_CPU_INTRPT_ENABLE();
697 return;
698 }
699
700 /**
701 * @brief Get an input line
702 *
703 * @param[in/out] inbuf poiner to the input buffer
704 * @param[out] bp the current buffer pointer
705 *
706 * @return 1 if there is input, 0 if the line should be ignored
707 *
708 * Note: the following Linux-like line editing commands are supported:
709 * Key Binding Editor Action
710 * Ctrl A Move cursor to start of the line
711 * Ctrl B Move left one character
712 * Ctrl D Delete a single character at the cursor position
713 * Ctrl E Move cursor to end of current line
714 * Ctrl F Move right one character
715 * Ctrl H or Backspace Delete character, left (backspace or Del key)
716 * Ctrl K Delete to the end of the line
717 * Ctrl U Delete the entire line
718 */
cli_get_input(char * inbuf,uint32_t size)719 static int32_t cli_get_input(char *inbuf, uint32_t size)
720 {
721 char c;
722 uint32_t i;
723
724 if (inbuf == NULL) {
725 cli_printf("input null\r\n");
726 return 0;
727 }
728
729 while (cli_getchar(&c) == 1) {
730 if (g_cli->cmd_end_pos >= size) {
731 cli_printf("\r\nError: input buffer overflow\r\n");
732 cli_prefix_print();
733 return 0;
734 }
735 /* received null or error */
736 if (c == '\0' || c == 0xFF) {
737 continue;
738 }
739
740 /* handle end of line, break */
741 if (c == RET_CHAR || c == END_CHAR) { /* end of input line */
742 g_cli->inbuf[g_cli->cmd_end_pos] = '\0';
743 memcpy(inbuf, g_cli->inbuf, size);
744 memset(g_cli->inbuf, 0, size);
745 g_cli->cmd_cur_pos = 0;
746 g_cli->cmd_end_pos = 0;
747 return 1;
748 }
749 /*
750 * handle arrow keys
751 * up key : 0x1b 0x5b 0x41
752 * down key: 0x1b 0x5b 0x42
753 * right key:0x1b 0x5b 0x43
754 * left key: 0x1b 0x5b 0x44
755 */
756 if (c == 0x1b) {
757 g_cli->input_status = CLI_WAIT_SPEC_KEY;
758 continue;
759 } else if (g_cli->input_status == CLI_WAIT_SPEC_KEY) {
760 if (c == 0x5b) {
761 g_cli->input_status = CLI_WAIT_FUNC_KEY;
762 continue;
763 }
764 g_cli->input_status = CLI_WAIT_NORMAL;
765 } else if (g_cli->input_status == CLI_WAIT_FUNC_KEY) {
766 g_cli->input_status = CLI_WAIT_NORMAL;
767 /* handle up/down/left/right key */
768 if (c == 0x41 || c == 0x42) {
769 /* UP or DWOWN key */
770 if (c == 0x41) {
771 cli_up_history(g_cli->inbuf);
772 } else {
773 cli_down_history(g_cli->inbuf);
774 }
775 g_cli->cmd_cur_pos = strlen((const char *)g_cli->inbuf);
776 g_cli->cmd_end_pos = g_cli->cmd_cur_pos;
777
778 /* clear the whole line */
779 cli_printf("\33[2K\r");
780 cli_prefix_print();
781 cli_printf("%s", g_cli->inbuf);
782 continue;
783 } else if (c == 0x44) {
784 /* left key */
785 if (g_cli->cmd_cur_pos) {
786 cli_printf("\b");
787 g_cli->cmd_cur_pos--;
788 }
789 continue;
790 } else if (c == 0x43) {
791 /* right key */
792 if (g_cli->cmd_cur_pos < g_cli->cmd_end_pos) {
793 cli_printf("%c", g_cli->inbuf[g_cli->cmd_cur_pos]);
794 g_cli->cmd_cur_pos++;
795 }
796 continue;
797 }
798 }
799 /* handle tab key */
800 if (c == '\t') {
801 /* Move the cursor to the beginning of line */
802 for (i = 0; i < g_cli->cmd_cur_pos; i++) {
803 cli_printf("\b");
804 }
805
806 g_cli->inbuf[g_cli->cmd_end_pos] = '\0';
807 #if CLI_IOBOX_ENABLE
808 if (strstr((const char *)g_cli->inbuf, " ")) {
809 cli_tab_complete_path(g_cli->inbuf, &g_cli->cmd_end_pos);
810 } else {
811 #endif
812 cli_tab_complete(g_cli->inbuf, &g_cli->cmd_end_pos);
813 #if CLI_IOBOX_ENABLE
814 }
815 #endif
816 g_cli->cmd_cur_pos = strlen((const char *)g_cli->inbuf);
817 g_cli->cmd_end_pos = g_cli->cmd_cur_pos;
818 continue;
819 }
820 /* handle backspace or Ctrl H or Del key*/
821 if ((c == 0x08) || (c == 0x7F)) {
822 if (g_cli->cmd_cur_pos == 0) {
823 continue;
824 }
825
826 g_cli->cmd_cur_pos--;
827 g_cli->cmd_end_pos--;
828
829 if (g_cli->cmd_end_pos > g_cli->cmd_cur_pos) {
830 memmove(&g_cli->inbuf[g_cli->cmd_cur_pos],
831 &g_cli->inbuf[g_cli->cmd_cur_pos + 1],
832 g_cli->cmd_end_pos - g_cli->cmd_cur_pos);
833
834 g_cli->inbuf[g_cli->cmd_end_pos] = 0;
835 if (!g_cli->echo_disabled) {
836 cli_printf("\b%s \b", &g_cli->inbuf[g_cli->cmd_cur_pos]);
837 }
838 /* move cursor */
839 for (i = g_cli->cmd_cur_pos; i <= g_cli->cmd_end_pos; i++) {
840 cli_printf("\b");
841 }
842 } else {
843 cli_printf("\b \b");
844 g_cli->inbuf[g_cli->cmd_end_pos] = 0;
845 }
846 continue;
847 }
848 /* handle Ctrl B */
849 if (c == 0x02) {
850 /* Same as left key < -- */
851 if (g_cli->cmd_cur_pos) {
852 cli_printf("\b");
853 g_cli->cmd_cur_pos--;
854 }
855 continue;
856 }
857 /* handle Ctrl F */
858 if (c == 0x06) {
859 /* Same as right key --> */
860 if (g_cli->cmd_cur_pos < g_cli->cmd_end_pos) {
861 cli_printf("%c", g_cli->inbuf[g_cli->cmd_cur_pos]);
862 g_cli->cmd_cur_pos++;
863 }
864 continue;
865 }
866 /* handle Ctrl U */
867 if (c == 0x15) {
868 /* Delete the entire line */
869 cli_printf("\33[2K\r");
870 cli_prefix_print();
871 g_cli->cmd_cur_pos = 0;
872 g_cli->cmd_end_pos = 0;
873 continue;
874 }
875 /* handle Ctrl K */
876 if (c == 0x0B) {
877 /* Delete to the end of the line */
878 if (g_cli->cmd_cur_pos < g_cli->cmd_end_pos) {
879 for (i = g_cli->cmd_cur_pos; i < g_cli->cmd_end_pos; i++) {
880 cli_printf(" ");
881 }
882 for (i = g_cli->cmd_cur_pos; i < g_cli->cmd_end_pos; i++) {
883 cli_printf("\b");
884 }
885 g_cli->cmd_end_pos = g_cli->cmd_cur_pos;
886 }
887 continue;
888 }
889 /* handle Ctrl A */
890 if (c == 0x01) {
891 /* Move cursor to start of the line */
892 for (i = 0; i < g_cli->cmd_cur_pos; i++) {
893 cli_printf("\b");
894 }
895 g_cli->cmd_cur_pos = 0;
896 continue;
897 }
898 /* handle Ctrl E */
899 if (c == 0x05) {
900 /* Move cursor to end of the line */
901 if (g_cli->cmd_cur_pos < g_cli->cmd_end_pos) {
902 for (i = g_cli->cmd_cur_pos; i < g_cli->cmd_end_pos; i++) {
903 cli_printf("%c", g_cli->inbuf[i]);
904 }
905 g_cli->cmd_cur_pos = g_cli->cmd_end_pos;
906 }
907 continue;
908 }
909 /* handle Ctrl D */
910 if (c == 0x04) {
911 /* Delete a single character at the cursor position */
912 if ((g_cli->cmd_end_pos == 0) ||
913 (g_cli->cmd_cur_pos == g_cli->cmd_end_pos)) {
914 continue;
915 }
916 g_cli->cmd_end_pos--;
917
918 if (g_cli->cmd_cur_pos == g_cli->cmd_end_pos) {
919 /* cursor point to last character */
920 cli_printf(" \b");
921 } else {
922 /* end_pos > cur_pos */
923 memmove(&g_cli->inbuf[g_cli->cmd_cur_pos],
924 &g_cli->inbuf[g_cli->cmd_cur_pos + 1],
925 g_cli->cmd_end_pos - g_cli->cmd_cur_pos);
926
927 g_cli->inbuf[g_cli->cmd_end_pos] = 0;
928 if (!g_cli->echo_disabled) {
929 cli_printf("%s \b", &g_cli->inbuf[g_cli->cmd_cur_pos]);
930 }
931 /* move cursor */
932 for (i = g_cli->cmd_cur_pos; i <= g_cli->cmd_end_pos; i++) {
933 cli_printf("\b");
934 }
935 }
936 continue;
937 }
938
939 /* discard large cmd */
940 if (g_cli->cmd_end_pos >= size) {
941 g_cli->cmd_end_pos = 0;
942 }
943
944 /* others: handle normal character */
945 if (g_cli->cmd_cur_pos < g_cli->cmd_end_pos) {
946 memmove(&g_cli->inbuf[g_cli->cmd_cur_pos + 1],
947 &g_cli->inbuf[g_cli->cmd_cur_pos],
948 g_cli->cmd_end_pos - g_cli->cmd_cur_pos);
949 g_cli->inbuf[g_cli->cmd_cur_pos] = c;
950
951 if (!g_cli->echo_disabled) {
952 cli_printf("%s", &g_cli->inbuf[g_cli->cmd_cur_pos]);
953 }
954
955 /* move cursor to new position */
956 for (i = g_cli->cmd_cur_pos; i < g_cli->cmd_end_pos; i++) {
957 cli_printf("\b");
958 }
959 } else {
960 g_cli->inbuf[g_cli->cmd_end_pos] = c;
961 if (!g_cli->echo_disabled) {
962 cli_printf("%c", c);
963 }
964 }
965
966 g_cli->cmd_cur_pos++;
967 g_cli->cmd_end_pos++;
968 if (g_cli->cmd_end_pos >= size) {
969 g_cli->cmd_cur_pos = 0;
970 g_cli->cmd_end_pos = 0;
971 }
972
973 c = 0;
974 }
975
976 return 0;
977 }
978
979 /**
980 * @brief Print out a bad command string
981 *
982 * @param[in] cmd_string the command string
983 *
984 * @return none
985 *
986 * @Note print including a hex representation of non-printable characters.
987 * Non-printable characters show as "\0xXX".
988 */
cli_print_bad_command(char * cmd_string)989 static void cli_print_bad_command(char *cmd_string)
990 {
991 if (cmd_string != NULL) {
992 cli_printf("command '%s' not found\r\n", cmd_string);
993 }
994 }
995
996 /**
997 * @brief Main CLI processing loop
998 *
999 * @param[in] data pointer to the process arguments
1000 *
1001 * @return none
1002 *
1003 * @Note Waits to receive a command buffer pointer from an input collector,
1004 * and then process. it must cleanup the buffer when done with it.
1005 * Input collectors handle their own lexical analysis and must pass complete
1006 * command lines to CLI.
1007 *
1008 */
cli_main(void * data)1009 void cli_main(void *data)
1010 {
1011 int32_t ret;
1012
1013 char *msg = NULL;
1014 cli_console *cur_console = NULL;
1015
1016 cli_task_set_console(krhino_cur_task_get(), (cli_console *)data);
1017
1018 CPSR_ALLOC();
1019 RHINO_CPU_INTRPT_DISABLE();
1020 if (g_cli == NULL) {
1021 g_cli = (struct cli_status *)cli_malloc(sizeof(struct cli_status));
1022 if (g_cli == NULL) {
1023 printf("%s : %d malloc fail\r\n", __func__, __LINE__);
1024 RHINO_CPU_INTRPT_ENABLE();
1025 return;
1026 }
1027 memset((void *)g_cli, 0, sizeof(struct cli_status));
1028 }
1029 RHINO_CPU_INTRPT_ENABLE();
1030 if (get_clitask_console() != get_default_console()) {
1031 cli_printf("\r\n%s\r\n",
1032 " Welcome to AliOS Things ");
1033 }
1034
1035 #if CLI_IOBOX_ENABLE
1036 ret = aos_chdir(CONFIG_LFS_MOUNTPOINT);
1037 if (ret != 0) {
1038 cli_printf("Failed to change to %s, errno: %d\r\n",
1039 CONFIG_LFS_MOUNTPOINT, ret);
1040 }
1041 #endif
1042 cli_prefix_print();
1043
1044 #if CLI_SEPRATED_CONSOLE
1045 char ch = 0x1F;
1046 cli_console_set_tag(ch, 0, NULL);
1047 #endif
1048
1049 while (!cli_console_task_check_exit_flag()) {
1050 cur_console = get_clitask_console();
1051 if (cur_console == NULL) {
1052 break;
1053 }
1054 if (cli_get_input(cur_console->cli_console_inbuf, CLI_INBUF_SIZE) !=
1055 0) {
1056 msg = cur_console->cli_console_inbuf;
1057 if (!msg) {
1058 goto out;
1059 }
1060 if (cur_console->start_callback) {
1061 cur_console->start_callback(cur_console->private_data);
1062 }
1063 if (strlen(cur_console->cli_console_inbuf) > 0) {
1064 cli_history_input(cur_console->cli_console_inbuf);
1065 }
1066 ret = cli_handle_input(msg);
1067 switch (ret) {
1068 case CLI_ERR_SYNTAX:
1069 cli_printf("syntax error\r\n");
1070 break;
1071 case CLI_ERR_CMDNOTEXIST:
1072 cli_printf("cmd not found\r\n");
1073 break;
1074 case CLI_ERR_BADCMD:
1075 cli_print_bad_command(msg);
1076 break;
1077 default:
1078 break;
1079 }
1080
1081 if (cur_console->finsh_callback) {
1082 cur_console->finsh_callback(cur_console->private_data);
1083 }
1084 out:
1085 cli_printf("\r\n");
1086 cli_prefix_print();
1087 }
1088 }
1089 cli_printf("CLI exited\r\n");
1090
1091 cli_console_current_task_destory();
1092 check_console_task_exit();
1093 }
1094
cli_main_panic(void)1095 void cli_main_panic(void)
1096 {
1097 int32_t ret;
1098 char *msg = NULL;
1099 char cli_console_inbuf[CLI_INBUF_SIZE] = {0};
1100
1101 /* set uart console for panic*/
1102 cli_task_set_console(krhino_cur_task_get(), &cli_uart_console);
1103
1104 while (1) {
1105 if (cli_get_input(cli_console_inbuf, CLI_INBUF_SIZE) != 0) {
1106 msg = cli_console_inbuf;
1107
1108 if (strlen((const char *)cli_console_inbuf) > 0) {
1109 cli_history_input(cli_console_inbuf);
1110 }
1111
1112 ret = cli_handle_input(msg);
1113 switch (ret) {
1114 case CLI_ERR_SYNTAX:
1115 cli_printf("syntax error\r\n");
1116 break;
1117 case CLI_ERR_CMDNOTEXIST:
1118 cli_printf("cmd not found\r\n");
1119 break;
1120 case CLI_ERR_BADCMD:
1121 cli_print_bad_command(msg);
1122 break;
1123 default:
1124 break;
1125 }
1126
1127 cli_printf("\r\n");
1128 cli_printf("(panic)#");
1129 }
1130 }
1131 }
1132
1133 __attribute__((weak)) struct cli_region _cli_region_begin, _cli_region_end;
1134
usr_cli_register_init(void)1135 void usr_cli_register_init(void)
1136 {
1137 int32_t ret;
1138 intptr_t addr;
1139 struct cli_region *index;
1140 struct cli_command *cmd;
1141
1142 for (addr = (intptr_t)&_cli_region_begin; addr < (intptr_t)&_cli_region_end;) {
1143 index = (struct cli_region *)addr;
1144 addr += sizeof(struct cli_region);
1145
1146 cmd = (struct cli_command *)cli_malloc(sizeof(struct cli_command));
1147 if (cmd == NULL) {
1148 cli_printf("usr cli malloc fail\n");
1149 return;
1150 }
1151
1152 cmd->name = index->name;
1153 cmd->help = index->desc;
1154 cmd->function = (cmd_fun_t)(index->func);
1155
1156 ret = cli_register_command(cmd);
1157 if (ret != CLI_OK) {
1158 cli_printf("usr cli register fail\n");
1159 return;
1160 }
1161 }
1162 }
1163
help_cmd(char * buf,int len,int argc,char ** argv)1164 static void help_cmd(char *buf, int len, int argc, char **argv)
1165 {
1166 int32_t i, n, commands_num;
1167 struct cli_command *cmd = NULL;
1168
1169 commands_num = cli_get_commands_num();
1170
1171 cli_printf("================ AliOS Things Command List ==============\r\n");
1172
1173 for (i = 0, n = 0; i < commands_num; i++) {
1174 cmd = cli_get_command(i);
1175 if (!cmd) {
1176 cli_printf("cmd is null\r\n");
1177 continue;
1178 }
1179 if (cmd->name) {
1180 cli_printf("%-15s: %s\r\n", cmd->name, cmd->help ? cmd->help : "");
1181 n++;
1182 }
1183 }
1184 cli_printf(
1185 "****************** Commands Num : %d *******************\r\n\r\n", n);
1186 cli_printf(
1187 "================ AliOS Things Command end ===============\r\n\r\n");
1188 }
1189
reboot_cmd(char * buf,int len,int argc,char ** argv)1190 static void reboot_cmd(char *buf, int len, int argc, char **argv)
1191 {
1192 hal_reboot();
1193 }
1194
1195 static const struct cli_command built_ins[] = {
1196 /*cli self*/
1197 {"help", "print this", help_cmd},
1198 {"reboot", "reboot system", reboot_cmd},
1199 };
1200
cli_register_default_commands(void)1201 static int32_t cli_register_default_commands(void)
1202 {
1203 int32_t ret;
1204
1205 ret = cli_register_commands(built_ins,
1206 sizeof(built_ins) / sizeof(struct cli_command));
1207 if (ret != CLI_OK) {
1208 return ret;
1209 }
1210
1211 return CLI_OK;
1212 }
1213
cli_init(void)1214 int32_t cli_init(void)
1215 {
1216 int32_t ret;
1217
1218 g_cli = (struct cli_status *)cli_malloc(sizeof(struct cli_status));
1219 if (g_cli == NULL) {
1220 return CLI_ERR_NOMEM;
1221 }
1222
1223 memset((void *)g_cli, 0, sizeof(struct cli_status));
1224
1225 ret = create_default_console_task(CLI_CONFIG_STACK_SIZE, CLI_TASK_PRIORITY);
1226 if (ret != CLI_OK) {
1227 cli_printf("Error: Failed to create cli thread: %d\r\n", ret);
1228 goto init_err;
1229 }
1230
1231 g_cli->inited = 1;
1232 g_cli->echo_disabled = 0;
1233
1234 ret = cli_register_default_commands();
1235 if (ret != CLI_OK) {
1236 cli_printf("Error: register built-in commands failed");
1237 goto init_err;
1238 }
1239
1240 /* register cli cmd for ALIOS_CLI_CMD_REGISTER */
1241 usr_cli_register_init();
1242
1243 #if CLI_UAGENT_ENABLE
1244 cli_uagent_init();
1245 #endif
1246
1247 return CLI_OK;
1248
1249 init_err:
1250 if (g_cli != NULL) {
1251 cli_free(g_cli);
1252 g_cli = NULL;
1253 }
1254
1255 return ret;
1256 }
1257
cli_stop(void)1258 int32_t cli_stop(void)
1259 {
1260 cli_console_set_exit_flag(get_clitask_console());
1261
1262 return CLI_OK;
1263 }
1264
cli_tag_get(void)1265 char *cli_tag_get(void) { return cli_console_get_all_tag(NULL); }
1266
cli_register_command(const struct cli_command * cmd)1267 int32_t cli_register_command(const struct cli_command *cmd)
1268 {
1269 int32_t i = 0;
1270
1271 if (g_cli == NULL) {
1272 return CLI_ERR_DENIED;
1273 }
1274
1275 if (!cmd->name || !cmd->function) {
1276 return CLI_ERR_INVALID;
1277 }
1278
1279 if (g_cli->num >= CLI_MAX_COMMANDS) {
1280 return CLI_ERR_NOMEM;
1281 }
1282
1283 /*
1284 * Check if the command has already been registered.
1285 * Return 0, if it has been registered.
1286 */
1287 for (i = 0; i < g_cli->num; i++) {
1288 if (g_cli->cmds[i] == cmd) {
1289 return CLI_OK;
1290 }
1291 }
1292
1293 g_cli->cmds[g_cli->num++] = cmd;
1294
1295 return CLI_OK;
1296 }
1297
cli_unregister_command(const struct cli_command * cmd)1298 int32_t cli_unregister_command(const struct cli_command *cmd)
1299 {
1300 int32_t remaining_cmds;
1301 int32_t i = 0;
1302 if (g_cli == NULL) {
1303 return CLI_ERR_DENIED;
1304 }
1305
1306 if (!cmd->name || !cmd->function) {
1307 return CLI_ERR_INVALID;
1308 }
1309
1310 for (i = 0; i < g_cli->num; i++) {
1311 if (g_cli->cmds[i] == cmd) {
1312 g_cli->num--;
1313
1314 remaining_cmds = g_cli->num - i;
1315 if (remaining_cmds > 0) {
1316 memmove(&g_cli->cmds[i], &g_cli->cmds[i + 1],
1317 (remaining_cmds * sizeof(struct cli_command *)));
1318 }
1319
1320 g_cli->cmds[g_cli->num] = NULL;
1321
1322 return CLI_OK;
1323 }
1324 }
1325
1326 return CLI_ERR_NOMEM;
1327 }
1328
cli_register_commands(const struct cli_command * cmds,int32_t num)1329 int32_t cli_register_commands(const struct cli_command *cmds, int32_t num)
1330 {
1331 int32_t i, err;
1332
1333 for (i = 0; i < num; i++) {
1334 if ((err = cli_register_command(cmds++)) != 0) {
1335 return err;
1336 }
1337 }
1338
1339 return CLI_OK;
1340 }
1341
cli_unregister_commands(const struct cli_command * cmds,int32_t num)1342 int32_t cli_unregister_commands(const struct cli_command *cmds, int32_t num)
1343 {
1344 int32_t i, err;
1345
1346 for (i = 0; i < num; i++) {
1347 if ((err = cli_unregister_command(cmds++)) != 0) {
1348 return err;
1349 }
1350 }
1351
1352 return CLI_OK;
1353 }
1354
cli_do_output(char * msg)1355 static int32_t cli_do_output(char *msg)
1356 {
1357 if (cli_console_get_tag_len(NULL) > 0) {
1358 cli_putstr(cli_console_get_all_tag(NULL));
1359 }
1360 return cli_putstr(msg);
1361 }
1362
cli_va_printf(const char * fmt,va_list va)1363 int32_t cli_va_printf(const char *fmt, va_list va)
1364 {
1365 int32_t len;
1366 char *message = NULL, *child_message = NULL;
1367
1368 #if AOS_COMP_DEBUG
1369 int ret;
1370 (void)ret;
1371 extern uint32_t debug_cpu_in_crash(void);
1372 if (debug_cpu_in_crash()) {
1373 #if CLI_SEPRATED_CONSOLE
1374 /* for smartTrace tool */
1375 char temp[1] = {0x1F};
1376 extern int alios_debug_print(const char *buf, int size);
1377 ret = alios_debug_print(temp, 1);
1378 if (ret != 1) {
1379 return 0;
1380 }
1381 #endif
1382 extern int print_driver(const char *fmt, va_list ap,
1383 unsigned int buf[]);
1384 print_driver(fmt, va, NULL);
1385 return 0;
1386 }
1387 #endif
1388
1389 message = (char *)cli_malloc(CLI_OUTBUF_SIZE);
1390 if (message == NULL) {
1391 return CLI_ERR_NOMEM;
1392 }
1393 memset(message, 0, CLI_OUTBUF_SIZE);
1394
1395 child_message = (char *)cli_malloc(CLI_OUTBUF_SIZE);
1396 if (child_message == NULL) {
1397 cli_free(message);
1398 return CLI_ERR_NOMEM;
1399 }
1400 memset(child_message, 0, CLI_OUTBUF_SIZE);
1401
1402 len = vsnprintf(message, CLI_OUTBUF_SIZE, fmt, va);
1403
1404 if (len <= 0) {
1405 cli_free(message);
1406 cli_free(child_message);
1407 return CLI_OK;
1408 }
1409
1410 /* multi '\n' in one cli_printf */
1411 int i = 0;
1412 char *p2 = message;
1413 char *p;
1414 while (i != -1) {
1415 i = ((p = strchr(p2, '\n')) == NULL) ? -1 : (p - p2);
1416 if (i == -1) {
1417 // cli_printf("%s", p2);
1418 if (*p2) {
1419 cli_do_output(p2);
1420 }
1421 } else {
1422 // cli_printf("%.*s\n", i + 1, p2); // strlen = pos + 1
1423 memset(child_message, 0, CLI_OUTBUF_SIZE);
1424 strncpy(child_message, p2, i + 1);
1425 cli_do_output(child_message);
1426 }
1427 p2 = p + 1; // skip '\n'
1428 }
1429
1430 cli_free(message);
1431 cli_free(child_message);
1432
1433 return CLI_OK;
1434 }
1435
cli_printf(const char * fmt,...)1436 int32_t cli_printf(const char *fmt, ...)
1437 {
1438 va_list params;
1439 int32_t ret;
1440
1441 va_start(params, fmt);
1442 ret = cli_va_printf(fmt, params);
1443 va_end(params);
1444 return ret;
1445 }
1446
cli_get_commands_num(void)1447 int32_t cli_get_commands_num(void) { return g_cli->num; }
1448
cli_get_command(int32_t index)1449 struct cli_command *cli_get_command(int32_t index)
1450 {
1451 return (struct cli_command *)(g_cli->cmds[index]);
1452 }
1453
cli_get_echo_status(void)1454 int32_t cli_get_echo_status(void) { return g_cli->echo_disabled; }
1455
cli_set_echo_status(int32_t status)1456 int32_t cli_set_echo_status(int32_t status)
1457 {
1458 g_cli->echo_disabled = status;
1459
1460 return CLI_OK;
1461 }
1462