diff options
-rw-r--r-- | Makefile | 25 | ||||
-rw-r--r-- | include/zx/basic.h | 35 | ||||
-rw-r--r-- | include/zx/charset.h | 40 | ||||
-rw-r--r-- | include/zx/hexdump.h | 12 | ||||
-rw-r--r-- | main.c | 475 | ||||
-rw-r--r-- | src/Makefile | 27 | ||||
-rw-r--r-- | src/basic.c | 163 | ||||
-rw-r--r-- | src/charset.c | 66 | ||||
-rw-r--r-- | src/hexdump.c | 109 | ||||
-rw-r--r-- | src/main.c | 99 |
10 files changed, 556 insertions, 495 deletions
@@ -1,23 +1,8 @@ -CC = cc -CFLAGS = -g -Wall -O2 -LDFLAGS = -OBJS = main.o -NAME = zxdump +all: + $(MAKE) -C src all -PREFIX = /usr/local -INSTALL = /usr/bin/install -RM = /bin/rm - -all: $(NAME) - -$(NAME): $(OBJS) - $(CC) $(OBJS) -o $(NAME) $(LDFLAGS) - -$(OBJS): %.o: %.c - $(CC) $(CFLAGS) -c $< - -install: $(NAME) - $(INSTALL) -c -m 0755 $(NAME) $(PREFIX)/bin +install: + $(MAKE) -C src install clean: - $(RM) -f $(NAME) $(OBJS) + $(MAKE) -C src clean diff --git a/include/zx/basic.h b/include/zx/basic.h new file mode 100644 index 0000000..1989954 --- /dev/null +++ b/include/zx/basic.h @@ -0,0 +1,35 @@ +#ifndef _ZX_BASIC_H +#define _ZX_BASIC_H + +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> + +#define ZX81_CHAR_TOKEN_LOW_START 0x40 +#define ZX81_CHAR_TOKEN_LOW_END 0x42 + +#define ZX81_CHAR_TOKEN_HIGH_START 0xc0 +#define ZX81_CHAR_TOKEN_HIGH_END 0xff + +#define ZX_BASIC_STATE_SIZE 116 +#define ZX_BASIC_LINE_LAST 0x7676 + +extern char *zx81_basic_tokens_low[3]; + +extern char *zx81_basic_tokens[64]; + +typedef struct _zx_basic_line { + uint16_t num, len; +} zx_basic_line; + +enum zx_basic_token_type { + ZX_BASIC_TOKEN_UNKNOWN, + ZX_BASIC_TOKEN_ALNUM, + ZX_BASIC_TOKEN_QUOTE, + ZX_BASIC_TOKEN_SYMBOL, + ZX_BASIC_TOKEN_WORD, +}; + +ssize_t zx81_basic_dump(int fd, FILE *stream); + +#endif /* _ZX_BASIC_H */ diff --git a/include/zx/charset.h b/include/zx/charset.h new file mode 100644 index 0000000..34f6eb8 --- /dev/null +++ b/include/zx/charset.h @@ -0,0 +1,40 @@ +#ifndef _ZX_CHARSET_H +#define _ZX_CHARSET_H + +#include <stdio.h> +#include <inttypes.h> + +#define ZX81_CHARSET_LEN 64 + +#define ZX81_CHAR_LOW(c) \ + (c <= ZX81_CHARSET_LEN) + +#define ZX81_CHAR_INVERSE_START 0x80 +#define ZX81_CHAR_INVERSE_END 0xbf + +#define ZX81_CHAR_INVERSE(c) \ + (c >= ZX81_CHAR_INVERSE_START && c <= ZX81_CHAR_INVERSE_END) + +#define ZX81_CHAR_TOKEN_LOW(c) \ + (c >= ZX81_CHAR_TOKEN_LOW_START && c <= ZX81_CHAR_TOKEN_LOW_END) + +#define ZX81_CHAR_NEWLINE(c) \ + (c == 0x76) + +#define ZX81_CHAR_TOKEN_HIGH(c) \ + (c >= 0xc0) + +#define ZX81_CHAR_TOKEN(c) \ + (ZX81_CHAR_TOKEN_LOW(c) || ZX81_CHAR_TOKEN_HIGH(c)) + +#define ZX81_CHAR_TOKEN_INTEGRAL(c) \ + (c == 0x0e) + +#define ZX81_CHAR_TOKEN_FLOAT(c) \ + (c == 0x7e) + +extern uint32_t zx81_charset[ZX81_CHARSET_LEN]; + +int zx81_fputc(uint8_t c, int inverse, FILE *stream); + +#endif /* _ZX_CHARSET_H */ diff --git a/include/zx/hexdump.h b/include/zx/hexdump.h new file mode 100644 index 0000000..5b38796 --- /dev/null +++ b/include/zx/hexdump.h @@ -0,0 +1,12 @@ +#ifndef _ZX81_HEXDUMP_H +#define _ZX81_HEXDUMP_H + +#include <stdio.h> +#include <sys/types.h> + +#define ZX81_HEXDUMP_STRIDE_LINE 16 +#define ZX81_HEXDUMP_STRIDE_GROUP 2 + +ssize_t zx81_hexdump(int source_fd, FILE *stream); + +#endif /* _ZX81_HEXDUMP_H */ @@ -1,475 +0,0 @@ -#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 <endian.h> -#include <errno.h> - -#define ZX_CHARSET_LEN 64 - -#define ZX_CHAR_LOW(c) \ - (c <= ZX_CHARSET_LEN) - -#define ZX_CHAR_INVERSE_START 0x80 -#define ZX_CHAR_INVERSE_END 0xbf - -#define ZX_CHAR_INVERSE(c) \ - (c >= ZX_CHAR_INVERSE_START && c <= ZX_CHAR_INVERSE_END) - -#define ZX_CHAR_TOKEN_LOW_START 0x40 -#define ZX_CHAR_TOKEN_LOW_END 0x42 - -#define ZX_CHAR_TOKEN_LOW(c) \ - (c >= ZX_CHAR_TOKEN_LOW_START && c <= ZX_CHAR_TOKEN_LOW_END) - -#define ZX_CHAR_NEWLINE(c) \ - (c == 0x76) - -#define ZX_CHAR_TOKEN_HIGH_START 0xc0 -#define ZX_CHAR_TOKEN_HIGH_END 0xff - -#define ZX_CHAR_TOKEN_HIGH(c) \ - (c >= 0xc0) - -#define ZX_CHAR_TOKEN(c) \ - (ZX_CHAR_TOKEN_LOW(c) || ZX_CHAR_TOKEN_HIGH(c)) - -#define ZX_CHAR_TOKEN_INTEGRAL(c) \ - (c == 0x0e) - -#define ZX_CHAR_TOKEN_FLOAT(c) \ - (c == 0x7e) - -typedef struct _zx_basic_line { - uint16_t num, len; -} zx_basic_line; - -enum zx_basic_token_type { - ZX_BASIC_TOKEN_UNKNOWN, - ZX_BASIC_TOKEN_ALNUM, - ZX_BASIC_TOKEN_QUOTE, - ZX_BASIC_TOKEN_SYMBOL, - ZX_BASIC_TOKEN_WORD, -}; - -#define ZX_BASIC_STATE_SIZE 116 -#define ZX_BASIC_LINE_LAST 0x7676 - -static uint32_t zx_charset[ZX_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 char *zx_tokens_low[3] = { - "RND", "INKEY$", "PI", -}; - -static char *zx_tokens[64] = { - "\"\"", "AT", "TAB", NULL, "CODE", "VAL", "LEN", "SIN", - "COS", "TAN", "ASN", "ACS", "ATN", "LN", "EXP", "INT", - "SQR", "SGN", "ABS", "PEEK", "USR", "STR$", "CHR$", "NOT", - "**", "OR", "AND", "<=", ">=", "<>", "THEN", "TO", - "STEP", "LPRINT", "LLIST", "STOP", "SLOW", "FAST", "NEW", "SCROLL", - "CONT", "DIM", "REM", "FOR", "GOTO", "GOSUB", "INPUT", "LOAD", - "LIST", "LET", "PAUSE", "NEXT", "POKE", "PRINT", "PLOT", "RUN", - "SAVE", "RAND", "IF", "CLS", "UNPLOT", "CLEAR", "RETURN", "COPY", -}; - -static inline size_t utf8_encode(uint8_t *buf, uint32_t codepoint) { - if ((codepoint & 0x007f) == codepoint) { - buf[0] = codepoint & 0x007f; - - return 1; - } else if ((codepoint & 0x07ff) == codepoint) { - buf[0] = 0xc0 | ((codepoint & 0x07c0) >> 6); - buf[1] = 0x80 | (codepoint & 0x003f); - - return 2; - } else if ((codepoint & 0xffff) == codepoint) { - buf[0] = 0xe0 | ((codepoint & 0xf000) >> 12); - buf[1] = 0x80 | ((codepoint & 0x0fc0) >> 6); - buf[2] = 0x80 | (codepoint & 0x003f); - - return 3; - } else { - buf[0] = 0xf0 | ((codepoint & 0x1c0000) >> 18); - buf[1] = 0x80 | ((codepoint & 0x03f000) >> 12); - buf[2] = 0x80 | ((codepoint & 0x000fc0) >> 6); - buf[3] = 0x80 | (codepoint & 0x00003f); - - return 4; - } -} - -static inline int zx_putchar(uint8_t c, int inverse) { - uint8_t sequence[4]; - size_t len = utf8_encode(sequence, zx_charset[c]); - - if (inverse) { - if (fputs("\033[7m", stdout) < 0) { - goto error_io; - } - } - - if (fwrite(sequence, len, 1, stdout) < 1) { - goto error_io; - } - - if (inverse) { - if (fputs("\033[27m", stdout) < 0) { - goto error_io; - } - } - - return 0; - -error_io: - return -1; -} - -#define ZX_HEXDUMP_STRIDE_LINE 16 -#define ZX_HEXDUMP_STRIDE_GROUP 2 - -static ssize_t hexdump_line(off_t offset, void *buf, size_t len, int tty) { - size_t i; - - if (printf("%08zx: ", offset) < 0) { - goto error_io; - } - - for (i=0; i<len; i++) { - if (i > 0 && (i % ZX_HEXDUMP_STRIDE_GROUP) == 0) { - if (putchar(' ') < 0) { - goto error_io; - } - } - - if (printf("%02x", ((uint8_t *)buf)[offset+i]) < 0) { - goto error_io; - } - } - - if (fputs(" ", stdout) < 0) { - goto error_io; - } - - for (i=0; i<len; i++) { - uint8_t c = ((uint8_t *)buf)[offset+i]; - - if (c <= 0x3f) { - if (zx_putchar(c, 0) < 0) { - goto error_io; - } - } else if (c >= 0xa0 && c <= 0xbf) { - if (zx_putchar(c - 0xa0, tty) < 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 zx_dump_hex(int fd) { - void *buf; - ssize_t total = 0; - int tty = isatty(fileno(stdout)); - 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; - off_t offset = 0; - - if ((len = read(fd, buf, st.st_blksize)) < 0) { - goto error_read; - } else if (len == 0) { - break; - } - - for (i=0; i<len; i+=ZX_HEXDUMP_STRIDE_LINE) { - size_t left = len - i, - linesz = left < ZX_HEXDUMP_STRIDE_LINE? left: ZX_HEXDUMP_STRIDE_LINE; - - if (hexdump_line(offset, buf, linesz, tty) < 0) { - goto error_hexdump_line; - } - - offset += linesz; - total += linesz; - } - } - - free(buf); - - return total; - -error_hexdump_line: -error_read: - free(buf); - -error_malloc: -error_fstat: - return -1; -} - -static inline int zx_print(uint8_t c, int tty) { - if (ZX_CHAR_LOW(c)) { - return zx_putchar(c, 0); - } else if (ZX_CHAR_TOKEN_LOW(c)) { - return fputs(zx_tokens_low[c - ZX_CHAR_TOKEN_LOW_START], stdout); - } else if (ZX_CHAR_NEWLINE(c)) { - return putchar('\n'); - } else if (ZX_CHAR_INVERSE(c)) { - return zx_putchar(c - ZX_CHAR_INVERSE_START, tty); - } else if (ZX_CHAR_TOKEN_HIGH(c)) { - return fputs(zx_tokens[c - ZX_CHAR_TOKEN_HIGH_START], stdout); - } - - return 0; -} - -static inline enum zx_basic_token_type zx_basic_token_type_get(uint8_t b) { - if (ZX_CHAR_LOW(b)) { - uint32_t codepoint = zx_charset[b]; - - if ((codepoint >= 'A' && codepoint <= 'Z') - || (codepoint >= '0' && codepoint <= '9')) { - return ZX_BASIC_TOKEN_ALNUM; - } else if (codepoint == '"') { - return ZX_BASIC_TOKEN_QUOTE; - } else { - return ZX_BASIC_TOKEN_SYMBOL; - } - } else if (ZX_CHAR_INVERSE(b)) { - return zx_basic_token_type_get(b - ZX_CHAR_INVERSE_START); - } else if (ZX_CHAR_TOKEN_LOW(b)) { - return ZX_BASIC_TOKEN_WORD; - } else if (ZX_CHAR_TOKEN_HIGH(b)) { - char *token = zx_tokens[b-ZX_CHAR_TOKEN_HIGH_START]; - - if (token[0] >= 'A' && token[0] <= 'Z') { - return ZX_BASIC_TOKEN_WORD; - } else { - return ZX_BASIC_TOKEN_SYMBOL; - } - } - - return ZX_BASIC_TOKEN_UNKNOWN; -} - -static ssize_t zx_dump_basic(int fd) { - void *buf; - ssize_t total = 0; - int tty = isatty(fileno(stdout)); - struct stat st; - - if (fstat(fd, &st) < 0) { - goto error_fstat; - } - - if ((buf = malloc(st.st_blksize)) == NULL) { - goto error_malloc; - } - - if (lseek(fd, ZX_BASIC_STATE_SIZE, SEEK_CUR) < 0) { - goto error_io; - } - - while (1) { - ssize_t readlen, len, i; - zx_basic_line line; - uint8_t last = 0xc0; - - if ((readlen = read(fd, &line, sizeof(line))) < 0) { - goto error_io; - } else if (readlen == 0) { - break; - } - - len = le16toh(line.len); - - if (be16toh(line.num) == len && len == ZX_BASIC_LINE_LAST) { - break; - } - - if (printf("%d ", (int)be16toh(line.num)) < 0) { - goto error_io; - } - - if (read(fd, buf, len) < 0) { - goto error_io; - } - - for (i=0; i<len; i++) { - uint8_t c = ((uint8_t *)buf)[i]; - - enum zx_basic_token_type type = zx_basic_token_type_get(c), - type_last = zx_basic_token_type_get(last); - - int space = 0; - - if (type == ZX_BASIC_TOKEN_ALNUM) { - if (type_last == ZX_BASIC_TOKEN_WORD) { - space = 1; - } - } else if (type == ZX_BASIC_TOKEN_QUOTE) { - if (type_last == ZX_BASIC_TOKEN_WORD) { - space = 1; - } - } else if (type == ZX_BASIC_TOKEN_SYMBOL) { - space = 0; - } else if (type == ZX_BASIC_TOKEN_WORD) { - if (type_last != ZX_BASIC_TOKEN_SYMBOL) { - space = 1; - } - } else if (ZX_CHAR_TOKEN_INTEGRAL(c) || ZX_CHAR_TOKEN_FLOAT(c)) { - i += 5; - } - - if (space && putchar(' ') < 0) { - goto error_io; - } - - if (zx_print(c, tty) < 0) { - goto error_io; - } - - last = c; - } - - if (!ZX_CHAR_NEWLINE(((uint8_t *)buf)[i-1])) { - if (putchar('\n') < 0) { - goto error_io; - } - } - } - - free(buf); - - return total; - -error_io: - free(buf); - -error_malloc: -error_fstat: - return -1; -} - -static void usage(const char *prog, 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 [-b] [file]\n", prog); - exit(1); -} - -#define ZX_DUMP_FLAGS_NONE 0 -#define ZX_DUMP_FLAGS_BASIC (1 << 0) - -int main(int argc, char **argv) { - int fd, - flags = ZX_DUMP_FLAGS_NONE, - argn; - - while (1) { - int c; - - if ((c = getopt(argc, argv, "b")) < 0) { - break; - } - - switch (c) { - case 'b': - flags |= ZX_DUMP_FLAGS_BASIC; - break; - - case '?': - default: - usage(argv[0], NULL); - } - } - - argn = argc - optind; - - if (argn == 0) { - fd = fileno(stdin); - } else if (argn == 1) { - if ((fd = open(argv[optind], O_RDONLY)) < 0) { - fprintf(stderr, "%s: %s: %s\n", argv[0], argv[optind], strerror(errno)); - - goto error_open; - } - } else { - usage(argv[0], "Too many arguments provided"); - } - - if (flags & ZX_DUMP_FLAGS_BASIC) { - if (zx_dump_basic(fd) < 0) { - fprintf(stderr, "%s: %s: %s\n", - argv[0], - (argn == 1)? argv[optind]: "(stdin)", - strerror(errno)); - - goto error_dump; - } - } else { - if (zx_dump_hex(fd) < 0) { - fprintf(stderr, "%s: %s: %s\n", - argv[0], - (argn == 1)? argv[optind]: "(stdin)", - strerror(errno)); - - goto error_dump; - } - } - - if (argn == 1) { - close(fd); - } - - return 0; - -error_dump: - if (argn == 1) { - close(fd); - } - -error_open: - return 1; -} diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..ba8a9b0 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,27 @@ +CC = cc +CFLAGS = -g -Wall -O2 +LDFLAGS = +OBJS = charset.o basic.o hexdump.o main.o +NAME = zxdump + +PREFIX = /usr/local +INSTALL = /usr/bin/install +RM = /bin/rm + +INCLUDE_PATH = ../include +HEADERS_SUBDIR = zx +HEADERS_BUILD = $(addprefix $(INCLUDE_PATH)/$(HEADERS_SUBDIR)/, $(HEADERS)) + +all: $(NAME) + +$(NAME): $(OBJS) + $(CC) $(OBJS) -o $(NAME) $(LDFLAGS) + +$(OBJS): %.o: %.c $(HEADERS_BUILD) + $(CC) $(CFLAGS) -I$(INCLUDE_PATH) -c $< + +install: $(NAME) + $(INSTALL) -c -m 0755 $(NAME) $(PREFIX)/bin + +clean: + $(RM) -f $(NAME) $(OBJS) diff --git a/src/basic.c b/src/basic.c new file mode 100644 index 0000000..043c66a --- /dev/null +++ b/src/basic.c @@ -0,0 +1,163 @@ +#include <unistd.h> +#include <sys/stat.h> + +#include <zx/charset.h> +#include <zx/basic.h> + +char *zx81_basic_tokens_low[3] = { + "RND", "INKEY$", "PI", +}; + +char *zx81_basic_tokens[64] = { + "\"\"", "AT", "TAB", NULL, "CODE", "VAL", "LEN", "SIN", + "COS", "TAN", "ASN", "ACS", "ATN", "LN", "EXP", "INT", + "SQR", "SGN", "ABS", "PEEK", "USR", "STR$", "CHR$", "NOT", + "**", "OR", "AND", "<=", ">=", "<>", "THEN", "TO", + "STEP", "LPRINT", "LLIST", "STOP", "SLOW", "FAST", "NEW", "SCROLL", + "CONT", "DIM", "REM", "FOR", "GOTO", "GOSUB", "INPUT", "LOAD", + "LIST", "LET", "PAUSE", "NEXT", "POKE", "PRINT", "PLOT", "RUN", + "SAVE", "RAND", "IF", "CLS", "UNPLOT", "CLEAR", "RETURN", "COPY", +}; + +static inline enum zx_basic_token_type zx_basic_token_type_get(uint8_t b) { + if (ZX81_CHAR_LOW(b)) { + uint32_t codepoint = zx81_charset[b]; + + if ((codepoint >= 'A' && codepoint <= 'Z') + || (codepoint >= '0' && codepoint <= '9')) { + return ZX_BASIC_TOKEN_ALNUM; + } else if (codepoint == '"') { + return ZX_BASIC_TOKEN_QUOTE; + } else { + return ZX_BASIC_TOKEN_SYMBOL; + } + } else if (ZX81_CHAR_INVERSE(b)) { + return zx_basic_token_type_get(b - ZX81_CHAR_INVERSE_START); + } else if (ZX81_CHAR_TOKEN_LOW(b)) { + return ZX_BASIC_TOKEN_WORD; + } else if (ZX81_CHAR_TOKEN_HIGH(b)) { + char *token = zx81_basic_tokens[b-ZX81_CHAR_TOKEN_HIGH_START]; + + if (token[0] >= 'A' && token[0] <= 'Z') { + return ZX_BASIC_TOKEN_WORD; + } else { + return ZX_BASIC_TOKEN_SYMBOL; + } + } + + return ZX_BASIC_TOKEN_UNKNOWN; +} + +static inline int basic_print(uint8_t c, int tty, FILE *stream) { + if (ZX81_CHAR_LOW(c)) { + return zx81_fputc(c, 0, stream); + } else if (ZX81_CHAR_TOKEN_LOW(c)) { + return fputs(zx81_basic_tokens_low[c - ZX81_CHAR_TOKEN_LOW_START], stream); + } else if (ZX81_CHAR_NEWLINE(c)) { + return putchar('\n'); + } else if (ZX81_CHAR_INVERSE(c)) { + return zx81_fputc(c - ZX81_CHAR_INVERSE_START, tty, stream); + } else if (ZX81_CHAR_TOKEN_HIGH(c)) { + return fputs(zx81_basic_tokens[c - ZX81_CHAR_TOKEN_HIGH_START], stream); + } + + return 0; +} +ssize_t zx81_basic_dump(int fd, FILE *stream) { + void *buf; + ssize_t total = 0; + int tty = isatty(fileno(stream)); + struct stat st; + + if (fstat(fd, &st) < 0) { + goto error_fstat; + } + + if ((buf = malloc(st.st_blksize)) == NULL) { + goto error_malloc; + } + + if (lseek(fd, ZX_BASIC_STATE_SIZE, SEEK_CUR) < 0) { + goto error_io; + } + + while (1) { + ssize_t readlen, len, i; + zx_basic_line line; + uint8_t last = 0xc0; + + if ((readlen = read(fd, &line, sizeof(line))) < 0) { + goto error_io; + } else if (readlen == 0) { + break; + } + + len = le16toh(line.len); + + if (be16toh(line.num) == len && len == ZX_BASIC_LINE_LAST) { + break; + } + + if (printf("%d ", (int)be16toh(line.num)) < 0) { + goto error_io; + } + + if (read(fd, buf, len) < 0) { + goto error_io; + } + + for (i=0; i<len; i++) { + uint8_t c = ((uint8_t *)buf)[i]; + + enum zx_basic_token_type type = zx_basic_token_type_get(c), + type_last = zx_basic_token_type_get(last); + + int space = 0; + + if (type == ZX_BASIC_TOKEN_ALNUM) { + if (type_last == ZX_BASIC_TOKEN_WORD) { + space = 1; + } + } else if (type == ZX_BASIC_TOKEN_QUOTE) { + if (type_last == ZX_BASIC_TOKEN_WORD) { + space = 1; + } + } else if (type == ZX_BASIC_TOKEN_SYMBOL) { + space = 0; + } else if (type == ZX_BASIC_TOKEN_WORD) { + if (type_last != ZX_BASIC_TOKEN_SYMBOL) { + space = 1; + } + } else if (ZX81_CHAR_TOKEN_INTEGRAL(c) || ZX81_CHAR_TOKEN_FLOAT(c)) { + i += 5; + } + + if (space && fputc(' ', stream) < 0) { + goto error_io; + } + + if (basic_print(c, tty, stream) < 0) { + goto error_io; + } + + last = c; + } + + if (!ZX81_CHAR_NEWLINE(((uint8_t *)buf)[i-1])) { + if (fputc('\n', stream) < 0) { + goto error_io; + } + } + } + + free(buf); + + return total; + +error_io: + free(buf); + +error_malloc: +error_fstat: + return -1; +} diff --git a/src/charset.c b/src/charset.c new file mode 100644 index 0000000..64d5b64 --- /dev/null +++ b/src/charset.c @@ -0,0 +1,66 @@ +#include <sys/types.h> + +#include <zx/charset.h> + +uint32_t zx81_charset[ZX81_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 inline size_t utf8_encode(uint8_t *buf, uint32_t codepoint) { + if ((codepoint & 0x007f) == codepoint) { + buf[0] = codepoint & 0x007f; + + return 1; + } else if ((codepoint & 0x07ff) == codepoint) { + buf[0] = 0xc0 | ((codepoint & 0x07c0) >> 6); + buf[1] = 0x80 | (codepoint & 0x003f); + + return 2; + } else if ((codepoint & 0xffff) == codepoint) { + buf[0] = 0xe0 | ((codepoint & 0xf000) >> 12); + buf[1] = 0x80 | ((codepoint & 0x0fc0) >> 6); + buf[2] = 0x80 | (codepoint & 0x003f); + + return 3; + } else { + buf[0] = 0xf0 | ((codepoint & 0x1c0000) >> 18); + buf[1] = 0x80 | ((codepoint & 0x03f000) >> 12); + buf[2] = 0x80 | ((codepoint & 0x000fc0) >> 6); + buf[3] = 0x80 | (codepoint & 0x00003f); + + return 4; + } +} + +int zx81_fputc(uint8_t c, int inverse, FILE *stream) { + uint8_t sequence[4]; + size_t len = utf8_encode(sequence, zx81_charset[c]); + + if (inverse) { + if (fputs("\033[7m", stream) < 0) { + goto error_io; + } + } + + if (fwrite(sequence, len, 1, stream) < 1) { + goto error_io; + } + + if (inverse) { + if (fputs("\033[27m", stream) < 0) { + goto error_io; + } + } + + return 0; + +error_io: + return -1; +} diff --git a/src/hexdump.c b/src/hexdump.c new file mode 100644 index 0000000..fd7d7ff --- /dev/null +++ b/src/hexdump.c @@ -0,0 +1,109 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <sys/stat.h> + +#include <zx/charset.h> +#include <zx/hexdump.h> + +static ssize_t hexdump_line(off_t offset, void *buf, size_t len, int tty, FILE *stream) { + size_t i; + + if (printf("%08zx: ", offset) < 0) { + goto error_io; + } + + for (i=0; i<len; i++) { + if (i > 0 && (i % ZX81_HEXDUMP_STRIDE_GROUP) == 0) { + if (fputc(' ', stream) < 0) { + goto error_io; + } + } + + if (fprintf(stream, "%02x", ((uint8_t *)buf)[offset+i]) < 0) { + goto error_io; + } + } + + if (fputs(" ", stream) < 0) { + goto error_io; + } + + for (i=0; i<len; i++) { + uint8_t c = ((uint8_t *)buf)[offset+i]; + + if (c <= 0x3f) { + if (zx81_fputc(c, 0, stream) < 0) { + goto error_io; + } + } else if (c >= 0xa0 && c <= 0xbf) { + if (zx81_fputc(c - 0xa0, tty, stream) < 0) { + goto error_io; + } + } else { + if (fputc('.', stream) < 0) { + goto error_io; + } + } + } + + if (fputc('\n', stream) < 0) { + goto error_io; + } + + return fflush(stream); + +error_io: + return -1; +} + +ssize_t zx81_hexdump(int fd, FILE *stream) { + void *buf; + ssize_t total = 0; + int tty = isatty(fileno(stdout)); + 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; + off_t offset = 0; + + if ((len = read(fd, buf, st.st_blksize)) < 0) { + goto error_read; + } else if (len == 0) { + break; + } + + for (i=0; i<len; i+=ZX81_HEXDUMP_STRIDE_LINE) { + size_t left = len - i, + linesz = left < ZX81_HEXDUMP_STRIDE_LINE? left: ZX81_HEXDUMP_STRIDE_LINE; + + if (hexdump_line(offset, buf, linesz, tty, stream) < 0) { + goto error_hexdump_line; + } + + offset += linesz; + total += linesz; + } + } + + free(buf); + + return total; + +error_hexdump_line: +error_read: + free(buf); + +error_malloc: +error_fstat: + return -1; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..bc200cb --- /dev/null +++ b/src/main.c @@ -0,0 +1,99 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include <zx/hexdump.h> +#include <zx/basic.h> + +#define ZX_DUMP_FLAGS_NONE 0 +#define ZX_DUMP_FLAGS_BASIC (1 << 0) + +static void usage(const char *prog, 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 [-b] [file]\n", prog); + exit(1); +} + +int main(int argc, char **argv) { + int fd, + flags = ZX_DUMP_FLAGS_NONE, + argn; + + while (1) { + int c; + + if ((c = getopt(argc, argv, "b")) < 0) { + break; + } + + switch (c) { + case 'b': + flags |= ZX_DUMP_FLAGS_BASIC; + break; + + case '?': + default: + usage(argv[0], NULL); + } + } + + argn = argc - optind; + + if (argn == 0) { + fd = fileno(stdin); + } else if (argn == 1) { + if ((fd = open(argv[optind], O_RDONLY)) < 0) { + fprintf(stderr, "%s: %s: %s\n", argv[0], argv[optind], strerror(errno)); + + goto error_open; + } + } else { + usage(argv[0], "Too many arguments provided"); + } + + if (flags & ZX_DUMP_FLAGS_BASIC) { + if (zx81_basic_dump(fd, stdout) < 0) { + fprintf(stderr, "%s: %s: %s\n", + argv[0], + (argn == 1)? argv[optind]: "(stdin)", + strerror(errno)); + + goto error_dump; + } + } else { + if (zx81_hexdump(fd, stdout) < 0) { + fprintf(stderr, "%s: %s: %s\n", + argv[0], + (argn == 1)? argv[optind]: "(stdin)", + strerror(errno)); + + goto error_dump; + } + } + + if (argn == 1) { + close(fd); + } + + return 0; + +error_dump: + if (argn == 1) { + close(fd); + } + +error_open: + return 1; +} |