Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -l list option to only list filenames #3

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 38 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ Synopsis
_sunzip_ is a streaming unzip utility. It will read a .zip file from stdin and
decompress its contents into the current directory. Command line options allow
specifying a different destination directory, overwriting existing files
(normally prevented), and testing the contents of .zip file instead of writing
the decompressed files.
(normally prevented), and testing or listing the contents of .zip file instead
of writing the decompressed files.

_sunzip_ can decompress methods 0 (stored), 8 (deflated), 9 (Deflate64), 10
(DCL imploded), and 12 (bzip2). _sunzip_ handles Zip64 .zip files. It does not
Expand All @@ -28,12 +28,44 @@ Compile and link with zlib, infback9.c and inftree9.c (found in zlib's contrib
directory), blast.c (also in contrib), and libbz2. blast.c from zlib 1.2.9 or
later must be used.

Test
----

`cat any.zip | sunzip`

Usage
-----

cat any.zip | sunzip

For help, run `sunzip` without arguments on a terminal:

```
sunzip 0.4, streaming unzip by Mark Adler
usage: ... | sunzip [-t] [-o] [-p x] [-q[q]] [dir]
sunzip [-t] [-o] [-p x] [-q[q]] [dir] < infile.zip

-t: test -- don't write files
-l: list zip filenames -- don't write files
-o: overwrite existing files
-p x: replace parent reference .. with this character
-q: quiet -- display summary info and errors only
-qq: really quiet -- display errors only
dir: subdirectory to create files in (if writing)
```

`sunzip` will decompress to the current directory unless `dir` is specified.

The `sunzip -t` option will test decompression and verify crc checksums
without writing any files to disk.

The `sunzip -l` option is equivalent to `-t -q -q`, but instead
of testing decompression will only print the file and directory
names as they are encountered in the stream. Note that file
names from the local file headers are less reliable than the
end-of-file TOC that would otherwise be used, and may
include duplicates, deleted and encrypted files.



License
-------

This code is under the zlib license, permitting free commercial use.
This code is under the [zlib license](sunzip.c), permitting free commercial use.
63 changes: 47 additions & 16 deletions sunzip.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
Allow bit 11 to be set in general purpose flags
0.4 11 Jul 2016 Use blast for DCL imploded entries (method 10)
Add zlib license
NEXT xx xxx 2019 Add -l list option to only list filenames

*/

