Implement the rename query, for when a file with the same name as the one

about to be extracted already exists.  The question, and interpretation
of the response is deliberately compatible with Info-Zip.

This change was originally obtained from NetBSD, but has three changes:
 - better compatibility with Info-Zip in the handling of ^D
 - Use getdelim() rather than getline()
 - bug fix: != changed to == in the "file rename" code

I suspect the latter is also a bug in NetBSD, but I can't easily confirm
this.

PR:		bin/143307
Reviewed by:	rdivacky (change to unzip.c only)
Obtained from:	NetBSD src/usr.bin/unzip/unzip.c 1.8
MFC after:	1 month
This commit is contained in:
Gavin Atkinson 2010-02-16 22:53:18 +00:00
parent d01c5f360e
commit cf4bd0f272
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=203977
2 changed files with 69 additions and 28 deletions

View File

@ -158,17 +158,6 @@ utility is only able to process ZIP archives handled by
Depending on the installed version of Depending on the installed version of
.Xr libarchive , .Xr libarchive ,
this may or may not include self-extracting archives. this may or may not include self-extracting archives.
.Sh BUGS
The
.Nm
utility currently does not support asking the user whether to
overwrite or skip a file that already exists on disk.
To be on the safe side, it will fail if it encounters a file that
already exists and neither the
.Fl n
nor the
.Fl o
command line option was specified.
.Sh SEE ALSO .Sh SEE ALSO
.Xr libarchive 3 .Xr libarchive 3
.Sh HISTORY .Sh HISTORY

View File

@ -411,17 +411,65 @@ extract_dir(struct archive *a, struct archive_entry *e, const char *path)
static unsigned char buffer[8192]; static unsigned char buffer[8192];
static char spinner[] = { '|', '/', '-', '\\' }; static char spinner[] = { '|', '/', '-', '\\' };
static int
handle_existing_file(char **path)
{
size_t alen;
ssize_t len;
char buf[4];
for (;;) {
fprintf(stderr,
"replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ",
*path);
if (fgets(buf, sizeof(buf), stdin) == 0) {
clearerr(stdin);
printf("NULL\n(EOF or read error, "
"treating as \"[N]one\"...)\n");
n_opt = 1;
return -1;
}
switch (*buf) {
case 'A':
o_opt = 1;
/* FALLTHROUGH */
case 'y':
case 'Y':
(void)unlink(*path);
return 1;
case 'N':
n_opt = 1;
/* FALLTHROUGH */
case 'n':
return -1;
case 'r':
case 'R':
printf("New name: ");
fflush(stdout);
free(*path);
*path = NULL;
alen = 0;
len = getdelim(path, &alen, '\n', stdin);
if ((*path)[len - 1] == '\n')
(*path)[len - 1] = '\0';
return 0;
default:
break;
}
}
}
/* /*
* Extract a regular file. * Extract a regular file.
*/ */
static void static void
extract_file(struct archive *a, struct archive_entry *e, const char *path) extract_file(struct archive *a, struct archive_entry *e, char **path)
{ {
int mode; int mode;
time_t mtime; time_t mtime;
struct stat sb; struct stat sb;
struct timeval tv[2]; struct timeval tv[2];
int cr, fd, text, warn; int cr, fd, text, warn, check;
ssize_t len; ssize_t len;
unsigned char *p, *q, *end; unsigned char *p, *q, *end;
@ -431,32 +479,36 @@ extract_file(struct archive *a, struct archive_entry *e, const char *path)
mtime = archive_entry_mtime(e); mtime = archive_entry_mtime(e);
/* look for existing file of same name */ /* look for existing file of same name */
if (lstat(path, &sb) == 0) { recheck:
if (lstat(*path, &sb) == 0) {
if (u_opt || f_opt) { if (u_opt || f_opt) {
/* check if up-to-date */ /* check if up-to-date */
if (S_ISREG(sb.st_mode) && sb.st_mtime >= mtime) if (S_ISREG(sb.st_mode) && sb.st_mtime >= mtime)
return; return;
(void)unlink(path); (void)unlink(*path);
} else if (o_opt) { } else if (o_opt) {
/* overwrite */ /* overwrite */
(void)unlink(path); (void)unlink(*path);
} else if (n_opt) { } else if (n_opt) {
/* do not overwrite */ /* do not overwrite */
return; return;
} else { } else {
/* XXX ask user */ check = handle_existing_file(path);
errorx("not implemented"); if (check == 0)
goto recheck;
if (check == -1)
return; /* do not overwrite */
} }
} else { } else {
if (f_opt) if (f_opt)
return; return;
} }
if ((fd = open(path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0)
error("open('%s')", path); error("open('%s')", *path);
/* loop over file contents and write to disk */ /* loop over file contents and write to disk */
info(" extracting: %s", path); info(" extracting: %s", *path);
text = a_opt; text = a_opt;
warn = 0; warn = 0;
cr = 0; cr = 0;
@ -473,7 +525,7 @@ extract_file(struct archive *a, struct archive_entry *e, const char *path)
if (a_opt && cr) { if (a_opt && cr) {
if (len == 0 || buffer[0] != '\n') if (len == 0 || buffer[0] != '\n')
if (write(fd, "\r", 1) != 1) if (write(fd, "\r", 1) != 1)
error("write('%s')", path); error("write('%s')", *path);
cr = 0; cr = 0;
} }
@ -504,7 +556,7 @@ extract_file(struct archive *a, struct archive_entry *e, const char *path)
/* simple case */ /* simple case */
if (!a_opt || !text) { if (!a_opt || !text) {
if (write(fd, buffer, len) != len) if (write(fd, buffer, len) != len)
error("write('%s')", path); error("write('%s')", *path);
continue; continue;
} }
@ -514,7 +566,7 @@ extract_file(struct archive *a, struct archive_entry *e, const char *path)
if (!warn && !isascii(*q)) { if (!warn && !isascii(*q)) {
warningx("%s may be corrupted due" warningx("%s may be corrupted due"
" to weak text file detection" " to weak text file detection"
" heuristic", path); " heuristic", *path);
warn = 1; warn = 1;
} }
if (q[0] != '\r') if (q[0] != '\r')
@ -527,7 +579,7 @@ extract_file(struct archive *a, struct archive_entry *e, const char *path)
break; break;
} }
if (write(fd, p, q - p) != q - p) if (write(fd, p, q - p) != q - p)
error("write('%s')", path); error("write('%s')", *path);
} }
} }
if (tty) if (tty)
@ -542,9 +594,9 @@ extract_file(struct archive *a, struct archive_entry *e, const char *path)
tv[1].tv_sec = mtime; tv[1].tv_sec = mtime;
tv[1].tv_usec = 0; tv[1].tv_usec = 0;
if (futimes(fd, tv) != 0) if (futimes(fd, tv) != 0)
error("utimes('%s')", path); error("utimes('%s')", *path);
if (close(fd) != 0) if (close(fd) != 0)
error("close('%s')", path); error("close('%s')", *path);
} }
/* /*
@ -620,7 +672,7 @@ extract(struct archive *a, struct archive_entry *e)
if (S_ISDIR(filetype)) if (S_ISDIR(filetype))
extract_dir(a, e, realpathname); extract_dir(a, e, realpathname);
else else
extract_file(a, e, realpathname); extract_file(a, e, &realpathname);
free(realpathname); free(realpathname);
free(pathname); free(pathname);