From nobody@FreeBSD.org  Thu Jan 28 07:10:06 2010
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 18822106566C
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 28 Jan 2010 07:10:06 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21])
	by mx1.freebsd.org (Postfix) with ESMTP id 00BD38FC15
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 28 Jan 2010 07:10:06 +0000 (UTC)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.14.3/8.14.3) with ESMTP id o0S7A5CK032521
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 28 Jan 2010 07:10:05 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.14.3/8.14.3/Submit) id o0S7A5fw032520;
	Thu, 28 Jan 2010 07:10:05 GMT
	(envelope-from nobody)
Message-Id: <201001280710.o0S7A5fw032520@www.freebsd.org>
Date: Thu, 28 Jan 2010 07:10:05 GMT
From: Alex Deiter <alex.deiter@gmail.com>
To: freebsd-gnats-submit@FreeBSD.org
Subject: /usr/bin/unzip file.zip: not implemented
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         143307
>Category:       bin
>Synopsis:       unzip(1): /usr/bin/unzip file.zip: not implemented
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    gavin
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Jan 28 07:20:05 UTC 2010
>Closed-Date:    Tue May 04 19:18:56 UTC 2010
>Last-Modified:  Tue May  4 19:20:02 UTC 2010
>Originator:     Alex Deiter
>Release:        9.0-CURRENT
>Organization:
N/A
>Environment:
FreeBSD x60.deiter.net.ru 9.0-CURRENT FreeBSD 9.0-CURRENT #0: Tue Jan 26 17:26:54 MSK 2010     root@x60.deiter.net.ru:/usr/obj/usr/src/sys/X60  amd64

>Description:
/usr/bin/unzip does not extract zip files:

$ /usr/bin/unzip -t test.zip 
Archive:  test.zip
    testing: 1	 OK
    testing: 2	 OK
No errors detected in compressed data of test.zip.

$ /usr/bin/unzip  test.zip 
Archive:  test.zip
unzip: not implemented

But if you specify the -d: archive is unpacked:

$ /usr/bin/unzip -d /var/tmp/dir test.zip 
Archive:  test.zip
 extracting: /var/tmp/dir/1  
 extracting: /var/tmp/dir/2  

/usr/local/bin/unzip works as expected:

$ /usr/local/bin/unzip test.zip 
Archive:  test.zip
  inflating: 1                       
  inflating: 2             

Could you please confirm this bug ?

Thanks a lot!
>How-To-Repeat:

>Fix:


>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->feedback 
State-Changed-By: gavin 
State-Changed-When: Sun Jan 31 09:53:12 UTC 2010 
State-Changed-Why:  
I cannot recreate this on a similar system.  Are you able to provide 
the zip file that shows this behaviour? 


Responsible-Changed-From-To: freebsd-bugs->gavin 
Responsible-Changed-By: gavin 
Responsible-Changed-When: Sun Jan 31 09:53:12 UTC 2010 
Responsible-Changed-Why:  
Track 

http://www.freebsd.org/cgi/query-pr.cgi?pr=143307 
State-Changed-From-To: feedback->open 
State-Changed-By: gavin 
State-Changed-When: Sun Jan 31 18:33:44 UTC 2010 
State-Changed-Why:  
Turns out that this message is printed due to unimplemented handling 
of the situaton where a file already exists.  I have patches out for 
review to fix this. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=143307 

From: Edwin Groothuis <edwin@mavetju.org>
To: Garrett Cooper <yanefbsd@gmail.com>
Cc: gavin@freebsd.org, bug-followup@freebsd.org, alex.deiter@gmail.com
Subject: Re: bin/143307: unzip(1): /usr/bin/unzip file.zip: not implemented
Date: Mon, 1 Feb 2010 13:30:00 +1100

 Could be related to AES support (or absence of it).
 But yes, the zip file he has would be handy :-)
 
 -- 
 Edwin Groothuis		Website: http://www.mavetju.org/
 edwin@mavetju.org	Weblog:  http://www.mavetju.org/weblog/

From: Alex Deiter <alex.deiter@gmail.com>
To: bug-followup@FreeBSD.org, alex.deiter@gmail.com, 
	Garrett Cooper <yanefbsd@gmail.com>, gavin@freebsd.org, Edwin Groothuis <edwin@mavetju.org>
