shell: Add new way of adding subcommands
Added macro SHELL_SUBCMD_SET_CREATE which creates a set of subcommands. SHELL_SUBCMD_ADD and SHELL_SUBCMD_COND_ADD can be used from any file to add command to the set. This approach allows to have subcommands added from multiple files. Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
parent
270dea717c
commit
3ee5c57934
3 changed files with 178 additions and 34 deletions
|
@ -26,6 +26,20 @@
|
||||||
__shell_root_cmds_end = .;
|
__shell_root_cmds_end = .;
|
||||||
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
||||||
|
|
||||||
|
SECTION_DATA_PROLOGUE(shell_subcmds_sections,,)
|
||||||
|
{
|
||||||
|
__shell_subcmds_start = .;
|
||||||
|
KEEP(*(SORT(.shell_subcmd_*)));
|
||||||
|
__shell_subcmds_end = .;
|
||||||
|
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
||||||
|
|
||||||
|
SECTION_DATA_PROLOGUE(shell_dynamic_subcmds_sections,,)
|
||||||
|
{
|
||||||
|
__shell_dynamic_subcmds_start = .;
|
||||||
|
KEEP(*(SORT(.shell_dynamic_subcmd_*)));
|
||||||
|
__shell_dynamic_subcmds_end = .;
|
||||||
|
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
||||||
|
|
||||||
SECTION_DATA_PROLOGUE(font_entry_sections,,)
|
SECTION_DATA_PROLOGUE(font_entry_sections,,)
|
||||||
{
|
{
|
||||||
__font_entry_start = .;
|
__font_entry_start = .;
|
||||||
|
|
|
@ -91,15 +91,12 @@ typedef void (*shell_dynamic_get)(size_t idx,
|
||||||
/**
|
/**
|
||||||
* @brief Shell command descriptor.
|
* @brief Shell command descriptor.
|
||||||
*/
|
*/
|
||||||
struct shell_cmd_entry {
|
union shell_cmd_entry {
|
||||||
bool is_dynamic;
|
/*!< Pointer to function returning dynamic commands.*/
|
||||||
union union_cmd_entry {
|
shell_dynamic_get dynamic_get;
|
||||||
/*!< Pointer to function returning dynamic commands.*/
|
|
||||||
shell_dynamic_get dynamic_get;
|
|
||||||
|
|
||||||
/*!< Pointer to array of static commands. */
|
/*!< Pointer to array of static commands. */
|
||||||
const struct shell_static_entry *entry;
|
const struct shell_static_entry *entry;
|
||||||
} u;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct shell;
|
struct shell;
|
||||||
|
@ -158,15 +155,26 @@ typedef int (*shell_cmd_handler)(const struct shell *shell,
|
||||||
typedef int (*shell_dict_cmd_handler)(const struct shell *shell, size_t argc,
|
typedef int (*shell_dict_cmd_handler)(const struct shell *shell, size_t argc,
|
||||||
char **argv, void *data);
|
char **argv, void *data);
|
||||||
|
|
||||||
|
/* When entries are added to the memory section a padding is applied for
|
||||||
|
* native_posix_64 and x86_64 targets. Adding padding to allow handle data
|
||||||
|
* in the memory section as array.
|
||||||
|
*/
|
||||||
|
#if (defined(CONFIG_ARCH_POSIX) && defined(CONFIG_64BIT)) || defined(CONFIG_X86_64)
|
||||||
|
#define Z_SHELL_STATIC_ENTRY_PADDING 24
|
||||||
|
#else
|
||||||
|
#define Z_SHELL_STATIC_ENTRY_PADDING 0
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @brief Shell static command descriptor.
|
* @brief Shell static command descriptor.
|
||||||
*/
|
*/
|
||||||
struct shell_static_entry {
|
struct shell_static_entry {
|
||||||
const char *syntax; /*!< Command syntax strings. */
|
const char *syntax; /*!< Command syntax strings. */
|
||||||
const char *help; /*!< Command help string. */
|
const char *help; /*!< Command help string. */
|
||||||
const struct shell_cmd_entry *subcmd; /*!< Pointer to subcommand. */
|
const union shell_cmd_entry *subcmd; /*!< Pointer to subcommand. */
|
||||||
shell_cmd_handler handler; /*!< Command handler. */
|
shell_cmd_handler handler; /*!< Command handler. */
|
||||||
struct shell_static_args args; /*!< Command arguments. */
|
struct shell_static_args args; /*!< Command arguments. */
|
||||||
|
uint8_t padding[Z_SHELL_STATIC_ENTRY_PADDING];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -188,12 +196,11 @@ struct shell_static_entry {
|
||||||
mandatory, optional) \
|
mandatory, optional) \
|
||||||
static const struct shell_static_entry UTIL_CAT(_shell_, syntax) = \
|
static const struct shell_static_entry UTIL_CAT(_shell_, syntax) = \
|
||||||
SHELL_CMD_ARG(syntax, subcmd, help, handler, mandatory, optional); \
|
SHELL_CMD_ARG(syntax, subcmd, help, handler, mandatory, optional); \
|
||||||
static const struct shell_cmd_entry UTIL_CAT(shell_cmd_, syntax) \
|
static const union shell_cmd_entry UTIL_CAT(shell_cmd_, syntax) \
|
||||||
__attribute__ ((section("." \
|
__attribute__ ((section("." \
|
||||||
STRINGIFY(UTIL_CAT(shell_root_cmd_, syntax))))) \
|
STRINGIFY(UTIL_CAT(shell_root_cmd_, syntax))))) \
|
||||||
__attribute__((used)) = { \
|
__attribute__((used)) = { \
|
||||||
.is_dynamic = false, \
|
.entry = &UTIL_CAT(_shell_, syntax) \
|
||||||
.u = {.entry = &UTIL_CAT(_shell_, syntax)} \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -227,9 +234,9 @@ struct shell_static_entry {
|
||||||
(\
|
(\
|
||||||
static shell_cmd_handler dummy_##syntax##_handler __unused = \
|
static shell_cmd_handler dummy_##syntax##_handler __unused = \
|
||||||
handler;\
|
handler;\
|
||||||
static const struct shell_cmd_entry *dummy_subcmd_##syntax \
|
static const union shell_cmd_entry *dummy_subcmd_##syntax \
|
||||||
__unused = subcmd\
|
__unused = subcmd\
|
||||||
)\
|
) \
|
||||||
)
|
)
|
||||||
/**
|
/**
|
||||||
* @brief Macro for defining and adding a root command (level 0) with
|
* @brief Macro for defining and adding a root command (level 0) with
|
||||||
|
@ -281,11 +288,85 @@ struct shell_static_entry {
|
||||||
static const struct shell_static_entry shell_##name[] = { \
|
static const struct shell_static_entry shell_##name[] = { \
|
||||||
__VA_ARGS__ \
|
__VA_ARGS__ \
|
||||||
}; \
|
}; \
|
||||||
static const struct shell_cmd_entry name = { \
|
static const union shell_cmd_entry name = { \
|
||||||
.is_dynamic = false, \
|
.entry = shell_##name \
|
||||||
.u = { .entry = shell_##name } \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define Z_SHELL_UNDERSCORE(x) _##x
|
||||||
|
#define Z_SHELL_SUBCMD_NAME(...) \
|
||||||
|
UTIL_CAT(shell_subcmd, MACRO_MAP_CAT(Z_SHELL_UNDERSCORE, __VA_ARGS__))
|
||||||
|
|
||||||
|
/** @brief Create set of subcommands.
|
||||||
|
*
|
||||||
|
* Commands to this set are added using @ref SHELL_SUBCMD_ADD and @ref SHELL_SUBCMD_COND_ADD.
|
||||||
|
* Commands can be added from multiple files.
|
||||||
|
*
|
||||||
|
* @param[in] _name Name of the set. @p _name is used to refer the set in the parent
|
||||||
|
* command.
|
||||||
|
*
|
||||||
|
* @param[in] _parent Set of comma separated parent commands in parenthesis, e.g.
|
||||||
|
* (foo_cmd) if subcommands are for the root command "foo_cmd".
|
||||||
|
*/
|
||||||
|
#define SHELL_SUBCMD_SET_CREATE(_name, _parent) \
|
||||||
|
static const struct shell_static_entry _name \
|
||||||
|
__attribute__ ((section("." \
|
||||||
|
STRINGIFY(Z_SHELL_SUBCMD_NAME(NUM_VA_ARGS_LESS_1 _parent, \
|
||||||
|
__DEBRACKET _parent))))) \
|
||||||
|
__attribute__((used))
|
||||||
|
|
||||||
|
/** @brief Conditionally add command to the set of subcommands.
|
||||||
|
*
|
||||||
|
* Add command to the set created with @ref SHELL_SUBCMD_SET_CREATE.
|
||||||
|
*
|
||||||
|
* @note The name of the section is formed as concatenation of number of parent
|
||||||
|
* commands, names of all parent commands and own syntax. Number of parent commands
|
||||||
|
* is added to ensure that section prefix is unique. Without it subcommands of
|
||||||
|
* (foo) and (foo, cmd1) would mix.
|
||||||
|
*
|
||||||
|
* @param[in] _flag Compile time flag. Command is present only if flag
|
||||||
|
* exists and equals 1.
|
||||||
|
* @param[in] _parent Parent command sequence. Comma separated in parenthesis.
|
||||||
|
* @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.
|
||||||
|
* @param[in] _mand Number of mandatory arguments including command name.
|
||||||
|
* @param[in] _opt Number of optional arguments.
|
||||||
|
*/
|
||||||
|
#define SHELL_SUBCMD_COND_ADD(_flag, _parent, _syntax, _subcmd, _help, _handler, \
|
||||||
|
_mand, _opt) \
|
||||||
|
COND_CODE_1(_flag, \
|
||||||
|
(static const struct shell_static_entry \
|
||||||
|
Z_SHELL_SUBCMD_NAME(__DEBRACKET _parent, _syntax)\
|
||||||
|
__attribute__ ((section("." \
|
||||||
|
STRINGIFY(Z_SHELL_SUBCMD_NAME(NUM_VA_ARGS_LESS_1 _parent, \
|
||||||
|
__DEBRACKET _parent, _syntax))))) \
|
||||||
|
__attribute__((used)) = \
|
||||||
|
SHELL_EXPR_CMD_ARG(1, _syntax, _subcmd, _help, \
|
||||||
|
_handler, _mand, _opt)\
|
||||||
|
), \
|
||||||
|
(static shell_cmd_handler dummy_##syntax##_handler __unused = _handler;\
|
||||||
|
static const union shell_cmd_entry dummy_subcmd_##syntax __unused = { \
|
||||||
|
.entry = (const struct shell_static_entry *)_subcmd\
|
||||||
|
} \
|
||||||
|
) \
|
||||||
|
)
|
||||||
|
|
||||||
|
/** @brief Add command to the set of subcommands.
|
||||||
|
*
|
||||||
|
* Add command to the set created with @ref SHELL_SUBCMD_SET_CREATE.
|
||||||
|
*
|
||||||
|
* @param[in] _parent Parent command sequence. Comma separated in parenthesis.
|
||||||
|
* @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.
|
||||||
|
* @param[in] _mand Number of mandatory arguments including command name.
|
||||||
|
* @param[in] _opt Number of optional arguments.
|
||||||
|
*/
|
||||||
|
#define SHELL_SUBCMD_ADD(_parent, _syntax, _subcmd, _help, _handler, _mand, _opt) \
|
||||||
|
SHELL_SUBCMD_COND_ADD(1, _parent, _syntax, _subcmd, _help, _handler, _mand, _opt)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Define ending subcommands set.
|
* @brief Define ending subcommands set.
|
||||||
*
|
*
|
||||||
|
@ -298,10 +379,12 @@ struct shell_static_entry {
|
||||||
* @param[in] name Name of the dynamic entry.
|
* @param[in] name Name of the dynamic entry.
|
||||||
* @param[in] get Pointer to the function returning dynamic commands array
|
* @param[in] get Pointer to the function returning dynamic commands array
|
||||||
*/
|
*/
|
||||||
#define SHELL_DYNAMIC_CMD_CREATE(name, get) \
|
#define SHELL_DYNAMIC_CMD_CREATE(name, get) \
|
||||||
static const struct shell_cmd_entry name = { \
|
static const union shell_cmd_entry name \
|
||||||
.is_dynamic = true, \
|
__attribute__ ((section("." \
|
||||||
.u = { .dynamic_get = get } \
|
STRINGIFY(UTIL_CAT(shell_dynamic_subcmd_, syntax))))) \
|
||||||
|
__attribute__((used)) = { \
|
||||||
|
.dynamic_get = get \
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -367,7 +450,7 @@ struct shell_static_entry {
|
||||||
{ \
|
{ \
|
||||||
.syntax = (_expr) ? (const char *)STRINGIFY(_syntax) : "", \
|
.syntax = (_expr) ? (const char *)STRINGIFY(_syntax) : "", \
|
||||||
.help = (_expr) ? (const char *)_help : NULL, \
|
.help = (_expr) ? (const char *)_help : NULL, \
|
||||||
.subcmd = (const struct shell_cmd_entry *)((_expr) ? \
|
.subcmd = (const union shell_cmd_entry *)((_expr) ? \
|
||||||
_subcmd : NULL), \
|
_subcmd : NULL), \
|
||||||
.handler = (shell_cmd_handler)((_expr) ? _handler : NULL), \
|
.handler = (shell_cmd_handler)((_expr) ? _handler : NULL), \
|
||||||
.args = { .mandatory = _mand, .optional = _opt} \
|
.args = { .mandatory = _mand, .optional = _opt} \
|
||||||
|
|
|
@ -9,14 +9,49 @@
|
||||||
#include "shell_utils.h"
|
#include "shell_utils.h"
|
||||||
#include "shell_wildcard.h"
|
#include "shell_wildcard.h"
|
||||||
|
|
||||||
extern const struct shell_cmd_entry __shell_root_cmds_start[];
|
extern const union shell_cmd_entry __shell_root_cmds_start[];
|
||||||
extern const struct shell_cmd_entry __shell_root_cmds_end[];
|
extern const union shell_cmd_entry __shell_root_cmds_end[];
|
||||||
|
|
||||||
static inline const struct shell_cmd_entry *shell_root_cmd_get(uint32_t id)
|
extern const union shell_cmd_entry __shell_dynamic_subcmds_start[];
|
||||||
|
extern const union shell_cmd_entry __shell_dynamic_subcmds_end[];
|
||||||
|
|
||||||
|
extern const union shell_cmd_entry __shell_subcmds_start[];
|
||||||
|
extern const union shell_cmd_entry __shell_subcmds_end[];
|
||||||
|
|
||||||
|
/* Macro creates empty entry at the bottom of the memory section with subcommands
|
||||||
|
* it is used to detect end of subcommand set that is located before this marker.
|
||||||
|
*/
|
||||||
|
#define Z_SHELL_SUBCMD_END_MARKER_CREATE() \
|
||||||
|
static const struct shell_static_entry z_shell_subcmd_end_marker\
|
||||||
|
__attribute__ ((section("." \
|
||||||
|
STRINGIFY(Z_SHELL_SUBCMD_NAME(999))))) \
|
||||||
|
__attribute__((used))
|
||||||
|
|
||||||
|
Z_SHELL_SUBCMD_END_MARKER_CREATE();
|
||||||
|
|
||||||
|
static inline const union shell_cmd_entry *shell_root_cmd_get(uint32_t id)
|
||||||
{
|
{
|
||||||
return &__shell_root_cmds_start[id];
|
return &__shell_root_cmds_start[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Determine if entry is a dynamic command by checking if address is within
|
||||||
|
* dynamic commands memory section.
|
||||||
|
*/
|
||||||
|
static inline bool is_dynamic_cmd(const union shell_cmd_entry *entry)
|
||||||
|
{
|
||||||
|
return (entry >= __shell_dynamic_subcmds_start) &&
|
||||||
|
(entry < __shell_dynamic_subcmds_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine if entry is a section command by checking if address is within
|
||||||
|
* subcommands memory section.
|
||||||
|
*/
|
||||||
|
static inline bool is_section_cmd(const union shell_cmd_entry *entry)
|
||||||
|
{
|
||||||
|
return (entry >= __shell_subcmds_start) &&
|
||||||
|
(entry < __shell_subcmds_end);
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculates relative line number of given position in buffer */
|
/* Calculates relative line number of given position in buffer */
|
||||||
static uint32_t line_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
|
static uint32_t line_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
|
||||||
uint16_t buffer_pos)
|
uint16_t buffer_pos)
|
||||||
|
@ -226,19 +261,19 @@ static inline uint32_t shell_root_cmd_count(void)
|
||||||
{
|
{
|
||||||
return ((uint8_t *)__shell_root_cmds_end -
|
return ((uint8_t *)__shell_root_cmds_end -
|
||||||
(uint8_t *)__shell_root_cmds_start)/
|
(uint8_t *)__shell_root_cmds_start)/
|
||||||
sizeof(struct shell_cmd_entry);
|
sizeof(union shell_cmd_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Function returning pointer to parent command matching requested syntax. */
|
/* Function returning pointer to parent command matching requested syntax. */
|
||||||
const struct shell_static_entry *root_cmd_find(const char *syntax)
|
const struct shell_static_entry *root_cmd_find(const char *syntax)
|
||||||
{
|
{
|
||||||
const size_t cmd_count = shell_root_cmd_count();
|
const size_t cmd_count = shell_root_cmd_count();
|
||||||
const struct shell_cmd_entry *cmd;
|
const union shell_cmd_entry *cmd;
|
||||||
|
|
||||||
for (size_t cmd_idx = 0; cmd_idx < cmd_count; ++cmd_idx) {
|
for (size_t cmd_idx = 0; cmd_idx < cmd_count; ++cmd_idx) {
|
||||||
cmd = shell_root_cmd_get(cmd_idx);
|
cmd = shell_root_cmd_get(cmd_idx);
|
||||||
if (strcmp(syntax, cmd->u.entry->syntax) == 0) {
|
if (strcmp(syntax, cmd->entry->syntax) == 0) {
|
||||||
return cmd->u.entry;
|
return cmd->entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,20 +289,32 @@ const struct shell_static_entry *z_shell_cmd_get(
|
||||||
|
|
||||||
if (parent == NULL) {
|
if (parent == NULL) {
|
||||||
return (idx < shell_root_cmd_count()) ?
|
return (idx < shell_root_cmd_count()) ?
|
||||||
shell_root_cmd_get(idx)->u.entry : NULL;
|
shell_root_cmd_get(idx)->entry : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
__ASSERT_NO_MSG(dloc != NULL);
|
__ASSERT_NO_MSG(dloc != NULL);
|
||||||
|
|
||||||
if (parent->subcmd) {
|
if (parent->subcmd) {
|
||||||
if (parent->subcmd->is_dynamic) {
|
if (is_dynamic_cmd(parent->subcmd)) {
|
||||||
parent->subcmd->u.dynamic_get(idx, dloc);
|
parent->subcmd->dynamic_get(idx, dloc);
|
||||||
if (dloc->syntax != NULL) {
|
if (dloc->syntax != NULL) {
|
||||||
res = dloc;
|
res = dloc;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (parent->subcmd->u.entry[idx].syntax != NULL) {
|
const struct shell_static_entry *entry_list;
|
||||||
res = &parent->subcmd->u.entry[idx];
|
|
||||||
|
if (is_section_cmd(parent->subcmd)) {
|
||||||
|
/* First element is null */
|
||||||
|
entry_list =
|
||||||
|
(const struct shell_static_entry *)parent->subcmd;
|
||||||
|
idx++;
|
||||||
|
} else {
|
||||||
|
entry_list = parent->subcmd->entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (entry_list[idx].syntax != NULL) {
|
||||||
|
res = &entry_list[idx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue