shell: Redesign internal command handling

Make the internal commands (exit, select & help) as any other
commands, so that e.g. "help help" works as expected. Also redesign
the way commands are looked up to avoid duplicate lookups.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
diff --git a/subsys/shell/shell.c b/subsys/shell/shell.c
index 791fece..14308c7 100644
--- a/subsys/shell/shell.c
+++ b/subsys/shell/shell.c
@@ -150,60 +150,15 @@
 	return -1;
 }
 
-/* For a specific command: argv[0] = module name, argv[1] = command name
- * If a default module was selected: argv[0] = command name
- */
-static const char *get_command_and_module(char *argv[], int *module)
+static int show_cmd_help(const struct shell_cmd *cmd, bool full)
 {
-	*module = -1;
+	printk("Usage: %s %s\n", cmd->cmd_name, cmd->help ? cmd->help : "");
 
-	if (default_module == -1) {
-		if (!argv[1] || argv[1][0] == '\0') {
-			printk("Unrecognized command: %s\n", argv[0]);
-			return NULL;
-		}
-
-		*module = get_destination_module(argv[0]);
-		if (*module == -1) {
-			printk("Illegal module %s\n", argv[0]);
-			return NULL;
-		}
-
-		return argv[1];
+	if (full && cmd->desc) {
+		printk("%s\n", cmd->desc);
 	}
 
-	*module = default_module;
-	return argv[0];
-}
-
-static int show_cmd_help(char *argv[], bool full)
-{
-	const char *command = NULL;
-	int module = -1;
-	const struct shell_module *shell_module;
-	int i;
-
-	command = get_command_and_module(argv, &module);
-	if ((module == -1) || (command == NULL)) {
-		return 0;
-	}
-
-	shell_module = &__shell_cmd_start[module];
-	for (i = 0; shell_module->commands[i].cmd_name; i++) {
-		if (!strcmp(command, shell_module->commands[i].cmd_name)) {
-			printk("Usage: %s %s\n",
-			       shell_module->commands[i].cmd_name,
-			       shell_module->commands[i].help ?
-			       shell_module->commands[i].help : "");
-			if (full && shell_module->commands[i].desc) {
-				printk("%s\n", shell_module->commands[i].desc);
-			}
-			return 0;
-		}
-	}
-
-	printk("Unrecognized command: %s\n", argv[0]);
-	return -EINVAL;
+	return 0;
 }
 
 static void print_module_commands(const int module)
@@ -221,27 +176,65 @@
 	}
 }
 
