shell: Shell subsystem reimplementation

New shell support features like:
- multi-instance
- command tree
- static and dynamic commands
- multiline
- help print function
- smart tab (autocompletion)
- meta-keys
- history, wildcards etc.
- generic transport (initially, uart present)

Signed-off-by: Jakub Rzeszutko <jakub.rzeszutko@nordicsemi.no>
Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
Signed-off-by: Piotr Zięcik <piotr.ziecik@nordicsemi.no>
This commit is contained in:
Krzysztof Chruscinski 2018-08-09 09:56:10 +02:00 committed by Anas Nashif
commit 6aed72e487
17 changed files with 3781 additions and 2 deletions

View file

@ -73,3 +73,10 @@
KEEP(*(".log_backends"));
__log_backends_end = .;
} GROUP_LINK_IN(ROMABLE_REGION)
SECTION_DATA_PROLOGUE(shell_root_cmds_sections, (OPTIONAL),)
{
__shell_root_cmds_start = .;
KEEP(*(SORT(.shell_root_cmd_*)));
__shell_root_cmds_end = .;
} GROUP_LINK_IN(ROMABLE_REGION)

538
include/shell/shell.h Normal file
View file

@ -0,0 +1,538 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef SHELL_H__
#define SHELL_H__
#include <zephyr.h>
#include <shell/shell_types.h>
#include <shell/shell_fprintf.h>
#include <logging/log_backend.h>
#include <logging/log_instance.h>
#include <logging/log.h>
#include <misc/util.h>
#ifdef __cplusplus
extern "C" {
#endif
#define SHELL_RX_BUFF_SIZE 16
#ifndef CONFIG_SHELL_CMD_BUFF_SIZE
#define CONFIG_SHELL_CMD_BUFF_SIZE 0
#endif
#ifndef CONFIG_SHELL_PRINTF_BUFF_SIZE
#define CONFIG_SHELL_PRINTF_BUFF_SIZE 0
#endif
#define SHELL_CMD_ROOT_LVL (0u)
/*
* @defgroup shell Shell
* @ingroup subsys
*
* @brief Module for providing shell.
*
* @{
*/
struct shell_static_entry;
/*
* @brief Shell dynamic command descriptor.
*
* @details Function shall fill the received shell_static_entry structure
* with requested (idx) dynamic subcommand data. If there is more than
* one dynamic subcommand available, the function shall ensure that the
* returned commands: entry->syntax are sorted in alphabetical order.
* If idx exceeds the available dynamic subcommands, the function must
* write to entry->syntax NULL value. This will indicate to the shell
* module that there are no more dynamic commands to read.
*/
typedef void (*shell_dynamic_get)(size_t idx,
struct shell_static_entry *entry);
/*
* @brief Shell command descriptor.
*/
struct shell_cmd_entry {
bool is_dynamic;
union {
/*!< Pointer to function returning dynamic commands.*/
shell_dynamic_get dynamic_get;
/*!< Pointer to array of static commands. */
const struct shell_static_entry *entry;
} u;
};
struct shell;
/*
* @brief Shell command handler prototype.
*/
typedef void (*shell_cmd_handler)(const struct shell *shell,
size_t argc, char **argv);
/*
* @brief Shell static command descriptor.
*/
struct shell_static_entry {
const char *syntax; /*!< Command syntax strings. */
const char *help; /*!< Command help string. */
const struct shell_cmd_entry *subcmd; /*!< Pointer to subcommand. */
shell_cmd_handler handler; /*!< Command handler. */
};
#define SHELL_CMD_NAME(name) UTIL_CAT(shell_cmd_, name)
/*
* @brief Macro for defining and adding a root command (level 0).
*
* @note Each root command shall have unique syntax.
*
* @param[in] syntax Command syntax (for example: history).
* @param[in] subcmd Pointer to a subcommands array.
* @param[in] help Pointer to a command help string.
* @param[in] handler Pointer to a function handler.
*/
#define SHELL_CMD_REGISTER(syntax, subcmd, help, handler) \
static const struct shell_static_entry UTIL_CAT(shell_, syntax) = \
SHELL_CMD(syntax, subcmd, help, handler); \
static const struct shell_cmd_entry UTIL_CAT(shell_cmd_, syntax) \
__attribute__ ((section("." \
STRINGIFY(UTIL_CAT(shell_root_cmd_, syntax))))) \
__attribute__((used)) = { \
.is_dynamic = false, \
.u.entry = &UTIL_CAT(shell_, syntax) \
}
/*
* @brief Macro for creating a subcommand set. It must be used outside of any
* function body.
*
* @param[in] name Name of the subcommand set.
*/
#define SHELL_CREATE_STATIC_SUBCMD_SET(name) \
static const struct shell_static_entry shell_##name[]; \
static const struct shell_cmd_entry name = { \
.is_dynamic = false, \
.u.entry = shell_##name \
}; \
static const struct shell_static_entry shell_##name[] =
/*
* @brief Define ending subcommands set.
*
*/
#define SHELL_SUBCMD_SET_END {NULL}
/*
* @brief Macro for creating a dynamic entry.
*
* @param[in] name Name of the dynamic entry.
* @param[in] get Pointer to the function returning dynamic commands array
*/
#define SHELL_CREATE_DYNAMIC_CMD(name, get) \
static const struct shell_cmd_entry name = { \
.is_dynamic = true, \
.u.dynamic_get = get \
}
/*
* @brief Initializes a shell command.
*
* @param[in] _syntax Command syntax (for example: history).
* @param[in] _subcmd Pointer to a subcommands array.
* @param[in] _help Pointer to a command help string.
* @param[in] _handler Pointer to a function handler.
*/
#define SHELL_CMD(_syntax, _subcmd, _help, _handler) { \
.syntax = (const char *)STRINGIFY(_syntax), \
.subcmd = _subcmd, \
.help = (const char *)_help, \
.handler = _handler \
}
/*
* @internal @brief Internal shell state in response to data received from the
* terminal.
*/
enum shell_receive_state {
SHELL_RECEIVE_DEFAULT,
SHELL_RECEIVE_ESC,
SHELL_RECEIVE_ESC_SEQ,
SHELL_RECEIVE_TILDE_EXP
};
/*
* @internal @brief Internal shell state.
*/
enum shell_state {
SHELL_STATE_UNINITIALIZED,
SHELL_STATE_INITIALIZED,
SHELL_STATE_ACTIVE,
SHELL_STATE_PANIC_MODE_ACTIVE, /*!< Panic activated.*/
SHELL_STATE_PANIC_MODE_INACTIVE /*!< Panic requested, not supported.*/
};
/* @brief Shell transport event. */
enum shell_transport_evt {
SHELL_TRANSPORT_EVT_RX_RDY,
SHELL_TRANSPORT_EVT_TX_RDY
};
typedef void (*shell_transport_handler_t)(enum shell_transport_evt evt,
void *context);
struct shell_transport;
/*
* @brief Unified shell transport interface.
*/
struct shell_transport_api {
/*
* @brief Function for initializing the shell transport interface.
*
* @param[in] transport Pointer to the transfer instance.
* @param[in] config Pointer to instance configuration.
* @param[in] evt_handler Event handler.
* @param[in] context Pointer to the context passed to event
* handler.
*
* @return Standard error code.
*/
int (*init)(const struct shell_transport *transport,
const void *config,
shell_transport_handler_t evt_handler,
void *context);
/*
* @brief Function for uninitializing the shell transport interface.
*
* @param[in] transport Pointer to the transfer instance.
*
* @return Standard error code.
*/
int (*uninit)(const struct shell_transport *transport);
/*
* @brief Function for reconfiguring the transport to work in blocking
* mode.
*
* @param transport Pointer to the transfer instance.
* @param blocking If true, the transport is enabled in blocking mode.
*
* @return NRF_SUCCESS on successful enabling, error otherwise (also if
* not supported).
*/
int (*enable)(const struct shell_transport *transport, bool blocking);
/*
* @brief Function for writing data to the transport interface.
*
* @param[in] transport Pointer to the transfer instance.
* @param[in] data Pointer to the source buffer.
* @param[in] length Source buffer length.
* @param[in] cnt Pointer to the sent bytes counter.
*
* @return Standard error code.
*/
int (*write)(const struct shell_transport *transport,
const void *data, size_t length, size_t *cnt);
/*
* @brief Function for reading data from the transport interface.
*
* @param[in] p_transport Pointer to the transfer instance.
* @param[in] p_data Pointer to the destination buffer.
* @param[in] length Destination buffer length.
* @param[in] cnt Pointer to the received bytes counter.
*
* @return Standard error code.
*/
int (*read)(const struct shell_transport *transport,
void *data, size_t length, size_t *cnt);
};
struct shell_transport {
const struct shell_transport_api *api;
void *ctx;
};
/*
* @internal @brief Flags for internal shell usage.
*/
struct shell_flags {
u32_t insert_mode :1; /*!< Controls insert mode for text introduction.*/
u32_t show_help :1; /*!< Shows help if -h or --help option present.*/
u32_t use_colors :1; /*!< Controls colored syntax.*/
u32_t echo :1; /*!< Controls shell echo.*/
u32_t processing :1; /*!< Shell is executing process function.*/
u32_t tx_rdy :1;
u32_t mode_delete :1; /*!< Operation mode of backspace key */
};
_Static_assert(sizeof(struct shell_flags) == sizeof(u32_t), "Must fit in 32b.");
/*
* @internal @brief Union for internal shell usage.
*/
union shell_internal {
u32_t value;
struct shell_flags flags;
};
enum shell_signal {
SHELL_SIGNAL_RXRDY,
SHELL_SIGNAL_TXDONE,
SHELL_SIGNAL_KILL,
SHELL_SIGNALS
};
/*
* @brief Shell instance context.
*/
struct shell_ctx {
enum shell_state state; /*!< Internal module state.*/
enum shell_receive_state receive_state;/*!< Escape sequence indicator.*/
/*!< Currently executed command.*/
struct shell_static_entry active_cmd;
/*!< VT100 color and cursor position, terminal width.*/
struct shell_vt100_ctx vt100_ctx;
u16_t cmd_buff_len;/*!< Command length.*/
u16_t cmd_buff_pos; /*!< Command buffer cursor position.*/
u16_t cmd_tmp_buff_len; /*!< Command length in tmp buffer.*/
/*!< Command input buffer.*/
char cmd_buff[CONFIG_SHELL_CMD_BUFF_SIZE];
/*!< Command temporary buffer.*/
char temp_buff[CONFIG_SHELL_CMD_BUFF_SIZE];
/*!< Printf buffer size.*/
char printf_buff[CONFIG_SHELL_PRINTF_BUFF_SIZE];
volatile union shell_internal internal; /*!< Internal shell data.*/
struct k_poll_signal signals[SHELL_SIGNALS];
struct k_poll_event events[SHELL_SIGNALS];
};
extern const struct log_backend_api log_backend_shell_api;
/*
* @brief Shell instance internals.
*/
struct shell {
const char *const name; /*!< Terminal name. */
const struct shell_transport *iface; /*!< Transport interface.*/
struct shell_ctx *ctx; /*!< Internal context.*/
const struct shell_fprintf *fprintf_ctx;
LOG_INSTANCE_PTR_DECLARE(log);
/*!< New line character, only allowed values: \\n and \\r.*/
const char newline_char;
struct k_thread *thread;
k_thread_stack_t *stack;
};
/*
* @brief Macro for defining a shell instance.
*
* @param[in] _name Instance name.
* @param[in] shell_prefix Shell prefix string.
* @param[in] transport_iface Pointer to the transport interface.
* @param[in] newline_ch New line character - only allowed values are
* '\\n' or '\\r'.
* @param[in] log_queue_size Logger processing queue size.
*/
#define SHELL_DEFINE(_name, shell_prefix, transport_iface, \
newline_ch, log_queue_size) \
static const struct shell _name; \
static struct shell_ctx UTIL_CAT(_name, _ctx); \
static u8_t _name##_out_buffer[CONFIG_SHELL_PRINTF_BUFF_SIZE]; \
SHELL_FPRINTF_DEFINE(_name## _fprintf, &_name, _name##_out_buffer, \
CONFIG_SHELL_PRINTF_BUFF_SIZE, \
true, shell_print_stream); \
LOG_INSTANCE_REGISTER(shell, _name, CONFIG_SHELL_LOG_LEVEL); \
static struct k_thread _name##_thread; \
static K_THREAD_STACK_DEFINE(_name##_stack, CONFIG_SHELL_STACK_SIZE); \
static const struct shell _name = { \
.name = shell_prefix, \
.iface = transport_iface, \
.ctx = &UTIL_CAT(_name, _ctx), \
.fprintf_ctx = &_name##_fprintf, \
LOG_INSTANCE_PTR_INIT(log, shell, _name) \
.newline_char = newline_ch, \
.thread = &_name##_thread, \
.stack = _name##_stack \
}
/*
* @brief Function for initializing a transport layer and internal shell state.
*
* @param[in] shell Pointer to shell instance.
* @param[in] transport_config Transport configuration during initialization.
* @param[in] use_colors Enables colored prints.
* @param[in] log_backend If true, the console will be used as logger
* backend.
* @param[in] init_log_level Default severity level for the logger.
*
* @return Standard error code.
*/
int shell_init(const struct shell *shell, const void *transport_config,
bool use_colors, bool log_backend, u32_t init_log_level);
/*
* @brief Uninitializes the transport layer and the internal shell state.
*
* @param shell Pointer to shell instance.
*
* @return Standard error code.
*/
int shell_uninit(const struct shell *shell);
/*
* @brief Function for starting shell processing.
*
* @param shell Pointer to the shell instance.
*
* @return Standard error code.
*/
int shell_start(const struct shell *shell);
/*
* @brief Function for stopping shell processing.
*
* @param shell Pointer to shell instance.
*
* @return Standard error code.
*/
int shell_stop(const struct shell *shell);
/*
* @brief Shell colors for nrf_shell_fprintf function.
*/
#define SHELL_NORMAL SHELL_VT100_COLOR_DEFAULT
#define SHELL_INFO SHELL_VT100_COLOR_GREEN
#define SHELL_OPTION SHELL_VT100_COLOR_CYAN
#define SHELL_WARNING SHELL_VT100_COLOR_YELLOW
#define SHELL_ERROR SHELL_VT100_COLOR_RED
/*
* @brief Printf-like function which sends formatted data stream to the shell.
* This function shall not be used outside of the shell command context.
*
* @param[in] shell Pointer to the shell instance.
* @param[in] color Printf color.
* @param[in] p_fmt Format string.
* @param[in] ... List of parameters to print.
*/
void shell_fprintf(const struct shell *shell, enum shell_vt100_color color,
const char *p_fmt, ...);
/*
* @brief Process function, which should be executed when data is ready in the
* transport interface. To be used if shell thread is disabled.
*
* @param[in] shell Pointer to the shell instance.
*/
void shell_process(const struct shell *shell);
/*
* @brief Option descriptor.
*/
struct shell_getopt_option {
const char *optname; /*!< Option long name.*/
const char *optname_short; /*!< Option short name.*/
const char *optname_help; /*!< Option help string.*/
};
/*
* @brief Option structure initializer.
*
* @param[in] _optname Option name long.
* @param[in] _shortname Option name short.
* @param[in] _help Option help string.
*/
#define SHELL_OPT(_optname, _shortname, _help) { \
.optname = _optname, \
.optname_short = _shortname, \
.optname_help = _help, \
}
/*
* @brief Informs that a command has been called with -h or --help option.
*
* @param[in] shell Pointer to the shell instance.
*
* @return True if help has been requested.
*/
static inline bool shell_help_requested(const struct shell *shell)
{
return shell->ctx->internal.flags.show_help;
}
/*
* @brief Prints the current command help.
*
* Function will print a help string with: the currently entered command, its
* options,and subcommands (if they exist).
*
* @param[in] shell Pointer to the shell instance.
* @param[in] opt Pointer to the optional option array.
* @param[in] opt_len Option array size.
*/
void shell_help_print(const struct shell *shell,
const struct shell_getopt_option *opt, size_t opt_len);
/*
* @brief Prints help if request and prints error message on wrong argument
* count.
*
* Optionally, printing help on wrong argument count can be enabled.
*
* @param[in] shell Pointer to the shell instance.
* @param[in] arg_cnt_ok Flag indicating valid number of arguments.
* @param[in] opt Pointer to the optional option array.
* @param[in] opt_len Option array size.
*
* @return True if check passed, false otherwise or help was requested.
*/
bool shell_cmd_precheck(const struct shell *shell,
bool arg_cnt_nok,
const struct shell_getopt_option *opt,
size_t opt_len);
/*
* @internal @brief This function shall not be used directly, it is required by
* the fprintf module.
*
* @param[in] p_user_ctx Pointer to the context for the shell instance.
* @param[in] p_data Pointer to the data buffer.
* @param[in] data_len Data buffer size.
*/
void shell_print_stream(const void *user_ctx, const char *data,
size_t data_len);
/* @} */
#ifdef __cplusplus
}
#endif
#endif /* SHELL_H__ */

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef SHELL_FPRINTF_H__
#define SHELL_FPRINTF_H__
#include <zephyr.h>
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*shell_fprintf_fwrite)(const void *user_ctx,
const char *data,
size_t length);
struct shell_fprintf_control_block {
size_t buffer_cnt;
bool autoflush;
};
/**
* @brief fprintf context
*/
struct shell_fprintf {
u8_t *buffer;
size_t buffer_size;
shell_fprintf_fwrite fwrite;
const void *user_ctx;
struct shell_fprintf_control_block *ctrl_blk;
};
/**
* @brief Macro for defining shell_fprintf instance.
*
* @param _name Instance name.
* @param _user_ctx Pointer to user data.
* @param _buf Pointer to output buffer
* @param _size Size of output buffer.
* @param _autoflush Indicator if buffer shall be automatically flush.
* @param _fwrite Pointer to function sending data stream.
*/
#define SHELL_FPRINTF_DEFINE(_name, _user_ctx, _buf, _size, \
_autoflush, _fwrite) \
static struct shell_fprintf_control_block \
_name##_shell_fprintf_ctx = { \
.autoflush = _autoflush, \
.buffer_cnt = 0 \
}; \
static const struct shell_fprintf _name = { \
.buffer = _buf, \
.buffer_size = _size, \
.fwrite = _fwrite, \
.user_ctx = _user_ctx, \
.ctrl_blk = &_name##_shell_fprintf_ctx \
}
/**
* @brief fprintf like function which send formated data stream to output.
*
* @param sh_fprintf fprintf instance.
* @param fmt Format string.
* @param args List of parameters to print.
*/
void shell_fprintf_fmt(const struct shell_fprintf *sh_fprintf,
char const *fmt, va_list args);
/**
* @brief function flushing data stored in io_buffer.
*
* @param sh_fprintf fprintf instance
*/
void shell_fprintf_buffer_flush(const struct shell_fprintf *sh_fprintf);
#ifdef __cplusplus
}
#endif
#endif /* SHELL_FPRINTF_H__ */

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef SHELL_TYPES_H__
#define SHELL_TYPES_H__
#ifdef __cplusplus
extern "C" {
#endif
enum shell_vt100_color {
SHELL_VT100_COLOR_DEFAULT,
SHELL_VT100_COLOR_BLACK,
SHELL_VT100_COLOR_RED,
SHELL_VT100_COLOR_GREEN,
SHELL_VT100_COLOR_YELLOW,
SHELL_VT100_COLOR_BLUE,
SHELL_VT100_COLOR_MAGENTA,
SHELL_VT100_COLOR_CYAN,
SHELL_VT100_COLOR_WHITE,
VT100_COLOR_END
};
struct shell_vt100_colors {
enum shell_vt100_color col; /* Text color. */
enum shell_vt100_color bgcol; /* Background color. */
};
struct shell_multiline_cons {
u16_t cur_x; /* horizontal cursor position in edited command line.*/
u16_t cur_x_end; /* horizontal cursor position at the end of command.*/
u16_t cur_y; /* vertical cursor position in edited command.*/
u16_t cur_y_end; /* vertical cursor position at the end of command.*/
u16_t terminal_hei; /* terminal screen height.*/
u16_t terminal_wid; /* terminal screen width.*/
u8_t name_len; /*!<console name length.*/
};
struct shell_vt100_ctx {
struct shell_multiline_cons cons;
struct shell_vt100_colors col;
u16_t printed_cmd; /* printed commands counter */
};
#ifdef __cplusplus
}
#endif
#endif /* SHELL_TYPES_H__ */

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef SHELL_UART_H__
#define SHELL_UART_H__
#include <shell/shell.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const struct shell_transport_api shell_uart_transport_api;
struct shell_uart {
struct device *dev;
shell_transport_handler_t handler;
struct k_timer timer;
void *context;
u8_t rx[1];
size_t rx_cnt;
};
#define SHELL_UART_DEFINE(_name) \
static struct shell_uart _name##_shell_uart; \
struct shell_transport _name = { \
.api = &shell_uart_transport_api, \
.ctx = (struct shell_uart *)&_name##_shell_uart \
}
#ifdef __cplusplus
}
#endif
#endif /* SHELL_UART_H__ */

View file

@ -654,7 +654,7 @@ class SizeCalculator:
"kobject_data", "mmu_tables", "app_pad", "priv_stacks",
"ccm_data", "usb_descriptor", "usb_data", "usb_bos_desc",
'log_backends_sections', 'log_dynamic_sections',
'log_const_sections',"app_smem"]
'log_const_sections',"app_smem", 'shell_root_cmds_sections']
# These get copied into RAM only on non-XIP
ro_sections = ["text", "ctors", "init_array", "reset", "object_access",
"rodata", "devconfig", "net_l2", "vector", "_bt_settings_area"]

View file

@ -4,6 +4,7 @@ add_subdirectory(logging)
add_subdirectory_ifdef(CONFIG_BT bluetooth)
add_subdirectory_ifdef(CONFIG_CONSOLE_SUBSYS console)
add_subdirectory_ifdef(CONFIG_CONSOLE_SHELL shell)
add_subdirectory_ifdef(CONFIG_SHELL shell)
add_subdirectory_ifdef(CONFIG_CPLUSPLUS cpp)
add_subdirectory_ifdef(CONFIG_DISK_ACCESS disk)
add_subdirectory(fs)

View file

@ -2,9 +2,19 @@ zephyr_include_directories_ifdef(CONFIG_CONSOLE_SHELL
${ZEPHYR_BASE}/include/drivers
)
zephyr_sources(
zephyr_sources_ifdef(
CONFIG_CONSOLE_SHELL
shell_service.c
legacy_shell.c
)
add_subdirectory(modules)
zephyr_sources_ifdef(
CONFIG_SHELL
shell.c
shell_fprintf.c
shell_utils.c
shell_ops.c
shell_uart.c
)

View file

@ -33,3 +33,89 @@ config CONSOLE_SHELL_MAX_CMD_QUEUED
source "subsys/shell/modules/Kconfig"
endif
config SHELL
bool "Enable shell"
select LOG_RUNTIME_FILTERING
select POLL
if SHELL
module = SHELL
module-str = Shell
source "subsys/logging/Kconfig.template.log_config"
config SHELL_STACK_SIZE
int "Shell thread stack size"
default 1024 if MULTITHREADING
default 0 if !MULTITHREADING
help
Stack size for thread created for each instance.
config SHELL_THREAD_PRIO
int "Shell thread priority"
depends on MULTITHREADING
default -2
help
Shell thread priority.
config SHELL_BACKSPACE_MODE_DELETE
bool "Default escape code for backspace is DELETE (0x7F)"
default y
help
Terminals have different escape code settings for backspace button.
Some terminals send code: 0x08 (backspace) other 0x7F (delete). When
this option is set shell will expect 0x7F for backspace key.
config SHELL_CMD_BUFF_SIZE
int "Shell command buffer size"
default 256
help
Maximum command size.
config SHELL_PRINTF_BUFF_SIZE
int "Shell print buffer size"
default 30
help
Maximum text buffer size for fprintf function.
It is working like stdio buffering in Linux systems
to limit number of peripheral access calls.
config SHELL_ARGC_MAX
int "Maximum arguments in shell command"
default 12
help
Maximum number of arguments that can build a command.
If command is composed of more than defined, argument SHELL_ARGC_MAX
and following are passed as one argument in the string.
config SHELL_ECHO_STATUS
bool "Enable echo on shell"
default y
help
If enabled shell prints back every input byte.
config SHELL_VT100_COLORS
bool "Enable colors in shell"
default y
help
If enabled VT100 colors are used in shell (e.g. print errors in red).
config SHELL_METAKEYS
bool "Enable metakeys"
default y
help
Enables shell metakeys: Home, End, ctrl+a, ctrl+c, ctrl+e, ctrl+l,
ctrl+u, ctrl+w
config SHELL_HELP
bool "Enable help message"
default y
help
Enables formatting help message when requested with '-h' or '--help'.
config SHELL_HELP_ON_WRONG_ARGUMENT_COUNT
bool "Enable printing help on wrong argument count"
default y
endif #SHELL

1451
subsys/shell/shell.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <shell/shell_fprintf.h>
#ifdef CONFIG_NEWLIB_LIBC
typedef int (*out_func_t)(int c, void *ctx);
extern void _vprintk(out_func_t out, void *ctx, const char *fmt, va_list ap);
#else
extern int _prf(int (*func)(), void *dest, char *format, va_list vargs);
#endif
static int out_func(int c, void *ctx)
{
const struct shell_fprintf *sh_fprintf;
sh_fprintf = (const struct shell_fprintf *)ctx;
sh_fprintf->buffer[sh_fprintf->ctrl_blk->buffer_cnt] = (u8_t)c;
sh_fprintf->ctrl_blk->buffer_cnt++;
if (sh_fprintf->ctrl_blk->buffer_cnt == sh_fprintf->buffer_size) {
shell_fprintf_buffer_flush(sh_fprintf);
}
return 0;
}
void shell_fprintf_fmt(const struct shell_fprintf *sh_fprintf,
const char *fmt, va_list args)
{
#ifndef CONFIG_NEWLIB_LIBC
(void)_prf(out_func, (void *)sh_fprintf, (char *)fmt, args);
#else
_vprintk(out_func, (void *)sh_fprintf, fmt, args);
#endif
if (sh_fprintf->ctrl_blk->autoflush) {
shell_fprintf_buffer_flush(sh_fprintf);
}
}
void shell_fprintf_buffer_flush(const struct shell_fprintf *sh_fprintf)
{
sh_fprintf->fwrite(sh_fprintf->user_ctx, sh_fprintf->buffer,
sh_fprintf->ctrl_blk->buffer_cnt);
sh_fprintf->ctrl_blk->buffer_cnt = 0;
}

264
subsys/shell/shell_ops.c Normal file
View file

@ -0,0 +1,264 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "shell_ops.h"
void shell_op_cursor_vert_move(const struct shell *shell, s32_t delta)
{
if (delta != 0) {
shell_raw_fprintf(shell->fprintf_ctx, "\033[%d%c",
delta > 0 ? delta : -delta,
delta > 0 ? 'A' : 'B');
}
}
void shell_op_cursor_horiz_move(const struct shell *shell, s32_t delta)
{
if (delta != 0) {
shell_raw_fprintf(shell->fprintf_ctx, "\033[%d%c",
delta > 0 ? delta : -delta,
delta > 0 ? 'C' : 'D');
}
}
/* Function returns true if command length is equal to multiplicity of terminal
* width.
*/
static inline bool full_line_cmd(const struct shell *shell)
{
return ((shell->ctx->cmd_buff_len + shell_strlen(shell->name))
% shell->ctx->vt100_ctx.cons.terminal_wid == 0);
}
/* Function returns true if cursor is at beginning of an empty line. */
bool shell_cursor_in_empty_line(const struct shell *shell)
{
return ((shell->ctx->cmd_buff_pos + shell_strlen(shell->name))
% shell->ctx->vt100_ctx.cons.terminal_wid == 0);
}
void shell_op_cond_next_line(const struct shell *shell)
{
if (shell_cursor_in_empty_line(shell) || full_line_cmd(shell)) {
cursor_next_line_move(shell);
}
}
void shell_op_cursor_position_synchronize(const struct shell *shell)
{
struct shell_multiline_cons *cons = &shell->ctx->vt100_ctx.cons;
bool last_line;
shell_multiline_data_calc(cons, shell->ctx->cmd_buff_pos,
shell->ctx->cmd_buff_len);
last_line = (cons->cur_y == cons->cur_y_end);
/* In case cursor reaches the bottom line of a terminal, it will
* be moved to the next line.
*/
if (full_line_cmd(shell)) {
cursor_next_line_move(shell);
}
if (last_line) {
shell_op_cursor_horiz_move(shell, cons->cur_x -
cons->cur_x_end);
} else {
shell_op_cursor_vert_move(shell, cons->cur_y_end - cons->cur_y);
shell_op_cursor_horiz_move(shell, cons->cur_x -
cons->cur_x_end);
}
}
void shell_op_cursor_move(const struct shell *shell, s16_t val)
{
struct shell_multiline_cons *cons = &shell->ctx->vt100_ctx.cons;
u16_t new_pos = shell->ctx->cmd_buff_pos + val;
s32_t row_span;
s32_t col_span;
shell_multiline_data_calc(cons, shell->ctx->cmd_buff_pos,
shell->ctx->cmd_buff_len);
/* Calculate the new cursor. */
row_span = row_span_with_buffer_offsets_get(&shell->ctx->vt100_ctx.cons,
shell->ctx->cmd_buff_pos,
new_pos);
col_span = column_span_with_buffer_offsets_get(
&shell->ctx->vt100_ctx.cons,
shell->ctx->cmd_buff_pos,
new_pos);
shell_op_cursor_vert_move(shell, -row_span);
shell_op_cursor_horiz_move(shell, col_span);
shell->ctx->cmd_buff_pos = new_pos;
}
void shell_op_word_remove(const struct shell *shell)
{
char *str = &shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos - 1];
char *str_start = &shell->ctx->cmd_buff[0];
u16_t chars_to_delete;
/* Line must not be empty and cursor must not be at 0 to continue. */
if ((shell->ctx->cmd_buff_len == 0) ||
(shell->ctx->cmd_buff_pos == 0)) {
return;
}
/* Start at the current position. */
chars_to_delete = 0;
/* Look back for all spaces then for non-spaces. */
while ((str >= str_start) && (*str == ' ')) {
++chars_to_delete;
--str;
}
while ((str >= str_start) && (*str != ' ')) {
++chars_to_delete;
--str;
}
/* Manage the buffer. */
memmove(str + 1, str + 1 + chars_to_delete,
shell->ctx->cmd_buff_len - chars_to_delete);
shell->ctx->cmd_buff_len -= chars_to_delete;
shell->ctx->cmd_buff[shell->ctx->cmd_buff_len] = '\0';
/* Update display. */
shell_op_cursor_move(shell, -chars_to_delete);
cursor_save(shell);
shell_fprintf(shell, SHELL_NORMAL, "%s", str + 1);
clear_eos(shell);
cursor_restore(shell);
}
void shell_op_cursor_home_move(const struct shell *shell)
{
shell_op_cursor_move(shell, -shell->ctx->cmd_buff_pos);
}
void shell_op_cursor_end_move(const struct shell *shell)
{
shell_op_cursor_move(shell, shell->ctx->cmd_buff_len -
shell->ctx->cmd_buff_pos);
}
void shell_op_left_arrow(const struct shell *shell)
{
if (shell->ctx->cmd_buff_pos > 0) {
shell_op_cursor_move(shell, -1);
}
}
void shell_op_right_arrow(const struct shell *shell)
{
if (shell->ctx->cmd_buff_pos < shell->ctx->cmd_buff_len) {
shell_op_cursor_move(shell, 1);
}
}
static void reprint_from_cursor(const struct shell *shell, u16_t diff,
bool data_removed)
{
/* Clear eos is needed only when newly printed command is shorter than
* previously printed command. This can happen when delete or backspace
* was called.
*
* Such condition is useful for Bluetooth devices to save number of
* bytes transmitted between terminal and device.
*/
if (data_removed) {
clear_eos(shell);
}
shell_fprintf(shell, SHELL_NORMAL, "%s",
&shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos]);
shell->ctx->cmd_buff_pos = shell->ctx->cmd_buff_len;
if (full_line_cmd(shell)) {
if (((data_removed) && (diff > 0)) || (!data_removed)) {
cursor_next_line_move(shell);
}
}
shell_op_cursor_move(shell, -diff);
}
static void data_insert(const struct shell *shell, const char *data, u16_t len)
{
u16_t after = shell->ctx->cmd_buff_len - shell->ctx->cmd_buff_pos;
char *curr_pos = &shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos];
if ((shell->ctx->cmd_buff_len + len) >= CONFIG_SHELL_CMD_BUFF_SIZE) {
return;
}
memmove(curr_pos + len, curr_pos, after);
memcpy(curr_pos, data, len);
shell->ctx->cmd_buff_len += len;
shell->ctx->cmd_buff[shell->ctx->cmd_buff_len] = '\0';
if (!flag_echo_is_set(shell)) {
shell->ctx->cmd_buff_pos += len;
return;
}
reprint_from_cursor(shell, after, false);
}
void char_replace(const struct shell *shell, char data)
{
shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos++] = data;
shell_raw_fprintf(shell->fprintf_ctx, "%c", data);
if (shell_cursor_in_empty_line(shell)) {
cursor_next_line_move(shell);
}
}
void shell_op_char_insert(const struct shell *shell, char data)
{
if (shell->ctx->internal.flags.insert_mode &&
(shell->ctx->cmd_buff_len != shell->ctx->cmd_buff_pos)) {
char_replace(shell, data);
} else {
data_insert(shell, &data, 1);
}
}
void shell_op_char_backspace(const struct shell *shell)
{
if ((shell->ctx->cmd_buff_len == 0) ||
(shell->ctx->cmd_buff_pos == 0)) {
return;
}
shell_op_cursor_move(shell, -1);
shell_op_char_delete(shell);
}
void shell_op_char_delete(const struct shell *shell)
{
u16_t diff = shell->ctx->cmd_buff_len - shell->ctx->cmd_buff_pos;
char *str = &shell->ctx->cmd_buff[shell->ctx->cmd_buff_pos];
if (diff == 0) {
return;
}
memmove(str, str + 1, diff);
--shell->ctx->cmd_buff_len;
reprint_from_cursor(shell, --diff, true);
}
void shell_op_completion_insert(const struct shell *shell,
const char *compl,
u16_t compl_len)
{
data_insert(shell, compl, compl_len);
}

128
subsys/shell/shell_ops.h Normal file
View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef SHELL_OPS_H__
#define SHELL_OPS_H__
#include <zephyr.h>
#include <shell/shell.h>
#include "shell_vt100.h"
#include "shell_utils.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SHELL_DEFAULT_TERMINAL_WIDTH (80u) /* Default PuTTY width. */
#define SHELL_DEFAULT_TERMINAL_HEIGHT (24u) /* Default PuTTY height. */
static inline void shell_raw_fprintf(const struct shell_fprintf *const ctx,
const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
shell_fprintf_fmt(ctx, fmt, args);
va_end(args);
}
/* Macro to send VT100 commands. */
#define SHELL_VT100_CMD(_shell_, _cmd_) \
do { \
static const char cmd[] = _cmd_; \
shell_raw_fprintf(_shell_->fprintf_ctx, "%s", cmd); \
} while (0)
/* Function sends VT100 command to clear the screen from cursor position to
* end of the screen.
*/
static inline void clear_eos(const struct shell *shell)
{
SHELL_VT100_CMD(shell, SHELL_VT100_CLEAREOS);
}
/* Function sends VT100 command to save cursor position. */
static inline void cursor_save(const struct shell *shell)
{
SHELL_VT100_CMD(shell, SHELL_VT100_SAVECURSOR);
}
/* Function sends VT100 command to restore saved cursor position. */
static inline void cursor_restore(const struct shell *shell)
{
SHELL_VT100_CMD(shell, SHELL_VT100_RESTORECURSOR);
}
/* Function forcing new line - cannot be replaced with function
* cursor_down_move.
*/
static inline void cursor_next_line_move(const struct shell *shell)
{
shell_raw_fprintf(shell->fprintf_ctx, "\r\n");
}
/* Function sends 1 character to the shell instance. */
static inline void shell_putc(const struct shell *shell, char ch)
{
shell_raw_fprintf(shell->fprintf_ctx, "%c", ch);
}
static inline bool flag_echo_is_set(const struct shell *shell)
{
return shell->ctx->internal.flags.echo == 1 ? true : false;
}
void shell_op_cursor_vert_move(const struct shell *shell, s32_t delta);
void shell_op_cursor_horiz_move(const struct shell *shell, s32_t delta);
void shell_op_cond_next_line(const struct shell *shell);
/* Function will move cursor back to position == cmd_buff_pos. Example usage is
* when cursor needs to be moved back after printing some text. This function
* cannot be used to move cursor to new location by manual change of
* cmd_buff_pos.
*/
void shell_op_cursor_position_synchronize(const struct shell *shell);
void shell_op_cursor_move(const struct shell *shell, s16_t val);
void shell_op_left_arrow(const struct shell *shell);
void shell_op_right_arrow(const struct shell *shell);
/*
* Removes the "word" to the left of the cursor:
* - if there are spaces at the cursor position, remove all spaces to the left
* - remove the non-spaces (word) until a space is found or a beginning of
* buffer
*/
void shell_op_word_remove(const struct shell *shell);
/* Function moves cursor to begin of command position, just after console
* name.
*/
void shell_op_cursor_home_move(const struct shell *shell);
/* Function moves cursor to end of command. */
void shell_op_cursor_end_move(const struct shell *shell);
void char_replace(const struct shell *shell, char data);
void shell_op_char_insert(const struct shell *shell, char data);
void shell_op_char_backspace(const struct shell *shell);
void shell_op_char_delete(const struct shell *shell);
void shell_op_completion_insert(const struct shell *shell,
const char *compl,
u16_t compl_len);
bool shell_cursor_in_empty_line(const struct shell *shell);
#ifdef __cplusplus
}
#endif
#endif /* SHELL_OPS_H__ */