Cc:  
Subject: Re: bin/143307: unzip(1): /usr/bin/unzip file.zip: not implemented
Date: Mon, 1 Feb 2010 09:44:16 +0300

 Hi,
 
 1. problem reproduced on different file systems: UFS and ZFS.
 2. problem appears only if the files already exist:
 
 $ touch 1 2 3
 $ zip test.zip 1 2 3
   adding: 1 (stored 0%)
   adding: 2 (stored 0%)
   adding: 3 (stored 0%)
 $ unzip test.zip
 Archive:  test.zip
 unzip: not implemented
 $ rm 1 2 3
 $ unzip test.zip
 Archive:  test.zip
  extracting: 1
  extracting: 2
  extracting: 3
 
 Thanks a lot!
State-Changed-From-To: open->patched 
State-Changed-By: gavin 
State-Changed-When: Tue Feb 16 22:53:41 UTC 2010 
State-Changed-Why:  
Fixed in head, MFC in a month. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=143307 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/143307: commit references a PR
Date: Tue, 16 Feb 2010 23:04:48 +0000 (UTC)

 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-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: patched->closed 
State-Changed-By: delphij 
State-Changed-When: Tue May 4 19:18:25 UTC 2010 
State-Changed-Why:  
Fix MFC'ed to 8-STABLE. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=143307 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/143307: commit references a PR
Date: Tue,  4 May 2010 19:18:10 +0000 (UTC)

 Author: delphij
 Date: Tue May  4 19:18:00 2010
 New Revision: 207629
 URL: http://svn.freebsd.org/changeset/base/207629
 
 Log:
   MFC r196981, r200844, r201630, r203977, r203978, r204352:
   
   r196981 (rdivacky):
   
   Add C/c/f/p/v switches plus a bunch of minor fixes and cleanups.
   
   Obtained from:	NetBSD
   
   r200844 (jh):
   
   Don't print the archive name with -p and -q options.
   
   PR:		bin/141280
   
   r201630 (kientzle):
   
   When restoring files, use the mode for the mode.
   
   Thanks to: Jun Kuriyama for pointing this out
   
   r203977 (gavin):
   
   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
   
   r203978 (gavin):
   
   Bump .Dd for r203977
   
   r204352 (ru):
   
   Fixed static linkage.
   
   ==
   
   Requested by:	Alex Kozlov <spam rm-rf kiev ua>
 
 Modified:
   stable/8/usr.bin/unzip/Makefile
   stable/8/usr.bin/unzip/unzip.1
   stable/8/usr.bin/unzip/unzip.c
 Directory Properties:
   stable/8/usr.bin/unzip/   (props changed)
 
 Modified: stable/8/usr.bin/unzip/Makefile
 ==============================================================================
 --- stable/8/usr.bin/unzip/Makefile	Tue May  4 19:04:51 2010	(r207628)
 +++ stable/8/usr.bin/unzip/Makefile	Tue May  4 19:18:00 2010	(r207629)
 @@ -3,7 +3,7 @@
  PROG = unzip
  WARNS ?= 6
  CSTD = c99
 -DPADD = ${LIBARCHIVE}
 -LDADD = -larchive
 +DPADD = ${LIBARCHIVE} ${LIBZ}
 +LDADD = -larchive -lz
  
  .include <bsd.prog.mk>
 
 Modified: stable/8/usr.bin/unzip/unzip.1
 ==============================================================================
 --- stable/8/usr.bin/unzip/unzip.1	Tue May  4 19:04:51 2010	(r207628)
 +++ stable/8/usr.bin/unzip/unzip.1	Tue May  4 19:18:00 2010	(r207629)
 @@ -25,7 +25,7 @@
  .\"
  .\" $FreeBSD$
  .\"
 -.Dd June 30, 2008
 +.Dd February 16, 2010
  .Dt UNZIP 1
  .Os
  .Sh NAME
 @@ -33,7 +33,7 @@
  .Nd extract files from a ZIP archive
  .Sh SYNOPSIS
  .Nm
 -.Op Fl ajLlnoqtu
 +.Op Fl aCcfjLlnopqtuv
  .Op Fl d Ar dir
  .Ar zipfile
  .Sh DESCRIPTION
 @@ -44,9 +44,22 @@ The following options are available:
  .It Fl a
  When extracting a text file, convert DOS-style line endings to
  Unix-style line endings.
 +.It Fl C
 +Match file names case-insensitively.
 +.It Fl c
 +Extract to stdout/screen.
 +When extracting files from the zipfile, they are written to stdout.
 +This is similar to
 +.Fl p ,
 +but doesn't suppress normal output.
  .It Fl d Ar dir
  Extract files into the specified directory rather than the current
  directory.
 +.It Fl f
 +Update existing.
 +Extract only files from the zipfile if a file with the same name
 +already exists on disk and is older than the former.
 +Otherwise, the file is silently skipped.
  .It Fl j
  Ignore directories stored in the zipfile; instead, extract all files
  directly into the extraction directory.
 @@ -56,13 +69,19 @@ Convert the names of the extracted files
  List, rather than extract, the contents of the zipfile.
  .It Fl n
  No 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 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
 @@ -70,15 +89,25 @@ Test: do not extract anything, but verif
  in the archive.
  .It Fl u
  Update.
 -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 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 listing.
 +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 .
  .El
  .Pp
  Note that only one of
  .Fl n ,
 -.Fl o
 +.Fl o ,
  and
  .Fl u
  may be specified.
 @@ -129,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: stable/8/usr.bin/unzip/unzip.c
 ==============================================================================
 --- stable/8/usr.bin/unzip/unzip.c	Tue May  4 19:04:51 2010	(r207628)
 +++ stable/8/usr.bin/unzip/unzip.c	Tue May  4 19:18:00 2010	(r207629)
 @@ -1,4 +1,5 @@
  /*-
 + * Copyright (c) 2009 Joerg Sonnenberger <joerg@NetBSD.org>
   * Copyright (c) 2007-2008 Dag-Erling Codan Smrgrav
   * All rights reserved.
   *
 @@ -51,15 +52,19 @@
  
  /* command-line options */
  static int		 a_opt;		/* convert EOL */
 +static int		 C_opt;		/* match case-insensitively */
 +static int		 c_opt;		/* extract to stdout */
  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, quiet */
  static int		 q_opt;		/* quiet */
  static int		 t_opt;		/* test */
  static int		 u_opt;		/* update */
 +static int		 v_opt;		/* verbose/list */
  
  /* time when unzip started */
  static time_t		 now;
 @@ -70,9 +75,6 @@ static int		 unzip_debug;
  /* running on tty? */
  static int		 tty;
  
 -/* error flag for -t */
 -static int		 test_failed;
 -
  /* convenience macro */
  /* XXX should differentiate between ARCHIVE_{WARN,FAIL,RETRY} */
  #define ac(call)						\
 @@ -162,7 +164,6 @@ static void
  info(const char *fmt, ...)
  {
  	va_list ap;
 -	int i;
  
  	if (q_opt && !unzip_debug)
  		return;
 @@ -171,9 +172,10 @@ info(const char *fmt, ...)
  	va_end(ap);
  	fflush(stdout);
  
 -	for (i = 0; fmt[i] != '\0'; ++i)
 -		/* nothing */ ;
 -	noeol = !(i && fmt[i - 1] == '\n');
 +	if (*fmt == '\0')
 +		noeol = 1;
 +	else
 +		noeol = fmt[strlen(fmt) - 1] != '\n';
  }
  
  /* debug message (if unzip_debug) */
 @@ -181,7 +183,6 @@ static void
  debug(const char *fmt, ...)
  {
  	va_list ap;
 -	int i;
  
  	if (!unzip_debug)
  		return;
 @@ -190,9 +191,10 @@ debug(const char *fmt, ...)
  	va_end(ap);
  	fflush(stderr);
  
 -	for (i = 0; fmt[i] != '\0'; ++i)
 -		/* nothing */ ;
 -	noeol = !(i && fmt[i - 1] == '\n');
 +	if (*fmt == '\0')
 +		noeol = 1;
 +	else
 +		noeol = fmt[strlen(fmt) - 1] != '\n';
  }
  
  /* duplicate a path name, possibly converting to lower case */
 @@ -200,7 +202,7 @@ static char *
  pathdup(const char *path)
  {
  	char *str;
 -	int len;
 +	size_t i, len;
  
  	len = strlen(path);
  	while (len && path[len - 1] == '/')
 @@ -209,8 +211,12 @@ pathdup(const char *path)
  		errno = ENOMEM;
  		error("malloc()");
  	}
 -	for (int i = 0; i < len; ++i)
 -		str[i] = L_opt ? tolower(path[i]) : path[i];
 +	if (L_opt) {
 +		for (i = 0; i < len; ++i)
 +			str[i] = tolower((unsigned char)path[i]);
 +	} else {
 +		memcpy(str, path, len);
 +	}
  	str[len] = '\0';
  
  	return (str);
 @@ -221,7 +227,7 @@ static char *
  pathcat(const char *prefix, const char *path)
  {
  	char *str;
 -	int prelen, len;
 +	size_t prelen, len;
  
  	prelen = prefix ? strlen(prefix) + 1 : 0;
  	len = strlen(path) + 1;
 @@ -257,7 +263,7 @@ static void
  add_pattern(struct pattern_list *list, const char *pattern)
  {
  	struct pattern *entry;
 -	int len;
 +	size_t len;
  
  	debug("adding pattern '%s'\n", pattern);
  	len = strlen(pattern);
 @@ -265,7 +271,6 @@ add_pattern(struct pattern_list *list, c
  		errno = ENOMEM;
  		error("malloc()");
  	}
 -	memset(&entry->link, 0, sizeof entry->link);
  	memcpy(entry->pattern, pattern, len + 1);
  	STAILQ_INSERT_TAIL(list, entry, link);
  }
 @@ -279,7 +284,7 @@ match_pattern(struct pattern_list *list,
  	struct pattern *entry;
  
  	STAILQ_FOREACH(entry, list, link) {
 -		if (fnmatch(entry->pattern, str, 0) == 0)
 +		if (fnmatch(entry->pattern, str, C_opt ? FNM_CASEFOLD : 0) == 0)
  			return (1);
  	}
  	return (0);
 @@ -378,7 +383,7 @@ extract_dir(struct archive *a, struct ar
  {
  	int mode;
  
 -	mode = archive_entry_filetype(e) & 0777;
 +	mode = archive_entry_mode(e) & 0777;
  	if (mode == 0)
  		mode = 0755;
  
 @@ -406,49 +411,104 @@ 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;
  
 -	mode = archive_entry_filetype(e) & 0777;
 +	mode = archive_entry_mode(e) & 0777;
  	if (mode == 0)
  		mode = 0644;
  	mtime = archive_entry_mtime(e);
  
  	/* look for existing file of same name */
 -	if (lstat(path, &sb) == 0) {
 -		if (u_opt) {
 +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)
 +			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("x %s", path);
 +	info(" extracting: %s", *path);
  	text = a_opt;
  	warn = 0;
  	cr = 0;
 @@ -465,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;
  		}
  
 @@ -496,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;
  		}
  
 @@ -506,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')
 @@ -519,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)
 @@ -534,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);
  }
  
  /*
 @@ -612,46 +672,178 @@ 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);
  }
  
 +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;
 +	}
 +
 +	if (c_opt)
 +		info("x %s\n", pathname);
 +
 +	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 (fwrite("\r", 1, 1, stderr) != 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 (fwrite(buffer, 1, len, stdout) != (size_t)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 (fwrite(p, 1, q - p, stdout) != (size_t)(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)
  {
 +	char buf[20];
 +	time_t mtime;
  
 -	printf("%s\n", archive_entry_pathname(e));
 +	mtime = archive_entry_mtime(e);
 +	strftime(buf, sizeof(buf), "%m-%d-%g %R", localtime(&mtime));
 +
 +	if (v_opt == 1) {
 +		printf(" %8ju  %s   %s\n",
 +		    (uintmax_t)archive_entry_size(e),
 +		    buf, archive_entry_pathname(e));
 +	} else if (v_opt == 2) {
 +		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));
 +	}
  	ac(archive_read_data_skip(a));
  }
  
  /*
   * Extract to memory to check CRC
   */
 -static void
 +static int
  test(struct archive *a, struct archive_entry *e)
  {
  	ssize_t len;
 +	int error_count;
  
 +	error_count = 0;
  	if (S_ISDIR(archive_entry_filetype(e)))
 -		return;
 +		return 0;
  
 -	info("%s ", archive_entry_pathname(e));
 +	info("    testing: %s\t", archive_entry_pathname(e));
  	while ((len = archive_read_data(a, buffer, sizeof buffer)) > 0)
  		/* nothing */;
  	if (len < 0) {
 -		info("%s\n", archive_error_string(a));
 -		++test_failed;
 +		info(" %s\n", archive_error_string(a));
 +		++error_count;
  	} else {
 -		info("OK\n");
 +		info(" OK\n");
  	}
  
  	/* shouldn't be necessary, but it doesn't hurt */
  	ac(archive_read_data_skip(a));
 +
 +	return error_count;
  }
  
  
 @@ -665,6 +857,7 @@ unzip(const char *fn)
  	struct archive *a;
  	struct archive_entry *e;
  	int fd, ret;
 +	uintmax_t total_size, file_count, error_count;
  
  	if ((fd = open(fn, O_RDONLY)) < 0)
  		error("%s", fn);
 @@ -673,33 +866,70 @@ unzip(const char *fn)
  	ac(archive_read_support_format_zip(a));
  	ac(archive_read_open_fd(a, fd, 8192));
  
 +	if (!p_opt && !q_opt)
 +		printf("Archive:  %s\n", fn);
 +	if (v_opt == 1) {
 +		printf("  Length     Date   Time    Name\n");
 +		printf(" --------    ----   ----    ----\n");
 +	} else if (v_opt == 2) {
 +		printf(" Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n");
 +		printf("--------  ------  ------- -----   ----   ----   ------    ----\n");
 +	}
 +
 +	total_size = 0;
 +	file_count = 0;
 +	error_count = 0;
  	for (;;) {
  		ret = archive_read_next_header(a, &e);
  		if (ret == ARCHIVE_EOF)
  			break;
  		ac(ret);
  		if (t_opt)
 -			test(a, e);
 -		else if (l_opt)
 +			error_count += test(a, e);
 +		else if (v_opt)
  			list(a, e);
 +		else if (p_opt || c_opt)
 +			extract_stdout(a, e);
  		else
  			extract(a, e);
 +
 +		total_size += archive_entry_size(e);
 +		++file_count;
 +	}
 +
 +	if (v_opt == 1) {
 +		printf(" --------                   -------\n");
 +		printf(" %8ju                   %ju file%s\n",
 +		    total_size, 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,
 +		    file_count != 1 ? "s" : "");
  	}
  
  	ac(archive_read_close(a));
  	(void)archive_read_finish(a);
 +
  	if (close(fd) != 0)
  		error("%s", fn);
  
 -	if (t_opt && test_failed)
 -		errorx("%d checksum error(s) found.", test_failed);
 +	if (t_opt) {
 +		if (error_count > 0) {
 +			errorx("%d checksum error(s) found.", error_count);
 +		}
 +		else {
 +			printf("No errors detected in compressed data of %s.\n",
 +			       fn);
 +		}
 +	}
  }
  
  static void
  usage(void)
  {
  
 -	fprintf(stderr, "usage: unzip [-ajLlnoqtu] [-d dir] zipfile\n");
 +	fprintf(stderr, "usage: unzip [-aCcfjLlnopqtuv] [-d dir] [-x pattern] zipfile\n");
  	exit(1);
  }
  
 @@ -709,14 +939,23 @@ getopts(int argc, char *argv[])
  	int opt;
  
  	optreset = optind = 1;
 -	while ((opt = getopt(argc, argv, "ad:jLlnoqtux:")) != -1)
 +	while ((opt = getopt(argc, argv, "aCcd:fjLlnopqtuvx:")) != -1)
  		switch (opt) {
  		case 'a':
  			a_opt = 1;
  			break;
 +		case 'C':
 +			C_opt = 1;
 +			break;
 +		case 'c':
 +			c_opt = 1;
 +			break;
  		case 'd':
  			d_arg = optarg;
  			break;
 +		case 'f':
 +			f_opt = 1;
 +			break;
  		case 'j':
  			j_opt = 1;
  			break;
 @@ -724,13 +963,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;
 @@ -741,6 +985,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;
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
>Unformatted:
