1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <ctype.h>
7 #include <zephyr/device.h>
8 #include <zephyr/sys/iterable_sections.h>
9 #include <stdlib.h>
10 #include "shell_utils.h"
11 #include "shell_wildcard.h"
12
13 TYPE_SECTION_START_EXTERN(union shell_cmd_entry, shell_dynamic_subcmds);
14 TYPE_SECTION_END_EXTERN(union shell_cmd_entry, shell_dynamic_subcmds);
15
16 TYPE_SECTION_START_EXTERN(union shell_cmd_entry, shell_subcmds);
17 TYPE_SECTION_END_EXTERN(union shell_cmd_entry, shell_subcmds);
18
19 /* Macro creates empty entry at the bottom of the memory section with subcommands
20 * it is used to detect end of subcommand set that is located before this marker.
21 */
22 #define Z_SHELL_SUBCMD_END_MARKER_CREATE() \
23 static const TYPE_SECTION_ITERABLE(struct shell_static_entry, \
24 z_shell_subcmd_end_marker, shell_subcmds, Z_SHELL_UNDERSCORE(999))
25
26 Z_SHELL_SUBCMD_END_MARKER_CREATE();
27
shell_root_cmd_get(uint32_t id)28 static inline const union shell_cmd_entry *shell_root_cmd_get(uint32_t id)
29 {
30 const union shell_cmd_entry *cmd;
31
32 TYPE_SECTION_GET(union shell_cmd_entry, shell_root_cmds, id, &cmd);
33
34 return cmd;
35 }
36
37 /* Determine if entry is a dynamic command by checking if address is within
38 * dynamic commands memory section.
39 */
is_dynamic_cmd(const union shell_cmd_entry * entry)40 static inline bool is_dynamic_cmd(const union shell_cmd_entry *entry)
41 {
42 return (entry >= TYPE_SECTION_START(shell_dynamic_subcmds)) &&
43 (entry < TYPE_SECTION_END(shell_dynamic_subcmds));
44 }
45
46 /* Determine if entry is a section command by checking if address is within
47 * subcommands memory section.
48 */
is_section_cmd(const union shell_cmd_entry * entry)49 static inline bool is_section_cmd(const union shell_cmd_entry *entry)
50 {
51 return (entry >= TYPE_SECTION_START(shell_subcmds)) &&
52 (entry < TYPE_SECTION_END(shell_subcmds));
53 }
54
55 /* Calculates relative line number of given position in buffer */
line_num_with_buffer_offset_get(struct shell_multiline_cons * cons,uint16_t buffer_pos)56 static uint32_t line_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
57 uint16_t buffer_pos)
58 {
59 return ((buffer_pos + cons->name_len) / cons->terminal_wid);
60 }
61
62 /* Calculates column number of given position in buffer */
col_num_with_buffer_offset_get(struct shell_multiline_cons * cons,uint16_t buffer_pos)63 static uint32_t col_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
64 uint16_t buffer_pos)
65 {
66 /* columns are counted from 1 */
67 return (1 + ((buffer_pos + cons->name_len) % cons->terminal_wid));
68 }
69
z_column_span_with_buffer_offsets_get(struct shell_multiline_cons * cons,uint16_t offset1,uint16_t offset2)70 int32_t z_column_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
71 uint16_t offset1,
72 uint16_t offset2)
73 {
74 return col_num_with_buffer_offset_get(cons, offset2)
75 - col_num_with_buffer_offset_get(cons, offset1);
76 }
77
z_row_span_with_buffer_offsets_get(struct shell_multiline_cons * cons,uint16_t offset1,uint16_t offset2)78 int32_t z_row_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
79 uint16_t offset1,
80 uint16_t offset2)
81 {
82 return line_num_with_buffer_offset_get(cons, offset2)
83 - line_num_with_buffer_offset_get(cons, offset1);
84 }
85
z_shell_multiline_data_calc(struct shell_multiline_cons * cons,uint16_t buff_pos,uint16_t buff_len)86 void z_shell_multiline_data_calc(struct shell_multiline_cons *cons,
87 uint16_t buff_pos, uint16_t buff_len)
88 {
89 /* Current cursor position in command.
90 * +1 -> because home position is (1, 1)
91 */
92 cons->cur_x = (buff_pos + cons->name_len) % cons->terminal_wid + 1;
93 cons->cur_y = (buff_pos + cons->name_len) / cons->terminal_wid + 1;
94
95 /* Extreme position when cursor is at the end of command. */
96 cons->cur_y_end = (buff_len + cons->name_len) / cons->terminal_wid + 1;
97 cons->cur_x_end = (buff_len + cons->name_len) % cons->terminal_wid + 1;
98 }
99
make_argv(char ** ppcmd,uint8_t c)100 static char make_argv(char **ppcmd, uint8_t c)
101 {
102 char *cmd = *ppcmd;
103 char quote = 0;
104
105 while (1) {
106 c = *cmd;
107
108 if (c == '\0') {
109 break;
110 }
111
112 if (!quote) {
113 switch (c) {
114 case '\\':
115 memmove(cmd, cmd + 1,
116 z_shell_strlen(cmd));
117 cmd += 1;
118 continue;
119
120 case '\'':
121 case '\"':
122 memmove(cmd, cmd + 1,
123 z_shell_strlen(cmd));
124 quote = c;
125 continue;
126 default:
127 break;
128 }
129 }
130
131 if (quote == c) {
132 memmove(cmd, cmd + 1, z_shell_strlen(cmd));
133 quote = 0;
134 continue;
135 }
136
137 if (quote && c == '\\') {
138 char t = *(cmd + 1);
139
140 if (t == quote) {
141 memmove(cmd, cmd + 1,
142 z_shell_strlen(cmd));
143 cmd += 1;
144 continue;
145 }
146
147 if (t == '0') {
148 uint8_t i;
149 uint8_t v = 0U;
150
151 for (i = 2U; i < (2 + 3); i++) {
152 t = *(cmd + i);
153
154 if (t >= '0' && t <= '7') {
155 v = (v << 3) | (t - '0');
156 } else {
157 break;
158 }
159 }
160
161 if (i > 2) {
162 memmove(cmd, cmd + (i - 1),
163 z_shell_strlen(cmd) - (i - 2));
164 *cmd++ = v;
165 continue;
166 }
167 }
168
169 if (t == 'x') {
170 uint8_t i;
171 uint8_t v = 0U;
172
173 for (i = 2U; i < (2 + 2); i++) {
174 t = *(cmd + i);
175
176 if (t >= '0' && t <= '9') {
177 v = (v << 4) | (t - '0');
178 } else if ((t >= 'a') &&
179 (t <= 'f')) {
180 v = (v << 4) | (t - 'a' + 10);
181 } else if ((t >= 'A') && (t <= 'F')) {
182 v = (v << 4) | (t - 'A' + 10);
183 } else {
184 break;
185 }
186 }
187
188 if (i > 2) {
189 memmove(cmd, cmd + (i - 1),
190 z_shell_strlen(cmd) - (i - 2));
191 *cmd++ = v;
192 continue;
193 }
194 }
195 }
196
197 if (!quote && isspace((int) c) != 0) {
198 break;
199 }
200
201 cmd += 1;
202 }
203 *ppcmd = cmd;
204
205 return quote;
206 }
207
208
z_shell_make_argv(size_t * argc,const char ** argv,char * cmd,uint8_t max_argc)209 char z_shell_make_argv(size_t *argc, const char **argv, char *cmd,
210 uint8_t max_argc)
211 {
212 char quote = 0;
213 char c;
214
215 *argc = 0;
216 do {
217 c = *cmd;
218 if (c == '\0') {
219 break;
220 }
221
222 if (isspace((int) c) != 0) {
223 *cmd++ = '\0';
224 continue;
225 }
226
227 argv[(*argc)++] = cmd;
228 if (*argc == max_argc) {
229 break;
230 }
231 quote = make_argv(&cmd, c);
232 } while (true);
233
234 return quote;
235 }
236
z_shell_pattern_remove(char * buff,uint16_t * buff_len,const char * pattern)237 void z_shell_pattern_remove(char *buff, uint16_t *buff_len, const char *pattern)
238 {
239 char *pattern_addr = strstr(buff, pattern);
240 uint16_t shift;
241 uint16_t pattern_len = z_shell_strlen(pattern);
242
243 if (!pattern_addr) {
244 return;
245 }
246
247 if (pattern_addr > buff) {
248 if (*(pattern_addr - 1) == ' ') {
249 pattern_len++; /* space needs to be removed as well */
250 pattern_addr--; /* set pointer to space */
251 }
252 }
253
254 shift = z_shell_strlen(pattern_addr) - pattern_len + 1; /* +1 for EOS */
255 *buff_len -= pattern_len;
256
257 memmove(pattern_addr, pattern_addr + pattern_len, shift);
258 }
259
shell_root_cmd_count(void)260 static inline uint32_t shell_root_cmd_count(void)
261 {
262 size_t len;
263
264 TYPE_SECTION_COUNT(union shell_cmd_entry, shell_root_cmds, &len);
265
266 return len;
267 }
268
269 /* Function returning pointer to parent command matching requested syntax. */
root_cmd_find(const char * syntax)270 const struct shell_static_entry *root_cmd_find(const char *syntax)
271 {
272 const size_t cmd_count = shell_root_cmd_count();
273 const union shell_cmd_entry *cmd;
274
275 for (size_t cmd_idx = 0; cmd_idx < cmd_count; ++cmd_idx) {
276 cmd = shell_root_cmd_get(cmd_idx);
277 if (strcmp(syntax, cmd->entry->syntax) == 0) {
278 return cmd->entry;
279 }
280 }
281
282 return NULL;
283 }
284
z_shell_cmd_get(const struct shell_static_entry * parent,size_t idx,struct shell_static_entry * dloc)285 const struct shell_static_entry *z_shell_cmd_get(
286 const struct shell_static_entry *parent,
287 size_t idx,
288 struct shell_static_entry *dloc)
289 {
290 const struct shell_static_entry *res = NULL;
291
292 if (parent == NULL) {
293 return (idx < shell_root_cmd_count()) ?
294 shell_root_cmd_get(idx)->entry : NULL;
295 }
296
297 __ASSERT_NO_MSG(dloc != NULL);
298
299 if (parent->subcmd) {
300 if (is_dynamic_cmd(parent->subcmd)) {
301 parent->subcmd->dynamic_get(idx, dloc);
302 if (dloc->syntax != NULL) {
303 res = dloc;
304 }
305 } else {
306 const struct shell_static_entry *entry_list;
307
308 if (is_section_cmd(parent->subcmd)) {
309 /* First element is null */
310 entry_list =
311 (const struct shell_static_entry *)parent->subcmd;
312 idx++;
313 } else {
314 entry_list = parent->subcmd->entry;
315 }
316
317
318 if (entry_list[idx].syntax != NULL) {
319 res = &entry_list[idx];
320 }
321 }
322 }
323
324 return res;
325 }
326
327 /* Function returns pointer to a command matching given pattern.
328 *
329 * @param cmd Pointer to commands array that will be searched.
330 * @param lvl Root command indicator. If set to 0 shell will search
331 * for pattern in parent commands.
332 * @param cmd_str Command pattern to be found.
333 * @param dloc Shell static command descriptor.
334 *
335 * @return Pointer to found command.
336 */
z_shell_find_cmd(const struct shell_static_entry * parent,const char * cmd_str,struct shell_static_entry * dloc)337 const struct shell_static_entry *z_shell_find_cmd(
338 const struct shell_static_entry *parent,
339 const char *cmd_str,
340 struct shell_static_entry *dloc)
341 {
342 const struct shell_static_entry *entry;
343 struct shell_static_entry parent_cpy;
344 size_t idx = 0;
345
346 /* Dynamic command operates on shared memory. If we are processing two
347 * dynamic commands at the same time (current and subcommand) they
348 * will operate on the same memory region what can cause undefined
349 * behaviour.
350 * Hence we need a separate memory for each of them.
351 */
352 if (parent) {
353 memcpy(&parent_cpy, parent, sizeof(struct shell_static_entry));
354 parent = &parent_cpy;
355 }
356
357 while ((entry = z_shell_cmd_get(parent, idx++, dloc)) != NULL) {
358 if (strcmp(cmd_str, entry->syntax) == 0) {
359 return entry;
360 }
361 }
362
363 return NULL;
364 }
365
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)366 const struct shell_static_entry *z_shell_get_last_command(
367 const struct shell_static_entry *entry,
368 size_t argc,
369 const char *argv[],
370 size_t *match_arg,
371 struct shell_static_entry *dloc,
372 bool only_static)
373 {
374 const struct shell_static_entry *prev_entry = NULL;
375
376 *match_arg = Z_SHELL_CMD_ROOT_LVL;
377
378 while (*match_arg < argc) {
379
380 if (IS_ENABLED(CONFIG_SHELL_WILDCARD)) {
381 /* ignore wildcard argument */
382 if (z_shell_has_wildcard(argv[*match_arg])) {
383 (*match_arg)++;
384 continue;
385 }
386 }
387
388 prev_entry = entry;
389 entry = z_shell_find_cmd(entry, argv[*match_arg], dloc);
390 if (entry) {
391 (*match_arg)++;
392 } else {
393 entry = prev_entry;
394 break;
395 }
396
397 if (only_static && (entry == dloc)) {
398 (*match_arg)--;
399 return NULL;
400 }
401 }
402
403 return entry;
404 }
405
shell_set_root_cmd(const char * cmd)406 int shell_set_root_cmd(const char *cmd)
407 {
408 const struct shell_static_entry *entry;
409
410 entry = cmd ? root_cmd_find(cmd) : NULL;
411
412 if (cmd && (entry == NULL)) {
413 return -EINVAL;
414 }
415
416 STRUCT_SECTION_FOREACH(shell, sh) {
417 sh->ctx->selected_cmd = entry;
418 }
419
420 return 0;
421 }
422
423
424
425
z_shell_spaces_trim(char * str)426 void z_shell_spaces_trim(char *str)
427 {
428 uint16_t len = z_shell_strlen(str);
429 uint16_t shift = 0U;
430
431 if (len == 0U) {
432 return;
433 }
434
435 for (uint16_t i = 0; i < len - 1; i++) {
436 if (isspace((int)str[i]) != 0) {
437 for (uint16_t j = i + 1; j < len; j++) {
438 if (isspace((int)str[j]) != 0) {
439 shift++;
440 continue;
441 }
442
443 if (shift > 0) {
444 /* +1 for EOS */
445 memmove(&str[i + 1],
446 &str[j],
447 len - j + 1);
448 len -= shift;
449 shift = 0U;
450 }
451
452 break;
453 }
454 }
455 }
456 }
457
458 /** @brief Remove white chars from beginning and end of command buffer.
459 *
460 */
buffer_trim(char * buff,uint16_t * buff_len)461 static void buffer_trim(char *buff, uint16_t *buff_len)
462 {
463 uint16_t i = 0U;
464
465 /* no command in the buffer */
466 if (buff[0] == '\0') {
467 return;
468 }
469
470 while (isspace((int) buff[*buff_len - 1U]) != 0) {
471 *buff_len -= 1U;
472 if (*buff_len == 0U) {
473 buff[0] = '\0';
474 return;
475 }
476 }
477 buff[*buff_len] = '\0';
478
479 /* Counting whitespace characters starting from beginning of the
480 * command.
481 */
482 while (isspace((int) buff[i++]) != 0) {
483 }
484
485
486 /* Removing counted whitespace characters. */
487 if (--i > 0) {
488 memmove(buff, buff + i, (*buff_len + 1U) - i); /* +1 for '\0' */
489 *buff_len = *buff_len - i;
490 }
491 }
492
z_shell_cmd_trim(const struct shell * sh)493 void z_shell_cmd_trim(const struct shell *sh)
494 {
495 buffer_trim(sh->ctx->cmd_buff, &sh->ctx->cmd_buff_len);
496 sh->ctx->cmd_buff_pos = sh->ctx->cmd_buff_len;
497 }
498
shell_device_lookup(size_t idx,const char * prefix)499 const struct device *shell_device_lookup(size_t idx,
500 const char *prefix)
501 {
502 size_t match_idx = 0;
503 const struct device *dev;
504 size_t len = z_device_get_all_static(&dev);
505 const struct device *dev_end = dev + len;
506
507 while (dev < dev_end) {
508 if (device_is_ready(dev)
509 && (dev->name != NULL)
510 && (strlen(dev->name) != 0)
511 && ((prefix == NULL)
512 || (strncmp(prefix, dev->name,
513 strlen(prefix)) == 0))) {
514 if (match_idx == idx) {
515 return dev;
516 }
517 ++match_idx;
518 }
519 ++dev;
520 }
521
522 return NULL;
523 }
524
shell_strtol(const char * str,int base,int * err)525 long shell_strtol(const char *str, int base, int *err)
526 {
527 long val;
528 char *endptr = NULL;
529
530 errno = 0;
531 val = strtol(str, &endptr, base);
532 if (errno == ERANGE) {
533 *err = -ERANGE;
534 return 0;
535 } else if (errno || endptr == str || *endptr) {
536 *err = -EINVAL;
537 return 0;
538 }
539
540 return val;
541 }
542
shell_strtoul(const char * str,int base,int * err)543 unsigned long shell_strtoul(const char *str, int base, int *err)
544 {
545 unsigned long val;
546 char *endptr = NULL;
547
548 if (*str == '-') {
549 *err = -EINVAL;
550 return 0;
551 }
552
553 errno = 0;
554 val = strtoul(str, &endptr, base);
555 if (errno == ERANGE) {
556 *err = -ERANGE;
557 return 0;
558 } else if (errno || endptr == str || *endptr) {
559 *err = -EINVAL;
560 return 0;
561 }
562
563 return val;
564 }
565
shell_strtoull(const char * str,int base,int * err)566 unsigned long long shell_strtoull(const char *str, int base, int *err)
567 {
568 unsigned long long val;
569 char *endptr = NULL;
570
571 if (*str == '-') {
572 *err = -EINVAL;
573 return 0;
574 }
575
576 errno = 0;
577 val = strtoull(str, &endptr, base);
578 if (errno == ERANGE) {
579 *err = -ERANGE;
580 return 0;
581 } else if (errno || endptr == str || *endptr) {
582 *err = -EINVAL;
583 return 0;
584 }
585
586 return val;
587 }
588
shell_strtobool(const char * str,int base,int * err)589 bool shell_strtobool(const char *str, int base, int *err)
590 {
591 if (!strcmp(str, "on") || !strcmp(str, "enable") || !strcmp(str, "true")) {
592 return true;
593 }
594
595 if (!strcmp(str, "off") || !strcmp(str, "disable") || !strcmp(str, "false")) {
596 return false;
597 }
598
599 return shell_strtoul(str, base, err);
600 }
601