|  | /* | 
|  | * Copyright (c) 2018 Nordic Semiconductor ASA | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  | #include <ctype.h> | 
|  | #include <zephyr/device.h> | 
|  | #include <zephyr/sys/iterable_sections.h> | 
|  | #include <stdlib.h> | 
|  | #include "shell_utils.h" | 
|  | #include "shell_wildcard.h" | 
|  |  | 
|  | TYPE_SECTION_START_EXTERN(union shell_cmd_entry, shell_dynamic_subcmds); | 
|  | TYPE_SECTION_END_EXTERN(union shell_cmd_entry, shell_dynamic_subcmds); | 
|  |  | 
|  | TYPE_SECTION_START_EXTERN(union shell_cmd_entry, shell_subcmds); | 
|  | TYPE_SECTION_END_EXTERN(union shell_cmd_entry, shell_subcmds); | 
|  |  | 
|  | /* Macro creates empty entry at the bottom of the memory section with subcommands | 
|  | * it is used to detect end of subcommand set that is located before this marker. | 
|  | */ | 
|  | #define Z_SHELL_SUBCMD_END_MARKER_CREATE()				\ | 
|  | static const TYPE_SECTION_ITERABLE(struct shell_static_entry, \ | 
|  | z_shell_subcmd_end_marker, shell_subcmds, Z_SHELL_UNDERSCORE(999)) | 
|  |  | 
|  | Z_SHELL_SUBCMD_END_MARKER_CREATE(); | 
|  |  | 
|  | static inline const union shell_cmd_entry *shell_root_cmd_get(uint32_t id) | 
|  | { | 
|  | const union shell_cmd_entry *cmd; | 
|  |  | 
|  | TYPE_SECTION_GET(union shell_cmd_entry, shell_root_cmds, id, &cmd); | 
|  |  | 
|  | return cmd; | 
|  | } | 
|  |  | 
|  | /* Determine if entry is a dynamic command by checking if address is within | 
|  | * dynamic commands memory section. | 
|  | */ | 
|  | static inline bool is_dynamic_cmd(const union shell_cmd_entry *entry) | 
|  | { | 
|  | return (entry >= TYPE_SECTION_START(shell_dynamic_subcmds)) && | 
|  | (entry < TYPE_SECTION_END(shell_dynamic_subcmds)); | 
|  | } | 
|  |  | 
|  | /* Determine if entry is a section command by checking if address is within | 
|  | * subcommands memory section. | 
|  | */ | 
|  | static inline bool is_section_cmd(const union shell_cmd_entry *entry) | 
|  | { | 
|  | return (entry >= TYPE_SECTION_START(shell_subcmds)) && | 
|  | (entry < TYPE_SECTION_END(shell_subcmds)); | 
|  | } | 
|  |  | 
|  | /* Calculates relative line number of given position in buffer */ | 
|  | static uint32_t line_num_with_buffer_offset_get(struct shell_multiline_cons *cons, | 
|  | uint16_t buffer_pos) | 
|  | { | 
|  | return ((buffer_pos + cons->name_len) / cons->terminal_wid); | 
|  | } | 
|  |  | 
|  | /* Calculates column number of given position in buffer */ | 
|  | static uint32_t col_num_with_buffer_offset_get(struct shell_multiline_cons *cons, | 
|  | uint16_t buffer_pos) | 
|  | { | 
|  | /* columns are counted from 1 */ | 
|  | return (1 + ((buffer_pos + cons->name_len) % cons->terminal_wid)); | 
|  | } | 
|  |  | 
|  | int32_t z_column_span_with_buffer_offsets_get(struct shell_multiline_cons *cons, | 
|  | uint16_t offset1, | 
|  | uint16_t offset2) | 
|  | { | 
|  | return col_num_with_buffer_offset_get(cons, offset2) | 
|  | - col_num_with_buffer_offset_get(cons, offset1); | 
|  | } | 
|  |  | 
|  | int32_t z_row_span_with_buffer_offsets_get(struct shell_multiline_cons *cons, | 
|  | uint16_t offset1, | 
|  | uint16_t offset2) | 
|  | { | 
|  | return line_num_with_buffer_offset_get(cons, offset2) | 
|  | - line_num_with_buffer_offset_get(cons, offset1); | 
|  | } | 
|  |  | 
|  | void z_shell_multiline_data_calc(struct shell_multiline_cons *cons, | 
|  | uint16_t buff_pos, uint16_t buff_len) | 
|  | { | 
|  | /* Current cursor position in command. | 
|  | * +1 -> because home position is (1, 1) | 
|  | */ | 
|  | cons->cur_x = (buff_pos + cons->name_len) % cons->terminal_wid + 1; | 
|  | cons->cur_y = (buff_pos + cons->name_len) / cons->terminal_wid + 1; | 
|  |  | 
|  | /* Extreme position when cursor is at the end of command. */ | 
|  | cons->cur_y_end = (buff_len + cons->name_len) / cons->terminal_wid + 1; | 
|  | cons->cur_x_end = (buff_len + cons->name_len) % cons->terminal_wid + 1; | 
|  | } | 
|  |  | 
|  | static char make_argv(char **ppcmd, uint8_t c) | 
|  | { | 
|  | char *cmd = *ppcmd; | 
|  | char quote = 0; | 
|  |  | 
|  | while (1) { | 
|  | c = *cmd; | 
|  |  | 
|  | if (c == '\0') { | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (!quote) { | 
|  | switch (c) { | 
|  | case '\\': | 
|  | memmove(cmd, cmd + 1, | 
|  | z_shell_strlen(cmd)); | 
|  | cmd += 1; | 
|  | continue; | 
|  |  | 
|  | case '\'': | 
|  | case '\"': | 
|  | memmove(cmd, cmd + 1, | 
|  | z_shell_strlen(cmd)); | 
|  | quote = c; | 
|  | continue; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (quote == c) { | 
|  | memmove(cmd, cmd + 1, z_shell_strlen(cmd)); | 
|  | quote = 0; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (quote && c == '\\') { | 
|  | char t = *(cmd + 1); | 
|  |  | 
|  | if (t == quote) { | 
|  | memmove(cmd, cmd + 1, | 
|  | z_shell_strlen(cmd)); | 
|  | cmd += 1; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (t == '0') { | 
|  | uint8_t i; | 
|  | uint8_t v = 0U; | 
|  |  | 
|  | for (i = 2U; i < (2 + 3); i++) { | 
|  | t = *(cmd + i); | 
|  |  | 
|  | if (t >= '0' && t <= '7') { | 
|  | v = (v << 3) | (t - '0'); | 
|  | } else { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (i > 2) { | 
|  | memmove(cmd, cmd + (i - 1), | 
|  | z_shell_strlen(cmd) - (i - 2)); | 
|  | *cmd++ = v; | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (t == 'x') { | 
|  | uint8_t i; | 
|  | uint8_t v = 0U; | 
|  |  | 
|  | for (i = 2U; i < (2 + 2); i++) { | 
|  | t = *(cmd + i); | 
|  |  | 
|  | if (t >= '0' && t <= '9') { | 
|  | v = (v << 4) | (t - '0'); | 
|  | } else if ((t >= 'a') && | 
|  | (t <= 'f')) { | 
|  | v = (v << 4) | (t - 'a' + 10); | 
|  | } else if ((t >= 'A') && (t <= 'F')) { | 
|  | v = (v << 4) | (t - 'A' + 10); | 
|  | } else { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (i > 2) { | 
|  | memmove(cmd, cmd + (i - 1), | 
|  | z_shell_strlen(cmd) - (i - 2)); | 
|  | *cmd++ = v; | 
|  | continue; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!quote && isspace((int) c) != 0) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | cmd += 1; | 
|  | } | 
|  | *ppcmd = cmd; | 
|  |  | 
|  | return quote; | 
|  | } | 
|  |  | 
|  |  | 
|  | char z_shell_make_argv(size_t *argc, const char **argv, char *cmd, | 
|  | uint8_t max_argc) | 
|  | { | 
|  | char quote = 0; | 
|  | char c; | 
|  |  | 
|  | *argc = 0; | 
|  | do { | 
|  | c = *cmd; | 
|  | if (c == '\0') { | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (isspace((int) c) != 0) { | 
|  | *cmd++ = '\0'; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | argv[(*argc)++] = cmd; | 
|  | if (*argc == max_argc) { | 
|  | break; | 
|  | } | 
|  | quote = make_argv(&cmd, c); | 
|  | } while (true); | 
|  |  | 
|  | return quote; | 
|  | } | 
|  |  | 
|  | void z_shell_pattern_remove(char *buff, uint16_t *buff_len, const char *pattern) | 
|  | { | 
|  | char *pattern_addr = strstr(buff, pattern); | 
|  | uint16_t shift; | 
|  | uint16_t pattern_len = z_shell_strlen(pattern); | 
|  |  | 
|  | if (!pattern_addr) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (pattern_addr > buff) { | 
|  | if (*(pattern_addr - 1) == ' ') { | 
|  | pattern_len++; /* space needs to be removed as well */ | 
|  | pattern_addr--; /* set pointer to space */ | 
|  | } | 
|  | } | 
|  |  | 
|  | shift = z_shell_strlen(pattern_addr) - pattern_len + 1; /* +1 for EOS */ | 
|  | *buff_len -= pattern_len; | 
|  |  | 
|  | memmove(pattern_addr, pattern_addr + pattern_len, shift); | 
|  | } | 
|  |  | 
|  | static inline uint32_t shell_root_cmd_count(void) | 
|  | { | 
|  | size_t len; | 
|  |  | 
|  | TYPE_SECTION_COUNT(union shell_cmd_entry, shell_root_cmds, &len); | 
|  |  | 
|  | return len; | 
|  | } | 
|  |  | 
|  | /* Function returning pointer to parent command matching requested syntax. */ | 
|  | const struct shell_static_entry *root_cmd_find(const char *syntax) | 
|  | { | 
|  | const size_t cmd_count = shell_root_cmd_count(); | 
|  | const union shell_cmd_entry *cmd; | 
|  |  | 
|  | for (size_t cmd_idx = 0; cmd_idx < cmd_count; ++cmd_idx) { | 
|  | cmd = shell_root_cmd_get(cmd_idx); | 
|  | if (strcmp(syntax, cmd->entry->syntax) == 0) { | 
|  | return cmd->entry; | 
|  | } | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | const struct shell_static_entry *z_shell_cmd_get( | 
|  | const struct shell_static_entry *parent, | 
|  | size_t idx, | 
|  | struct shell_static_entry *dloc) | 
|  | { | 
|  | const struct shell_static_entry *res = NULL; | 
|  |  | 
|  | if (parent == NULL) { | 
|  | return  (idx < shell_root_cmd_count()) ? | 
|  | shell_root_cmd_get(idx)->entry : NULL; | 
|  | } | 
|  |  | 
|  | __ASSERT_NO_MSG(dloc != NULL); | 
|  |  | 
|  | if (parent->subcmd) { | 
|  | if (is_dynamic_cmd(parent->subcmd)) { | 
|  | parent->subcmd->dynamic_get(idx, dloc); | 
|  | if (dloc->syntax != NULL) { | 
|  | res = dloc; | 
|  | } | 
|  | } else { | 
|  | const struct shell_static_entry *entry_list; | 
|  |  | 
|  | if (is_section_cmd(parent->subcmd)) { | 
|  | /* First element is null */ | 
|  | entry_list = | 
|  | (const struct shell_static_entry *)parent->subcmd; | 
|  | idx++; | 
|  | } else { | 
|  | entry_list = parent->subcmd->entry; | 
|  | } | 
|  |  | 
|  |  | 
|  | if (entry_list[idx].syntax != NULL) { | 
|  | res = &entry_list[idx]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /* Function returns pointer to a command matching given pattern. | 
|  | * | 
|  | * @param cmd		Pointer to commands array that will be searched. | 
|  | * @param lvl		Root command indicator. If set to 0 shell will search | 
|  | *			for pattern in parent commands. | 
|  | * @param cmd_str	Command pattern to be found. | 
|  | * @param dloc	Shell static command descriptor. | 
|  | * | 
|  | * @return		Pointer to found command. | 
|  | */ | 
|  | const struct shell_static_entry *z_shell_find_cmd( | 
|  | const struct shell_static_entry *parent, | 
|  | const char *cmd_str, | 
|  | struct shell_static_entry *dloc) | 
|  | { | 
|  | const struct shell_static_entry *entry; | 
|  | struct shell_static_entry parent_cpy; | 
|  | size_t idx = 0; | 
|  |  | 
|  | /* Dynamic command operates on shared memory. If we are processing two | 
|  | * dynamic commands at the same time (current and subcommand) they | 
|  | * will operate on the same memory region what can cause undefined | 
|  | * behaviour. | 
|  | * Hence we need a separate memory for each of them. | 
|  | */ | 
|  | if (parent) { | 
|  | memcpy(&parent_cpy, parent, sizeof(struct shell_static_entry)); | 
|  | parent = &parent_cpy; | 
|  | } | 
|  |  | 
|  | while ((entry = z_shell_cmd_get(parent, idx++, dloc)) != NULL) { | 
|  | if (strcmp(cmd_str, entry->syntax) == 0) { | 
|  | return entry; | 
|  | } | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | const struct shell_static_entry *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) | 
|  | { | 
|  | const struct shell_static_entry *prev_entry = NULL; | 
|  |  | 
|  | *match_arg = Z_SHELL_CMD_ROOT_LVL; | 
|  |  | 
|  | while (*match_arg < argc) { | 
|  |  | 
|  | if (IS_ENABLED(CONFIG_SHELL_WILDCARD)) { | 
|  | /* ignore wildcard argument */ | 
|  | if (z_shell_has_wildcard(argv[*match_arg])) { | 
|  | (*match_arg)++; | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | prev_entry = entry; | 
|  | entry = z_shell_find_cmd(entry, argv[*match_arg], dloc); | 
|  | if (entry) { | 
|  | (*match_arg)++; | 
|  | } else { | 
|  | entry = prev_entry; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (only_static && (entry == dloc)) { | 
|  | (*match_arg)--; | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | return entry; | 
|  | } | 
|  |  | 
|  | int shell_set_root_cmd(const char *cmd) | 
|  | { | 
|  | const struct shell_static_entry *entry; | 
|  |  | 
|  | entry = cmd ? root_cmd_find(cmd) : NULL; | 
|  |  | 
|  | if (cmd && (entry == NULL)) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | STRUCT_SECTION_FOREACH(shell, sh) { | 
|  | sh->ctx->selected_cmd = entry; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  | void z_shell_spaces_trim(char *str) | 
|  | { | 
|  | uint16_t len = z_shell_strlen(str); | 
|  | uint16_t shift = 0U; | 
|  |  | 
|  | if (len == 0U) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (uint16_t i = 0; i < len - 1; i++) { | 
|  | if (isspace((int)str[i]) != 0) { | 
|  | for (uint16_t j = i + 1; j < len; j++) { | 
|  | if (isspace((int)str[j]) != 0) { | 
|  | shift++; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (shift > 0) { | 
|  | /* +1 for EOS */ | 
|  | memmove(&str[i + 1], | 
|  | &str[j], | 
|  | len - j + 1); | 
|  | len -= shift; | 
|  | shift = 0U; | 
|  | } | 
|  |  | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** @brief Remove white chars from beginning and end of command buffer. | 
|  | * | 
|  | */ | 
|  | static void buffer_trim(char *buff, uint16_t *buff_len) | 
|  | { | 
|  | uint16_t i = 0U; | 
|  |  | 
|  | /* no command in the buffer */ | 
|  | if (buff[0] == '\0') { | 
|  | return; | 
|  | } | 
|  |  | 
|  | while (isspace((int) buff[*buff_len - 1U]) != 0) { | 
|  | *buff_len -= 1U; | 
|  | if (*buff_len == 0U) { | 
|  | buff[0] = '\0'; | 
|  | return; | 
|  | } | 
|  | } | 
|  | buff[*buff_len] = '\0'; | 
|  |  | 
|  | /* Counting whitespace characters starting from beginning of the | 
|  | * command. | 
|  | */ | 
|  | while (isspace((int) buff[i++]) != 0) { | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Removing counted whitespace characters. */ | 
|  | if (--i > 0) { | 
|  | memmove(buff, buff + i, (*buff_len + 1U) - i); /* +1 for '\0' */ | 
|  | *buff_len = *buff_len - i; | 
|  | } | 
|  | } | 
|  |  | 
|  | void z_shell_cmd_trim(const struct shell *sh) | 
|  | { | 
|  | buffer_trim(sh->ctx->cmd_buff, &sh->ctx->cmd_buff_len); | 
|  | sh->ctx->cmd_buff_pos = sh->ctx->cmd_buff_len; | 
|  | } | 
|  |  | 
|  | enum shell_device_status { | 
|  | SHELL_DEVICE_STATUS_ANY, | 
|  | SHELL_DEVICE_STATUS_READY, | 
|  | SHELL_DEVICE_STATUS_NON_READY | 
|  | }; | 
|  |  | 
|  | static const struct device *shell_device_internal(size_t idx, | 
|  | const char *prefix, | 
|  | shell_device_filter_t filter, | 
|  | enum shell_device_status status) | 
|  | { | 
|  | size_t match_idx = 0; | 
|  | const struct device *dev; | 
|  | size_t len = z_device_get_all_static(&dev); | 
|  | const struct device *dev_end = dev + len; | 
|  |  | 
|  | while (dev < dev_end) { | 
|  | if ((status == SHELL_DEVICE_STATUS_ANY | 
|  | || (status == SHELL_DEVICE_STATUS_READY && | 
|  | device_is_ready(dev)) | 
|  | || (status == SHELL_DEVICE_STATUS_NON_READY && | 
|  | !device_is_ready(dev))) | 
|  | && (dev->name != NULL) | 
|  | && (strlen(dev->name) != 0) | 
|  | && ((prefix == NULL) | 
|  | || (strncmp(prefix, dev->name, | 
|  | strlen(prefix)) == 0)) | 
|  | && (filter == NULL || filter(dev))) { | 
|  | if (match_idx == idx) { | 
|  | return dev; | 
|  | } | 
|  | ++match_idx; | 
|  | } | 
|  | ++dev; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | const struct device *shell_device_filter(size_t idx, | 
|  | shell_device_filter_t filter) | 
|  | { | 
|  | return shell_device_internal(idx, NULL, filter, | 
|  | SHELL_DEVICE_STATUS_READY); | 
|  | } | 
|  |  | 
|  | const struct device *shell_device_lookup(size_t idx, | 
|  | const char *prefix) | 
|  | { | 
|  | return shell_device_internal(idx, prefix, NULL, | 
|  | SHELL_DEVICE_STATUS_READY); | 
|  | } | 
|  |  | 
|  | const struct device *shell_device_lookup_all(size_t idx, | 
|  | const char *prefix) | 
|  | { | 
|  | return shell_device_internal(idx, prefix, NULL, | 
|  | SHELL_DEVICE_STATUS_ANY); | 
|  | } | 
|  |  | 
|  | const struct device *shell_device_lookup_non_ready(size_t idx, | 
|  | const char *prefix) | 
|  | { | 
|  | return shell_device_internal(idx, prefix, NULL, | 
|  | SHELL_DEVICE_STATUS_NON_READY); | 
|  | } | 
|  |  | 
|  | const struct device *shell_device_get_binding(const char *name) | 
|  | { | 
|  | const struct device *dev = device_get_binding(name); | 
|  |  | 
|  | if (IS_ENABLED(CONFIG_DEVICE_DT_METADATA) && dev == NULL) { | 
|  | dev = device_get_by_dt_nodelabel(name); | 
|  | } | 
|  |  | 
|  | return dev; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_DEVICE_DT_METADATA | 
|  | static inline bool device_has_nodelabel(const struct device *dev, | 
|  | const char *name) | 
|  | { | 
|  | const struct device_dt_nodelabels *nl; | 
|  |  | 
|  | nl = device_get_dt_nodelabels(dev); | 
|  | if (nl != NULL) { | 
|  | size_t i; | 
|  |  | 
|  | for (i = 0; i < nl->num_nodelabels; i++) { | 
|  | const char *dev_nl = nl->nodelabels[i]; | 
|  |  | 
|  | if ((strlen(dev_nl) == strlen(name)) && | 
|  | (strcmp(name, dev_nl) == 0)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  | #else | 
|  | #define device_has_nodelabel(...) false | 
|  | #endif /* CONFIG_DEVICE_DT_METADATA */ | 
|  |  | 
|  | const struct device *shell_device_get_binding_all(const char *name) | 
|  | { | 
|  | const struct device *dev; | 
|  | size_t len = z_device_get_all_static(&dev); | 
|  | const struct device *dev_end = dev + len; | 
|  |  | 
|  | if (name != NULL) { | 
|  | for (; dev < dev_end; dev++) { | 
|  | if (((dev->name != NULL) | 
|  | && (strlen(dev->name) == strlen(name)) | 
|  | && (strcmp(name, dev->name) == 0)) | 
|  | || device_has_nodelabel(dev, name)) { | 
|  | return dev; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | long shell_strtol(const char *str, int base, int *err) | 
|  | { | 
|  | long val; | 
|  | char *endptr; | 
|  |  | 
|  | errno = 0; | 
|  | val = strtol(str, &endptr, base); | 
|  | if (errno == ERANGE) { | 
|  | *err = -ERANGE; | 
|  | return 0; | 
|  | } else if (errno || endptr == str || *endptr) { | 
|  | *err = -EINVAL; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return val; | 
|  | } | 
|  |  | 
|  | unsigned long shell_strtoul(const char *str, int base, int *err) | 
|  | { | 
|  | unsigned long val; | 
|  | char *endptr; | 
|  |  | 
|  | if (*str == '-') { | 
|  | *err = -EINVAL; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | errno = 0; | 
|  | val = strtoul(str, &endptr, base); | 
|  | if (errno == ERANGE) { | 
|  | *err = -ERANGE; | 
|  | return 0; | 
|  | } else if (errno || endptr == str || *endptr) { | 
|  | *err = -EINVAL; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return val; | 
|  | } | 
|  |  | 
|  | unsigned long long shell_strtoull(const char *str, int base, int *err) | 
|  | { | 
|  | unsigned long long val; | 
|  | char *endptr; | 
|  |  | 
|  | if (*str == '-') { | 
|  | *err = -EINVAL; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | errno = 0; | 
|  | val = strtoull(str, &endptr, base); | 
|  | if (errno == ERANGE) { | 
|  | *err = -ERANGE; | 
|  | return 0; | 
|  | } else if (errno || endptr == str || *endptr) { | 
|  | *err = -EINVAL; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return val; | 
|  | } | 
|  |  | 
|  | bool shell_strtobool(const char *str, int base, int *err) | 
|  | { | 
|  | if (!strcmp(str, "on") || !strcmp(str, "enable") || !strcmp(str, "true")) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (!strcmp(str, "off") || !strcmp(str, "disable") || !strcmp(str, "false")) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return shell_strtoul(str, base, err); | 
|  | } |