| /* | 
 |  * Copyright (c) 2018 Nordic Semiconductor ASA | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 |  | 
 | #include <string.h> | 
 | #include <fnmatch.h> | 
 | #include "shell_wildcard.h" | 
 | #include "shell_utils.h" | 
 | #include "shell_ops.h" | 
 |  | 
 | static enum shell_wildcard_status command_add(char *buff, uint16_t *buff_len, | 
 | 					      char const *cmd, | 
 | 					      char const *pattern) | 
 | { | 
 | 	uint16_t cmd_len = z_shell_strlen(cmd); | 
 | 	char *completion_addr; | 
 | 	uint16_t shift; | 
 |  | 
 | 	/* +1 for space */ | 
 | 	if ((*buff_len + cmd_len + 1) > CONFIG_SHELL_CMD_BUFF_SIZE) { | 
 | 		return SHELL_WILDCARD_CMD_MISSING_SPACE; | 
 | 	} | 
 |  | 
 | 	completion_addr = strstr(buff, pattern); | 
 |  | 
 | 	if (!completion_addr) { | 
 | 		return SHELL_WILDCARD_CMD_NO_MATCH_FOUND; | 
 | 	} | 
 |  | 
 | 	shift = z_shell_strlen(completion_addr); | 
 |  | 
 | 	/* make place for new command: + 1 for space + 1 for EOS */ | 
 | 	memmove(completion_addr + cmd_len + 1, completion_addr, shift + 1); | 
 | 	memcpy(completion_addr, cmd, cmd_len); | 
 | 	/* adding space to not brake next command in the buffer */ | 
 | 	completion_addr[cmd_len] = ' '; | 
 |  | 
 | 	*buff_len += cmd_len + 1; /* + 1 for space */ | 
 |  | 
 | 	return SHELL_WILDCARD_CMD_ADDED; | 
 | } | 
 |  | 
 | /** | 
 |  * @internal @brief Function for searching and adding commands to the temporary | 
 |  * shell buffer matching to wildcard pattern. | 
 |  * | 
 |  * Function will search commands tree for commands matching wildcard pattern | 
 |  * stored in argv[cmd_lvl]. When match is found wildcard pattern will be | 
 |  * replaced by matching commands. If there is no space in the buffer to add all | 
 |  * matching commands function will add as many as possible. Next it will | 
 |  * continue to search for next wildcard pattern and it will try to add matching | 
 |  * commands. | 
 |  * | 
 |  * | 
 |  * This function is internal to shell module and shall be not called directly. | 
 |  * | 
 |  * @param[in/out] shell		Pointer to the CLI instance. | 
 |  * @param[in]	  cmd		Pointer to command which will be processed | 
 |  * @param[in]	  pattern	Pointer to wildcard pattern. | 
 |  * | 
 |  * @retval WILDCARD_CMD_ADDED	All matching commands added to the buffer. | 
 |  * @retval WILDCARD_CMD_ADDED_MISSING_SPACE  Not all matching commands added | 
 |  *					     because CONFIG_SHELL_CMD_BUFF_SIZE | 
 |  *					     is too small. | 
 |  * @retval WILDCARD_CMD_NO_MATCH_FOUND No matching command found. | 
 |  */ | 
 | static enum shell_wildcard_status commands_expand(const struct shell *shell, | 
 | 					const struct shell_static_entry *cmd, | 
 | 					const char *pattern) | 
 | { | 
 | 	enum shell_wildcard_status ret_val = SHELL_WILDCARD_CMD_NO_MATCH_FOUND; | 
 | 	struct shell_static_entry const *entry = NULL; | 
 | 	struct shell_static_entry dloc; | 
 | 	size_t cmd_idx = 0; | 
 | 	size_t cnt = 0; | 
 |  | 
 | 	while ((entry = z_shell_cmd_get(cmd, cmd_idx++, &dloc)) != NULL) { | 
 |  | 
 | 		if (fnmatch(pattern, entry->syntax, 0) == 0) { | 
 | 			ret_val = command_add(shell->ctx->temp_buff, | 
 | 					      &shell->ctx->cmd_tmp_buff_len, | 
 | 					      entry->syntax, pattern); | 
 | 			if (ret_val == SHELL_WILDCARD_CMD_MISSING_SPACE) { | 
 | 				z_shell_fprintf(shell, SHELL_WARNING, | 
 | 					"Command buffer is too short to" | 
 | 					" expand all commands matching" | 
 | 					" wildcard pattern: %s\n", pattern); | 
 | 				break; | 
 | 			} else if (ret_val != SHELL_WILDCARD_CMD_ADDED) { | 
 | 				break; | 
 | 			} | 
 | 			cnt++; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (cnt > 0) { | 
 | 		z_shell_pattern_remove(shell->ctx->temp_buff, | 
 | 				       &shell->ctx->cmd_tmp_buff_len, pattern); | 
 | 	} | 
 |  | 
 | 	return ret_val; | 
 | } | 
 |  | 
 | bool z_shell_has_wildcard(const char *str) | 
 | { | 
 | 	uint16_t str_len = z_shell_strlen(str); | 
 |  | 
 | 	for (size_t i = 0; i < str_len; i++) { | 
 | 		if ((str[i] == '?') || (str[i] == '*')) { | 
 | 			return true; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return false; | 
 | } | 
 |  | 
 | void z_shell_wildcard_prepare(const struct shell *shell) | 
 | { | 
 | 	/* Wildcard can be correctly handled under following conditions: | 
 | 	 * - wildcard command does not have a handler | 
 | 	 * - wildcard command is on the deepest commands level | 
 | 	 * - other commands on the same level as wildcard command shall also not | 
 | 	 *   have a handler | 
 | 	 * | 
 | 	 * Algorithm: | 
 | 	 * 1. Command buffer: ctx->cmd_buff is copied to temporary buffer: | 
 | 	 *    ctx->temp_buff. | 
 | 	 * 2. Algorithm goes through command buffer to find handlers and | 
 | 	 *    subcommands. | 
 | 	 * 3. If algorithm will find a wildcard character it switches to | 
 | 	 *    Temporary buffer. | 
 | 	 * 4. In the Temporary buffer command containing wildcard character is | 
 | 	 *    replaced by matching command(s). | 
 | 	 * 5. Algorithm switch back to Command buffer and analyzes next command. | 
 | 	 * 6. When all arguments are analyzed from Command buffer, Temporary | 
 | 	 *    buffer with all expanded commands is copied to Command buffer. | 
 | 	 * 7. Deepest found handler is executed and all lower level commands, | 
 | 	 *    including expanded commands, are passed as arguments. | 
 | 	 */ | 
 |  | 
 | 	memset(shell->ctx->temp_buff, 0, sizeof(shell->ctx->temp_buff)); | 
 | 	memcpy(shell->ctx->temp_buff, | 
 | 			shell->ctx->cmd_buff, | 
 | 			shell->ctx->cmd_buff_len); | 
 |  | 
 | 	/* Function shell_spaces_trim must be used instead of shell_make_argv. | 
 | 	 * At this point it is important to keep temp_buff as one string. | 
 | 	 * It will allow to find wildcard commands easily with strstr function. | 
 | 	 */ | 
 | 	z_shell_spaces_trim(shell->ctx->temp_buff); | 
 |  | 
 | 	/* +1 for EOS*/ | 
 | 	shell->ctx->cmd_tmp_buff_len = z_shell_strlen(shell->ctx->temp_buff) + 1; | 
 | } | 
 |  | 
 |  | 
 | enum shell_wildcard_status z_shell_wildcard_process(const struct shell *shell, | 
 | 					const struct shell_static_entry *cmd, | 
 | 					const char *pattern) | 
 | { | 
 | 	enum shell_wildcard_status ret_val = SHELL_WILDCARD_NOT_FOUND; | 
 |  | 
 | 	if (cmd == NULL) { | 
 | 		return ret_val; | 
 | 	} | 
 |  | 
 | 	if (!z_shell_has_wildcard(pattern)) { | 
 | 		return ret_val; | 
 | 	} | 
 |  | 
 | 	/* Function will search commands tree for commands matching wildcard | 
 | 	 * pattern stored in argv[cmd_lvl]. When match is found wildcard pattern | 
 | 	 * will be replaced by matching commands. If there is no space in the | 
 | 	 * buffer to add all matching commands function will add as many as | 
 | 	 * possible. Next it will continue to search for next wildcard pattern | 
 | 	 * and it will try to add matching commands. | 
 | 	 */ | 
 | 	ret_val = commands_expand(shell, cmd, pattern); | 
 |  | 
 | 	return ret_val; | 
 | } | 
 |  | 
 | void z_shell_wildcard_finalize(const struct shell *shell) | 
 | { | 
 | 	memcpy(shell->ctx->cmd_buff, | 
 | 	       shell->ctx->temp_buff, | 
 | 	       shell->ctx->cmd_tmp_buff_len); | 
 | 	shell->ctx->cmd_buff_len = shell->ctx->cmd_tmp_buff_len; | 
 | } |