- Allow to extract symlinks

- Implement 4-digit year format listing (-y option)
- Improve detection of text files
- Use %ju for error_count as it is unsigned

Obtained from:	NetBSD
Approved by:	des
This commit is contained in:
Alex Kozlov 2015-12-11 23:52:08 +00:00
parent 2aa7d573a0
commit 04e17b6a6d
2 changed files with 75 additions and 34 deletions

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd May 10, 2012
.Dd December 12, 2015
.Dt UNZIP 1
.Os
.Sh NAME
@ -33,7 +33,7 @@
.Nd extract files from a ZIP archive
.Sh SYNOPSIS
.Nm
.Op Fl aCcfjLlnopqtuv
.Op Fl aCcfjLlnopqtuvy
.Op Fl d Ar dir
.Ar zipfile
.Sh DESCRIPTION
@ -102,6 +102,8 @@ content of the archive.
.It Fl x Ar pattern
Exclude files matching the pattern
.Ar pattern .
.It Fl y
Print four digit years in listings instead of two.
.It Fl Z Ar mode
Emulate
.Xr zipinfo 1L

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2009 Joerg Sonnenberger <joerg@NetBSD.org>
* Copyright (c) 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
* Copyright (c) 2007-2008 Dag-Erling Smørgrav
* All rights reserved.
*
@ -65,6 +65,7 @@ static int q_opt; /* quiet */
static int t_opt; /* test */
static int u_opt; /* update */
static int v_opt; /* verbose/list */
static const char *y_str = ""; /* 4 digit year */
static int Z1_opt; /* zipinfo mode list files only */
/* debug flag */
@ -460,6 +461,34 @@ handle_existing_file(char **path)
}
}
/*
* Detect binary files by a combination of character white list and
* black list. NUL bytes and other control codes without use in text files
* result directly in switching the file to binary mode. Otherwise, at least
* one white-listed byte has to be found.
*
* Black-listed: 0..6, 14..25, 28..31
* White-listed: 9..10, 13, >= 32
*
* See the proginfo/txtvsbin.txt in the zip sources for a detailed discussion.
*/
#define BYTE_IS_BINARY(x) ((x) < 32 && (0xf3ffc07fU & (1U << (x))))
#define BYTE_IS_TEXT(x) ((x) >= 32 || (0x00002600U & (1U << (x))))
static int
check_binary(const unsigned char *buf, size_t len)
{
int rv;
for (rv = 1; len--; ++buf) {
if (BYTE_IS_BINARY(*buf))
return 1;
if (BYTE_IS_TEXT(*buf))
rv = 0;
}
return rv;
}
/*
* Extract a regular file.
*/
@ -472,6 +501,7 @@ extract_file(struct archive *a, struct archive_entry *e, char **path)
struct timespec ts[2];
int cr, fd, text, warn, check;
ssize_t len;
const char *linkname;
unsigned char *p, *q, *end;
mode = archive_entry_mode(e) & 0777;
@ -485,7 +515,7 @@ extract_file(struct archive *a, struct archive_entry *e, char **path)
if (lstat(*path, &sb) == 0) {
if (u_opt || f_opt) {
/* check if up-to-date */
if (S_ISREG(sb.st_mode) &&
if ((S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode)) &&
(sb.st_mtim.tv_sec > mtime.tv_sec ||
(sb.st_mtim.tv_sec == mtime.tv_sec &&
sb.st_mtim.tv_nsec >= mtime.tv_nsec)))
@ -509,6 +539,15 @@ extract_file(struct archive *a, struct archive_entry *e, char **path)
return;
}
/* process symlinks */
linkname = archive_entry_symlink(e);
if (linkname != NULL) {
if (symlink(linkname, *path) < 0)
error("symlink('%s')", *path);
info(" extracting: %s -> %s\n", *path, linkname);
return;
}
if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0)
error("open('%s')", *path);
@ -550,12 +589,8 @@ extract_file(struct archive *a, struct archive_entry *e, char **path)
* 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;
}
}
if (check_binary(buffer, len))
text = 0;
}
/* simple case */
@ -568,7 +603,7 @@ extract_file(struct archive *a, struct archive_entry *e, char **path)
/* 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)) {
if (!warn && BYTE_IS_BINARY(*q)) {
warningx("%s may be corrupted due"
" to weak text file detection"
" heuristic", *path);
@ -639,7 +674,7 @@ extract(struct archive *a, struct archive_entry *e)
}
/* I don't think this can happen in a zipfile.. */
if (!S_ISDIR(filetype) && !S_ISREG(filetype)) {
if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) {
warningx("skipping non-regular entry '%s'", pathname);
ac(archive_read_data_skip(a));
free(pathname);
@ -695,7 +730,7 @@ extract_stdout(struct archive *a, struct archive_entry *e)
filetype = archive_entry_filetype(e);
/* I don't think this can happen in a zipfile.. */
if (!S_ISDIR(filetype) && !S_ISREG(filetype)) {
if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) {
warningx("skipping non-regular entry '%s'", pathname);
ac(archive_read_data_skip(a));
free(pathname);
@ -753,12 +788,8 @@ extract_stdout(struct archive *a, struct archive_entry *e)
* 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;
}
}
if (check_binary(buffer, len))
text = 0;
}
/* simple case */
@ -771,7 +802,7 @@ extract_stdout(struct archive *a, struct archive_entry *e)
/* 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)) {
if (!warn && BYTE_IS_BINARY(*q)) {
warningx("%s may be corrupted due"
" to weak text file detection"
" heuristic", pathname);
@ -802,9 +833,14 @@ list(struct archive *a, struct archive_entry *e)
{
char buf[20];
time_t mtime;
struct tm *tm;
mtime = archive_entry_mtime(e);
strftime(buf, sizeof(buf), "%m-%d-%g %R", localtime(&mtime));
tm = localtime(&mtime);
if (*y_str)
strftime(buf, sizeof(buf), "%m-%d-%G %R", tm);
else
strftime(buf, sizeof(buf), "%m-%d-%g %R", tm);
if (!zipinfo_mode) {
if (v_opt == 1) {
@ -877,11 +913,11 @@ unzip(const char *fn)
if (!p_opt && !q_opt)
printf("Archive: %s\n", fn);
if (v_opt == 1) {
printf(" Length Date Time Name\n");
printf(" -------- ---- ---- ----\n");
printf(" Length %sDate Time Name\n", y_str);
printf(" -------- %s---- ---- ----\n", y_str);
} else if (v_opt == 2) {
printf(" Length Method Size Ratio Date Time CRC-32 Name\n");
printf("-------- ------ ------- ----- ---- ---- ------ ----\n");
printf(" Length Method Size Ratio %sDate Time CRC-32 Name\n", y_str);
printf("-------- ------ ------- ----- %s---- ---- ------ ----\n", y_str);
}
}
@ -913,13 +949,13 @@ unzip(const char *fn)
if (zipinfo_mode) {
if (v_opt == 1) {
printf(" -------- -------\n");
printf(" %8ju %ju file%s\n",
total_size, file_count, file_count != 1 ? "s" : "");
printf(" -------- %s-------\n", y_str);
printf(" %8ju %s%ju file%s\n",
total_size, y_str, file_count, file_count != 1 ? "s" : "");
} else if (v_opt == 2) {
printf("-------- ------- --- -------\n");
printf("%8ju %7ju 0%% %ju file%s\n",
total_size, total_size, file_count,
printf("-------- ------- --- %s-------\n", y_str);
printf("%8ju %7ju 0%% %s%ju file%s\n",
total_size, total_size, y_str, file_count,
file_count != 1 ? "s" : "");
}
}
@ -929,7 +965,7 @@ unzip(const char *fn)
if (t_opt) {
if (error_count > 0) {
errorx("%d checksum error(s) found.", error_count);
errorx("%ju checksum error(s) found.", error_count);
}
else {
printf("No errors detected in compressed data of %s.\n",
@ -942,7 +978,7 @@ static void
usage(void)
{
fprintf(stderr, "usage: unzip [-aCcfjLlnopqtuvZ1] [-d dir] [-x pattern] zipfile\n");
fprintf(stderr, "Usage: unzip [-aCcfjLlnopqtuvyZ1] [-d dir] [-x pattern] zipfile\n");
exit(1);
}
@ -952,7 +988,7 @@ getopts(int argc, char *argv[])
int opt;
optreset = optind = 1;
while ((opt = getopt(argc, argv, "aCcd:fjLlnopqtuvx:Z1")) != -1)
while ((opt = getopt(argc, argv, "aCcd:fjLlnopqtuvx:yZ1")) != -1)
switch (opt) {
case '1':
Z1_opt = 1;
@ -1007,6 +1043,9 @@ getopts(int argc, char *argv[])
case 'x':
add_pattern(&exclude, optarg);
break;
case 'y':
y_str = " ";
break;
case 'Z':
zipinfo_mode = 1;
break;