Expand Down Expand Up @@ -1127,8 +1128,8 @@ local void bad(char *why, unsigned long entry,
limit output if quiet is 1, more so if quiet is >= 2, write the decompressed
data to files if write is true, otherwise just verify the entries, overwrite
existing files if over is true, otherwise don't -- over must not be true if
write is false */
local void sunzip(int file, int quiet, int write, int over)
write is false; if list is true, only print file names */
local void sunzip(int file, int quiet, int write, int over, int list)
{
enum { /* looking for ... */
MARK, /* spanning signature (optional) */
Expand Down Expand Up @@ -1253,6 +1254,8 @@ local void sunzip(int file, int quiet, int write, int over)
if (flag & 0xf7f0U)
bye("unknown zip header flags set");
method = get2(in); /* compression method */
if ((flag & 8) && list)
bye("cannot handle deferred lengths without decompressing (try -t)");
if ((flag & 8) && method != 8 && method != 9 && method != 12)
bye("cannot handle deferred lengths for pre-deflate methods");
acc = mod = dos2time(get4(in)); /* file date/time */
Expand All @@ -1265,8 +1268,21 @@ local void sunzip(int file, int quiet, int write, int over)
nlen = get2(in); /* file name length */
xlen = get2(in); /* extra field length */

/* skip file name (will get from central directory later) */
skip(nlen, in);
if (list) {
/* read file name for listing */
field(nlen, in);
tmp = crc32(crc32(0L, Z_NULL, 0), outbuf, nlen); /* name crc */
from = (char *)outbuf;
tostr(from, nlen); /* make name into a string */
name = strnew(from); /* copy from outbuf */

// We will print later, as we have to check for UTF-8
//outbuf[nlen] = '\0';
//printf("%s\n", outbuf); // TODO: encoding etc
} else {
/* skip file name field */
skip(nlen, in);
}

/* process extra field -- get entry times if there and, if needed,
get zip64 lengths */
Expand All @@ -1276,6 +1292,14 @@ local void sunzip(int file, int quiet, int write, int over)
high = zip64local(outbuf, xlen,
&clen, &clen_hi, &ulen, &ulen_hi);

if (list) {
/* check for replacement utf8 name in extra field */
name = utf8name(outbuf, xlen, tmp, name);
name = tohere(name, madeby); /* convert name for this OS */
name = guard(name); /* keep the name safe */
printf("%s\n", name);
}

/* create temporary file (including for directories and links) */
if (write && (method == 0 || method == 8 || method == 9 ||
method == 10 || method == 12)) {
Expand All @@ -1297,7 +1321,7 @@ local void sunzip(int file, int quiet, int write, int over)
/* process compressed data */
if (flag & 1)
method = UINT_MAX;
if (method == 0) { /* stored */
if (!list && method == 0) { /* stored */
if (clen != ulen || clen_hi != ulen_hi)
bye("zip file format error (stored lengths mismatch)");
while (clen_hi || clen > left) {
Expand All @@ -1316,7 +1340,7 @@ local void sunzip(int file, int quiet, int write, int over)
clen = ulen;
clen_hi = ulen_hi;
}
else if (method == 8) { /* deflated */
else if (!list && method == 8) { /* deflated */
if (strm == NULL) { /* initialize inflater first time */
strm = &strms;
strm->zalloc = Z_NULL;
Expand All @@ -1339,7 +1363,7 @@ local void sunzip(int file, int quiet, int write, int over)
}
}
#ifndef JUST_DEFLATE
else if (method == 9) { /* deflated with deflate64 */
else if (!list && method == 9) { /* deflated with deflate64 */
if (strm9 == NULL) { /* initialize first time */
strm9 = &strms9;
strm9->zalloc = Z_NULL;
Expand All @@ -1361,15 +1385,15 @@ local void sunzip(int file, int quiet, int write, int over)
bye("zip file corrupted -- cannot continue");
}
}
else if (method == 10) { /* PKWare DCL implode */
else if (!list && method == 10) { /* PKWare DCL implode */
ret = blast(get, in, put, out, &left, &next);
if (ret != 0) {
bad("DCL imploded data corrupted",
entries, here, here_hi);
bye("zip file corrupted -- cannot continue");
}
}
else if (method == 12) { /* bzip2 compression */
else if (!list && method == 12) { /* bzip2 compression */
left = bunzip2(next, left, in, out, outbuf, &back);
if (back == NULL) {
bad("bzip2 compressed data corrupted",
Expand All @@ -1379,8 +1403,8 @@ local void sunzip(int file, int quiet, int write, int over)
next = back;
}
#endif
else { /* skip encrpyted or unknown method */
if (quiet < 1)
else { /* skip (encrypted or unknown method?) */
if (!list && quiet < 1)
bad(flag & 1 ? "skipping encrypted entry" :
"skipping unknown compression method",
entries, here, here_hi);
Expand Down Expand Up @@ -1460,8 +1484,8 @@ local void sunzip(int file, int quiet, int write, int over)
}

/* verify entry and display information (won't do if skipped) */
if (method == 0 || method == 8 || method == 9 || method == 10 ||
method == 12) {
if (! list && (method == 0 || method == 8 || method == 9 || method == 10 ||
method == 12)) {
if (!GOOD()) {
bad("compressed data corrupted, check values mismatch",
entries, here, here_hi);
Expand Down Expand Up @@ -1693,7 +1717,8 @@ local void sunzip(int file, int quiet, int write, int over)
} until (mode == END); /* until end record reached (or EOF) */

/* summarize and clean up */
summary(entries, exist, write, quiet);
if (! list)
summary(entries, exist, write, quiet);
if (write) {
rmtempdir(); /* remove the temporary directory */
setdirtimes(root); /* set saved directory times */
Expand Down Expand Up @@ -1728,7 +1753,7 @@ local void cutshort(int n)
int main(int argc, char **argv)
{
int n, parm;
int quiet = 0, write = 1, over = 0;
int quiet = 0, write = 1, over = 0, list = 0;
char *arg;

/* for rmtempdir(), called by bye() */
Expand All @@ -1744,6 +1769,7 @@ int main(int argc, char **argv)
puts(" sunzip [-t] [-o] [-p x] [-q[q]] [dir] < infile.zip");
puts("");
puts("\t-t: test -- don't write files");
puts("\t-l: list zip filenames -- don't write files");
puts("\t-o: overwrite existing files");
puts("\t-p x: replace parent reference .. with this character");
puts("\t-q: quiet -- display summary info and errors only");
Expand Down Expand Up @@ -1777,6 +1803,11 @@ int main(int argc, char **argv)
case 't': /* test */
write = 0;
break;
case 'l': /* list */
write = 0;
quiet = 2;
list = 1;
break;
default:
bye("unknown option");
}
Expand Down Expand Up @@ -1813,6 +1844,6 @@ int main(int argc, char **argv)
}

/* unzip from stdin */
sunzip(0, quiet, write, over);
sunzip(0, quiet, write, over, list);
return 0;
}