aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt1
-rw-r--r--TODO.txt2
-rw-r--r--src/extract.c93
-rw-r--r--src/opts.c8
-rw-r--r--src/unalf.121
-rw-r--r--src/unalf.h2
-rw-r--r--src/unalf.rst17
-rw-r--r--src/usage.c2
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).
diff --git a/TODO.txt b/TODO.txt
index cd010ec..5b0b9d9 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -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) {
diff --git a/src/opts.c b/src/opts.c
index 977a10d..06ad9dc 100644
--- a/src/opts.c
+++ b/src/opts.c
@@ -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.",