svn commit: r203977 - head/usr.bin/unzip

Gavin Atkinson gavin at FreeBSD.org
Tue Feb 16 22:53:18 UTC 2010


Author: gavin
Date: Tue Feb 16 22:53:18 2010
New Revision: 203977
URL: http://svn.freebsd.org/changeset/base/203977

Log:
  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

Modified:
  head/usr.bin/unzip/unzip.1
  head/usr.bin/unzip/unzip.c

Modified: head/usr.bin/unzip/unzip.1
==============================================================================
--- head/usr.bin/unzip/unzip.1	Tue Feb 16 22:23:33 2010	(r203976)
+++ head/usr.bin/unzip/unzip.1	Tue Feb 16 22:53:18 2010	(r203977)
@@ -158,17 +158,6 @@ utility is only able to process ZIP arch
 Depending on the installed version of
 .Xr libarchive ,
 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
 .Xr libarchive 3
 .Sh HISTORY

Modified: head/usr.bin/unzip/unzip.c
==============================================================================
--- head/usr.bin/unzip/unzip.c	Tue Feb 16 22:23:33 2010	(r203976)
+++ head/usr.bin/unzip/unzip.c	Tue Feb 16 22:53:18 2010	(r203977)
@@ -411,17 +411,65 @@ extract_dir(struct archive *a, struct ar
 static unsigned char buffer[8192];
 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.
  */
 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;
 	time_t mtime;
 	struct stat sb;
 	struct timeval tv[2];
-	int cr, fd, text, warn;
+	int cr, fd, text, warn, check;
 	ssize_t len;
 	unsigned char *p, *q, *end;
 
@@ -431,32 +479,36 @@ extract_file(struct archive *a, struct a
 	mtime = archive_entry_mtime(e);
 
 	/* look for existing file of same name */
-	if (lstat(path, &sb) == 0) {
+recheck:
+	if (lstat(*path, &sb) == 0) {
 		if (u_opt || f_opt) {
 			/* check if up-to-date */
 			if (S_ISREG(sb.st_mode) && sb.st_mtime >= mtime)
 				return;
-			(void)unlink(path);
+			(void)unlink(*path);
 		} else if (o_opt) {
 			/* overwrite */
-			(void)unlink(path);
+			(void)unlink(*path);
 		} else if (n_opt) {
 			/* do not overwrite */
 			return;
 		} else {
-			/* XXX ask user */
-			errorx("not implemented");
+			check = handle_existing_file(path);
+			if (check == 0)
+				goto recheck;
+			if (check == -1)
+				return; /* do not overwrite */
 		}
 	} else {
 		if (f_opt)
 			return;
 	}
 
-	if ((fd = open(path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0)
-		error("open('%s')", path);
+	if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0)
+		error("open('%s')", *path);
 
 	/* loop over file contents and write to disk */
-	info(" extracting: %s", path);
+	info(" extracting: %s", *path);
 	text = a_opt;
 	warn = 0;
 	cr = 0;
@@ -473,7 +525,7 @@ extract_file(struct archive *a, struct a
 		if (a_opt && cr) {
 			if (len == 0 || buffer[0] != '\n')
 				if (write(fd, "\r", 1) != 1)
-					error("write('%s')", path);
+					error("write('%s')", *path);
 			cr = 0;
 		}
 
@@ -504,7 +556,7 @@ extract_file(struct archive *a, struct a
 		/* simple case */
 		if (!a_opt || !text) {
 			if (write(fd, buffer, len) != len)
-				error("write('%s')", path);
+				error("write('%s')", *path);
 			continue;
 		}
 
@@ -514,7 +566,7 @@ extract_file(struct archive *a, struct a
 				if (!warn && !isascii(*q)) {
 					warningx("%s may be corrupted due"
 					    " to weak text file detection"
-					    " heuristic", path);
+					    " heuristic", *path);
 					warn = 1;
 				}
 				if (q[0] != '\r')
@@ -527,7 +579,7 @@ extract_file(struct archive *a, struct a
 					break;
 			}
 			if (write(fd, p, q - p) != q - p)
-				error("write('%s')", path);
+				error("write('%s')", *path);
 		}
 	}
 	if (tty)
@@ -542,9 +594,9 @@ extract_file(struct archive *a, struct a
 	tv[1].tv_sec = mtime;
 	tv[1].tv_usec = 0;
 	if (futimes(fd, tv) != 0)
-		error("utimes('%s')", path);
+		error("utimes('%s')", *path);
 	if (close(fd) != 0)
-		error("close('%s')", path);
+		error("close('%s')", *path);
 }
 
 /*
@@ -620,7 +672,7 @@ extract(struct archive *a, struct archiv
 	if (S_ISDIR(filetype))
 		extract_dir(a, e, realpathname);
 	else
-		extract_file(a, e, realpathname);
+		extract_file(a, e, &realpathname);
 
 	free(realpathname);
 	free(pathname);


More information about the svn-src-head mailing list