1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <ctype.h>
7 #include <device.h>
8 #include "shell_utils.h"
9 #include "shell_wildcard.h"
10
11 extern const struct shell_cmd_entry __shell_root_cmds_start[];
12 extern const struct shell_cmd_entry __shell_root_cmds_end[];
13
shell_root_cmd_get(uint32_t id)14 static inline const struct shell_cmd_entry *shell_root_cmd_get(uint32_t id)
15 {
16 return &__shell_root_cmds_start[id];
17 }
18
19 /* Calculates relative line number of given position in buffer */
line_num_with_buffer_offset_get(struct shell_multiline_cons * cons,uint16_t buffer_pos)20 static uint32_t line_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
21 uint16_t buffer_pos)
22 {
23 return ((buffer_pos + cons->name_len) / cons->terminal_wid);
24 }
25
26 /* Calculates column number of given position in buffer */
col_num_with_buffer_offset_get(struct shell_multiline_cons * cons,uint16_t buffer_pos)27 static uint32_t col_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
28 uint16_t buffer_pos)
29 {
30 /* columns are counted from 1 */
31 return (1 + ((buffer_pos + cons->name_len) % cons->terminal_wid));
32 }
33
z_column_span_with_buffer_offsets_get(struct shell_multiline_cons * cons,uint16_t offset1,uint16_t offset2)34 int32_t z_column_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
35 uint16_t offset1,
36 uint16_t offset2)
37 {
38 return col_num_with_buffer_offset_get(cons, offset2)
39 - col_num_with_buffer_offset_get(cons, offset1);
40 }
41
z_row_span_with_buffer_offsets_get(struct shell_multiline_cons * cons,uint16_t offset1,uint16_t offset2)42 int32_t z_row_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
43 uint16_t offset1,
44 uint16_t offset2)
45 {
46 return line_num_with_buffer_offset_get(cons, offset2)
47 - line_num_with_buffer_offset_get(cons, offset1);
48 }
49
z_shell_multiline_data_calc(struct shell_multiline_cons * cons,uint16_t buff_pos,uint16_t buff_len)50 void z_shell_multiline_data_calc(struct shell_multiline_cons *cons,
51 uint16_t buff_pos, uint16_t buff_len)
52 {
53 /* Current cursor position in command.
54 * +1 -> because home position is (1, 1)
55 */
56 cons->cur_x = (buff_pos + cons->name_len) % cons->terminal_wid + 1;
57 cons->cur_y = (buff_pos + cons->name_len) / cons->terminal_wid + 1;
58
59 /* Extreme position when cursor is at the end of command. */
60 cons->cur_y_end = (buff_len + cons->name_len) / cons->terminal_wid + 1;
61 cons->cur_x_end = (buff_len + cons->name_len) % cons->terminal_wid + 1;
62 }
63
make_argv(char ** ppcmd,uint8_t c)64 static char make_argv(char **ppcmd, uint8_t c)
65 {
66 char *cmd = *ppcmd;
67 char quote = 0;
68
69 while (1) {
70 c = *cmd;
71
72 if (c == '\0') {
73 break;
74 }
75
76 if (!quote) {
77 switch (c) {
78 case '\\':
79 memmove(cmd, cmd + 1,
80 z_shell_strlen(cmd));
81 cmd += 1;
82 continue;
83
84 case '\'':
85 case '\"':
86 memmove(cmd, cmd + 1,
87 z_shell_strlen(cmd));
88 quote = c;
89 continue;
90 default:
91 break;
92 }
93 }
94
95 if (quote == c) {
96 memmove(cmd, cmd + 1, z_shell_strlen(cmd));
97 quote = 0;
98 continue;
99 }
100
101 if (quote && c == '\\') {
102 char t = *(cmd + 1);
103
104 if (t == quote) {
105 memmove(cmd, cmd + 1,
106 z_shell_strlen(cmd));
107 cmd += 1;
108 continue;
109 }
110
111 if (t == '0') {
112 uint8_t i;
113 uint8_t v = 0U;
114
115 for (i = 2U; i < (2 + 3); i++) {
116 t = *(cmd + i);
117
118 if (t >= '0' && t <= '7') {
119 v = (v << 3) | (t - '0');
120 } else {
121 break;
122 }
123 }
124
125 if (i > 2) {
126 memmove(cmd, cmd + (i - 1),
127 z_shell_strlen(cmd) - (i - 2));
128 *cmd++ = v;
129 continue;
130 }
131 }
132
133 if (t == 'x') {
134 uint8_t i;
135 uint8_t v = 0U;
136
137 for (i = 2U; i < (2 + 2); i++) {
138 t = *(cmd + i);
139
140 if (t >= '0' && t <= '9') {
141 v = (v << 4) | (t - '0');
142 } else if ((t >= 'a') &&
143 (t <= 'f')) {
144 v = (v << 4) | (t - 'a' + 10);
145 } else if ((t >= 'A') && (t <= 'F')) {
146 v = (v << 4) | (t - 'A' + 10);
147 } else {
148 break;
149 }
150 }
151
152 if (i > 2) {
153 memmove(cmd, cmd + (i - 1),
154 z_shell_strlen(cmd) - (i - 2));
155 *cmd++ = v;
156 continue;
157 }
158 }
159 }
160
161 if (!quote && isspace((int) c)) {
162 break;
163 }
164
165 cmd += 1;
166 }
167 *ppcmd = cmd;
168
169 return quote;
170 }
171
172
z_shell_make_argv(size_t * argc,const char ** argv,char * cmd,uint8_t max_argc)173 char z_shell_make_argv(size_t *argc, const char **argv, char *cmd,
174 uint8_t max_argc)
175 {
176 char quote = 0;
177 char c;
178
179 *argc = 0;
180 do {
181 c = *cmd;
182 if (c == '\0') {
183 break;
184 }
185
186 if (isspace((int) c)) {
187 *cmd++ = '\0';
188 continue;
189 }
190
191 argv[(*argc)++] = cmd;
192 if (*argc == max_argc) {
193 break;
194 }
195 quote = make_argv(&cmd, c);
196 } while (true);
197
198 return quote;
199 }
200
z_shell_pattern_remove(char * buff,uint16_t * buff_len,const char * pattern)201 void z_shell_pattern_remove(char *buff, uint16_t *buff_len, const char *pattern)
202 {
203 char *pattern_addr = strstr(buff, pattern);
204 uint16_t shift;
205 uint16_t pattern_len = z_shell_strlen(pattern);
206
207 if (!pattern_addr) {
208 return;
209 }
210
211 if (pattern_addr > buff) {
212 if (*(pattern_addr - 1) == ' ') {
213 pattern_len++; /* space needs to be removed as well */
214 pattern_addr--; /* set pointer to space */
215 }
216 }
217
218 shift = z_shell_strlen(pattern_addr) - pattern_len + 1; /* +1 for EOS */
219 *buff_len -= pattern_len;
220
221 memmove(pattern_addr, pattern_addr + pattern_len, shift);
222 }
223
shell_root_cmd_count(void)224 static inline uint32_t shell_root_cmd_count(void)
225 {
226 return ((uint8_t *)__shell_root_cmds_end -
227 (uint8_t *)__shell_root_cmds_start)/
228 sizeof(struct shell_cmd_entry);
229 }
230
231 /* Function returning pointer to parent command matching requested syntax. */
root_cmd_find(const char * syntax)232 const struct shell_static_entry *root_cmd_find(const char *syntax)
233 {
234 const size_t cmd_count = shell_root_cmd_count();
235 const struct shell_cmd_entry *cmd;
236
237 for (size_t cmd_idx = 0; cmd_idx < cmd_count; ++cmd_idx) {
238 cmd = shell_root_cmd_get(cmd_idx);
239 if (strcmp(syntax, cmd->u.entry->syntax) == 0) {
240 return cmd->u.entry;
241 }
242 }
243
244 return NULL;
245 }
246
z_shell_cmd_get(const struct shell_static_entry * parent,size_t idx,struct shell_static_entry * dloc)247 const struct shell_static_entry *z_shell_cmd_get(
248 const struct shell_static_entry *parent,
249 size_t idx,
250 struct shell_static_entry *dloc)
251 {
252 const struct shell_static_entry *res = NULL;
253
254 if (parent == NULL) {
255 return (idx < shell_root_cmd_count()) ?
256 shell_root_cmd_get(idx)->u.entry : NULL;
257 }
258
259 __ASSERT_NO_MSG(dloc != NULL);
260
261 if (parent->subcmd) {
262 if (parent->subcmd->is_dynamic) {
263 parent->subcmd->u.dynamic_get(idx, dloc);
264 if (dloc->syntax != NULL) {
265 res = dloc;
266 }
267 } else {
268 if (parent->subcmd->u.entry[idx].syntax != NULL) {
269 res = &parent->subcmd->u.entry[idx];
270 }
271 }
272 }
273
274 return res;
275 }
276
277 /* Function returns pointer to a command matching given pattern.
278 *
279 * @param cmd Pointer to commands array that will be searched.
280 * @param lvl Root command indicator. If set to 0 shell will search
281 * for pattern in parent commands.
282 * @param cmd_str Command pattern to be found.
283 * @param dloc Shell static command descriptor.
284 *
285 * @return Pointer to found command.
286 */
z_shell_find_cmd(const struct shell_static_entry * parent,const char * cmd_str,struct shell_static_entry * dloc)287 const struct shell_static_entry *z_shell_find_cmd(
288 const struct shell_static_entry *parent,
289 const char *cmd_str,
290 struct shell_static_entry *dloc)
291 {
292 const struct shell_static_entry *entry;
293 struct shell_static_entry parent_cpy;
294 size_t idx = 0;
295
296 /* Dynamic command operates on shared memory. If we are processing two
297 * dynamic commands at the same time (current and subcommand) they
298 * will operate on the same memory region what can cause undefined
299 * behaviour.
300 * Hence we need a separate memory for each of them.
301 */
302 if (parent) {
303 memcpy(&parent_cpy, parent, sizeof(struct shell_static_entry));
304 parent = &parent_cpy;
305 }
306
307 while ((entry = z_shell_cmd_get(parent, idx++, dloc)) != NULL) {
308 if (strcmp(cmd_str, entry->syntax) == 0) {
309 return entry;
310 }
311 }
312
313 return NULL;
314 }
315
z_shell_get_last_command(const struct shell_static_entry * entry,size_t argc,const char * argv[],size_t * match_arg,struct shell_static_entry * dloc,bool only_static)316 const struct shell_static_entry *z_shell_get_last_command(
317 const struct shell_static_entry *entry,
318 size_t argc,
319 const char *argv[],
320 size_t *match_arg,
321 struct shell_static_entry *dloc,
322 bool only_static)
323 {
324 const struct shell_static_entry *prev_entry = NULL;
325
326 *match_arg = Z_SHELL_CMD_ROOT_LVL;
327
328 while (*match_arg < argc) {
329
330 if (IS_ENABLED(CONFIG_SHELL_WILDCARD)) {
331 /* ignore wildcard argument */
332 if (z_shell_has_wildcard(argv[*match_arg])) {
333 (*match_arg)++;
334 continue;
335 }
336 }
337
338 prev_entry = entry;
339 entry = z_shell_find_cmd(entry, argv[*match_arg], dloc);
340 if (entry) {
341 (*match_arg)++;
342 } else {
343 entry = prev_entry;
344 break;
345 }
346
347 if (only_static && (entry == dloc)) {
348 (*match_arg)--;
349 return NULL;
350 }
351 }
352
353 return entry;
354 }
355
shell_set_root_cmd(const char * cmd)356 int shell_set_root_cmd(const char *cmd)
357 {
358 const struct shell_static_entry *entry;
359
360 entry = cmd ? root_cmd_find(cmd) : NULL;
361
362 if (cmd && (entry == NULL)) {
363 return -EINVAL;
364 }
365
366 STRUCT_SECTION_FOREACH(shell, sh) {
367 sh->ctx->selected_cmd = entry;
368 }
369
370 return 0;
371 }
372
373
374
375
z_shell_spaces_trim(char * str)376 void z_shell_spaces_trim(char *str)
377 {
378 uint16_t len = z_shell_strlen(str);
379 uint16_t shift = 0U;
380
381 if (!str) {
382 return;
383 }
384
385 for (uint16_t i = 0; i < len - 1; i++) {
386 if (isspace((int)str[i])) {
387 for (uint16_t j = i + 1; j < len; j++) {
388 if (isspace((int)str[j])) {
389 shift++;
390 continue;
391 }
392
393 if (shift > 0) {
394 /* +1 for EOS */
395 memmove(&str[i + 1],
396 &str[j],
397 len - j + 1);
398 len -= shift;
399 shift = 0U;
400 }
401
402 break;
403 }
404 }
405 }
406 }
407
408 /** @brief Remove white chars from beginning and end of command buffer.
409 *
410 */
buffer_trim(char * buff,uint16_t * buff_len)411 static void buffer_trim(char *buff, uint16_t *buff_len)
412 {
413 uint16_t i = 0U;
414
415 /* no command in the buffer */
416 if (buff[0] == '\0') {
417 return;
418 }
419
420 while (isspace((int) buff[*buff_len - 1U])) {
421 *buff_len -= 1U;
422 if (*buff_len == 0U) {
423 buff[0] = '\0';
424 return;
425 }
426 }
427 buff[*buff_len] = '\0';
428
429 /* Counting whitespace characters starting from beginning of the
430 * command.
431 */
432 while (isspace((int) buff[i++])) {
433 }
434
435
436 /* Removing counted whitespace characters. */
437 if (--i > 0) {
438 memmove(buff, buff + i, (*buff_len + 1U) - i); /* +1 for '\0' */
439 *buff_len = *buff_len - i;
440 }
441 }
442
z_shell_cmd_trim(const struct shell * shell)443 void z_shell_cmd_trim(const struct shell *shell)
444 {
445 buffer_trim(shell->ctx->cmd_buff, &shell->ctx->cmd_buff_len);
446 shell->ctx->cmd_buff_pos = shell->ctx->cmd_buff_len;
447 }
448
shell_device_lookup(size_t idx,const char * prefix)449 const struct device *shell_device_lookup(size_t idx,
450 const char *prefix)
451 {
452 size_t match_idx = 0;
453 const struct device *dev;
454 size_t len = z_device_get_all_static(&dev);
455 const struct device *dev_end = dev + len;
456
457 while (dev < dev_end) {
458 if (device_is_ready(dev)
459 && (dev->name != NULL)
460 && (strlen(dev->name) != 0)
461 && ((prefix == NULL)
462 || (strncmp(prefix, dev->name,
463 strlen(prefix)) == 0))) {
464 if (match_idx == idx) {
465 return dev;
466 }
467 ++match_idx;
468 }
469 ++dev;
470 }
471
472 return NULL;
473 }
474