89
subsys/shell/shell_uart.c Normal file
View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <shell/shell_uart.h>
#include <uart.h>
static void timer_handler(struct k_timer *timer)
{
struct shell_uart *sh_uart =
CONTAINER_OF(timer, struct shell_uart, timer);
if (uart_poll_in(sh_uart->dev, sh_uart->rx) == 0) {
sh_uart->rx_cnt = 1;
sh_uart->handler(SHELL_TRANSPORT_EVT_RX_RDY, sh_uart->context);
}
}
static int init(const struct shell_transport *transport,
const void *config,
shell_transport_handler_t evt_handler,
void *context)
{
struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
sh_uart->dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME);
sh_uart->handler = evt_handler;
sh_uart->context = context;
k_timer_init(&sh_uart->timer, timer_handler, NULL);
k_timer_start(&sh_uart->timer, 20, 20);
return 0;
}
static int uninit(const struct shell_transport *transport)
{
return 0;
}
static int enable(const struct shell_transport *transport, bool blocking)
{
return 0;
}
static int write(const struct shell_transport *transport,
const void *data, size_t length, size_t *cnt)
{
struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
const u8_t *data8 = (const u8_t *)data;
for (size_t i = 0; i < length; i++) {
uart_poll_out(sh_uart->dev, data8[i]);
}
*cnt = length;
sh_uart->handler(SHELL_TRANSPORT_EVT_TX_RDY, sh_uart->context);
return 0;
}
static int read(const struct shell_transport *transport,
void *data, size_t length, size_t *cnt)
{
struct shell_uart *sh_uart = (struct shell_uart *)transport->ctx;
if (sh_uart->rx_cnt) {
memcpy(data, sh_uart->rx, 1);
sh_uart->rx_cnt = 0;
*cnt = 1;
} else {
*cnt = 0;
}
return 0;
}
const struct shell_transport_api shell_uart_transport_api = {
.init = init,
.uninit = uninit,
.enable = enable,
.write = write,
.read = read
};

