Add -p and -q support.

This commit is contained in:
joerg 2009-08-22 02:19:42 +00:00
parent 66681277bc
commit 5e2ef53f3e
2 changed files with 164 additions and 11 deletions

View File

@ -1,4 +1,5 @@
.\"-
.\" Copyright (c) 2009 Joerg Sonnenberger <joerg@NetBSD.org>
.\" Copyright (c) 2007-2008 Dag-Erling Coïdan Smørgrav
.\" All rights reserved.
.\"
@ -24,9 +25,9 @@
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD: revision 180125$
.\" $NetBSD: unzip.1,v 1.2 2009/06/26 09:31:04 wiz Exp $
.\" $NetBSD: unzip.1,v 1.3 2009/08/22 02:19:42 joerg Exp $
.\"
.Dd June 30, 2008
.Dd August 22, 2009
.Dt UNZIP 1
.Os
.Sh NAME
@ -34,7 +35,7 @@
.Nd extract files from a ZIP archive
.Sh SYNOPSIS
.Nm
.Op Fl afjLlnoqtu
.Op Fl afjLlnopqtuv
.Op Fl d Ar dir
.Op Fl x Ar pattern
.Ar zipfile
@ -65,9 +66,15 @@ When extracting a file from the zipfile, if a file with the same name
already exists on disk, the file is silently skipped.
.It Fl o
Overwrite.
When extacting a file from the zipfile, if a file with the same name
When extracting a file from the zipfile, if a file with the same name
already exists on disk, the existing file is replaced with the file
from the zipfile.
.It Fl p
Extract to stdout.
When extracting files from the zipfile, they are written to stdout.
The normal output is suppressed as if
.Fl q
was specified.
.It Fl q
Quiet: print less information while extracting.
.It Fl t
@ -79,6 +86,13 @@ When extracting a file from the zipfile, if a file with the same name
already exists on disk, the existing file is replaced with the file
from the zipfile if and only if the latter is newer than the former.
Otherwise, the file is silently skipped.
.It Fl v
List verbosely, rather than extract, the contents of the zipfile.
This differs from
.Fl l
by using the long listening.
Note that most of the data is currently fake and does not reflect the
content of the archive.
.It Fl x Ar pattern
Exclude files matching the pattern
.Ar pattern .

View File

