#include #include #include #include #include #include #include #include #include "bas.h" #include "bcdfp.h" #include "tokens.h" #include "atables.h" #define C_RED 1 #define C_GREEN 2 #define C_YELLOW 3 #define C_PURPLE 5 #define C_CYAN 6 int immediate = 0, utf8 = 1, magazine = 0, inv = 0, color = 1, bold = 0, badtok = 0; const char **table = ata2utf; FILE *outfh; void print_help(void) { printf("Usage: %s [-a] [-b] [-i] [-m] [-n] [-v] \n", self); printf(" -a: output raw ATASCII.\n"); printf(" -b: use bold for color output.\n"); printf(" -i: show immediate mode command (line 32768).\n"); printf(" -m: magazine style listing (see a8cat(1)).\n"); printf(" -n: disable color syntax highlighting.\n"); printf(" -v: verbose.\n"); } void parse_args(int argc, char **argv) { int opt; while( (opt = getopt(argc, argv, "viamnbh")) != -1) { switch(opt) { case 'v': verbose = 1; break; case 'i': immediate = 1; break; case 'a': utf8 = magazine = 0; color = 0; break; case 'm': utf8 = 0 ; magazine = 1; color = 0; table = ata2mag; break; case 'n': color = 0; break; case 'b': bold = 1; break; case 'h': print_help(); exit(0); default: print_help(); exit(1); } } if(optind >= argc) die("No input file given (use - for stdin)."); else open_input(argv[optind]); } void setup_outfh(void) { if(! (utf8 || magazine) ) { if(isatty(fileno(stdout))) { die("Refusing to write ATASCII data to the terminal."); } } outfh = stdout; return; } void outchr(char c) { putc(c, outfh); } /* this should probably be moved to bcdfp.c */ double bcd2double(const unsigned char *num) { double result = 0, sign; int exp, i; exp = *num; if(!exp) { return 0.0; } sign = (exp & 0x80 ? -1.0 : 1.0); exp &= 0x7f; exp -= 0x40; for(i = 1; i < 6; i++) { result *= 100.0; result += bcd2int(num[i]); } result *= pow(100, exp - 4); result *= sign; return result; } void color_on(unsigned char c) { printf("\x1b[%d;3%dm", bold, c); } void color_off(void) { fputs("\x1b[0m", outfh); } void print_number(unsigned int pos) { if(color) color_on(C_RED); fprintf(outfh, "%G", bcd2double(program + pos)); if(color) color_off(); } /* only called in magazine mode. cursor control characters like a bell in the middle of a non-inverse string should not cause it to print {inv}{bell}{norm}. The {bell} is "inherently" inverse (since its high bit is set) but doesn't need to be printed that way. */ int affects_inv(unsigned char c) { switch(c) { case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f: case 0x7d: case 0x7e: case 0x7f: case 0xfd: case 0xfe: case 0xff: return 0; default: return 1; } } void start_inv(unsigned char c) { if(utf8) { fputs("\x1b[7m", outfh); inv = 1; } else if(magazine) { if(affects_inv(c)) { fputs("{inv}", outfh); inv = 1; } } } void end_inv(unsigned char c) { if(utf8) { fputs("\x1b[0m", outfh); inv = 0; } else if(magazine) { if(affects_inv(c)) { fputs("{norm}", outfh); inv = 0; } } } void print_ata_chr(unsigned char c) { if(c & 0x80) { if(!inv) { start_inv(c); } } else { if(inv) { end_inv(c); } } fputs(table[c & 0x7f], outfh); } void print_string(unsigned int pos, unsigned int len) { inv = 0; if(color) color_on(C_RED); outchr('"'); while(len--) print_ata_chr(program[pos++]); if(inv) end_inv(0); outchr('"'); if(color) color_off(); } CALLBACK(print_lineno) { if(color) color_on(C_CYAN); fprintf(outfh, "%d ", lineno); if(color) color_off(); } CALLBACK(print_cmd) { const char *name; if(tok == CMD_ILET) return; if(color) color_on(C_YELLOW); if(tok > last_command || (!(name = commands[tok]))) { fprintf(outfh, "(bad cmd token $%02x) ", tok); badtok = 1; } else { fprintf(outfh, "%s ", name); } if(color) color_off(); } CALLBACK(print_op) { const char *name; switch(tok) { case OP_NUMCONST: print_number(pos + 1); return; case OP_STRCONST: print_string(pos + 2, program[pos + 1]); return; case OP_EOL: return; default: break; } if(color) { if(tok > 0x3c) color_on(C_PURPLE); else if(tok == OP_UMINUS) color_on(C_RED); /* show leading - in same color as the number */ else if((tok >= 0x17 && tok <= 0x1b) || (tok >= 0x28 && tok <= 0x2a)) color_on(C_YELLOW); else color_on(C_GREEN); } if(tok > last_operator || (!(name = operators[tok]))) { fprintf(outfh, "(bad op token $%02x)", tok); badtok = 1; } else { fprintf(outfh, "%s", name); } if(color) color_off(); } CALLBACK(print_varname) { int i, count; unsigned char c, c_on = 0;; tok &= 0x7f; for(i = vnstart, count = 0; count < tok; i++) { if(program[i] & 0x80) count++; } do { c = program[i++]; if(color && c == ('(' | 0x80)) { color_on(C_GREEN); c_on = 1; } outchr(c & 0x7f); } while (c < 0x80); if(c_on) color_off(); } CALLBACK(print_text) { unsigned char c, is_data = program[pos - 1] == CMD_DATA, comma = 0; inv = 0; if(color) color_on(C_CYAN); while(program[pos] != 0x9b) { c = program[pos++]; if(color && is_data && c == ',') { color_off(); comma = 1; } else { comma = 0; } print_ata_chr(c); if(comma) color_on(C_CYAN); } if(inv) end_inv(0); if(color) color_off(); } CALLBACK(print_newline) { if(utf8 || magazine) outchr('\n'); else outchr(0x9b); } CALLBACK(code_prot) { fprintf(stderr, "%s: Program is code-protected, stopping at line %d.\n", self, lineno); exit(0); } void list(void) { on_start_line = print_lineno; on_cmd_token = print_cmd; on_exp_token = print_op; on_var_token = print_varname; on_end_line = print_newline; on_text = print_text; on_bad_line_length = code_prot; walk_code(0, 32767 + immediate); } int main(int argc, char **argv) { set_self(*argv); parse_general_args(argc, argv, print_help); parse_args(argc, argv); readfile(); parse_header(); if(!vntable_ok()) die("Program is variable-protected; unprotect it first."); setup_outfh(); list(); if(badtok) { fprintf(stderr, "%s: program has unknown tokens; maybe Turbo BASIC or BASIC XL/XE?\n", self); return 2; } return 0; }