1
0
mirror of git://projects.qi-hardware.com/xburst-tools.git synced 2025-03-14 22:09:11 +02:00
xburst-tools/shell.c

319 lines
6.0 KiB
C
Raw Normal View History

2010-12-04 04:14:23 +03:00
/*
* JzBoot: an USB bootloader for JZ series of Ingenic(R) microprocessors.
* Copyright (C) 2010 Sergey Gridassov <grindars@gmail.com>,
* Peter Zotov <whitequark@whitequark.org>
2010-12-04 04:14:23 +03:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdio.h>
#ifdef WITH_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif
#include <stdlib.h>
2010-12-04 05:07:30 +03:00
#include <unistd.h>
2010-12-04 04:14:23 +03:00
#include <errno.h>
2010-12-10 12:43:54 +03:00
#include "shell_internal.h"
2010-12-04 04:14:23 +03:00
#include "debug.h"
#include "ingenic.h"
static void shell_update_cmdset(void *arg);
static const ingenic_callbacks_t shell_callbacks = {
shell_update_cmdset,
};
2010-12-10 12:43:54 +03:00
static const struct {
int set;
const char *name;
const shell_command_t *commands;
} cmdsets[] = {
{ CMDSET_SPL, "SPL", spl_cmdset },
{ CMDSET_USBBOOT, "USBBoot", usbboot_cmdset },
{ 0, NULL, NULL }
};
2010-12-04 04:14:23 +03:00
2010-12-10 12:43:54 +03:00
shell_context_t *shell_init(void *ingenic) {
2010-12-04 04:14:23 +03:00
#ifdef WITH_READLINE
rl_initialize();
#endif
debug(LEVEL_DEBUG, "Initializing shell\n");
2010-12-10 12:43:54 +03:00
shell_context_t *ctx = malloc(sizeof(shell_context_t));
memset(ctx, 0, sizeof(shell_context_t));
ctx->device = ingenic;
ingenic_set_callbacks(ingenic, &shell_callbacks, ctx);
shell_update_cmdset(ctx);
return ctx;
}
int shell_enumerate_commands(shell_context_t *ctx, int (*callback)(shell_context_t *ctx, const shell_command_t *cmd, void *arg), void *arg) {
for(int i = 0; builtin_cmdset[i].cmd != NULL; i++) {
int ret = callback(ctx, builtin_cmdset + i, arg);
if(ret != 0)
return ret;
}
2010-12-04 04:14:23 +03:00
2010-12-10 12:43:54 +03:00
if(ctx->set_cmds)
for(int i = 0; ctx->set_cmds[i].cmd != NULL; i++) {
int ret = callback(ctx, ctx->set_cmds + i, arg);
2010-12-04 04:14:23 +03:00
2010-12-10 12:43:54 +03:00
if(ret != 0)
return ret;
}
2010-12-04 04:14:23 +03:00
return 0;
}
2010-12-10 12:43:54 +03:00
static int shell_run_function(shell_context_t *ctx, const shell_command_t *cmd, void *arg) {
shell_run_data_t *data = arg;
2010-12-04 04:14:23 +03:00
2010-12-10 12:43:54 +03:00
if(strcmp(cmd->cmd, data->argv[0]) == 0) {
int ret = cmd->handler(ctx, data->argc, data->argv);
2010-12-04 04:14:23 +03:00
2010-12-10 12:43:54 +03:00
if(ret == 0)
return 1;
else
return ret;
} else
return 0;
}
int shell_run(shell_context_t *ctx, int argc, char *argv[]) {
shell_run_data_t data = { argc, argv };
int ret = shell_enumerate_commands(ctx, shell_run_function, &data);
2010-12-04 04:14:23 +03:00
2010-12-10 12:43:54 +03:00
if(ret != 0) {
debug(LEVEL_ERROR, "Bad command '%s'\n", argv[0]);
2010-12-04 04:14:23 +03:00
2010-12-10 12:43:54 +03:00
errno = EINVAL;
return -1;
} else if(ret == 1) {
return 0;
} else
return ret;
2010-12-04 04:14:23 +03:00
}
2010-12-10 12:43:54 +03:00
int shell_execute(shell_context_t *ctx, const char *cmd) {
yyscan_t scanner;
if(yylex_init_extra(ctx, &scanner) == -1)
return -1;
ctx->line = strdup(cmd);
char *ptr = ctx->line;
2010-12-04 04:14:23 +03:00
int token;
int state = STATE_WANTSTR;
int argc = 0;
char **argv = NULL;
2010-12-10 12:43:54 +03:00
int fret = -1;
2010-12-04 04:14:23 +03:00
do {
int noway = 0;
2010-12-10 12:43:54 +03:00
token = yylex(&scanner);
2010-12-04 04:14:23 +03:00
if((token == TOK_SEPARATOR || token == TOK_COMMENT || token == 0)) {
if(argc > 0) {
2010-12-10 12:43:54 +03:00
int ret = shell_run(ctx, argc, argv);
2010-12-04 04:14:23 +03:00
for(int i = 0; i < argc; i++) {
free(argv[i]);
}
free(argv);
argv = NULL;
argc = 0;
if(ret == -1) {
2010-12-10 12:43:54 +03:00
fret = -1;
2010-12-04 04:14:23 +03:00
2010-12-10 12:43:54 +03:00
break;
2010-12-04 04:14:23 +03:00
}
}
state = STATE_WANTSTR;
} else {
switch(state) {
case STATE_WANTSTR:
if(token == TOK_SPACE) {
state = STATE_WANTSTR;
} else if(token == TOK_STRING) {
int oargc = argc++;
argv = realloc(argv, sizeof(char *) * argc);
2010-12-10 12:43:54 +03:00
argv[oargc] = ctx->strval;
2010-12-04 04:14:23 +03:00
state = STATE_WANTSPACE;
} else {
noway = 1;
}
break;
case STATE_WANTSPACE:
if(token == TOK_STRING) {
2010-12-10 12:43:54 +03:00
free(ctx->strval);
2010-12-04 04:14:23 +03:00
noway = 1;
} else if(token == TOK_SPACE) {
state = STATE_WANTSTR;
} else
noway = 1;
}
if(noway) {
debug(LEVEL_ERROR, "No way from state %d by token %d\n", state, token);
for(int i = 0; i < argc; i++)
free(argv[i]);
free(argv);
2010-12-10 12:43:54 +03:00
fret = -1;
break;
2010-12-04 04:14:23 +03:00
}
}
} while(token && token != TOK_COMMENT);
free(ptr);
2010-12-10 12:43:54 +03:00
yylex_destroy(&scanner);
return fret;
2010-12-04 04:14:23 +03:00
}
2010-12-10 12:43:54 +03:00
int shell_pull(shell_context_t *ctx, char *buf, int maxlen) {
size_t len = strlen(ctx->line);
2010-12-04 04:14:23 +03:00
if(len < maxlen)
maxlen = len;
2010-12-10 12:43:54 +03:00
memcpy(buf, ctx->line, maxlen);
2010-12-04 04:14:23 +03:00
2010-12-10 12:43:54 +03:00
ctx->line += maxlen;
2010-12-04 04:14:23 +03:00
return maxlen;
}
2010-12-10 12:43:54 +03:00
void shell_fini(shell_context_t *ctx) {
free(ctx);
2010-12-04 04:14:23 +03:00
}
2010-12-10 12:43:54 +03:00
int shell_source(shell_context_t *ctx, const char *filename) {
ctx->shell_exit = 0;
2010-12-04 04:14:23 +03:00
FILE *file = fopen(filename, "r");
if(file == NULL) {
return -1;
}
char *line;
2010-12-10 12:43:54 +03:00
while((line = fgets(ctx->linebuf, sizeof(ctx->linebuf), file)) && !ctx->shell_exit) {
if(shell_execute(ctx, line) == -1) {
2010-12-04 04:14:23 +03:00
fclose(file);
return -1;
}
}
fclose(file);
return 0;
}
2010-12-10 12:43:54 +03:00
void shell_interactive(shell_context_t *ctx) {
ctx->shell_exit = 0;
2010-12-04 04:14:23 +03:00
#ifndef WITH_READLINE
char *line;
2010-12-10 12:43:54 +03:00
while(!ctx->shell_exit) {
2010-12-04 04:14:23 +03:00
fputs("jzboot> ", stdout);
fflush(stdout);
2010-12-10 12:43:54 +03:00
line = fgets(ctx->linebuf, sizeof(ctx->linebuf), stdin);
2010-12-04 04:14:23 +03:00
if(line == NULL)
break;
2010-12-10 12:43:54 +03:00
shell_execute(ctx, line);
2010-12-04 04:14:23 +03:00
}
#else
rl_set_signals();
2010-12-10 12:43:54 +03:00
while(!ctx->shell_exit) {
2010-12-04 04:14:23 +03:00
char *line = readline("jzboot> ");
if(line == NULL) {
break;
}
add_history(line);
2010-12-10 12:43:54 +03:00
shell_execute(ctx, line);
2010-12-04 04:14:23 +03:00
free(line);
}
rl_clear_signals();
#endif
}
static void shell_update_cmdset(void *arg) {
2010-12-10 12:43:54 +03:00
shell_context_t *ctx = arg;
ctx->set_cmds = NULL;
2010-12-04 04:14:23 +03:00
2010-12-10 12:43:54 +03:00
int set = ingenic_cmdset(ctx->device);
2010-12-04 04:14:23 +03:00
for(int i = 0; cmdsets[i].name != NULL; i++) {
if(cmdsets[i].set == set) {
2010-12-10 12:43:54 +03:00
printf("Shell: using command set '%s', run 'help' for command list. CPU: %04X\n", cmdsets[i].name, ingenic_type(ctx->device));
2010-12-04 04:14:23 +03:00
2010-12-10 12:43:54 +03:00
ctx->set_cmds = cmdsets[i].commands;
2010-12-04 04:14:23 +03:00
return;
}
}
debug(LEVEL_ERROR, "Shell: unknown cmdset %d\n", set);
}
2010-12-10 12:43:54 +03:00
void *shell_device(shell_context_t *ctx) {
return ctx->device;
2010-12-04 04:14:23 +03:00
}
2010-12-10 12:43:54 +03:00
void shell_exit(shell_context_t *ctx, int val) {
ctx->shell_exit = val;
}