--- unzoo-4.4.orig/unzoo.c +++ unzoo-4.4/unzoo.c @@ -245,6 +245,13 @@ */ #include +#ifdef SYS_IS_UNIX +#include +#include +#include +#include +#include +#endif /**************************************************************************** ** @@ -613,8 +620,8 @@ ** members with the correct time as stored in the archive. */ #ifdef SYS_IS_UNIX -unsigned int Secs [2]; -#define SETF_TIME(patl,secs) (Secs[0]=Secs[1]=(secs),!utime((patl),Secs)) +struct utimbuf Secs; +#define SETF_TIME(patl,secs) (Secs.actime=Secs.modtime=(secs),!utime((patl),&Secs)) #endif #ifdef SYS_IS_DOS_DJGPP unsigned long Secs [2]; @@ -1265,10 +1272,12 @@ /* try to match the name part */ while ( *pat != '\0' || *str != '\0' ) { if ( *pat==*str ) { pat++; str++; } - else if ( *pat=='?' && ! IsSpec[*str] ) { pat++; str++; } + else if ( *pat=='?' && ! IsSpec[(unsigned int)*str] ) + { pat++; str++; } else if ( *pat=='?' && *str != '\0' ) { pat++; str++; } else if ( *pat=='*' ) { pos = ++pat; tmp = str; } - else if ( tmp != 0 && ! IsSpec[*tmp] ) { pat = pos; str = ++tmp; } + else if ( tmp != 0 && ! IsSpec[(unsigned int)*tmp] ) + { pat = pos; str = ++tmp; } else break; } return *pat == '\0' && *str == '\0'; @@ -2470,7 +2479,7 @@ /**************************************************************************** ** -*F ExtrArch(,,,
,,,) . extract members
+*F  ExtrArch(,,,
,,,,) . extract members
 **
 **  'ExtrArch' extracts the members  of the archive with  the name  that
 **  match one  of the file name  patterns '[0] .. [-1]'.
@@ -2482,12 +2491,15 @@
 **  stdout, i.e., to the screen.  and if it  is 2, the members are extracted.
 **  If  is 0, members will not overwrite  existing files; otherwise they
 **  will.  
 is a prefix that is prepended to all path names.
+**   is 1 if the user requested extraction of members even if this would
+** result in a directory traversal.
 */
-int             ExtrArch ( bim, out, ovr, pre, arc, filec, files )
+int             ExtrArch ( bim, out, ovr, pre, frc, arc, filec, files )
     unsigned long       bim;
     unsigned long       out;
     unsigned long       ovr;
     char *              pre;
+    unsigned long       frc;
     char *              arc;
     unsigned long       filec;
     char *              files [];
@@ -2573,6 +2585,102 @@
             continue;
         }
 
