summaryrefslogtreecommitdiff
path: root/cmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmd.c')
-rw-r--r--cmd.c468
1 files changed, 349 insertions, 119 deletions
diff --git a/cmd.c b/cmd.c
index c4a7d20..aa8beff 100644
--- a/cmd.c
+++ b/cmd.c
@@ -3,199 +3,429 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <signal.h>
#include <errno.h>
-typedef int (*cmd_command)(int, const struct param*, int, char**);
-
+#ifdef USE_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif // USE_READLINE
+
+struct command;
+
+enum args_type {
+ A_END,
+ A_BYTE,
+ A_DBYTE,
+ A_QBYTE,
+ A_OBYTE,
+ A_STRING,
+ A_OPTIONAL = 16
+};
+
+typedef int (*fptr)(int, const struct command *, const struct param *,
+ int, char **);
+
+struct command {
+ char alias;
+ const char *name;
+ int *args_spec;
+ const char *usage;
+ fptr fptr;
+ int quit;
+};
+
+#define ARGS(...) (int []){__VA_ARGS__}
+
+// FIXME: Assert ranges for integers
static int
-cmd_boot(int fd, const struct param *param, int argc, char **args)
+parse_args(const struct command *cmd, int argc, char **args, ...)
{
+ int i = 0;
+ int opt = 0;
int err = 0;
- uint8_t bank;
- uint16_t address;
+ va_list ap;
+ va_start(ap, args);
+
+ void *ptr = NULL;
+ int *p = cmd->args_spec;
+ for ( ; *p != A_END; ++p) {
+ // Avoid runaway p
+ assert(i < MAX_ARGS);
+
+ if (*p == A_OPTIONAL) {
+ opt = 1;
+ continue;
+ }
- if (argc != 2) {
- fputs("BOOT expects 2 arguments\n", stderr);
- return 1;
- }
+ if (i == argc) {
+ if (!opt)
+ goto syntax_error;
+ break;
+ }
- errno = 0;
- bank = strtoul(args[0], NULL, 16);
- if (errno)
- goto error;
+ ptr = va_arg(ap, void *);
+ errno = 0;
+ char *end = NULL;
- address = strtoul(args[1], NULL, 16);
- if (errno)
- goto error;
+ switch (*p) {
+ case A_BYTE:
+ *((uint8_t *)ptr) = strtoul(args[i], &end, 16);
+ break;
- return z_boot(fd, bank, address);
+ case A_DBYTE:
+ *((uint16_t *)ptr) = strtoul(args[i], &end, 16);
+ break;
-error:
- perror("BOOT: Invalid arguments\n");
+ case A_STRING:
+ *((const char **)ptr) = args[i];
+ break;
+ }
+
+ if (errno || args[i] == end) {
+ fprintf(stderr, "Invalid argument: %s\n", args[i]);
+ goto syntax_error;
+ }
+
+ ++i;
+ }
+
+ va_end(ap);
+ return i;
+
+syntax_error:
+ va_end(ap);
+ fputs(cmd->usage, stderr);
return -1;
}
static int
-cmd_read(int fd, const struct param *param, int argc, char **args)
+cmd_boot(int fd, const struct command *cmd, const struct param *param,
+ int argc, char **args)
{
- int err = 0;
-
+ int err;
uint8_t bank;
uint16_t address;
- uint16_t length;
- const char *pathname;
- if (!((argc == 4 && !param->human_readable)
- || (argc == 3 && param->human_readable))) {
- fputs("READ expects 4 arguments\n", stderr);
- return 1;
- }
+ if ((err = parse_args(cmd, argc, args, &bank, &address) < 0))
+ return err;
- errno = 0;
- bank = strtoul(args[0], NULL, 16);
- err |= errno;
- errno = 0;
- address = strtoul(args[1], NULL, 16);
- err |= errno;
+ return z_boot(fd, bank, address);
+}
- errno = 0;
- length = strtoul(args[2], NULL, 16);
- err |= errno;
+static int
+cmd_read(int fd, const struct command *cmd, const struct param *param,
+ int argc, char **args)
+{
+ int err = 0;
+ uint8_t buf[MAX_PACKET_SIZE];
- pathname = args[3];
+ uint8_t bank;
+ uint16_t address;
+ uint16_t length;
+ const char *pathname = NULL;
- if (err) {
- fputs("READ: Invalid arguments\n", stderr);
+ err = parse_args(cmd, argc, args, &bank, &address, &length, &pathname);
+ if (err < 0)
return -1;
- }
- uint8_t buf[MAX_PACKET_SIZE];
- if ((err = z_read(fd, bank, address, length, buf)))
+ if ((err = z_read(fd, bank, address, length, buf))) {
+ print_error(err);
return err;
+ }
if (param->human_readable) {
hexdump(address, length, buf);
- } else {
- FILE *file;
- if (!(file = fopen(pathname, "w"))) {
+ } else if (pathname) {
+ FILE *fp;
+ if (!(fp = fopen(pathname, "w"))) {
perror("File could not be opened");
return -1;
}
- fwrite(buf, sizeof(uint8_t), length, file);
- fclose(file);
+ fwrite(buf, sizeof(uint8_t), length, fp);
+ fclose(fp);
+ } else {
+ fwrite(buf, sizeof(uint8_t), length, stdout);
+ fflush(stdout);
}
return 0;
}
-// TODO: Handle length 0 at protcol level
static int
-cmd_write(int fd, const struct param *param, int argc, char **args)
+cmd_write(int fd, const struct command *cmd, const struct param *param,
+ int argc, char **args)
{
- int err = 0;
+ int err;
+ char line[512];
+ uint8_t buf[MAX_PACKET_SIZE];
+
+ char *p;
uint8_t bank;
uint16_t address;
- uint16_t length;
- const char *pathname;
+ uint16_t length = 0;
+ const char *pathname = NULL;
- if (argc != 3) {
- fputs("WRITE expects 3 arguments\n", stderr);
+ err = parse_args(cmd, argc, args, &bank, &address, &pathname);
+ if (err < 0)
return -1;
- }
- errno = 0;
- bank = strtoul(args[0], NULL, 16);
- err |= errno;
+ if (param->human_readable) {
+ do {
+ // TODO: Handle fgets error
+ fgets(line, LEN(line), stdin);
+ if (line && !strcmp("\n", line))
+ break;
- errno = 0;
- address = strtoul(args[1], NULL, 16);
- err |= errno;
+ p = strtok(line, " \n");
+ while (p && length < 10) {
+ errno = 0;
+ buf[length++] = strtoul(p, NULL, 16);
+ p = strtok(NULL, " \n");
+ }
- pathname = args[2];
+ if (p) {
+ fputs("WRITE: Input can't be longer than 256 bytes\n", stderr);
+ return -1;
+ }
+ } while (1);
- if (err) {
- fputs("WRITE: Invalid arguments\n", stderr);
- return -1;
+ if ((err = z_write(fd, bank, address, length, buf))) {
+ print_error(err);
+ return err;
+ }
+ } else {
+ FILE *fp = pathname ? fopen(pathname, "r") : stdin;
+
+ if (!fp) {
+ perror("File could not be opened");
+ return -1;
+ }
+
+ // FIXME: fread MAX_PACKET_SIZE insted of 8
+ // Split file into packets
+ while ((length = fread(buf, sizeof(uint8_t), 8, fp))) {
+ if ((err = z_write(fd, bank, address, length, buf)))
+ break;
+ address += length;
+ }
+
+ if (fp != stdin)
+ fclose(fp);
}
- uint8_t buf[MAX_PACKET_SIZE];
- FILE *fp;
+ return err;
+}
- if (!(fp = fopen(pathname, "r"))) {
- perror("File could not be opened");
+static int
+cmd_echo(int fd, const struct command *cmd, const struct param *param,
+ int argc, char **args)
+{
+ int err;
+ char line[512];
+ char buf[MAX_PACKET_SIZE];
+
+ if (!param->human_readable) {
+ fputs("Echo is a interactive command only", stderr);
return -1;
}
- // Split file into packets
- while ((length = fread(buf, sizeof(uint8_t), 8, fp))) {
- if ((err = z_write(fd, bank, address, length, buf)))
+ parse_args(cmd, argc, args);
+
+ memset(line, 0, sizeof(line));
+ memset(buf, 0, sizeof(buf));
+
+ size_t length = 0;
+
+ do {
+ // TODO: Handle fgets error
+ fgets(line, LEN(line), stdin);
+ if (line && !strcmp("\n", line))
break;
- address += length;
+
+ size_t len = strlen(line);
+ if (line[len - 1] == '\n')
+ line[--len] = '\0';
+
+ if (length + len >= MAX_PACKET_SIZE) {
+ fputs("ECHO: Input can't be longer than 256 bytes\n", stderr);
+ return -1;
+ }
+
+ strcat(buf, line);
+ length += len;
+ } while (1);
+
+ if ((err = z_echo(fd, length, (uint8_t *)buf))) {
+ print_error(err);
+ return err;
}
- fclose(fp);
+ fwrite(buf, sizeof(uint8_t), length, stdout);
+ putchar('\n');
+ fflush(stdout);
- return err;
+ return 0;
}
static int
-cmd_io_read(int fd, const struct param *param, int argc, char **args)
+repl_io_write(int fd, const struct command *cmd, const struct param *param,
+ int argc, char **args)
{
return 0;
}
static int
-cmd_io_write(int fd, const struct param *param, int argc, char **args)
+repl_io_read(int fd, const struct command *cmd, const struct param *param,
+ int argc, char **args)
{
return 0;
}
+/*
+static volatile sig_atomic_t quit = 0;
+
+void
+interrupt_handler(int signal)
+{
+ puts("Bye!");
+ quit = 1;
+ // fclose(stdin);
+}
+*/
+
+const struct command cmd_table[] = {
+ {
+ .alias = 'b',
+ .name = "boot",
+ .fptr = cmd_boot,
+ .quit = 0,
+ .args_spec = ARGS(A_BYTE, A_DBYTE, A_END),
+ .usage = "Usage: boot <bank> <address>\n"
+ },
+ {
+ .alias = 'r',
+ .name = "read",
+ .fptr = cmd_read,
+ .quit = 0,
+ .args_spec = ARGS(A_BYTE, A_DBYTE, A_DBYTE, A_OPTIONAL, A_STRING, A_END),
+ .usage = "Usage: read <bank> <address> <length> [pathname]\n"
+ },
+ {
+ .alias = 'w',
+ .name = "write",
+ .fptr = cmd_write,
+ .quit = 0,
+ .args_spec = ARGS(A_BYTE, A_DBYTE, A_OPTIONAL, A_STRING, A_END),
+ .usage = "Usage: write <bank> <address> [pathname]\n"
+ },
+ {
+ .alias = 'i',
+ .name = "io_read",
+ .fptr = NULL,
+ .quit = 0,
+ .args_spec = ARGS(A_DBYTE, A_DBYTE, A_END),
+ .usage = "Usage: io_read <address> <length>\n"
+ },
+ {
+ .alias = 'o',
+ .name = "io_write",
+ .fptr = NULL,
+ .quit = 0,
+ .args_spec = ARGS(A_DBYTE, A_END),
+ .usage = "Usage: io_write <address>\n"
+ },
+ {
+ .alias = 'e',
+ .name = "echo",
+ .fptr = cmd_echo,
+ .quit = 0,
+ .args_spec = ARGS(A_END),
+ .usage = ""
+ },
+ {
+ .alias = 'q',
+ .name = "quit",
+ .fptr = NULL,
+ .quit = 1,
+ .args_spec = ARGS(A_END),
+ .usage = ""
+ }
+};
+
int
-run_commands(int fd, const struct param *param, int ncmd, char **cmds)
+run_line(int fd, struct param *param, char *line, const char *sep)
{
- struct command {
- const char *name;
- cmd_command fptr;
- };
-
- const struct command cmd_table[] = {
- {"b", cmd_boot},
- {"r", cmd_read},
- {"w", cmd_write},
- {"i", cmd_io_read},
- {"o", cmd_io_write}
- };
-
- for (int i = 0; i < ncmd; ++i) {
- int err;
- int argc = 1;
- char *args[MAX_ARGS] = { NULL };
-
- // Parse command
- args[0] = strtok(cmds[i], ":");
- while(argc < MAX_ARGS && (args[argc] = strtok(NULL, ":")))
- ++argc;
-
- // Match action
- int cmdi;
- for (cmdi = 0; cmdi < LEN(cmd_table); ++cmdi) {
- if (!strcmp(args[0], cmd_table[cmdi].name))
- break;
- }
+ int err;
+ int argc = 1;
+ char *args[MAX_ARGS + 1] = { NULL };
+ fptr p;
+
+ // Tokenize command
+ args[0] = strtok(line, sep);
+ if (!args[0])
+ return 0;
+ while(argc < LEN(args) && (args[argc] = strtok(NULL, sep)))
+ ++argc;
+
+ // Match action
+ int cmdi;
+ for (cmdi = 0; cmdi < LEN(cmd_table); ++cmdi) {
+ if ((!args[0][1] && args[0][0] == cmd_table[cmdi].alias)
+ || !strcmp(args[0], cmd_table[cmdi].name))
+ break;
+ }
- // Call action
- if (cmdi == LEN(cmd_table)) {
- fprintf(stderr, "Unknown command %s\n", args[0]);
- return 1;
- }
+ // Call action
+ if (cmdi == LEN(cmd_table)) {
+ fprintf(stdout, "Unknown command %s\n", args[0]);
+ return -1;
+ }
- if ((err = cmd_table[cmdi].fptr(fd, param, argc - 1, &args[1]))) {
- print_error(err);
- return err;
- }
+ if ((p = cmd_table[cmdi].fptr)
+ && (err = p(fd, &cmd_table[cmdi], param, argc - 1, &args[1]))) {
+ print_error(err);
+ return err;
}
- return 0;
+ return cmd_table[cmdi].quit;
+}
+
+// TODO: Handle signals (CTRL-C)
+void
+repl(int fd, struct param *param)
+{
+ int quit = 0;
+
+#ifdef USE_READLINE
+ using_history();
+#endif
+
+ while (!quit) {
+#ifdef USE_READLINE
+ char *line = readline("* ");
+
+ if (!line)
+ break;
+
+ add_history(line);
+ quit = run_line(fd, param, line, " ");
+ free(line);
+#else
+ size_t len = 0;
+ char line[512];
+ fputs("* ", stdout);
+
+ if (!fgets(line, sizeof(line), stdin))
+ break;
+
+ len = strlen(line);
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+
+ quit = run_line(fd, param, line, " ");
+#endif
+ }
}