blob: 6fa4a4b8880e9d0dbc5871c25be23cc5b4191820 [file] [log] [blame]
/*
* Copyright (c) 2008 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <app/console.h>
#include <debug.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
static cmd_block *command_list = NULL;
/* a linear array of statically defined command blocks,
defined in the linker script.
*/
extern cmd_block __commands_start;
extern cmd_block __commands_end;
static int cmd_help(int argc, const cmd_args *argv);
static int cmd_test(int argc, const cmd_args *argv);
STATIC_COMMAND_START
{ "help", "this list", &cmd_help },
{ "test", "test the command processor", &cmd_test },
STATIC_COMMAND_END(help);
int console_init(void)
{
printf("console_init: entry\n");
/* add all the statically defined commands to the list */
cmd_block *block;
for (block = &__commands_start; block != &__commands_end; block++) {
console_register_commands(block);
}
return 0;
}
static const cmd *match_command(const char *command)
{
cmd_block *block;
size_t i;
for (block = command_list; block != NULL; block = block->next) {
const cmd *curr_cmd = block->list;
for (i = 0; i < block->count; i++) {
if (strcmp(command, curr_cmd[i].cmd_str) == 0) {
return &curr_cmd[i];
}
}
}
return NULL;
}
static int read_line(char *buffer, int len)
{
int pos = 0;
int escape_level = 0;
for (;;) {
char c;
/* loop until we get a char */
if (dgetc(&c) < 0)
continue;
// printf("c = 0x%hhx\n", c);
if (escape_level == 0) {
switch (c) {
case '\r':
case '\n':
dputc(c);
goto done;
case 0x7f: // backspace or delete
case 0x8:
if (pos > 0) {
pos--;
dputs("\x1b[1D"); // move to the left one
dputc(' ');
dputs("\x1b[1D"); // move to the left one
}
break;
case 0x1b: // escape
escape_level++;
break;
default:
buffer[pos++] = c;
dputc(c);
}
} else if (escape_level == 1) {
// inside an escape, look for '['
if (c == '[') {
escape_level++;
} else {
// we didn't get it, abort
escape_level = 0;
}
} else { // escape_level > 1
switch (c) {
case 67: // right arrow
buffer[pos++] = ' ';
dputc(' ');
break;
case 68: // left arrow
if (pos > 0) {
pos--;
dputs("\x1b[1D"); // move to the left one
dputc(' ');
dputs("\x1b[1D"); // move to the left one
}
break;
case 65: // up arrow
case 66: // down arrow
// XXX do history here
break;
default:
break;
}
escape_level = 0;
}
/* end of line. */
if (pos == (len - 1)) {
dputs("\nerror: line too long\n");
pos = 0;
goto done;
}
}
done:
// printf("returning pos %d\n", pos);
buffer[pos] = 0;
return pos;
}
static int tokenize_command(char *buffer, cmd_args *args, int arg_count)
{
int pos;
int arg;
bool finished;
enum {
INITIAL = 0,
IN_SPACE,
IN_TOKEN
} state;
pos = 0;
arg = 0;
state = INITIAL;
finished = false;
for (;;) {
char c = buffer[pos];
if (c == '\0')
finished = true;
// printf("c 0x%hhx state %d arg %d pos %d\n", c, state, arg, pos);
switch (state) {
case INITIAL:
if (isspace(c)) {
state = IN_SPACE;
} else {
state = IN_TOKEN;
args[arg].str = &buffer[pos];
}
break;
case IN_TOKEN:
if (finished) {
arg++;
goto done;
}
if (isspace(c)) {
arg++;
buffer[pos] = 0;
/* are we out of tokens? */
if (arg == arg_count)
goto done;
state = IN_SPACE;
}
pos++;
break;
case IN_SPACE:
if (finished)
goto done;
if (!isspace(c)) {
state = IN_TOKEN;
args[arg].str = &buffer[pos];
}
pos++;
break;
}
}
done:
return arg;
}
static void convert_args(int argc, cmd_args *argv)
{
int i;
for (i = 0; i < argc; i++) {
argv[i].u = atoui(argv[i].str);
argv[i].i = atoi(argv[i].str);
}
}
static void console_loop(void)
{
cmd_args args[16];
char buffer[256];
printf("entering main console loop\n");
for (;;) {
dputs("] ");
int len = read_line(buffer, sizeof(buffer));
if (len == 0)
continue;
// printf("line = '%s'\n", buffer);
/* tokenize the line */
int argc = tokenize_command(buffer, args, 16);
if (argc < 0) {
printf("syntax error\n");
continue;
} else if (argc == 0) {
continue;
}
// printf("after tokenize: argc %d\n", argc);
// for (int i = 0; i < argc; i++)
// printf("%d: '%s'\n", i, args[i].str);
/* convert the args */
convert_args(argc, args);
/* try to match the command */
const cmd *command = match_command(args[0].str);
if (!command) {
printf("command not found\n");
continue;
}
int result = command->cmd_callback(argc, args);
// XXX do something with the result
}
}
void console_start(void)
{
console_loop();
}
int console_run_command(const char *string)
{
const cmd *command;
ASSERT(string != NULL);
command = match_command(string);
if (!command)
return -1;
int result = command->cmd_callback(0, NULL);
return result;
}
void console_register_commands(cmd_block *block)
{
ASSERT(block);
ASSERT(block->next == NULL);
block->next = command_list;
command_list = block;
}
static int cmd_help(int argc, const cmd_args *argv)
{
printf("command list:\n");
cmd_block *block;
size_t i;
for (block = command_list; block != NULL; block = block->next) {
const cmd *curr_cmd = block->list;
for (i = 0; i < block->count; i++) {
printf("\t%-16s: %s\n", curr_cmd[i].cmd_str, curr_cmd[i].help_str ? curr_cmd[i].help_str : "");
}
}
return 0;
}
static int cmd_test(int argc, const cmd_args *argv)
{
int i;
printf("argc %d, argv %p\n", argc, argv);
for (i = 0; i < argc; i++)
printf("\t%d: str '%s', i %d, u %#x\n", i, argv[i].str, argv[i].i, argv[i].u);
return 0;
}