#include #include #include #include #include #include #include #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); }