aboutsummaryrefslogtreecommitdiff
path: root/a8cat.c
diff options
context:
space:
mode:
Diffstat (limited to 'a8cat.c')
-rw-r--r--a8cat.c287
1 files changed, 287 insertions, 0 deletions
diff --git a/a8cat.c b/a8cat.c
new file mode 100644
index 0000000..76ec67a
--- /dev/null
+++ b/a8cat.c
@@ -0,0 +1,287 @@
+#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;
+
+/* XXX: hard-coded ANSI/vt100 escape sequences. would be
+ better but more complex to use terminfo to support any ol'
+ terminal... */
+const char *inverse_on = "\x1b[7m";
+const char *inverse_off = "\x1b[0m";
+
+int verbose = 0;
+int underline = 0, reverse = 0, textmode = 0, ics = 0;
+int magazine = 0, stripinv = 0;
+
+const char *self;
+
+void set_self(const char *argv0) {
+ char *p;
+
+ self = argv0;
+ p = strrchr(self, '/');
+ if(p) self = p + 1;
+}
+
+void die(const char *msg) {
+ fprintf(stderr, "%s: %s\n", self, msg);
+ exit(1);
+}
+
+void print_help(void) {
+ printf("Usage: %s [-i] [-u] [file ...]\n", self);
+}
+
+FILE *open_input(const char *file) {
+ FILE *input;
+
+ if(file[0] == '-' && file[1] == 0) {
+ if(verbose)
+ fprintf(stderr, "%s: reading from standard input.\n", self);
+ if(freopen(NULL, "rb", stdin)) {
+ input = stdin;
+ } else {
+ perror("(standard input)");
+ return NULL;
+ }
+ } else if(!(input = fopen(file, "rb"))) {
+ perror(file);
+ return NULL;
+ }
+
+ if(verbose)
+ fprintf(stderr, "%s: reading from file '%s'.\n", self, file);
+
+ return input;
+}
+
+int handle_escape_seq(int inv, FILE *input, const char *file, int line) {
+ 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 {
+ fprintf(stderr, "%s: warning: %s:%d: unrecognized ANSI escape sequence.\n", self, file, line);
+ fputs(buf, stdout);
+ return inv;
+ }
+}
+
+int a8revcat(const char *file) {
+ FILE *input;
+ int c, d, inv = 0, line = 1;
+
+ if( !(input = open_input(file)) )
+ return 1;
+
+ while( (c = fgetwc(input)) != WEOF ) {
+ if(c == 0x1b) {
+ inv = handle_escape_seq(inv, input, file, line);
+ } else if(c == '\r') {
+ continue; /* swallow carriage returns */
+ } else if(c == '\n') {
+ putchar(0x9b);
+ line++;
+ } else if(c == '\t') {
+ putchar(0x7f);
+ } else if(c == '\b' || c == 0x7f) {
+ putchar(0x7e);
+ } else if(c == '\a') {
+ putchar(0xfd);
+ } else if(c < 0x80) {
+ putchar(c | inv);
+ } else {
+ d = wchar2atascii(c, ics);
+ if(d == -1) {
+ fprintf(stderr, "%s: warning: %s:%d: unrecognized Unicode character %04x.\n", self, file, line, c);
+ } else {
+ putchar(d | inv);
+ }
+ }
+ }
+
+ if(verbose)
+ fprintf(stderr, "%s: %s: converted %d lines, closing file.\n", self, file, line - 1);
+
+ fclose(input);
+ return 0;
+}
+
+void inverse(int onoff) {
+ fputs((onoff ? inverse_on : inverse_off ), stdout);
+}
+
+int a8cat(const char *file) {
+ FILE *input;
+ int c, inv = 0, line = 1;
+
+ if( !(input = open_input(file)) )
+ return 1;
+
+ while( (c = fgetc(input)) != EOF ) {
+ if(c == 0x9b) {
+ putchar('\n');
+ line++;
+ continue;
+ }
+
+ if(stripinv) c &= 0x7f;
+
+ if(textmode) {
+ switch(c) {
+ case 0x7f: /* tab */
+ 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;
+ if(magazine)
+ fputs("{inv}", stdout);
+ else
+ inverse(1);
+ }
+ } else {
+ if(inv) {
+ inv = 0;
+ if(magazine)
+ fputs("{norm}", stdout);
+ else
+ inverse(0);
+ }
+ }
+ }
+
+ if(magazine) {
+ /* special cases: control codes with bit 7 set can't go
+ in the table since it's only got 128 entries. */
+ switch(c) {
+ case 0x9c:
+ fputs("{del-line}", stdout); continue;
+ case 0x9d:
+ fputs("{ins-line}", stdout); continue;
+ case 0x9e:
+ fputs("{clr-tab}", stdout); continue;
+ case 0x9f:
+ fputs("{set-tab}", stdout); continue;
+ case 0xfd:
+ fputs("{bell}", stdout); continue;
+ case 0xfe:
+ fputs("{del-char}", stdout); continue;
+ case 0xff:
+ fputs("{ins-char}", stdout); continue;
+ }
+ }
+
+ 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) {
+ if(magazine)
+ fputs("{norm}", stdout);
+ else
+ inverse(0);
+ }
+
+ if(verbose)
+ fprintf(stderr, "%s: %s: converted %d lines, closing file.\n", self, file, line - 1);
+
+ fclose(input);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int opt, result = 0;
+
+ set_self(argv[0]);
+
+ if(argc > 1 && strcmp(argv[1], "--help") == 0) {
+ print_help();
+ exit(0);
+ }
+
+ if(argc > 1 && strcmp(argv[1], "--version") == 0) {
+ printf("%s %s\n", self, VERSION);
+ exit(0);
+ }
+
+ while( (opt = getopt(argc, argv, "ihurtmsv")) != -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;
+ case 'm': table = ata2mag; magazine = 1; break;
+ case 's': stripinv = 1; break;
+ case 'v': verbose = 1; break;
+ default: print_help(); exit(1); break;
+ }
+ }
+
+ if(reverse) {
+ if(isatty(fileno(stdout)))
+ die("Refusing to write ATASCII to a terminal.");
+
+ if(underline || textmode || stripinv || magazine) {
+ die("-t, -u, -m, -s options don't make sense with -r.\n");
+ }
+ /* the language_country part of the locale doesn't matter,
+ since we aren't doing localization. the encoding *has*
+ to be UTF-8. */
+ setlocale(LC_CTYPE, "en_US.UTF-8");
+ }
+
+ if(magazine) {
+ if(ics || stripinv || underline) {
+ die("-i, -s, -u options don't make sense with -m.\n");
+ }
+ }
+
+ if(optind >= argc) {
+ result = (reverse ? a8revcat("-") : a8cat("-"));
+ } else {
+ while(optind < argc) {
+ result += (reverse ? a8revcat(argv[optind]) : a8cat(argv[optind]));
+ optind++;
+ }
+ }
+
+ exit(result);
+}