blob: 1c24d586873983e7dd642a23ec89e5f536c88a61 [file] [log] [blame]
/*
* 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 <zephyr.h>
#include <stdio.h>
#include <string.h>
#include <console/uart_console.h>
#include <misc/printk.h>
#include <misc/util.h>
#include <misc/shell.h>
/* 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;
}