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 --- Makefile | 2 +- cmd.c | 468 +++++++++++++++++++++++++++++++++++++++++++++++---------------- repl.c | 356 ------------------------------------------------ zup.c | 15 +- zup.h | 3 + 5 files changed, 365 insertions(+), 479 deletions(-) delete mode 100644 repl.c diff --git a/Makefile b/Makefile index 8e1c0dd..38084b9 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ TARGET = zup PREFIX = /usr -SRC = crc16.c serial.c repl.c cmd.c zup.c +SRC = crc16.c serial.c cmd.c zup.c OBJ = $(SRC:%.c=%.o) CFLAGS = -Werror -Wpedantic -I. -g -DUSE_READLINE 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 + } } diff --git a/repl.c b/repl.c deleted file mode 100644 index edac70f..0000000 --- a/repl.c +++ /dev/null @@ -1,356 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -#ifdef USE_READLINE -#include -#include -#endif // USE_READLINE - -typedef int (*repl_command)(int, const struct param*, int, char**); - -static int -repl_read(int fd, const struct param *param, int argc, char **args) -{ - int err = 0; - uint8_t buf[MAX_PACKET_SIZE]; - - uint8_t bank; - uint16_t address; - uint16_t length; - const char *pathname = NULL; - - if (argc != 3 && argc != 4) - goto syntax_error; - - errno = 0; - bank = strtoul(args[0], NULL, 16); - err |= errno; - - errno = 0; - address = strtoul(args[1], NULL, 16); - err |= errno; - - errno = 0; - length = strtoul(args[2], NULL, 16); - err |= errno; - - if (argc == 4) - pathname = args[3]; - - if (err) - goto syntax_error; - - if ((err = z_read(fd, bank, address, length, buf))) { - print_error(err); - return err; - } - - hexdump(address, length, buf); - - if (pathname) { - FILE *file; - if (!(file = fopen(pathname, "w"))) { - perror("File could not be opened"); - return -1; - } - fwrite(buf, sizeof(uint8_t), length, file); - fclose(file); - } - - return err; - -syntax_error: - fputs("Usage: read
[pathname]\n", stderr); - return 1; -} - -static int -repl_write(int fd, const struct param *param, int argc, char **args) -{ - int err; - char line[512]; - uint8_t buf[MAX_PACKET_SIZE]; - - char *p; - - uint8_t bank; - uint16_t address; - uint16_t length = 0; - - do { - // TODO: Handle fgets error - fgets(line, LEN(line), stdin); - if (line && !strcmp("\n", line)) - break; - - p = strtok(line, " \n"); - while (p && length < 10) { - errno = 0; - buf[length++] = strtoul(p, NULL, 16); - p = strtok(NULL, " \n"); - } - - if (p) { - fputs("WRITE: Input can't be longer than 256 bytes\n", stderr); - return -1; - } - } while (1); - - if ((err = z_write(fd, bank, address, length, buf))) { - print_error(err); - return err; - } - - return 0; -} - -static int -repl_echo(int fd, const struct param *param, int argc, char **args) -{ - int err; - char line[512]; - char buf[MAX_PACKET_SIZE]; - - 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; - - 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; - } - - fwrite(buf, sizeof(uint8_t), length, stdout); - putchar('\n'); - fflush(stdout); - - return 0; -} - -static int -repl_io_write(int fd, 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); -} -*/ - -enum args_type { - A_END, - A_BYTE, - A_DBYTE, - A_QBYTE, - A_OBYTE, - A_STRING, - A_OPTIONAL = 16 -}; - -struct command; - -typedef int (*fptr)(int, const struct command *, struct param *, int, char **); - -struct command { - char alias; - const char *name; - int *args_spec; - const char *usage; - fptr fptr; - int quit; -}; - -static int -parse_args(const struct command *cmd, int argc, char **args, ...) -{ - int i = 0; - int opt = 0; - int err = 0; - 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 (i == argc && !opt) - goto syntax_error; - - ptr = va_arg(ap, void *); - errno = 0; - char *end = NULL; - - switch (*p) { - case A_BYTE: - *((uint8_t *)ptr) = strtoul(args[i], &end, 16); - break; - - case A_DBYTE: - *((uint16_t *)ptr) = strtoul(args[i], &end, 16); - break; - - 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; -} - -#define ARGS(...) (int []){__VA_ARGS__} - -static int -repl_io_read(int fd, const struct param *param, int argc, char **args) -{ - uint8_t bank; - uint16_t address; - const char *filename = NULL; - - struct command cmd = { - .alias = 'i', - .name = "io_read", - .usage = "", - .fptr = NULL, - .quit = 0, - .args_spec = ARGS(A_BYTE, A_DBYTE, A_OPTIONAL, A_STRING, A_END), - .usage = "Usage: read
[pathname]\n" - }; - - parse_args(&cmd, argc, args, &bank, &address, &filename); - printf("result: %hhx %hx %s\n", bank, address, filename); - return 0; -} - -// TODO: Handle signals (CTRL-C) -// TODO: Print errors when in repl and crash if from terminal -void -repl(int fd, struct param *param) -{ - struct command { - const char *name; - repl_command fptr; - int quit; - }; - - const struct command cmd_table[] = { - {"read", repl_read, 0}, - {"write", repl_write, 0}, - {"echo", repl_echo, 0}, - {"io_read", repl_io_read, 0}, - {"io_write", repl_io_write, 0}, - {"quit", NULL, 1} - }; - - int quit = 0; - -#ifdef USE_READLINE - using_history(); -#endif - - while (!quit) { -#ifdef USE_READLINE - char *line = readline("* "); - - if (!line) - break; - - add_history(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'; -#endif - int argc = 1; - char *args[MAX_ARGS] = { NULL }; - - // Parse command - args[0] = strtok(line, " "); - if (!args[0]) - goto skip; - 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; - } - - // Call action - if (cmdi == LEN(cmd_table)) { - fprintf(stdout, "Unknown command %s\n", args[0]); - } else { - repl_command fptr = cmd_table[cmdi].fptr; - if (fptr) - fptr(fd, param, argc - 1, &args[1]); - quit = cmd_table[cmdi].quit; - } - skip: -#ifdef USE_READLINE - free(line); -#endif - } -} diff --git a/zup.c b/zup.c index 1647bb3..9dff1c0 100644 --- a/zup.c +++ b/zup.c @@ -234,10 +234,19 @@ main(int argc, char *argv[]) int fd = open_tty(param.port, param.baud); - if (ind == argc || param.repl) + if (ind == argc || param.repl) { repl(fd, ¶m); - else - suc = run_commands(fd, ¶m, argc - ind, &argv[ind]); + } else { + argc -= ind; + argv = &argv[ind]; + + int err = 0; + for (int i = 0; i < argc && !err; ++i) + err = run_line(fd, ¶m, argv[i], ":"); + + if (err) + suc = EXIT_FAILURE; + } close(fd); exit(EXIT_SUCCESS); diff --git a/zup.h b/zup.h index cce84ed..7631e93 100644 --- a/zup.h +++ b/zup.h @@ -107,6 +107,9 @@ open_tty(const char *port, int baud); int run_commands(int fd, const struct param *param, int ncmd, char **cmds); +int +run_line(int fd, struct param *param, char *line, const char *sep); + void repl(int fd, struct param *param); -- cgit v1.2.3