mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-09-19 00:21:15 +08:00
feat(console): Bring console and its example from esp-idf
Commit ID: c7d0df54
This commit is contained in:
243
components/console/commands.c
Normal file
243
components/console/commands.c
Normal file
@ -0,0 +1,243 @@
|
||||
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_console.h"
|
||||
#include "linenoise/linenoise.h"
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "rom/queue.h"
|
||||
|
||||
#define ANSI_COLOR_DEFAULT 39 /** Default foreground color */
|
||||
|
||||
typedef struct cmd_item_ {
|
||||
/**
|
||||
* Command name (statically allocated by application)
|
||||
*/
|
||||
const char *command;
|
||||
/**
|
||||
* Help text (statically allocated by application), may be NULL.
|
||||
*/
|
||||
const char *help;
|
||||
/**
|
||||
* Hint text, usually lists possible arguments, dynamically allocated.
|
||||
* May be NULL.
|
||||
*/
|
||||
char *hint;
|
||||
esp_console_cmd_func_t func; //!< pointer to the command handler
|
||||
void *argtable; //!< optional pointer to arg table
|
||||
SLIST_ENTRY(cmd_item_) next; //!< next command in the list
|
||||
} cmd_item_t;
|
||||
|
||||
/** linked list of command structures */
|
||||
static SLIST_HEAD(cmd_list_, cmd_item_) s_cmd_list;
|
||||
|
||||
/** run-time configuration options */
|
||||
static esp_console_config_t s_config;
|
||||
|
||||
/** temporary buffer used for command line parsing */
|
||||
static char *s_tmp_line_buf;
|
||||
|
||||
static const cmd_item_t *find_command_by_name(const char *name);
|
||||
|
||||
esp_err_t esp_console_init(const esp_console_config_t *config)
|
||||
{
|
||||
if (s_tmp_line_buf) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
memcpy(&s_config, config, sizeof(s_config));
|
||||
if (s_config.hint_color == 0) {
|
||||
s_config.hint_color = ANSI_COLOR_DEFAULT;
|
||||
}
|
||||
s_tmp_line_buf = calloc(config->max_cmdline_length, 1);
|
||||
if (s_tmp_line_buf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_console_deinit()
|
||||
{
|
||||
if (!s_tmp_line_buf) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
free(s_tmp_line_buf);
|
||||
cmd_item_t *it, *tmp;
|
||||
SLIST_FOREACH_SAFE(it, &s_cmd_list, next, tmp) {
|
||||
free(it->hint);
|
||||
free(it);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd)
|
||||
{
|
||||
cmd_item_t *item = (cmd_item_t *) calloc(1, sizeof(*item));
|
||||
if (item == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (cmd->command == NULL) {
|
||||
free(item);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (strchr(cmd->command, ' ') != NULL) {
|
||||
free(item);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
item->command = cmd->command;
|
||||
item->help = cmd->help;
|
||||
if (cmd->hint) {
|
||||
/* Prepend a space before the hint. It separates command name and
|
||||
* the hint. arg_print_syntax below adds this space as well.
|
||||
*/
|
||||
int unused __attribute__((unused));
|
||||
unused = asprintf(&item->hint, " %s", cmd->hint);
|
||||
} else if (cmd->argtable) {
|
||||
/* Generate hint based on cmd->argtable */
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
if (f != NULL) {
|
||||
arg_print_syntax(f, cmd->argtable, NULL);
|
||||
fclose(f);
|
||||
}
|
||||
item->hint = buf;
|
||||
}
|
||||
item->argtable = cmd->argtable;
|
||||
item->func = cmd->func;
|
||||
cmd_item_t *last = SLIST_FIRST(&s_cmd_list);
|
||||
if (last == NULL) {
|
||||
SLIST_INSERT_HEAD(&s_cmd_list, item, next);
|
||||
} else {
|
||||
cmd_item_t *it;
|
||||
while ((it = SLIST_NEXT(last, next)) != NULL) {
|
||||
last = it;
|
||||
}
|
||||
SLIST_INSERT_AFTER(last, item, next);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_console_get_completion(const char *buf, linenoiseCompletions *lc)
|
||||
{
|
||||
size_t len = strlen(buf);
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
cmd_item_t *it;
|
||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
||||
/* Check if command starts with buf */
|
||||
if (strncmp(buf, it->command, len) == 0) {
|
||||
linenoiseAddCompletion(lc, it->command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *esp_console_get_hint(const char *buf, int *color, int *bold)
|
||||
{
|
||||
int len = strlen(buf);
|
||||
cmd_item_t *it;
|
||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
||||
if (strlen(it->command) == len &&
|
||||
strncmp(buf, it->command, len) == 0) {
|
||||
*color = s_config.hint_color;
|
||||
*bold = s_config.hint_bold;
|
||||
return it->hint;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const cmd_item_t *find_command_by_name(const char *name)
|
||||
{
|
||||
const cmd_item_t *cmd = NULL;
|
||||
cmd_item_t *it;
|
||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
||||
if (strcmp(name, it->command) == 0) {
|
||||
cmd = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
esp_err_t esp_console_run(const char *cmdline, int *cmd_ret)
|
||||
{
|
||||
if (s_tmp_line_buf == NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
char **argv = (char **) calloc(s_config.max_cmdline_args, sizeof(char *));
|
||||
if (argv == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
strlcpy(s_tmp_line_buf, cmdline, s_config.max_cmdline_length);
|
||||
|
||||
size_t argc = esp_console_split_argv(s_tmp_line_buf, argv,
|
||||
s_config.max_cmdline_args);
|
||||
if (argc == 0) {
|
||||
free(argv);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
const cmd_item_t *cmd = find_command_by_name(argv[0]);
|
||||
if (cmd == NULL) {
|
||||
free(argv);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
*cmd_ret = (*cmd->func)(argc, argv);
|
||||
free(argv);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int help_command(int argc, char **argv)
|
||||
{
|
||||
cmd_item_t *it;
|
||||
|
||||
/* Print summary of each command */
|
||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
||||
if (it->help == NULL) {
|
||||
continue;
|
||||
}
|
||||
/* First line: command name and hint
|
||||
* Pad all the hints to the same column
|
||||
*/
|
||||
const char *hint = (it->hint) ? it->hint : "";
|
||||
printf("%-s %s\n", it->command, hint);
|
||||
/* Second line: print help.
|
||||
* Argtable has a nice helper function for this which does line
|
||||
* wrapping.
|
||||
*/
|
||||
printf(" "); // arg_print_formatted does not indent the first line
|
||||
arg_print_formatted(stdout, 2, 78, it->help);
|
||||
/* Finally, print the list of arguments */
|
||||
if (it->argtable) {
|
||||
arg_print_glossary(stdout, (void **) it->argtable, " %12s %s\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_console_register_help_command()
|
||||
{
|
||||
esp_console_cmd_t command = {
|
||||
.command = "help",
|
||||
.help = "Print the list of registered commands",
|
||||
.func = &help_command
|
||||
};
|
||||
return esp_console_cmd_register(&command);
|
||||
}
|
Reference in New Issue
Block a user