#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 } }