aboutsummaryrefslogtreecommitdiff
path: root/a8cat.c
diff options
context:
space:
mode:
Diffstat (limited to 'a8cat.c')
-rw-r--r--a8cat.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/a8cat.c b/a8cat.c
new file mode 100644
index 0000000..752e96f
--- /dev/null
+++ b/a8cat.c
@@ -0,0 +1,187 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <locale.h>
+#include <wchar.h>
+#include <errno.h>
+
+#include "atables.h"
+#include "wtable.h"
+
+const char **table = ata2utf;
+
+const char *inverse_on = "\x1b[7m";
+const char *inverse_off = "\x1b[0m";
+
+int underline = 0, reverse = 0, textmode = 0, ics = 0;
+
+void print_help(void) {
+ printf("Usage: a8cat [-i] [-u] [file ...]\n");
+}
+
+FILE *open_input(const char *file) {
+ FILE *input;
+
+ if(file[0] == '-' && file[1] == 0) {
+ if(freopen(NULL, "rb", stdin)) {
+ input = stdin;
+ } else {
+ perror("(standard input)");
+ return NULL;
+ }
+ } else if(!(input = fopen(file, "rb"))) {
+ perror(file);
+ return NULL;
+ }
+
+ return input;
+}
+
+int handle_escape_seq(int inv, FILE *input) {
+ int count, c;
+ char buf[5] = { 0x1b, 0, 0, 0, 0 };
+
+ for(count = 1; count < 4; count++) {
+ c = fgetwc(input);
+ if(c == WEOF) break;
+ buf[count] = c; /* FIXME: might be a wide char! */
+ }
+
+ if(strcmp(inverse_on, buf) == 0) {
+ return 0x80;
+ } else if(strcmp(inverse_off, buf) == 0) {
+ return 0;
+ } else {
+ fputs(buf, stdout);
+ return inv;
+ }
+}
+
+int a8revcat(const char *file) {
+ FILE *input;
+ int c, d, inv = 0;
+
+ if( !(input = open_input(file)) )
+ return 1;
+
+ setlocale(LC_CTYPE, "en_US.UTF-8");
+ while( (c = fgetwc(input)) != WEOF ) {
+ if(c == 0x1b) {
+ inv = handle_escape_seq(inv, input);
+ } else if(c == '\n') {
+ putchar(0x9b);
+ } else if(c < 0x80) {
+ putchar(c | inv);
+ } else {
+ d = wchar2atascii(c, ics);
+ if(d == -1) {
+ fprintf(stderr, "warning: unrecognized Unicode character %04x\n", c);
+ } else {
+ putchar(d | inv);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* XXX: hard-coded ANSI/vt100 escape sequences. would be
+ better but more complex to use terminfo to support any ol'
+ terminal... */
+void inverse(int onoff) {
+ fputs((onoff ? inverse_on : inverse_off ), stdout);
+}
+
+int a8cat(const char *file) {
+ FILE *input;
+ int c, inv = 0;
+
+ if( !(input = open_input(file)) )
+ return 1;
+
+ while( (c = fgetc(input)) != EOF ) {
+ if(c == 0x9b) {
+ putchar('\n');
+ continue;
+ }
+
+ if(textmode) {
+ switch(c) {
+ case 0x09: /* Atari TAB is same as ASCII */
+ putchar('\t');
+ continue;
+ case 0xfd: /* bell */
+ putchar('\a');
+ continue;
+ case 0x7e: /* backspace */
+ putchar('\b');
+ continue;
+ default: break;
+ }
+ }
+
+ if(!underline) {
+ /* strings of inverse chars only get one "inverse on" ANSI
+ sequence, and one "inverse off" afterwards. */
+ if(c & 0x80) {
+ if(!inv) {
+ inv = 1;
+ inverse(1);
+ }
+ } else {
+ if(inv) {
+ inv = 0;
+ inverse(0);
+ }
+ }
+ }
+
+ fputs(table[c & 0x7f], stdout);
+
+ if(underline && (c & 0x80)) {
+ putchar('\b');
+ putchar('_');
+ }
+ }
+
+ /* gotta turn off inverse, so if there's another file after this one,
+ it doesn't start out being printed in inverse. */
+ if(inv && !underline) inverse(0);
+
+ fclose(input);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int opt, result = 0;
+
+ while( (opt = getopt(argc, argv, "ihurt")) != -1) {
+ switch(opt) {
+ case 'i': table = ics2utf; ics = 1; break;
+ case 'h': print_help(); exit(0); break;
+ case 'u': underline = 1; break;
+ case 'r': reverse = 1; break;
+ case 't': textmode = 1; break;
+ default: print_help(); exit(1); break;
+ }
+ }
+
+ if(reverse) {
+ if(underline || textmode) {
+ fprintf(stderr, "-t and -u options don't make sense with -r.\n");
+ exit(1);
+ }
+ }
+
+ if(optind >= argc) {
+ result = (reverse ? a8revcat("-") : a8cat("-"));
+ } else {
+ while(optind < argc) {
+ result += (reverse ? a8revcat(argv[optind]) : a8cat(argv[optind]));
+ optind++;
+ }
+ }
+
+ exit(result);
+}