diff options
| author | B. Watson <urchlay@slackware.uk> | 2025-12-05 01:29:03 -0500 |
|---|---|---|
| committer | B. Watson <urchlay@slackware.uk> | 2025-12-05 01:29:03 -0500 |
| commit | d254450d9dcf29ee08df42e2594cb86abc1d54d3 (patch) | |
| tree | dd452e3030e8933f34e6e098a6621c1bfef4bcca | |
| parent | 27e2056ec8f5aa2c8ac31ec7d0e34abb99a53cb5 (diff) | |
| download | alftools-d254450d9dcf29ee08df42e2594cb86abc1d54d3.tar.gz | |
unalf: Add -n and -s options.
| -rw-r--r-- | CHANGES.txt | 1 | ||||
| -rw-r--r-- | TODO.txt | 2 | ||||
| -rw-r--r-- | src/extract.c | 93 | ||||
| -rw-r--r-- | src/opts.c | 8 | ||||
| -rw-r--r-- | src/unalf.1 | 21 | ||||
| -rw-r--r-- | src/unalf.h | 2 | ||||
| -rw-r--r-- | src/unalf.rst | 17 | ||||
| -rw-r--r-- | src/usage.c | 2 |
8 files changed, 121 insertions, 25 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index d88259b..c376a11 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,6 @@ 0.4.0: - alf and unalf now detect duplicate Atari filenames. +- unalf can extract by file number (-n) rather than wildcards. 0.3.2: - unalf: truncation is a fatal error again (avoids segfaults). @@ -1,4 +1,4 @@ -- unalf: -s option (split alf file), -n (extract by number) +- error checking for unalf -n (dup option, bad #, wildcards) - maybe: alf foo.alf unix.filename=ATARI.NAM ...unalf could use this too. diff --git a/src/extract.c b/src/extract.c index 98523db..6f24c0d 100644 --- a/src/extract.c +++ b/src/extract.c @@ -76,27 +76,81 @@ void set_datetime() { } } -void make_backup(void) { +void make_backup(const char *fname) { /* up to 12-char FILENAME.EXT, plus a ~, plus null terminator = 14 */ - char backup[14]; + /* has to handle up to 16 chars for -s option: LONGFILE.NAME.ALF */ + char backup[20]; - strncpy(backup, out_filename, 13); - strncat(backup, "~", 13); + strncpy(backup, fname, 19); + strncat(backup, "~", 19); /* silently ignore errors! */ - rename(out_filename, backup); + rename(fname, backup); +} + +void open_out_file(const char *fname) { + if(opts.testonly) { + fname = "/dev/null"; + } else if(!opts.overwrite) { + make_backup(fname); + } + + if(!(out_file = fopen(fname, "wb"))) { + fprintf(stderr, "%s: fatal: ", self); + perror(fname); + exit(1); + } +} + +void truncated_err(void); + +void write_split_file(void) { + int i, c; + unsigned int len; + char splitname[20]; + + strncpy(splitname, out_filename, 13); + strncat(splitname, ".ALF", 6); + + if(!opts.quiet) + printf("Writing %s\n", splitname); + + len = getquad(alf_hdr_compsize0); + + open_out_file(splitname); + fwrite(&mem[alf_header], 1, 29, out_file); + for(i = 0; i < len; i++) { + c = fgetc(in_file); + if(c == EOF) { + truncated_err(); + } + fputc(c, out_file); + } + fclose(out_file); } void extract_alf(void) { + int files_extracted = 0, skip, file_number = 0; + /* get ready to call fake 6502 stuff. set up memory like the Atari. */ dpoke(MEMTOP, 0xbc1f); while(read_alf_header()) { + file_number++; + skip = 0; + out_filename = (char *)(mem + alf_hdr_filename); fix_filename(); - if(!file_wanted(out_filename)) { + if(opts.extract_num) + skip = (opts.extract_num != file_number); + else + skip = !file_wanted(out_filename); + + if(skip) { + if(!opts.quiet) + printf("Skipping %s\n", out_filename); if(fseek(in_file, getquad(alf_hdr_compsize0), SEEK_CUR) != 0) { fprintf(stderr, "%s: fatal: seek failed on input!\n", self); exit(1); @@ -105,6 +159,13 @@ void extract_alf(void) { continue; } + files_extracted++; + + if(opts.split) { + write_split_file(); + continue; + } + if(!opts.quiet) { printf("%s %s\n", opts.testonly ? "Testing" : "Uncrunching", out_filename); } @@ -112,21 +173,7 @@ void extract_alf(void) { if(opts.extract_to_stdout) { out_file = stdout; } else { - char *realname = out_filename; - - if(opts.testonly) { - out_filename = "/dev/null"; - } else if(!opts.overwrite) { - make_backup(); - } - - if(!(out_file = fopen(out_filename, "wb"))) { - fprintf(stderr, "%s: fatal: ", self); - perror(out_filename); - exit(1); - } - - out_filename = realname; + open_out_file(out_filename); } bad_checksum = bytes_written = 0; @@ -153,6 +200,10 @@ void extract_alf(void) { else printf("All files OK.\n"); } + + if(!opts.quiet && !files_extracted) { + printf("No files %s!\n", opts.testonly ? "tested" : "extracted"); + } } void chksum_err(void) { @@ -1,6 +1,6 @@ #include "unalf.h" -#define OPTIONS "aefFklLopqtTvVd:x:" +#define OPTIONS "aefFklLon:pqstTvVd:x:" /* uncomment to test exclude/include glob lists */ // #define DEBUG_GLOBS @@ -25,6 +25,10 @@ static void show_globs(void) { } #endif +int parsenum(const char opt, const char *arg) { + return atoi(arg); /* TODO: use strtol(), die on error */ +} + void parse_opts(int argc, char * const *argv) { int opt; char **ig; @@ -41,9 +45,11 @@ void parse_opts(int argc, char * const *argv) { case 'F': opts.force++; break; case 'l': opts.listonly++; opts.testonly = 0; break; case 'L': opts.lowercase++; break; + case 'n': opts.extract_num = parsenum('n', optarg); break; case 'o': opts.overwrite++; break; case 'p': opts.extract_to_stdout++; opts.quiet++; break; case 'q': opts.quiet++; break; + case 's': opts.split++; opts.testonly = 0; opts.listonly = 0; break; case 't': opts.testonly++; opts.listonly = 0; break; case 'T': opts.ignore_datetime = 1; break; case 'v': opts.listonly = 1; opts.testonly = 0; opts.verbose_list++; break; diff --git a/src/unalf.1 b/src/unalf.1 index 743cc27..9af4d47 100644 --- a/src/unalf.1 +++ b/src/unalf.1 @@ -36,7 +36,7 @@ unalf \- extract Atari 8-bit ALF archives . .SH SYNOPSIS .sp -unalf [\fB\-\-help\fP] [\fB\-aehtklLopqtv\fP] [\fB\-d\fP \fIdir\fP] [\fB\-x\fP \fIwildcard\fP] \fIalf\-file\fP [\fIwildcard\fP ...] +unalf [\fB\-\-help\fP] [\fB\-aehtklLopqstv\fP] [\fB\-d\fP \fIdir\fP] [\fB\-n\fP \fInumber\fP] [\fB\-x\fP \fIwildcard\fP] \fIalf\-file\fP [\fIwildcard\fP ...] .SH DESCRIPTION .sp \fBunalf\fP lists, extracts, or tests the contents of an \fIALF\fP archive. @@ -154,6 +154,15 @@ Lowercase filenames. Example: \fBFOO.TXT\fP will extract to \fBfoo.txt\fP\&. . .INDENT 0.0 .TP +.BI \-n \ number +Only extract the \fInumber\fPth file. \fIwildcards\fP are ignored when using +this option. Numbering begins at 1 for the first file in the archive; +\fB\-n0\fP is an error. +.UNINDENT +.\" extract only file #<number> (counting from 1). +. +.INDENT 0.0 +.TP .B \-o Overwrite files, if they already exist. The default is to rename existing files, adding a \fB~\fP suffix. Note that renaming \fBfile\fP @@ -188,6 +197,16 @@ doesn\(aqt do anything when used with \fB\-l\fP or \fB\-v\fP\&. . .INDENT 0.0 .TP +.B \-s +Split \fIALF\fP file into multiple \fIALF\fP files, one per archive member. +The new \fIALF\fP files are named after the filenames in the input +\fIALF\fP file, with ".ALF" appended. There is no corresponding option +to join them back together, but you can use \fBcat\fP(1) for that. +.UNINDENT +.\" split ALF file into multiple ALFs. +. +.INDENT 0.0 +.TP .B \-t Test archive. Same as extraction, except the files are not written anywhere. diff --git a/src/unalf.h b/src/unalf.h index 4adaeb3..f03ae04 100644 --- a/src/unalf.h +++ b/src/unalf.h @@ -81,6 +81,8 @@ typedef struct { int fixjunk; int ignore_datetime; int force; + int extract_num; + int split; } opts_t; #define MAX_EXCLUDES 256 diff --git a/src/unalf.rst b/src/unalf.rst index ed89d2d..9d8e14a 100644 --- a/src/unalf.rst +++ b/src/unalf.rst @@ -21,7 +21,7 @@ extract Atari 8-bit ALF archives SYNOPSIS ======== -unalf [**--help**] [**-aehtklLopqtv**] [**-d** *dir*] [**-x** *wildcard*] *alf-file* [*wildcard* ...] +unalf [**--help**] [**-aehtklLopqstv**] [**-d** *dir*] [**-n** *number*] [**-x** *wildcard*] *alf-file* [*wildcard* ...] DESCRIPTION =========== @@ -122,6 +122,13 @@ OPTIONS .. use lowercase filenames. +-n number + Only extract the *number*\th file. *wildcards* are ignored when using + this option. Numbering begins at 1 for the first file in the archive; + **-n0** is an error. + +.. extract only file #<number> (counting from 1). + -o Overwrite files, if they already exist. The default is to rename existing files, adding a **~** suffix. Note that renaming **file** @@ -150,6 +157,14 @@ OPTIONS .. quiet: don't print filenames during extraction/testing. +-s + Split *ALF* file into multiple *ALF* files, one per archive member. + The new *ALF* files are named after the filenames in the input + *ALF* file, with ".ALF" appended. There is no corresponding option + to join them back together, but you can use **cat**\(1) for that. + +.. split ALF file into multiple ALFs. + -t Test archive. Same as extraction, except the files are not written anywhere. diff --git a/src/usage.c b/src/usage.c index 0f43fca..f03a373 100644 --- a/src/usage.c +++ b/src/usage.c @@ -9,9 +9,11 @@ const char *usage_msg[] = { " -k: keep trailing periods (dots) in filenames.", " -l: list files in archive (filenames only).", " -L: use lowercase filenames.", + " -n: extract only file #<number> (counting from 1).", " -o: overwrite files (do not create file~ backups).", " -p: extract to stdout (enables -q).", " -q: quiet: don't print filenames during extraction/testing.", + " -s: split ALF file into multiple ALFs.", " -t: test archive.", " -v: verbose listing of archive contents.", " -V: show version number.", |
