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_internal(size_t idx,const char * prefix,shell_device_filter_t filter)499 static const struct device *shell_device_internal(size_t idx,
500 						  const char *prefix,
501 						  shell_device_filter_t filter)
502 {
503 	size_t match_idx = 0;
504 	const struct device *dev;
505 	size_t len = z_device_get_all_static(&dev);
506 	const struct device *dev_end = dev + len;
507 
508 	while (dev < dev_end) {
509 		if (device_is_ready(dev)
510 		    && (dev->name != NULL)
511 		    && (strlen(dev->name) != 0)
512 		    && ((prefix == NULL)
513 			|| (strncmp(prefix, dev->name,
514 				    strlen(prefix)) == 0))
515 		    && (filter == NULL || filter(dev))) {
516 			if (match_idx == idx) {
517 				return dev;
518 			}
519 			++match_idx;
520 		}
521 		++dev;
522 	}
523 
524 	return NULL;
525 }
526 
shell_device_filter(size_t idx,shell_device_filter_t filter)527 const struct device *shell_device_filter(size_t idx,
528 					 shell_device_filter_t filter)
529 {
530 	return shell_device_internal(idx, NULL, filter);
531 }
532 
shell_device_lookup(size_t idx,const char * prefix)533 const struct device *shell_device_lookup(size_t idx,
534 					 const char *prefix)
535 {
536 	return shell_device_internal(idx, prefix, NULL);
537 }
538 
shell_strtol(const char * str,int base,int * err)539 long shell_strtol(const char *str, int base, int *err)
540 {
541 	long val;
542 	char *endptr = NULL;
543 
544 	errno = 0;
545 	val = strtol(str, &endptr, base);
546 	if (errno == ERANGE) {
547 		*err = -ERANGE;
548 		return 0;
549 	} else if (errno || endptr == str || *endptr) {
550 		*err = -EINVAL;
551 		return 0;
552 	}
553 
554 	return val;
555 }
556 
shell_strtoul(const char * str,int base,int * err)557 unsigned long shell_strtoul(const char *str, int base, int *err)
558 {
559 	unsigned long val;
560 	char *endptr = NULL;
561 
562 	if (*str == '-') {
563 		*err = -EINVAL;
564 		return 0;
565 	}
566 
567 	errno = 0;
568 	val = strtoul(str, &endptr, base);
569 	if (errno == ERANGE) {
570 		*err = -ERANGE;
571 		return 0;
572 	} else if (errno || endptr == str || *endptr) {
573 		*err = -EINVAL;
574 		return 0;
575 	}
576 
577 	return val;
578 }
579 
shell_strtoull(const char * str,int base,int * err)580 unsigned long long shell_strtoull(const char *str, int base, int *err)
581 {
582 	unsigned long long val;
583 	char *endptr = NULL;
584 
585 	if (*str == '-') {
586 		*err = -EINVAL;
587 		return 0;
588 	}
589 
590 	errno = 0;
591 	val = strtoull(str, &endptr, base);
592 	if (errno == ERANGE) {
593 		*err = -ERANGE;
594 		return 0;
595 	} else if (errno || endptr == str || *endptr) {
596 		*err = -EINVAL;
597 		return 0;
598 	}
599 
600 	return val;
601 }
602 
shell_strtobool(const char * str,int base,int * err)603 bool shell_strtobool(const char *str, int base, int *err)
604 {
605 	if (!strcmp(str, "on") || !strcmp(str, "enable") || !strcmp(str, "true")) {
606 		return true;
607 	}
608 
609 	if (!strcmp(str, "off") || !strcmp(str, "disable") || !strcmp(str, "false")) {
610 		return false;
611 	}
612 
613 	return shell_strtoul(str, base, err);
614 }
615