diff options
author | B. Watson <urchlay@slackware.uk> | 2024-07-08 22:45:32 -0400 |
---|---|---|
committer | B. Watson <urchlay@slackware.uk> | 2024-07-08 22:45:32 -0400 |
commit | 6f1133ef4cd268098d0695dbdf9d285748a4108c (patch) | |
tree | 709d4e48a6a600bc917c21058da683c85ed88e45 /whichbas.c | |
parent | dbb9ac8e29103dc4dce5c9fe830ddb59e5b4e785 (diff) | |
download | bw-atari8-tools-6f1133ef4cd268098d0695dbdf9d285748a4108c.tar.gz |
whichbas: add -s (script mode) option.
Diffstat (limited to 'whichbas.c')
-rw-r--r-- | whichbas.c | 152 |
1 files changed, 116 insertions, 36 deletions
@@ -17,24 +17,40 @@ int bas_type = 0x0f; /* start out with all enabled */ +#define SRET_ATARI 3 +#define SRET_TURBO 4 +#define SRET_BXL 5 +#define SRET_BXE 6 +#define SRET_TURBO_OR_BXL 7 +#define SRET_TURBO_OR_BXE 8 +#define SRET_TURBO_OR_BXL_OR_BXE 9 +#define SRET_UKNOWN_DERIV 10 +#define SRET_AMSB 11 +#define SRET_EXTENDED_BXE 12 +#define SRET_NOT_BASIC 32 + +int script_mode = 0; /* -s flag */ +int script_ret; /* -s mode, exit with one of SRET_*, above, as status */ + +int keep_going = 0; /* -k flag */ + int comma_count; /* count of regular commas (not string/array) in statement */ unsigned char last_cmd; unsigned char last_op_tok; unsigned short last_cmd_pos; -int keep_going = 0; /* -k flag */ - void print_help(void) { - printf("Usage: %s [-v] [-k] <inputfile>\n", self); + printf("Usage: %s [-v] [-k] [-s] <inputfile>\n", self); } void parse_args(int argc, char **argv) { int opt; - while( (opt = getopt(argc, argv, "vk")) != -1) { + while( (opt = getopt(argc, argv, "vks")) != -1) { switch(opt) { case 'v': verbose = 1; break; case 'k': keep_going = verbose = 1; break; + case 's': script_mode = 1; break; default: print_help(); exit(1); @@ -62,28 +78,39 @@ void print_result(void) { if(bas_type == BT_INVALID) { name = "Unknown variant: Not Atari BASIC, Turbo, BXL, or BXE"; + script_ret = SRET_UKNOWN_DERIV; } else if(bas_type & BT_ATARI) { name = "Atari BASIC"; + script_ret = SRET_ATARI; } else if(bas_type & BT_TURBO) { if(bas_type & BT_BXL) { name = "Either Turbo BASIC XL or OSS BASIC XL"; + script_ret = SRET_TURBO_OR_BXL; } else if(bas_type & BT_BXE) { name = "Either Turbo BASIC XL or OSS BASIC XE"; + script_ret = SRET_TURBO_OR_BXE; } else { /* bas_type == BT_TURBO */ name = "Turbo BASIC XL"; + script_ret = SRET_TURBO; } } else if(bas_type == BT_BXL || bas_type == (BT_BXL | BT_BXE)) { name = "OSS BASIC XL"; + script_ret = SRET_BXL; } else if(bas_type == BT_BXE) { name = "OSS BASIC XE"; + script_ret = SRET_BXE; } else { /* this one should never happen: */ name = "Either Turbo BASIC XL, OSS BASIC XL, or OSS BASIC XE"; + script_ret = SRET_TURBO_OR_BXL_OR_BXE; } - puts(name); - - exit(bas_type == BT_ATARI ? 0 : bas_type + 8); + if(script_mode) { + exit(script_ret); + } else { + puts(name); + exit(0); + } } void remove_type(int type) { @@ -137,7 +164,7 @@ CALLBACK(handle_cmd) { } /* partial: we really should detect GET #1,A$. this is Turbo-only, but probably nobody ever uses it because it doesn't seem to *work*, - at least not in TB 1.5. */ + at least not in TB 1.5. A$ always ends up empty with length 0. */ break; case CMD_RESTORE: case CMD_TRAP: @@ -171,13 +198,18 @@ CALLBACK(handle_cmd) { 0x4B: RENAME in both Turbo and BXL/XE (take the same args) 0x60: CLS or HITCLR (no args either way) This leaves 42 we can check. - Covered so far: 38 (90%) + Covered so far: 39 (93%) TODO: Unknown tokens: - 0x54: ??? in TB (find out what), LVAR in BXL/BXE. - 0x5A: BLOAD or... what? (Jindroush lists it as ?5A?) + 0x5A: BLOAD or... what? (Jindroush lists it as BXL_EXTEND for BXL and + ?5A? for BXE) TODO: 0x5B: BRUN or CALL (both take a string, CALL allows "USING" though) - 0x5F: PAINT (req 2 args) or NUM (optional 2 args, probly not appear in program) + This isn't really important, as CALL requires a PROCEDURE to + exit, and we *do* catch the PROCEDURE token. + 0x5F: PAINT (req 2 args) or NUM (optional 2 args). + Again, not important, because it's highly unlikely any BXL/BXE + program will contain NUM... because when it executes, it stops the + program and goes back to the READY prompt (in auto-numbering mode). */ switch(tok) { case 0x39: /* MOVE <args> or ENDWHILE */ @@ -263,6 +295,17 @@ CALLBACK(handle_cmd) { remove_type(BT_TURBO); } break; + case 0x54: /* -- in TB, LVAR in BXL/BXE */ + /* we can tell these apart because TB gives us a next-statement offset of 5 + when we use this (normally, the minimum offset is 6, but there's no OP_EOS + after this token for some reason). */ + if(program[pos - 1] == 0x05) { + bas_type = BT_TURBO; + print_result(); + } else { + remove_type(BT_TURBO); + } + break; case 0x57: /* DUMP (1 optional string arg) or LOCAL (1 *numeric* variable arg) */ /* BXL/BXE's LOCAL only works on scalars, not arrays or strings. so if there's no arg, or one string arg... */ @@ -539,62 +582,99 @@ CALLBACK(handle_end_stmt) { last_cmd = last_op_tok = 0; } -void foreign(const char *name) { +/* return true if input_file is Atari MS BASIC. + AMSB files begin with a 3-byte header: 0x00, then 2 byte length + (LSB/MSB), which is actually 3 bytes less than the full length of + the file (or, it's the length of the file minus the 3-byte header). + Also, the files always end with 3 0x00 bytes. + We check that the header length is 3 bytes less than the file length, + then check for the 3 0x00's at the end. + */ +int detect_amsb(void) { + int len, c; + + if(verbose) fprintf(stderr, "entering detect_amsb()\n"); + + rewind(input_file); + c = fgetc(input_file); + if(c) return 0; + c = fgetc(input_file); + if(c == EOF) return 0; + len = (fgetc(input_file) << 8) | c; + + if(verbose) fprintf(stderr, "detect_amsb() header len==%d (file size should be %d)\n", len, len + 3); + + fseek(input_file, 0, SEEK_END); + c = ftell(input_file); + if(verbose) fprintf(stderr, "detect_amsb() file size %d\n", c); + if(len != (c - 3)) { + if(verbose) fprintf(stderr, "detect_amsb() wrong file size!\n"); + return 0; + } + + if(verbose) fprintf(stderr, "detect_amsb() file size is correct, checking for 3 nulls\n"); + fseek(input_file, -3, SEEK_END); + if(fgetc(input_file)) return 0; + if(fgetc(input_file)) return 0; + if(fgetc(input_file)) return 0; + + if(verbose) fprintf(stderr, "detect_amsb() found 3 nulls, return 1\n"); + + return 1; +} + +void foreign(const char *name, int srval) { fclose(input_file); - puts(name); - exit(0); /* TODO: pick a better number */ + if(script_mode) { + exit(srval); + } else { + puts(name); + exit(0); + } } void detect_foreign(void) { - int i, nuls, c, d; + int c, d; c = fgetc(input_file); d = fgetc(input_file); + if(c == EOF || d == EOF) + die("File is too short to be a BASIC program of any kind."); + if(c == 0 && d == 0) { /* This is why we can't read from stdin. */ rewind(input_file); return; } - if(c == EOF || d == EOF) - die("File is too short to be a BASIC program of any kind."); - if(c == 0xff && d == 0xff) - foreign("XEX executable (not BASIC at all!)"); + foreign("XEX executable (not BASIC at all!)", SRET_NOT_BASIC); if(c == 0xfe && d == 0xfe) - foreign("Mac/65 tokenized source (not BASIC at all!)"); + foreign("Mac/65 tokenized source (not BASIC at all!)", SRET_NOT_BASIC); if(c == 0xdd && d == 0x00) - foreign("EXTENDed OSS BASIC XE"); + foreign("EXTENDed OSS BASIC XE", SRET_EXTENDED_BXE); if(c == 0x7f && d == 'E') { c = fgetc(input_file); d = fgetc(input_file); if(c == 'L' && d == 'F') - foreign("ELF executable (huh?)"); + foreign("ELF executable (not BASIC at all!)", SRET_NOT_BASIC); } - if(!(c == 0 && d == 0)) { - if(fseek(input_file, -3, SEEK_END) == 0) { - nuls = 0; - for(i = 0; i < 3; i++) { - if(fgetc(input_file) == 0) nuls++; - } - if(nuls == 3) { - foreign("Microsoft BASIC"); - } - } + if(c == 0 && detect_amsb()) { + foreign("Atari Microsoft BASIC", SRET_AMSB); } if(isdigit(c) && (d == 0x20 || isdigit(d))) - foreign("Text file, could be LISTed BASIC (or not)"); + foreign("Text file, could be LISTed BASIC (or not)", SRET_NOT_BASIC); if(isprint(c) && isprint(d)) - foreign("Text file (not BASIC at all!)"); + foreign("Text file (not BASIC at all!)", SRET_NOT_BASIC); - foreign("Unknown file type (not BASIC at all!)"); + foreign("Unknown file type (not BASIC at all!)", SRET_NOT_BASIC); } void check_variables(void) { |