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