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:
Krzysztof Chruscinski 2018-08-09 10:57:07 +02:00 committed by Anas Nashif
parent 82ca811661
commit 989fef9c0e
3 changed files with 510 additions and 0 deletions

View file

@ -23,3 +23,8 @@ zephyr_sources_ifdef(
CONFIG_SHELL_HISTORY
shell_history.c
)
zephyr_sources_ifdef(
CONFIG_SHELL_CMDS
shell_cmds.c
)

View file

@ -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
View 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);