diff options
author | B. Watson <urchlay@slackware.uk> | 2025-03-04 04:00:18 -0500 |
---|---|---|
committer | B. Watson <urchlay@slackware.uk> | 2025-03-04 04:00:18 -0500 |
commit | 1dd4dfd7f11161b62b8ae1c4ca80b8cea51e42f1 (patch) | |
tree | f9407344412e1da37d99ccc600ef3d56db72326f /listamsb.c | |
parent | dc1b4554e386a3335376482225a39b919e3a23f8 (diff) | |
download | bw-atari8-tools-1dd4dfd7f11161b62b8ae1c4ca80b8cea51e42f1.tar.gz |
listamsb: clean up warnings and error exits, only print initial EOL if any code was found.
Diffstat (limited to 'listamsb.c')
-rw-r--r-- | listamsb.c | 163 |
1 files changed, 84 insertions, 79 deletions
@@ -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 */ } |