diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | listbas.1 | 62 | ||||
-rw-r--r-- | listbas.c | 187 | ||||
-rw-r--r-- | listbas.rst | 60 |
4 files changed, 254 insertions, 59 deletions
@@ -61,8 +61,8 @@ vxrefbas: bas.o cxrefbas: bas.o bcdfp.o linetab.o -listbas: listbas.c bas.o bcdfp.o tokens.o - $(CC) $(CFLAGS) -o listbas listbas.c bas.o bcdfp.o tokens.o -lm +listbas: listbas.c bas.o bcdfp.o tokens.o atables.o + $(CC) $(CFLAGS) -o listbas listbas.c bas.o bcdfp.o tokens.o atables.o -lm bas.o: bas.c bas.h @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "LISTBAS" 1 "2024-07-02" "0.2.1" "Urchlay's Atari 8-bit Tools" +.TH "LISTBAS" 1 "2024-07-05" "0.2.1" "Urchlay's Atari 8-bit Tools" .SH NAME listbas \- List the source of a tokenized Atari 8-bit BASIC program .SH SYNOPSIS @@ -39,16 +39,16 @@ listbas [\fB\-v\fP] [\fB\-i\fP] [\fB\-a\fP | \fB\-m\fP ] \fBinput\-file\fP tokenized (SAVEd) BASIC program and prints the code in human\-readable format. .sp -By default, output is piped through \fBa8cat\fP(1), to covert ATASCII -characters into Unicode equivalents. Raw ATASCII and "magazine listing" -mode are also available. -.sp -\fBa8cat\fP is run as an external processe. The executables is searched -for first in the current directory, and then in \fIPATH\fP\&. +By default, output is Unicode in UTF\-8 encoding, with ANSI/VT220 +escape sequences for inverse video and color syntax highlighting. .SH OPTIONS .SS List options .INDENT 0.0 .TP +.B \fB\-b\fP +Use bold, for color output. This may make it easier to read on +some terminals. Or, it may hurt your eyes... +.TP .B \fB\-i\fP Include the immediate mode command (line 32768) in the output. .TP @@ -56,9 +56,12 @@ Include the immediate mode command (line 32768) in the output. Output raw ATASCII; no translation to the host character set. Must be used with redirection; \fBlistbas\fP will not write ATASCII to the terminal. .TP +.B \fB\-n\fP +No color. Has no effect if \fB\-a\fP or \fB\-m\fP are in effect, since these +modes don\(aqt support color anyway. +.TP .B \fB\-m\fP -Output "magazine listing". Pipes output through \fBa8cat \-m\fP; see the man -page for \fBa8cat\fP for details. +Output "magazine listing". See the \fB\-m\fP option for \fBa8cat\fP for details. .UNINDENT .SS General Options .INDENT 0.0 @@ -73,6 +76,42 @@ Print version number and exit. Verbose operation. When displaying a number in verbose mode, it will be prefixed with \fI$\fP if it\(aqs in hex, or no prefix for decimal. .UNINDENT +.SH COLORS +.sp +Color output only works on terminal emulators (or real terminals) +that support ANSI/VT220 style escape codes. This includes all modern +terminal emulators, and most not\-so\-modern ones in the UNIX world. +.sp +The color scheme is: +.INDENT 0.0 +.TP +.B \fByellow\fP +Commands. Also "command operators" such as the \fBGOTO\fP in \fBON/GOTO\fP and +the \fBSTEP\fP in a \fBFOR\fP command. These are really operators as far as +BASIC is concerned, but it makes more sense to colorize them as commands. +.TP +.B \fBgreen\fP +Operators (except functions and "command operators"). +.TP +.B \fBpurple\fP +Functions. +.TP +.B \fBred\fP +Numbers (except line numbers at the start of a line) and string +constants. +.TP +.B \fBcyan\fP +Line numbers at the start of a line, comments (\fBREM\fP text) and \fBDATA\fP elements. +.UNINDENT +.sp +Variable names and commas between \fBDATA\fP elements are not highlighted, +so they\(aqll appear in the default foreground color (usually white if the +terminal has a black background, or black if the background is white). +.sp +Note that nothing is highlighted in blue. This is because it\(aqs +difficult to read on many terminals. Also, black and white are not +used because presumably, one or the other is the background color of +the terminal. .SH NOTES .sp \fBlistbas\fP will refuse to operate on a LIST\-protected program with @@ -83,6 +122,11 @@ protection. \fBlistbas\fP is similar to Jindroush\(aqs \fBchkbas\fP(1). The main differences are: .INDENT 0.0 .IP \(bu 2 +\fBlistbas\fP prints ATASCII graphics as Unicode equivalents, so the listing +looks very similar to how it would appear on the Atari. +.IP \(bu 2 +\fBlistbas\fP does color syntax highlighting. +.IP \(bu 2 \fBlistbas\fP only supports Atari BASIC, not Turbo BASIC or BASIC XL/XE. .IP \(bu 2 \fBlistbas\fP doesn\(aqt show information about the variables. Use \fBvxrefbas\fP(1) @@ -11,28 +11,41 @@ #include "bas.h" #include "bcdfp.h" #include "tokens.h" +#include "atables.h" -int immediate = 0, a8cat = 1, magazine = 0; +#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; + +const char **table = ata2utf; FILE *outfh; void print_help(void) { - printf("Usage: %s [-v] [-i] [-a|-u] <inputfile>\n", self); + printf("Usage: %s [-v] [-i] [-n] [-a|-m] <inputfile>\n", self); printf(" -v: verbose.\n"); printf(" -i: show immediate mode command (line 32768).\n"); printf(" -a: output raw ATASCII.\n"); printf(" -m: magazine style listing (see a8cat(1)).\n"); + printf(" -n: disable color syntax highlighting.\n"); + printf(" -b: use bold for color output.\n"); } void parse_args(int argc, char **argv) { int opt; - while( (opt = getopt(argc, argv, "viamh")) != -1) { + while( (opt = getopt(argc, argv, "viamnbh")) != -1) { switch(opt) { case 'v': verbose = 1; break; case 'i': immediate = 1; break; - case 'a': a8cat = magazine = 0; break; - case 'm': a8cat = magazine = 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(); @@ -47,38 +60,13 @@ void parse_args(int argc, char **argv) { } void setup_outfh(void) { - const char *cmd; - - /* search current dir before PATH. no easy way to detect errors here, - have to wait until we call pclose(). */ - if(a8cat) { - if(magazine) - cmd = "./a8cat -m 2>/dev/null || a8cat -m 2>/dev/null || exit 1"; - else - cmd = "./a8cat 2>/dev/null || a8cat 2>/dev/null || exit 1"; - } else { + if(! (utf8 || magazine) ) { if(isatty(fileno(stdout))) { die("Refusing to write ATASCII data to the terminal."); } - outfh = stdout; - return; - } - - outfh = popen(cmd, "w"); - if(!outfh) { - /* fork() or pipe() failed. does NOT detect if the command - wasn't found. */ - perror(self); - exit(1); - } -} - -void close_outfh(void) { - if(a8cat) { - if(pclose(outfh)) { - die("output filter failed; a8cat not in current dir or $PATH."); - } } + outfh = stdout; + return; } void outchr(char c) { @@ -110,18 +98,100 @@ double bcd2double(const unsigned char *num) { 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--) outchr(program[pos++]); + 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) { @@ -129,10 +199,12 @@ CALLBACK(print_cmd) { 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); else fprintf(outfh, "%s ", name); + if(color) color_off(); } CALLBACK(print_op) { @@ -150,36 +222,72 @@ CALLBACK(print_op) { 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); else fprintf(outfh, "%s", name); + if(color) color_off(); } CALLBACK(print_varname) { int i, count; - unsigned char c; + unsigned char c, c_on = 0;; tok &= 0x7f; for(i = vnstart, count = 0; count < tok; i++) { if(program[i] & 0x80) count++; } do { - outchr( (c = program[i++]) & 0x7f); + 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) { - while(program[pos] != 0x9b) outchr(program[pos++]); + 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) { - outchr(0x9b); + 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); - close_outfh(); exit(0); } @@ -207,7 +315,6 @@ int main(int argc, char **argv) { setup_outfh(); list(); - close_outfh(); return 0; } diff --git a/listbas.rst b/listbas.rst index d6d17b1..d7f5d26 100644 --- a/listbas.rst +++ b/listbas.rst @@ -20,12 +20,8 @@ DESCRIPTION tokenized (SAVEd) BASIC program and prints the code in human-readable format. -By default, output is piped through **a8cat**\(1), to covert ATASCII -characters into Unicode equivalents. Raw ATASCII and "magazine listing" -mode are also available. - -**a8cat** is run as an external processe. The executables is searched -for first in the current directory, and then in *PATH*. +By default, output is Unicode in UTF-8 encoding, with ANSI/VT220 +escape sequences for inverse video and color syntax highlighting. OPTIONS ======= @@ -33,6 +29,10 @@ OPTIONS List options ------------ +**-b** + Use bold, for color output. This may make it easier to read on + some terminals. Or, it may hurt your eyes... + **-i** Include the immediate mode command (line 32768) in the output. @@ -40,12 +40,51 @@ List options Output raw ATASCII; no translation to the host character set. Must be used with redirection; **listbas** will not write ATASCII to the terminal. +**-n** + No color. Has no effect if **-a** or **-m** are in effect, since these + modes don't support color anyway. + **-m** - Output "magazine listing". Pipes output through **a8cat -m**; see the man - page for **a8cat** for details. + Output "magazine listing". See the **-m** option for **a8cat** for details. .. include:: genopts.rst +COLORS +====== + +Color output only works on terminal emulators (or real terminals) +that support ANSI/VT220 style escape codes. This includes all modern +terminal emulators, and most not-so-modern ones in the UNIX world. + +The color scheme is: + +**yellow** + Commands. Also "command operators" such as the **GOTO** in **ON/GOTO** and + the **STEP** in a **FOR** command. These are really operators as far as + BASIC is concerned, but it makes more sense to colorize them as commands. + +**green** + Operators (except functions and "command operators"). + +**purple** + Functions. + +**red** + Numbers (except line numbers at the start of a line) and string + constants. + +**cyan** + Line numbers at the start of a line, comments (**REM** text) and **DATA** elements. + +Variable names and commas between **DATA** elements are not highlighted, +so they'll appear in the default foreground color (usually white if the +terminal has a black background, or black if the background is white). + +Note that nothing is highlighted in blue. This is because it's +difficult to read on many terminals. Also, black and white are not +used because presumably, one or the other is the background color of +the terminal. + NOTES ===== @@ -56,6 +95,11 @@ protection. **listbas** is similar to Jindroush's **chkbas**\(1). The main differences are: +- **listbas** prints ATASCII graphics as Unicode equivalents, so the listing + looks very similar to how it would appear on the Atari. + +- **listbas** does color syntax highlighting. + - **listbas** only supports Atari BASIC, not Turbo BASIC or BASIC XL/XE. - **listbas** doesn't show information about the variables. Use **vxrefbas**\(1) |