/* * Copyright (c) 2015 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * @brief Console handler implementation of shell.h API */ #include #include #include #include #include #include #include /* maximum number of command parameters */ #define ARGC_MAX 10 static const struct shell_cmd *commands; static const char *prompt; #define STACKSIZE CONFIG_CONSOLE_HANDLER_SHELL_STACKSIZE static char __stack stack[STACKSIZE]; #define MAX_CMD_QUEUED 3 static struct uart_console_input buf[MAX_CMD_QUEUED]; static struct nano_fifo avail_queue; static struct nano_fifo cmds_queue; static shell_cmd_function_t app_cmd_handler; static shell_prompt_function_t app_prompt_handler; static const char *get_prompt(void) { if (app_prompt_handler) { const char *str; str = app_prompt_handler(); if (str) { return str; } } return prompt; } static void line_queue_init(void) { int i; for (i = 0; i < MAX_CMD_QUEUED; i++) { nano_fifo_put(&avail_queue, &buf[i]); } } static size_t line2argv(char *str, char *argv[], size_t size) { size_t argc = 0; if (!strlen(str)) { return 0; } while (*str && *str == ' ') { str++; } if (!*str) { return 0; } argv[argc++] = str; while ((str = strchr(str, ' '))) { *str++ = '\0'; while (*str && *str == ' ') { str++; } if (!*str) { break; } argv[argc++] = str; if (argc == size) { printk("Too many parameters (max %u)\n", size - 1); return 0; } } /* keep it POSIX style where argv[argc] is required to be NULL */ argv[argc] = NULL; return argc; } static int show_cmd_help(int argc, char *argv[]) { int i; if (!argv[0] || argv[0][0] == '\0') { goto done; } for (i = 0; commands[i].cmd_name; i++) { if (!strcmp(argv[0], commands[i].cmd_name)) { printk("%s %s\n", commands[i].cmd_name, commands[i].help ? commands[i].help : ""); return 0; } } done: printk("Unrecognized command: %s\n", argv[0]); return 0; } static int show_help(int argc, char *argv[]) { int i; if (argc > 1) { return show_cmd_help(--argc, &argv[1]); } printk("Available commands:\n"); printk("help\n"); for (i = 0; commands[i].cmd_name; i++) { printk("%s\n", commands[i].cmd_name); } return 0; } static shell_cmd_function_t get_cb(const char *string) { int i; if (!string || string[0] == '\0') { return NULL; } if (!strcmp(string, "help")) { return show_help; } for (i = 0; commands[i].cmd_name; i++) { if (!strcmp(string, commands[i].cmd_name)) { return commands[i].cb; } } return NULL; } static void shell(int arg1, int arg2) { char *argv[ARGC_MAX + 1]; size_t argc; while (1) { struct uart_console_input *cmd; shell_cmd_function_t cb; printk("%s", get_prompt()); cmd = nano_fiber_fifo_get(&cmds_queue, TICKS_UNLIMITED); argc = line2argv(cmd->line, argv, ARRAY_SIZE(argv)); if (!argc) { nano_fiber_fifo_put(&avail_queue, cmd); continue; } cb = get_cb(argv[0]); 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"); nano_fiber_fifo_put(&avail_queue, cmd); continue; } } /* Execute callback with arguments */ if (cb(argc, argv) < 0) { show_cmd_help(argc, argv); } nano_fiber_fifo_put(&avail_queue, cmd); } } static uint8_t completion(char *line, uint8_t len) { char cmds[MAX_LINE_LEN]; int common_chars = -1; size_t cmds_len = 0; int i; for (i = 0; commands[i].cmd_name; i++) { int name_len, j; if (strncmp(line, commands[i].cmd_name, len)) { continue; } name_len = strlen(commands[i].cmd_name); if (name_len > (MAX_LINE_LEN - (cmds_len + 1))) { break; } memcpy(cmds + cmds_len, commands[i].cmd_name, name_len); cmds_len += name_len; cmds[cmds_len++] = '\n'; /* first match */ if (common_chars < 0) { common_chars = name_len; continue; } /* cut common part of matching names */ for (j = 0; j < common_chars; j++) { if (cmds[j] != commands[i].cmd_name[j]) { break; } } common_chars = j; } /* no match */ if (common_chars < 0) { return 0; } /* alter line with common part of commands */ memcpy(line + len, cmds + len, common_chars - len); if (common_chars < cmds_len - 1) { /* * more than one match, print matching commands, restore prompt * and print common part of matched commands */ cmds[cmds_len] = '\0'; printk("\n%s", cmds); /* restore prompt */ printk("%s", get_prompt()); /* print common part after prompt */ for (i = 0; i < common_chars; i++) { printk("%c", line[i]); } } else { /* only one match, complete command name */ for (i = len; i < common_chars; i++) { printk("%c", line[i]); } /* for convenience add space after command */ printk(" "); line[common_chars] = ' '; common_chars++; } return common_chars - len; } void shell_init(const char *str, const struct shell_cmd *cmds) { nano_fifo_init(&cmds_queue); nano_fifo_init(&avail_queue); commands = cmds; line_queue_init(); prompt = str ? str : ""; task_fiber_start(stack, STACKSIZE, shell, 0, 0, 7, 0); /* Register serial console handler */ uart_register_input(&avail_queue, &cmds_queue, completion); } /** @brief Optionally register an app default cmd handler. * * @param handler To be called if no cmd found in cmds registered with shell_init. */ void shell_register_app_cmd_handler(shell_cmd_function_t handler) { app_cmd_handler = handler; } void shell_register_prompt_handler(shell_prompt_function_t handler) { app_prompt_handler = handler; }