summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--main.c189
1 files changed, 189 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..9d762b5
--- /dev/null
+++ b/main.c
@@ -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;
+}