323
subsys/shell/shell_utils.c Normal file
View file

@ -0,0 +1,323 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "shell_utils.h"
#include <ctype.h>
/* Calculates relative line number of given position in buffer */
static u32_t line_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
u16_t buffer_pos)
{
return ((buffer_pos + cons->name_len) / cons->terminal_wid);
}
/* Calculates column number of given position in buffer */
static u32_t col_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
u16_t buffer_pos)
{
/* columns are counted from 1 */
return (1 + ((buffer_pos + cons->name_len) % cons->terminal_wid));
}
s32_t column_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
u16_t offset1,
u16_t offset2)
{
return col_num_with_buffer_offset_get(cons, offset2)
- col_num_with_buffer_offset_get(cons, offset1);
}
s32_t row_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
u16_t offset1,
u16_t offset2)
{
return line_num_with_buffer_offset_get(cons, offset2)
- line_num_with_buffer_offset_get(cons, offset1);
}
void shell_multiline_data_calc(struct shell_multiline_cons *cons,
u16_t buff_pos, u16_t buff_len)
{
/* Current cursor position in command.
* +1 -> because home position is (1, 1)
*/
cons->cur_x = (buff_pos + cons->name_len) % cons->terminal_wid + 1;
cons->cur_y = (buff_pos + cons->name_len) / cons->terminal_wid + 1;
/* Extreme position when cursor is at the end of command. */
cons->cur_y_end = (buff_len + cons->name_len) / cons->terminal_wid + 1;
cons->cur_x_end = (buff_len + cons->name_len) % cons->terminal_wid + 1;
}
static void make_argv(char **ppcmd, u8_t c, u8_t quote)
{
char *cmd = *ppcmd;
while (1) {
c = *cmd;
if (c == '\0') {
break;
}
if (!quote) {
switch (c) {
case '\\':
memmove(cmd, cmd + 1,
shell_strlen(cmd));
cmd += 1;
continue;
case '\'':
case '\"':
memmove(cmd, cmd + 1,
shell_strlen(cmd));
quote = c;
continue;
default:
break;
}
}
if (quote == c) {
memmove(cmd, cmd + 1, shell_strlen(cmd));
quote = 0;
continue;
}
if (quote && c == '\\') {
char t = *(cmd + 1);
if (t == quote) {
memmove(cmd, cmd + 1,
shell_strlen(cmd));
cmd += 1;
continue;
}
if (t == '0') {
u8_t i;
u8_t v = 0;
for (i = 2; i < (2 + 3); i++) {
t = *(cmd + i);
if (t >= '0' && t <= '7') {
v = (v << 3) | (t - '0');
} else {
break;
}
}
if (i > 2) {
memmove(cmd, cmd + (i - 1),
shell_strlen(cmd) - (i - 2));
*cmd++ = v;
continue;
}
}
if (t == 'x') {
u8_t i;
u8_t v = 0;
for (i = 2; i < (2 + 2); i++) {
t = *(cmd + i);
if (t >= '0' && t <= '9') {
v = (v << 4) | (t - '0');
} else if ((t >= 'a') &&
(t <= 'f')) {
v = (v << 4) | (t - 'a' + 10);
} else if ((t >= 'A') && (t <= 'F')) {
v = (v << 4) | (t - 'A' + 10);
} else {
break;
}
}
if (i > 2) {
memmove(cmd, cmd + (i - 1),
shell_strlen(cmd) - (i - 2));
*cmd++ = v;
continue;
}
}
}
if (!quote && isspace((int) c)) {
break;
}
cmd += 1;
}
*ppcmd = cmd;
}
char shell_make_argv(size_t *argc, char **argv, char *cmd, u8_t max_argc)
{
char quote = 0;
char c;
*argc = 0;
do {
c = *cmd;
if (c == '\0') {
break;
}
if (isspace((int) c)) {
*cmd++ = '\0';
continue;
}
argv[(*argc)++] = cmd;
quote = 0;
make_argv(&cmd, c, quote);
} while (*argc < max_argc);
argv[*argc] = 0;
return quote;
}
void shell_pattern_remove(char *buff, u16_t *buff_len, const char *pattern)
{
char *pattern_addr = strstr(buff, pattern);
u16_t pattern_len = shell_strlen(pattern);
size_t shift;
if (!pattern_addr) {
return;
}
if (pattern_addr > buff) {
if (*(pattern_addr - 1) == ' ') {
pattern_len++; /* space needs to be removed as well */
pattern_addr--; /* set pointer to space */
}
}
shift = shell_strlen(pattern_addr) - pattern_len + 1; /* +1 for EOS */
*buff_len -= pattern_len;
memmove(pattern_addr, pattern_addr + pattern_len, shift);
}
int shell_command_add(char *buff, u16_t *buff_len,
const char *new_cmd, const char *pattern)
{
u16_t cmd_len = shell_strlen(new_cmd);
char *cmd_source_addr;
u16_t shift;
/* +1 for space */
if ((*buff_len + cmd_len + 1) > CONFIG_SHELL_CMD_BUFF_SIZE) {
return -ENOMEM;
}
cmd_source_addr = strstr(buff, pattern);
if (!cmd_source_addr) {
return -EINVAL;
}
shift = shell_strlen(cmd_source_addr);
/* make place for new command: + 1 for space + 1 for EOS */
memmove(cmd_source_addr + cmd_len + 1, cmd_source_addr, shift + 1);
memcpy(cmd_source_addr, new_cmd, cmd_len);
cmd_source_addr[cmd_len] = ' ';
*buff_len += cmd_len + 1; /* + 1 for space */
return 0;
}
void shell_spaces_trim(char *str)
{
u16_t len = shell_strlen(str);
u16_t shift = 0;
if (!str) {
return;
}
for (u16_t i = 0; i < len - 1; i++) {
if (isspace((int)str[i])) {
for (u16_t j = i + 1; j < len; j++) {
if (isspace((int)str[j])) {
shift++;
continue;
}
if (shift > 0) {
/* +1 for EOS */
memmove(&str[i + 1],
&str[j],
len - shift + 1);
len -= shift;
shift = 0;
}
break;
}
}
}
}
void shell_buffer_trim(char *buff, u16_t *buff_len)
{
u16_t i = 0;
/* no command in the buffer */
if (buff[0] == '\0') {
return;
}
while (isspace((int) buff[*buff_len - 1])) {
*buff_len -= 1;
if (*buff_len == 0) {
buff[0] = '\0';
return;
}
}
buff[*buff_len] = '\0';
/* Counting whitespace characters starting from beginning of the
* command.
*/
while (isspace((int) buff[i++])) {
if (i == 0) {
buff[0] = '\0';
return;
}
}
/* Removing counted whitespace characters. */
if (--i > 0) {
memmove(buff, buff + i, (*buff_len + 1) - i); /* +1 for '\0' */
*buff_len = *buff_len - i;
}
}
u16_t shell_str_similarity_check(const char *str_a, const char *str_b)
{
u16_t cnt = 0;
while (str_a[cnt] != '\0') {
if (str_a[cnt] != str_b[cnt]) {
return cnt;
}
if (++cnt == 0) {
return --cnt; /* too long strings */
}
}
return cnt;
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef SHELL_UTILS_H__
#define SHELL_UTILS_H__
#include <zephyr.h>
#include <shell/shell.h>
#ifdef __cplusplus
extern "C" {
#endif
#define SHELL_MSG_SPECIFY_SUBCOMMAND "Please specify a subcommand.\r\n"
#define SHELL_DEFAULT_TERMINAL_WIDTH (80u) /* Default PuTTY width. */
#define SHELL_DEFAULT_TERMINAL_HEIGHT (24u) /* Default PuTTY height. */
s32_t row_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
u16_t offset1,
u16_t offset2);
s32_t column_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
u16_t offset1,
u16_t offset2);
void shell_multiline_data_calc(struct shell_multiline_cons *cons,
u16_t buff_pos, u16_t buff_len);
static inline size_t shell_strlen(const char *str)
{
return str == NULL ? 0 : strlen(str);
}
char shell_make_argv(size_t *argc, char **argv, char *cmd, uint8_t max_argc);
/** @brief Removes pattern and following space
*
*/
void shell_pattern_remove(char *buff, u16_t *buff_len, const char *pattern);
int shell_command_add(char *buff, u16_t *buff_len,
const char *new_cmd, const char *pattern);
void shell_spaces_trim(char *str);
/** @brief Remove white chars from beginning and end of command buffer.
*
*/
void shell_buffer_trim(char *buff, u16_t *buff_len);
/* Function checks how many identical characters have two strings starting
* from the first character.
*/
u16_t shell_str_similarity_check(const char *str_a, const char *str_b);
#ifdef __cplusplus
}
#endif
#endif /* SHELL_UTILS_H__ */

