Skip to content

Commit 76d6cb7

Browse files
nordic-krchJakub Rzeszutko
authored and
Jakub Rzeszutko
committed
shell: Add wildcard support
Extended shell to support wildcard characters (*, ?) and expand commands accordingly. Sign-off-by: Jakub Rzeszutko <[email protected]>
1 parent de333d4 commit 76d6cb7

File tree

5 files changed

+321
-0
lines changed

5 files changed

+321
-0
lines changed

subsys/shell/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,8 @@ zephyr_sources_ifdef(
3333
CONFIG_LOG
3434
shell_log_backend.c
3535
)
36+
37+
zephyr_sources_ifdef(
38+
CONFIG_SHELL_WILDCARD
39+
shell_wildcard.c
40+
)

subsys/shell/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ config SHELL_ARGC_MAX
8080
If command is composed of more than defined, argument SHELL_ARGC_MAX
8181
and follwing are passed as one argument in the string.
8282

83+
config SHELL_WILDCARD
84+
bool "Enable wildcard support in shell"
85+
select FNMATCH
86+
help
87+
Enables using * in shell.
88+
8389
config SHELL_ECHO_STATUS
8490
bool "Enable echo on shell"
8591
default y

subsys/shell/shell.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <shell/shell.h>
1111
#include "shell_utils.h"
1212
#include "shell_ops.h"
13+
#include "shell_wildcard.h"
1314
#include "shell_vt100.h"
1415
#include <assert.h>
1516
#include <atomic.h>
@@ -385,6 +386,15 @@ static const struct shell_static_entry *get_last_command(
385386
*match_arg = SHELL_CMD_ROOT_LVL;
386387

387388
while (*match_arg < argc) {
389+
390+
if (IS_ENABLED(CONFIG_SHELL_WILDCARD)) {
391+
/* ignore wildcard argument */
392+
if (shell_wildcard_character_exist(argv[*match_arg])) {
393+
(*match_arg)++;
394+
continue;
395+
}
396+
}
397+
388398
entry = find_cmd(prev_cmd, *match_arg, argv[*match_arg],
389399
d_entry);
390400
if (entry) {
@@ -904,6 +914,7 @@ static void shell_execute(const struct shell *shell)
904914
struct shell_cmd_entry const *p_cmd = NULL;
905915
size_t cmd_lvl = SHELL_CMD_ROOT_LVL;
906916
size_t cmd_with_handler_lvl = 0;
917+
bool wildcard_found = false;
907918
size_t cmd_idx;
908919
size_t argc;
909920
char quote;
@@ -915,6 +926,10 @@ static void shell_execute(const struct shell *shell)
915926
history_put(shell, shell->ctx->cmd_buff,
916927
shell->ctx->cmd_buff_len);
917928

929+
if (IS_ENABLED(CONFIG_SHELL_WILDCARD)) {
930+
shell_wildcard_prepare(shell);
931+
}
932+
918933
shell_op_cursor_end_move(shell);
919934
cursor_next_line_move(shell);
920935

@@ -966,6 +981,27 @@ static void shell_execute(const struct shell *shell)
966981
break;
967982
}
968983

984+
if (IS_ENABLED(CONFIG_SHELL_WILDCARD)) {
985+
enum shell_wildcard_status status;
986+
status = shell_wildcard_process(shell, p_cmd,
987+
argv[cmd_lvl]);
988+
/* Wildcard character found but there is no matching
989+
* command.
990+
*/
991+
if (status == SHELL_WILDCARD_CMD_NO_MATCH_FOUND) {
992+
break;
993+
}
994+
995+
/* Wildcard character was not found function can process
996+
* argument.
997+
*/
998+
if (status != SHELL_WILDCARD_NOT_FOUND) {
999+
++cmd_lvl;
1000+
wildcard_found = true;
1001+
continue;
1002+
}
1003+
}
1004+
9691005
cmd_get(p_cmd, cmd_lvl, cmd_idx++, &p_static_entry, &d_entry);
9701006

9711007
if ((cmd_idx == 0) || (p_static_entry == NULL)) {
@@ -975,6 +1011,27 @@ static void shell_execute(const struct shell *shell)
9751011
if (strcmp(argv[cmd_lvl], p_static_entry->syntax) == 0) {
9761012
/* checking if command has a handler */
9771013
if (p_static_entry->handler != NULL) {
1014+
if (IS_ENABLED(CONFIG_SHELL_WILDCARD)) {
1015+
if (wildcard_found) {
1016+
shell_op_cursor_end_move(shell);
1017+
shell_op_cond_next_line(shell);
1018+
1019+
/* An error occurred, fnmatch
1020+
* argument cannot be followed
1021+
* by argument with a handler to
1022+
* avoid multiple function
1023+
* calls.
1024+
*/
1025+
shell_fprintf(shell,SHELL_ERROR,
1026+
"Error: requested "
1027+
"multiple function "
1028+
"executions\r\n");
1029+
flag_help_clear(shell);
1030+
1031+
return;
1032+
}
1033+
}
1034+
9781035
shell->ctx->active_cmd = *p_static_entry;
9791036
cmd_with_handler_lvl = cmd_lvl;
9801037
}
@@ -985,6 +1042,17 @@ static void shell_execute(const struct shell *shell)
9851042
}
9861043
}
9871044

1045+
if (IS_ENABLED(CONFIG_SHELL_WILDCARD)) {
1046+
shell_wildcard_finalize(shell);
1047+
/* cmd_buffer has been overwritten by function finalize function
1048+
* with all expanded commands. Hence shell_make_argv needs to
1049+
* be called again.
1050+
*/
1051+
(void)shell_make_argv(&argc, &argv[0],
1052+
shell->ctx->cmd_buff,
1053+
CONFIG_SHELL_ARGC_MAX);
1054+
}
1055+
9881056
/* Executing the deepest found handler. */
9891057
if (shell->ctx->active_cmd.handler == NULL) {
9901058
if (shell->ctx->active_cmd.help) {

subsys/shell/shell_wildcard.c

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/*
2+
* Copyright (c) 2018 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <string.h>
8+
#include <lib/fnmatch/fnmatch.h>
9+
#include "shell_wildcard.h"
10+
#include "shell_utils.h"
11+
12+
static void subcmd_get(const struct shell_cmd_entry *cmd,
13+
size_t idx, const struct shell_static_entry **entry,
14+
struct shell_static_entry *d_entry) {
15+
assert(entry != NULL);
16+
assert(st_entry != NULL);
17+
18+
if (cmd == NULL) {
19+
*entry = NULL;
20+
return;
21+
}
22+
23+
if (cmd->is_dynamic) {
24+
cmd->u.dynamic_get(idx, d_entry);
25+
*entry = (d_entry->syntax != NULL) ? d_entry : NULL;
26+
} else {
27+
*entry = (cmd->u.entry[idx].syntax != NULL) ?
28+
&cmd->u.entry[idx] : NULL;
29+
}
30+
}
31+
32+
static enum shell_wildcard_status command_add(char *buff, u16_t *buff_len,
33+
char const *cmd,
34+
char const *pattern)
35+
{
36+
u16_t cmd_len = shell_strlen(cmd);
37+
char *completion_addr;
38+
u16_t shift;
39+
40+
/* +1 for space */
41+
if ((*buff_len + cmd_len + 1) > CONFIG_SHELL_CMD_BUFF_SIZE) {
42+
return SHELL_WILDCARD_CMD_MISSING_SPACE;
43+
}
44+
45+
completion_addr = strstr(buff, pattern);
46+
47+
if (!completion_addr) {
48+
return SHELL_WILDCARD_CMD_NO_MATCH_FOUND;
49+
}
50+
51+
shift = shell_strlen(completion_addr);
52+
53+
/* make place for new command: + 1 for space + 1 for EOS */
54+
memmove(completion_addr + cmd_len + 1, completion_addr, shift + 1);
55+
memcpy(completion_addr, cmd, cmd_len);
56+
/* adding space to not brake next command in the buffer */
57+
completion_addr[cmd_len] = ' ';
58+
59+
*buff_len += cmd_len + 1; // + 1 for space
60+
61+
return SHELL_WILDCARD_CMD_ADDED;
62+
}
63+
64+
/**
65+
* @internal @brief Function for searching and adding commands to the temporary
66+
* shell buffer matching to wildcard pattern.
67+
*
68+
* This function is internal to shell module and shall be not called directly.
69+
*
70+
* @param[in/out] shell Pointer to the CLI instance.
71+
* @param[in] cmd Pointer to command which will be processed
72+
* @param[in] pattern Pointer to wildcard pattern.
73+
*
74+
* @retval WILDCARD_CMD_ADDED All matching commands added to the buffer.
75+
* @retval WILDCARD_CMD_ADDED_MISSING_SPACE Not all matching commands added
76+
* because CONFIG_SHELL_CMD_BUFF_SIZE
77+
* is too small.
78+
* @retval WILDCARD_CMD_NO_MATCH_FOUND No matching command found.
79+
*/
80+
static enum shell_wildcard_status commands_expand(const struct shell *shell,
81+
const struct shell_cmd_entry *cmd,
82+
const char *pattern)
83+
{
84+
enum shell_wildcard_status ret_val = SHELL_WILDCARD_CMD_NO_MATCH_FOUND;
85+
struct shell_static_entry const * p_static_entry = NULL;
86+
struct shell_static_entry static_entry;
87+
size_t cmd_idx = 0;
88+
size_t cnt = 0;
89+
90+
do {
91+
subcmd_get(cmd, cmd_idx++, &p_static_entry, &static_entry);
92+
93+
if (!p_static_entry) {
94+
break;
95+
}
96+
97+
if (0 == fnmatch(pattern, p_static_entry->syntax, 0)) {
98+
ret_val = command_add(shell->ctx->temp_buff,
99+
&shell->ctx->cmd_tmp_buff_len,
100+
p_static_entry->syntax, pattern);
101+
if (ret_val == SHELL_WILDCARD_CMD_MISSING_SPACE) {
102+
shell_fprintf(shell,
103+
SHELL_WARNING,
104+
"Command buffer is too short to"
105+
"expand all commands matching "
106+
"wildcard pattern: %s\r\n",
107+
pattern);
108+
break;
109+
} else if (ret_val != SHELL_WILDCARD_CMD_ADDED)
110+
{
111+
break;
112+
}
113+
cnt++;
114+
}
115+
} while(cmd_idx);
116+
117+
if (cnt > 0)
118+
{
119+
shell_pattern_remove(shell->ctx->temp_buff,
120+
&shell->ctx->cmd_tmp_buff_len, pattern);
121+
}
122+
123+
return ret_val;
124+
}
125+
126+
bool shell_wildcard_character_exist(const char *str)
127+
{
128+
size_t str_len = shell_strlen(str);
129+
130+
for (size_t i = 0; i < str_len; i++) {
131+
if ((str[i] == '?') || (str[i] == '*')) {
132+
return true;
133+
}
134+
}
135+
136+
return false;
137+
}
138+
139+
void shell_wildcard_prepare(const struct shell *shell)
140+
{
141+
/* Wildcard can be correctly handled under following conditions:
142+
* - wildcard command does not have a handler
143+
* - wildcard command is on the deepest commands level
144+
* - other commands on the same level as wildcard command shall also not
145+
* have a handler
146+
*
147+
* Algorithm:
148+
* 1. Command buffer is copied to Temp buffer.
149+
* 2. Algorithm goes through Command buffer to find handlers and
150+
* subcommands.
151+
* 3. If algorithm will find a wildcard character it switches to Temp
152+
* buffer.
153+
* 4. In the Temp buffer command with found wildcard character is
154+
* changed into matching command(s).
155+
* 5. Algorithm switch back to Command buffer and analyzes next command.
156+
* 6. When all arguments are analyzed from Command buffer, Temp buffer
157+
* is copied to Command buffer.
158+
* 7. Last found handler is executed with all arguments in the Command
159+
* buffer.
160+
*/
161+
162+
memset(shell->ctx->temp_buff, 0, sizeof(shell->ctx->temp_buff));
163+
memcpy(shell->ctx->temp_buff,
164+
shell->ctx->cmd_buff,
165+
shell->ctx->cmd_buff_len);
166+
167+
/* Function shell_spaces_trim must be used instead of shell_make_argv.
168+
* At this point it is important to keep temp_buff as a one string.
169+
* It will allow to find wildcard commands easily with strstr function.
170+
*/
171+
shell_spaces_trim(shell->ctx->temp_buff);
172+
173+
/* +1 for EOS*/
174+
shell->ctx->cmd_tmp_buff_len = shell_strlen(shell->ctx->temp_buff) + 1;
175+
}
176+
177+
178+
enum shell_wildcard_status shell_wildcard_process(const struct shell *shell,
179+
const struct shell_cmd_entry *cmd,
180+
const char *pattern)
181+
{
182+
enum shell_wildcard_status ret_val = SHELL_WILDCARD_NOT_FOUND;
183+
184+
if (cmd == NULL) {
185+
return ret_val;
186+
}
187+
188+
if (!shell_wildcard_character_exist(pattern)) {
189+
return ret_val;
190+
}
191+
192+
/* Function will search commands tree for commands matching wildcard
193+
* pattern stored in argv[cmd_lvl]. If match is found wildcard pattern
194+
* will be replaced by matching commands in temp_buffer. If there is no
195+
* space to add all matching commands function will add as many as
196+
* possible. Next it will continue to search for next wildcard pattern
197+
* and it will try to add matching commands.
198+
*/
199+
ret_val = commands_expand(shell, cmd, pattern);
200+
201+
return ret_val;
202+
}
203+
204+
void shell_wildcard_finalize(const struct shell *shell)
205+
{
206+
memcpy(shell->ctx->cmd_buff,
207+
shell->ctx->temp_buff,
208+
shell->ctx->cmd_tmp_buff_len);
209+
shell->ctx->cmd_buff_len = shell->ctx->cmd_tmp_buff_len;
210+
}

subsys/shell/shell_wildcard.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2018 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef SHELL_SHELL_WILDCARDS_H__
8+
#define SHELL_SHELL_WILDCARDS_H__
9+
10+
#include <shell/shell.h>
11+
12+
enum shell_wildcard_status {
13+
SHELL_WILDCARD_CMD_ADDED,
14+
SHELL_WILDCARD_CMD_MISSING_SPACE,
15+
SHELL_WILDCARD_CMD_NO_MATCH_FOUND, /* no matching command */
16+
SHELL_WILDCARD_NOT_FOUND /* wildcard character not found */
17+
};
18+
19+
20+
bool shell_wildcard_character_exist(const char *str);
21+
22+
void shell_wildcard_prepare(const struct shell *shell);
23+
24+
/* Function expands wildcards in the shell temporary buffer */
25+
enum shell_wildcard_status shell_wildcard_process(const struct shell *shell,
26+
const struct shell_cmd_entry *cmd,
27+
const char *pattern);
28+
29+
void shell_wildcard_finalize(const struct shell *shell);
30+
31+
32+
#endif /* SHELL_SHELL_WILDCARDS_H__ */

0 commit comments

Comments
 (0)