Skip to content
Permalink
Browse files

shell: Add macros for creating conditional commands

Added macros which can be used to create a command which depends
on compilation flag. Macros are a cleaner alternative to #ifdefs
around command registration and command handler.

Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
  • Loading branch information...
nordic-krch authored and nashif committed Feb 4, 2019
1 parent e96673d commit 7e26f534709e3f79e37a8e00b086ea3d3733e7ef
Showing with 173 additions and 25 deletions.
  1. +12 −0 doc/reference/shell/index.rst
  2. +140 −20 include/shell/shell.h
  3. +21 −5 subsys/shell/shell.c
@@ -82,10 +82,22 @@ Use the following macros for adding shell commands:

* :c:macro:`SHELL_CMD_REGISTER` - Create root command. All root commands must
have different name.
* :c:macro:`SHELL_COND_CMD_REGISTER` - Conditionally (if compile time flag is
set) create root command. All root commands must have different name.
* :c:macro:`SHELL_CMD_ARG_REGISTER` - Create root command with arguments.
All root commands must have different name.
* :c:macro:`SHELL_COND_CMD_ARG_REGISTER` - Conditionally (if compile time flag
is set) create root command with arguments. All root commands must have
different name.
* :c:macro:`SHELL_CMD` - Initialize a command.
* :c:macro:`SHELL_COND_CMD` - Initialize a command if compile time flag is set.
* :c:macro:`SHELL_EXPR_CMD` - Initialize a command if compile time expression is
non-zero.
* :c:macro:`SHELL_CMD_ARG` - Initialize a command with arguments.
* :c:macro:`SHELL_COND_CMD_ARG` - Initialize a command with arguments if compile
time flag is set.
* :c:macro:`SHELL_EXPR_CMD_ARG` - Initialize a command with arguments if compile
time expression is non-zero.
* :c:macro:`SHELL_STATIC_SUBCMD_SET_CREATE` - Create a static subcommands
array.
* :c:macro:`SHELL_DYNAMIC_CMD_CREATE` - Create a dynamic subcommands array.
@@ -129,6 +129,41 @@ struct shell_static_entry {
.u = {.entry = &UTIL_CAT(_shell_, syntax)} \
}

/**
* @brief Macro for defining and adding a conditional root command (level 0)
* with required number of arguments.
*
* @see SHELL_CMD_ARG_REGISTER for details.
*
* Macro can be used to create a command which can be conditionally present.
* It is and alternative to \#ifdefs around command registration and command
* handler. If command is disabled handler and subcommands are removed from
* the application.
*
* @param[in] flag Compile time flag. Command is present only if flag
* exists and equals 1.
* @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] mandatory Number of mandatory arguments.
* @param[in] optional Number of optional arguments.
*/
#define SHELL_COND_CMD_ARG_REGISTER(flag, syntax, subcmd, help, handler, \
mandatory, optional) \
COND_CODE_1(\
flag, \
(\
SHELL_CMD_ARG_REGISTER(syntax, subcmd, help, handler, \
mandatory, optional) \
), \
(\
static shell_cmd_handler dummy_##syntax##handler \
__attribute__((unused)) = handler;\
static const struct shell_cmd_entry *dummy_subcmd_##syntax \
__attribute__((unused)) = subcmd\
)\
)
/**
* @brief Macro for defining and adding a root command (level 0) with
* arguments.
@@ -141,15 +176,23 @@ struct shell_static_entry {
* @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); \
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) } \
}
SHELL_CMD_ARG_REGISTER(syntax, subcmd, help, handler, 0, 0)

/**
* @brief Macro for defining and adding a conditional root command (level 0)
* with arguments.
*
* @see SHELL_COND_CMD_ARG_REGISTER.
*
* @param[in] flag Compile time flag. Command is present only if flag
* exists and equals 1.
* @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_COND_CMD_REGISTER(flag, syntax, subcmd, help, handler) \
SHELL_COND_CMD_ARG_REGISTER(flag, syntax, subcmd, help, handler, 0, 0)

/**
* @brief Macro for creating a subcommand set. It must be used outside of any
@@ -226,21 +269,68 @@ struct shell_static_entry {
* @note If a command will be called with wrong number of arguments shell will
* print an error message and command handler will not be called.
*
* @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.
* @param[in] opt Number of optional arguments.
*/
#define SHELL_CMD_ARG(syntax, subcmd, help, handler, mand, opt) \
SHELL_EXPR_CMD_ARG(1, syntax, subcmd, help, handler, mand, opt)

/**
* @brief Initializes a conditional shell command with arguments.
*
* @see SHELL_CMD_ARG. Based on the flag, creates a valid entry or an empty
* command which is ignored by the shell. It is an alternative to \#ifdefs
* around command registration and command handler. However, empty structure is
* present in the flash even if command is disabled (subcommands and handler are
* removed). Macro internally handles case if flag is not defined so flag must
* be provided without any wrapper, e.g.: SHELL_COND_CMD_ARG(CONFIG_FOO, ...)
*
* @param[in] flag Compile time flag. Command is present only if flag
* exists and equals 1.
* @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.
* @param[in] opt Number of optional arguments.
*/
#define SHELL_COND_CMD_ARG(flag, syntax, subcmd, help, handler, mand, opt) \
SHELL_EXPR_CMD_ARG(IS_ENABLED(flag), syntax, subcmd, help, \
handler, mand, opt)

