aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--listbas.162
-rw-r--r--listbas.c187
-rw-r--r--listbas.rst60
4 files changed, 254 insertions, 59 deletions
diff --git a/Makefile b/Makefile
index ac7c4b3..727f8be 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/listbas.1 b/listbas.1
index cf16a93..110fde5 100644
--- a/listbas.1
+++ b/listbas.1
@@ -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)
diff --git a/listbas.c b/listbas.c
index 9577cec..0c807c2 100644
--- a/listbas.c
+++ b/listbas.c
@@ -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)