diff options
Diffstat (limited to 'a8cat.c')
-rw-r--r-- | a8cat.c | 287 |
1 files changed, 287 insertions, 0 deletions
@@ -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); +} |