/* * 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) { const char *first_match = NULL; int common_chars = -1; int i; for (i = 0; commands[i].cmd_name; i++) { int j; if (strncmp(line, commands[i].cmd_name, len)) { continue; } if (!first_match) { first_match = commands[i].cmd_name; continue; } /* more commands match, print first match */ if (first_match && (common_chars < 0)) { printk("\n%s\n", first_match); common_chars = strlen(first_match); } /* cut common part of matching names */ for (j = 0; j < common_chars; j++) { if (first_match[j] != commands[i].cmd_name[j]) { break; } } common_chars = j; printk("%s\n", commands[i].cmd_name); } /* no match, do nothing */ if (!first_match) { return 0; } if (common_chars >= 0) { /* multiple match, restore prompt */ printk("%s", get_prompt()); /* add common part after prompt */ for (i = 0; i < common_chars; i++) { printk("%c", first_match[i]); line[i] = first_match[i]; } } else { common_chars = strlen(first_match); /* full command name with trailing spaces, do nothing */ if (len > common_chars) { return 0; } /* complete matched command name */ for (i = len; i < common_chars; i++) { printk("%c", first_match[i]); line[i] = first_match[i]; } /* for convenience add space after command */ printk(" "); line[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; }