@ -1,4 +1,4 @@
/* $NetBSD: unzip.c,v 1.1 2009/06/25 20:27:05 joerg Exp $ */
/* $NetBSD: unzip.c,v 1.2 2009/08/22 02:19:42 joerg Exp $ */
/*-
* Copyright (c) 2009 Joerg Sonnenberger <joerg@NetBSD.org>
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: unzip.c,v 1.1 2009/06/25 20:27:05 joerg Exp $");
__RCSID("$NetBSD: unzip.c,v 1.2 2009/08/22 02:19:42 joerg Exp $");
#include <sys/queue.h>
#include <sys/stat.h>
@ -61,12 +61,13 @@ static const char *d_arg; /* directory */
static int f_opt; /* update existing files only */
static int j_opt; /* junk directories */
static int L_opt; /* lowercase names */
static int l_opt; /* list */
static int n_opt; /* never overwrite */
static int o_opt; /* always overwrite */
static int p_opt; /* extract to stdout */
static int q_opt; /* quiet */
static int t_opt; /* test */
static int u_opt; /* update */
static int v_opt; /* verbose */
/* time when unzip started */
static time_t now;
@ -631,14 +632,142 @@ extract(struct archive *a, struct archive_entry *e)
free(pathname);
}
static void
extract_stdout(struct archive *a, struct archive_entry *e)
{
char *pathname;
mode_t filetype;
int cr, text, warn;
ssize_t len;
unsigned char *p, *q, *end;
pathname = pathdup(archive_entry_pathname(e));
filetype = archive_entry_filetype(e);
/* I don't think this can happen in a zipfile.. */
if (!S_ISDIR(filetype) && !S_ISREG(filetype)) {
warningx("skipping non-regular entry '%s'", pathname);
ac(archive_read_data_skip(a));
free(pathname);
return;
}
/* skip directories in -j case */
if (S_ISDIR(filetype)) {
ac(archive_read_data_skip(a));
free(pathname);
return;
}
/* apply include / exclude patterns */
if (!accept_pathname(pathname)) {
ac(archive_read_data_skip(a));
free(pathname);
return;
}
text = a_opt;
warn = 0;
cr = 0;
for (int n = 0; ; n++) {
len = archive_read_data(a, buffer, sizeof buffer);
if (len < 0)
ac(len);
/* left over CR from previous buffer */
if (a_opt && cr) {
if (len == 0 || buffer[0] != '\n')
if (write(STDOUT_FILENO, "\r", 1) != 1)
error("write('%s')", pathname);
cr = 0;
}
/* EOF */
if (len == 0)
break;
end = buffer + len;
/*
* Detect whether this is a text file. The correct way to
* do this is to check the least significant bit of the
* "internal file attributes" field of the corresponding
* file header in the central directory, but libarchive
* does not read the central directory, so we have to
* guess by looking for non-ASCII characters in the
* buffer. Hopefully we won't guess wrong. If we do
* guess wrong, we print a warning message later.
*/
if (a_opt && n == 0) {
for (p = buffer; p < end; ++p) {
if (!isascii((unsigned char)*p)) {
text = 0;
break;
}
}
}
/* simple case */
if (!a_opt || !text) {
if (write(STDOUT_FILENO, buffer, len) != len)
error("write('%s')", pathname);
continue;
}
/* hard case: convert \r\n to \n (sigh...) */
for (p = buffer; p < end; p = q + 1) {
for (q = p; q < end; q++) {
if (!warn && !isascii(*q)) {
warningx("%s may be corrupted due"
" to weak text file detection"
" heuristic", pathname);
warn = 1;
}
if (q[0] != '\r')
continue;
if (&q[1] == end) {
cr = 1;
break;
}
if (q[1] == '\n')
break;
}
if (write(STDOUT_FILENO, p, q - p) != q - p)
error("write('%s')", pathname);
}
}
free(pathname);
}
/*
* Print the name of an entry to stdout.
*/
static void
list(struct archive *a, struct archive_entry *e)
{
static int printed_header;
char buf[20];
time_t mtime;
printf("%s\n", archive_entry_pathname(e));
if (!printed_header && !q_opt) {
printed_header = 1;
printf(" Length Method Size Ratio Date Time CRC-32 Name\n");
printf("-------- ------ ------- ----- ---- ---- ------ ----\n");
}
if (v_opt == 2) {
mtime = archive_entry_mtime(e);
strftime(buf, sizeof(buf), "%m-%d-%g %R", localtime(&mtime));
printf("%8ju Stored %7ju 0%% %s %08x %s\n",
(uintmax_t)archive_entry_size(e),
(uintmax_t)archive_entry_size(e),
buf,
0U,
archive_entry_pathname(e));
} else {
printf("%s\n", archive_entry_pathname(e));
}
ac(archive_read_data_skip(a));
}
@ -693,8 +822,10 @@ unzip(const char *fn)
ac(ret);
if (t_opt)
test(a, e);
else if (l_opt)
else if (v_opt)
list(a, e);
else if (p_opt)
extract_stdout(a, e);
else
extract(a, e);
}
@ -722,7 +853,7 @@ getopts(int argc, char *argv[])
int opt;
optreset = optind = 1;
while ((opt = getopt(argc, argv, "ad:fjLlnoqtux:")) != -1)
while ((opt = getopt(argc, argv, "ad:fjLlnopqtuvx:")) != -1)
switch (opt) {
case 'a':
a_opt = 1;
@ -740,13 +871,18 @@ getopts(int argc, char *argv[])
L_opt = 1;
break;
case 'l':
l_opt = 1;
if (v_opt == 0)
v_opt = 1;
break;
case 'n':
n_opt = 1;
break;
case 'o':
o_opt = 1;
q_opt = 1;
break;
case 'p':
p_opt = 1;
break;
case 'q':
q_opt = 1;
@ -757,6 +893,9 @@ getopts(int argc, char *argv[])
case 'u':
u_opt = 1;
break;
case 'v':
v_opt = 2;
break;
case 'x':
add_pattern(&exclude, optarg);
break;