#include #include #include #include #include #include #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 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) { if (!opt) goto syntax_error; break; } 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; } static int cmd_boot(int fd, const struct command *cmd, const struct param *param, int argc, char **args) { int err; uint8_t bank; uint16_t address; if ((err = parse_args(cmd, argc, args, &bank, &address) < 0)) return err; return z_boot(fd, bank, address); } 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]; uint8_t bank; uint16_t address; uint16_t length; const char *pathname = NULL; err = parse_args(cmd, argc, args, &bank, &address, &length, &pathname); if (err < 0) return -1; if ((err = z_read(fd, bank, address, length, buf))) { print_error(err); return err; } if (param->human_readable) { hexdump(address, length, buf); } else if (pathname) { FILE *fp; if (!(fp = fopen(pathname, "w"))) { perror("File could not be opened"); return -1; } fwrite(buf, sizeof(uint8_t), length, fp); fclose(fp); } else { fwrite(buf, sizeof(uint8_t), length, stdout); fflush(stdout); } return 0; } static int cmd_write(int fd, const struct command *cmd, 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; const char *pathname = NULL; err = parse_args(cmd, argc, args, &bank, &address, &pathname); if (err < 0) return -1; if (param->human_readable) { 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; } } 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); } return err; } 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; } 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; 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 command *cmd, const struct param *param, int argc, char **args) { return 0; } static int 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_line(int fd, struct param *param, char *line, const char *sep) { 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(stdout, "Unknown command %s\n", args[0]); return -1; } if ((p = cmd_table[cmdi].fptr) && (err = p(fd, &cmd_table[cmdi], param, argc - 1, &args[1]))) { print_error(err); return err; } 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 } }