shell: Add built-in shell commands
Added optional shell commands: - clear - for clearing terminal - history - commands history - resize - terminal resize - shell - controling echo and colors Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no> Signed-off-by: Jakub Rzeszutko <jakub.rzeszutko@nordicsemi.no>
This commit is contained in:
parent
82ca811661
commit
989fef9c0e
|
@ -23,3 +23,8 @@ zephyr_sources_ifdef(
|
|||
CONFIG_SHELL_HISTORY
|
||||
shell_history.c
|
||||
)
|
||||
|
||||
zephyr_sources_ifdef(
|
||||
CONFIG_SHELL_CMDS
|
||||
shell_cmds.c
|
||||
)
|
||||
|
|
|
@ -135,4 +135,20 @@ config SHELL_HISTORY_BUFFER
|
|||
|
||||
endif #SHELL_HISTORY
|
||||
|
||||
config SHELL_CMDS
|
||||
bool "Enable built-in commands"
|
||||
default y
|
||||
help
|
||||
Enable built-in commands like 'clear', 'history', etc.
|
||||
|
||||
config SHELL_CMDS_RESIZE
|
||||
bool "Enable resize command"
|
||||
depends on SHELL_CMDS
|
||||
default y
|
||||
help
|
||||
By default shell assumes width of a terminal screen set to 80
|
||||
characters. Each time terminal screen width is changed resize command
|
||||
must be called to ensure correct text display on the terminal screen.
|
||||
Resize command can be turned off to safe code memory (~0,5k).
|
||||
|
||||
endif #SHELL
|
||||
|
|
489
subsys/shell/shell_cmds.c
Normal file
489
subsys/shell/shell_cmds.c
Normal file
|
@ -0,0 +1,489 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <shell/shell.h>
|
||||
#include "shell_utils.h"
|
||||
#include "shell_ops.h"
|
||||
#include "shell_vt100.h"
|
||||
|
||||
#define SHELL_HELP_CLEAR "Clear screen."
|
||||
#define SHELL_HELP_BACKSPACE_MODE "Toggle backspace key mode.\r\n" \
|
||||
"Some terminals are not sending separate escape code for" \
|
||||
"backspace and delete button. Hence backspace is not working as" \
|
||||
"expected. This command can force shell to interpret delete" \
|
||||
" escape as backspace."
|
||||
#define SHELL_HELP_BACKSPACE_MODE_BACKSPACE "Set different escape" \
|
||||
" code for backspace and delete key."
|
||||
#define SHELL_HELP_BACKSPACE_MODE_DELETE "Set the same escape" \
|
||||
" code for backspace and delete key."
|
||||
|
||||
#define SHELL_HELP_COLORS "Toggle colored syntax."
|
||||
#define SHELL_HELP_COLORS_OFF "Disable colored syntax."
|
||||
#define SHELL_HELP_COLORS_ON "Enable colored syntax."
|
||||
#define SHELL_HELP_STATISTICS "Shell statistics."
|
||||
#define SHELL_HELP_STATISTICS_SHOW \
|
||||
"Get shell statistics for the Logger module."
|
||||
#define SHELL_HELP_STATISTICS_RESET \
|
||||
"Reset shell statistics for the Logger module."
|
||||
#define SHELL_HELP_RESIZE \
|
||||
"Console gets terminal screen size or assumes 80 in case " \
|
||||
"the readout fails. It must be executed after each terminal " \
|
||||
"width change to ensure correct text display."
|
||||
#define SHELL_HELP_RESIZE_DEFAULT \
|
||||
"Assume 80 chars screen width and send this setting " \
|
||||
"to the terminal."
|
||||
#define SHELL_HELP_HISTORY "Command history."
|
||||
#define SHELL_HELP_ECHO "Toggle shell echo."
|
||||
#define SHELL_HELP_ECHO_ON "Enable shell echo."
|
||||
#define SHELL_HELP_ECHO_OFF \
|
||||
"Disable shell echo. Arrows and buttons: Backspace, Delete, End, " \
|
||||
"Home, Insert are not handled."
|
||||
#define SHELL_HELP_SHELL "Useful, not Unix-like shell commands."
|
||||
|
||||
#define SHELL_MSG_UNKNOWN_PARAMETER " unknown parameter: "
|
||||
|
||||
#define SHELL_MAX_TERMINAL_SIZE (250u)
|
||||
|
||||
/* 10 == {esc, [, 2, 5, 0, ;, 2, 5, 0, '\0'} */
|
||||
#define SHELL_CURSOR_POSITION_BUFFER (10u)
|
||||
|
||||
/* Function reads cursor position from terminal. */
|
||||
static int cursor_position_get(const struct shell *shell, u16_t *x, u16_t *y)
|
||||
{
|
||||
u16_t buff_idx = 0;
|
||||
size_t cnt;
|
||||
char c = 0;
|
||||
|
||||
*x = 0;
|
||||
*y = 0;
|
||||
|
||||
memset(shell->ctx->temp_buff, 0, sizeof(shell->ctx->temp_buff));
|
||||
|
||||
/* escape code asking terminal about its size */
|
||||
static char const cmd_get_terminal_size[] = "\033[6n";
|
||||
|
||||
shell_raw_fprintf(shell->fprintf_ctx, cmd_get_terminal_size);
|
||||
|
||||
/* fprintf buffer needs to be flushed to start sending prepared
|
||||
* escape code to the terminal.
|
||||
*/
|
||||
shell_fprintf_buffer_flush(shell->fprintf_ctx);
|
||||
|
||||
/* timeout for terminal response = ~1s */
|
||||
for (u16_t i = 0; i < 1000; i++) {
|
||||
do {
|
||||
(void)shell->iface->api->read(shell->iface, &c,
|
||||
sizeof(c), &cnt);
|
||||
if (cnt == 0) {
|
||||
k_sleep(1);
|
||||
break;
|
||||
}
|
||||
if ((c != SHELL_VT100_ASCII_ESC) &&
|
||||
(shell->ctx->temp_buff[0] !=
|
||||
SHELL_VT100_ASCII_ESC)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == 'R') { /* End of response from the terminal. */
|
||||
shell->ctx->temp_buff[buff_idx] = '\0';
|
||||
if (shell->ctx->temp_buff[1] != '[') {
|
||||
shell->ctx->temp_buff[0] = 0;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Index start position in the buffer where 'y'
|
||||
* is stored.
|
||||
*/
|
||||
buff_idx = 2;
|
||||
|
||||
while (shell->ctx->temp_buff[buff_idx] != ';') {
|
||||
*y = *y * 10 +
|
||||
(shell->ctx->temp_buff[buff_idx++] -
|
||||
'0');
|
||||
if (buff_idx >=
|
||||
CONFIG_SHELL_CMD_BUFF_SIZE) {
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if (++buff_idx >= CONFIG_SHELL_CMD_BUFF_SIZE) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
while (shell->ctx->temp_buff[buff_idx]
|
||||
!= '\0') {
|
||||
*x = *x * 10 +
|
||||
(shell->ctx->temp_buff[buff_idx++] -
|
||||
'0');
|
||||
|
||||
if (buff_idx >=
|
||||
CONFIG_SHELL_CMD_BUFF_SIZE) {
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
}
|
||||
/* horizontal cursor position */
|
||||
if (*x > SHELL_MAX_TERMINAL_SIZE) {
|
||||
*x = SHELL_MAX_TERMINAL_SIZE;
|
||||
}
|
||||
|
||||
/* vertical cursor position */
|
||||
if (*y > SHELL_MAX_TERMINAL_SIZE) {
|
||||
*y = SHELL_MAX_TERMINAL_SIZE;
|
||||
}
|
||||
|
||||
shell->ctx->temp_buff[0] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
shell->ctx->temp_buff[buff_idx] = c;
|
||||
|
||||
if (++buff_idx > SHELL_CURSOR_POSITION_BUFFER - 1) {
|
||||
shell->ctx->temp_buff[0] = 0;
|
||||
/* data_buf[SHELL_CURSOR_POSITION_BUFFER - 1]
|
||||
* is reserved for '\0'
|
||||
*/
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
} while (cnt > 0);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Function gets terminal width and height. */
|
||||
static int terminal_size_get(const struct shell *shell)
|
||||
{
|
||||
u16_t x; /* horizontal position */
|
||||
u16_t y; /* vertical position */
|
||||
int ret_val = 0;
|
||||
|
||||
cursor_save(shell);
|
||||
|
||||
/* Assumption: terminal width and height < 999. */
|
||||
/* Move to last column. */
|
||||
shell_op_cursor_vert_move(shell, -SHELL_MAX_TERMINAL_SIZE);
|
||||
/* Move to last row. */
|
||||
shell_op_cursor_horiz_move(shell, SHELL_MAX_TERMINAL_SIZE);
|
||||
|
||||
if (cursor_position_get(shell, &x, &y) == 0) {
|
||||
shell->ctx->vt100_ctx.cons.terminal_wid = x;
|
||||
shell->ctx->vt100_ctx.cons.terminal_hei = y;
|
||||
} else {
|
||||
ret_val = -ENOTSUP;
|
||||
}
|
||||
|
||||
cursor_restore(shell);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static void cmd_clear(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
(void)argv;
|
||||
|
||||
if ((argc == 2) && (shell_help_requested(shell))) {
|
||||
shell_help_print(shell, NULL, 0);
|
||||
return;
|
||||
}
|
||||
SHELL_VT100_CMD(shell, SHELL_VT100_CURSORHOME);
|
||||
SHELL_VT100_CMD(shell, SHELL_VT100_CLEARSCREEN);
|
||||
}
|
||||
|
||||
static void cmd_shell(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
(void)argv;
|
||||
|
||||
if ((argc == 1) || ((argc == 2) && shell_help_requested(shell))) {
|
||||
shell_help_print(shell, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
shell_fprintf(shell, SHELL_ERROR, SHELL_MSG_SPECIFY_SUBCOMMAND);
|
||||
}
|
||||
|
||||
static void cmd_bacskpace_mode(const struct shell *shell, size_t argc,
|
||||
char **argv)
|
||||
{
|
||||
(void)shell_cmd_precheck(shell, (argc == 2), NULL, 0);
|
||||
}
|
||||
|
||||
static void cmd_bacskpace_mode_backspace(const struct shell *shell, size_t argc,
|
||||
char **argv)
|
||||
{
|
||||
if (!shell_cmd_precheck(shell, (argc == 1), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell->ctx->internal.flags.mode_delete = 0;
|
||||
}
|
||||
|
||||
static void cmd_bacskpace_mode_delete(const struct shell *shell, size_t argc,
|
||||
char **argv)
|
||||
{
|
||||
if (!shell_cmd_precheck(shell, (argc == 1), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell->ctx->internal.flags.mode_delete = 1;
|
||||
}
|
||||
|
||||
static void cmd_colors_off(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
if (!shell_cmd_precheck(shell, (argc == 1), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell->ctx->internal.flags.use_colors = 0;
|
||||
}
|
||||
|
||||
static void cmd_colors_on(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
if (!shell_cmd_precheck(shell, (argc == 1), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
shell->ctx->internal.flags.use_colors = 1;
|
||||
}
|
||||
|
||||
static void cmd_colors(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
if (argc == 1) {
|
||||
shell_help_print(shell, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shell_cmd_precheck(shell, (argc == 2), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell_fprintf(shell, SHELL_ERROR, "%s:%s%s\r\n", argv[0],
|
||||
SHELL_MSG_UNKNOWN_PARAMETER, argv[1]);
|
||||
}
|
||||
|
||||
static void cmd_echo(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
if (!shell_cmd_precheck(shell, (argc <= 2), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc == 2) {
|
||||
shell_fprintf(shell, SHELL_ERROR, "%s:%s%s\r\n", argv[0],
|
||||
SHELL_MSG_UNKNOWN_PARAMETER, argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
shell_fprintf(shell, SHELL_NORMAL, "Echo status: %s\r\n",
|
||||
flag_echo_is_set(shell) ? "on" : "off");
|
||||
}
|
||||
|
||||
static void cmd_echo_off(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
if (!shell_cmd_precheck(shell, (argc == 1), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell->ctx->internal.flags.echo = 0;
|
||||
}
|
||||
|
||||
static void cmd_echo_on(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
if (!shell_cmd_precheck(shell, (argc == 1), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell->ctx->internal.flags.echo = 1;
|
||||
}
|
||||
|
||||
static void cmd_help(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
shell_fprintf(shell, SHELL_NORMAL, "Please press the <Tab> button to "
|
||||
"see all available commands.\r\n"
|
||||
"You can also use the <Tab> button "
|
||||
"to prompt or auto-complete all "
|
||||
"commands or its subcommands.\r\n"
|
||||
"You can try to call commands "
|
||||
"with <-h> or <--help> parameter to "
|
||||
"get know what they are doing.\r\n");
|
||||
|
||||
}
|
||||
|
||||
static void cmd_history(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
size_t i = 0;
|
||||
size_t len;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_SHELL_HISTORY)) {
|
||||
shell_fprintf(shell, SHELL_ERROR, "Command not supported.\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shell_cmd_precheck(shell, (argc == 1), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
shell_history_get(shell->history, true,
|
||||
shell->ctx->temp_buff, &len);
|
||||
|
||||
if (len) {
|
||||
shell_fprintf(shell, SHELL_NORMAL, "[%3d] %s\r\n",
|
||||
i++, shell->ctx->temp_buff);
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
shell->ctx->temp_buff[0] = '\0';
|
||||
}
|
||||
|
||||
static void cmd_shell_stats(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
if (argc == 1) {
|
||||
shell_help_print(shell, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc == 2) {
|
||||
shell_fprintf(shell, SHELL_ERROR, "%s:%s%s\r\n", argv[0],
|
||||
SHELL_MSG_UNKNOWN_PARAMETER, argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
(void)shell_cmd_precheck(shell, (argc <= 2), NULL, 0);
|
||||
}
|
||||
|
||||
static void cmd_shell_stats_show(const struct shell *shell, size_t argc,
|
||||
char **argv)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_SHELL_STATS)) {
|
||||
shell_fprintf(shell, SHELL_ERROR, "Command not supported.\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shell_cmd_precheck(shell, (argc == 1), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell_fprintf(shell, SHELL_NORMAL, "Lost logs: %u\r\n",
|
||||
shell->stats->log_lost_cnt);
|
||||
}
|
||||
|
||||
static void cmd_shell_stats_reset(const struct shell *shell,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_SHELL_STATS)) {
|
||||
shell_fprintf(shell, SHELL_ERROR, "Command not supported.\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shell_cmd_precheck(shell, (argc == 1), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell->stats->log_lost_cnt = 0;
|
||||
}
|
||||
|
||||
static void cmd_resize_default(const struct shell *shell,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
if (!shell_cmd_precheck(shell, (argc == 1), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SHELL_VT100_CMD(shell, SHELL_VT100_SETCOL_80);
|
||||
shell->ctx->vt100_ctx.cons.terminal_wid = SHELL_DEFAULT_TERMINAL_WIDTH;
|
||||
shell->ctx->vt100_ctx.cons.terminal_hei = SHELL_DEFAULT_TERMINAL_HEIGHT;
|
||||
}
|
||||
|
||||
static void cmd_resize(const struct shell *shell, size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_SHELL_CMDS_RESIZE)) {
|
||||
shell_fprintf(shell, SHELL_ERROR, "Command not supported.\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shell_cmd_precheck(shell, (argc <= 2), NULL, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc != 1) {
|
||||
shell_fprintf(shell, SHELL_ERROR, "%s:%s%s\r\n", argv[0],
|
||||
SHELL_MSG_UNKNOWN_PARAMETER, argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
err = terminal_size_get(shell);
|
||||
if (err != 0) {
|
||||
shell->ctx->vt100_ctx.cons.terminal_wid =
|
||||
SHELL_DEFAULT_TERMINAL_WIDTH;
|
||||
shell->ctx->vt100_ctx.cons.terminal_hei =
|
||||
SHELL_DEFAULT_TERMINAL_HEIGHT;
|
||||
shell_fprintf(shell, SHELL_WARNING,
|
||||
"No response from the terminal, assumed 80x24 "
|
||||
"screen size\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Warning!
|
||||
* Subcommands must be placed in alphabetical order to ensure correct
|
||||
* autocompletion.
|
||||
*/
|
||||
SHELL_CREATE_STATIC_SUBCMD_SET(m_sub_colors)
|
||||
{
|
||||
SHELL_CMD(off, NULL, SHELL_HELP_COLORS_OFF, cmd_colors_off),
|
||||
SHELL_CMD(on, NULL, SHELL_HELP_COLORS_ON, cmd_colors_on),
|
||||
SHELL_SUBCMD_SET_END
|
||||
};
|
||||
|
||||
SHELL_CREATE_STATIC_SUBCMD_SET(m_sub_echo)
|
||||
{
|
||||
SHELL_CMD(off, NULL, SHELL_HELP_ECHO_OFF, cmd_echo_off),
|
||||
SHELL_CMD(on, NULL, SHELL_HELP_ECHO_ON, cmd_echo_on),
|
||||
SHELL_SUBCMD_SET_END
|
||||
};
|
||||
|
||||
SHELL_CREATE_STATIC_SUBCMD_SET(m_sub_shell_stats)
|
||||
{
|
||||
SHELL_CMD(reset, NULL, SHELL_HELP_STATISTICS_RESET,
|
||||
cmd_shell_stats_reset),
|
||||
SHELL_CMD(show, NULL, SHELL_HELP_STATISTICS_SHOW, cmd_shell_stats_show),
|
||||
SHELL_SUBCMD_SET_END
|
||||
};
|
||||
|
||||
SHELL_CREATE_STATIC_SUBCMD_SET(m_sub_backspace_mode)
|
||||
{
|
||||
SHELL_CMD(backspace, NULL, SHELL_HELP_BACKSPACE_MODE_BACKSPACE,
|
||||
cmd_bacskpace_mode_backspace),
|
||||
SHELL_CMD(delete, NULL, SHELL_HELP_BACKSPACE_MODE_DELETE,
|
||||
cmd_bacskpace_mode_delete),
|
||||
SHELL_SUBCMD_SET_END
|
||||
};
|
||||
|
||||
SHELL_CREATE_STATIC_SUBCMD_SET(m_sub_shell)
|
||||
{
|
||||
SHELL_CMD(backspace_mode, &m_sub_backspace_mode,
|
||||
SHELL_HELP_BACKSPACE_MODE, cmd_bacskpace_mode),
|
||||
SHELL_CMD(colors, &m_sub_colors, SHELL_HELP_COLORS, cmd_colors),
|
||||
SHELL_CMD(echo, &m_sub_echo, SHELL_HELP_ECHO, cmd_echo),
|
||||
SHELL_CMD(stats, &m_sub_shell_stats, SHELL_HELP_STATISTICS,
|
||||
cmd_shell_stats),
|
||||
SHELL_SUBCMD_SET_END
|
||||
};
|
||||
|
||||
SHELL_CREATE_STATIC_SUBCMD_SET(m_sub_resize)
|
||||
{
|
||||
SHELL_CMD(default, NULL, SHELL_HELP_RESIZE_DEFAULT, cmd_resize_default),
|
||||
SHELL_SUBCMD_SET_END
|
||||
};
|
||||
|
||||
SHELL_CMD_REGISTER(clear, NULL, SHELL_HELP_CLEAR, cmd_clear);
|
||||
SHELL_CMD_REGISTER(shell, &m_sub_shell, SHELL_HELP_SHELL, cmd_shell);
|
||||
SHELL_CMD_REGISTER(help, NULL, NULL, cmd_help);
|
||||
SHELL_CMD_REGISTER(history, NULL, SHELL_HELP_HISTORY, cmd_history);
|
||||
SHELL_CMD_REGISTER(resize, &m_sub_resize, SHELL_HELP_RESIZE, cmd_resize);
|
Loading…
Reference in a new issue