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