Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

v25i144: zip - file compression/archive tool, Part03/07

4 views
Skip to first unread message

Mark Adler

unread,
Mar 1, 1992, 10:07:48 PM3/1/92
to
Submitted-By: mad...@cco.caltech.edu (Mark Adler)
Posting-Number: Volume 25, Issue 144
Archive-Name: zip/part03

#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 3 (of 7)."
# Contents: zip.1 zipfile.c zipsplit.c
# Wrapped by vi...@cognition.pa.dec.com on Sun Mar 1 18:57:38 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'zip.1' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'zip.1'\"
else
echo shar: Extracting \"'zip.1'\" \(21611 characters\)
sed "s/^X//" >'zip.1' <<'END_OF_FILE'
X.\" Copyright (C) 1990,1991 Mark Adler, Richard B. Wales, and Jean-loup Gailly.
X.\" Permission is granted to any individual or institution to use, copy, or
X.\" redistribute this software so long as all of the original files are included
X.\" unmodified, that it is not sold for profit, and that this copyright notice
X.\" is retained.
X.\"
X.\" zip.1 by Mark Adler.
X.\"
X.TH ZIP 1
X.SH NAME
zip \- package and compress (archive) files
X.SH SYNOPSIS
X.B zip
X[
X.B \-cdefghijklmnoqrsuwyz
X] [
X.B \-b
path ] [
X.B \-t
mmddyy ] zipfile list [
X.B \-x
list ]
X.br
X.SH DESCRIPTION
X.I Zip
is a compression and file packaging utility for Unix, MSDOS, OS/2, and VMS.
It is
analogous to a combination of tar and compress and is compatible with PKZIP
X(Phil Katz ZIP) for MSDOS systems.
X.PP
There is a companion to
X.I Zip
called
X.I UnZip
X(of course) which you should be able
to find the same place you got
X.I Zip. Zip
and
X.I UnZip
can work with files
produced by PKZIP under MSDOS, and PKZIP and PKUNZIP can work with files
produced by
X.I Zip.
X.PP
X.I Zip
puts one or more compressed files into a single "zip file" along with
information about the files, including the name, path if requested, date
and time last modified, protection, and check information to verify the
fidelity of each entry.
X.I Zip
can pack an entire directory structure in a
zip file with a single command. Compression ratios of 2:1 to 3:1 are
common for text files.
X.I Zip
has two compression methods, implosion and
shrinking, and automatically chooses the better of the two for each file
to be compressed.
X.PP
X.I Zip
is useful for packaging a set of files to send to someone or for
distribution; for archiving or backing up files; and for saving disk space by temporarily
compressing unused files or directories.
X.SH "HOW TO INSTALL ZIP"
X.I Zip
is distributed as C source code that can be compiled on a wide range of
Unix machines, VAXes running VMS, and MSDOS machines using
Microsoft or Borland C++, and OS/2 machines using Microsoft C.
You will
need Unzip (under Unix, MSDOS, or VMS) or PKUNZIP (under MSDOS) to unpack the
distribution file, zip10.zip.
X.PP
XFirst, unpack the source as follows, assuming that you
have zip10.zip in the current directory:
X.PP
X.ti+5n
mkdir zipsrc
X.ti+5n
cd zipsrc
X.ti+5n
unzip ../zip10
X.PP
This extracts all source files and documentation in the directory called
X"zipsrc". You then do:
X.PP
X.ti+5n
make system
X.PP
where "system" is one of: bsd, bsdold, sysv, next, next10, sun, hpux, dnix,
cray, 3b1, zilog, aux, convex, aix, or minix. If you are using a NeXT
running version
X2.0 or greater, then make next. If you are using 1.0, then make
next10. If you
are using Sun OS 4.x, then make sun. If you are using HPUX, then make hpux.
The other special systems are DNIX 5.2 or 5.3, Cray Unicos,
AT&T 3B1 (also known as Unix PC or PC 7300), Zilog Zeus, A/UX, Convex, AIX,
and MINIX.
Otherwise, if you are using BSD Unix, try bsd. If the linker cannot find
X_memset or _memcpy, try bsdold. If you are using System V Unix or SCO Unix,
try sysv. Also use sysv on a Silicon Graphics (SGI) machine.
You can also cross-compile
X.I Zip
for MSDOS under SCO 386 Unix using "make scodos".
X.PP
If none of these compiles, links, and functions properly on your Unix system,
see the section BUGS below for how to get help.
X.PP
If the appropriate system was selected, then the executable "zip" will be
created. You can move the executable "zip" to an appropriate directory
in the search path using a command like:
X.PP
X.ti+5n
mv zip ~/bin
X.PP
or
X.PP
X.ti+5n
mv zip /usr/local/bin
X.PP
You can use the command "set" to see the current search path. If you are
using the C-Shell (csh), enter the command:
X.PP
X.ti+5n
rehash
X.PP
so csh can find the new command in the path. You are now ready to use
X.I Zip.
X.PP
You can also move the manual page (the raw form of what you're reading)
to where the Unix man command can find it (assuming you have the necessary
privileges):
X.PP
X.ti+5n
mv zip.1 /usr/man/man1
X.PP
You can get rid of the now unnecessary source and object files with:
X.PP
X.ti+5n
cd ..
X.ti+5n
rm -r zipsrc
X.PP
This will remove the directory zip and its contents created by unzip.
You should keep the zip10.zip file around though, in case you
need to build it again or want to give it to a colleague.
X.PP
The steps for installation under MSDOS, OS/2, and VMS are similar to the above:
first unzip the distribution files into their own directory. Then under
MSDOS do one of:
X.PP
X.ti+5n
make makefile.msc
X.ti+5n
make -fmakefile.bor
X.PP
for Microsoft or Borland C++, respectively. Under OS/2:
X.PP
X.ti+5n
nmake -f makefile.os2
X.PP
for Microsoft C 6.00. Under VAX VMS:
X.PP
X.ti+5n
X@makevms
X.PP
The installation process will also compile and link several
other utilities. They are zipcloak for encrypting and decrypting zip files,
zipnote for editing zip file comments, zipsplit for splitting a zip file
into several zip files, and ship for sending zip files or any other binary
file via electronic mail. For command help on any of the zip* utilities,
simply enter the name with no arguments. For help with ship, enter "ship -h".
X.SH "HOW TO USE ZIP"
The simplest use of
X.I Zip
is as follows:
X.PP
X.ti+5n
zip stuff *
X.PP
This will create the file "stuff.zip" (assuming it does not exist) and put
all the files in the current directory in stuff.zip in a compressed form.
The .zip suffix is added automatically, unless that file name given contains
a dot already. This allows specifying suffixes other than ".zip".
X.PP
Because of the way the shell does filename substitution, files that start
with a "." are not included. To include those as well, you can:
X.PP
X.ti+5n
zip stuff .* *
X.PP
XEven this will not include any subdirectories that are in the current
directory. To zip up an entire directory, the command:
X.PP
X.ti+5n
zip -r foo foo
X.PP
will create the file "foo.zip" containing all the files and directories in
the directory "foo" that is in the current directory. The "r" option means
recurse through the directory structure. In this case, all the
files and directories in foo are zipped, including the ones that start with
a ".", since the recursion does not use the shell's file-name substitution.
You should not use -r with the name ".*", since that matches ".." which will
attempt to zip up the parent directory--probably not what was intended.
X.PP
You may want to make a zip file that contains the files in foo, but not record
the directory name, foo. You can use the -j (junk path) option to leave off
the path:
X.PP
X.ti+5n
zip -j foo foo/*
X.PP
The -y option (only under Unix) will store symbolic links as such in the
zip file, instead of compressing and storing the file referred to in the link.
X.PP
You might be zipping to save disk space, in which case you could:
X.PP
X.ti+5n
zip -rm foo foo
X.PP
where the "m" option means "move". This will delete foo and its contents
after making foo.zip. No deletions will be done until the zip has completed
with no errors. This option is obviously more dangerous and should be
used with care.
X.PP
If the zip file already exists, these commands will replace existing or add
new entries to the zip file. For example, if you were really short on disk
space, you might not have enough room simultaneously to hold the directory
foo and the compressed foo.zip. In this case, you could do it in steps. If
foo contained the subdirectories tom, dick, and harry, then you could:
X.PP
X.ti+5n
zip -rm foo foo/tom
X.ti+5n
zip -rm foo foo/dick
X.ti+5n
zip -rm foo foo/harry
X.PP
where the first command would create foo.zip, and the next two would add to
it. At the completion of each zip command, the directory just zipped would
be deleted, making room in which the next
X.I Zip
command could work.
X.SH "MODIFYING EXISTING ZIP FILES"
When given the name of an existing zip file with the above commands,
X.I Zip
will replace identically named entries in the
X.I Zip
file or add entries for
new names. For example, if foo.zip exists and contains foo/file1 and
foo/file2, and the directory foo contains the files foo/file1 and foo/file3,
then:
X.PP
X.ti+5n
zip -r foo foo
X.PP
will replace foo/file1 in foo.zip and add foo/file3 to foo.zip. After
this, foo.zip contains foo/file1, foo/file2, and foo/file3, with foo/file2
unchanged from before.
X.PP
When changing an existing zip file,
X.I Zip
will write a temporary file with
the new contents, and only replace the old one when the zip has completed
with no errors. Also, the two methods, shrink and implode, create
temporary files that are deleted after each file is zipped. You can use
the -b option to specify a different path (usually a different device) to
put the temporary files in. For example:
X.PP
X.ti+5n
zip -b /tmp stuff *
X.PP
will put the temporary zip file and the temporary compression files in the
directory "/tmp", copying over stuff.zip in the current directory when
done.
X.PP
If you are only adding entries to a zip file, not replacing, and the
X-g option is given, then
X.I Zip
grows (appends to) the file instead of copying it. The danger of this is that
if the operation fails, the original zip file is corrupted and lost.
X.PP
There are two other ways to change or add entries in a zip file that are
restrictions of simple addition or replacement. The first is -u (update)
which will add new entries to the zip file as before but will replace
existing entries only if the modified date of the file is more recent than
the date recorded for that name in the zip file. For example:
X.PP
X.ti+5n
zip -u stuff *
X.PP
will add any new files in the current directory, and update any changed files
in the zip file stuff.zip. Note that
X.I Zip
will not try to pack stuff.zip into
itself when you do this.
X.I Zip
will always exclude the zip file from the files on which to be operated.
X.PP
The second restriction is -f (freshen) which, like update, will only replace
entries with newer files; unlike update, will not add files that are not
already in the zip file. For this option, you may want to simply freshen all
of the files that are in the specified zip file. To do this you would simply:
X.PP
X.ti+5n
zip -f foo
X.PP
Note that the -f option with no arguments freshens all the entries in the
zip file. The same is true of -u, and hence "zip -u foo" and "zip -f foo"
both do the same thing.
X.PP
This command should
be run from the same directory from which the original zip command was run,
since paths stored in zip files are always relative.
X.PP
Another restriction that can be used with adding, updating, or freshening is
X-t (time), which will not operate on files modified earlier than the specified
date. For example:
X.PP
X.ti+5n
zip -rt 120791 infamy foo
X.PP
will add all the files in foo and its subdirectories that were last modified
on December 7, 1991, or later to the zip file infamy.zip.
X.PP
Also, files can be explicitly excluded using the -x option:
X.PP
X.ti+5n
zip -r foo foo -x \\*.o
X.PP
which will zip up the contents of foo into foo.zip but exclude all the
files that end in ".o". Here the backslash causes
X.I Zip
to match file names
that were found when foo was searched.
X.PP
The last operation is -d (delete) which will remove entries from a zip file.
An example might be:
X.PP
X.ti+5n
zip -d foo foo/tom/junk foo/harry/\\* \\*.o
X.PP
which will remove the entry foo/tom/junk, all of the files that start with
X"foo/harry/", and all of the files that end with ".o" (in any path). Note
that once again, the shell expansion has been inhibited with backslashes, so
that
X.I Zip
can see the asterisks.
X.I Zip
can then match on the contents of the zip
file instead of the contents of the current directory.
X.PP
Under MSDOS, -d is case sensitive when it matches names in the zip file.
This allows deleting names that were zipped on other systems, but requires
that the names be entered in upper case if they were zipped on an MSDOS
system, so that the names can be found in the zip file and deleted.
X.SH "MORE OPTIONS"
As mentioned before,
X.I Zip
will use the best of two methods: shrink or implode.
Usually implode is better, but sometimes shrink is better, especially for
smaller files. Sometimes neither method produces a packed version smaller
than the original file, in which case it is stored in the zip file with no
compression (called the "store" method).
X.PP
The option -s (shrink) will force
X.I Zip
always to use shrink or store, and the
X-i (implode) option forces
X.I Zip
to use implode or store. Shrinking is faster
than imploding, and so -s might be used when speed is more important than
optimal compression. Implode only (-i) might be used when the unzipper
for which the
zip file is destined can only handle implosion. An example of this is
the PKSFXjr program that comes with PKZIP. Also, -i is slightly faster
than imploding and shrinking at the same time. For example:
X.PP
X.ti+5n
zip -rs foo foo
X.PP
will zip up the directory foo into foo.zip using only shrink or store.
The speed of implosion can also be controlled with options -0 (fastest
method but less compression) to -9 (best compression but slower). The
default value is -5. For example:
X.PP
X.ti+5n
zip -r0 foo foo
X.PP
In nearly all cases, a file that is already compressed cannot be compressed
further by
X.I Zip,
or if it can, the effect is minimal. The -n option prevents
X.I Zip
from trying to compress files that have the suffixes: .Z, .zip, .zoo,
or .arc. Such files are simply stored (0% compression) in the output zip file,
so that
X.I Zip
doesn't waste its time trying to compress them.
If the environment variable NOZIP is set, then the suffixes listed
there are used instead of the default list. The suffixes are separated by
either colons or semicolons. For example, in Unix csh:
X.PP
X.ti+5n
setenv NOZIP .Z:.zip:.tiff:.gif:.snd
X.ti+5n
zip -rn foo foo
X.PP
will put everything in foo into foo.zip, but will store any files that end
in .Z, .zip, .tiff, .gif, or .snd without trying to compress them. (Image
and sound files often have their own specialized compression methods.) If
the environment variable NOZIP exists but is empty or contains just a colon
or semicolon, then zip -n will store all the entries and do no compression.
X.PP
Under Unix and under OS/2 (if files from a HPFS are stored),
X.I Zip
will store the full path (relative to the current path) and name of the
file (or just the name if -j is specified) in the zip file along with the
Unix attributes, and it will mark
the entry as made under Unix. If the zip file is intended for PKUNZIP under
MSDOS, then the -k (Katz) option should be used to attempt to convert the
names and paths to conform to MSDOS, store only the MSDOS attribute (just
the user write attribute from Unix), and mark the entry as made under MSDOS
X(even though it wasn't).
X.PP
The -o (older) option will set the "last modified" time of the zip file to
the latest "last modified" time of the entries in the zip file. This can
be used without any other operations, if desired. For example:
X.PP
X.ti+5n
zip -o foo
X.PP
will change the last modified time of foo.zip to the latest time of the
entries in foo.zip.
X.PP
The -e and -c options operate on all files updated or added to the zip file.
XEncryption (-e) will prompt for a password on the terminal and will
not echo the password as it is typed (if stderr is not a TTY, Zip will exit
with an error). New zip entries will be encrypted using that password. For
added peace of mind, you can use -ee, which will prompt for the password
twice, checking that the two are the same before using it.
X.PP
One-line comments can be added for each file with the -c option. The zip
file operations (adding or updating) will be done first, and you will then be
prompted for a one-line comment for each file. You can then enter the comment
followed by return, or just return for no comment.
X.PP
The -z option will prompt you for a multi-line comment for the entire zip
file. This option can be used by itself, or in combination with other
options. The comment is ended by a line containing just a period, or an end
of file condition (^D on Unix, ^Z on MSDOS, OS/2, and VAX/VMS).
Since -z reads the
lines from stdin, you can simply take the comment from a file:
X.PP
X.ti+5n
zip -z foo < foowhat
X.PP
The -q (quiet) option eliminates the informational messages and comment prompts
while
X.I Zip
is operating. This might be used in shell scripts, for example, or if the
zip operation is being performed as a background task ("zip -q foo *.c &").
X.PP
X.I Zip
can take a list of file names to operate on from stdin using the - option.
In Unix, this option can be used with the find command to extend greatly
the functionality of
X.I Zip.
XFor example, to zip up all the C source files in the current directory and
its subdirectories, you can:
X.PP
X.ti+5n
find . -type f -name "*.[ch]" -print | zip source -
X.PP
Note that the pattern must be quoted to keep the shell from expanding it.
X.PP
Under VMS only, the -w option will append the version number of the files to
the name and zip up multiple versions of files. Without -w,
X.I Zip
will only use the most recent version of the specified file(s).
X.PP
If
X.I Zip
is run with no arguments or with the -h option, the license and the
command-argument and option help is shown. The -l option just shows the
license.
X.SH "ABOUT PATTERN MATCHING"
X(Note: this section applies to Unix. Watch this space for details on MSDOS
and VMS operation.)
X.PP
The Unix shell (sh or csh) does filename substitution on command arguments.
The special characters are ?, which matches any single character; * which
matches any number of characters (including none); and [] which matches any
character in the range inside the brackets (like [a\-f] or [0\-9]). When
these characters are encountered (and not escaped with a backslash or
quotes), the
shell will look for files relative to the current path that match the
pattern, and replace the argument with a list of the names that matched.
X.PP
X.I Zip
can do the same matching on names that are in the zip file being
modified or, in the case of the -x (exclude) option, on the list of
files to be operated on, by using backslashes or quotes
to tell the shell not to do the name expansion. In general, when
X.I Zip
encounters a name in the list
of files to do, it first looks for the name in the file system. If it
finds it, it then adds it to the list of files to do. If it does not
find it, it will look for the name in the zip file being modified (if it
exists), using the pattern matching characters above, if any. For each
match, it will add that name to the list of files to do. After -x
X(exclude), the names are removed from the to-do list instead of added.
X.PP
The pattern matching includes the path, and so patterns like \\*.o match
names that end in ".o", no matter what the path prefix is. Note that the
backslash must precede every special character (i.e. ?*[]), or the entire
argument must be enclosed in double quotes ("").
X.PP
In general, using backslash to make
X.I Zip
do the pattern matching is used
with the -f (freshen) and -d (delete) options, and sometimes after the
X-x (exclude) option when used with any operation (add, -u, -f, or -d).
X.I Zip
will never use pattern matching to search the file system. If
X.I Zip
has recursed into a directory, all files (and all directories) in there
are fair game.
X.SH COPYRIGHT
Copyright (C) 1990,1991 Mark Adler, Richard B. Wales, and Jean-loup Gailly.
Permission is granted to any individual or institution to use, copy, or
redistribute this software so long as all of the original files are included
unmodified, that it is not sold for profit, and that this copyright notice
is retained.
X.SH ACKNOWLEDGEMENTS
Thanks to R. P. Byrne for his Shrink.Pas program which inspired this project,
and from which the shrink algorithm was stolen; to Phil Katz for making the zip
file format, compression format, and .ZIP filename extension all public domain;
to Keith Petersen for providing a mailing list and ftp site for the INFO-ZIP
group to use; and most importantly, to the INFO-ZIP group itself (listed in
the file infozip.who) without whose tireless testing and bug-fixing efforts
a portable
X.I Zip
would not have been possible. Finally we should thank (blame) the INFO-ZIP
moderator, David Kirschbaum for getting us into this mess in the first place.
X.SH "SEE ALSO"
unzip(1), tar(1), compress(1)
X.SH BUGS
Versions of PKUNZIP before 1.1 have a bug that on rare occasions will prevent
it from unzipping files produced by
X.I Zip
or PKZIP 1.1. If you experience such problems, we recommend that you get
PKUNZIP 1.1 or the portable
X.I Unzip,
neither of which have this problem.
X.PP
Under MSDOS, Zip will find hidden and system files, but not set the
attributes appropriately in the zip file so that Unzip can restore them.
This will be fixed in the next version.
X.PP
Under VMS, not all of the odd file formats are treated properly. Only
stream-LF format zip files are expected to work with Zip. Others can be
converted using Rahul Dhesi's BILF program. The next version of Zip will
handle some of the conversion internally.
X.PP
LIKE ANYTHING ELSE THAT'S FREE, ZIP AND ITS ASSOCIATED UTILITIES ARE
PROVIDED AS IS AND COME WITH NO WARRANTY OF ANY KIND, EITHER EXPRESSED OR
IMPLIED. IN NO EVENT WILL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DAMAGES
RESULTING FROM THE USE OF THIS SOFTWARE.
X.PP
That having been said, please send any problems or comments via email to
the Internet address
zip\-bu...@cs.ucla.edu. For bug reports, please include the
version of Zip, the make options you used to compile it, the machine and
operating system you are using, and as much additional information as
possible. Thank you for your support.
END_OF_FILE
if test 21611 -ne `wc -c <'zip.1'`; then
echo shar: \"'zip.1'\" unpacked with wrong size!
fi
# end of 'zip.1'
fi
if test -f 'zipfile.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'zipfile.c'\"
else
echo shar: Extracting \"'zipfile.c'\" \(20530 characters\)
sed "s/^X//" >'zipfile.c' <<'END_OF_FILE'
X/*
X
X Copyright (C) 1990,1991 Mark Adler, Richard B. Wales, and Jean-loup Gailly.
X Permission is granted to any individual or institution to use, copy, or
X redistribute this software so long as all of the original files are included
X unmodified, that it is not sold for profit, and that this copyright notice
X is retained.
X
X*/
X
X/*
X * zipfile.c by Mark Adler.
X */
X
X#include "zip.h"
X
X
X/* Macros for converting integers in little-endian to machine format */
X#define SH(a) (((ush)(uch)(a)[0]) | (((ush)(uch)(a)[1]) << 8))
X#define LG(a) ((ulg)SH(a) | ((ulg)SH((a)+2) << 16))
X
X/* Macros for writing machine integers to little-endian format */
X#define PUTSH(a,f) {putc((char)(a),(f)); putc((char)((a) >> 8),(f));}
X#define PUTLG(a,f) {PUTSH(a,f) PUTSH((a) >> 16,f)}
X
X
X/* -- Structure of a ZIP file -- */
X
X/* Signatures for zip file information headers */
X#define LOCSIG 0x04034b50L
X#define CENSIG 0x02014b50L
X#define ENDSIG 0x06054b50L
X
X/* Offsets of values in headers */
X#define LOCVER 0 /* version needed to extract */
X#define LOCFLG 2 /* encrypt, implosion flags */
X#define LOCHOW 4 /* compression method */
X#define LOCTIM 6 /* last modified file time, DOS format */
X#define LOCDAT 8 /* last modified file date, DOS format */
X#define LOCCRC 10 /* uncompressed crc-32 for file */
X#define LOCSIZ 14 /* compressed size in zip file */
X#define LOCLEN 18 /* uncompressed size */
X#define LOCNAM 22 /* length of filename */
X#define LOCEXT 24 /* length of extra field */
X
X#define CENVEM 0 /* version made by */
X#define CENVER 2 /* version needed to extract */
X#define CENFLG 4 /* encrypt, implosion flags */
X#define CENHOW 6 /* compression method */
X#define CENTIM 8 /* last modified file time, DOS format */
X#define CENDAT 10 /* last modified file date, DOS format */
X#define CENCRC 12 /* uncompressed crc-32 for file */
X#define CENSIZ 16 /* compressed size in zip file */
X#define CENLEN 20 /* uncompressed size */
X#define CENNAM 24 /* length of filename */
X#define CENEXT 26 /* length of extra field */
X#define CENCOM 28 /* file comment length */
X#define CENDSK 30 /* disk number start */
X#define CENATT 32 /* internal file attributes */
X#define CENATX 34 /* external file attributes */
X#define CENOFF 38 /* relative offset of local header */
X
X#define ENDDSK 0 /* number of this disk */
X#define ENDBEG 2 /* number of the starting disk */
X#define ENDSUB 4 /* entries on this disk */
X#define ENDTOT 6 /* total number of entries */
X#define ENDSIZ 8 /* size of entire central directory */
X#define ENDOFF 12 /* offset of central on starting disk */
X#define ENDCOM 16 /* length of zip file comment */
X
X
X/* Local functions */
X#ifdef PROTO
X local int zqcmp(voidp *, voidp *);
X# ifndef UTIL
X local int zbcmp(voidp *, voidp far *);
X local char *cutpath(char *);
X# endif /* !UTIL */
X#endif /* PROTO */
X
X
local int zqcmp(a, b)
voidp *a, *b; /* pointers to pointers to zip entries */
X/* Used by qsort() to compare entries in the zfile list. */
X{
X return strcmp((*(struct zlist far **)a)->zname,
X (*(struct zlist far **)b)->zname);
X}
X
X
X#ifndef UTIL
X
local int zbcmp(n, z)
voidp *n; /* string to search for */
voidp far *z; /* pointer to a pointer to a zip entry */
X/* Used by search() to compare a target to an entry in the zfile list. */
X{
X#ifdef OS2
X return stricmp((char *)n, ((struct zlist far *)z)->zname);
X#else /* !OS2 */
X return strcmp((char *)n, ((struct zlist far *)z)->zname);
X#endif /* ?OS2 */
X}
X
X
struct zlist far *zsearch(n)
char *n; /* name to find */
X/* Return a pointer to the entry in zfile with the name n, or NULL if
X not found. */
X{
X voidp far **p; /* result of search() */
X
X if (zcount && (p = search(n, (voidp far **)zsort, zcount, zbcmp)) != NULL)
X return *(struct zlist far **)p;
X else
X return NULL;
X}
X
X#endif /* !UTIL */
X
X
X#ifdef VMS
X# define PATHCUT ']'
X#else /* !VMS */
X# define PATHCUT '/'
X#endif /* VMS */
X
char *ziptyp(s)
char *s; /* file name to force to zip */
X/* If the file name *s has a dot (other than the first char), then return
X the name, otherwise append .zip to the name. Allocate the space for
X the name in either case. Return a pointer to the new name, or NULL
X if malloc() fails. */
X{
X char *q; /* temporary pointer */
X char *t; /* pointer to malloc'ed string */
X
X if ((t = malloc(strlen(s) + 5)) == NULL)
X return NULL;
X strcpy(t, s);
X#ifdef MSDOS
X for (q = t; *q; q++)
X if (*q == '\\')
X *q = '/';
X#endif /* MSDOS */
X if (strrchr((q = strrchr(t, PATHCUT)) == NULL ? t : q + 1, '.') == NULL)
X strcat(t, ".zip");
X#ifdef MSDOS
X#ifndef OS2
X strupr(t);
X#endif /* !OS2 */
X#endif /* MSDOS */
X return t;
X}
X
X
int readzipfile()
X/*
X Make first pass through zip file, reading information from local file
X headers and then verifying that information with the central file
X headers. Any deviation from the expected zip file format returns an
X error. At the end, a sorted list of file names in the zip file is made
X to facilitate searching by name.
X
X The name of the zip file is pointed to by the global "zipfile". The
X globals zfiles, zcount, zcomlen, zcomment, and zsort are filled in.
X Return an error code in the ZE_ class.
X*/
X{
X char b[CENHEAD]; /* buffer for central headers */
X FILE *f; /* zip file */
X int m; /* mismatch flag */
X extent n; /* length of name */
X ulg p; /* current file offset */
X char r; /* holds reserved bits during memcmp() */
X ulg s; /* size of data, start of central */
X char *t; /* temporary variable */
X char far *u; /* temporary variable */
X struct zlist far * far *x; /* pointer last entry's link */
X struct zlist far *z; /* current zip entry structure */
X
X /* Initialize zip file info */
X zipbeg = 0;
X zfiles = NULL; /* Points to first header */
X zcomlen = 0; /* zip file comment length */
X
X /* If zip file exists, read headers and check structure */
X if ((f = fopen(zipfile, FOPR)) != NULL)
X {
X x = &zfiles; /* first link */
X p = 0; /* starting file offset */
X zcount = 0; /* number of files */
X
X /* Find start of zip structures */
X while (fread(b, 4, 1, f) == 1 && (s = LG(b)) != LOCSIG && s != ENDSIG)
X if (fseek(f, -3L, SEEK_CUR))
X return ferror(f) ? ZE_READ : ZE_EOF;
X else
X p++;
X zipbeg = p;
X
X /* Read local headers */
X while (LG(b) == LOCSIG)
X {
X /* Read local header raw to compare later with central header
X (this requires that the offest of ext in the zlist structure
X be greater than or equal to LOCHEAD) */
X if ((z = (struct zlist far *)farmalloc(sizeof(struct zlist))) == NULL)
X return ZE_MEM;
X if (fread(b, LOCHEAD, 1, f) != 1)
X return ferror(f) ? ZE_READ : ZE_EOF;
X t = b; u = (char far *)z; n = LOCHEAD;
X do {
X *u++ = *t++;
X } while (--n);
X
X /* Link into list */
X *x = z;
X z->nxt = NULL;
X x = &z->nxt;
X
X /* Read file name and extra field and skip data */
X n = SH(LOCNAM + (uch far *)z);
X z->ext = SH(LOCEXT + (uch far *)z);
X s = LG(LOCSIZ + (uch far *)z);
X if (n == 0)
X {
X sprintf(errbuf, "%d", zcount + 1);
X warn("zero-length name for entry #", errbuf);
X return ZE_FORM;
X }
X if ((z->zname = malloc(n+1)) == NULL ||
X (z->ext && (z->extra = malloc(z->ext)) == NULL))
X return ZE_MEM;
X if (fread(z->zname, n, 1, f) != 1 ||
X (z->ext && fread(z->extra, z->ext, 1, f) != 1) ||
X fseek(f, (long)s, SEEK_CUR))
X return ferror(f) ? ZE_READ : ZE_EOF;
X z->zname[n] = 0; /* terminate name */
X#ifdef UTIL
X z->name = z->zname;
X#else /* !UTIL */
X z->name = in2ex(z->zname); /* convert to external name */
X if (z->name == NULL)
X return ZE_MEM;
X#endif /* ?UTIL */
X
X /* Save offset, update for next header */
X z->off = p;
X p += 4 + LOCHEAD + n + z->ext + s;
X zcount++;
X
X /* Read next signature */
X if (fread(b, 4, 1, f) != 1)
X return ferror(f) ? ZE_READ : ZE_EOF;
X }
X
X /* Point to start of header list and read central headers */
X z = zfiles;
X s = p; /* save start of central */
X while (LG(b) == CENSIG)
X {
X if (z == NULL)
X {
X warn("extraneous central header signature", "");
X return ZE_FORM;
X }
X
X /* Read central header */
X if (fread(b, CENHEAD, 1, f) != 1)
X return ferror(f) ? ZE_READ : ZE_EOF;
X
X /* Compare local header with that part of central header (except
X for the reserved bits in the general purpose flags and except
X for length of extra fields--authentication can make these
X different in central and local headers) */
X z->lflg = SH(LOCFLG + (uch far *)z); /* Save reserved bits */
X r = b[CENFLG+1];
X ((uch far *)z)[LOCFLG+1] &= 0x1f; /* Zero out reserved bits */
X b[CENFLG+1] &= 0x1f;
X for (m = 0, u = (char far *)z, n = 0; n < LOCHEAD - 2; n++)
X if (u[n] != b[n+2])
X {
X if (!m)
X warn("local and central headers differ for ", z->zname);
X m = 1;
X sprintf(errbuf, " offset %d--local = %02x, central = %02x",
X n, (uch)u[n], (uch)b[n+2]);
X warn(errbuf, "");
X }
X if (m)
X return ZE_FORM;
X b[CENFLG+1] = r; /* Restore reserved bits */
X
X /* Overwrite local header with translated central header */
X z->vem = SH(CENVEM + b);
X z->ver = SH(CENVER + b);
X z->flg = SH(CENFLG + b); /* may be different from z->lflg */
X z->how = SH(CENHOW + b);
X z->tim = LG(CENTIM + b); /* time and date into one long */
X z->crc = LG(CENCRC + b);
X z->siz = LG(CENSIZ + b);
X z->len = LG(CENLEN + b);
X z->nam = SH(CENNAM + b);
X z->cext = SH(CENEXT + b); /* may be different from z->ext */
X z->com = SH(CENCOM + b);
X z->dsk = SH(CENDSK + b);
X z->att = SH(CENATT + b);
X z->atx = LG(CENATX + b);
X if (z->off != LG(CENOFF + b))
X {
X warn("local offset in central header incorrect for ", z->zname);
X return ZE_FORM;
X }
X
X /* Compare name and extra fields and read comment field */
X if ((t = malloc(z->nam)) == NULL)
X return ZE_MEM;
X if (fread(t, z->nam, 1, f) != 1)
X {
X free((voidp *)t);
X return ferror(f) ? ZE_READ : ZE_EOF;
X }
X if (memcmp(t, z->zname, z->nam))
X {
X free((voidp *)t);
X warn("names in local and central differ for ", z->zname);
X return ZE_FORM;
X }
X free((voidp *)t);
X if (z->cext)
X {
X if ((z->cextra = malloc(z->cext)) == NULL)
X return ZE_MEM;
X if (fread(z->cextra, z->cext, 1, f) != 1)
X {
X free((voidp *)(z->cextra));
X return ferror(f) ? ZE_READ : ZE_EOF;
X }
X if (z->ext == z->cext && memcmp(z->extra, z->cextra, z->ext) == 0)
X {
X free((voidp *)(z->cextra));
X z->cextra = z->extra;
X }
X }
X if (z->com)
X {
X if ((z->comment = malloc(z->com)) == NULL)
X return ZE_MEM;
X if (fread(z->comment, z->com, 1, f) != 1)
X {
X free((voidp *)(z->comment));
X return ferror(f) ? ZE_READ : ZE_EOF;
X }
X }
X
X /* Note oddities */
X if (verbose)
X {
X if (z->vem != 10 && z->vem != 11 &&
X (n = z->vem >> 8) != 3 && n != 2 && n != 6)
X {
X sprintf(errbuf, "made by version %d.%d on system type %d: ",
X (z->vem & 0xff) / 10, (z->vem & 0xff) % 10, z->vem >> 8);
X warn(errbuf, z->zname);
X }
X if (z->ver != 10 && z->ver != 11)
X {
X sprintf(errbuf, "needs unzip %d.%d on system type %d: ",
X (z->ver & 0xff) / 10, (z->ver & 0xff) % 10, z->ver >> 8);
X warn(errbuf, z->zname);
X }
X if (z->flg != z->lflg)
X {
X sprintf(errbuf, "local flags = 0x%04x, central = 0x%04x: ",
X z->lflg, z->flg);
X warn(errbuf, z->zname);
X }
X else if (z->flg & ~7)
X {
X sprintf(errbuf, "undefined bits used in flags = 0x%04x: ", z->flg);
X warn(errbuf, z->zname);
X }
X if (z->how > IMPLODE)
X {
X sprintf(errbuf, "unknown compression method %u: ", z->how);
X warn(errbuf, z->zname);
X }
X if (z->dsk)
X {
X sprintf(errbuf, "starts on disk %u: ", z->dsk);
X warn(errbuf, z->zname);
X }
X if (z->att & ~1)
X {
X sprintf(errbuf, "unknown internal attributes = 0x%04x: ", z->att);
X warn(errbuf, z->zname);
X }
X if (((n = z->vem >> 8) != 3) && n != 2 && z->atx & ~0xffL)
X {
X sprintf(errbuf, "unknown external attributes = 0x%08lx: ", z->atx);
X warn(errbuf, z->zname);
X }
X if (z->ext || z->cext)
X if (z->ext == z->cext && z->extra == z->cextra)
X {
X sprintf(errbuf, "has %d bytes of extra data: ", z->ext);
X warn(errbuf, z->zname);
X }
X else
X {
X sprintf(errbuf,
X "local extra (%d bytes) != central extra (%d bytes): ",
X z->ext, z->cext);
X warn(errbuf, z->zname);
X }
X }
X
X /* Clear actions */
X z->mark = 0;
X z->trash = 0;
X
X /* Update file offset */
X p += 4 + CENHEAD + z->nam + z->cext + z->com;
X
X /* Advance to next header structure */
X z = z->nxt;
X
X /* Read next signature */
X if (fread(b, 4, 1, f) != 1)
X return ferror(f) ? ZE_READ : ZE_EOF;
X }
X
X /* Read end header */
X if (z != NULL || LG(b) != ENDSIG)
X {
X warn("missing end signature--probably not a zip file (did you", "");
X warn("remember to use binary mode when you transferred it?)", "");
X return ZE_FORM;
X }
X if (fread(b, ENDHEAD, 1, f) != 1)
X return ferror(f) ? ZE_READ : ZE_EOF;
X if (SH(ENDDSK + b) || SH(ENDBEG + b) ||
X SH(ENDSUB + b) != SH(ENDTOT + b))
X warn("multiple disk information ignored", "");
X if (zcount != SH(ENDSUB + b))
X {
X warn("count in end of central directory incorrect", "");
X return ZE_FORM;
X }
X if (LG(ENDSIZ + b) != p - s)
X {
X warn("central directory size is incorrect", "");
X return ZE_FORM;
X }
X if (LG(ENDOFF + b) != s)
X {
X warn("central directory start is incorrect", "");
X return ZE_FORM;
X }
X cenbeg = s;
X zcomlen = SH(ENDCOM + b);
X if (zcomlen)
X {
X if ((zcomment = malloc(zcomlen)) == NULL)
X return ZE_MEM;
X if (fread(zcomment, zcomlen, 1, f) != 1)
X {
X free((voidp *)zcomment);
X return ferror(f) ? ZE_READ : ZE_EOF;
X }
X }
X if (zipbeg)
X {
X sprintf(errbuf, " has a preamble of %ld bytes", zipbeg);
X warn(zipfile, errbuf);
X }
X if (getc(f) != EOF)
X warn("garbage at end of zip file ignored", "");
X
X /* Done with zip file for now */
X fclose(f);
X
X /* If one or more files, sort by name */
X if (zcount)
X {
X if ((x = zsort =
X (struct zlist far **)malloc(zcount * sizeof(struct zlist far *))) ==
X NULL)
X return ZE_MEM;
X for (z = zfiles; z != NULL; z = z->nxt)
X *x++ = z;
X qsort((char *)zsort, zcount, sizeof(struct zlist far *), zqcmp);
X }
X }
X return ZE_OK;
X}
X
X
int putlocal(z, f)
struct zlist far *z; /* zip entry to write local header for */
XFILE *f; /* file to write to */
X/* Write a local header described by *z to file *f. Return an error code
X in the ZE_ class. */
X{
X PUTLG(LOCSIG, f);
X PUTSH(z->ver, f);
X PUTSH(z->lflg, f);
X PUTSH(z->how, f);
X PUTLG(z->tim, f);
X PUTLG(z->crc, f);
X PUTLG(z->siz, f);
X PUTLG(z->len, f);
X PUTSH(z->nam, f);
X PUTSH(z->ext, f);
X if (fwrite(z->zname, 1, z->nam, f) != z->nam ||
X z->ext && fwrite(z->extra, 1, z->ext, f) != z->ext)
X return ZE_TEMP;
X return ZE_OK;
X}
X
X
int putcentral(z, f)
struct zlist far *z; /* zip entry to write central header for */
XFILE *f; /* file to write to */
X/* Write a central header described by *z to file *f. Return an error code
X in the ZE_ class. */
X{
X PUTLG(CENSIG, f);
X PUTSH(z->vem, f);
X PUTSH(z->ver, f);
X PUTSH(z->flg, f);
X PUTSH(z->how, f);
X PUTLG(z->tim, f);
X PUTLG(z->crc, f);
X PUTLG(z->siz, f);
X PUTLG(z->len, f);
X PUTSH(z->nam, f);
X PUTSH(z->cext, f);
X PUTSH(z->com, f);
X PUTSH(z->dsk, f);
X PUTSH(z->att, f);
X PUTLG(z->atx, f);
X PUTLG(z->off, f);
X if (fwrite(z->zname, 1, z->nam, f) != z->nam ||
X z->cext && fwrite(z->cextra, 1, z->cext, f) != z->cext ||
X z->com && fwrite(z->comment, 1, z->com, f) != z->com)
X return ZE_TEMP;
X return ZE_OK;
X}
X
X
int putend(n, s, c, m, z, f)
int n; /* number of entries in central directory */
ulg s, c; /* size and offset of central directory */
extent m; /* length of zip file comment (0 if none) */
char *z; /* zip file comment if m != 0 */
XFILE *f; /* file to write to */
X/* Write the end of central directory data to file *f. Return an error code
X in the ZE_ class. */
X{
X PUTLG(ENDSIG, f);
X PUTSH(0, f);
X PUTSH(0, f);
X PUTSH(n, f);
X PUTSH(n, f);
X PUTLG(s, f);
X PUTLG(c, f);
X PUTSH(m, f);
X if (m && fwrite(z, 1, m, f) != m)
X return ZE_TEMP;
X return ZE_OK;
X}
X
X
X#ifndef UTIL
X
local char *cutpath(p)
char *p; /* path string */
X/* Cut the last path component off the name *p in place. Return p. */
X{
X char *r; /* pointer to last path delimiter */
X
X#ifdef VMS /* change [w.x.y]z to [w.x]y.DIR */
X if ((r = strrchr(p, ']')) != NULL)
X {
X *r = 0;
X if ((r = strrchr(p, '.')) != NULL)
X {
X *r = ']';
X strcat(r, ".DIR"); /* this assumes a little padding--see PAD */
X }
X else
X *p = 0;
X }
X else
X *p = 0;
X#else /* !VMS */ /* change w/x/y/z to w/x/y */
X if ((r = strrchr(p, '/')) != NULL)
X *r = 0;
X else
X *p = 0;
X#endif /* ?VMS */
X return p;
X}
X
X
int trash()
X/* Delete the compressed files and the directories that contained the deleted
X files, if empty. Return an error code in the ZE_ class. Failure of
X destroy() or deletedir() is ignored. */
X{
X extent i; /* counter on deleted names */
X extent k; /* number of deleted directories this pass */
X extent n; /* number of deleted names left to handle */
X struct zlist far **s; /* table of zip entries to handle, sorted */
X struct zlist far *z; /* current zip entry */
X
X /* Count and delete marked names */
X n = 0;
X for (z = zfiles; z != NULL; z = z->nxt)
X if (z->mark || z->trash)
X {
X z->mark = 1;
X n++;
X if (verbose)
X printf("zip diagnostic: trashing file %s\n", z->name);
X destroy(z->name);
X }
X
X /* Try to delete all paths that lead up to marked names */
X if (n)
X {
X if ((s = (struct zlist far **)malloc((n+1)*sizeof(struct zlist far *))) ==
X NULL ||
X (s[0] = (struct zlist far *)farmalloc(sizeof(struct zlist))) == NULL)
X return ZE_MEM;
X s[0]->name = "";
X s++;
X do {
X n = k = 0;
X for (z = zfiles; z != NULL; z = z->nxt)
X if (z->mark)
X s[n++] = z;
X qsort((char *)s, n, sizeof(struct zlist far *), zqcmp);
X for (i = 0; i < n; i++)
X if (*cutpath(s[i]->name) && strcmp(s[i]->name, s[i-1]->name))
X {
X if (verbose)
X printf("zip diagnostic: trashing directory %s\n", s[i]->name);
X deletedir(s[i]->name);
X k++;
X }
X else
X s[i]->mark = 0;
X } while (k);
X farfree((voidp far *)((--s)[0]));
X free((voidp *)s);
X }
X return ZE_OK;
X}
X
X#endif /* !UTIL */
END_OF_FILE
if test 20530 -ne `wc -c <'zipfile.c'`; then
echo shar: \"'zipfile.c'\" unpacked with wrong size!
fi
# end of 'zipfile.c'
fi
if test -f 'zipsplit.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'zipsplit.c'\"
else
echo shar: Extracting \"'zipsplit.c'\" \(16863 characters\)
sed "s/^X//" >'zipsplit.c' <<'END_OF_FILE'
X/*
X
X Copyright (C) 1990,1991 Mark Adler, Richard B. Wales, and Jean-loup Gailly.
X Permission is granted to any individual or institution to use, copy, or
X redistribute this software so long as all of the original files are included
X unmodified, that it is not sold for profit, and that this copyright notice
X is retained.
X
X*/
X
X/*
X * zipsplit.c by Mark Adler.
X */
X
X#define UTIL
X#include "revision.h"
X#include "zip.h"
X#include <signal.h>
X
X#define DEFSIZ 36000L /* Default split size (change in help() too) */
X#ifdef MSDOS
X# define NL 2 /* Number of bytes written for a \n */
X#else /* !MSDOS */
X# define NL 1 /* Number of bytes written for a \n */
X#endif /* ?MSDOS */
X#define INDEX "zipsplit.idx" /* Name of index file */
X
X
X/* Local functions */
X#ifdef PROTO
X local void err(int, char *);
X local void handler(int);
X local void license(void);
X local void help(void);
X local extent simple(ulg *, extent, ulg, ulg);
X local int descmp(voidp *, voidp *);
X local extent greedy(ulg *, extent, ulg, ulg);
X void main(int, char **);
X#endif /* PROTO */
X
X
X/* Output zip files */
local char template[16]; /* name template for output files */
local int zipsmade = 0; /* number of zip files made */
local int indexmade = 0; /* true if index file made */
local char *path = NULL; /* space for full name */
local char *name; /* where name goes in path[] */
X
X
local void err(c, h)
int c; /* error code from the ZE_ class */
char *h; /* message about how it happened */
X/* Issue a message for the error, clean up files and memory, and exit. */
X{
X if (PERR(c))
X perror("zipsplit error");
X fprintf(stderr, "zipsplit error: %s (%s)\n", errors[c-1], h);
X if (indexmade)
X {
X strcpy(name, INDEX);
X destroy(path);
X }
X for (; zipsmade; zipsmade--)
X {
X sprintf(name, template, zipsmade);
X destroy(path);
X }
X if (path != NULL)
X free((voidp *)path);
X if (zipfile != NULL)
X free((voidp *)zipfile);
X#ifdef VMS
X exit(0);
X#else /* !VMS */
X exit(c);
X#endif /* ?VMS */
X}
X
X
X
local void handler(s)
int s; /* signal number (ignored) */
X/* Upon getting a user interrupt, abort cleanly using err(). */
X{
X#ifndef MSDOS
X putc('\n', stderr);
X#endif /* !MSDOS */
X err(ZE_ABORT, "aborting");
X s++; /* keep some compilers happy */
X}
X
X
void warn(a, b)
char *a, *b; /* message strings juxtaposed in output */
X/* Print a warning message to stderr and return. */
X{
X fprintf(stderr, "zipsplit warning: %s%s\n", a, b);
X}
X
X
local void license()
X/* Print license information to stdout. */
X{
X extent i; /* counter for copyright array */
X
X for (i = 0; i < sizeof(copyright)/sizeof(char *); i++)
X puts(copyright[i]);
X for (i = 0; i < sizeof(disclaimer)/sizeof(char *); i++)
X puts(disclaimer[i]);
X}
X
X
local void help()
X/* Print help (along with license info) to stdout. */
X{
X extent i; /* counter for help array */
X
X /* help array */
X static char *text[] = {
X"",
X"ZipSplit %d.%d (%s)",
X"Usage: zipsplit [-ti] [-n size] [-b path] zipfile",
X" -t report how many files it will take, but don't make them",
X" -i make index (zipsplit.idx) and count its size against first zip file",
X" -n make zip files no larger than \"size\" (default = 36000)",
X" -b use \"path\" for the output zip files",
X" -s do a sequential split even if it takes more zip files",
X" -h show this help -l show software license"
X };
X
X for (i = 0; i < sizeof(copyright)/sizeof(char *); i++)
X puts(copyright[i]);
X for (i = 0; i < sizeof(text)/sizeof(char *); i++)
X {
X printf(text[i], REVISION / 10, REVISION % 10, REVDATE);
X putchar('\n');
X }
X}
X
X
local extent simple(a, n, c, d)
ulg *a; /* items to put in bins, return value: destination bins */
extent n; /* number of items */
ulg c; /* capacity of each bin */
ulg d; /* amount to deduct from first bin */
X/* Return the number of bins of capacity c that are needed to contain the
X integers in a[0..n-1] placed sequentially into the bins. The value d
X is deducted initially from the first bin (space for index). The entries
X in a[] are replaced by the destination bins. */
X{
X extent k; /* current bin number */
X ulg t; /* space used in current bin */
X
X t = k = 0;
X while (n--)
X {
X if (*a + t > c - (k == 0 ? d : 0))
X {
X k++;
X t = 0;
X }
X t += *a;
X *(ulg huge *)a++ = k;
X }
X return k + 1;
X}
X
X
local int descmp(a, b)
voidp *a, *b; /* pointers to pointers to ulg's to compare */
X/* Used by qsort() in greedy() to do a descending sort. */
X{
X return **(ulg **)a < **(ulg **)b ? 1 : (**(ulg **)a > **(ulg **)b ? -1 : 0);
X}
X
X
local extent greedy(a, n, c, d)
ulg *a; /* items to put in bins, return value: destination bins */
extent n; /* number of items */
ulg c; /* capacity of each bin */
ulg d; /* amount to deduct from first bin */
X/* Return the number of bins of capacity c that are needed to contain the
X items with sizes a[0..n-1] placed non-sequentially into the bins. The
X value d is deducted initially from the first bin (space for index).
X The entries in a[] are replaced by the destination bins. */
X{
X ulg *b; /* space left in each bin (malloc'ed for each m) */
X ulg *e; /* copy of argument a[] (malloc'ed) */
X extent i; /* steps through items */
X extent j; /* steps through bins */
X extent k; /* best bin to put current item in */
X extent m; /* current number of bins */
X ulg **s; /* pointers to e[], sorted descending (malloc'ed) */
X ulg t; /* space left in best bin (index k) */
X
X /* Algorithm:
X 1. Copy a[] to e[] and sort pointers to e[0..n-1] (in s[]), in
X descending order.
X 2. Compute total of s[] and set m to the smallest number of bins of
X capacity c that can hold the total.
X 3. Allocate m bins.
X 4. For each item in s[], starting with the largest, put it in the
X bin with the smallest current capacity greater than or equal to the
X item's size. If no bin has enough room, increment m and go to step 4.
X 5. Else, all items ended up in a bin--return m.
X */
X
X /* Copy a[] to e[], put pointers to e[] in s[], and sort s[]. Also compute
X the initial number of bins (minus 1). */
X if ((e = (ulg *)malloc(n * sizeof(ulg))) == NULL ||
X (s = (ulg **)malloc(n * sizeof(ulg *))) == NULL)
X {
X if (e != NULL)
X free((voidp *)e);
X err(ZE_MEM, "was trying a smart split");
X return 0; /* only to make compiler happy */
X }
X memcpy((char *)e, (char *)a, n * sizeof(ulg));
X for (t = i = 0; i < n; i++)
X t += *(s[i] = e + i);
X m = (extent)((t + c - 1) / c) - 1; /* pre-decrement for loop */
X qsort((char *)s, n, sizeof(ulg *), descmp);
X
X /* Stuff bins until successful */
X do {
X /* Increment the number of bins, allocate and initialize bins */
X if ((b = (ulg *)malloc(++m * sizeof(ulg))) == NULL)
X {
X free((voidp *)s);
X free((voidp *)e);
X err(ZE_MEM, "was trying a smart split");
X }
X b[0] = c - d; /* leave space in first bin */
X for (j = 1; j < m; j++)
X b[j] = c;
X
X /* Fill the bins greedily */
X for (i = 0; i < n; i++)
X {
X /* Find smallest bin that will hold item i (size s[i]) */
X t = c + 1;
X for (k = j = 0; j < m; j++)
X if (*s[i] <= b[j] && b[j] < t)
X t = b[k = j];
X
X /* If no bins big enough for *s[i], try next m */
X if (t == c + 1)
X break;
X
X /* Diminish that bin and save where it goes */
X b[k] -= *s[i];
X a[(int)((ulg huge *)(s[i]) - (ulg huge *)e)] = k;
X }
X
X /* Clean up */
X free((voidp *)b);
X
X /* Do until all items put in a bin */
X } while (i < n);
X
X /* Done--clean up and return the number of bins needed */
X free((voidp *)s);
X free((voidp *)e);
X return m;
X}
X
X
void main(argc, argv)
int argc; /* number of tokens in command line */
char **argv; /* command line tokens */
X/* Split a zip file into several zip files less than a specified size. See
X the command help in help() above. */
X{
X ulg *a; /* malloc'ed list of sizes, dest bins */
X extent *b; /* heads of bin linked lists (malloc'ed) */
X ulg c; /* bin capacity, start of central directory */
X int d; /* if true, just report the number of disks */
X FILE *e; /* input zip file */
X FILE *f; /* output index and zip files */
X extent g; /* number of bins from greedy(), entry to write */
X int h; /* how to split--true means simple split, counter */
X ulg i; /* size of index file or zero if none */
X extent j; /* steps through zip entries, bins */
X int k; /* next argument type */
X ulg *p; /* malloc'ed list of sizes, dest bins for greedy() */
X char *q; /* steps through option characters */
X int r; /* temporary variable, counter */
X extent s; /* number of bins needed */
X ulg t; /* total of sizes, end of central directory */
X struct zlist far **w; /* malloc'ed table for zfiles linked list */
X int x; /* if true, make an index file */
X struct zlist far *z; /* steps through zfiles linked list */
X
X
X /* If no args, show help */
X if (argc == 1)
X {
X help();
X exit(0);
X }
X
X /* Go through args */
X signal(SIGINT, handler);
X signal(SIGTERM, handler);
X k = h = x = d = 0;
X c = DEFSIZ;
X for (r = 1; r < argc; r++)
X if (*argv[r] == '-')
X if (argv[r][1])
X for (q = argv[r]+1; *q; q++)
X switch(*q)
X {
X case 'b': /* Specify path for output files */
X if (k)
X err(ZE_PARMS, "options are separate and precede zip file");
X else
X k = 1; /* Next non-option is path */
X break;
X case 'h': /* Show help */
X help(); exit(0);
X case 'i': /* Make an index file */
X x = 1;
X break;
X case 'l': /* Show copyright and disclaimer */
X license(); exit(0);
X case 'n': /* Specify maximum size of resulting zip files */
X if (k)
X err(ZE_PARMS, "options are separate and precede zip file");
X else
X k = 2; /* Next non-option is size */
X break;
X case 's':
X h = 1; /* Only try simple */
X break;
X case 't': /* Just report number of disks */
X d = 1;
X break;
X default:
X err(ZE_PARMS, "unknown option");
X }
X else
X err(ZE_PARMS, "zip file cannot be stdin");
X else
X if (k == 0)
X if (zipfile == NULL)
X {
X if ((zipfile = ziptyp(argv[r])) == NULL)
X err(ZE_MEM, "was processing arguments");
X }
X else
X err(ZE_PARMS, "can only specify one zip file");
X else if (k == 1)
X {
X tempath = argv[r];
X k = 0;
X }
X else /* k must be 2 */
X {
X if ((c = (ulg)atol(argv[r])) < 100) /* 100 is smallest zip file */
X err(ZE_PARMS, "invalid size given");
X k = 0;
X }
X if (zipfile == NULL)
X err(ZE_PARMS, "need to specify zip file");
X
X
X /* Read zip file */
X if ((r = readzipfile()) != ZE_OK)
X err(r, zipfile);
X if (zfiles == NULL)
X err(ZE_NAME, zipfile);
X
X /* Make a list of sizes and check against capacity. Also compute the
X size of the index file. */
X c -= ENDHEAD + 4; /* subtract overhead/zipfile */
X if ((a = (ulg *)malloc(zcount * sizeof(ulg))) == NULL ||
X (w = (struct zlist far **)malloc(zcount * sizeof(struct zlist far *))) ==
X NULL)
X {
X if (a != NULL)
X free((voidp *)a);
X err(ZE_MEM, "was computing split");
X return;
X }
X i = t = 0;
X for (j = 0, z = zfiles; j < zcount; j++, z = z->nxt)
X {
X w[j] = z;
X if (x)
X i += z->nam + 6 + NL;
X t += a[j] = 8 + LOCHEAD + CENHEAD +
X 2 * (ulg)z->nam + 2 * (ulg)z->ext + z->com + z->siz;
X if (a[j] > c)
X {
X free((voidp *)w); free((voidp *)a);
X err(ZE_BIG, z->zname);
X }
X }
X
X /* Decide on split to use, report number of files */
X if (h)
X s = simple(a, zcount, c, i);
X else
X {
X if ((p = (ulg *)malloc(zcount * sizeof(ulg))) == NULL)
X {
X free((voidp *)w); free((voidp *)a);
X err(ZE_MEM, "was computing split");
X }
X memcpy((char *)p, (char *)a, zcount * sizeof(ulg));
X s = simple(a, zcount, c, i);
X g = greedy(p, zcount, c, i);
X if (s <= g)
X free((voidp *)p);
X else
X {
X free((voidp *)a);
X a = p;
X s = g;
X }
X }
X printf("%d zip files w%s be made (%d%% efficiency)\n",
X s, d ? "ould" : "ill", ((200 * ((t + c - 1)/c)) / s + 1) >> 1);
X if (d)
X {
X free((voidp *)w); free((voidp *)a);
X free((voidp *)zipfile);
X zipfile = NULL;
X return;
X }
X
X /* Set up path for output files */
X if ((path = malloc(tempath == NULL ? 13 : strlen(tempath) + 14)) == NULL)
X err(ZE_MEM, "was making output file names");
X if (tempath == NULL)
X name = path;
X else
X {
X strcpy(path, tempath);
X if (path[0] && path[strlen(path) - 1] != '/')
X strcat(path, "/");
X name = path + strlen(path);
X }
X
X /* Write the index file */
X if (x)
X {
X strcpy(name, INDEX);
X printf("creating %s\n", path);
X indexmade = 1;
X if ((f = fopen(path, "w")) == NULL)
X {
X free((voidp *)w); free((voidp *)a);
X err(ZE_CREAT, path);
X }
X for (j = 0; j < zcount; j++)
X fprintf(f, "%5ld %s\n", a[j] + 1, w[j]->zname);
X if ((j = ferror(f)) != 0 || fclose(f))
X {
X if (j)
X fclose(f);
X free((voidp *)w); free((voidp *)a);
X err(ZE_WRITE, path);
X }
X }
X
X /* Make linked lists of results */
X if ((b = (extent *)malloc(s * sizeof(extent))) == NULL)
X {
X free((voidp *)w); free((voidp *)a);
X err(ZE_MEM, "was computing split");
X }
X for (j = 0; j < s; j++)
X b[j] = -1;
X j = zcount;
X while (j--)
X {
X g = (extent)a[j];
X a[j] = b[g];
X b[g] = j;
X }
X
X /* Make a name template for the zip files that is eight or less characters
X before the .zip, and that will not overwrite the original zip file. */
X for (k = 1, j = s; j >= 10; j /= 10)
X k++;
X if (k > 7)
X {
X free((voidp *)b); free((voidp *)w); free((voidp *)a);
X err(ZE_PARMS, "way too many zip files must be made");
X }
X if ((q = strrchr(zipfile, '/')) != NULL)
X q++;
X else
X q = zipfile;
X r = 0;
X while ((g = *q++) != 0 && g != '.' && r < 8 - k)
X template[r++] = (char)g;
X if (r == 0)
X template[r++] = '_';
X else if (g >= '0' && g <= '9')
X template[r - 1] = (char)(template[r - 1] == '_' ? '-' : '_');
X sprintf(template + r, "%%0%dd.zip", k);
X
X /* Make the zip files from the linked lists of entry numbers */
X if ((e = fopen(zipfile, FOPR)) == NULL)
X {
X free((voidp *)b); free((voidp *)w); free((voidp *)a);
X err(ZE_NAME, zipfile);
X }
X free((voidp *)zipfile);
X zipfile = NULL;
X for (j = 0; j < s; j++)
X {
X sprintf(name, template, j + 1);
X printf("creating %s\n", path);
X zipsmade = j + 1;
X if ((f = fopen(path, FOPW)) == NULL)
X {
X free((voidp *)b); free((voidp *)w); free((voidp *)a);
X err(ZE_CREAT, path);
X }
X for (g = b[j]; g != (extent)-1; g = (extent)a[g])
X {
X if (fseek(e, w[g]->off, SEEK_SET))
X {
X free((voidp *)b); free((voidp *)w); free((voidp *)a);
X err(ferror(e) ? ZE_READ : ZE_EOF, zipfile);
X }
X if ((r = zipcopy(w[g], e, f)) != ZE_OK)
X {
X free((voidp *)b); free((voidp *)w); free((voidp *)a);
X if (r == ZE_TEMP)
X err(ZE_WRITE, path);
X else
X err(r, zipfile);
X }
X }
X if ((c = ftell(f)) == -1L)
X {
X free((voidp *)b); free((voidp *)w); free((voidp *)a);
X err(ZE_WRITE, path);
X }
X for (g = b[j], k = 0; g != (extent)-1; g = (extent)a[g], k++)
X if ((r = putcentral(w[g], f)) != ZE_OK)
X {
X free((voidp *)b); free((voidp *)w); free((voidp *)a);
X err(ZE_WRITE, path);
X }
X if ((t = ftell(f)) == -1L)
X {
X free((voidp *)b); free((voidp *)w); free((voidp *)a);
X err(ZE_WRITE, path);
X }
X if ((r = putend(k, t - c, c, (extent)0, (char *)NULL, f)) != ZE_OK)
X {
X free((voidp *)b); free((voidp *)w); free((voidp *)a);
X err(ZE_WRITE, path);
X }
X if (ferror(f) || fclose(f))
X {
X free((voidp *)b); free((voidp *)w); free((voidp *)a);
X err(ZE_WRITE, path);
X }
X }
X free((voidp *)b); free((voidp *)w); free((voidp *)a);
X fclose(e);
X
X /* Done! */
X exit(0);
X}
END_OF_FILE
if test 16863 -ne `wc -c <'zipsplit.c'`; then
echo shar: \"'zipsplit.c'\" unpacked with wrong size!
fi
# end of 'zipsplit.c'
fi
echo shar: End of archive 3 \(of 7\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 7 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 7 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0

0 new messages