-static int show_help(int argc, char *argv[])
+static const struct shell_cmd *get_cmd(const struct shell_cmd cmds[],
+				       const char *cmd_str)
 {
-	int module;
+	int i;
 
-	/* help per command */
-	if ((argc > 2) || ((default_module != -1) && (argc == 2))) {
-		return show_cmd_help(&argv[1], true);
+	for (i = 0; cmds[i].cmd_name; i++) {
+		if (!strcmp(cmd_str, cmds[i].cmd_name)) {
+			return &cmds[i];
+		}
 	}
 
-	/* help per module */
-	if ((argc == 2) || ((default_module != -1) && (argc == 1))) {
-		if (default_module == -1) {
-			module = get_destination_module(argv[1]);
-			if (module == -1) {
-				printk("Illegal module %s\n", argv[1]);
-				return -EINVAL;
+	return NULL;
+}
+
+static const struct shell_cmd *get_mod_cmd(int module, const char *cmd_str)
+{
+	return get_cmd(__shell_cmd_start[module].commands, cmd_str);
+}
+
+static int cmd_help(int argc, char *argv[])
+{
+	int module = default_module;
+
+	/* help per command */
+	if (argc > 1) {
+		const struct shell_cmd *cmd;
+		const char *cmd_str;
+
+		module = get_destination_module(argv[1]);
+		if (module != -1) {
+			if (argc == 2) {
+				goto module_help;
 			}
+
+			cmd_str = argv[2];
+		} else if (argc > 2) {
+			cmd_str = argv[1];
 		} else {
-			module = default_module;
+			printk("Unknown module or command\n");
+			return -EINVAL;
 		}
 
+		if (module == -1) {
+			printk("No help found for '%s'\n", cmd_str);
+			return -EINVAL;
+		}
+
+		cmd = get_mod_cmd(module, cmd_str);
+		if (cmd) {
+			return show_cmd_help(cmd, true);
+		} else {
+			printk("Unknown command '%s'\n", cmd_str);
+			return -EINVAL;
+		}
+	}
+
+module_help:
+	/* help per module */
+	if (module != -1) {
 		print_module_commands(module);
 		printk("\nEnter 'exit' to leave current module.\n");
 	} else { /* help for all entities */
@@ -280,7 +273,7 @@
 	return 0;
 }
 
-static int select_module(int argc, char *argv[])
+static int cmd_select(int argc, char *argv[])
 {
 	if (argc == 1) {
 		default_module = -1;
@@ -290,7 +283,7 @@
 	return set_default_module(argv[1]);
 }
 
-static int exit_module(int argc, char *argv[])
+static int cmd_exit(int argc, char *argv[])
 {
 	if (argc == 1) {
 		default_module = -1;
@@ -299,42 +292,22 @@
 	return 0;
 }
 
-static shell_cmd_function_t get_cb(int module, const char *command)
+static const struct shell_cmd *get_internal(const char *command)
 {
-	const struct shell_module *shell_module;
-	int i;
+	static const struct shell_cmd internal_commands[] = {
+		{ "help", cmd_help, "[command]" },
+		{ "select", cmd_select, "[module]" },
+		{ "exit", cmd_exit, NULL },
+		{ NULL },
+	};
 
-	shell_module = &__shell_cmd_start[module];
-	for (i = 0; shell_module->commands[i].cmd_name; i++) {
-		if (!strcmp(command, shell_module->commands[i].cmd_name)) {
-			return shell_module->commands[i].cb;
-		}
-	}
-
-	return NULL;
-}
-
-static shell_cmd_function_t get_internal(const char *command)
-{
-	if (!strcmp(command, "help")) {
-		return show_help;
-	}
-
-	if (!strcmp(command, "select")) {
-		return select_module;
-	}
-
-	if (!strcmp(command, "exit")) {
-		return exit_module;
-	}
-
-	return NULL;
+	return get_cmd(internal_commands, command);
 }
 
 int shell_exec(char *line)
 {
 	char *argv[ARGC_MAX + 1], **argv_start = argv;
-	shell_cmd_function_t cb;
+	const struct shell_cmd *cmd;
 	int argc, err;
 
 	argc = line2argv(line, argv, ARRAY_SIZE(argv));
@@ -342,9 +315,9 @@
 		return -EINVAL;
 	}
 
-	cb = get_internal(argv[0]);
-	if (cb) {
-		return cb(argc, argv_start);
+	cmd = get_internal(argv[0]);
+	if (cmd) {
+		goto done;
 	}
 
 	if (argc == 1 && default_module == -1) {
@@ -353,35 +326,36 @@
 	}
 
 	if (default_module != -1) {
-		cb = get_cb(default_module, argv[0]);
+		cmd = get_mod_cmd(default_module, argv[0]);
 	}
 
-	if (!cb && argc > 1) {
+	if (!cmd && argc > 1) {
 		int module;
 
 		module = get_destination_module(argv[0]);
 		if (module != -1) {
-			cb = get_cb(module, argv[1]);
-			if (cb) {
+			cmd = get_mod_cmd(module, argv[1]);
+			if (cmd) {
 				argc--;
 				argv_start++;
 			}
 		}
 	}
 
-	if (!cb) {
-		if (app_cmd_handler != NULL) {
-			cb = app_cmd_handler;
-		} else {
-			printk("Unrecognized command: %s\n", argv[0]);
-			printk("Type 'help' for list of available commands\n");
-			return -EINVAL;
+	if (!cmd) {
+		if (app_cmd_handler) {
+			return app_cmd_handler(argc, argv);
 		}
+
+		printk("Unrecognized command: %s\n", argv[0]);
+		printk("Type 'help' for list of available commands\n");
+		return -EINVAL;
 	}
 
-	err = cb(argc, argv_start);
+done:
+	err = cmd->cb(argc, argv_start);
 	if (err < 0) {
-		show_cmd_help(argv, false);
+		show_cmd_help(cmd, false);
 	}
 
 	return err;