/**
* @brief Initializes a conditional shell command with arguments if expression
* gives non-zero result at compile time.
*
* @see SHELL_CMD_ARG. Based on the expression, creates a valid entry or an
* empty command which is ignored by the shell. It should be used instead of
* @ref SHELL_COND_CMD_ARG if condition is not a single configuration flag,
* e.g.:
* SHELL_EXPR_CMD_ARG(IS_ENABLED(CONFIG_FOO) &&
* IS_ENABLED(CONFIG_FOO_SETTING_1), ...)
*
* @param[in] _expr Expression.
* @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] _mandatory Number of mandatory arguments.
* @param[in] _optional Number of optional arguments.
*/
#define SHELL_CMD_ARG(_syntax, _subcmd, _help, _handler, \
_mandatory, _optional) { \
.syntax = (const char *)STRINGIFY(_syntax), \
.help = (const char *)_help, \
.subcmd = _subcmd, \
.handler = _handler, \
.args = {. mandatory = _mandatory, .optional = _optional} \
}
* @param[in] _mand Number of mandatory arguments.
* @param[in] _opt Number of optional arguments.
*/
#define SHELL_EXPR_CMD_ARG(_expr, _syntax, _subcmd, _help, _handler, \
_mand, _opt) \
{ \
.syntax = (_expr) ? (const char *)STRINGIFY(_syntax) : "", \
.help = (_expr) ? (const char *)_help : NULL, \
.subcmd = (const struct shell_cmd_entry *)((_expr) ? \
_subcmd : NULL), \
.handler = (shell_cmd_handler)((_expr) ? _handler : NULL), \
.args = { .mandatory = _mand, .optional = _opt} \
}

/**
* @brief Initializes a shell command.
@@ -253,6 +343,36 @@ struct shell_static_entry {
#define SHELL_CMD(_syntax, _subcmd, _help, _handler) \
SHELL_CMD_ARG(_syntax, _subcmd, _help, _handler, 0, 0)

/**
* @brief Initializes a conditional shell command.
*
* @see SHELL_COND_CMD_ARG.
*
* @param[in] _flag Compile time flag. Command is present only if flag
* exists and equals 1.
* @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_COND_CMD(_flag, _syntax, _subcmd, _help, _handler) \
SHELL_COND_CMD_ARG(_flag, _syntax, _subcmd, _help, _handler, 0, 0)

/**
* @brief Initializes shell command if expression gives non-zero result at
* compile time.
*
* @see SHELL_EXPR_CMD_ARG.
*
* @param[in] _expr Compile time expression. Command is present only if
* expression is non-zero.
* @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_EXPR_CMD(_expr, _syntax, _subcmd, _help, _handler) \
SHELL_EXPR_CMD_ARG(_expr, _syntax, _subcmd, _help, _handler, 0, 0)

/**
* @internal @brief Internal shell state in response to data received from the
@@ -321,6 +321,15 @@ static bool tab_prepare(const struct shell *shell,
return true;
}

/* Empty command is identified by null handler and subcommand but contrary
* to array termination null command, it has non-null syntax address.
*/
static inline bool is_empty_cmd(const struct shell_static_entry *entry)
{
return entry->syntax &&
(entry->handler == NULL) && (entry->subcmd == NULL);
}

static inline bool is_completion_candidate(const char *candidate,
const char *str, size_t len)
{
@@ -342,15 +351,20 @@ static void find_completion_candidates(const struct shell_static_entry *cmd,
*cnt = 0;

while (true) {
bool is_empty;
bool is_candidate;

shell_cmd_get(cmd ? cmd->subcmd : NULL, cmd ? 1 : 0,
idx, &candidate, &dynamic_entry);

if (!candidate) {
break;
}

if (is_completion_candidate(candidate->syntax, incompl_cmd,
incompl_cmd_len)) {
is_empty = is_empty_cmd(candidate);
is_candidate = is_completion_candidate(candidate->syntax,
incompl_cmd, incompl_cmd_len);
if (!is_empty && is_candidate) {
size_t slen = strlen(candidate->syntax);

*longest = (slen > *longest) ? slen : *longest;
@@ -438,15 +452,17 @@ static void tab_options_print(const struct shell *shell,
tab_item_print(shell, SHELL_INIT_OPTION_PRINTER, longest);

while (cnt) {
bool is_empty;

/* shell->ctx->active_cmd can be safely used outside of command
* context to save stack
*/
shell_cmd_get(cmd ? cmd->subcmd : NULL, cmd ? 1 : 0,
idx, &match, &shell->ctx->active_cmd);
idx++;

if (str && match->syntax &&
!is_completion_candidate(match->syntax, str, str_len)) {
is_empty = is_empty_cmd(match);
if (is_empty || (str && match->syntax &&
!is_completion_candidate(match->syntax, str, str_len))) {
continue;
}

0 comments on commit 7e26f53

Please sign in to comment.
You can’t perform that action at this time.