2848 lines
113 KiB
C
2848 lines
113 KiB
C
/****************************************************************************
|
|
**
|
|
*A unzoo.c Tools Martin Schoenert
|
|
**
|
|
*H @(#)$Id: unzoo.c,v 4.4 2000/05/29 08:56:57 sal Exp $
|
|
**
|
|
*Y This file is in the Public Domain.
|
|
**
|
|
** SYNTAX
|
|
**
|
|
** 'unzoo'
|
|
** 'unzoo [-l] [-v] <archive>[.zoo] [<file>..]'
|
|
** 'unzoo -x [-abnpo] <archive>[.zoo] [<file>..]'
|
|
**
|
|
** 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 <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 <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 <lf> as line separator as under UNIX) to
|
|
** the local text file format (e.g., with <cr>/<lf> 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
|
|
** '<file> 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 <prefix>' suboption causes 'unzoo' to prepend the string <prefix>
|
|
** 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 <prefix> must exist,
|
|
** 'unzoo' will not create it on demand.
|
|
**
|
|
** If no <files> argument is given all members are listed or extracted.
|
|
** If one or more <files> arguments are given, only members whose names
|
|
** match at least one of the <files> patterns are listed or extracted.
|
|
** <files> can contain the wildcard '?', which matches any character in
|
|
** names, and '*', which matches any number of characters in names. When
|
|
** you pass the <files> 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 ';<nr>' to a path name pattern the generation
|
|
** with the number <nr> is listed or extracted. <nr> 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'.
|
|
**
|
|
** COMPILATION
|
|
**
|
|
** Under UNIX with the standard C compiler, compile 'unzoo' as follows
|
|
** cc -o unzoo -DSYS_IS_UNIX -O unzoo.c
|
|
** If your UNIX has the 'mkdir' system call, you may add '-DSYS_HAS_MKDIR'
|
|
** for a slightly faster executable. BSD has it, else try 'man 2 mkdir'.
|
|
**
|
|
** Under DOS with the DJGPP GNU C compiler, compile 'unzoo' as follows
|
|
** gcc -o unzoo.out -DSYS_IS_DOS_DJGPP -O2 unzoo.c
|
|
** copy /b \djgpp\bin\go32.exe+unzoo.out unzoo.exe
|
|
**
|
|
** Under TOS with the GNU compiler and unixmode, compile 'unzoo' as follows
|
|
** gcc -o unzoo.ttp -DSYS_IS_TOS_GCC -O2 unzoo.c
|
|
**
|
|
** Under OS/2 2 with the emx development system, compile 'unzoo' as follows
|
|
** gcc -o unzoo.exe -DSYS_IS_OS2_EMX -Zomf -Zsys -O2 unzoo.c
|
|
** To create an executable that runs under OS/2 and DOS, but which requires
|
|
** the emx runtime, compile without the '-Zomf' and '-Zsys' options.
|
|
**
|
|
** On a VAX running VMS with the DEC C compiler, compile 'unzoo' as follows
|
|
** cc unzoo/define=SYS_IS_VMS
|
|
** link unzoo
|
|
** Then perform the following global symbolic assignment
|
|
** unzoo :== $<dev>:[<dir>]unzoo.exe
|
|
** where <dir> is the name of the directory where you have installed
|
|
** 'unzoo' and <dev> is the device on which this directory is, for example
|
|
** unzoo :== $dia1:[progs.archivers]unzoo
|
|
** You may want to put this symbolic assignment into your 'login.com' file.
|
|
**
|
|
** On a Macintosh with the MPW C compiler, compile 'unzoo' as follows
|
|
** C -model far -d SYS_IS_MAC_MPW -opt on unzoo.c
|
|
** Link -model far -d -c '????' -t APPL unzoo.c.o -o unzoo <continued>
|
|
** "{CLibraries}"StdClib.o "{Libraries}"SIOW.o <continued>
|
|
** "{Libraries}"Runtime.o "{Libraries}"Interface.o
|
|
** Rez -a "{RIncludes}"SIOW.r -o unzoo
|
|
** Afterwards choose the 'Get Info' command in the finder 'File' menu and
|
|
** increase the amount of memory 'unzoo' gets upon startup to 256 KBytes.
|
|
** To create a MPW tool instead of a standalone, link with creator 'MPS '
|
|
** instead of '????', with type 'MPST' instead of 'APPL' and with 'Stubs.o'
|
|
** instead of 'SIOW.o'. The 'Rez' command is not required for the tool.
|
|
** Alternatively choose the 'Create Build Commands...' command from the MPW
|
|
** 'Build' menu to create a makefile. Edit it and add '-d SYS_IS_MAC_MPW'
|
|
** to the compile command. Choose the 'Build...' command from the 'Build'
|
|
** menu to build 'unzoo'.
|
|
**
|
|
** On other systems with a C compiler, try to compile 'unzoo' as follows
|
|
** cc -o unzoo -DSYS_IS_GENERIC -O unzoo.c
|
|
**
|
|
** PORTING
|
|
**
|
|
** If this does not work, you must supply new definitions for the macros
|
|
** 'OPEN_READ_ARCH', 'OPEN_READ_TEXT' and 'OPEN_WRIT_TEXT'. If you want
|
|
** 'unzoo' to keep long file names, you must supply a definition for the
|
|
** macro 'CONV_NAME'. If you want 'unzoo' to extract into subdirectories,
|
|
** you must supply a definition for the macro 'CONV_DIRE'. If you want
|
|
** 'unzoo' to automatically create directories, you must supply a definition
|
|
** for the macro 'MAKE_DIR'. If you want 'unzoo' to set the permissions of
|
|
** extracted members to those recorded in the archive, you must supply a
|
|
** definition for the macro 'SETF_PERM'. Finally if you want 'unzoo' to set
|
|
** the times of the extracted members to the times recorded in the archive,
|
|
** you must supply a definition for the macro 'SETF_TIME'. Everything else
|
|
** should be system independent.
|
|
**
|
|
** 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.
|
|
**
|
|
** HISTORY
|
|
*H $Log: unzoo.c,v $
|
|
*H Revision 4.4 2000/05/29 08:56:57 sal
|
|
*H Remove all the \ continuation lines -- who needs the hassle. SL
|
|
*H
|
|
*H Revision 4.3 1999/10/27 08:51:11 sal
|
|
*H Fix date problem on alphas (I hope) SL
|
|
*H
|
|
*H Revision 4.2 1999/05/26 09:27:03 gap
|
|
*H burkhard: use fseek to access file comments; Mac version: several minor fixes
|
|
*H
|
|
*H Revision 1.5 1994/01/21 13:32:32 mschoene
|
|
*H added Mac support from Dave Bayer
|
|
*H
|
|
*H Revision 1.4 1994/01/20 20:45:46 mschoene
|
|
*H cleaned up determination of write mode
|
|
*H
|
|
*H Revision 1.3 1993/12/02 12:43:12 mschoene
|
|
*H added OS/2 support from Harald Boegeholz
|
|
*H
|
|
*H Revision 1.2 1993/12/02 12:33:39 mschoene
|
|
*H fixed several typos, renamed MS-DOS to DOS
|
|
*H
|
|
*H Revision 1.1 1993/11/09 07:17:50 mschoene
|
|
*H Initial revision
|
|
*H
|
|
*/
|
|
#include <stdio.h>
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F OPEN_READ_ARCH(<patl>) . . . . . . . . . . . open an archive for reading
|
|
*F CLOS_READ_ARCH() . . . . . . . . . . . . . . . . . . . close an archive
|
|
*F BLCK_READ_ARCH(<blk>,<len>) . . . . . . . . read a block from an archive
|
|
*F RWND_READ_ARCH() . . . . . . . . . . . reset file read position to start
|
|
*F SEEK_READ_ARCH(pos) . . . . . . . . . . . . . . move read position to pos
|
|
**
|
|
** 'OPEN_READ_ARCH' returns 1 if the archive file with the path name <patl>
|
|
** (as specified by the user on the command line) could be opened for
|
|
** reading and 0 otherwise. Because archive files are binary files,
|
|
** 'OPEN_READ_ARCH' must open the file in binary mode.
|
|
**
|
|
** 'CLOS_READ_ARCH' closes the archive file opened by 'OPEN_READ_ARCH'
|
|
** again.
|
|
**
|
|
** 'BLCK_READ_ARCH' reads up to <len> characters from the archive file
|
|
** opened with 'OPEN_READ_ARCH' into the blkfer <blk>, and returns the
|
|
** actual number of characters read.
|
|
**
|
|
** This operation is operating system dependent because the archive file
|
|
** must be opened in binary mode, so that for example no <cr>/<lf> <-> <lf>
|
|
** translation happens. You must supply a definition for each new port.
|
|
*/
|
|
#ifdef SYS_IS_UNIX
|
|
FILE * ReadArch;
|
|
#define OPEN_READ_ARCH(patl) ((ReadArch = fopen( (patl), "r" )) != 0)
|
|
#define CLOS_READ_ARCH() (fclose( ReadArch ) == 0)
|
|
#define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
|
|
#define RWND_READ_ARCH() (fseek( ReadArch, 0, 0 ) == 0)
|
|
#define SEEK_READ_ARCH(pos) (fseek( ReadArch, pos, SEEK_SET) == 0)
|
|
#endif
|
|
#ifdef SYS_IS_DOS_DJGPP
|
|
FILE * ReadArch;
|
|
#define OPEN_READ_ARCH(patl) ((ReadArch = fopen( (patl), "rb" )) != 0)
|
|
#define CLOS_READ_ARCH() (fclose( ReadArch ) == 0)
|
|
#define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
|
|
#define RWND_READ_ARCH() (fseek( ReadArch, 0, 0 ) == 0)
|
|
#endif
|
|
#ifdef SYS_IS_OS2_EMX
|
|
FILE * ReadArch;
|
|
#define OPEN_READ_ARCH(patl) ((ReadArch = fopen( (patl), "rb" )) != 0)
|
|
#define CLOS_READ_ARCH() (fclose( ReadArch ) == 0)
|
|
#define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
|
|
#define RWND_READ_ARCH() (fseek( ReadArch, 0, 0 ) == 0)
|
|
#endif
|
|
#ifdef SYS_IS_TOS_GCC
|
|
FILE * ReadArch;
|
|
#define OPEN_READ_ARCH(patl) ((ReadArch = fopen( (patl), "rb" )) != 0)
|
|
#define CLOS_READ_ARCH() (fclose( ReadArch ) == 0)
|
|
#define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
|
|
#define RWND_READ_ARCH() (fseek( ReadArch, 0, 0 ) == 0)
|
|
#endif
|
|
#ifdef SYS_IS_VMS
|
|
FILE * ReadArch;
|
|
#define OPEN_READ_ARCH(patl) ((ReadArch = fopen( (patl), "r" )) != 0)
|
|
#define CLOS_READ_ARCH() (fclose( ReadArch ) == 0)
|
|
#define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
|
|
#define RWND_READ_ARCH() (fseek( ReadArch, 0, 0 ) == 0)
|
|
#endif
|
|
#ifdef SYS_IS_MAC_MPW
|
|
FILE * ReadArch;
|
|
#define OPEN_READ_ARCH(patl) ((ReadArch = fopen( (patl), "r") ) != 0)
|
|
#define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
|
|
#define CLOS_READ_ARCH() (fclose( ReadArch ) == 0)
|
|
#define RWND_READ_ARCH() (fseek( ReadArch, 0, 0 ) == 0)
|
|
#endif
|
|
#ifdef SYS_IS_MAC_THC
|
|
#include <SIOUX.h>
|
|
FILE * ReadArch;
|
|
#define OPEN_READ_ARCH(patl) ((ReadArch = fopen( (patl), "rb") ) != 0)
|
|
#define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
|
|
#define CLOS_READ_ARCH() (fclose( ReadArch ) == 0)
|
|
#define RWND_READ_ARCH() (fseek( ReadArch, 0, SEEK_SET ) == 0)
|
|
#define SEEK_READ_ARCH(pos) (fseek( ReadArch, pos, SEEK_SET) == 0)
|
|
#define SYS_IS_MAC_MPW /* BH: use most parts of MPW port */
|
|
#endif
|
|
#ifdef SYS_IS_GENERIC
|
|
FILE * ReadArch;
|
|
#define OPEN_READ_ARCH(patl) ((ReadArch = fopen( (patl), "r" )) != 0)
|
|
#define CLOS_READ_ARCH() (fclose( ReadArch ) == 0)
|
|
#define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
|
|
#define RWND_READ_ARCH() (fseek( ReadArch, 0, 0 ) == 0)
|
|
#define SEEK_READ_ARCH(pos) (fseek( ReadArch, pos, SEEK_SET) == 0)
|
|
#endif
|
|
#ifndef OPEN_READ_ARCH
|
|
#include "You_must_specify_the_system.h"
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F OPEN_READ_TEXT(<patl>) . . . . . . . . . . . . . open a file for reading
|
|
*F CLOS_READ_TEXT() . . . . . . . . . . . . . . . . . . . . . close a file
|
|
*F BLCK_READ_TEXT(<blk>,<len>) . . . . . . . . . . read a block from a file
|
|
**
|
|
** 'OPEN_READ_TEXT' returns 1 if the file with the path name <patl> (as
|
|
** specified by the user on the command line) could be opened for reading
|
|
** and 0 otherwise. 'OPEN_READ_TEXT' is used only for text files, so it
|
|
** should open the file in text mode.
|
|
**
|
|
** 'CLOS_READ_TEXT' closes the file opened by 'OPEN_READ_TEXT' again.
|
|
**
|
|
** 'BLCK_READ_TEXT' reads up to <len> characters from the file opened with
|
|
** 'OPEN_READ_TEXT' into the blkfer <blk>, and returns the actual number of
|
|
** characters read.
|
|
**
|
|
** In 'unzoo' these functions are only used to test if a file exists.
|
|
**
|
|
** This operation is operating system dependent because it may be neccessary
|
|
** to translate between the local text format and the UNIX style text format
|
|
** usually used in archives. The default is to use 'fopen', 'fread', and
|
|
** 'fclose', which should work everywhere according to the ANSI standard.
|
|
** You may want to use 'open', 'read', and 'close' for better performance.
|
|
*/
|
|
#ifndef OPEN_READ_TEXT
|
|
FILE * ReadText;
|
|
#define OPEN_READ_TEXT(patl) ((ReadText = fopen( (patl), "r" )) != 0)
|
|
#define CLOS_READ_TEXT() (fclose( ReadText ) == 0)
|
|
#define BLCK_READ_TEXT(blk,len) fread( (blk), 1L, (len), ReadText )
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F OPEN_WRIT_TEXT(<patl>) . . . . . . . . . . . . . open a file for writing
|
|
*F CLOS_WRIT_TEXT() . . . . . . . . . . . . . . . . . . . . . close a file
|
|
*F BLCK_WRIT_TEXT(<blk>,<len>) . . . . . . . . . . . write a block to a file
|
|
**
|
|
** 'OPEN_WRIT_TEXT' returns 1 if the file with the path name <patl> (as
|
|
** specified by the user on the command line) could be opened for writing
|
|
** and 0 otherwise. 'OPEN_WRIT_TEXT' is used only for text files, so it
|
|
** must open the file in text mode.
|
|
**
|
|
** 'CLOS_WRIT_TEXT' closes the file opened by 'OPEN_WRIT_TEXT' again.
|
|
**
|
|
** 'BLCK_WRIT_TEXT' writes up to <len> characters from <blk> into the file
|
|
** opened with 'OPEN_WRIT_TEXT', and returns the actual number of characters
|
|
** written.
|
|
**
|
|
** This operation is operating system dependent because it may be neccessary
|
|
** to translate between the UNIX style text format usually used in archives
|
|
** and the local text format. The default is to use 'fopen', 'fwrite', and
|
|
** 'fclose', which should work everywhere according to the ANSI standard.
|
|
** You may want to use 'open', 'write', and 'close' for better performance.
|
|
*/
|
|
#ifdef SYS_IS_MAC_MPW
|
|
#ifndef SYS_IS_MAC_THC
|
|
FILE * WritText;
|
|
#define OPEN_WRIT_TEXT(patl) MacOpenWritText( (patl) )
|
|
#define CLOS_WRIT_TEXT() MacClosWritText()
|
|
#define BLCK_WRIT_TEXT(blk,len) MacBlckWritText( (blk), (len) )
|
|
#else
|
|
FILE * WritText;
|
|
#define OPEN_WRIT_TEXT(patl) MacOpenWritText( (patl) )
|
|
#define CLOS_WRIT_TEXT() (fclose( WritText ) == 0)
|
|
#define BLCK_WRIT_TEXT(blk,len) fwrite( (blk), 1L, (len), WritText )
|
|
#endif
|
|
#endif
|
|
#ifndef OPEN_WRIT_TEXT
|
|
FILE * WritText;
|
|
#define OPEN_WRIT_TEXT(patl) ((WritText = fopen( (patl), "w" )) != 0)
|
|
#define CLOS_WRIT_TEXT() (fclose( WritText ) == 0)
|
|
#define BLCK_WRIT_TEXT(blk,len) fwrite( (blk), 1L, (len), WritText )
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F OPEN_READ_BINR(<patl>) . . . . . . . . . . . . . open a file for reading
|
|
*F CLOS_READ_BINR() . . . . . . . . . . . . . . . . . . . . . close a file
|
|
*F BLCK_READ_BINR(<blk>,<len>) . . . . . . . . . . read a block from a file
|
|
**
|
|
** 'OPEN_READ_BINR' returns 1 if the file with the path name <patl> (as
|
|
** specified by the user on the command line) could be opened for reading
|
|
** and 0 otherwise. 'OPEN_READ_BINR' is used only for binary files, so it
|
|
** should open the file in binary mode.
|
|
**
|
|
** 'CLOS_READ_BINR' closes the file opened by 'OPEN_READ_BINR' again.
|
|
**
|
|
** 'BLCK_READ_BINR' reads up to <len> characters from the file opened with
|
|
** 'OPEN_READ_BINR' into the blkfer <blk>, and returns the actual number of
|
|
** characters read.
|
|
**
|
|
** In 'unzoo' these functions are currently not used at all.
|
|
**
|
|
** This operation is operating system dependent because the file must be
|
|
** opened in binary mode, so that for example no <cr>/<lf> <-> <lf>
|
|
** translation happens. The default is to use 'fopen' with mode 'rb',
|
|
** 'fwrite', and 'fclose', with should work on most systems.
|
|
*/
|
|
#ifndef OPEN_READ_BINR
|
|
FILE * ReadBinr;
|
|
#define OPEN_READ_BINR(patl) ((ReadBinr = fopen( (patl), "rb" )) != 0)
|
|
#define CLOS_READ_BINR() (fclose( ReadBinr ) == 0)
|
|
#define BLCK_READ_BINR(blk,len) fread( (blk), 1L, (len), ReadBinr )
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F OPEN_WRIT_BINR(<patl>) . . . . . . . . . . . . . open a file for writing
|
|
*F CLOS_WRIT_BINR() . . . . . . . . . . . . . . . . . . . . . close a file
|
|
*F BLCK_WRIT_BINR(<blk>,<len>) . . . . . . . . . . . write a block to a file
|
|
**
|
|
** 'OPEN_WRIT_BINR' returns 1 if the file with the path name <patl> (as
|
|
** specified by the user on the command line) could be opened for writing
|
|
** and 0 otherwise. 'OPEN_WRIT_BINR' is used only for binary files, so it
|
|
** must open the file in binary mode.
|
|
**
|
|
** 'CLOS_WRIT_BINR' closes the file opened by 'OPEN_WRIT_BINR' again.
|
|
**
|
|
** 'BLCK_WRIT_BINR' writes up to <len> characters from <blk> into the file
|
|
** opened with 'OPEN_WRIT_BINR', and returns the actual number of characters
|
|
** written.
|
|
**
|
|
** This operation is operating system dependent because the file must be
|
|
** opened in binary mode, so that for example no <cr>/<lf> <-> <lf>
|
|
** translation happens. The default is to use 'fopen' with mode 'wb',
|
|
** 'fwrite', and 'fclose', with should work on most systems. You must
|
|
** supply a definition is this does not work and you want 'unzoo' to extract
|
|
** binary files.
|
|
*/
|
|
#ifdef SYS_IS_VMS
|
|
#include <file.h>
|
|
long WritBinr;
|
|
#define OPEN_WRIT_BINR(patl) ((WritBinr = creat( (patl), 0, "rfm=fix", "mrs=512" )) != -1)
|
|
#define BLCK_WRIT_BINR(blk,len) VmsBlckWritBinr( WritBinr, (blk), (len) )
|
|
#define CLOS_WRIT_BINR() (close( WritBinr ) == 0)
|
|
#endif
|
|
#ifdef SYS_IS_MAC_THC
|
|
FILE * WritBinr;
|
|
#define OPEN_WRIT_BINR(patl) MacOpenWritBinr (patl)
|
|
#define BLCK_WRIT_BINR(blk,len) fwrite( (blk), 1L, (len), WritBinr )
|
|
#define CLOS_WRIT_BINR() (fclose( WritBinr ) == 0)
|
|
#endif
|
|
#ifndef OPEN_WRIT_BINR
|
|
FILE * WritBinr;
|
|
#define OPEN_WRIT_BINR(patl) ((WritBinr = fopen( (patl), "wb" )) != 0)
|
|
#define BLCK_WRIT_BINR(blk,len) fwrite( (blk), 1L, (len), WritBinr )
|
|
#define CLOS_WRIT_BINR() (fclose( WritBinr ) == 0)
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F CONV_NAME(<naml>,<namu>) . . . . . . . . . . . . . . convert a file name
|
|
**
|
|
** 'CONV_NAME' returns in <naml> the universal file name <namu> converted
|
|
** to the local format. <namu> may contain uppercase, lowercase, and all
|
|
** special characters, and may be up to 255 characters long.
|
|
**
|
|
** You must define this for a new port if you want 'unzoo' to keep the long
|
|
** names instead of using the default local format for the file names, which
|
|
** contains up to eight lowercase characters before an optional dot ('.'),
|
|
** up to three characters after the dot, and no special characters. You may
|
|
** want to use the universal conversion function 'ConvName'.
|
|
*/
|
|
#ifdef SYS_IS_UNIX
|
|
#define CONV_NAME(naml,namu) strcpy( (naml), (namu) )
|
|
#endif
|
|
#ifdef SYS_IS_DOS_DJGPP
|
|
#define CONV_NAME(naml,namu) ConvName( (naml), (namu), 8L, 3L, '_' )
|
|
#endif
|
|
#ifdef SYS_IS_OS2_EMX
|
|
#define CONV_NAME(naml,namu) strcpy( (naml), (namu) )
|
|
#endif
|
|
#ifdef SYS_IS_TOS_GCC
|
|
#define CONV_NAME(naml,namu) strcpy( (naml), (namu) )
|
|
#endif
|
|
#ifdef SYS_IS_VMS
|
|
#define CONV_NAME(naml,namu) ConvName( (naml), (namu), 39L, 39L, '_' )
|
|
#endif
|
|
#ifdef SYS_IS_MAC_MPW
|
|
#define CONV_NAME(naml,namu) strncpy( (naml), (namu), 31 ); naml[32] = '\0'
|
|
#endif
|
|
#ifndef CONV_NAME
|
|
#define CONV_NAME(naml,namu) ConvName( (naml), (namu), 8L, 3L, 'x' )
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F CONV_DIRE(<dirl>,<diru>) . . . . . . . . . . . convert a directory name
|
|
**
|
|
** 'CONV_DIRE' returns in <dirl> the universal directory name <diru>
|
|
** converted to the local format. <diru> contains an arbitrary number of
|
|
** components separated by slashes ('/'), where each component may contain
|
|
** uppercase, lowercase, and all special characters, and may be up to 255
|
|
** characters long.
|
|
**
|
|
** You must define this for a new port if you want 'unzoo' to extract
|
|
** members into subdirectories, instead of extracting them to the current
|
|
** directory. You may want to use the universal conversion function
|
|
** 'ConvDire'.
|
|
*/
|
|
#ifdef SYS_IS_UNIX
|
|
#define CONV_DIRE(dirl,diru) ConvDire((dirl),(diru),"/","/","","/","/")
|
|
#endif
|
|
#ifdef SYS_IS_DOS_DJGPP
|
|
#define CONV_DIRE(dirl,diru) ConvDire((dirl),(diru),"\\","\\","","\\","\\")
|
|
#endif
|
|
#ifdef SYS_IS_OS2_EMX
|
|
#define CONV_DIRE(dirl,diru) ConvDire((dirl),(diru),"/","/","","/","/")
|
|
#endif
|
|
#ifdef SYS_IS_TOS_GCC
|
|
#define CONV_DIRE(dirl,diru) ConvDire((dirl),(diru),"\\","\\","","\\","\\")
|
|
#endif
|
|
#ifdef SYS_IS_VMS
|
|
#define CONV_DIRE(dirl,diru) ConvDire((dirl),(diru),"[]","[","[.",".","]")
|
|
#endif
|
|
#ifdef SYS_IS_MAC_MPW
|
|
#define CONV_DIRE(dirl,diru) ConvDire((dirl),(diru),"","",":",":",":")
|
|
#endif
|
|
#ifndef CONV_DIRE
|
|
#define CONV_DIRE(dirl,diru) ((dirl)[0]='\0',1)
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F MAKE_DIRE(<patl>) . . . . . . . . . . . . . . . . . . . make a directory
|
|
**
|
|
** 'MAKE_DIRE' makes the directory with the local path name <patl> (as
|
|
** converted by 'CONV_NAME' and 'CONV_DIRE' with the prefix of 'MakeDirs').
|
|
**
|
|
** You must define this for a new port if you want 'unzoo' to automatically
|
|
** create directories instead of requiring the user to create them.
|
|
*/
|
|
#ifdef SYS_IS_UNIX
|
|
#ifdef SYS_HAS_MKDIR
|
|
#define MAKE_DIRE(patl) mkdir( (patl), 0777L )
|
|
#else
|
|
char Cmd [256];
|
|
#define MAKE_DIRE(patl) (sprintf(Cmd,"/bin/mkdir %s",(patl)),!system(Cmd))
|
|
#endif
|
|
#endif
|
|
#ifdef SYS_IS_DOS_DJGPP
|
|
#define MAKE_DIRE(patl) mkdir( (patl), 0777L )
|
|
#endif
|
|
#ifdef SYS_IS_OS2_EMX
|
|
#include <stdlib.h>
|
|
#define MAKE_DIRE(patl) mkdir( (patl), 0777L )
|
|
#endif
|
|
#ifdef SYS_IS_TOS_GCC
|
|
#define MAKE_DIRE(patl) mkdir( (patl), 0777L )
|
|
#endif
|
|
#ifdef SYS_IS_VMS
|
|
#define MAKE_DIRE(patl) VmsMakeDire( (patl) )
|
|
#endif
|
|
#ifdef SYS_IS_MAC_MPW
|
|
#define MAKE_DIRE(patl) MacMakeDire( (patl) )
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F SETF_TIME(<patl>,<secs>) . . . . . . . . . . . change the time of a file
|
|
**
|
|
** 'SETF_TIME' changes the time of the file with the local path name <patl>
|
|
** (as converted by 'CONV_NAME' and 'CONV_DIRE') to <secs>, which is the
|
|
** number of seconds since 1970/01/01 00:00:00.
|
|
**
|
|
** You must define this for a new port if you want 'unzoo' to extract
|
|
** 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))
|
|
#endif
|
|
#ifdef SYS_IS_DOS_DJGPP
|
|
unsigned long Secs [2];
|
|
#define SETF_TIME(patl,secs) (Secs[0]=Secs[1]=(secs),!utime((patl),Secs))
|
|
#endif
|
|
#ifdef SYS_IS_OS2_EMX
|
|
#include <sys/utime.h>
|
|
struct utimbuf Secs;
|
|
#define SETF_TIME(patl,secs) (Secs.actime=Secs.modtime=(secs),!utime((patl),&Secs))
|
|
#endif
|
|
#ifdef SYS_IS_TOS_GCC
|
|
unsigned long Secs [2];
|
|
#define SETF_TIME(patl,secs) (Secs[0]=Secs[1]=(secs),!utime((patl),Secs))
|
|
#endif
|
|
#ifndef SETF_TIME
|
|
#define SETF_TIME(patl,secs) (1)
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F SETF_PERM(<patl>,<mode>) . . . . . . . change the permissions of a file
|
|
**
|
|
** 'SETF_PERM' changes the permissions of the file with the local path name
|
|
** <patl> (as converted by 'CONV_NAME' and 'CONV_DIRE') to <mode>, which is
|
|
** a UNIX style mode word.
|
|
**
|
|
** You must define this for a new port if you want 'unzoo' to extract
|
|
** members with the permissions stored in the archive.
|
|
*/
|
|
#ifdef SYS_IS_UNIX
|
|
#define SETF_PERM(patl,mode) (!chmod((patl),(int)(mode)))
|
|
#endif
|
|
#ifdef SYS_IS_DOS_DJGPP
|
|
#define SETF_PERM(patl,mode) (!chmod((patl),(int)(mode)))
|
|
#endif
|
|
#ifdef SYS_IS_OS2_EMX
|
|
#include <io.h>
|
|
#define SETF_PERM(patl,mode) (!chmod((patl),(int)(mode)))
|
|
#endif
|
|
#ifdef SYS_IS_TOS_GCC
|
|
#define SETF_PERM(patl,mode) (!chmod((patl),(int)(mode)))
|
|
#endif
|
|
#ifndef SETF_PERM
|
|
#define SETF_PERM(patl,mode) (1)
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F ConvName(...) . . . . . . . . . . . . convert a file name to local format
|
|
**
|
|
** 'ConvName( <naml>, <namu>, <pre>,<pst>,<rpl> )'
|
|
**
|
|
** 'ConvName' returns in <naml> the universal file name <namu> converted to
|
|
** the local format described by <pre>, <pst>, and <rpl>.
|
|
**
|
|
** <pre> is the maximum number of characters before the optional dot, <pst>
|
|
** is the maximum number of characters after the optional dot, and <rpl> is
|
|
** the character that replaces special characters.
|
|
*/
|
|
int ConvName ( naml, namu, pre, pst, rpl )
|
|
char * naml;
|
|
char * namu;
|
|
unsigned long pre;
|
|
unsigned long pst;
|
|
char rpl;
|
|
{
|
|
char * dotu; /* position of last dot in <namu> */
|
|
char * l; /* loop variable */
|
|
char * u; /* loop variable */
|
|
|
|
/* find the final dot */
|
|
dotu = 0;
|
|
for ( u = namu; *u != '\0'; u++ )
|
|
if ( *u == '.' )
|
|
dotu = u;
|
|
if ( dotu == 0 ) dotu = u;
|
|
|
|
/* copy the first part */
|
|
l = naml;
|
|
for ( u = namu; u < dotu && u < namu+pre; u++ ) {
|
|
if ( 'a' <= *u && *u <= 'z' ) *l++ = *u;
|
|
else if ( 'A' <= *u && *u <= 'Z' ) *l++ = *u - 'A' + 'a';
|
|
else if ( '0' <= *u && *u <= '9' ) *l++ = *u;
|
|
else *l++ = rpl;
|
|
}
|
|
|
|
/* the part before the dot may not be empty */
|
|
if ( l == naml )
|
|
*l++ = rpl;
|
|
|
|
/* if the universal file name had no dot, thats it */
|
|
if ( *dotu == '\0' || pst == 0 ) {
|
|
*l = '\0';
|
|
return 1;
|
|
}
|
|
|
|
/* copy the dot */
|
|
*l++ = '.';
|
|
|
|
/* copy the remaining part */
|
|
for ( u = dotu+1; *u && u < dotu+1+pst; u++ ) {
|
|
if ( 'a' <= *u && *u <= 'z' ) *l++ = *u;
|
|
else if ( 'A' <= *u && *u <= 'Z' ) *l++ = *u - 'A' + 'a';
|
|
else if ( '0' <= *u && *u <= '9' ) *l++ = *u;
|
|
else *l++ = rpl;
|
|
}
|
|
|
|
/* terminate the local name and indicate success */
|
|
*l = '\0';
|
|
return 1;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F ConvDire(...) . . . . . . . . . convert a directory name to local format
|
|
**
|
|
** 'ConvDire( <dirl>, <diru>, <root>,<abs>,<rel>,<sep>,<end> )'
|
|
**
|
|
** 'ConvDire' returns in <dirl> the universal directory name <diru>
|
|
** converted to the local format. <diru> contains an arbitrary number of
|
|
** components separated by slashes ('/'), where each component may contain
|
|
** uppercase, lowercase, and all special characters, and may be up to 255
|
|
** characters long.
|
|
**
|
|
** <root> is the string that is used for the root directory in local format.
|
|
** <abs> is the string that starts absolute directory names in local format,
|
|
** <rel> starts relative names, directory components are separated by <sep>,
|
|
** and <end> separates the directory part and a proper file name.
|
|
**
|
|
** If <diru> is the empty string, then 'ConvDire' returns in <dirl> also the
|
|
** empty string, instead of '<rel><end>'.
|
|
*/
|
|
int ConvDire ( dirl, diru, root, abs, rel, sep, end )
|
|
char * dirl;
|
|
char * diru;
|
|
char * root;
|
|
char * abs;
|
|
char * rel;
|
|
char * sep;
|
|
char * end;
|
|
{
|
|
char namu [256]; /* file name part, univ. */
|
|
char naml [256]; /* file name part, local */
|
|
char * d; /* loop variable */
|
|
char * s; /* loop variable */
|
|
|
|
/* special case for the root directory */
|
|
if ( *diru == '/' && diru[1] == '\0' ) {
|
|
for ( s = root; *s != '\0'; s++ ) *dirl++ = *s;
|
|
*dirl = '\0';
|
|
return 1;
|
|
}
|
|
|
|
/* start the file name with <abs> or <rel> */
|
|
d = diru;
|
|
if ( *diru == '/' )
|
|
for ( d++, s = abs; *s != '\0'; s++ ) *dirl++ = *s;
|
|
else if ( *diru != '\0' )
|
|
for ( s = rel; *s != '\0'; s++ ) *dirl++ = *s;
|
|
|
|
/* add the components of the directory part separated by <sep> */
|
|
while ( *d != '\0' ) {
|
|
s = namu;
|
|
while ( *d != '\0' && *d != '/' ) *s++ = *d++;
|
|
*s = '\0';
|
|
CONV_NAME( naml, namu );
|
|
for ( s = naml; *s != '\0'; s++ ) *dirl++ = *s;
|
|
if ( *d == '/' )
|
|
for ( d++, s = sep; *s != '\0'; s++ ) *dirl++ = *s;
|
|
}
|
|
|
|
/* add the divisor <end> */
|
|
if ( *diru != '\0' )
|
|
for ( s = end; *s != '\0'; s++ ) *dirl++ = *s;
|
|
|
|
/* terminate the file name and indicate success */
|
|
*dirl = '\0';
|
|
return 1;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F VmsBlckWritBinr(<blk>,<len>) . write a block to a binary file under VMS
|
|
*F VmsMakeDire(<patl>) . . . . . . . . . . . . create a directory under VMS
|
|
**
|
|
** 'VmsBlckWritBinr' writes the block <blk> of length <len> to the file
|
|
** opened with 'OPEN_WRIT_BINR'.
|
|
**
|
|
** 'VmsMakeDire' creates a directory under VMS. It has to change the path
|
|
** name from '[<components>]<dirl>' to '[<components>.<dirl>]'.
|
|
*/
|
|
#ifdef SYS_IS_VMS
|
|
|
|
unsigned long VmsBlckWritBinr ( blk, len )
|
|
unsigned char * blk;
|
|
unsigned long len;
|
|
{
|
|
unsigned char buf [512]; /* local buffer (padded with 0) */
|
|
long i, k, l; /* loop variables */
|
|
|
|
/* write the full 512 byte blocks */
|
|
for ( i = 0; i+512 < len; i += 512 ) {
|
|
if ( (l = write( WritBinr, blk+i, 512 )) != 512 )
|
|
return i + l;
|
|
}
|
|
|
|
/* write an incomplete last block padded with 0 */
|
|
for ( k = 0; k < 512; k++ )
|
|
buf[k] = (i+k < len ? blk[i+k] : 0);
|
|
if ( (l = write( WritBinr, buf, 512 )) != 512 )
|
|
return i + l;
|
|
|
|
/* indicate success */
|
|
return len;
|
|
}
|
|
|
|
int VmsMakeDire ( patl )
|
|
char * patl;
|
|
{
|
|
char * p;
|
|
|
|
/* replace the separator with a dot */
|
|
for ( p = patl; *p != '\0' && *p != ']'; p++ ) ;
|
|
if ( *p == ']' ) *p = '.';
|
|
|
|
/* append another separator */
|
|
for ( ; *p != '\0'; p++ ) ;
|
|
*p++ = ']';
|
|
*p = '\0';
|
|
|
|
/* make the directory and indicate success */
|
|
return mkdir( patl, 0 );
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F MacOpenWritText(<patl>) . . . . . open a text file for writing under MPW
|
|
*F MacClosWritText() . . . . . . . . . . . . . . close a text file under MPW
|
|
*F MacBlckWritText(<blk>,<len>) . . write a block to a text file under MPW
|
|
*F OPEN_WRIT_MACB(<patl>) . . . open a MacBinary file for writing under MPW
|
|
*F CLOS_WRIT_MACB() . . . . . . . . . . . close a MacBinary file under MPW
|
|
*F BLCK_WRIT_MACB(<blk>,<len>) . write a block to a MacBinary file under MPW
|
|
*F MacMakeDire(<patl>) . . . . . . . . . . . . create a directory under MPW
|
|
**
|
|
** 'MacBlckWritText' writes the block <blk> of length <len> to the text file
|
|
** opened with 'OPEN_WRIT_TEXT'. It converts <lf> ('\012') characters,
|
|
** which represent <newline> in universal text format, to '\n' characters,
|
|
** which represent <newline> in the system defined text format.
|
|
**
|
|
** 'MacMakeDire' creates the directory with local path name <patl>. The
|
|
** code comes from the Macintosh 'tar' port by Gail Zacharias.
|
|
*/
|
|
#ifdef SYS_IS_MAC_MPW
|
|
|
|
#include <Devices.h>
|
|
#include <Files.h>
|
|
|
|
#ifdef SYS_IS_MAC_THC
|
|
|
|
int MacOpenWritText ( patl )
|
|
char * patl;
|
|
{
|
|
FileParam fndrInfo;
|
|
char patp [256]; /* <patl> as a Pascal string */
|
|
int len; /* length of <patp> */
|
|
|
|
/* open the file */
|
|
if ( ! (WritText = fopen( (patl), "w" )) )
|
|
return 0;
|
|
|
|
/* convert <patl> from a C string to a Pascal string */
|
|
len = strlen( patl );
|
|
len = len < 256 ? len : 255;
|
|
patp[0] = len;
|
|
strncpy( patp+1, patl, len );
|
|
|
|
/* set the file type to 'TEXT' and the creator to TeachText */
|
|
fndrInfo.ioNamePtr = (unsigned char*)patp;
|
|
fndrInfo.ioVRefNum = 0;
|
|
fndrInfo.ioFVersNum = 0;
|
|
fndrInfo.ioFDirIndex = 0;
|
|
if ( PBGetFInfo( (ParmBlkPtr)&fndrInfo, 0 ) ) {
|
|
return 0;
|
|
}
|
|
fndrInfo.ioFlFndrInfo.fdType = 'TEXT';
|
|
fndrInfo.ioFlFndrInfo.fdCreator = 'ttxt';
|
|
if ( PBSetFInfo( (ParmBlkPtr)&fndrInfo, 0 ) ) {
|
|
return 0;
|
|
}
|
|
/* indicate success */
|
|
return 1;
|
|
}
|
|
|
|
int MacOpenWritBinr ( patl )
|
|
char * patl;
|
|
{
|
|
FileParam fndrInfo;
|
|
char patp [256]; /* <patl> as a Pascal string */
|
|
int len; /* length of <patp> */
|
|
|
|
/* open the file */
|
|
if ( ! (WritBinr = fopen( (patl), "wb" )) )
|
|
return 0;
|
|
|
|
|
|
/* convert <patl> from a C string to a Pascal string */
|
|
len = strlen( patl );
|
|
len = len < 256 ? len : 255;
|
|
patp[0] = len;
|
|
strncpy( patp+1, patl, len );
|
|
|
|
/* set the file type and creator */
|
|
fndrInfo.ioNamePtr = (unsigned char*)patp;
|
|
fndrInfo.ioVRefNum = 0;
|
|
fndrInfo.ioFVersNum = 0;
|
|
fndrInfo.ioFDirIndex = 0;
|
|
if ( PBGetFInfo( (ParmBlkPtr)&fndrInfo, 0 ) ) {
|
|
return 0;
|
|
}
|
|
fndrInfo.ioFlFndrInfo.fdType = 'BINA';
|
|
fndrInfo.ioFlFndrInfo.fdCreator = '????';
|
|
if ( PBSetFInfo( (ParmBlkPtr)&fndrInfo, 0 ) ) {
|
|
return 0;
|
|
}
|
|
/* indicate success */
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef SYS_IS_MAC_THC
|
|
int MacOpenWritText ( patl )
|
|
char * patl;
|
|
{
|
|
FInfo fndrInfo;
|
|
|
|
/* open the file */
|
|
if ( ! (WritText = fopen( (patl), "w" )) )
|
|
return 0;
|
|
|
|
/* set the file type to 'TEXT' and the creator to TeachText */
|
|
getfinfo( patl, 0, &fndrInfo );
|
|
if ( fndrInfo.fdType == 0 )
|
|
fndrInfo.fdType = 'TEXT';
|
|
if ( fndrInfo.fdCreator == 0 )
|
|
fndrInfo.fdCreator = 'ttxt';
|
|
setfinfo( patl, 0, &fndrInfo );
|
|
/* indicate success */
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
|
|
int MacClosWritText ()
|
|
{
|
|
return (fclose( WritText ) == 0);
|
|
}
|
|
|
|
unsigned long MacBlckWritText ( blk, len )
|
|
unsigned char * blk;
|
|
unsigned long len;
|
|
{
|
|
unsigned long i; /* loop variable */
|
|
|
|
for ( i = 0; i < len; i++ ) {
|
|
if (fputc( (blk[i] != '\012' ? blk[i] : '\n'), WritText ) == EOF)
|
|
return i;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
char WritName [256]; /* name of the file */
|
|
IOParam WritIOPB; /* IO parameter block */
|
|
FileParam WritFIPB; /* Finder Info parameter block */
|
|
unsigned long WritPart; /* current part of MacBinary file */
|
|
unsigned long WritType; /* type of file, e.g. 'TEXT' */
|
|
unsigned long WritCrtr; /* creator of file, e.g. 'ttxt' */
|
|
unsigned long WritFlgs; /* finder flags */
|
|
unsigned long WritCDat; /* creation date of file */
|
|
unsigned long WritMDat; /* last modification date of file */
|
|
unsigned long WritLDat; /* nr. of bytes left in data fork */
|
|
unsigned long WritLRsc; /* nr. of bytes left in resource */
|
|
|
|
int OPEN_WRIT_MACB ( patl )
|
|
char * patl;
|
|
{
|
|
unsigned long i; /* loop variable */
|
|
|
|
/* find the last semicolon */
|
|
for ( i = strlen(patl); 0 < i && patl[i] != ':'; i-- )
|
|
;
|
|
|
|
/* copy the directory part to 'WritName' */
|
|
WritName[0] = (0 < i ? i+1 : 0);
|
|
for ( i = 1; i <= WritName[0]; i++ )
|
|
WritName[i] = patl[i-1];
|
|
|
|
/* indicate success */
|
|
WritPart = 0;
|
|
return 1;
|
|
}
|
|
|
|
int CLOS_WRIT_MACB ()
|
|
{
|
|
|
|
/* first get the current settings */
|
|
#ifdef SYS_IS_MAC_THC
|
|
WritFIPB.ioNamePtr = (StringPtr)WritName;
|
|
#else
|
|
WritFIPB.ioNamePtr = WritName;
|
|
#endif
|
|
WritFIPB.ioVRefNum = 0;
|
|
WritFIPB.ioFVersNum = 0;
|
|
WritFIPB.ioFDirIndex = 0;
|
|
if ( PBGetFInfo( (ParmBlkPtr)&WritFIPB, 0 ) ) {
|
|
return 0;
|
|
}
|
|
|
|
/* now set some fields to the values found in the MacBinary header */
|
|
WritFIPB.ioFlFndrInfo.fdType = WritType;
|
|
WritFIPB.ioFlFndrInfo.fdCreator = WritCrtr;
|
|
WritFIPB.ioFlFndrInfo.fdFlags = WritFlgs;
|
|
WritFIPB.ioFlCrDat = WritCDat;
|
|
WritFIPB.ioFlMdDat = WritMDat;
|
|
if ( PBSetFInfo( (ParmBlkPtr)&WritFIPB, 0 ) ) {
|
|
return 0;
|
|
}
|
|
|
|
/* indicate success */
|
|
return 1;
|
|
}
|
|
|
|
unsigned long BLCK_WRIT_MACB ( blk, len )
|
|
unsigned char * blk;
|
|
unsigned long len;
|
|
{
|
|
unsigned long cnt; /* number of bytes written */
|
|
unsigned long i; /* loop variable */
|
|
|
|
/* first comes the header (128 bytes long) */
|
|
cnt = 0;
|
|
if ( WritPart == 0 ) {
|
|
for ( i = 1; i <= blk[1]; i++ )
|
|
WritName[WritName[0]+i] = blk[i+1];
|
|
WritName[0] += blk[1];
|
|
WritType = (blk[65]<<24) + (blk[66]<<16) + (blk[67]<< 8) + (blk[68]);
|
|
WritCrtr = (blk[69]<<24) + (blk[70]<<16) + (blk[71]<< 8) + (blk[72]);
|
|
WritFlgs = (blk[73]<< 8) + 0;
|
|
WritLDat = (blk[83]<<24) + (blk[84]<<16) + (blk[85]<< 8) + (blk[86]);
|
|
WritLRsc = (blk[87]<<24) + (blk[88]<<16) + (blk[89]<< 8) + (blk[90]);
|
|
WritCDat = (blk[91]<<24) + (blk[92]<<16) + (blk[93]<< 8) + (blk[94]);
|
|
WritMDat = (blk[95]<<24) + (blk[96]<<16) + (blk[97]<< 8) + (blk[98]);
|
|
cnt += 128;
|
|
WritPart = 1;
|
|
}
|
|
|
|
/* open the data fork */
|
|
if ( WritPart == 1 && cnt < len ) {
|
|
#ifdef SYS_IS_MAC_THC
|
|
WritFIPB.ioNamePtr = (StringPtr)WritName;
|
|
#else
|
|
WritFIPB.ioNamePtr = WritName;
|
|
#endif
|
|
WritIOPB.ioVRefNum = 0;
|
|
WritIOPB.ioVersNum = 0;
|
|
WritIOPB.ioPermssn = fsWrPerm;
|
|
WritIOPB.ioMisc = 0;
|
|
WritIOPB.ioRefNum = 0;
|
|
if ( PBCreate( (ParmBlkPtr)&WritIOPB, 0 ) ) {
|
|
return cnt;
|
|
}
|
|
if ( PBOpenSync( (ParmBlkPtr)&WritIOPB ) ) {
|
|
return cnt;
|
|
}
|
|
WritPart = 2;
|
|
}
|
|
|
|
/* next comes the data fork (padded to a multiple of 128 bytes) */
|
|
if ( WritPart == 2 ) {
|
|
while ( WritLDat != 0 && cnt < len ) {
|
|
WritIOPB.ioReqCount = (128 <= WritLDat ? 128 : WritLDat);
|
|
WritIOPB.ioPosMode = fsAtMark;
|
|
WritIOPB.ioPosOffset = 0;
|
|
WritIOPB.ioBuffer = (Ptr) (blk + cnt);
|
|
if ( PBWriteSync( (ParmBlkPtr)&WritIOPB )
|
|
|| WritIOPB.ioActCount != WritIOPB.ioReqCount ) {
|
|
PBCloseSync( (ParmBlkPtr)&WritIOPB );
|
|
return cnt;
|
|
}
|
|
cnt += 128;
|
|
WritLDat -= WritIOPB.ioReqCount;
|
|
}
|
|
if ( WritLDat == 0 ) WritPart = 3;
|
|
}
|
|
|
|
/* close the data fork */
|
|
if ( WritPart == 3 ) {
|
|
PBCloseSync( (ParmBlkPtr)&WritIOPB );
|
|
WritPart = 4;
|
|
}
|
|
|
|
/* open the resource fork */
|
|
if ( WritPart == 4 && cnt < len ) {
|
|
if ( PBOpenRF( (ParmBlkPtr)&WritIOPB, 0 ) ) {
|
|
return cnt;
|
|
}
|
|
WritPart = 5;
|
|
}
|
|
|
|
/* and finally comes the resource fork */
|
|
if ( WritPart == 5 ) {
|
|
while ( WritLRsc != 0 && cnt < len ) {
|
|
WritIOPB.ioReqCount = (128 <= WritLRsc ? 128 : WritLRsc);
|
|
WritIOPB.ioPosMode = fsAtMark;
|
|
WritIOPB.ioPosOffset = 0;
|
|
WritIOPB.ioBuffer = (Ptr) (blk + cnt);
|
|
if ( PBWriteSync( (ParmBlkPtr)&WritIOPB )
|
|
|| WritIOPB.ioActCount != WritIOPB.ioReqCount ) {
|
|
PBCloseSync( (ParmBlkPtr)&WritIOPB );
|
|
return cnt;
|
|
}
|
|
cnt += 128;
|
|
WritLRsc -= WritIOPB.ioReqCount;
|
|
}
|
|
if ( WritLRsc == 0 ) WritPart = 6;
|
|
}
|
|
|
|
/* close the resource fork */
|
|
if ( WritPart == 6 ) {
|
|
PBCloseSync( (ParmBlkPtr)&WritIOPB );
|
|
WritPart = 7;
|
|
}
|
|
|
|
/* indicate success */
|
|
return cnt;
|
|
}
|
|
|
|
int MacMakeDire ( patl )
|
|
char * patl;
|
|
{
|
|
HFileParam request; /* structure describing request */
|
|
char patp [256]; /* <patl> as a Pascal string */
|
|
int len; /* length of <patp> */
|
|
|
|
/* convert <patl> from a C string to a Pascal string */
|
|
len = strlen( patl );
|
|
len = len < 256 ? len : 255;
|
|
patp[0] = len;
|
|
strncpy( patp+1, patl, len );
|
|
|
|
/* set up the request */
|
|
request.ioNamePtr = (unsigned char*)patp;
|
|
request.ioVRefNum = 0;
|
|
request.ioDirID = 0;
|
|
if (noErr == PBDirCreate( (HParmBlkPtr)&request, 0 ))
|
|
/* return result */
|
|
return (request.ioResult == 0);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F MakeDirs(<pre>,<patu>) . . . . . . . . . . . . . . make all directories
|
|
**
|
|
** 'MakeDirs' tries to make all the directories along the universal path
|
|
** name <patu> (i.e., with components separated by '/'). <pre> is a prefix
|
|
** that is prepended to all path names.
|
|
*/
|
|
#ifdef MAKE_DIRE
|
|
|
|
int MakeDirs ( pre, patu )
|
|
char * pre;
|
|
char * patu;
|
|
{
|
|
char patl [1024]; /* path name, local */
|
|
char diru [256]; /* directory part of <patu>, univ. */
|
|
char dirl [256]; /* directory part of <patl>, local */
|
|
char namu [256]; /* file name part of <patu>, univ. */
|
|
char naml [256]; /* file name part of <patl>, local */
|
|
char * d, * n; /* loop variables */
|
|
|
|
/* if <patu> is an absolute path, copy the slash '/' */
|
|
d = diru;
|
|
if ( *patu == '/' ) *d++ = *patu++;
|
|
|
|
while ( *patu != '\0' ) {
|
|
|
|
/* copy the file name part of <patu> into <namu> */
|
|
for ( n = namu; *patu != '\0' && *patu != '/'; ) *n++ = *patu++;
|
|
if ( *patu != '\0' ) patu++;
|
|
|
|
/* convert the name into local format and make the directory */
|
|
*d = '\0'; *n = '\0';
|
|
CONV_DIRE( dirl, diru );
|
|
CONV_NAME( naml, namu );
|
|
strcpy( patl, pre );
|
|
strcat( patl, dirl );
|
|
strcat( patl, naml );
|
|
/*N 1993/11/03 martin what should I do with the return code? */
|
|
/*N 1993/11/03 martin it could be 0 if the directory exists! */
|
|
MAKE_DIRE( patl );
|
|
|
|
/* append the file name part to the directory part */
|
|
if ( d != diru && d[-1] != '/' ) *d++ = '/';
|
|
for ( n = namu; *n != '\0'; ) *d++ = *n++;
|
|
|
|
}
|
|
|
|
/* indicate success */
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F IsMatchName(<pat>,<str>) . . test if a string matches a wildcard pattern
|
|
**
|
|
** 'IsMatchName' return 1 if the pattern <pat> matches the string <str> and
|
|
** 0 otherwise. A '?' in <pat> matches any character in <str>, a '*'
|
|
** matches any string in <str>, other characters in <pat> match the same
|
|
** character in <str>. Characters for which 'IsSpec[<ch>]' is true will not
|
|
** be matched by '?' and '*'.
|
|
**
|
|
** Jeff Damens wrote the name match code in 'booz' (originally for Kermit).
|
|
*/
|
|
int IsSpec [256]; /* nonzero for special characters */
|
|
|
|
int IsMatchName ( pat, str )
|
|
char * pat; /* pattern to match against */
|
|
char * str; /* string to match */
|
|
{
|
|
char * pos = 0; /* pos. after last '*' in pattern */
|
|
char * tmp = 0; /* corresponding match in string */
|
|
|
|
/* 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=='?' && *str != '\0' ) { pat++; str++; }
|
|
else if ( *pat=='*' ) { pos = ++pat; tmp = str; }
|
|
else if ( tmp != 0 && ! IsSpec[*tmp] ) { pat = pos; str = ++tmp; }
|
|
else break;
|
|
}
|
|
return *pat == '\0' && *str == '\0';
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F OpenReadArch(<patl>) . . . . . . . . . . . . . . try to open the archive
|
|
*F ClosReadArch() . . . . . . . . . . . . . . . . . . . . close the archive
|
|
*F GotoReadArch(<pos>) . . . . . . goto an absolute position in the archive
|
|
*F ByteReadArch() . . . . . . . . . read a 8 bit unsigned from the archive
|
|
*F HalfReadArch() . . . . . . . . . read a 16 bit unsigned from the archive
|
|
*F TripReadArch() . . . . . . . . . read a 24 bit unsigned from the archive
|
|
*F WordReadArch() . . . . . . . . . read a 32 bit unsigned from the archive
|
|
*F BlckReadArch(<blk>,<len>) . . . . read a block of bytes from the archive
|
|
*V Descript . . . . . . . . . . . . . . . . . . . . header from the archive
|
|
*F DescReadArch() . . . . . . . . . . . . read the header from the archive
|
|
*V Entry . . . . . . . . . . . . . . . . header of a member from the archive
|
|
*F EntrReadArch() . . . . . . read the header of a member from the archive
|
|
**
|
|
** 'OpenReadArch' tries to open the archive with local path name <patl> (as
|
|
** specified by the user on the command line) for reading and returns 1 to
|
|
** indicate success or 0 to indicate that the file cannot be opened.
|
|
**
|
|
** 'ClosReadArch' closes the archive again.
|
|
**
|
|
** 'GotoReadArch' positions the archive at the position <pos>, i.e., the
|
|
** next call to 'ByteReadArch' will return the byte at position <pos>. Note
|
|
** that 'GotoReadArch' does not use 'fseek', because 'fseek' is unreliable.
|
|
**
|
|
** 'ByteReadArch' returns the next byte unsigned 8 bit from the archive.
|
|
** 'HalfReadArch' returns the next 2 bytes unsigned 16 bit from the archive.
|
|
** 'TripReadArch' returns the next 3 bytes unsigned 24 bit from the archive.
|
|
** 'WordReadArch' returns the next 4 bytes unsigned 32 bit from the archive.
|
|
** 'BlckReadArch' reads <len> bytes into the buffer <blk>.
|
|
**
|
|
** 'Descript' is the description of the archive.
|
|
**
|
|
** 'DescReadArch' reads the description of the archive that starts at the
|
|
** current position into the structure 'Descript'. It should of course only
|
|
** be called at the start of the archive file.
|
|
**
|
|
** 'Entry' is the directory entry of the current member from the archive.
|
|
**
|
|
** 'EntrReadArch' reads the directory entry of a member that starts at the
|
|
** current position into the structure 'Entry'.
|
|
*/
|
|
unsigned char BufArch [64+4096]; /* buffer for the archive */
|
|
|
|
unsigned char * PtrArch; /* pointer to the next byte */
|
|
|
|
unsigned char * EndArch; /* pointer to the last byte */
|
|
|
|
unsigned long PosArch; /* position of 'BufArch[0]' */
|
|
|
|
int OpenReadArch ( patl )
|
|
char * patl;
|
|
{
|
|
PtrArch = EndArch = (BufArch+64);
|
|
PosArch = 0;
|
|
return OPEN_READ_ARCH( patl );
|
|
}
|
|
|
|
int ClosReadArch ()
|
|
{
|
|
return CLOS_READ_ARCH();
|
|
}
|
|
|
|
int FillReadArch ()
|
|
{
|
|
unsigned char * s; /* loop variable */
|
|
unsigned char * d; /* loop variable */
|
|
|
|
/* copy the last characters to the beginning (for short backward seeks)*/
|
|
d = BufArch;
|
|
for ( s = EndArch-64; s < EndArch; s++ )
|
|
*d++ = *s;
|
|
PosArch += EndArch - (BufArch+64);
|
|
|
|
/* read a block */
|
|
PtrArch = BufArch+64;
|
|
EndArch = PtrArch + BLCK_READ_ARCH( PtrArch, 4096 );
|
|
|
|
/* return the first character */
|
|
return (PtrArch < EndArch ? *PtrArch++ : EOF);
|
|
}
|
|
|
|
int GotoReadArch ( pos )
|
|
unsigned long pos;
|
|
{
|
|
/* for long backward seeks goto the beginning of the file */
|
|
if ( pos+64 < PosArch ) {
|
|
#ifdef SEEK_READ_ARCH
|
|
if (!SEEK_READ_ARCH(pos))
|
|
return 0;
|
|
PtrArch = EndArch = BufArch+64;
|
|
PosArch = pos;
|
|
#else
|
|
if ( ! RWND_READ_ARCH() )
|
|
return 0;
|
|
PtrArch = EndArch = BufArch+64;
|
|
PosArch = 0;
|
|
#endif
|
|
}
|
|
|
|
/* jump forward bufferwise */
|
|
while ( PosArch + (EndArch - (BufArch+64)) <= pos ) {
|
|
if ( FillReadArch() == EOF )
|
|
return 0;
|
|
}
|
|
|
|
/* and goto the position (which is now in the buffer) */
|
|
PtrArch = (BufArch+64) + (pos - PosArch);
|
|
|
|
/* indicate success */
|
|
return 1;
|
|
}
|
|
|
|
#define ByteReadArch() (PtrArch<EndArch?*PtrArch++:FillReadArch())
|
|
|
|
unsigned long HalfReadArch ()
|
|
{
|
|
unsigned long result;
|
|
result = ((unsigned long)ByteReadArch());
|
|
result += ((unsigned long)ByteReadArch()) << 8;
|
|
return result;
|
|
}
|
|
|
|
unsigned long FlahReadArch ()
|
|
{
|
|
unsigned long result;
|
|
result = ((unsigned long)ByteReadArch()) << 8;
|
|
result += ((unsigned long)ByteReadArch());
|
|
return result;
|
|
}
|
|
|
|
unsigned long TripReadArch ()
|
|
{
|
|
unsigned long result;
|
|
result = ((unsigned long)ByteReadArch());
|
|
result += ((unsigned long)ByteReadArch()) << 8;
|
|
result += ((unsigned long)ByteReadArch()) << 16;
|
|
return result;
|
|
}
|
|
|
|
unsigned long WordReadArch ()
|
|
{
|
|
unsigned long result;
|
|
result = ((unsigned long)ByteReadArch());
|
|
result += ((unsigned long)ByteReadArch()) << 8;
|
|
result += ((unsigned long)ByteReadArch()) << 16;
|
|
result += ((unsigned long)ByteReadArch()) << 24;
|
|
return result;
|
|
}
|
|
|
|
unsigned long BlckReadArch ( blk, len )
|
|
char * blk;
|
|
unsigned long len;
|
|
{
|
|
int ch; /* character read */
|
|
unsigned long i; /* loop variable */
|
|
for ( i = 0; i < len; i++ ) {
|
|
if ( (ch = ByteReadArch()) == EOF )
|
|
return i;
|
|
else
|
|
*blk++ = ch;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
struct {
|
|
char text[20]; /* "ZOO 2.10 Archive.<ctr>Z" */
|
|
unsigned long magic; /* magic word 0xfdc4a7dc */
|
|
unsigned long posent; /* position of first directory ent.*/
|
|
unsigned long klhvmh; /* two's complement of posent */
|
|
unsigned char majver; /* major version needed to extract */
|
|
unsigned char minver; /* minor version needed to extract */
|
|
unsigned char type; /* type of current member (0,1) */
|
|
unsigned long poscmt; /* position of comment, 0 if none */
|
|
unsigned short sizcmt; /* length of comment, 0 if none */
|
|
unsigned char modgen; /* gens. on, gen. limit */
|
|
/* the following are not in the archive file and are computed */
|
|
unsigned long sizorg; /* uncompressed size of members */
|
|
unsigned long siznow; /* compressed size of members */
|
|
unsigned long number; /* number of members */
|
|
|
|
} Descript;
|
|
|
|
int DescReadArch ()
|
|
{
|
|
/* read the text at the beginning */
|
|
BlckReadArch(Descript.text,20L); Descript.text[20] = '\0';
|
|
|
|
/* try to read the magic words */
|
|
if ( (Descript.magic = WordReadArch()) != (unsigned long)0xfdc4a7dcL )
|
|
return 0;
|
|
|
|
/* read the old part of the description */
|
|
Descript.posent = WordReadArch();
|
|
Descript.klhvmh = WordReadArch();
|
|
Descript.majver = ByteReadArch();
|
|
Descript.minver = ByteReadArch();
|
|
|
|
/* read the new part of the description if present */
|
|
Descript.type = (34 < Descript.posent ? ByteReadArch() : 0);
|
|
Descript.poscmt = (34 < Descript.posent ? WordReadArch() : 0);
|
|
Descript.sizcmt = (34 < Descript.posent ? HalfReadArch() : 0);
|
|
Descript.modgen = (34 < Descript.posent ? ByteReadArch() : 0);
|
|
|
|
/* initialize the fake entries */
|
|
Descript.sizorg = 0;
|
|
Descript.siznow = 0;
|
|
Descript.number = 0;
|
|
|
|
/* indicate success */
|
|
return 1;
|
|
}
|
|
|
|
struct {
|
|
unsigned long magic; /* magic word 0xfdc4a7dc */
|
|
unsigned char type; /* type of current member (1) */
|
|
unsigned char method; /* packing method of member (0..2) */
|
|
unsigned long posnxt; /* position of next member */
|
|
unsigned long posdat; /* position of data */
|
|
unsigned short datdos; /* date (in DOS format) */
|
|
unsigned short timdos; /* time (in DOS format) */
|
|
unsigned short crcdat; /* crc value of member */
|
|
unsigned long sizorg; /* uncompressed size of member */
|
|
unsigned long siznow; /* compressed size of member */
|
|
unsigned char majver; /* major version needed to extract */
|
|
unsigned char minver; /* minor version needed to extract */
|
|
unsigned char delete; /* 1 if member is deleted, 0 else */
|
|
unsigned char spared; /* spare entry to pad entry */
|
|
unsigned long poscmt; /* position of comment, 0 if none */
|
|
unsigned short sizcmt; /* length of comment, 0 if none */
|
|
char nams [14]; /* short name of member or archive */
|
|
unsigned short lvar; /* length of variable part */
|
|
unsigned char timzon; /* time zone */
|
|
unsigned short crcent; /* crc value of entry */
|
|
unsigned char lnamu; /* length of long name */
|
|
unsigned char ldiru; /* length of directory */
|
|
char namu [256]; /* univ. name of member of archive */
|
|
char diru [256]; /* univ. name of directory */
|
|
unsigned short system; /* system identifier */
|
|
unsigned long permis; /* file permissions */
|
|
unsigned char modgen; /* gens. on, last gen., gen. limit */
|
|
unsigned short ver; /* version number of member */
|
|
/* the following are not in the archive file and are computed */
|
|
char naml [256]; /* local name of member of archive */
|
|
char dirl [256]; /* local name of directory */
|
|
char patl [512]; /* local path name of member */
|
|
char patv [512]; /* ditto but with version number */
|
|
char * patw; /* name used by '-l' */
|
|
unsigned long year; /* years since 1900 */
|
|
unsigned long month; /* month since January */
|
|
unsigned long day; /* day of month */
|
|
unsigned long hour; /* hours since midnight */
|
|
unsigned long min; /* minutes after the hour */
|
|
unsigned long sec; /* seconds after the minutes */
|
|
} Entry;
|
|
|
|
int EntrReadArch ()
|
|
{
|
|
unsigned long l; /* 'Entry.lnamu+Entry.ldiru' */
|
|
char * p; /* loop variable */
|
|
|
|
/* try to read the magic words */
|
|
if ( (Entry.magic = WordReadArch()) != (unsigned long)0xfdc4a7dcL )
|
|
return 0;
|
|
|
|
/* read the fixed part of the directory entry */
|
|
Entry.type = ByteReadArch();
|
|
Entry.method = ByteReadArch();
|
|
Entry.posnxt = WordReadArch();
|
|
Entry.posdat = WordReadArch();
|
|
Entry.datdos = HalfReadArch();
|
|
Entry.timdos = HalfReadArch();
|
|
Entry.crcdat = HalfReadArch();
|
|
Entry.sizorg = WordReadArch();
|
|
Entry.siznow = WordReadArch();
|
|
Entry.majver = ByteReadArch();
|
|
Entry.minver = ByteReadArch();
|
|
Entry.delete = ByteReadArch();
|
|
Entry.spared = ByteReadArch();
|
|
Entry.poscmt = WordReadArch();
|
|
Entry.sizcmt = HalfReadArch();
|
|
BlckReadArch(Entry.nams,13L); Entry.nams[13] = '\0';
|
|
|
|
/* handle the long name and the directory in the variable part */
|
|
Entry.lvar = (Entry.type == 2 ? HalfReadArch() : 0);
|
|
Entry.timzon = (Entry.type == 2 ? ByteReadArch() : 127);
|
|
Entry.crcent = (Entry.type == 2 ? HalfReadArch() : 0);
|
|
Entry.lnamu = (0 < Entry.lvar ? ByteReadArch() : 0);
|
|
Entry.ldiru = (1 < Entry.lvar ? ByteReadArch() : 0);
|
|
BlckReadArch(Entry.namu,(unsigned long)Entry.lnamu);
|
|
Entry.namu[Entry.lnamu] = '\0';
|
|
BlckReadArch(Entry.diru,(unsigned long)Entry.ldiru);
|
|
Entry.diru[Entry.ldiru] = '\0';
|
|
l = Entry.lnamu + Entry.ldiru;
|
|
Entry.system = (l+2 < Entry.lvar ? HalfReadArch() : 0);
|
|
Entry.permis = (l+4 < Entry.lvar ? TripReadArch() : 0);
|
|
Entry.modgen = (l+7 < Entry.lvar ? ByteReadArch() : 0);
|
|
Entry.ver = (l+7 < Entry.lvar ? HalfReadArch() : 0);
|
|
|
|
/* 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) );
|
|
}
|
|
strcpy( Entry.patl, Entry.dirl );
|
|
strcat( Entry.patl, Entry.naml );
|
|
|
|
/* create the name with the version appended */
|
|
strcpy( Entry.patv, Entry.patl );
|
|
p = Entry.patv; while ( *p != '\0' ) p++;
|
|
*p++ = ';';
|
|
for ( l = 10000; 0 < l; l /= 10 )
|
|
if ( l == 1 || l <= Entry.ver )
|
|
*p++ = (Entry.ver / l) % 10 + '0';
|
|
*p = '\0';
|
|
Entry.patw = ((Entry.modgen&0xc0)!=0x80 ? Entry.patl : Entry.patv);
|
|
|
|
/* convert the time */
|
|
Entry.year = ((Entry.datdos >> 9) & 0x7f) + 80;
|
|
Entry.month = ((Entry.datdos >> 5) & 0x0f) - 1;
|
|
Entry.day = ((Entry.datdos ) & 0x1f);
|
|
Entry.hour = ((Entry.timdos >> 11) & 0x1f);
|
|
Entry.min = ((Entry.timdos >> 5) & 0x3f);
|
|
Entry.sec = ((Entry.timdos ) & 0x1f) * 2;
|
|
|
|
/* indicate success */
|
|
return 1;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F OpenReadFile(<patl>,<bin>) . . . . . . . . . . . open a file for reading
|
|
*F ClosReadFile() . . . . . . . . . . . . . . . . . . . close a file again
|
|
*F BlckReadFile(<blk>,<len>) . . . . . . . write a block of bytes to a file
|
|
*F BufFile[] . . . . . . . . . . . . . . . . . . . . . . buffer for the file
|
|
**
|
|
** 'OpenReadFile' tries to open the archive with local path name <patl> (as
|
|
** converted by 'CONV_NAME' and 'CONV_DIRE') for reading and returns 1 to
|
|
** indicate success and 0 to indicate that the file cannot be opened. If
|
|
** <bin> is 0, the file is opened as a text file, otherwise the file is
|
|
** opened as a binary file.
|
|
**
|
|
** 'ClosReadFile' closes the file again.
|
|
**
|
|
** 'BlckReadFile' reads <len> bytes from the file to the buffer <blk> and
|
|
** returns the number of bytes actually read. If no file is open
|
|
** 'BlckReadFile' only returns 0.
|
|
**
|
|
** 'BufFile' is a buffer for the file (which is not used by the above
|
|
** functions).
|
|
*/
|
|
unsigned long IsOpenReadFile;
|
|
|
|
int OpenReadFile ( patl, bin )
|
|
char * patl;
|
|
unsigned long bin;
|
|
{
|
|
if ( bin == 0 && OPEN_READ_TEXT(patl) ) {
|
|
IsOpenReadFile = 1;
|
|
return 1;
|
|
}
|
|
else if ( bin == 1 && OPEN_READ_BINR(patl) ) {
|
|
IsOpenReadFile = 2;
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ClosReadFile ()
|
|
{
|
|
if ( IsOpenReadFile == 1 ) {
|
|
IsOpenReadFile = 0;
|
|
return CLOS_READ_TEXT();
|
|
}
|
|
else if ( IsOpenReadFile == 2 ) {
|
|
IsOpenReadFile = 0;
|
|
return CLOS_READ_BINR();
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
unsigned long BlckReadFile ( blk, len )
|
|
char * blk;
|
|
unsigned long len;
|
|
{
|
|
if ( IsOpenReadFile == 1 ) {
|
|
return BLCK_READ_TEXT( blk, len );
|
|
}
|
|
else if ( IsOpenReadFile == 2 ) {
|
|
return BLCK_READ_BINR( blk, len );
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
char BufFile [8192]; /* at least MAX_OFF */
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F OpenWritFile(<patl>,<bin>) . . . . . . . . . . . open a file for writing
|
|
*F ClosWritFile() . . . . . . . . . . . . . . . . . . . close a file again
|
|
*F BlckWritFile(<blk>,<len>) . . . . . . . write a block of bytes to a file
|
|
**
|
|
** 'OpenWritFile' tries to open the archive with local path name <patl> (as
|
|
** converted by 'CONV_NAME' and 'CONV_DIRE') for writing and returns 1 to
|
|
** indicate success and 0 to indicate that the file cannot be opened. If
|
|
** <bin> is 0, the file is opened as a text file, otherwise the file is
|
|
** opened as a binary file.
|
|
**
|
|
** 'ClosWritFile' closes the file again.
|
|
**
|
|
** 'BlckWritFile' writes <len> bytes from the buffer <blk> to the file and
|
|
** returns the number of bytes actually written, which is less than <len>
|
|
** only when a write error happened. If no file is open 'BlckWritFile' only
|
|
** returns <len>.
|
|
*/
|
|
unsigned long IsOpenWritFile;
|
|
|
|
int OpenWritFile ( patl, bin )
|
|
char * patl;
|
|
unsigned long bin;
|
|
{
|
|
if ( patl == 0 ) {
|
|
IsOpenWritFile = 1;
|
|
return 1;
|
|
}
|
|
else if ( bin == 1 && OPEN_WRIT_TEXT(patl) ) {
|
|
IsOpenWritFile = 2;
|
|
return 1;
|
|
}
|
|
else if ( bin == 2 && OPEN_WRIT_BINR(patl) ) {
|
|
IsOpenWritFile = 3;
|
|
return 1;
|
|
}
|
|
#ifdef SYS_IS_MAC_MPW
|
|
else if ( bin == 3 && OPEN_WRIT_MACB(patl) ) {
|
|
IsOpenWritFile = 4;
|
|
return 1;
|
|
}
|
|
#endif
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ClosWritFile ()
|
|
{
|
|
if ( IsOpenWritFile == 1 ) {
|
|
return 1;
|
|
}
|
|
else if ( IsOpenWritFile == 2 ) {
|
|
IsOpenWritFile = 0;
|
|
return CLOS_WRIT_TEXT();
|
|
}
|
|
else if ( IsOpenWritFile == 3 ) {
|
|
IsOpenWritFile = 0;
|
|
return CLOS_WRIT_BINR();
|
|
}
|
|
#ifdef SYS_IS_MAC_MPW
|
|
else if ( IsOpenWritFile == 4 ) {
|
|
IsOpenWritFile = 0;
|
|
return CLOS_WRIT_MACB();
|
|
}
|
|
#endif
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
unsigned long BlckWritFile ( blk, len )
|
|
char * blk;
|
|
unsigned long len;
|
|
{
|
|
unsigned long i; /* loop variable */
|
|
if ( IsOpenWritFile == 1 ) {
|
|
for ( i = 0; i < len; i++ )
|
|
putchar( blk[i] );
|
|
return len;
|
|
}
|
|
else if ( IsOpenWritFile == 2 ) {
|
|
return BLCK_WRIT_TEXT( blk, len );
|
|
}
|
|
else if ( IsOpenWritFile == 3 ) {
|
|
return BLCK_WRIT_BINR( blk, len );
|
|
}
|
|
#ifdef SYS_IS_MAC_MPW
|
|
else if ( IsOpenWritFile == 4 ) {
|
|
#ifdef SYS_IS_MAC_THC
|
|
return BLCK_WRIT_MACB( (StringPtr)blk, len );
|
|
#else
|
|
return BLCK_WRIT_MACB( blk, len );
|
|
#endif
|
|
}
|
|
#endif
|
|
else {
|
|
return len;
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*V Crc . . . . . . . . . . . . . . . . current cyclic redundancy check value
|
|
*F CRC_BYTE(<crc>,<byte>) . . . . . cyclic redundancy check value of a byte
|
|
*F InitCrc() . . . . . . . . . . . . initialize cylic redundancy check table
|
|
**
|
|
** 'Crc' is used by the decoding functions to communicate the computed
|
|
** CRC-16 value to the calling function.
|
|
**
|
|
** 'CRC_BYTE' returns the new value that one gets by updating the old CRC-16
|
|
** value <crc> with the additional byte <byte>. It is used to compute the
|
|
** ANSI CRC-16 value for each member of the archive. They idea is that if
|
|
** not too many bits of a member have corrupted, then the CRC-16 will be
|
|
** different, and so the corruption can be detected.
|
|
**
|
|
** 'InitCrc' initialize the table that 'CRC_BYTE' uses. You must call this
|
|
** before using 'CRC_BYTE'.
|
|
**
|
|
** The ANSI CRC-16 value for a sequence of bits of lenght <length> is
|
|
** computed by shifting the bits through the following shift register (where
|
|
** 'O' are the latches and '+' denotes logical xor)
|
|
**
|
|
** bit bit ... bit bit bit -->-
|
|
** <length> <length>-1 3 2 1 |
|
|
** V
|
|
** -<-------<---------------------------------------------------<----+
|
|
** | | | ^
|
|
** V V V |
|
|
** ->O-->O-+>O-->O-->O-->O-->O-->O-->O-->O-->O-->O-->O-->O-->O-+>O-->-
|
|
** MSB LSB
|
|
**
|
|
** Mathematically we compute in the polynomial ring $GF(2)[x]$ the remainder
|
|
**
|
|
** $$\sum_{i=1}^{i=length}{bit_i x^{length+16-i}} mod crcpol$$
|
|
**
|
|
** where $crcpol = x^{16} + x^{15} + x^2 + 1$. Then the CRC-16 value
|
|
** consists of the coefficients of the remainder, with the constant
|
|
** coefficient being the most significant bit (MSB) and the coefficient of
|
|
** $x^{15}$ the least significant bit (LSB).
|
|
**
|
|
** Changing a single bit will always cause the CRC-16 value to change,
|
|
** because $x^{i} mod crcpol$ is never zero.
|
|
**
|
|
** Changing two bits will cause the CRC-16 value to change, unless the
|
|
** distance between the bits is a multiple of 32767, which is the order of
|
|
** $x$ modulo $crcpol = (x+1)(x^{15} + x + 1)$ ($x^{15}+x+1$ is primitive).
|
|
**
|
|
** Changing 16 adjacent bits will always cause the CRC value to change,
|
|
** because $x^{16}$ and $crcpol$ are relatively prime.
|
|
**
|
|
** David Schwaderer provided the CRC-16 calculation in PC Tech Journal 4/85.
|
|
*/
|
|
unsigned long Crc;
|
|
|
|
unsigned long CrcTab [256];
|
|
|
|
#define CRC_BYTE(crc,byte) (((crc)>>8) ^ CrcTab[ ((crc)^(byte))&0xff ])
|
|
|
|
int InitCrc ()
|
|
{
|
|
unsigned long i, k; /* loop variables */
|
|
for ( i = 0; i < 256; i++ ) {
|
|
CrcTab[i] = i;
|
|
for ( k = 0; k < 8; k++ )
|
|
CrcTab[i] = (CrcTab[i]>>1) ^ ((CrcTab[i] & 1) ? 0xa001 : 0);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*V ErrMsg . . . . . . . . . . . . . . . . . . . . . . . . . . error message
|
|
**
|
|
** 'ErrMsg' is used by the decode functions to communicate the cause of an
|
|
** error to the calling function.
|
|
*/
|
|
char * ErrMsg;
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F DecodeCopy(<size>). . . . . . . . . . . . extract an uncompressed member
|
|
**
|
|
** 'DecodeCopy' simply copies <size> bytes from the archive to the output
|
|
** file.
|
|
*/
|
|
int DecodeCopy ( size )
|
|
unsigned long size;
|
|
{
|
|
unsigned long siz; /* size of current block */
|
|
unsigned long crc; /* CRC-16 value */
|
|
unsigned long i; /* loop variable */
|
|
|
|
/* initialize the crc value */
|
|
crc = 0;
|
|
|
|
/* loop until everything has been copied */
|
|
while ( 0 < size ) {
|
|
|
|
/* read as many bytes as possible in one go */
|
|
siz = (sizeof(BufFile) < size ? sizeof(BufFile) : size);
|
|
if ( BlckReadArch( BufFile, siz ) != siz ) {
|
|
ErrMsg = "unexpected <eof> in the archive";
|
|
return 0;
|
|
}
|
|
|
|
/* write them */
|
|
if ( BlckWritFile( BufFile, siz ) != siz ) {
|
|
ErrMsg = "cannot write output file";
|
|
return 0;
|
|
}
|
|
|
|
/* compute the crc */
|
|
for ( i = 0; i < siz; i++ )
|
|
crc = CRC_BYTE( crc, BufFile[i] );
|
|
|
|
/* on to the next block */
|
|
size -= siz;
|
|
}
|
|
|
|
/* store the crc and indicate success */
|
|
Crc = crc;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F DecodeLzd() . . . . . . . . . . . . . . . extract a LZ compressed member
|
|
**
|
|
*N 1993/10/21 martin add LZD.
|
|
*/
|
|
int DecodeLzd ()
|
|
{
|
|
ErrMsg = "LZD not yet implemented";
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F DecodeLzh() . . . . . . . . . . . . . . . extract a LZH compressed member
|
|
**
|
|
** 'DecodeLzh' decodes a LZH (Lempel-Ziv 77 with dynamic Huffman coding)
|
|
** encoded member from the archive to the output file.
|
|
**
|
|
** Each member is encoded as a series of blocks. Each block starts with a
|
|
** 16 bit field that contains the number of codes in this block <number>.
|
|
** The member is terminated by a block with 0 codes.
|
|
**
|
|
** Next each block contains the description of three Huffman codes, called
|
|
** pre code, literal/length code, and log code. The purpose of the pre code
|
|
** is to encode the description of the literal/length code. The purpose of
|
|
** the literal/length code and the log code is to encode the appropriate
|
|
** fields in the LZ code. I am too stupid to understand the format of the
|
|
** description.
|
|
**
|
|
** Then each block contains <number> codewords. There are two kinds of
|
|
** codewords, *literals* and *copy instructions*.
|
|
**
|
|
** A literal represents a certain byte. For the moment imaging the literal
|
|
** as having 9 bits. The first bit is zero, the other 8 bits contain the
|
|
** byte.
|
|
**
|
|
** +--+----------------+
|
|
** | 0| <byte> |
|
|
** +--+----------------+
|
|
**
|
|
** When a literal is encountered, the byte <byte> that it represents is
|
|
** appended to the output.
|
|
**
|
|
** A copy instruction represents a certain sequence of bytes that appeared
|
|
** already earlier in the output. The copy instruction consists of three
|
|
** parts, the length, the offset logarithm, and the offset mantissa.
|
|
**
|
|
** +--+----------------+--------+--------------------+
|
|
** | 1| <length>-3 | <log> | <mantissa> |
|
|
** +--+----------------+--------+--------------------+
|
|
**
|
|
** <length> is the length of the sequence which this copy instruction
|
|
** represents. We store '<length>-3', because <length> is never 0, 1, or 2;
|
|
** such sequences are better represented by 0, 1, or 2 literals. <log> and
|
|
** <mantissa> together represent the offset at which the sequence of bytes
|
|
** already appeared. '<log>-1' is the number of bits in the <mantissa>
|
|
** field, and the offset is $2^{<log>-1} + <mantissa>$. For example
|
|
**
|
|
** +--+----------------+--------+----------+
|
|
** | 1| 9 | 6 | 0 1 1 0 1|
|
|
** +--+----------------+--------+----------+
|
|
**
|
|
** represents the sequence of 12 bytes that appeared $2^5 + 8 + 4 + 1 = 45$
|
|
** bytes earlier in the output (so those 18 bits of input represent 12 bytes
|
|
** of output).
|
|
**
|
|
** When a copy instruction is encountered, the sequence of <length> bytes
|
|
** that appeared <offset> bytes earlier in the output is again appended
|
|
** (copied) to the output. For this purpose the last <max> bytes are
|
|
** remembered, where <max> is the maximal used offset. In 'zoo' this
|
|
** maximal offset is $2^{13} = 8192$. The buffer in which those bytes are
|
|
** remembered is called a sliding window for reasons that should be
|
|
** obvious.
|
|
**
|
|
** To save even more space the first 9 bits of each code, which represent
|
|
** the type of code and either the literal value or the length, are encoded
|
|
** using a Huffman code called the literal/length code. Also the next 4
|
|
** bits in copy instructions, which represent the logarithm of the offset,
|
|
** are encoded using a second Huffman code called the log code.
|
|
**
|
|
** Those codes are fixed, i.e., not adaptive, but may vary between the
|
|
** blocks, i.e., in each block literals/lengths and logs may be encoded by
|
|
** different codes. The codes are described at the beginning of each block.
|
|
**
|
|
** Haruhiko Okumura wrote the LZH code (originally for his 'ar' archiver).
|
|
*/
|
|
#define MAX_LIT 255 /* maximal literal code */
|
|
#define MIN_LEN 3 /* minimal length of match */
|
|
#define MAX_LEN 256 /* maximal length of match */
|
|
#define MAX_CODE (MAX_LIT+1 + MAX_LEN+1 - MIN_LEN)
|
|
#define BITS_CODE 9 /* 2^BITS_CODE > MAX_CODE (+1?) */
|
|
#define MAX_OFF 8192 /* 13 bit sliding directory */
|
|
#define MAX_LOG 13 /* maximal log_2 of offset */
|
|
#define BITS_LOG 4 /* 2^BITS_LOG > MAX_LOG (+1?) */
|
|
#define MAX_PRE 18 /* maximal pre code */
|
|
#define BITS_PRE 5 /* 2^BITS_PRE > MAX_PRE (+1?) */
|
|
|
|
unsigned short TreeLeft [2*MAX_CODE+1];/* tree for codes (upper half) */
|
|
unsigned short TreeRight[2*MAX_CODE+1];/* and for offsets (lower half) */
|
|
unsigned short TabCode [4096]; /* table for fast lookup of codes */
|
|
unsigned char LenCode [MAX_CODE+1]; /* number of bits used for code */
|
|
unsigned short TabLog [256]; /* table for fast lookup of logs */
|
|
unsigned char LenLog [MAX_LOG+1]; /* number of bits used for logs */
|
|
unsigned short TabPre [256]; /* table for fast lookup of pres */
|
|
unsigned char LenPre [MAX_PRE+1]; /* number of bits used for pres */
|
|
|
|
int MakeTablLzh ( nchar, bitlen, tablebits, table )
|
|
int nchar;
|
|
unsigned char bitlen[];
|
|
int tablebits;
|
|
unsigned short table[];
|
|
{
|
|
unsigned short count[17], weight[17], start[18], *p;
|
|
unsigned int i, k, len, ch, jutbits, avail, mask;
|
|
|
|
for (i = 1; i <= 16; i++) count[i] = 0;
|
|
for (i = 0; i < nchar; i++) count[bitlen[i]]++;
|
|
|
|
start[1] = 0;
|
|
for (i = 1; i <= 16; i++)
|
|
start[i + 1] = start[i] + (count[i] << (16 - i));
|
|
if (start[17] != (unsigned short)((unsigned) 1 << 16))
|
|
return 0;
|
|
|
|
jutbits = 16 - tablebits;
|
|
for (i = 1; i <= tablebits; i++) {
|
|
start[i] >>= jutbits;
|
|
weight[i] = (unsigned) 1 << (tablebits - i);
|
|
}
|
|
while (i <= 16) {
|
|
weight[i] = (unsigned) 1 << (16 - i);
|
|
i++;
|
|
}
|
|
|
|
i = start[tablebits + 1] >> jutbits;
|
|
if (i != (unsigned short)((unsigned) 1 << 16)) {
|
|
k = 1 << tablebits;
|
|
while (i != k) table[i++] = 0;
|
|
}
|
|
|
|
avail = nchar;
|
|
mask = (unsigned) 1 << (15 - tablebits);
|
|
for (ch = 0; ch < nchar; ch++) {
|
|
if ((len = bitlen[ch]) == 0) continue;
|
|
if (len <= tablebits) {
|
|
for ( i = 0; i < weight[len]; i++ ) table[i+start[len]] = ch;
|
|
}
|
|
else {
|
|
k = start[len];
|
|
p = &table[k >> jutbits];
|
|
i = len - tablebits;
|
|
while (i != 0) {
|
|
if (*p == 0) {
|
|
TreeRight[avail] = TreeLeft[avail] = 0;
|
|
*p = avail++;
|
|
}
|
|
if (k & mask) p = &TreeRight[*p];
|
|
else p = &TreeLeft[*p];
|
|
k <<= 1; i--;
|
|
}
|
|
*p = ch;
|
|
}
|
|
start[len] += weight[len];
|
|
}
|
|
|
|
/* indicate success */
|
|
return 1;
|
|
}
|
|
|
|
int DecodeLzh ()
|
|
{
|
|
unsigned long cnt; /* number of codes in block */
|
|
unsigned long cnt2; /* number of stuff in pre code */
|
|
unsigned long code; /* code from the Archive */
|
|
unsigned long len; /* length of match */
|
|
unsigned long log; /* log_2 of offset of match */
|
|
unsigned long off; /* offset of match */
|
|
unsigned long pre; /* pre code */
|
|
char * cur; /* current position in BufFile */
|
|
char * pos; /* position of match */
|
|
char * end; /* pointer to the end of BufFile */
|
|
char * stp; /* stop pointer during copy */
|
|
unsigned long crc; /* cyclic redundancy check value */
|
|
unsigned long i; /* loop variable */
|
|
unsigned long bits; /* the bits we are looking at */
|
|
unsigned long bitc; /* number of bits that are valid */
|
|
|
|
#define PEEK_BITS(N) ((bits >> (bitc-(N))) & ((1L<<(N))-1))
|
|
#define FLSH_BITS(N) if ( (bitc -= (N)) < 16 ) { bits = (bits<<16) + FlahReadArch(); bitc += 16; }
|
|
|
|
/* initialize bit source, output pointer, and crc */
|
|
bits = 0; bitc = 0; FLSH_BITS(0);
|
|
cur = BufFile; end = BufFile + MAX_OFF;
|
|
crc = 0;
|
|
|
|
/* loop until all blocks have been read */
|
|
cnt = PEEK_BITS( 16 ); FLSH_BITS( 16 );
|
|
while ( cnt != 0 ) {
|
|
|
|
/* read the pre code */
|
|
cnt2 = PEEK_BITS( BITS_PRE ); FLSH_BITS( BITS_PRE );
|
|
if ( cnt2 == 0 ) {
|
|
pre = PEEK_BITS( BITS_PRE ); FLSH_BITS( BITS_PRE );
|
|
for ( i = 0; i < 256; i++ ) TabPre[i] = pre;
|
|
for ( i = 0; i <= MAX_PRE; i++ ) LenPre[i] = 0;
|
|
}
|
|
else {
|
|
i = 0;
|
|
while ( i < cnt2 ) {
|
|
len = PEEK_BITS( 3 ); FLSH_BITS( 3 );
|
|
if ( len == 7 ) {
|
|
while ( PEEK_BITS( 1 ) ) { len++; FLSH_BITS( 1 ); }
|
|
FLSH_BITS( 1 );
|
|
}
|
|
LenPre[i++] = len;
|
|
if ( i == 3 ) {
|
|
len = PEEK_BITS( 2 ); FLSH_BITS( 2 );
|
|
while ( 0 < len-- ) LenPre[i++] = 0;
|
|
}
|
|
}
|
|
while ( i <= MAX_PRE ) LenPre[i++] = 0;
|
|
if ( ! MakeTablLzh( MAX_PRE+1, LenPre, 8, TabPre ) ) {
|
|
ErrMsg = "pre code description corrupted";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* read the code (using the pre code) */
|
|
cnt2 = PEEK_BITS( BITS_CODE ); FLSH_BITS( BITS_CODE );
|
|
if ( cnt2 == 0 ) {
|
|
code = PEEK_BITS( BITS_CODE ); FLSH_BITS( BITS_CODE );
|
|
for ( i = 0; i < 4096; i++ ) TabCode[i] = code;
|
|
for ( i = 0; i <= MAX_CODE; i++ ) LenCode[i] = 0;
|
|
}
|
|
else {
|
|
i = 0;
|
|
while ( i < cnt2 ) {
|
|
len = TabPre[ PEEK_BITS( 8 ) ];
|
|
if ( len <= MAX_PRE ) {
|
|
FLSH_BITS( LenPre[len] );
|
|
}
|
|
else {
|
|
FLSH_BITS( 8 );
|
|
do {
|
|
if ( PEEK_BITS( 1 ) ) len = TreeRight[len];
|
|
else len = TreeLeft [len];
|
|
FLSH_BITS( 1 );
|
|
} while ( MAX_PRE < len );
|
|
}
|
|
if ( len <= 2 ) {
|
|
if ( len == 0 ) {
|
|
len = 1;
|
|
}
|
|
else if ( len == 1 ) {
|
|
len = PEEK_BITS(4)+3; FLSH_BITS(4);
|
|
}
|
|
else {
|
|
len = PEEK_BITS(BITS_CODE)+20; FLSH_BITS(BITS_CODE);
|
|
}
|
|
while ( 0 < len-- ) LenCode[i++] = 0;
|
|
}
|
|
else {
|
|
LenCode[i++] = len - 2;
|
|
}
|
|
}
|
|
while ( i <= MAX_CODE ) LenCode[i++] = 0;
|
|
if ( ! MakeTablLzh( MAX_CODE+1, LenCode, 12, TabCode ) ) {
|
|
ErrMsg = "literal/length code description corrupted";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* read the log_2 of offsets */
|
|
cnt2 = PEEK_BITS( BITS_LOG ); FLSH_BITS( BITS_LOG );
|
|
if ( cnt2 == 0 ) {
|
|
log = PEEK_BITS( BITS_LOG ); FLSH_BITS( BITS_LOG );
|
|
for ( i = 0; i < 256; i++ ) TabLog[i] = log;
|
|
for ( i = 0; i <= MAX_LOG; i++ ) LenLog[i] = 0;
|
|
}
|
|
else {
|
|
i = 0;
|
|
while ( i < cnt2 ) {
|
|
len = PEEK_BITS( 3 ); FLSH_BITS( 3 );
|
|
if ( len == 7 ) {
|
|
while ( PEEK_BITS( 1 ) ) { len++; FLSH_BITS( 1 ); }
|
|
FLSH_BITS( 1 );
|
|
}
|
|
LenLog[i++] = len;
|
|
}
|
|
while ( i <= MAX_LOG ) LenLog[i++] = 0;
|
|
if ( ! MakeTablLzh( MAX_LOG+1, LenLog, 8, TabLog ) ) {
|
|
ErrMsg = "log code description corrupted";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* read the codes */
|
|
while ( 0 < cnt-- ) {
|
|
|
|
/* try to decode the code the fast way */
|
|
code = TabCode[ PEEK_BITS( 12 ) ];
|
|
|
|
/* if this code needs more than 12 bits look it up in the tree */
|
|
if ( code <= MAX_CODE ) {
|
|
FLSH_BITS( LenCode[code] );
|
|
}
|
|
else {
|
|
FLSH_BITS( 12 );
|
|
do {
|
|
if ( PEEK_BITS( 1 ) ) code = TreeRight[code];
|
|
else code = TreeLeft [code];
|
|
FLSH_BITS( 1 );
|
|
} while ( MAX_CODE < code );
|
|
}
|
|
|
|
/* if the code is a literal, stuff it into the buffer */
|
|
if ( code <= MAX_LIT ) {
|
|
*cur++ = code;
|
|
crc = CRC_BYTE( crc, code );
|
|
if ( cur == end ) {
|
|
if ( BlckWritFile(BufFile,cur-BufFile) != cur-BufFile ) {
|
|
ErrMsg = "cannot write output file";
|
|
return 0;
|
|
}
|
|
cur = BufFile;
|
|
}
|
|
}
|
|
|
|
/* otherwise compute match length and offset and copy */
|
|
else {
|
|
len = code - (MAX_LIT+1) + MIN_LEN;
|
|
|
|
/* try to decodes the log_2 of the offset the fast way */
|
|
log = TabLog[ PEEK_BITS( 8 ) ];
|
|
/* if this log_2 needs more than 8 bits look in the tree */
|
|
if ( log <= MAX_LOG ) {
|
|
FLSH_BITS( LenLog[log] );
|
|
}
|
|
else {
|
|
FLSH_BITS( 8 );
|
|
do {
|
|
if ( PEEK_BITS( 1 ) ) log = TreeRight[log];
|
|
else log = TreeLeft [log];
|
|
FLSH_BITS( 1 );
|
|
} while ( MAX_LOG < log );
|
|
}
|
|
|
|
/* compute the offset */
|
|
if ( log == 0 ) {
|
|
off = 0;
|
|
}
|
|
else {
|
|
off = ((unsigned)1 << (log-1)) + PEEK_BITS( log-1 );
|
|
FLSH_BITS( log-1 );
|
|
}
|
|
|
|
/* copy the match (this accounts for ~ 50% of the time) */
|
|
pos = BufFile + (((cur-BufFile) - off - 1) & (MAX_OFF - 1));
|
|
if ( cur < end-len && pos < end-len ) {
|
|
stp = cur + len;
|
|
do {
|
|
code = *pos++;
|
|
crc = CRC_BYTE( crc, code );
|
|
*cur++ = code;
|
|
} while ( cur < stp );
|
|
}
|
|
else {
|
|
while ( 0 < len-- ) {
|
|
code = *pos++;
|
|
crc = CRC_BYTE( crc, code );
|
|
*cur++ = code;
|
|
if ( pos == end ) {
|
|
pos = BufFile;
|
|
}
|
|
if ( cur == end ) {
|
|
if ( BlckWritFile(BufFile,cur-BufFile)
|
|
!= cur-BufFile ) {
|
|
ErrMsg = "cannot write output file";
|
|
return 0;
|
|
}
|
|
cur = BufFile;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cnt = PEEK_BITS( 16 ); FLSH_BITS( 16 );
|
|
}
|
|
|
|
/* write out the rest of the buffer */
|
|
if ( BlckWritFile(BufFile,cur-BufFile) != cur-BufFile ) {
|
|
ErrMsg = "cannot write output file";
|
|
return 0;
|
|
}
|
|
|
|
/* indicate success */
|
|
Crc = crc;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F ListArch(<ver>,<arc>,<filec>,<files>) . . list the members of the archive
|
|
**
|
|
** 'ListArch' lists the members of the archive with the name <arc> that
|
|
** match one of the file name patterns '<files>[0] .. <files>[<filec>-1]'.
|
|
** If <ver> is 1, comments are also printed.
|
|
*/
|
|
unsigned long BeginMonth [12] = {
|
|
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
|
|
};
|
|
|
|
char NameMonth [12] [4] = {
|
|
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
|
|
};
|
|
|
|
int ListArch ( ver, arc, filec, files )
|
|
unsigned long ver;
|
|
char * arc;
|
|
unsigned long filec;
|
|
char * files [];
|
|
{
|
|
char arczoo [256]; /* <arc> with '.zoo' tacked on */
|
|
int chr; /* character from comment */
|
|
unsigned long i; /* loop variable */
|
|
|
|
/* try to open the archive under various names */
|
|
strcpy(arczoo,arc); strcat(arczoo,".zoo");
|
|
if ( OpenReadArch(arc) ) {
|
|
if ( ! DescReadArch() ) {
|
|
ClosReadArch();
|
|
if ( ! OpenReadArch(arczoo) || ! DescReadArch() ) {
|
|
printf("unzoo: found bad description in archive '%s'\n",arc);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if ( OpenReadArch(arczoo) ) {
|
|
if ( ! DescReadArch() ) {
|
|
printf("unzoo: found bad description in archive '%s'\n",arczoo);
|
|
return 0;
|
|
}
|
|
}
|
|
else {
|
|
printf("unzoo: could not open archive '%s'\n",arc);
|
|
return 0;
|
|
}
|
|
|
|
/* if present, print the archive comment */
|
|
if ( ver && Descript.sizcmt != 0 ) {
|
|
if ( ! GotoReadArch( Descript.poscmt ) ) {
|
|
printf("unzoo: cannot find comment in archive '%s'\n",arc);
|
|
return 0;
|
|
}
|
|
chr = '\n';
|
|
for ( i = 0; i < Descript.sizcmt; i++ ) {
|
|
if ( chr == '\n' ) printf("# ");
|
|
chr = ByteReadArch();
|
|
if ( chr == '\012' ) chr = '\n';
|
|
printf("%c",chr);
|
|
}
|
|
if ( chr != '\n' ) printf("\n");
|
|
fflush( stdout );
|
|
}
|
|
|
|
/* print the header */
|
|
printf("Length CF Size Now Date Time \n");
|
|
printf("-------- --- -------- --------- --------\n");
|
|
fflush( stdout );
|
|
|
|
/* loop over the members of the archive */
|
|
Entry.posnxt = Descript.posent;
|
|
while ( 1 ) {
|
|
|
|
/* read the directory entry for the next member */
|
|
if ( ! GotoReadArch( Entry.posnxt ) || ! EntrReadArch() ) {
|
|
printf("unzoo: found bad directory entry in archive '%s'\n",arc);
|
|
return 0;
|
|
}
|
|
if ( ! Entry.posnxt ) break;
|
|
|
|
/* skip members we don't care about */
|
|
if ( Entry.delete == 1 )
|
|
continue;
|
|
if ( filec == 0 && ! IsMatchName( "*", Entry.patw ) )
|
|
continue;
|
|
for ( i = 0; i < filec; i++ )
|
|
if ( IsMatchName( files[i], Entry.patv )
|
|
|| IsMatchName( files[i], Entry.patw ) )
|
|
break;
|
|
if ( filec != 0 && i == filec )
|
|
continue;
|
|
|
|
/* print the information about the member */
|
|
printf("%8lu %3lu%% %8lu %2lu %3s %02lu %02lu:%02lu:%02lu %s\n",
|
|
Entry.sizorg,
|
|
(100*(Entry.sizorg-Entry.siznow)+Entry.sizorg/2)
|
|
/ (Entry.sizorg != 0 ? Entry.sizorg : 1),
|
|
Entry.siznow,
|
|
Entry.day, NameMonth[Entry.month], Entry.year % 100,
|
|
Entry.hour, Entry.min, Entry.sec,
|
|
(ver ? Entry.patv : Entry.patw) );
|
|
fflush( stdout );
|
|
|
|
/* update the counts for the whole archive */
|
|
Descript.sizorg += Entry.sizorg;
|
|
Descript.siznow += Entry.siznow;
|
|
Descript.number += 1;
|
|
|
|
/* if present print the file comment */
|
|
if ( ver && Entry.sizcmt != 0 ) {
|
|
if ( ! GotoReadArch( Entry.poscmt ) ) {
|
|
printf("unzoo: cannot find comment in archive '%s'\n",arc);
|
|
return 0;
|
|
}
|
|
chr = '\n';
|
|
for ( i = 0; i < Entry.sizcmt; i++ ) {
|
|
if ( chr == '\n' ) printf("# ");
|
|
chr = ByteReadArch();
|
|
if ( chr == '\012' ) chr = '\n';
|
|
printf("%c",chr);
|
|
}
|
|
if ( chr != '\n' ) printf("\n");
|
|
}
|
|
fflush( stdout );
|
|
|
|
}
|
|
|
|
/* print the footer */
|
|
printf("-------- --- -------- --------- --------\n");
|
|
printf("%8lu %3lu%% %8lu %4lu files\n",
|
|
Descript.sizorg,
|
|
(100*(Descript.sizorg-Descript.siznow)+Descript.sizorg/2)
|
|
/ (Descript.sizorg != 0 ? Descript.sizorg : 1),
|
|
Descript.siznow,
|
|
Descript.number );
|
|
fflush( stdout );
|
|
|
|
/* close the archive file */
|
|
if ( ! ClosReadArch() ) {
|
|
printf("unzoo: could not close archive '%s'\n",arc);
|
|
return 0;
|
|
}
|
|
|
|
/* indicate success */
|
|
return 1;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F ExtrArch(<bim>,<out>,<ovr>,<pre>,<arc>,<filec>,<files>) . extract members
|
|
**
|
|
** 'ExtrArch' extracts the members of the archive with the name <arc> that
|
|
** match one of the file name patterns '<files>[0] .. <files>[<filec>-1]'.
|
|
** If <bim> is 0, members with comments starting with '!TEXT!' are extracted
|
|
** as text files and the other members are extracted as binary files; if it
|
|
** is 1, all members are extracted as text files; if it is 2, all members
|
|
** are extracted as binary files. If <out> is 0, no members are extracted
|
|
** and only tested for integrity; if it is 1, the members are printed to
|
|
** stdout, i.e., to the screen. and if it is 2, the members are extracted.
|
|
** If <ovr> is 0, members will not overwrite existing files; otherwise they
|
|
** will. <pre> is a prefix that is prepended to all path names.
|
|
*/
|
|
int ExtrArch ( bim, out, ovr, pre, arc, filec, files )
|
|
unsigned long bim;
|
|
unsigned long out;
|
|
unsigned long ovr;
|
|
char * pre;
|
|
char * arc;
|
|
unsigned long filec;
|
|
char * files [];
|
|
{
|
|
char arczoo [256]; /* <arc> with '.zoo' tacked on */
|
|
char ans [256]; /* to read the answer */
|
|
char patl [1024]; /* local name with prefix */
|
|
unsigned long bin; /* extraction mode text/binary */
|
|
unsigned long res; /* status of decoding */
|
|
unsigned long secs; /* seconds since 70/01/01 00:00:00 */
|
|
unsigned long i; /* loop variable */
|
|
|
|
/* try to open the archive under various names */
|
|
strcpy(arczoo,arc); strcat(arczoo,".zoo");
|
|
if ( OpenReadArch(arc) ) {
|
|
if ( ! DescReadArch() ) {
|
|
ClosReadArch();
|
|
if ( ! OpenReadArch(arczoo) || ! DescReadArch() ) {
|
|
printf("unzoo: found bad description in archive '%s'\n",arc);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if ( OpenReadArch(arczoo) ) {
|
|
if ( ! DescReadArch() ) {
|
|
printf("unzoo: found bad description in archive '%s'\n",arczoo);
|
|
return 0;
|
|
}
|
|
}
|
|
else {
|
|
printf("unzoo: could not open archive '%s'\n",arc);
|
|
return 0;
|
|
}
|
|
|
|
/* test if the archive has a comment starting with '!TEXT!' */
|
|
if ( bim == 0
|
|
&& 6 <= Descript.sizcmt && GotoReadArch( Descript.poscmt )
|
|
&& ByteReadArch() == '!' && ByteReadArch() == 'T'
|
|
&& ByteReadArch() == 'E' && ByteReadArch() == 'X'
|
|
&& ByteReadArch() == 'T' && ByteReadArch() == '!' )
|
|
bim = 1;
|
|
|
|
/* test if the archive has a comment starting with '!MACBINARY!' */
|
|
#ifdef SYS_IS_MAC_MPW
|
|
else if ( bim == 0
|
|
&& 11 <= Descript.sizcmt && GotoReadArch( Descript.poscmt )
|
|
&& ByteReadArch() == '!' && ByteReadArch() == 'M'
|
|
&& ByteReadArch() == 'A' && ByteReadArch() == 'C'
|
|
&& ByteReadArch() == 'B' && ByteReadArch() == 'I'
|
|
&& ByteReadArch() == 'N' && ByteReadArch() == 'A'
|
|
&& ByteReadArch() == 'R' && ByteReadArch() == 'Y'
|
|
&& ByteReadArch() == '!' )
|
|
bim = 3;
|
|
#endif
|
|
|
|
/* loop over the members of the archive */
|
|
Entry.posnxt = Descript.posent;
|
|
while ( 1 ) {
|
|
|
|
/* read the directory entry for the next member */
|
|
if ( ! GotoReadArch( Entry.posnxt ) || ! EntrReadArch() ) {
|
|
printf("unzoo: found bad directory entry in archive '%s'\n",arc);
|
|
return 0;
|
|
}
|
|
if ( ! Entry.posnxt ) break;
|
|
|
|
/* skip members we don't care about */
|
|
if ( Entry.delete == 1 )
|
|
continue;
|
|
if ( filec == 0 && ! IsMatchName( "*", Entry.patw ) )
|
|
continue;
|
|
for ( i = 0; i < filec; i++ )
|
|
if ( IsMatchName( files[i], Entry.patv )
|
|
|| IsMatchName( files[i], Entry.patw ) )
|
|
break;
|
|
if ( filec != 0 && i == filec )
|
|
continue;
|
|
|
|
/* check that we can decode this file */
|
|
if ( (2 < Entry.method) || (2 < Entry.majver)
|
|
|| (2 == Entry.majver && 1 < Entry.minver) ) {
|
|
printf("unzoo: unknown method, you need a later version\n");
|
|
continue;
|
|
}
|
|
|
|
/* check that such a file does not already exist */
|
|
strcpy( patl, pre ); strcat( patl, Entry.patl );
|
|
if ( out == 2 && ovr == 0 && OpenReadFile(patl,0L) ) {
|
|
ClosReadFile();
|
|
do {
|
|
printf("'%s' exists, overwrite it? (Yes/No/All/Ren): ",patl);
|
|
fflush( stdout );
|
|
if ( fgets( ans, sizeof(ans), stdin ) == (char*)0 )
|
|
return 0;
|
|
} while ( *ans!='y' && *ans!='n' && *ans!='a' && *ans!='r'
|
|
&& *ans!='Y' && *ans!='N' && *ans!='A' && *ans!='R' );
|
|
if ( *ans == 'n' || *ans == 'N' ) {
|
|
continue;
|
|
}
|
|
else if ( *ans == 'a' || *ans == 'A' ) {
|
|
ovr = 1;
|
|
}
|
|
else if ( *ans == 'r' || *ans == 'R' ) {
|
|
do {
|
|
printf("enter a new local path name: ");
|
|
fflush( stdout );
|
|
if ( fgets( patl, sizeof(patl), stdin ) == (char*)0 )
|
|
return 0;
|
|
for ( i = 0; patl[i] != '\0' && patl[i] != '\n'; i++ ) ;
|
|
patl[i] = '\0';
|
|
} while ( OpenReadFile(patl,0L) && ClosReadFile() );
|
|
}
|
|
}
|
|
|
|
/* decide whether or not we want to open the file binary */
|
|
if ( bim == 0
|
|
&& 6 <= Entry.sizcmt && GotoReadArch( Entry.poscmt )
|
|
&& ByteReadArch() == '!' && ByteReadArch() == 'T'
|
|
&& ByteReadArch() == 'E' && ByteReadArch() == 'X'
|
|
&& ByteReadArch() == 'T' && ByteReadArch() == '!' )
|
|
bin = 1;
|
|
#ifdef SYS_IS_MAC_MPW
|
|
else if ( bim == 0
|
|
&& 11 <= Entry.sizcmt && GotoReadArch( Entry.poscmt )
|
|
&& ByteReadArch() == '!' && ByteReadArch() == 'M'
|
|
&& ByteReadArch() == 'A' && ByteReadArch() == 'C'
|
|
&& ByteReadArch() == 'B' && ByteReadArch() == 'I'
|
|
&& ByteReadArch() == 'N' && ByteReadArch() == 'A'
|
|
&& ByteReadArch() == 'R' && ByteReadArch() == 'Y'
|
|
&& ByteReadArch() == '!' )
|
|
bin = 3;
|
|
#endif
|
|
else if ( bim == 0 )
|
|
bin = 2;
|
|
else
|
|
bin = bim;
|
|
|
|
/* open the file for creation */
|
|
if ( out == 2 && ! OpenWritFile(patl,bin)
|
|
#ifdef MAKE_DIRE
|
|
&& (! MakeDirs(pre,Entry.diru) || ! OpenWritFile(patl,bin))
|
|
#endif
|
|
) {
|
|
printf("unzoo: '%s' cannot be created, ",patl);
|
|
#ifndef MAKE_DIRE
|
|
if ( Entry.dirl[0] != '\0' )
|
|
printf("check that the directory '%s' exists\n",Entry.dirl);
|
|
else
|
|
printf("check the permissions\n");
|
|
#else
|
|
printf("check the permissions\n");
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
/* or ``open'' stdout for printing */
|
|
if ( out == 1 )
|
|
OpenWritFile( (char*)0, 0L );
|
|
|
|
/* decode the file */
|
|
if ( ! GotoReadArch( Entry.posdat ) ) {
|
|
printf("unzoo: cannot find data in archive '%s'\n",arc);
|
|
return 0;
|
|
}
|
|
res = 0;
|
|
ErrMsg = "this should not happen";
|
|
if ( out == 0 || out == 2 )
|
|
printf("%s \t-- ",Entry.patl);
|
|
else
|
|
printf("********\n%s\n********\n",Entry.patl);
|
|
fflush( stdout );
|
|
if ( Entry.method == 0 ) res = DecodeCopy( Entry.siznow );
|
|
if ( Entry.method == 1 ) res = DecodeLzd();
|
|
if ( Entry.method == 2 ) res = DecodeLzh();
|
|
|
|
/* check that everything went ok */
|
|
if ( res == 0 ) printf("error, %s\n",ErrMsg);
|
|
else if ( Crc != Entry.crcdat ) printf("error, CRC failed\n");
|
|
else if ( out == 2 && bin == 1 ) printf("extracted as text\n");
|
|
else if ( out == 2 && bin == 2 ) printf("extracted as binary\n");
|
|
#ifdef SYS_IS_MAC_MPW
|
|
else if ( out == 2 && bin == 3 ) printf("extracted as MacBinary\n");
|
|
#endif
|
|
else if ( out == 0 ) printf("tested\n");
|
|
fflush( stdout );
|
|
|
|
/* close the file after extraction */
|
|
if ( out == 1 || out == 2 )
|
|
ClosWritFile();
|
|
|
|
/* set the file time, evt. correct for timezone of packing system */
|
|
secs = 24*60*60L*(365*(Entry.year - 70)
|
|
+ BeginMonth[Entry.month]
|
|
+ Entry.day - 1
|
|
+ (Entry.year - 69) / 4
|
|
+ (Entry.year % 4 == 0 && 1 < Entry.month)
|
|
- (Entry.year + 299) / 400
|
|
- (Entry.year % 400 == 100 && 1 < Entry.month))
|
|
+60*60L*Entry.hour + 60L*Entry.min + Entry.sec;
|
|
if ( Entry.timzon < 127 ) secs += 15*60*(Entry.timzon );
|
|
else if ( 127 < Entry.timzon ) secs += 15*60*(Entry.timzon - 256);
|
|
if ( out == 2 ) {
|
|
if ( ! SETF_TIME( patl, secs ) )
|
|
printf("unzoo: '%s' could not set the times\n",patl);
|
|
}
|
|
|
|
/* set the file permissions */
|
|
if ( out == 2 && (Entry.permis >> 22) == 1 ) {
|
|
if ( ! SETF_PERM( patl, Entry.permis ) )
|
|
printf("unzoo: '%s' could not set the permissions\n",patl);
|
|
}
|
|
|
|
}
|
|
|
|
/* close the archive file */
|
|
if ( ! ClosReadArch() ) {
|
|
printf("unzoo: could not close the archive '%s'\n",arc);
|
|
return 0;
|
|
}
|
|
|
|
/* indicate success */
|
|
return 1;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F HelpArch() . . . . . . . . . . . . . . . . . . . . . . . print some help
|
|
**
|
|
** 'HelpArch' prints some help about 'unzoo'.
|
|
*/
|
|
int HelpArch ()
|
|
{
|
|
printf("unzoo -- a zoo archive extractor by Martin Schoenert\n");
|
|
printf(" ($Id: unzoo.c,v 4.4 2000/05/29 08:56:57 sal Exp $)\n");
|
|
printf(" based on 'booz' version 2.0 by Rahul Dhesi\n");
|
|
printf("\n");
|
|
printf("unzoo [-l] [-v] <archive>[.zoo] [<file>..]\n");
|
|
printf(" list the members of the archive\n");
|
|
printf(" -v: list also the generation numbers and the comments\n");
|
|
printf(" <file>: list only files matching at least one pattern,\n");
|
|
printf(" '?' matches any char, '*' matches any string.\n");
|
|
printf("\n");
|
|
printf("unzoo -x [-abnpo] [-j <prefix>] <archive>[.zoo] [<file>..]\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");
|
|
printf(" -b: extract all members as binary files ");
|
|
printf("(even those with !TEXT! comments)\n");
|
|
printf(" -n: extract no members, only test the integrity\n");
|
|
printf(" -p: extract to stdout\n");
|
|
printf(" -o: extract over existing files\n");
|
|
printf(" -j: extract to '<prefix><membername>'\n");
|
|
printf(" <file>: extract only files matching at least one pattern,\n");
|
|
printf(" '?' matches any char, '*' matches any string.\n");
|
|
return 1;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
**
|
|
*F main(<argc>,<argv>) . . . . . . . . . . . . . . . . . . . . main program
|
|
**
|
|
** 'main' is the main program, it decodes the arguments and then calls the
|
|
** appropriate function.
|
|
*/
|
|
int main ( argc, argv )
|
|
int argc;
|
|
char * argv [];
|
|
{
|
|
unsigned long res; /* result of command */
|
|
unsigned long cmd; /* command help/list/extract */
|
|
unsigned long ver; /* list verbose option */
|
|
unsigned long bim; /* extraction mode option */
|
|
unsigned long out; /* output destination option */
|
|
unsigned long ovr; /* overwrite file option */
|
|
char * pre; /* prefix to prepend to path names */
|
|
char argl [256]; /* interactive command line */
|
|
int argd; /* interactive command count */
|
|
char * argw [256]; /* interactive command vector */
|
|
char * p; /* loop variable */
|
|
|
|
|
|
#ifdef SYS_IS_MAC_THC
|
|
SIOUXSettings.autocloseonquit = 1;
|
|
SIOUXSettings.asktosaveonclose = 0;
|
|
#endif
|
|
|
|
/* repeat until the user enters an empty line */
|
|
InitCrc();
|
|
IsSpec['\0'] = 1; IsSpec[';'] = 1;
|
|
argd = 1;
|
|
do {
|
|
|
|
/* scan the command line arguments */
|
|
cmd = 1; ver = 0; bim = 0; out = 2; ovr = 0;
|
|
pre = "";
|
|
while ( 1 < argc && argv[1][0] == '-' ) {
|
|
if ( argv[1][2] != '\0' ) cmd = 0;
|
|
switch ( argv[1][1] ) {
|
|
case 'l': case 'L': if ( cmd != 0 ) cmd = 1; break;
|
|
case 'v': case 'V': if ( cmd != 1 ) cmd = 0; ver = 1; break;
|
|
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 '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;
|
|
case 'j': case 'J': if ( argc == 2 ) { cmd = 0; break; }
|
|
pre = argv[2]; argc--; argv++;
|
|
break;
|
|
default: cmd = 0; break;
|
|
}
|
|
argc--; argv++;
|
|
}
|
|
|
|
/* execute the command or print help */
|
|
if ( cmd == 1 && 1 < argc )
|
|
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],
|
|
(unsigned long)argc-2, argv+2 );
|
|
else
|
|
res = HelpArch();
|
|
|
|
/* in interactive mode read another line */
|
|
if ( 1 < argd || argc <= 1 ) {
|
|
|
|
/* read a command line */
|
|
printf("\nEnter a command line or an empty line to quit:\n");
|
|
fflush( stdout );
|
|
if ( fgets( argl, sizeof(argl), stdin ) == (char*)0 ) break;
|
|
#ifdef SYS_IdfgdfsgasfgdsgfdS_MAC_THC
|
|
if ( *argl == '\n' && argl[1]=='\0') break;
|
|
#endif
|
|
/* parse the command line into argc */
|
|
argd = 1;
|
|
p = argl;
|
|
while ( *p==' ' || *p=='\t' || *p=='\n' ) *p++ = '\0';
|
|
while ( *p != '\0' ) {
|
|
argw[argd++] = p;
|
|
while ( *p!=' ' && *p!='\t' && *p!='\n' && *p!='\0' ) p++;
|
|
while ( *p==' ' || *p=='\t' || *p=='\n' ) *p++ = '\0';
|
|
}
|
|
argc = argd; argv = argw;
|
|
|
|
}
|
|
|
|
} while ( 1 < argd );
|
|
|
|
/* just to please lint */
|
|
return ! res;
|
|
}
|
|
|
|
|
|
|