aboutsummaryrefslogtreecommitdiff
path: root/listamsb.c
diff options
context:
space:
mode:
Diffstat (limited to 'listamsb.c')
-rw-r--r--listamsb.c163
1 files changed, 84 insertions, 79 deletions
diff --git a/listamsb.c b/listamsb.c
index c88c2e7..58ff958 100644
--- a/listamsb.c
+++ b/listamsb.c
@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <stdarg.h>
#include "amsbtok.h"
@@ -53,6 +54,9 @@
#define MIN_PROGLEN 5
#define EMPTY_PROGLEN 2
+/* a file shorter than this can't be an AMSB program */
+#define MIN_BYTES 5
+
/* an EOL address below this has to be an error, since
this is the lowest MEMLO can ever be. */
#define MIN_PTR 0x0700
@@ -69,7 +73,7 @@ const char *self;
char pipe_command[BUFSIZ + 1] = { "a8cat" };
-int verbose = 0; /* -v */
+int verbosity = 0; /* -v */
int raw_output = 0; /* -a */
int check_only = 0; /* -c */
int startline = 0; /* -r */
@@ -82,10 +86,39 @@ int bytes_read = 0;
int warnings = 0;
int proglen = 0;
int linecount = 0;
+int header_read = 0;
FILE *infile;
FILE *outfile;
+void verbose(int level, const char *fmt, ...) {
+ va_list ap;
+
+ if(verbosity < level) return;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+void finish(int rv);
+
+#define VA_FUNC(func, prefix, action) \
+ void func(const char *fmt, ...) { \
+ va_list ap; \
+ va_start(ap, fmt); \
+ fprintf(stderr, "%s: %s: ", self, prefix); \
+ vfprintf(stderr, fmt, ap); \
+ va_end(ap); \
+ fputc('\n', stderr); \
+ action; \
+ }
+
+VA_FUNC(die, "error", finish(2));
+VA_FUNC(os_err, "error", exit(1));
+VA_FUNC(warn, "warning", warnings++);
+
void set_self(const char *argv0) {
char *p;
@@ -94,70 +127,55 @@ void set_self(const char *argv0) {
if(p) self = p + 1;
}
-void die_with(const char *msg, int status) {
- fprintf(stderr, "%s: %s\n", self, msg);
- exit(status);
+const char *plural(int count) {
+ return count == 1 ? "" : "s";
}
-#define die(x) die_with(x,1)
-#define die2(x) die_with(x,2)
-
/* post-processing: print "summary", exit. called by either read_byte()
(on 'unexpected EOF'), or main() (on normal exit). */
-void finish(int rv, const char *msg) {
+void finish(int rv) {
int progsize;
- if(msg) fprintf(stderr, "%s: %s\n", self, msg);
+ verbose(1, "read %d bytes", bytes_read);
+ verbose(1, "listed %d line%s", linecount, plural(linecount));
- if(verbose) {
- fprintf(stderr, "read %d bytes\n", bytes_read);
- fprintf(stderr, "listed %d lines\n", linecount);
- }
+ if(!linecount) warn("no lines of code in program");
- if(!linecount) {
- fprintf(stderr, "%s: no lines of code in program\n", self);
- warnings++;
- }
-
- progsize = bytes_read - 3;
- if(proglen == progsize) {
- if(verbose)
- fprintf(stderr, "file size matches proglen\n");
- } else {
- warnings++;
- fprintf(stderr, "%s: actual program size %d "
- "doesn't match program size %d in header,\n"
- " ",
- self, progsize, proglen);
- if(proglen > progsize) {
- fprintf(stderr, "AMSB will give #136 ERROR and fail to LOAD this file\n");
+ if(header_read) {
+ progsize = bytes_read - 3;
+ if(proglen == progsize) {
+ verbose(1, "file size matches proglen");
} else {
- fprintf(stderr, "AMSB will stop LOADing before the end of this file\n");
+ warn("actual program size %d doesn't match program size %d in header,",
+ self, progsize, proglen);
+ fputs(" ", stderr);
+ if(proglen > progsize) {
+ fputs("AMSB will give #136 ERROR and fail to LOAD this file\n", stderr);
+ } else {
+ fputs("AMSB will stop LOADing before the end of this file\n", stderr);
+ }
}
+ } else {
+ warn("file is %d byte%s, too short to be an AMSB program", bytes_read, plural(bytes_read));
}
- if(fgetc(infile) != EOF) {
- fprintf(stderr, "%s: trailing garbage at end of file\n", self);
- warnings++;
- }
+ if(fgetc(infile) != EOF) warn("trailing garbage at end of file");
if(warnings) {
fprintf(stderr, "%s: file has %d warning%s\n",
- self, warnings, (warnings == 1 ? "" : "s"));
+ self, warnings, plural(warnings));
rv = 2;
}
if(need_pclose) {
int got = pclose(outfile);
- if(verbose)
- fprintf(stderr, "return value from pipe is %d\n", got);
+ verbose(1, "return value from pipe is %d", got);
if(got != 0) {
- die("a8cat child process failed, do you have a8cat on your PATH?");
+ os_err("a8cat child process failed, do you have a8cat on your PATH?");
}
}
- if(verbose)
- fprintf(stderr, "exit status: %d (%s)\n", rv, (rv ? "ERROR" : "OK"));
+ verbose(1, "exit status: %d (%s)", rv, (rv ? "ERROR" : "OK"));
exit(rv);
}
@@ -167,7 +185,7 @@ unsigned char read_byte(void) {
c = fgetc(infile);
- if(c < 0) finish(2, "unexpected EOF, file truncated?");
+ if(c < 0) die("unexpected EOF, file truncated?");
bytes_read++;
return (unsigned char)c;
@@ -210,33 +228,27 @@ int read_prog_word(void) {
void read_header(void) {
/* $00 for plain, $01 for SAVE with LOCK */
locked = read_byte();
- if(locked > 1) die2("not an AMSB file: first byte not $00 or $01");
+ if(locked > 1) die("not an AMSB file: first byte not $00 or $01");
- if(verbose && locked) {
- fprintf(stderr, "program is locked, decrypting\n");
- }
+ if(locked) verbose(1, "program is locked, decrypting");
proglen = read_word();
- if(verbose)
- fprintf(stderr, "proglen == %d (%04x)\n", proglen, proglen);
+ verbose(1, "proglen == %d (%04x)", proglen, proglen);
if(proglen > MAX_PROGLEN) {
- fprintf(stderr, "%s: not an AMSB file: too big (%d bytes), won't fit in Atari memory\n",
- self, proglen);
- exit(2);
+ die("%s: not an AMSB file: too big (%d bytes), won't fit in Atari memory\n", proglen);
}
if(proglen == EMPTY_PROGLEN) {
- fprintf(stderr, "%s: program length is 2, no code in file (SAVE after NEW)\n", self);
- warnings++;
+ warn("program length is 2, no code in file (SAVE after NEW)\n", self);
} else {
if(proglen < MIN_PROGLEN) {
- fprintf(stderr, "%s: not an AMSB file: program size too small (%d). Atari BASIC file?\n",
- self, proglen);
- exit(2);
+ die("%s: not an AMSB file: program size too small (%d). Atari BASIC file?\n", self, proglen);
}
}
+
+ header_read = 1;
}
void unknown_token(unsigned char byte, int ext) {
@@ -261,14 +273,12 @@ int next_line(void) {
last line. */
ptr = read_prog_word();
if(!ptr) {
- if(verbose)
- fprintf(stderr, "end of program\n");
+ verbose(1, "end of program");
return 0;
}
lineno = read_prog_word();
- if(verbose)
- fprintf(stderr, "found line %d, offset %d, end-of-line %d\n", lineno, offset, ptr);
+ verbose(2, "found line %d, offset %d, end-of-line %d", lineno, offset, ptr);
printing = (lineno >= startline) && (lineno <= endline);
@@ -302,6 +312,9 @@ int next_line(void) {
warnings++;
}
+ /* AMSB always prints a blank line when it LISTs, so we do too. */
+ if(last_lineno == -1) fputc(EOL, outfile);
+
last_lineno = lineno;
/* note that AMSB always puts a space after the line number in LIST */
@@ -393,9 +406,7 @@ int next_line(void) {
len = bytes_read - offset;
- if(verbose) {
- fprintf(stderr, " line %d length: %d\n", lineno, len);
- }
+ verbose(2, " line %d length: %d", lineno, len);
if(len > MAX_LINE_LEN) {
int hard = len > MAX_LINE_LEN_HARD;
@@ -456,7 +467,7 @@ void print_help(void) {
puts(" -a: raw ATASCII output");
puts(" -c: check only (no listing)");
puts(" -l: lock or unlock program");
- puts(" -v: verbose");
+ puts(" -v: verbosity");
puts(" -r: only list lines numbered from *start* to *end*");
puts(" --help, -h: print this help and exit");
puts(" --version: print version number and exit");
@@ -481,12 +492,12 @@ void get_line_range(const char *arg) {
exit(1);
}
} else if(*p == ',' || *p == '-') {
- if(comma) die("invalid argument for -r (too many commas)");
+ if(comma) os_err("invalid argument for -r (too many commas)");
comma++;
startline = val;
val = 0;
} else {
- if(comma) die("invalid argument for -r (only digits and comma allowed)");
+ if(comma) os_err("invalid argument for -r (only digits and comma allowed)");
}
p++;
}
@@ -497,7 +508,7 @@ void get_line_range(const char *arg) {
startline = endline = val;
if(endline < startline)
- die("invalid argument for -r (start > end)");
+ os_err("invalid argument for -r (start > end)");
}
void parse_args(int argc, char **argv) {
@@ -519,7 +530,7 @@ void parse_args(int argc, char **argv) {
case 'l': unlock_mode = 1; break;
case 'c': check_only = 1; break;
case 'a': raw_output = 1; break;
- case 'v': verbose = 1; break;
+ case 'v': verbosity++ ; break;
case 'h': print_help(); exit(0);
case 'r': get_line_range(optarg); break;
case 'i':
@@ -528,7 +539,7 @@ void parse_args(int argc, char **argv) {
case 'm':
case 's':
if(strlen(pipe_command) > (BUFSIZ - 10))
- die("too many a8cat options");
+ os_err("too many a8cat options");
sprintf(tmp, " -%c", opt);
strcat(pipe_command, tmp);
break;
@@ -562,8 +573,7 @@ void open_output() {
perror("/dev/null");
exit(1);
}
- if(verbose)
- fprintf(stderr, "using /dev/null for output (check_only)\n");
+ verbose(1, "using /dev/null for output (check_only)");
} else if(raw_output || unlock_mode) {
if(isatty(fileno(stdout))) {
fprintf(stderr, "%s: refusing to write %s to a terminal\n",
@@ -571,11 +581,9 @@ void open_output() {
exit(1);
}
outfile = stdout;
- if(verbose)
- fprintf(stderr, "using stdout for output\n");
+ verbose(1, "using stdout for output");
} else {
- if(verbose)
- fprintf(stderr, "using pipe for output: %s\n", pipe_command);
+ verbose(1, "using pipe for output: %s", pipe_command);
outfile = popen(pipe_command, "w");
if(!outfile) {
perror(pipe_command);
@@ -599,12 +607,9 @@ int main(int argc, char **argv) {
exit(0); /* don't need finish() here, no parsing done */
}
- /* AMSB always prints a blank line when it LISTs, so we do too. */
- fputc(EOL, outfile);
-
while(next_line())
linecount++;
- finish(0, NULL);
+ finish(0);
return 0; /* never executes; shuts up gcc warning */
}