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);
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to