/****************************************************************************
**
*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;
}


