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