From 30125839213e5b81e87c4fa3d7d3f4030b3659e3 Mon Sep 17 00:00:00 2001 From: Thomas Albers Date: Sun, 6 Aug 2023 16:04:59 +0200 Subject: Merge repl.c and cmd.c commands --- cmd.c | 468 +++++++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 349 insertions(+), 119 deletions(-) (limited to 'cmd.c') diff --git a/cmd.c b/cmd.c index c4a7d20..aa8beff 100644 --- a/cmd.c +++ b/cmd.c @@ -3,199 +3,429 @@ #include #include #include +#include #include -typedef int (*cmd_command)(int, const struct param*, int, char**); - +#ifdef USE_READLINE +#include +#include +#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
\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
[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
[pathname]\n" + }, + { + .alias = 'i', + .name = "io_read", + .fptr = NULL, + .quit = 0, + .args_spec = ARGS(A_DBYTE, A_DBYTE, A_END), + .usage = "Usage: io_read
\n" + }, + { + .alias = 'o', + .name = "io_write", + .fptr = NULL, + .quit = 0, + .args_spec = ARGS(A_DBYTE, A_END), + .usage = "Usage: io_write
\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 + } } -- cgit v1.2.3