+        /* check the path for directory traversal                          */
+        if (frc != 1) {
+            /* but only if the user did not request otherwise              */
+
+            /* building the universal path of this member                  */
+            char patu [sizeof(Entry.diru) + sizeof(Entry.namu) + 2];
+            strcpy( patu, Entry.diru );
+            if ( strlen(patu) && patu[strlen(patu)-1] != '/') strcat( patu, "/" );
+            strcat( patu, (Entry.lnamu ? Entry.namu : Entry.nams) );
+
+            int found_trav = 0;
+
+            if ( strstr( patu, "/../" )) {
+                found_trav = 1;
+
+                /* remove "/../" from the path                             */
+                char tmp [sizeof(patu)];
+                char *p;
+                char *q;
+                memset(tmp, 0, sizeof(tmp));
+                q = patu;
+
+                while ( !strncmp(q, "/../", 4) ) {
+                    q += 3;
+                }
+                if (q[0] == '/') q++;
+
+                while ((p = strstr( q, "/../" )) != NULL) {
+                    if (q[0] == '/') q++;
+                    if (p > q) strncat(tmp, q, p-q);
+                    if (tmp[strlen(tmp)-1] != '/') strcat(tmp, "/");
+                    p += 3;
+                    q = p;
+                }
+                strncat(tmp, q+1, patu + strlen(patu) - q);
+                strcpy(patu, tmp);
+
+                printf("unzoo: skipped \"/../\" path component(s) in '%s'\n", Entry.patl);
+            }
+            if ( *patu == '/' && !strlen( pre ) ) {
+                found_trav = 1;
+
+                char *p = malloc(sizeof(patu));
+                char *q = p;
+                memset(p, 0, sizeof(patu));
+                strcpy(p, patu);
+                while ( q[0] == '/' ) q++;
+                strcpy(patu, q);
+                free(p);
+
+                printf("unzoo: skipped root directory path component in '%s'\n", patl);
+            }
+            if ( !strncmp( patu, "../", 3 )) {
+                found_trav = 1;
+
+                char tmp [sizeof(patu)];
+                memset(tmp, 0, sizeof(tmp));
+                strcpy(tmp, patu + 3);
+                strcpy(patu, tmp);
+
+                printf("unzoo: skipped \"../\" path component in '%s'\n", patl);
+            }
+
+            if (found_trav) {
+                /* patu contains the sanitized 'universal' path, i.e.      */
+                /* separated by '/' characters, including the file name.   */
+
+                char *f = strrchr( patu, '/' );
+                *f++ = '\0';
+                /* Now, patu points to the directory part, f to the file   */
+
+                memset( Entry.diru, 0, sizeof(Entry.diru) );
+                strncpy( Entry.diru, patu, sizeof(Entry.diru)-1 );
+                if ( Entry.lnamu > 0 ) {
+                    memset( Entry.namu, 0, sizeof(Entry.namu) );
+                    strncpy( Entry.namu, f, sizeof(Entry.namu)-1 );
+                } else {
+                    memset( Entry.nams, 0, sizeof(Entry.nams) );
+                    strncpy( Entry.nams, f, sizeof(Entry.nams)-1 );
+                }
+
+                /* convert the names to local format                       */
+                if ( Entry.system == 0 || Entry.system == 2 ) {
+                    CONV_DIRE( Entry.dirl, Entry.diru );
+                    CONV_NAME( Entry.naml, (Entry.lnamu ? Entry.namu : Entry.nams) );
+                }
+                else {
+                    strcpy( Entry.dirl, Entry.diru );
+                    strcpy( Entry.naml, (Entry.lnamu ? Entry.namu : Entry.nams) );
+                }
+                /* sizeof(patl)=512, sizeof({dirl|naml}=256} */
+                strcpy( Entry.patl, Entry.dirl );
+                strcat( Entry.patl, Entry.naml );
+            }
+        }
+
         /* check that such a file does not already exist                   */
         strcpy( patl, pre );  strcat( patl, Entry.patl );
         if ( out == 2 && ovr == 0 && OpenReadFile(patl,0L) ) {
@@ -2731,7 +2839,7 @@
     printf("  : list only files matching at least one pattern,\n");
     printf("          '?' matches any char, '*' matches any string.\n");
     printf("\n");
-    printf("unzoo -x [-abnpo] [-j ] [.zoo] [..]\n");
+    printf("unzoo -x [-abnpo] [-t] [-j ] [.zoo] [..]\n");
     printf("  extract the members of the archive\n");
     printf("  -a:  extract all members as text files ");
     printf("(not only those with !TEXT! comments)\n");
@@ -2741,6 +2849,10 @@
     printf("  -p:  extract to stdout\n");
     printf("  -o:  extract over existing files\n");
     printf("  -j:  extract to ''\n");
+    printf("  -f:  force extraction of members to their original locations\n");
+    printf("       even if this results in files extracted outside the\n");
+    printf("       working directory. THIS COULD POTENTIALLY OVERWRITE\n");
+    printf("       IMPORTANT FILES, SO USE WITH CARE!\n");
     printf("  : extract only files matching at least one pattern,\n");
     printf("          '?' matches any char, '*' matches any string.\n");
     return 1;
@@ -2764,6 +2876,7 @@
     unsigned long       bim;            /* extraction mode option          */
     unsigned long       out;            /* output destination option       */
     unsigned long       ovr;            /* overwrite file option           */
+    unsigned long       frc;            /* force extraction option         */
     char *              pre;            /* prefix to prepend to path names */
     char                argl [256];     /* interactive command line        */
     int                 argd;           /* interactive command count       */
@@ -2783,7 +2896,7 @@
     do {
 
         /* scan the command line arguments                                 */
-        cmd = 1;  ver = 0;  bim = 0;  out = 2;  ovr = 0;
+        cmd = 1;  ver = 0;  bim = 0;  out = 2;  ovr = 0; frc = 0;
         pre = "";
         while ( 1 < argc && argv[1][0] == '-' ) {
             if ( argv[1][2] != '\0' )  cmd = 0;
@@ -2793,6 +2906,7 @@
             case 'x': case 'X': if ( cmd != 0 )  cmd = 2;            break;
             case 'a': case 'A': if ( cmd != 2 )  cmd = 0;  bim = 1;  break;
             case 'b': case 'B': if ( cmd != 2 )  cmd = 0;  bim = 2;  break;
+            case 'f': case 'F': if ( cmd != 2 )  cmd = 0;  frc = 1; break;
             case 'n': case 'N': if ( cmd != 2 )  cmd = 0;  out = 0;  break;
             case 'p': case 'P': if ( cmd != 2 )  cmd = 0;  out = 1;  break;
             case 'o': case 'O': if ( cmd != 2 )  cmd = 0;  ovr = 1;  break;
@@ -2809,7 +2923,7 @@
             res = ListArch( ver, argv[1],
                             (unsigned long)argc-2, argv+2 );
         else if ( cmd == 2 && 1 < argc )
-            res = ExtrArch( bim, out, ovr, pre, argv[1],
+            res = ExtrArch( bim, out, ovr, pre, frc, argv[1],
                             (unsigned long)argc-2, argv+2 );
         else
             res = HelpArch();
--- unzoo-4.4.orig/debian/changelog
+++ unzoo-4.4/debian/changelog
@@ -0,0 +1,35 @@
+unzoo (4.4-4) unstable; urgency=low
+
+  * Modified patch from -3 with feedback from Joey Hess. Thanks!
+
+ -- Thomas Schoepf   Tue, 17 May 2005 22:01:26 +0200
+
+unzoo (4.4-3) unstable; urgency=high
+
+  * Closes: #306164: unzoo: directory traversal security bug
+    Check for and remove patterns like '/../' that can be used for
+    directory traversal attacks.
+    Added an option to force extraction to original location.
+  * Updated Standards-Version to 3.6.1. No changes required.
+
+ -- Thomas Schoepf   Tue, 17 May 2005 11:36:24 +0200
+
+unzoo (4.4-2) unstable; urgency=low
+
+  * debian/control: Standards-Version: 3.5.7
+  * debian/rules: compile with -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 to
+    support large files.
+  * debian/rules: compile with -DSYS_HAS_MKDIR to fix compiler warning.
+  * no longer create /usr/doc -> /usr/share/doc symlink to be lintian clean.
+  * unzoo.c: include header files for strcat(), system(), chmod(), utime().
+  * unzoo.c: fixed parameter format for utime() call.
+  * unzoo.c: fixed warning: array subscript has type `char'
+
+ -- Thomas Schoepf   Fri, 15 Nov 2002 17:01:49 +0100
+
+unzoo (4.4-1) unstable; urgency=low
+
+  * Initial Release. Closes: Bug#157975.
+
+ -- Thomas Schoepf   Fri, 23 Aug 2002 19:33:24 +0200
+
--- unzoo-4.4.orig/debian/dirs
+++ unzoo-4.4/debian/dirs
@@ -0,0 +1 @@
+usr/bin
--- unzoo-4.4.orig/debian/control
+++ unzoo-4.4/debian/control
@@ -0,0 +1,16 @@
+Source: unzoo
+Section: utils
+Priority: extra
+Maintainer: Thomas Schoepf 
+Build-Depends: debhelper (>> 3.0.0)
+Standards-Version: 3.6.1
+
+Package: unzoo
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: zoo archive extractor
+ A zoo archive is a file that contains several files, called its
+ members, usually in compressed form to save space. 'unzoo' can list
+ all or selected members or extract all or selected members, i.e.,
+ uncompress them and write them to files. It cannot add new members or
+ delete members. For this you need the zoo archiver, called 'zoo'.
--- unzoo-4.4.orig/debian/rules
+++ unzoo-4.4/debian/rules
@@ -0,0 +1,102 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper.
+# GNU copyright 1997 to 1999 by Joey Hess.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# This is the debhelper compatibility version to use.
+export DH_COMPAT=3
+
+CFLAGS = -Wall -g -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+INSTALL = install
+INSTALL_PROGRAM = $(INSTALL) -p -o root -g root -m 755
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+CFLAGS += -O0
+else
+CFLAGS += -O2
+endif
+
+ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+INSTALL_PROGRAM += -s
+endif
+
+configure: configure-stamp
+configure-stamp:
+	dh_testdir
+	# Add here commands to configure the package.
+
+	touch configure-stamp
+
+
+build: build-stamp
+
+build-stamp: configure-stamp 
+	dh_testdir
+
+	# Add here commands to compile the package.
+	#$(MAKE)
+	gcc $(CFLAGS) -DSYS_HAS_MKDIR -DSYS_IS_UNIX unzoo.c -o unzoo
+	#/usr/bin/docbook-to-man debian/unzoo.sgml > unzoo.1
+
+	touch build-stamp
+
+clean:
+	dh_testdir
+	dh_testroot
+	rm -f build-stamp configure-stamp
+
+	# Add here commands to clean up after the build process.
+	#-$(MAKE) clean
+	rm -f unzoo
+
+	dh_clean
+
+install: build
+	dh_testdir
+	dh_testroot
+	dh_clean -k
+	dh_installdirs
+
+	# Add here commands to install the package into debian/unzoo.
+	#$(MAKE) install DESTDIR=$(CURDIR)/debian/unzoo
+	$(INSTALL_PROGRAM) unzoo $(CURDIR)/debian/unzoo/usr/bin/
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+	dh_testdir
+	dh_testroot
+#	dh_installdebconf	
+	dh_installdocs
+	dh_installexamples
+	dh_installmenu
+#	dh_installlogrotate
+#	dh_installemacsen
+#	dh_installpam
+#	dh_installmime
+#	dh_installinit
+	dh_installcron
+	dh_installman
+	dh_installinfo
+#	dh_undocumented
+	dh_installchangelogs 
+	dh_link
+	dh_strip
+	dh_compress
+	dh_fixperms
+#	dh_makeshlibs
+	dh_installdeb
+#	dh_perl
+	dh_shlibdeps
+	dh_gencontrol
+	dh_md5sums
+	dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
--- unzoo-4.4.orig/debian/manpages
+++ unzoo-4.4/debian/manpages
@@ -0,0 +1 @@
+debian/unzoo.1
--- unzoo-4.4.orig/debian/unzoo.1
+++ unzoo-4.4/debian/unzoo.1
@@ -0,0 +1,110 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH UNZOO 1 "August 23, 2002"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp     insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+unzoo \- zoo archive extractor
+.SH SYNOPSIS
+.B unzoo
+.br
+.B unzoo
+.RI [-l] " [-v] " [.zoo] " [..]
+.br
+.B unzoo
+.RI -x " [-abnpo]" " [-j ]" " [.zoo]" " [..]"
+.SH DESCRIPTION
+This manual page documents briefly the
+.B unzoo
+command.
+This manual page was written for the Debian distribution
+because the original program does not have a manual page.
+.PP
+.\" TeX users may be more comfortable with the \fB\fP and
+.\" \fI\fP escape sequences to invode bold face and italics, 
+.\" respectively.
+\fBunzoo\fP is a program that lists or extracts the members of a zoo archive.
+A zoo archive is a file that
+contains several files, called its members, usually in compressed form to
+save space. \fBunzoo\fP can list all or selected members or extract all or
+selected members, i.e., uncompress them and write them to files. It
+cannot add new members or delete members. For this you need the zoo
+archiver, called \fBzoo\fP, written by Rahul Dhesi.
+
+If you call \fBunzoo\fP with no arguments, it will first print a summary of
+the commands and then prompt for command lines interactively, until you
+enter an empty line.
+
+Usually \fBunzoo\fP will only list or extract the latest generation of each
+member. But if you append ';' to a path name pattern the generation
+with the number  is listed or extracted.  itself can contain the
+wildcard characters '?' and '*', so appending ';*' to a path name pattern
+causes all generations to be listed or extracted.
+
+.SH OPTIONS
+A summary of options is included below.
+.TP
+.B \-l
+list the members in the archive . For each member \fBunzoo\fP
+prints the size that the extracted file would have, the compression
+factor, the size that the member occupies in the archive (not counting the
+space needed to store the attributes such as the path name of the file), the
+date and time when the files were last modified, and finally the path name
+itself. Finally \fBunzoo\fP prints a grand total for the file sizes, the
+compression factor, and the member sizes.
+.TP
+.B 
+list only files matching at least one pattern, '?' matches
+any char, '*' matches any string.
+.TP
+.B \-v
+list also the generation numbers and the comments, where higher numbers mean
+later generations. Members for which generations are disabled are listed
+with  ';0'.
+.TP
+.B \-x
+extract the members from the archive . Members are stored with a
+full path name in the archive and if the operating system supports this, they
+will be extracted into appropriate subdirectories, which will be
+created on demand.
+.TP
+.B \-a
+extract all members as text files (not only those with !TEXT! comments)
+.TP
+.B \-b
+extract all members as binary files (even those with !TEXT! comments)
+.TP
+.B \-n
+extract no members, only test the integrity. For each member the name is
+printed followed by '-- tested' if the member is intact or
+by '-- error, CRC failed' if it is not.
+.TP
+.B \-p
+extract to stdout
+.TP
+.B \-o
+extract over existing files without asking for confirmation. The default is to
+ask for confirmation. \fBunzoo\fP will never overwrite existing read-only
+files.
+.TP
+.B \-j
+prepend the string  to all path names for the members before they are
+extracted. So for example if an archive contains absolute path names under
+UNIX, '-j ./' can be used to convert them to relative pathnames.
+Note that the directory  must exist, \fBunzoo\fP will not create it on
+demand.
+.SH AUTHOR
+This manual page was written by Thomas Schoepf ,
+for the Debian GNU/Linux system (but may be used by others).
--- unzoo-4.4.orig/debian/docs
+++ unzoo-4.4/debian/docs
@@ -0,0 +1 @@
+README
--- unzoo-4.4.orig/debian/copyright
+++ unzoo-4.4/debian/copyright
@@ -0,0 +1,11 @@
+This package was debianized by Thomas Schoepf  on
+Fri, 23 Aug 2002 19:33:24 +0200.
+
+It was downloaded from
+http://archives.math.utk.edu/software/multi-platform/gap/util/unzoo.c
+
+Upstream Author: Martin Schoenert
+
+Copyright:
+
+Public Domain.
--- unzoo-4.4.orig/README
+++ unzoo-4.4/README
@@ -0,0 +1,152 @@
+unzoo.c                     Tools                        Martin Schoenert
+
+@(#)$Id: unzoo.c,v 4.4 2000/05/29 08:56:57 sal Exp $
+
+This file is in the Public Domain.
+
+SYNTAX
+
+'unzoo'
+'unzoo [-l] [-v] [.zoo] [..]'
+'unzoo -x [-abnpo] [.zoo] [..]'
+
+DESCRIPTION
+
+'unzoo' is  a zoo  archive extractor.   A zoo archive   is  a  file  that
+contains several files, called its members, usually in compressed form to
+save space.  'unzoo' can list all or  selected members or  extract all or
+selected members, i.e.,  uncompress them and write   them  to files.   It
+cannot add new members or  delete  members.  For this   you need the  zoo
+archiver, called 'zoo', written by Rahul Dhesi.
+
+If you call 'unzoo'  with no arguments, it will  first print a summary of
+the commands and  then prompt for  command lines interactively, until you
+enter an empty line.  This is useful  on systems  that do not support the
+notion of command line arguments such as the Macintosh.
+
+If you call  'unzoo' with the  '-l' option,  it lists the  members in the
+archive .   For each member 'unzoo'   prints  the size  that the
+extracted file  would  have, the  compression factor,  the  size that the
+member occupies in the archive (not  counting  the  space needed to store
+the attributes such as the path name of the file), the date and time when
+the files were last modified, and finally  the path name itself.  Finally
+'unzoo' prints a grand total for the  file sizes, the compression factor,
+and the member sizes.
+
+The '-v' suboption causes 'unzoo' to append to each path name,  separated
+by a ';', the generation number of the member,  where higher numbers mean
+later generations.  Members for which generations are disabled are listed
+with  ';0'.  Also 'unzoo'   will print the  comments associated  with the
+archive itself or the members, preceeded by the string '# '.
+
+If you call 'unzoo' with the '-x' option,  it extracts the  members  from
+the archive .  Members are  stored with a  full path name in the
+archive and if the operating system supports this, they will be extracted
+into   appropriate subdirectories,   which will   be  created on  demand.
+The members are usually  extracted as binary files,  with no translation.
+However, if a member has a  comment that starts with the string '!TEXT!',
+it is  extracted as a  text file, i.e.,  it will be  translated from  the
+universal text file format (with  as line separator as under UNIX) to
+the local text file format (e.g., with / as separator under DOS).
+If the archive  itself has a  comment that starts with  '!TEXT!' then all
+members will be extracted as text files, even those that have no comment.
+For each member the name is printed followed by  '-- extracted as binary'
+or '-- extracted as text' when the member has been completely extracted.
+
+The '-a' suboption causes  'unzoo' to extract all members  as text files,
+even if they have no comment starting with  '!TEXT!'.
+
+The '-b' suboption causes 'unzoo' to extract all members as binary files,
+even if they have a comment starting with  '!TEXT!'.
+
+The '-n' suboption causes 'unzoo' to suppress writing the files.  You use
+this suboption  to test the integrity  of the archive  without extracting
+the members.  For each member the name is printed followed by '-- tested'
+if the member is intact or by '-- error, CRC failed' if it is not.
+
+The '-p' suboption causes 'unzoo' to print the files to stdout instead of
+writing them to files.
+
+The '-o'  suboption causes 'unzoo'   to overwrite existing  files without
+asking  you for confirmation.   The  default is  to ask for  confirmation
+' exists, overwrite it? (Yes/No/All/Ren)'.   To this you can answer
+with 'y' to overwrite the  file, 'n' to skip  extraction of the file, 'a'
+to overwrite this and all following files, or 'r' to enter a new name for
+the file.  'unzoo' will never overwrite existing read-only files.
+
+The '-j ' suboption causes 'unzoo' to prepend the string 
+to  all path names for  the members  before  they  are extracted.  So for
+example if an archive contains absolute  path names under  UNIX,  '-j ./'
+can be used to convert them to relative pathnames.   This option  is also
+useful  on  the Macintosh where   you start 'unzoo' by clicking,  because
+then the current directory will be the one where 'unzoo' is,  not the one
+where the  archive is.   Note  that the  directory   must  exist,
+'unzoo' will not create it on demand.
+
+If no    argument is given all members  are  listed or  extracted.
+If  one or  more   arguments are given,  only members whose  names
+match at least one of  the   patterns  are  listed  or  extracted.
+ can  contain the wildcard   '?', which  matches any character  in
+names, and '*', which  matches any number  of characters  in names.  When
+you pass the  arguments on the command  line you will usually have
+to quote them to keep the shell from trying to expand them.
+
+Usually 'unzoo' will  only list or extract the  latest generation of each
+member.  But if you append ';' to a path  name pattern the generation
+with the number  is listed or extracted.   itself can contain the
+wildcard characters '?' and '*', so appending ';*' to a path name pattern
+causes all generations to be listed or extracted.
+
+
+COMPATIBILITY
+
+'unzoo'  is based heavily on the 'booz' archive extractor by Rahul Dhesi.
+I basically stuffed everything in one file (so  no 'Makefile' is needed),
+cleaned it up (so that it is now more portable and  a little bit faster),
+and added the  support for  long file names,  directories,  and comments.
+
+'unzoo' differs in some details from  'booz' and the zoo archiver  'zoo'.
+
+'unzoo' can  only list  and extract members   from archives, like 'booz'.
+'zoo' can also add members, delete members, etc.
+
+'unzoo' can extract members as text files, converting from universal text
+format to the local text format,  if the '-a' option is given or the '-b'
+option is not given and the  member has a comment starting with '!TEXT!'.
+So in the absence of the '-a' option and comments starting with '!TEXT!',
+'unzoo' behaves like  'zoo' and 'booz',  which always extract as  binary.
+But  'unzoo' can  correctly extract  text files from  archives that  were
+created under UNIX (or other systems using the universal text format) and
+extended with '!TEXT!' comments on systems such as DOS, VMS, Macintosh.
+
+'unzoo' can handle  long names, which it converts  in  a system dependent
+manner to local  names, like  'zoo'  (this may not   be available on  all
+systems).  'booz' always uses the short DOS format names.
+
+'unzoo' extracts  members  into  subdirectories, which  it  automatically
+creates, like 'zoo' (this  may not be available on  all systems).  'booz'
+always extracts all members into the current directory.
+
+'unzoo'  can handle comments and generations in the  archive, like 'zoo'.
+'booz' ignores all comments and generations.
+
+'unzoo' cannot handle  members compressed with  the old method, only with
+the new  high method or  not compressed  at all.   'zoo' and  'booz' also
+handle members compress with the old method.  This shall be fixed soon.
+
+'unzoo' can handle archives in  binary format under  VMS, i.e., it is not
+necessary to convert  them to stream linefeed  format  with 'bilf' first.
+'zoo' and 'booz' require this conversion.
+
+'unzoo' is somewhat faster than 'zoo' and 'booz'.
+
+'unzoo' should be much easier to port than both 'zoo' and 'booz'.
+
+ACKNOWLEDGMENTS
+
+Rahul Dhesi  wrote the  'zoo' archiver and the  'booz' archive extractor.
+Haruhiko Okumura  wrote the  LZH code (originally for his 'ar' archiver).
+David Schwaderer provided the CRC-16 calculation in PC Tech Journal 4/85.
+Jeff Damens  wrote the name match code in 'booz' (originally for Kermit).
+Harald Boegeholz  ported 'unzoo' to OS/2 with the emx development system.
+Dave Bayer ported 'unzoo' to the Macintosh,  including Macbinary support.