diff options
-rw-r--r-- | main.c | 189 |
1 files changed, 189 insertions, 0 deletions
@@ -0,0 +1,189 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <inttypes.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> +#include <errno.h> + +#define ZXDUMP_STRIDE_LINE 16 +#define ZXDUMP_STRIDE_GROUP 2 +#define ZXDUMP_CHARSET_LEN 64 + +static uint16_t charset[ZXDUMP_CHARSET_LEN] = { + 0x0020, 0x2598, 0x259d, 0x2580, 0x2596, 0x258c, 0x259e, 0x259b, + 0x2592, '.', '.', '"', 0x00a3, '$', ':', '?', + '(', ')', '>', '<', '=', '+', '-', '*', + '/', ';', ',', '.', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', +}; + +static void usage(int argc, char **argv, char *message, ...) { + if (message != NULL) { + va_list args; + + va_start(args, message); + vfprintf(stderr, message, args); + fputc('\n', stderr); + va_end(args); + } + + fprintf(stderr, "usage: %s file\n", argv[0]); + exit(1); +} + +static ssize_t dump_line(off_t offset, void *buf, size_t len) { + size_t i; + + if (printf("%08zx: ", offset) < 0) { + goto error_io; + } + + for (i=0; i<ZXDUMP_STRIDE_LINE; i++) { + if (i > 0 && (i % ZXDUMP_STRIDE_GROUP) == 0) { + if (putchar(' ') < 0) { + goto error_io; + } + } + + if (i < len) { + if (printf("%02x", ((uint8_t *)buf)[offset+i]) < 0) { + goto error_io; + } + } else { + if (printf(" ") < 0) { + goto error_io; + } + } + } + + if (printf(" ") < 0) { + goto error_io; + } + + for (i=0; i<ZXDUMP_STRIDE_LINE; i++) { + uint8_t c = ((uint8_t *)buf)[offset+i]; + + if (c <= ZXDUMP_CHARSET_LEN) { + uint16_t printable = charset[c]; + + /* XXX: Implement support for inverse colours */ + if (printable & 0xf800) { + uint8_t sequence[2] = { + 0xc0 | ((printable & 0x1f00) >> 8), + 0x80 | (printable & 0x003f) + }; + + if (fwrite(sequence, 2, 1, stdout) < 1) { + goto error_io; + } + } else { + if (putchar((uint8_t)(printable & 0x00ff)) < 0) { + goto error_io; + } + } + } else { + if (putchar('.') < 0) { + goto error_io; + } + } + } + + if (putchar('\n') < 0) { + goto error_io; + } + + return fflush(stdout); + +error_io: + return -1; +} + +static ssize_t dump_fd(int fd) { + void *buf; + ssize_t offset = 0; + struct stat st; + + if (fstat(fd, &st) < 0) { + goto error_fstat; + } + + if ((buf = malloc(st.st_blksize)) == NULL) { + goto error_malloc; + } + + while (1) { + ssize_t len, i; + + if ((len = read(fd, buf, st.st_blksize)) < 0) { + goto error_read; + } else if (len == 0) { + break; + } + + for (i=0; i<len; i+=ZXDUMP_STRIDE_LINE) { + size_t left = len - i, + linesz = left < ZXDUMP_STRIDE_LINE? left: ZXDUMP_STRIDE_LINE; + + if (dump_line(offset, buf, linesz) < 0) { + goto error_dump_line; + } + + offset += linesz; + } + } + + free(buf); + + return offset; + +error_dump_line: +error_read: + free(buf); + +error_malloc: +error_fstat: + return -1; +} + +int main(int argc, char **argv) { + int fd; + + if (argc == 1) { + fd = fileno(stdin); + } else if (argc == 2) { + if ((fd = open(argv[1], O_RDONLY)) < 0) { + goto error_open; + } + } else { + usage(argc, argv, "Too many arguments provided"); + } + + if (dump_fd(fd) < 0) { + fprintf(stderr, "%s: %s: %s\n", + argv[0], + (argc == 2)? argv[1]: "(stdin)", + strerror(errno)); + + goto error_dump_fd; + } + + if (argc == 2) { + close(fd); + } + + return 0; + +error_dump_fd: + if (argc == 2) { + close(fd); + } + +error_open: + return 1; +} |