589
subsys/shell/shell_vt100.h Normal file
View file

@ -0,0 +1,589 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef SHELL_VT100_H__
#define SHELL_VT100_H__
#define SHELL_VT100_ASCII_ESC (0x1b)
#define SHELL_VT100_ASCII_DEL (0x7F)
#define SHELL_VT100_ASCII_BSPACE (0x08)
#define SHELL_VT100_ASCII_CTRL_A (0x01)
#define SHELL_VT100_ASCII_CTRL_C (0x03)
#define SHELL_VT100_ASCII_CTRL_E (0x05)
#define SHELL_VT100_ASCII_CTRL_L (0x0C)
#define SHELL_VT100_ASCII_CTRL_U (0x15)
#define SHELL_VT100_ASCII_CTRL_W (0x17)
#define SHELL_VT100_SETNL \
{ \
SHELL_VT100_ASCII_ESC, '[', '2', '0', 'h', '\0' \
} /* Set new line mode */
#define SHELL_VT100_SETAPPL \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '1', 'h', '\0' \
} /* Set cursor key to application */
#define SHELL_VT100_SETCOL_132 \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '3', 'h', '\0' \
} /* Set number of columns to 132 */
#define SHELL_VT100_SETSMOOTH \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '4', 'h', '\0' \
} /* Set smooth scrolling */
#define SHELL_VT100_SETREVSCRN \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '5', 'h', '\0' \
} /* Set reverse video on screen */
#define SHELL_VT100_SETORGREL \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '6', 'h', '\0' \
} /* Set origin to relative */
#define SHELL_VT100_SETWRAP_ON \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '7', 'h', '\0' \
} /* Set auto-wrap mode */
#define SHELL_VT100_SETWRAP_OFF \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '7', 'l', '\0' \
} /* Set auto-wrap mode */
#define SHELL_VT100_SETREP \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '8', 'h', '\0' \
} /* Set auto-repeat mode */
#define SHELL_VT100_SETINTER \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '9', 'h', '\0' \
} /* Set interlacing mode */
#define SHELL_VT100_SETLF \
{ \
SHELL_VT100_ASCII_ESC, '[', '2', '0', 'l', '\0' \
} /* Set line feed mode */
#define SHELL_VT100_SETCURSOR \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '1', 'l', '\0' \
} /* Set cursor key to cursor */
#define SHELL_VT100_SETVT52 \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '2', 'l', '\0' \
} /* Set VT52 (versus ANSI) */
#define SHELL_VT100_SETCOL_80 \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '3', 'l', '\0' \
} /* Set number of columns to 80 */
#define SHELL_VT100_SETJUMP \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '4', 'l', '\0' \
} /* Set jump scrolling */
#define SHELL_VT100_SETNORMSCRN \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '5', 'l', '\0' \
} /* Set normal video on screen */
#define SHELL_VT100_SETORGABS \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '6', 'l', '\0' \
} /* Set origin to absolute */
#define SHELL_VT100_RESETWRAP \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '7', 'l', '\0' \
} /* Reset auto-wrap mode */
#define SHELL_VT100_RESETREP \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '8', 'l', '\0' \
} /* Reset auto-repeat mode */
#define SHELL_VT100_RESETINTER \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '9', 'l', '\0' \
} /* Reset interlacing mode */
#define SHELL_VT100_ALTKEYPAD \
{ \
SHELL_VT100_ASCII_ESC, '=', '\0' \
} /* Set alternate keypad mode */
#define SHELL_VT100_NUMKEYPAD \
{ \
SHELL_VT100_ASCII_ESC, '>', '\0' \
} /* Set numeric keypad mode */
#define SHELL_VT100_SETUKG0 \
{ \
SHELL_VT100_ASCII_ESC, '(', 'A', '\0' \
} /* Set United Kingdom G0 character set */
#define SHELL_VT100_SETUKG1 \
{ \
SHELL_VT100_ASCII_ESC, ')', 'A', '\0' \
} /* Set United Kingdom G1 character set */
#define SHELL_VT100_SETUSG0 \
{ \
SHELL_VT100_ASCII_ESC, '(', 'B', '\0' \
} /* Set United States G0 character set */
#define SHELL_VT100_SETUSG1 \
{ \
SHELL_VT100_ASCII_ESC, ')', 'B', '\0' \
} /* Set United States G1 character set */
#define SHELL_VT100_SETSPECG0 \
{ \
SHELL_VT100_ASCII_ESC, '(', '0', '\0' \
} /* Set G0 special chars. & line set */
#define SHELL_VT100_SETSPECG1 \
{ \
SHELL_VT100_ASCII_ESC, ')', '0', '\0' \
} /* Set G1 special chars. & line set */
#define SHELL_VT100_SETALTG0 \
{ \
SHELL_VT100_ASCII_ESC, '(', '1', '\0' \
} /* Set G0 alternate character ROM */
#define SHELL_VT100_SETALTG1 \
{ \
SHELL_VT100_ASCII_ESC, ')', '1', '\0' \
} /* Set G1 alternate character ROM */
#define SHELL_VT100_SETALTSPECG0 \
{ \
SHELL_VT100_ASCII_ESC, '(', '2', '\0' \
} /* Set G0 alt char ROM and spec. graphics */
#define SHELL_VT100_SETALTSPECG1 \
{ \
SHELL_VT100_ASCII_ESC, ')', '2', '\0' \
} /* Set G1 alt char ROM and spec. graphics */
#define SHELL_VT100_SETSS2 \
{ \
SHELL_VT100_ASCII_ESC, 'N', '\0' \
} /* Set single shift 2 */
#define SHELL_VT100_SETSS3 \
{ \
SHELL_VT100_ASCII_ESC, 'O', '\0' \
} /* Set single shift 3 */
#define SHELL_VT100_MODESOFF \
{ \
SHELL_VT100_ASCII_ESC, '[', 'm', '\0' \
} /* Turn off character attributes */
#define SHELL_VT100_MODESOFF_ \
{ \
SHELL_VT100_ASCII_ESC, '[', '0', 'm', '\0' \
} /* Turn off character attributes */
#define SHELL_VT100_BOLD \
{ \
SHELL_VT100_ASCII_ESC, '[', '1', 'm', '\0' \
} /* Turn bold mode on */
#define SHELL_VT100_LOWINT \
{ \
SHELL_VT100_ASCII_ESC, '[', '2', 'm', '\0' \
} /* Turn low intensity mode on */
#define SHELL_VT100_UNDERLINE \
{ \
SHELL_VT100_ASCII_ESC, '[', '4', 'm', '\0' \
} /* Turn underline mode on */
#define SHELL_VT100_BLINK \
{ \
SHELL_VT100_ASCII_ESC, '[', '5', 'm', '\0' \
} /* Turn blinking mode on */
#define SHELL_VT100_REVERSE \
{ \
SHELL_VT100_ASCII_ESC, '[', '7', 'm', '\0' \
} /* Turn reverse video on */
#define SHELL_VT100_INVISIBLE \
{ \
SHELL_VT100_ASCII_ESC, '[', '8', 'm', '\0' \
} /* Turn invisible text mode on */
#define SHELL_VT100_SETWIN(t, b) \
{ \
SHELL_VT100_ASCII_ESC, '[', (t), ';', (b), 'r', '\0' \
} /* Set top and bottom line#s of a window */
#define SHELL_VT100_CURSORUP(n) \
{ \
SHELL_VT100_ASCII_ESC, '[', (n), 'A', '\0' \
} /* Move cursor up n lines */
#define SHELL_VT100_CURSORDN(n) \
{ \
SHELL_VT100_ASCII_ESC, '[', (n), 'B', '\0' \
} /* Move cursor down n lines */
#define SHELL_VT100_CURSORRT(n) \
{ \
SHELL_VT100_ASCII_ESC, '[', (n), 'C', '\0' \
} /* Move cursor right n lines */
#define SHELL_VT100_CURSORLF(n) \
{ \
SHELL_VT100_ASCII_ESC, '[', (n), 'D', '\0' \
} /* Move cursor left n lines */
#define SHELL_VT100_CURSORHOME \
{ \
SHELL_VT100_ASCII_ESC, '[', 'H', '\0' \
} /* Move cursor to upper left corner */
#define SHELL_VT100_CURSORHOME_ \
{ \
SHELL_VT100_ASCII_ESC, '[', ';', 'H', '\0' \
} /* Move cursor to upper left corner */
#define SHELL_VT100_CURSORPOS(v, h) \
{ \
SHELL_VT100_ASCII_ESC, '[', (v), ';', (h), 'H', '\0' \
} /* Move cursor to screen location v,h */
#define SHELL_VT100_HVHOME \
{ \
SHELL_VT100_ASCII_ESC, '[', 'f', '\0' \
} /* Move cursor to upper left corner */
#define SHELL_VT100_HVHOME_ \
{ \
SHELL_VT100_ASCII_ESC, '[', ';', 'f', '\0' \
} /* Move cursor to upper left corner */
#define SHELL_VT100_HVPOS(v, h) \
{ \
SHELL_VT100_ASCII_ESC, '[', (v), ';', (h), 'f', '\0' \
} /* Move cursor to screen location v,h */
#define SHELL_VT100_INDEX \
{ \
SHELL_VT100_ASCII_ESC, 'D', '\0' \
} /* Move/scroll window up one line */
#define SHELL_VT100_REVINDEX \
{ \
SHELL_VT100_ASCII_ESC, 'M', '\0' \
} /* Move/scroll window down one line */
#define SHELL_VT100_NEXTLINE \
{ \
SHELL_VT100_ASCII_ESC, 'E', '\0' \
} /* Move to next line */
#define SHELL_VT100_SAVECURSOR \
{ \
SHELL_VT100_ASCII_ESC, '7', '\0' \
} /* Save cursor position and attributes */
#define SHELL_VT100_RESTORECURSOR \
{ \
SHELL_VT100_ASCII_ESC, '8', '\0' \
} /* Restore cursor position and attribute */
#define SHELL_VT100_TABSET \
{ \
SHELL_VT100_ASCII_ESC, 'H', '\0' \
} /* Set a tab at the current column */
#define SHELL_VT100_TABCLR \
{ \
SHELL_VT100_ASCII_ESC, '[', 'g', '\0' \
} /* Clear a tab at the current column */
#define SHELL_VT100_TABCLR_ \
{ \
SHELL_VT100_ASCII_ESC, '[', '0', 'g', '\0' \
} /* Clear a tab at the current column */
#define SHELL_VT100_TABCLRALL \
{ \
SHELL_VT100_ASCII_ESC, '[', '3', 'g', '\0' \
} /* Clear all tabs */
#define SHELL_VT100_DHTOP \
{ \
SHELL_VT100_ASCII_ESC, '#', '3', '\0' \
} /* Double-height letters, top half */
#define SHELL_VT100_DHBOT \
{ \
SHELL_VT100_ASCII_ESC, '#', '4', '\0' \
} /* Double-height letters, bottom hal */
#define SHELL_VT100_SWSH \
{ \
SHELL_VT100_ASCII_ESC, '#', '5', '\0' \
} /* Single width, single height letters */
#define SHELL_VT100_DWSH \
{ \
SHELL_VT100_ASCII_ESC, '#', '6', '\0' \
} /* Double width, single height letters */
#define SHELL_VT100_CLEAREOL \
{ \
SHELL_VT100_ASCII_ESC, '[', 'K', '\0' \
} /* Clear line from cursor right */
#define SHELL_VT100_CLEAREOL_ \
{ \
SHELL_VT100_ASCII_ESC, '[', '0', 'K', '\0' \
} /* Clear line from cursor right */
#define SHELL_VT100_CLEARBOL \
{ \
SHELL_VT100_ASCII_ESC, '[', '1', 'K', '\0' \
} /* Clear line from cursor left */
#define SHELL_VT100_CLEARLINE \
{ \
SHELL_VT100_ASCII_ESC, '[', '2', 'K', '\0' \
} /* Clear entire line */
#define SHELL_VT100_CLEAREOS \
{ \
SHELL_VT100_ASCII_ESC, '[', 'J', '\0' \
} /* Clear screen from cursor down */
#define SHELL_VT100_CLEAREOS_ \
{ \
SHELL_VT100_ASCII_ESC, '[', '0', 'J', '\0' \
} /* Clear screen from cursor down */
#define SHELL_VT100_CLEARBOS \
{ \
SHELL_VT100_ASCII_ESC, '[', '1', 'J', '\0' \
} /* Clear screen from cursor up */
#define SHELL_VT100_CLEARSCREEN \
{ \
SHELL_VT100_ASCII_ESC, '[', '2', 'J', '\0' \
} /* Clear entire screen */
#define SHELL_VT100_DEVSTAT \
{ \
SHELL_VT100_ASCII_ESC, '5', 'n', '\0' \
} /* Device status report */
#define SHELL_VT100_TERMOK \
{ \
SHELL_VT100_ASCII_ESC, '0', 'n', '\0' \
} /* Response: terminal is OK */
#define SHELL_VT100_TERMNOK \
{ \
SHELL_VT100_ASCII_ESC, '3', 'n', '\0' \
} /* Response: terminal is not OK */
#define SHELL_VT100_GETCURSOR \
{ \
SHELL_VT100_ASCII_ESC, '[', '6', 'n', '\0' \
} /* Get cursor position */
#define SHELL_VT100_CURSORPOSAT \
{ \
SHELL_VT100_ASCII_ESC, (v), ';', (h), 'R', '\0' \
} /* Response: cursor is at v,h */
#define SHELL_VT100_IDENT \
{ \
SHELL_VT100_ASCII_ESC, '[', 'c', '\0' \
} /* Identify what terminal type */
#define SHELL_VT100_IDENT_ \
{ \
SHELL_VT100_ASCII_ESC, '[', '0', 'c', '\0' \
} /* Identify what terminal type */
#define SHELL_VT100_GETTYPE \
{ \
SHELL_VT100_ASCII_ESC, '[', '?', '1', ';', (n), '0', 'c', '\0' \
} /* Response: terminal type code n */
#define SHELL_VT100_RESET \
{ \
SHELL_VT100_ASCII_ESC, 'c', '\0' \
} /*Reset terminal to initial state */
#define SHELL_VT100_ALIGN \
{ \
SHELL_VT100_ASCII_ESC, '#', '8', '\0' \
} /* Screen alignment display */
#define SHELL_VT100_TESTPU \
{ \
SHELL_VT100_ASCII_ESC, '[', '2', ';', '1', 'y', '\0' \
} /* Confidence power up test */
#define SHELL_VT100_TESTLB \
{ \
SHELL_VT100_ASCII_ESC, '[', '2', ';', '2', 'y', '\0' \
} /* Confidence loopback test */
#define SHELL_VT100_TESTPUREP \
{ \
SHELL_VT100_ASCII_ESC, '[', '2', ';', '9', 'y', '\0' \
} /* Repeat power up test */
#define SHELL_VT100_TESTLBREP \
{ \
SHELL_VT100_ASCII_ESC, '[', '2', ';', '1', '0', 'y', '\0' \
} /* Repeat loopback test */
#define SHELL_VT100_LEDSOFF \
{ \
SHELL_VT100_ASCII_ESC, '[', '0', 'q', '\0' \
} /* Turn off all four leds */
#define SHELL_VT100_LED1 \
{ \
SHELL_VT100_ASCII_ESC, '[', '1', 'q', '\0' \
} /* Turn on LED #1 */
#define SHELL_VT100_LED2 \
{ \
SHELL_VT100_ASCII_ESC, '[', '2', 'q', '\0' \
} /* Turn on LED #2 */
#define SHELL_VT100_LED3 \
{ \
SHELL_VT100_ASCII_ESC, '[', '3', 'q', '\0' \
} /* Turn on LED #3 */
#define SHELL_VT100_LED4 \
{ \
SHELL_VT100_ASCII_ESC, '[', '4', 'q', '\0' \
} /* Turn on LED #4 */
/* Function Keys */
#define SHELL_VT100_PF1 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'P', '\0' \
}
#define SHELL_VT100_PF2 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'Q', '\0' \
}
#define SHELL_VT100_PF3 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'R', '\0' \
}
#define SHELL_VT100_PF4 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'S', '\0' \
}
/* Arrow keys */
#define SHELL_VT100_UP_RESET \
{ \
SHELL_VT100_ASCII_ESC, 'A', '\0' \
}
#define SHELL_VT100_UP_SET \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'A', '\0' \
}
#define SHELL_VT100_DOWN_RESET \
{ \
SHELL_VT100_ASCII_ESC, 'B', '\0' \
}
#define SHELL_VT100_DOWN_SET \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'B', '\0' \
}
#define SHELL_VT100_RIGHT_RESET \
{ \
SHELL_VT100_ASCII_ESC, 'C', '\0' \
}
#define SHELL_VT100_RIGHT_SET \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'C', '\0' \
}
#define SHELL_VT100_LEFT_RESET \
{ \
SHELL_VT100_ASCII_ESC, 'D', '\0' \
}
#define SHELL_VT100_LEFT_SET \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'D', '\0' \
}
/* Numeric Keypad Keys */
#define SHELL_VT100_NUMERIC_0 \
{ \
'0', '\0' \
}
#define SHELL_VT100_ALT_0 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'p', '\0' \
}
#define SHELL_VT100_NUMERIC_1 \
{ \
'1', '\0' \
}
#define SHELL_VT100_ALT_1 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'q', '\0' \
}
#define SHELL_VT100_NUMERIC_2 \
{ \
'2', '\0' \
}
#define SHELL_VT100_ALT_2 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'r', '\0' \
}
#define SHELL_VT100_NUMERIC_3 \
{ \
'3', '\0' \
}
#define SHELL_VT100_ALT_3 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 's', '\0' \
}
#define SHELL_VT100_NUMERIC_4 \
{ \
'4', '\0' \
}
#define SHELL_VT100_ALT_4 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 't', '\0' \
}
#define SHELL_VT100_NUMERIC_5 \
{ \
'5', '\0' \
}
#define SHELL_VT100_ALT_5 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'u', '\0' \
}
#define SHELL_VT100_NUMERIC_6 \
{ \
'6', '\0' \
}
#define SHELL_VT100_ALT_6 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'v', '\0' \
}
#define SHELL_VT100_NUMERIC_7 \
{ \
'7', '\0' \
}
#define SHELL_VT100_ALT_7 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'w', '\0' \
}
#define SHELL_VT100_NUMERIC_8 \
{ \
'8', '\0' \
}
#define SHELL_VT100_ALT_8 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'x', '\0' \
}
#define SHELL_VT100_NUMERIC_9 \
{ \
'9', '\0' \
}
#define SHELL_VT100_ALT_9 \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'y' \
}
#define SHELL_VT100_NUMERIC_MINUS \
{ \
'-', '\0' \
}
#define SHELL_VT100_ALT_MINUS \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'm', '\0' \
}
#define SHELL_VT100_NUMERIC_COMMA \
{ \
',', '\0' \
}
#define SHELL_VT100_ALT_COMMA \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'l', '\0' \
}
#define SHELL_VT100_NUMERIC_ENTER \
{ \
ASCII_CR \
}
#define SHELL_VT100_NUMERIC_PERIOD \
{ \
'.', '\0' \
}
#define SHELL_VT100_ALT_PERIOD \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'n', '\0' \
}
#define SHELL_VT100_ALT_ENTER \
{ \
SHELL_VT100_ASCII_ESC, 'O', 'M', '\0' \
}
#define SHELL_VT100_BGCOLOR(__col) \
{ \
SHELL_VT100_ASCII_ESC, '[', '4', '0' + (__col), 'm', '\0' \
}
#define SHELL_VT100_COLOR(__col) \
{ \
SHELL_VT100_ASCII_ESC, '[', '1', ';', '3', '0' + (__col), 'm', '\0' \
}
#endif /* SHELL_VT100_H__ */