1 // Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
18 #include <sys/param.h>
20 #include "esp_console.h"
21 #include "linenoise/linenoise.h"
22 #include "argtable3/argtable3.h"
23 #include "rom/queue.h"
25 #define ANSI_COLOR_DEFAULT 39 /** Default foreground color */
27 typedef struct cmd_item_ {
29 * Command name (statically allocated by application)
33 * Help text (statically allocated by application), may be NULL.
37 * Hint text, usually lists possible arguments, dynamically allocated.
41 esp_console_cmd_func_t func; //!< pointer to the command handler
42 void *argtable; //!< optional pointer to arg table
43 SLIST_ENTRY(cmd_item_) next; //!< next command in the list
46 /** linked list of command structures */
47 static SLIST_HEAD(cmd_list_, cmd_item_) s_cmd_list;
49 /** run-time configuration options */
50 static esp_console_config_t s_config;
52 /** temporary buffer used for command line parsing */
53 static char *s_tmp_line_buf;
55 static const cmd_item_t *find_command_by_name(const char *name);
57 esp_err_t esp_console_init(const esp_console_config_t *config)
60 return ESP_ERR_INVALID_STATE;
62 memcpy(&s_config, config, sizeof(s_config));
63 if (s_config.hint_color == 0) {
64 s_config.hint_color = ANSI_COLOR_DEFAULT;
66 s_tmp_line_buf = calloc(config->max_cmdline_length, 1);
67 if (s_tmp_line_buf == NULL) {
68 return ESP_ERR_NO_MEM;
73 esp_err_t esp_console_deinit()
75 if (!s_tmp_line_buf) {
76 return ESP_ERR_INVALID_STATE;
80 SLIST_FOREACH_SAFE(it, &s_cmd_list, next, tmp) {
87 esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd)
89 cmd_item_t *item = (cmd_item_t *) calloc(1, sizeof(*item));
91 return ESP_ERR_NO_MEM;
93 if (cmd->command == NULL) {
95 return ESP_ERR_INVALID_ARG;
97 if (strchr(cmd->command, ' ') != NULL) {
99 return ESP_ERR_INVALID_ARG;
101 item->command = cmd->command;
102 item->help = cmd->help;
104 /* Prepend a space before the hint. It separates command name and
105 * the hint. arg_print_syntax below adds this space as well.
107 asprintf(&item->hint, " %s", cmd->hint);
108 } else if (cmd->argtable) {
109 /* Generate hint based on cmd->argtable */
112 FILE *f = open_memstream(&buf, &buf_size);
114 arg_print_syntax(f, cmd->argtable, NULL);
119 item->argtable = cmd->argtable;
120 item->func = cmd->func;
121 cmd_item_t *last = SLIST_FIRST(&s_cmd_list);
123 SLIST_INSERT_HEAD(&s_cmd_list, item, next);
126 while ((it = SLIST_NEXT(last, next)) != NULL) {
129 SLIST_INSERT_AFTER(last, item, next);
134 void esp_console_get_completion(const char *buf, linenoiseCompletions *lc)
136 size_t len = strlen(buf);
141 SLIST_FOREACH(it, &s_cmd_list, next) {
142 /* Check if command starts with buf */
143 if (strncmp(buf, it->command, len) == 0) {
144 linenoiseAddCompletion(lc, it->command);
149 const char *esp_console_get_hint(const char *buf, int *color, int *bold)
151 int len = strlen(buf);
153 SLIST_FOREACH(it, &s_cmd_list, next) {
154 if (strlen(it->command) == len &&
155 strncmp(buf, it->command, len) == 0) {
156 *color = s_config.hint_color;
157 *bold = s_config.hint_bold;
164 static const cmd_item_t *find_command_by_name(const char *name)
166 const cmd_item_t *cmd = NULL;
168 SLIST_FOREACH(it, &s_cmd_list, next) {
169 if (strcmp(name, it->command) == 0) {
177 esp_err_t esp_console_run(const char *cmdline, int *cmd_ret)
179 if (s_tmp_line_buf == NULL) {
180 return ESP_ERR_INVALID_STATE;
182 char **argv = (char **) calloc(s_config.max_cmdline_args, sizeof(char *));
184 return ESP_ERR_NO_MEM;
186 strlcpy(s_tmp_line_buf, cmdline, s_config.max_cmdline_length);
188 size_t argc = esp_console_split_argv(s_tmp_line_buf, argv,
189 s_config.max_cmdline_args);
192 return ESP_ERR_INVALID_ARG;
194 const cmd_item_t *cmd = find_command_by_name(argv[0]);
197 return ESP_ERR_NOT_FOUND;
199 *cmd_ret = (*cmd->func)(argc, argv);
204 static int help_command(int argc, char **argv)
208 /* Print summary of each command */
209 SLIST_FOREACH(it, &s_cmd_list, next) {
210 if (it->help == NULL) {
213 /* First line: command name and hint
214 * Pad all the hints to the same column
216 const char *hint = (it->hint) ? it->hint : "";
217 printf("%-s %s\n", it->command, hint);
218 /* Second line: print help.
219 * Argtable has a nice helper function for this which does line
222 printf(" "); // arg_print_formatted does not indent the first line
223 arg_print_formatted(stdout, 2, 78, it->help);
224 /* Finally, print the list of arguments */
226 arg_print_glossary(stdout, (void **) it->argtable, " %12s %s\n");
234 esp_err_t esp_console_register_help_command()
236 esp_console_cmd_t command = {
238 .help = "Print the list of registered commands",
239 .func = &help_command
241 return esp_console_cmd_register(&command);