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;