settings: shell: improve reading and writing string values Make reading and writing string values more flexible: 1. Eliminate the intermediate buffer when saving a string setting. This needlessly limited the maximum string length that could be saved using the shell command. 2. Do not add nor assume that a string saved in the settings includes the null-terminator. The settings subsystem uses metadata for encoding the value length, so there it is redundant to also store the null-terminator in flash. By the way, also make sure the command handlers only return -EINVAL and -ENOEXEC error codes as documented in the handler type description. Signed-off-by: Damian Krolik <damian.krolik@nordicsemi.no>
diff --git a/subsys/settings/src/settings_shell.c b/subsys/settings/src/settings_shell.c index bef6b23..67e57b7 100644 --- a/subsys/settings/src/settings_shell.c +++ b/subsys/settings/src/settings_shell.c
@@ -9,6 +9,7 @@ #include <zephyr/sys/util.h> #include <zephyr/toolchain.h> +#include <ctype.h> #include <stdint.h> #include <stddef.h> @@ -51,6 +52,7 @@ if (err) { shell_error(shell_ptr, "Failed to load settings: %d", err); + err = -ENOEXEC; } return err; @@ -74,6 +76,7 @@ void *param) { uint8_t buffer[SETTINGS_MAX_VAL_LEN]; + ssize_t i; ssize_t num_read_bytes = MIN(len, SETTINGS_MAX_VAL_LEN); struct settings_read_callback_params *params = param; @@ -100,11 +103,13 @@ shell_hexdump(params->shell_ptr, buffer, num_read_bytes); break; case SETTINGS_VALUE_STRING: - if (buffer[num_read_bytes - 1] != '\0') { - shell_error(params->shell_ptr, "Value is not a string"); - return 0; + for (i = 0; i < num_read_bytes; i++) { + if (!isprint(buffer[i])) { + shell_error(params->shell_ptr, "Value is not a string"); + return 0; + } } - shell_print(params->shell_ptr, "%s", buffer); + shell_print(params->shell_ptr, "%.*s", (int)num_read_bytes, buffer); break; } @@ -138,7 +143,7 @@ err = settings_parse_type(argv[1], &value_type); if (err) { shell_error(shell_ptr, "Invalid type: %s", argv[1]); - return err; + return -EINVAL; } } @@ -152,9 +157,10 @@ if (err) { shell_error(shell_ptr, "Failed to load setting: %d", err); + err = -ENOEXEC; } else if (!params.value_found) { - err = -ENOENT; shell_error(shell_ptr, "Setting not found"); + err = -ENOEXEC; } return err; @@ -164,42 +170,39 @@ { int err; uint8_t buffer[CONFIG_SHELL_CMD_BUFF_SIZE / 2]; - size_t buffer_len = 0; + const void *value; + size_t value_len = 0; enum settings_value_types value_type = SETTINGS_VALUE_HEX; if (argc > 3) { err = settings_parse_type(argv[1], &value_type); if (err) { shell_error(shell_ptr, "Invalid type: %s", argv[1]); - return err; + return -EINVAL; } } switch (value_type) { case SETTINGS_VALUE_HEX: - buffer_len = hex2bin(argv[argc - 1], strlen(argv[argc - 1]), - buffer, sizeof(buffer)); + value = buffer; + value_len = hex2bin(argv[argc - 1], strlen(argv[argc - 1]), buffer, sizeof(buffer)); break; case SETTINGS_VALUE_STRING: - buffer_len = strlen(argv[argc - 1]) + 1; - if (buffer_len > sizeof(buffer)) { - shell_error(shell_ptr, "%s is bigger than shell's buffer", argv[argc - 1]); - return -EINVAL; - } - - memcpy(buffer, argv[argc - 1], buffer_len); + value = argv[argc - 1]; + value_len = strlen(argv[argc - 1]); break; } - if (buffer_len == 0) { + if (value_len == 0) { shell_error(shell_ptr, "Failed to parse value"); return -EINVAL; } - err = settings_save_one(argv[argc - 2], buffer, buffer_len); + err = settings_save_one(argv[argc - 2], value, value_len); if (err) { shell_error(shell_ptr, "Failed to write setting: %d", err); + err = -ENOEXEC; } return err; @@ -213,6 +216,7 @@ if (err) { shell_error(shell_ptr, "Failed to delete setting: %d", err); + err = -ENOEXEC; } return err;