1 /****************************************************************************
2 **
3 *A  unzoo.c                     Tools                        Martin Schoenert
4 **
5 *H  @(#)$Id: unzoo.c,v 4.4 2000/05/29 08:56:57 sal Exp $
6 **
7 *Y  This file is in the Public Domain.
8 **
9 **  SYNTAX
10 **
11 **  'unzoo'
12 **  'unzoo [-l] [-v] <archive>[.zoo] [<file>..]'
13 **  'unzoo -x [-abnpo] <archive>[.zoo] [<file>..]'
14 **
15 **  DESCRIPTION
16 **
17 **  'unzoo' is  a zoo  archive extractor.   A zoo archive   is  a  file  that
18 **  contains several files, called its members, usually in compressed form to
19 **  save space.  'unzoo' can list all or  selected members or  extract all or
20 **  selected members, i.e.,  uncompress them and write   them  to files.   It
21 **  cannot add new members or  delete  members.  For this   you need the  zoo
22 **  archiver, called 'zoo', written by Rahul Dhesi.
23 **
24 **  If you call 'unzoo'  with no arguments, it will  first print a summary of
25 **  the commands and  then prompt for  command lines interactively, until you
26 **  enter an empty line.  This is useful  on systems  that do not support the
27 **  notion of command line arguments such as the Macintosh.
28 **
29 **  If you call  'unzoo' with the  '-l' option,  it lists the  members in the
30 **  archive <archive>.   For each member 'unzoo'   prints  the size  that the
31 **  extracted file  would  have, the  compression factor,  the  size that the
32 **  member occupies in the archive (not  counting  the  space needed to store
33 **  the attributes such as the path name of the file), the date and time when
34 **  the files were last modified, and finally  the path name itself.  Finally
35 **  'unzoo' prints a grand total for the  file sizes, the compression factor,
36 **  and the member sizes.
37 **
38 **  The '-v' suboption causes 'unzoo' to append to each path name,  separated
39 **  by a ';', the generation number of the member,  where higher numbers mean
40 **  later generations.  Members for which generations are disabled are listed
41 **  with  ';0'.  Also 'unzoo'   will print the  comments associated  with the
42 **  archive itself or the members, preceeded by the string '# '.
43 **
44 **  If you call 'unzoo' with the '-x' option,  it extracts the  members  from
45 **  the archive <archive>.  Members are  stored with a  full path name in the
46 **  archive and if the operating system supports this, they will be extracted
47 **  into   appropriate subdirectories,   which will   be  created on  demand.
48 **  The members are usually  extracted as binary files,  with no translation.
49 **  However, if a member has a  comment that starts with the string '!TEXT!',
50 **  it is  extracted as a  text file, i.e.,  it will be  translated from  the
51 **  universal text file format (with <lf> as line separator as under UNIX) to
52 **  the local text file format (e.g., with <cr>/<lf> as separator under DOS).
53 **  If the archive  itself has a  comment that starts with  '!TEXT!' then all
54 **  members will be extracted as text files, even those that have no comment.
55 **  For each member the name is printed followed by  '-- extracted as binary'
56 **  or '-- extracted as text' when the member has been completely extracted.
57 **
58 **  The '-a' suboption causes  'unzoo' to extract all members  as text files,
59 **  even if they have no comment starting with  '!TEXT!'.
60 **
61 **  The '-b' suboption causes 'unzoo' to extract all members as binary files,
62 **  even if they have a comment starting with  '!TEXT!'.
63 **
64 **  The '-n' suboption causes 'unzoo' to suppress writing the files.  You use
65 **  this suboption  to test the integrity  of the archive  without extracting
66 **  the members.  For each member the name is printed followed by '-- tested'
67 **  if the member is intact or by '-- error, CRC failed' if it is not.
68 **
69 **  The '-p' suboption causes 'unzoo' to print the files to stdout instead of
70 **  writing them to files.
71 **
72 **  The '-o'  suboption causes 'unzoo'   to overwrite existing  files without
73 **  asking  you for confirmation.   The  default is  to ask for  confirmation
74 **  '<file> exists, overwrite it? (Yes/No/All/Ren)'.   To this you can answer
75 **  with 'y' to overwrite the  file, 'n' to skip  extraction of the file, 'a'
76 **  to overwrite this and all following files, or 'r' to enter a new name for
77 **  the file.  'unzoo' will never overwrite existing read-only files.
78 **
79 **  The '-j <prefix>' suboption causes 'unzoo' to prepend the string <prefix>
80 **  to  all path names for  the members  before  they  are extracted.  So for
81 **  example if an archive contains absolute  path names under  UNIX,  '-j ./'
82 **  can be used to convert them to relative pathnames.   This option  is also
83 **  useful  on  the Macintosh where   you start 'unzoo' by clicking,  because
84 **  then the current directory will be the one where 'unzoo' is,  not the one
85 **  where the  archive is.   Note  that the  directory  <prefix> must  exist,
86 **  'unzoo' will not create it on demand.
87 **
88 **  If no  <files>  argument is given all members  are  listed or  extracted.
89 **  If  one or  more <files>  arguments are given,  only members whose  names
90 **  match at least one of  the  <files> patterns  are  listed  or  extracted.
91 **  <files> can  contain the wildcard   '?', which  matches any character  in
92 **  names, and '*', which  matches any number  of characters  in names.  When
93 **  you pass the <files> arguments on the command  line you will usually have
94 **  to quote them to keep the shell from trying to expand them.
95 **
96 **  Usually 'unzoo' will  only list or extract the  latest generation of each
97 **  member.  But if you append ';<nr>' to a path  name pattern the generation
98 **  with the number <nr> is listed or extracted.  <nr> itself can contain the
99 **  wildcard characters '?' and '*', so appending ';*' to a path name pattern
100 **  causes all generations to be listed or extracted.
101 **
102 **
103 **  COMPATIBILITY
104 **
105 **  'unzoo'  is based heavily on the 'booz' archive extractor by Rahul Dhesi.
106 **  I basically stuffed everything in one file (so  no 'Makefile' is needed),
107 **  cleaned it up (so that it is now more portable and  a little bit faster),
108 **  and added the  support for  long file names,  directories,  and comments.
109 **
110 **  'unzoo' differs in some details from  'booz' and the zoo archiver  'zoo'.
111 **
112 **  'unzoo' can  only list  and extract members   from archives, like 'booz'.
113 **  'zoo' can also add members, delete members, etc.
114 **
115 **  'unzoo' can extract members as text files, converting from universal text
116 **  format to the local text format,  if the '-a' option is given or the '-b'
117 **  option is not given and the  member has a comment starting with '!TEXT!'.
118 **  So in the absence of the '-a' option and comments starting with '!TEXT!',
119 **  'unzoo' behaves like  'zoo' and 'booz',  which always extract as  binary.
120 **  But  'unzoo' can  correctly extract  text files from  archives that  were
121 **  created under UNIX (or other systems using the universal text format) and
122 **  extended with '!TEXT!' comments on systems such as DOS, VMS, Macintosh.
123 **
124 **  'unzoo' can handle  long names, which it converts  in  a system dependent
125 **  manner to local  names, like  'zoo'  (this may not   be available on  all
126 **  systems).  'booz' always uses the short DOS format names.
127 **
128 **  'unzoo' extracts  members  into  subdirectories, which  it  automatically
129 **  creates, like 'zoo' (this  may not be available on  all systems).  'booz'
130 **  always extracts all members into the current directory.
131 **
132 **  'unzoo'  can handle comments and generations in the  archive, like 'zoo'.
133 **  'booz' ignores all comments and generations.
134 **
135 **  'unzoo' cannot handle  members compressed with  the old method, only with
136 **  the new  high method or  not compressed  at all.   'zoo' and  'booz' also
137 **  handle members compress with the old method.  This shall be fixed soon.
138 **
139 **  'unzoo' can handle archives in  binary format under  VMS, i.e., it is not
140 **  necessary to convert  them to stream linefeed  format  with 'bilf' first.
141 **  'zoo' and 'booz' require this conversion.
142 **
143 **  'unzoo' is somewhat faster than 'zoo' and 'booz'.
144 **
145 **  'unzoo' should be much easier to port than both 'zoo' and 'booz'.
146 **
147 **  COMPILATION
148 **
149 **  Under  UNIX  with the  standard  C compiler,  compile  'unzoo' as follows
150 **      cc  -o unzoo  -DSYS_IS_UNIX   -O  unzoo.c
151 **  If your UNIX has the 'mkdir' system call,  you may add  '-DSYS_HAS_MKDIR'
152 **  for a slightly faster executable.   BSD has it,  else try  'man 2 mkdir'.
153 **
154 **  Under  DOS  with the  DJGPP  GNU C compiler,  compile  'unzoo' as follows
155 **      gcc  -o unzoo.out  -DSYS_IS_DOS_DJGPP  -O2  unzoo.c
156 **      copy /b \djgpp\bin\go32.exe+unzoo.out unzoo.exe
157 **
158 **  Under TOS with the GNU compiler and unixmode, compile  'unzoo' as follows
159 **      gcc  -o unzoo.ttp  -DSYS_IS_TOS_GCC  -O2  unzoo.c
160 **
161 **  Under OS/2 2 with the emx development system, compile  'unzoo' as follows
162 **      gcc  -o unzoo.exe  -DSYS_IS_OS2_EMX  -Zomf -Zsys  -O2  unzoo.c
163 **  To create an executable that runs under OS/2 and DOS,  but which requires
164 **  the emx runtime, compile without the '-Zomf' and '-Zsys' options.
165 **
166 **  On a VAX running VMS with the DEC C compiler, compile  'unzoo' as follows
167 **      cc   unzoo/define=SYS_IS_VMS
168 **      link unzoo
169 **  Then perform the following global symbolic assignment
170 **      unzoo :== $<dev>:[<dir>]unzoo.exe
171 **  where  <dir> is the    name of the   directory  where you  have installed
172 **  'unzoo' and  <dev> is the device on which this directory is,  for example
173 **      unzoo :== $dia1:[progs.archivers]unzoo
174 **  You may want to put this symbolic assignment into your  'login.com' file.
175 **
176 **  On a  Macintosh  with  the  MPW C  compiler,  compile  'unzoo' as follows
177 **      C    -model far  -d SYS_IS_MAC_MPW  -opt on  unzoo.c
178 **      Link -model far -d -c '????' -t APPL unzoo.c.o -o unzoo   <continued>
179 **          "{CLibraries}"StdClib.o "{Libraries}"SIOW.o           <continued>
180 **          "{Libraries}"Runtime.o  "{Libraries}"Interface.o
181 **      Rez  -a "{RIncludes}"SIOW.r  -o unzoo
182 **  Afterwards choose the  'Get Info' command in the  finder 'File' menu  and
183 **  increase the  amount of memory  'unzoo' gets upon startup to  256 KBytes.
184 **  To  create a MPW  tool instead of a  standalone, link with creator 'MPS '
185 **  instead of '????', with type 'MPST' instead  of 'APPL' and with 'Stubs.o'
186 **  instead of 'SIOW.o'.  The  'Rez' command  is  not required for the  tool.
187 **  Alternatively choose the 'Create Build Commands...'  command from the MPW
188 **  'Build' menu to create a  makefile.  Edit it  and add '-d SYS_IS_MAC_MPW'
189 **  to the  compile command.  Choose the  'Build...' command from the 'Build'
190 **  menu to build 'unzoo'.
191 **
192 **  On  other systems with a C compiler,  try to  compile  'unzoo' as follows
193 **      cc  -o unzoo -DSYS_IS_GENERIC  -O  unzoo.c
194 **
195 **  PORTING
196 **
197 **  If this  does not work,  you must supply new   definitions for the macros
198 **  'OPEN_READ_ARCH',   'OPEN_READ_TEXT' and  'OPEN_WRIT_TEXT'.  If you  want
199 **  'unzoo' to keep long file  names, you must   supply a definition for  the
200 **  macro 'CONV_NAME'.  If  you want 'unzoo'  to extract into subdirectories,
201 **  you   must supply a  definition for  the macro 'CONV_DIRE'.   If you want
202 **  'unzoo' to automatically create directories, you must supply a definition
203 **  for the macro 'MAKE_DIR'.  If you want  'unzoo' to set the permissions of
204 **  extracted  members to those  recorded in the archive,  you must  supply a
205 **  definition for the macro 'SETF_PERM'.  Finally if you want 'unzoo' to set
206 **  the times of the extracted members to  the times recorded in the archive,
207 **  you must supply a definition for the  macro 'SETF_TIME'.  Everything else
208 **  should be system independent.
209 **
210 **  ACKNOWLEDGMENTS
211 **
212 **  Rahul Dhesi  wrote the  'zoo' archiver and the  'booz' archive extractor.
213 **  Haruhiko Okumura  wrote the  LZH code (originally for his 'ar' archiver).
214 **  David Schwaderer provided the CRC-16 calculation in PC Tech Journal 4/85.
215 **  Jeff Damens  wrote the name match code in 'booz' (originally for Kermit).
216 **  Harald Boegeholz  ported 'unzoo' to OS/2 with the emx development system.
217 **  Dave Bayer ported 'unzoo' to the Macintosh,  including Macbinary support.
218 **
219 **  HISTORY
220 *H  $Log: unzoo.c,v $
221 *H  Revision 4.4  2000/05/29 08:56:57  sal
222 *H  Remove all the \ continuation lines -- who needs the hassle.	SL
223 *H
224 *H  Revision 4.3  1999/10/27 08:51:11  sal
225 *H  Fix date problem on alphas (I hope)	SL
226 *H
227 *H  Revision 4.2  1999/05/26 09:27:03  gap
228 *H  burkhard: use fseek to access file comments; Mac version: several minor fixes
229 *H
230 *H  Revision 1.5  1994/01/21  13:32:32  mschoene
231 *H  added Mac support from Dave Bayer
232 *H
233 *H  Revision 1.4  1994/01/20  20:45:46  mschoene
234 *H  cleaned up determination of write mode
235 *H
236 *H  Revision 1.3  1993/12/02  12:43:12  mschoene
237 *H  added OS/2 support from Harald Boegeholz
238 *H
239 *H  Revision 1.2  1993/12/02  12:33:39  mschoene
240 *H  fixed several typos, renamed MS-DOS to DOS
241 *H
242 *H  Revision 1.1  1993/11/09  07:17:50  mschoene
243 *H  Initial revision
244 *H
245 */
246 #include        <stdio.h>
247 
248 #ifdef SYS_IS_UNIX
249 #include <sys/types.h>
250 #include <sys/stat.h>
251 #include <string.h>
252 #include <stdlib.h>
253 #include <utime.h>
254 #endif
255 
256 /****************************************************************************
257 **
258 *F  OPEN_READ_ARCH(<patl>)  . . . . . . . . . . . open an archive for reading
259 *F  CLOS_READ_ARCH()  . . . . . . . . . . . . . . . . . . .  close an archive
260 *F  BLCK_READ_ARCH(<blk>,<len>) . . . . . . . .  read a block from an archive
261 *F  RWND_READ_ARCH()  . . . . . . . . . . . reset file read position to start
262 *F  SEEK_READ_ARCH(pos) . . . . . . . . . . . . . . move read position to pos
263 **
264 **  'OPEN_READ_ARCH' returns 1 if the archive file with  the path name <patl>
265 **  (as specified   by the user  on the  command line)   could  be opened for
266 **  reading and   0  otherwise.  Because   archive  files are   binary files,
267 **  'OPEN_READ_ARCH' must open the file in binary mode.
268 **
269 **  'CLOS_READ_ARCH'  closes   the archive  file  opened  by 'OPEN_READ_ARCH'
270 **  again.
271 **
272 **  'BLCK_READ_ARCH' reads up  to  <len>  characters  from the   archive file
273 **  opened with 'OPEN_READ_ARCH'  into  the  blkfer  <blk>, and  returns  the
274 **  actual number of characters read.
275 **
276 **  This operation is  operating system  dependent  because the archive  file
277 **  must be opened in binary mode, so that for example no  <cr>/<lf> <-> <lf>
278 **  translation happens.  You must supply a definition for each new port.
279 */
280 #ifdef  SYS_IS_UNIX
281 FILE *          ReadArch;
282 #define OPEN_READ_ARCH(patl)    ((ReadArch = fopen( (patl), "r" )) != 0)
283 #define CLOS_READ_ARCH()        (fclose( ReadArch ) == 0)
284 #define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
285 #define RWND_READ_ARCH()        (fseek( ReadArch, 0, 0 ) == 0)
286 #define SEEK_READ_ARCH(pos)		(fseek( ReadArch, pos, SEEK_SET) == 0)
287 #endif
288 #ifdef  SYS_IS_DOS_DJGPP
289 FILE *          ReadArch;
290 #define OPEN_READ_ARCH(patl)    ((ReadArch = fopen( (patl), "rb" )) != 0)
291 #define CLOS_READ_ARCH()        (fclose( ReadArch ) == 0)
292 #define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
293 #define RWND_READ_ARCH()        (fseek( ReadArch, 0, 0 ) == 0)
294 #endif
295 #ifdef  SYS_IS_OS2_EMX
296 FILE *          ReadArch;
297 #define OPEN_READ_ARCH(patl)    ((ReadArch = fopen( (patl), "rb" )) != 0)
298 #define CLOS_READ_ARCH()        (fclose( ReadArch ) == 0)
299 #define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
300 #define RWND_READ_ARCH()        (fseek( ReadArch, 0, 0 ) == 0)
301 #endif
302 #ifdef  SYS_IS_TOS_GCC
303 FILE *          ReadArch;
304 #define OPEN_READ_ARCH(patl)    ((ReadArch = fopen( (patl), "rb" )) != 0)
305 #define CLOS_READ_ARCH()        (fclose( ReadArch ) == 0)
306 #define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
307 #define RWND_READ_ARCH()        (fseek( ReadArch, 0, 0 ) == 0)
308 #endif
309 #ifdef  SYS_IS_VMS
310 FILE *          ReadArch;
311 #define OPEN_READ_ARCH(patl)    ((ReadArch = fopen( (patl), "r" )) != 0)
312 #define CLOS_READ_ARCH()        (fclose( ReadArch ) == 0)
313 #define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
314 #define RWND_READ_ARCH()        (fseek( ReadArch, 0, 0 ) == 0)
315 #endif
316 #ifdef  SYS_IS_MAC_MPW
317 FILE *          ReadArch;
318 #define OPEN_READ_ARCH(patl)    ((ReadArch = fopen( (patl), "r") ) != 0)
319 #define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
320 #define CLOS_READ_ARCH()        (fclose( ReadArch ) == 0)
321 #define RWND_READ_ARCH()        (fseek( ReadArch, 0, 0 ) == 0)
322 #endif
323 #ifdef  SYS_IS_MAC_THC
324 #include <SIOUX.h>
325 FILE *          ReadArch;
326 #define OPEN_READ_ARCH(patl)    ((ReadArch = fopen( (patl), "rb") ) != 0)
327 #define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
328 #define CLOS_READ_ARCH()        (fclose( ReadArch ) == 0)
329 #define RWND_READ_ARCH()        (fseek( ReadArch, 0, SEEK_SET ) == 0)
330 #define SEEK_READ_ARCH(pos)		(fseek( ReadArch, pos, SEEK_SET) == 0)
331 #define SYS_IS_MAC_MPW          /* BH: use most parts of MPW port */
332 #endif
333 #ifdef  SYS_IS_GENERIC
334 FILE *          ReadArch;
335 #define OPEN_READ_ARCH(patl)    ((ReadArch = fopen( (patl), "r" )) != 0)
336 #define CLOS_READ_ARCH()        (fclose( ReadArch ) == 0)
337 #define BLCK_READ_ARCH(blk,len) fread( (blk), 1L, (len), ReadArch )
338 #define RWND_READ_ARCH()        (fseek( ReadArch, 0, 0 ) == 0)
339 #define SEEK_READ_ARCH(pos)		(fseek( ReadArch, pos, SEEK_SET) == 0)
340 #endif
341 #ifndef OPEN_READ_ARCH
342 #include        "You_must_specify_the_system.h"
343 #endif
344 
345 
346 /****************************************************************************
347 **
348 *F  OPEN_READ_TEXT(<patl>)  . . . . . . . . . . . . . open a file for reading
349 *F  CLOS_READ_TEXT()  . . . . . . . . . . . . . . . . . . . . .  close a file
350 *F  BLCK_READ_TEXT(<blk>,<len>) . . . . . . . . . .  read a block from a file
351 **
352 **  'OPEN_READ_TEXT' returns 1  if  the file with  the  path name  <patl> (as
353 **  specified by  the user on the command  line) could be opened  for reading
354 **  and 0 otherwise.   'OPEN_READ_TEXT' is  used  only for text files,  so it
355 **  should open the file in text mode.
356 **
357 **  'CLOS_READ_TEXT' closes the file opened by 'OPEN_READ_TEXT' again.
358 **
359 **  'BLCK_READ_TEXT' reads up  to <len> characters from  the file opened with
360 **  'OPEN_READ_TEXT' into the blkfer <blk>, and  returns the actual number of
361 **  characters read.
362 **
363 **  In 'unzoo' these functions are only used to test if a file exists.
364 **
365 **  This operation is operating system dependent because it may be neccessary
366 **  to translate between the local text format and the UNIX style text format
367 **  usually used in archives.   The default is to  use 'fopen', 'fread',  and
368 **  'fclose', which  should work everywhere  according  to the ANSI standard.
369 **  You may want to use 'open', 'read', and 'close' for better performance.
370 */
371 #ifndef OPEN_READ_TEXT
372 FILE *          ReadText;
373 #define OPEN_READ_TEXT(patl)    ((ReadText = fopen( (patl), "r" )) != 0)
374 #define CLOS_READ_TEXT()        (fclose( ReadText ) == 0)
375 #define BLCK_READ_TEXT(blk,len) fread( (blk), 1L, (len), ReadText )
376 #endif
377 
378 
379 /****************************************************************************
380 **
381 *F  OPEN_WRIT_TEXT(<patl>)  . . . . . . . . . . . . . open a file for writing
382 *F  CLOS_WRIT_TEXT()  . . . . . . . . . . . . . . . . . . . . .  close a file
383 *F  BLCK_WRIT_TEXT(<blk>,<len>) . . . . . . . . . . . write a block to a file
384 **
385 **  'OPEN_WRIT_TEXT'   returns 1 if the  file  with the path  name <patl> (as
386 **  specified by the  user on the command  line) could be  opened for writing
387 **  and  0 otherwise.  'OPEN_WRIT_TEXT'  is used only for  text  files, so it
388 **  must open the file in text mode.
389 **
390 **  'CLOS_WRIT_TEXT' closes the file opened by 'OPEN_WRIT_TEXT' again.
391 **
392 **  'BLCK_WRIT_TEXT' writes up  to <len> characters  from <blk> into the file
393 **  opened with 'OPEN_WRIT_TEXT', and returns the actual number of characters
394 **  written.
395 **
396 **  This operation is operating system dependent because it may be neccessary
397 **  to translate between the UNIX style text format  usually used in archives
398 **  and the local text format.  The default is to  use 'fopen', 'fwrite', and
399 **  'fclose', which should work   everywhere according to the  ANSI standard.
400 **  You may want to use 'open', 'write', and 'close' for better performance.
401 */
402 #ifdef  SYS_IS_MAC_MPW
403 #ifndef  SYS_IS_MAC_THC
404 FILE *          WritText;
405 #define OPEN_WRIT_TEXT(patl)    MacOpenWritText( (patl) )
406 #define CLOS_WRIT_TEXT()        MacClosWritText()
407 #define BLCK_WRIT_TEXT(blk,len) MacBlckWritText( (blk), (len) )
408 #else
409 FILE *          WritText;
410 #define OPEN_WRIT_TEXT(patl)    MacOpenWritText( (patl) )
411 #define CLOS_WRIT_TEXT()        (fclose( WritText ) == 0)
412 #define BLCK_WRIT_TEXT(blk,len) fwrite( (blk), 1L, (len), WritText )
413 #endif
414 #endif
415 #ifndef OPEN_WRIT_TEXT
416 FILE *          WritText;
417 #define OPEN_WRIT_TEXT(patl)    ((WritText = fopen( (patl), "w" )) != 0)
418 #define CLOS_WRIT_TEXT()        (fclose( WritText ) == 0)
419 #define BLCK_WRIT_TEXT(blk,len) fwrite( (blk), 1L, (len), WritText )
420 #endif
421 
422 
423 /****************************************************************************
424 **
425 *F  OPEN_READ_BINR(<patl>)  . . . . . . . . . . . . . open a file for reading
426 *F  CLOS_READ_BINR()  . . . . . . . . . . . . . . . . . . . . .  close a file
427 *F  BLCK_READ_BINR(<blk>,<len>) . . . . . . . . . .  read a block from a file
428 **
429 **  'OPEN_READ_BINR'  returns 1 if   the file with  the  path name <patl> (as
430 **  specified by  the user on the  command line) could  be opened for reading
431 **  and 0  otherwise.  'OPEN_READ_BINR' is used only  for binary files, so it
432 **  should open the file in binary mode.
433 **
434 **  'CLOS_READ_BINR' closes the file opened by 'OPEN_READ_BINR' again.
435 **
436 **  'BLCK_READ_BINR' reads up  to <len> characters from  the file opened with
437 **  'OPEN_READ_BINR' into the blkfer <blk>, and  returns the actual number of
438 **  characters read.
439 **
440 **  In 'unzoo' these functions are currently not used at all.
441 **
442 **  This operation is  operating system dependent   because the file  must be
443 **  opened  in binary mode,  so  that   for  example  no <cr>/<lf>  <->  <lf>
444 **  translation happens.  The default   is to use  'fopen'  with  mode  'rb',
445 **  'fwrite', and 'fclose', with should work on most systems.
446 */
447 #ifndef OPEN_READ_BINR
448 FILE *          ReadBinr;
449 #define OPEN_READ_BINR(patl)    ((ReadBinr = fopen( (patl), "rb" )) != 0)
450 #define CLOS_READ_BINR()        (fclose( ReadBinr ) == 0)
451 #define BLCK_READ_BINR(blk,len) fread( (blk), 1L, (len), ReadBinr )
452 #endif
453 
454 
455 /****************************************************************************
456 **
457 *F  OPEN_WRIT_BINR(<patl>)  . . . . . . . . . . . . . open a file for writing
458 *F  CLOS_WRIT_BINR()  . . . . . . . . . . . . . . . . . . . . .  close a file
459 *F  BLCK_WRIT_BINR(<blk>,<len>) . . . . . . . . . . . write a block to a file
460 **
461 **  'OPEN_WRIT_BINR' returns 1   if the file  with the  path name <patl>  (as
462 **  specified  by the user  on the command line) could  be opened for writing
463 **  and 0 otherwise.   'OPEN_WRIT_BINR' is used  only for binary files, so it
464 **  must open the file in binary mode.
465 **
466 **  'CLOS_WRIT_BINR' closes the file opened by 'OPEN_WRIT_BINR' again.
467 **
468 **  'BLCK_WRIT_BINR' writes up  to <len> characters  from <blk> into the file
469 **  opened with 'OPEN_WRIT_BINR', and returns the actual number of characters
470 **  written.
471 **
472 **  This  operation is operating  system dependent  because the  file must be
473 **  opened  in   binary mode, so  that  for  example no   <cr>/<lf>  <-> <lf>
474 **  translation happens.   The default is   to use 'fopen'  with  mode  'wb',
475 **  'fwrite', and  'fclose', with should   work  on most systems.  You   must
476 **  supply a definition is this does not work and you want 'unzoo' to extract
477 **  binary files.
478 */
479 #ifdef  SYS_IS_VMS
480 #include        <file.h>
481 long            WritBinr;
482 #define OPEN_WRIT_BINR(patl)    ((WritBinr = creat( (patl), 0, "rfm=fix", "mrs=512" )) != -1)
483 #define BLCK_WRIT_BINR(blk,len) VmsBlckWritBinr( WritBinr, (blk), (len) )
484 #define CLOS_WRIT_BINR()        (close( WritBinr ) == 0)
485 #endif
486 #ifdef  SYS_IS_MAC_THC
487 FILE *          WritBinr;
488 #define OPEN_WRIT_BINR(patl)    MacOpenWritBinr (patl)
489 #define BLCK_WRIT_BINR(blk,len) fwrite( (blk), 1L, (len), WritBinr )
490 #define CLOS_WRIT_BINR()        (fclose( WritBinr ) == 0)
491 #endif
492 #ifndef OPEN_WRIT_BINR
493 FILE *          WritBinr;
494 #define OPEN_WRIT_BINR(patl)    ((WritBinr = fopen( (patl), "wb" )) != 0)
495 #define BLCK_WRIT_BINR(blk,len) fwrite( (blk), 1L, (len), WritBinr )
496 #define CLOS_WRIT_BINR()        (fclose( WritBinr ) == 0)
497 #endif
498 
499 
500 /****************************************************************************
501 **
502 *F  CONV_NAME(<naml>,<namu>)  . . . . . . . . . . . . . . convert a file name
503 **
504 **  'CONV_NAME'  returns in <naml> the  universal file name <namu>  converted
505 **  to the local format.  <namu>  may contain  uppercase, lowercase,  and all
506 **  special characters, and may be up to 255 characters long.
507 **
508 **  You must define this for a new port if you want  'unzoo' to keep the long
509 **  names instead of using the default local format for the file names, which
510 **  contains up to eight lowercase  characters before an optional dot  ('.'),
511 **  up to three characters after the dot, and no special characters.  You may
512 **  want to use the universal conversion function 'ConvName'.
513 */
514 #ifdef  SYS_IS_UNIX
515 #define CONV_NAME(naml,namu)    strcpy( (naml), (namu) )
516 #endif
517 #ifdef  SYS_IS_DOS_DJGPP
518 #define CONV_NAME(naml,namu)    ConvName( (naml), (namu), 8L, 3L, '_' )
519 #endif
520 #ifdef  SYS_IS_OS2_EMX
521 #define CONV_NAME(naml,namu)    strcpy( (naml), (namu) )
522 #endif
523 #ifdef  SYS_IS_TOS_GCC
524 #define CONV_NAME(naml,namu)    strcpy( (naml), (namu) )
525 #endif
526 #ifdef  SYS_IS_VMS
527 #define CONV_NAME(naml,namu)    ConvName( (naml), (namu), 39L, 39L, '_' )
528 #endif
529 #ifdef  SYS_IS_MAC_MPW
530 #define CONV_NAME(naml,namu)    strncpy( (naml), (namu), 31 ); naml[32] = '\0'
531 #endif
532 #ifndef CONV_NAME
533 #define CONV_NAME(naml,namu)    ConvName( (naml), (namu), 8L, 3L, 'x' )
534 #endif
535 
536 
537 /****************************************************************************
538 **
539 *F  CONV_DIRE(<dirl>,<diru>)  . . . . . . . . . . .  convert a directory name
540 **
541 **  'CONV_DIRE'  returns  in  <dirl>  the  universal  directory  name  <diru>
542 **  converted to the  local format.  <diru> contains an  arbitrary number  of
543 **  components separated by  slashes ('/'),  where each component may contain
544 **  uppercase,  lowercase,  and all special characters,  and may be up to 255
545 **  characters long.
546 **
547 **  You  must  define this  for a new   port if you  want  'unzoo' to extract
548 **  members into subdirectories, instead of  extracting  them to the  current
549 **  directory.    You may  want  to   use the  universal conversion  function
550 **  'ConvDire'.
551 */
552 #ifdef  SYS_IS_UNIX
553 #define CONV_DIRE(dirl,diru)    ConvDire((dirl),(diru),"/","/","","/","/")
554 #endif
555 #ifdef  SYS_IS_DOS_DJGPP
556 #define CONV_DIRE(dirl,diru)    ConvDire((dirl),(diru),"\\","\\","","\\","\\")
557 #endif
558 #ifdef  SYS_IS_OS2_EMX
559 #define CONV_DIRE(dirl,diru)    ConvDire((dirl),(diru),"/","/","","/","/")
560 #endif
561 #ifdef  SYS_IS_TOS_GCC
562 #define CONV_DIRE(dirl,diru)    ConvDire((dirl),(diru),"\\","\\","","\\","\\")
563 #endif
564 #ifdef  SYS_IS_VMS
565 #define CONV_DIRE(dirl,diru)    ConvDire((dirl),(diru),"[]","[","[.",".","]")
566 #endif
567 #ifdef  SYS_IS_MAC_MPW
568 #define CONV_DIRE(dirl,diru)    ConvDire((dirl),(diru),"","",":",":",":")
569 #endif
570 #ifndef CONV_DIRE
571 #define CONV_DIRE(dirl,diru)    ((dirl)[0]='\0',1)
572 #endif
573 
574 
575 /****************************************************************************
576 **
577 *F  MAKE_DIRE(<patl>) . . . . . . . . . . . . . . . . . . .  make a directory
578 **
579 **  'MAKE_DIRE' makes  the directory  with the local   path name  <patl>  (as
580 **  converted by 'CONV_NAME' and 'CONV_DIRE' with the prefix of 'MakeDirs').
581 **
582 **  You must define this for a new port  if you want 'unzoo' to automatically
583 **  create directories instead of requiring the user to create them.
584 */
585 #ifdef  SYS_IS_UNIX
586 #ifdef  SYS_HAS_MKDIR
587 #define MAKE_DIRE(patl)         mkdir( (patl), 0777L )
588 #else
589 char            Cmd [256];
590 #define MAKE_DIRE(patl)    (sprintf(Cmd,"/bin/mkdir %s",(patl)),!system(Cmd))
591 #endif
592 #endif
593 #ifdef  SYS_IS_DOS_DJGPP
594 #define MAKE_DIRE(patl)         mkdir( (patl), 0777L )
595 #endif
596 #ifdef  SYS_IS_OS2_EMX
597 #include        <stdlib.h>
598 #define MAKE_DIRE(patl)         mkdir( (patl), 0777L )
599 #endif
600 #ifdef  SYS_IS_TOS_GCC
601 #define MAKE_DIRE(patl)         mkdir( (patl), 0777L )
602 #endif
603 #ifdef  SYS_IS_VMS
604 #define MAKE_DIRE(patl)         VmsMakeDire( (patl) )
605 #endif
606 #ifdef  SYS_IS_MAC_MPW
607 #define MAKE_DIRE(patl)         MacMakeDire( (patl) )
608 #endif
609 
610 
611 /****************************************************************************
612 **
613 *F  SETF_TIME(<patl>,<secs>)  . . . . . . . . . . . change the time of a file
614 **
615 **  'SETF_TIME' changes the time of the file with  the local path name <patl>
616 **  (as converted  by 'CONV_NAME' and  'CONV_DIRE')  to <secs>, which  is the
617 **  number of seconds since 1970/01/01 00:00:00.
618 **
619 **  You  must define  this for  a new  port  if you  want 'unzoo'  to extract
620 **  members with the correct time as stored in the archive.
621 */
622 #ifdef  SYS_IS_UNIX
623 struct utimbuf Secs;
624 #define SETF_TIME(patl,secs)    (Secs.actime=Secs.modtime=(secs),!utime((patl),&Secs))
625 #endif
626 #ifdef  SYS_IS_DOS_DJGPP
627 unsigned long   Secs [2];
628 #define SETF_TIME(patl,secs)    (Secs[0]=Secs[1]=(secs),!utime((patl),Secs))
629 #endif
630 #ifdef  SYS_IS_OS2_EMX
631 #include        <sys/utime.h>
632 struct  utimbuf Secs;
633 #define SETF_TIME(patl,secs)    (Secs.actime=Secs.modtime=(secs),!utime((patl),&Secs))
634 #endif
635 #ifdef  SYS_IS_TOS_GCC
636 unsigned long   Secs [2];
637 #define SETF_TIME(patl,secs)    (Secs[0]=Secs[1]=(secs),!utime((patl),Secs))
638 #endif
639 #ifndef SETF_TIME
640 #define SETF_TIME(patl,secs)    (1)
641 #endif
642 
643 
644 /****************************************************************************
645 **
646 *F  SETF_PERM(<patl>,<mode>)  . . . . . . .  change the permissions of a file
647 **
648 **  'SETF_PERM' changes the permissions of the file with the  local path name
649 **  <patl> (as converted by 'CONV_NAME' and 'CONV_DIRE') to <mode>,  which is
650 **  a UNIX style mode word.
651 **
652 **  You  must define this  for a  new  port if  you want  'unzoo'  to extract
653 **  members with the permissions stored in the archive.
654 */
655 #ifdef  SYS_IS_UNIX
656 #define SETF_PERM(patl,mode)    (!chmod((patl),(int)(mode)))
657 #endif
658 #ifdef  SYS_IS_DOS_DJGPP
659 #define SETF_PERM(patl,mode)    (!chmod((patl),(int)(mode)))
660 #endif
661 #ifdef  SYS_IS_OS2_EMX
662 #include        <io.h>
663 #define SETF_PERM(patl,mode)    (!chmod((patl),(int)(mode)))
664 #endif
665 #ifdef  SYS_IS_TOS_GCC
666 #define SETF_PERM(patl,mode)    (!chmod((patl),(int)(mode)))
667 #endif
668 #ifndef SETF_PERM
669 #define SETF_PERM(patl,mode)    (1)
670 #endif
671 
672 
673 /****************************************************************************
674 **
675 *F  ConvName(...) . . . . . . . . . . . . convert a file name to local format
676 **
677 **  'ConvName( <naml>, <namu>, <pre>,<pst>,<rpl> )'
678 **
679 **  'ConvName' returns in <naml> the  universal file name <namu> converted to
680 **  the local format described by <pre>, <pst>, and <rpl>.
681 **
682 **  <pre> is the maximum number of characters  before the optional dot, <pst>
683 **  is the maximum number of characters after the optional  dot, and <rpl> is
684 **  the character that replaces special characters.
685 */
ConvName(naml,namu,pre,pst,rpl)686 int             ConvName ( naml, namu, pre, pst, rpl )
687     char *              naml;
688     char *              namu;
689     unsigned long       pre;
690     unsigned long       pst;
691     char                rpl;
692 {
693     char *              dotu;           /* position of last dot in <namu>  */
694     char *              l;              /* loop variable                   */
695     char *              u;              /* loop variable                   */
696 
697     /* find the final dot                                                  */
698     dotu = 0;
699     for ( u = namu; *u != '\0'; u++ )
700         if ( *u == '.' )
701             dotu = u;
702     if ( dotu == 0 )  dotu = u;
703 
704     /* copy the first part                                                 */
705     l = naml;
706     for ( u = namu; u < dotu && u < namu+pre; u++ ) {
707         if      ( 'a' <= *u && *u <= 'z' )  *l++ = *u;
708         else if ( 'A' <= *u && *u <= 'Z' )  *l++ = *u - 'A' + 'a';
709         else if ( '0' <= *u && *u <= '9' )  *l++ = *u;
710         else                                *l++ = rpl;
711     }
712 
713     /* the part before the dot may not be empty                            */
714     if ( l == naml )
715         *l++ = rpl;
716 
717     /* if the universal file name had no dot, thats it                     */
718     if ( *dotu == '\0' || pst == 0 ) {
719         *l = '\0';
720         return 1;
721     }
722 
723     /* copy the dot                                                        */
724     *l++ = '.';
725 
726     /* copy the remaining part                                             */
727     for ( u = dotu+1; *u && u < dotu+1+pst; u++ ) {
728         if      ( 'a' <= *u && *u <= 'z' )  *l++ = *u;
729         else if ( 'A' <= *u && *u <= 'Z' )  *l++ = *u - 'A' + 'a';
730         else if ( '0' <= *u && *u <= '9' )  *l++ = *u;
731         else                                *l++ = rpl;
732     }
733 
734     /* terminate the local name and indicate success                       */
735     *l = '\0';
736     return 1;
737 }
738 
739 
740 /****************************************************************************
741 **
742 *F  ConvDire(...) . . . . . . . . .  convert a directory name to local format
743 **
744 **  'ConvDire( <dirl>, <diru>, <root>,<abs>,<rel>,<sep>,<end> )'
745 **
746 **  'ConvDire'  returns  in  <dirl>  the  universal  directory  name   <diru>
747 **  converted to the  local format.  <diru> contains an  arbitrary number  of
748 **  components separated by  slashes ('/'),  where each component may contain
749 **  uppercase,  lowercase,  and all special characters,  and may be up to 255
750 **  characters long.
751 **
752 **  <root> is the string that is used for the root directory in local format.
753 **  <abs> is the string that starts absolute directory names in local format,
754 **  <rel> starts relative names, directory components are separated by <sep>,
755 **  and <end> separates the directory part and a proper file name.
756 **
757 **  If <diru> is the empty string, then 'ConvDire' returns in <dirl> also the
758 **  empty string, instead of '<rel><end>'.
759 */
ConvDire(dirl,diru,root,abs,rel,sep,end)760 int             ConvDire ( dirl, diru, root, abs, rel, sep, end )
761     char *              dirl;
762     char *              diru;
763     char *              root;
764     char *              abs;
765     char *              rel;
766     char *              sep;
767     char *              end;
768 {
769     char                namu [256];     /* file name part, univ.           */
770     char                naml [256];     /* file name part, local           */
771     char *              d;              /* loop variable                   */
772     char *              s;              /* loop variable                   */
773 
774     /* special case for the root directory                                 */
775     if ( *diru == '/' && diru[1] == '\0' ) {
776         for ( s = root; *s != '\0'; s++ )  *dirl++ = *s;
777         *dirl = '\0';
778         return 1;
779     }
780 
781     /* start the file name with <abs> or <rel>                             */
782     d = diru;
783     if ( *diru == '/' )
784         for ( d++, s = abs; *s != '\0'; s++ )  *dirl++ = *s;
785     else if ( *diru != '\0' )
786         for (      s = rel; *s != '\0'; s++ )  *dirl++ = *s;
787 
788     /* add the components of the directory part separated by <sep>         */
789     while ( *d != '\0' ) {
790         s = namu;
791         while ( *d != '\0' && *d != '/' )  *s++ = *d++;
792         *s = '\0';
793         CONV_NAME( naml, namu );
794         for ( s = naml; *s != '\0'; s++ )  *dirl++ = *s;
795         if ( *d == '/' )
796             for ( d++, s = sep; *s != '\0'; s++ )  *dirl++ = *s;
797     }
798 
799     /* add the divisor <end>                                               */
800     if ( *diru != '\0' )
801         for ( s = end; *s != '\0'; s++ )  *dirl++ = *s;
802 
803     /* terminate the file name and indicate success                        */
804     *dirl = '\0';
805     return 1;
806 }
807 
808 
809 /****************************************************************************
810 **
811 *F  VmsBlckWritBinr(<blk>,<len>)  .  write a block to a binary file under VMS
812 *F  VmsMakeDire(<patl>) . . . . . . . . . . . .  create a directory under VMS
813 **
814 **  'VmsBlckWritBinr' writes  the block  <blk> of  length  <len> to  the file
815 **  opened with 'OPEN_WRIT_BINR'.
816 **
817 **  'VmsMakeDire' creates a directory  under VMS.  It has  to change the path
818 **  name from '[<components>]<dirl>' to '[<components>.<dirl>]'.
819 */
820 #ifdef  SYS_IS_VMS
821 
VmsBlckWritBinr(blk,len)822 unsigned long   VmsBlckWritBinr ( blk, len )
823     unsigned char *     blk;
824     unsigned long       len;
825 {
826     unsigned char       buf [512];      /* local buffer (padded with 0)    */
827     long                i,  k,  l;      /* loop variables                  */
828 
829     /* write the full 512 byte blocks                                      */
830     for ( i = 0; i+512 < len; i += 512 ) {
831         if ( (l = write( WritBinr, blk+i, 512 )) != 512 )
832             return i + l;
833     }
834 
835     /* write an incomplete last block padded with 0                        */
836     for ( k = 0; k < 512; k++ )
837         buf[k] = (i+k < len ? blk[i+k] : 0);
838     if ( (l = write( WritBinr, buf, 512 )) != 512 )
839         return i + l;
840 
841     /* indicate success                                                    */
842     return len;
843 }
844 
VmsMakeDire(patl)845 int             VmsMakeDire ( patl )
846     char *              patl;
847 {
848     char *              p;
849 
850     /* replace the separator with a dot                                    */
851     for ( p = patl; *p != '\0' && *p != ']'; p++ )  ;
852     if ( *p == ']' )  *p = '.';
853 
854     /* append another separator                                            */
855     for ( ; *p != '\0'; p++ ) ;
856     *p++ = ']';
857     *p = '\0';
858 
859     /* make the directory and indicate success                             */
860     return mkdir( patl, 0 );
861 }
862 
863 #endif
864 
865 
866 /****************************************************************************
867 **
868 *F  MacOpenWritText(<patl>) . . . . .  open a text file for writing under MPW
869 *F  MacClosWritText() . . . . . . . . . . . . . . close a text file under MPW
870 *F  MacBlckWritText(<blk>,<len>)  . .  write a block to a text file under MPW
871 *F  OPEN_WRIT_MACB(<patl>)  . . . open a MacBinary file for writing under MPW
872 *F  CLOS_WRIT_MACB()  . . . . . . . . . . .  close a MacBinary file under MPW
873 *F  BLCK_WRIT_MACB(<blk>,<len>) . write a block to a MacBinary file under MPW
874 *F  MacMakeDire(<patl>) . . . . . . . . . . . .  create a directory under MPW
875 **
876 **  'MacBlckWritText' writes the block <blk> of length <len> to the text file
877 **  opened   with 'OPEN_WRIT_TEXT'.  It    converts <lf> ('\012') characters,
878 **  which represent <newline>  in universal text  format, to '\n' characters,
879 **  which represent <newline> in the system defined text format.
880 **
881 **  'MacMakeDire' creates  the directory  with local  path  name <patl>.  The
882 **  code comes from the Macintosh 'tar' port by Gail Zacharias.
883 */
884 #ifdef  SYS_IS_MAC_MPW
885 
886 #include        <Devices.h>
887 #include        <Files.h>
888 
889 #ifdef SYS_IS_MAC_THC
890 
MacOpenWritText(patl)891 int             MacOpenWritText ( patl )
892     char *              patl;
893 {
894     FileParam               fndrInfo;
895     char            	    patp [256];     /* <patl> as a Pascal string       */
896     int                		len;            /* length of <patp>                */
897 
898     /* open the file                                                       */
899     if ( ! (WritText = fopen( (patl), "w" )) )
900         return 0;
901 
902     /* convert <patl> from a C string to a Pascal string                   */
903     len = strlen( patl );
904     len = len < 256 ? len : 255;
905     patp[0] = len;
906     strncpy( patp+1, patl, len );
907 
908     /* set the file type to 'TEXT' and the creator to TeachText            */
909     fndrInfo.ioNamePtr   = (unsigned char*)patp;
910     fndrInfo.ioVRefNum   = 0;
911     fndrInfo.ioFVersNum  = 0;
912     fndrInfo.ioFDirIndex = 0;
913     if ( PBGetFInfo( (ParmBlkPtr)&fndrInfo, 0 ) ) {
914         return 0;
915     }
916     fndrInfo.ioFlFndrInfo.fdType    = 'TEXT';
917 	fndrInfo.ioFlFndrInfo.fdCreator = 'ttxt';
918      if ( PBSetFInfo( (ParmBlkPtr)&fndrInfo, 0 ) ) {
919         return 0;
920     }
921    /* indicate success                                                    */
922     return 1;
923 }
924 
MacOpenWritBinr(patl)925 int             MacOpenWritBinr ( patl )
926     char *              patl;
927 {
928     FileParam               fndrInfo;
929     char            	    patp [256];     /* <patl> as a Pascal string       */
930     int                		len;            /* length of <patp>                */
931 
932     /* open the file                                                       */
933     if ( ! (WritBinr = fopen( (patl), "wb" )) )
934         return 0;
935 
936 
937     /* convert <patl> from a C string to a Pascal string                   */
938     len = strlen( patl );
939     len = len < 256 ? len : 255;
940     patp[0] = len;
941     strncpy( patp+1, patl, len );
942 
943     /* set the file type and creator            */
944     fndrInfo.ioNamePtr   = (unsigned char*)patp;
945     fndrInfo.ioVRefNum   = 0;
946     fndrInfo.ioFVersNum  = 0;
947     fndrInfo.ioFDirIndex = 0;
948     if ( PBGetFInfo( (ParmBlkPtr)&fndrInfo, 0 ) ) {
949         return 0;
950     }
951     fndrInfo.ioFlFndrInfo.fdType    = 'BINA';
952 	fndrInfo.ioFlFndrInfo.fdCreator = '????';
953      if ( PBSetFInfo( (ParmBlkPtr)&fndrInfo, 0 ) ) {
954         return 0;
955     }
956    /* indicate success                                                    */
957     return 1;
958 }
959 
960 #endif
961 
962 #ifndef SYS_IS_MAC_THC
MacOpenWritText(patl)963 int             MacOpenWritText ( patl )
964     char *              patl;
965 {
966     FInfo               fndrInfo;
967 
968     /* open the file                                                       */
969     if ( ! (WritText = fopen( (patl), "w" )) )
970         return 0;
971 
972     /* set the file type to 'TEXT' and the creator to TeachText            */
973     getfinfo( patl, 0, &fndrInfo );
974     if ( fndrInfo.fdType == 0 )
975         fndrInfo.fdType    = 'TEXT';
976     if ( fndrInfo.fdCreator == 0 )
977         fndrInfo.fdCreator = 'ttxt';
978     setfinfo( patl, 0, &fndrInfo );
979     /* indicate success                                                    */
980     return 1;
981 }
982 #endif
983 
984 
MacClosWritText()985 int             MacClosWritText ()
986 {
987     return (fclose( WritText ) == 0);
988 }
989 
MacBlckWritText(blk,len)990 unsigned long   MacBlckWritText ( blk, len )
991     unsigned char *     blk;
992     unsigned long       len;
993 {
994     unsigned long       i;              /* loop variable                   */
995 
996     for ( i = 0; i < len; i++ ) {
997         if (fputc( (blk[i] != '\012' ? blk[i] : '\n'), WritText ) == EOF)
998             return i;
999     }
1000     return len;
1001 }
1002 
1003 char            WritName [256];         /* name of the file                */
1004 IOParam         WritIOPB;               /* IO parameter block              */
1005 FileParam       WritFIPB;               /* Finder Info parameter block     */
1006 unsigned long   WritPart;               /* current part of MacBinary file  */
1007 unsigned long   WritType;               /* type of file, e.g. 'TEXT'       */
1008 unsigned long   WritCrtr;               /* creator of file, e.g. 'ttxt'    */
1009 unsigned long   WritFlgs;               /* finder flags                    */
1010 unsigned long   WritCDat;               /* creation date of file           */
1011 unsigned long   WritMDat;               /* last modification date of file  */
1012 unsigned long   WritLDat;               /* nr. of bytes left in data fork  */
1013 unsigned long   WritLRsc;               /* nr. of bytes left in resource   */
1014 
OPEN_WRIT_MACB(patl)1015 int             OPEN_WRIT_MACB ( patl )
1016     char *              patl;
1017 {
1018     unsigned long       i;              /* loop variable                   */
1019 
1020     /* find the last semicolon                                             */
1021     for ( i = strlen(patl); 0 < i && patl[i] != ':'; i-- )
1022         ;
1023 
1024     /* copy the directory part to 'WritName'                               */
1025     WritName[0] = (0 < i ? i+1 : 0);
1026     for ( i = 1; i <= WritName[0]; i++ )
1027         WritName[i] = patl[i-1];
1028 
1029     /* indicate success                                                    */
1030     WritPart = 0;
1031     return 1;
1032 }
1033 
CLOS_WRIT_MACB()1034 int             CLOS_WRIT_MACB ()
1035 {
1036 
1037     /* first get the current settings                                      */
1038 #ifdef SYS_IS_MAC_THC
1039     WritFIPB.ioNamePtr   = (StringPtr)WritName;
1040 #else
1041     WritFIPB.ioNamePtr   = WritName;
1042 #endif
1043     WritFIPB.ioVRefNum   = 0;
1044     WritFIPB.ioFVersNum  = 0;
1045     WritFIPB.ioFDirIndex = 0;
1046     if ( PBGetFInfo( (ParmBlkPtr)&WritFIPB, 0 ) ) {
1047         return 0;
1048     }
1049 
1050     /* now set some fields to the values found in the MacBinary header     */
1051     WritFIPB.ioFlFndrInfo.fdType    = WritType;
1052     WritFIPB.ioFlFndrInfo.fdCreator = WritCrtr;
1053     WritFIPB.ioFlFndrInfo.fdFlags   = WritFlgs;
1054     WritFIPB.ioFlCrDat              = WritCDat;
1055     WritFIPB.ioFlMdDat              = WritMDat;
1056     if ( PBSetFInfo( (ParmBlkPtr)&WritFIPB, 0 ) ) {
1057         return 0;
1058     }
1059 
1060     /* indicate success                                                    */
1061     return 1;
1062 }
1063 
BLCK_WRIT_MACB(blk,len)1064 unsigned long   BLCK_WRIT_MACB ( blk, len )
1065     unsigned char *     blk;
1066     unsigned long       len;
1067 {
1068     unsigned long       cnt;            /* number of bytes written         */
1069     unsigned long       i;              /* loop variable                   */
1070 
1071     /* first comes the header (128 bytes long)                             */
1072     cnt = 0;
1073     if ( WritPart == 0 ) {
1074         for ( i = 1; i <= blk[1]; i++ )
1075             WritName[WritName[0]+i] = blk[i+1];
1076         WritName[0] += blk[1];
1077         WritType = (blk[65]<<24) + (blk[66]<<16) + (blk[67]<< 8) + (blk[68]);
1078         WritCrtr = (blk[69]<<24) + (blk[70]<<16) + (blk[71]<< 8) + (blk[72]);
1079         WritFlgs = (blk[73]<< 8) + 0;
1080         WritLDat = (blk[83]<<24) + (blk[84]<<16) + (blk[85]<< 8) + (blk[86]);
1081         WritLRsc = (blk[87]<<24) + (blk[88]<<16) + (blk[89]<< 8) + (blk[90]);
1082         WritCDat = (blk[91]<<24) + (blk[92]<<16) + (blk[93]<< 8) + (blk[94]);
1083         WritMDat = (blk[95]<<24) + (blk[96]<<16) + (blk[97]<< 8) + (blk[98]);
1084         cnt += 128;
1085         WritPart = 1;
1086     }
1087 
1088     /* open the data fork                                                  */
1089     if ( WritPart == 1 && cnt < len ) {
1090 #ifdef SYS_IS_MAC_THC
1091     WritFIPB.ioNamePtr   = (StringPtr)WritName;
1092 #else
1093     WritFIPB.ioNamePtr   = WritName;
1094 #endif
1095         WritIOPB.ioVRefNum = 0;
1096         WritIOPB.ioVersNum = 0;
1097         WritIOPB.ioPermssn = fsWrPerm;
1098         WritIOPB.ioMisc    = 0;
1099         WritIOPB.ioRefNum  = 0;
1100         if ( PBCreate( (ParmBlkPtr)&WritIOPB, 0 ) ) {
1101             return cnt;
1102         }
1103         if ( PBOpenSync(   (ParmBlkPtr)&WritIOPB ) ) {
1104             return cnt;
1105         }
1106         WritPart = 2;
1107     }
1108 
1109     /* next comes the data fork (padded to a multiple of 128 bytes)        */
1110     if ( WritPart == 2 ) {
1111         while ( WritLDat != 0 && cnt < len ) {
1112             WritIOPB.ioReqCount  = (128 <= WritLDat ? 128 : WritLDat);
1113             WritIOPB.ioPosMode   = fsAtMark;
1114             WritIOPB.ioPosOffset = 0;
1115             WritIOPB.ioBuffer    = (Ptr) (blk + cnt);
1116             if ( PBWriteSync( (ParmBlkPtr)&WritIOPB )
1117               || WritIOPB.ioActCount != WritIOPB.ioReqCount ) {
1118                 PBCloseSync( (ParmBlkPtr)&WritIOPB );
1119                 return cnt;
1120             }
1121             cnt += 128;
1122             WritLDat -= WritIOPB.ioReqCount;
1123         }
1124         if ( WritLDat == 0 )  WritPart = 3;
1125     }
1126 
1127     /* close the data fork                                                 */
1128     if ( WritPart == 3 ) {
1129         PBCloseSync( (ParmBlkPtr)&WritIOPB );
1130         WritPart = 4;
1131     }
1132 
1133     /* open the resource fork                                              */
1134     if ( WritPart == 4 && cnt < len ) {
1135         if ( PBOpenRF( (ParmBlkPtr)&WritIOPB, 0 ) ) {
1136             return cnt;
1137         }
1138         WritPart = 5;
1139     }
1140 
1141     /* and finally comes the resource fork                                 */
1142     if ( WritPart == 5 ) {
1143         while ( WritLRsc != 0 && cnt < len ) {
1144             WritIOPB.ioReqCount  = (128 <= WritLRsc ? 128 : WritLRsc);
1145             WritIOPB.ioPosMode   = fsAtMark;
1146             WritIOPB.ioPosOffset = 0;
1147             WritIOPB.ioBuffer    = (Ptr) (blk + cnt);
1148             if ( PBWriteSync( (ParmBlkPtr)&WritIOPB )
1149               || WritIOPB.ioActCount != WritIOPB.ioReqCount ) {
1150                 PBCloseSync( (ParmBlkPtr)&WritIOPB );
1151                 return cnt;
1152             }
1153             cnt += 128;
1154             WritLRsc -= WritIOPB.ioReqCount;
1155         }
1156         if ( WritLRsc == 0 )  WritPart = 6;
1157     }
1158 
1159     /* close the resource fork                                             */
1160     if ( WritPart == 6 ) {
1161         PBCloseSync( (ParmBlkPtr)&WritIOPB );
1162         WritPart = 7;
1163     }
1164 
1165     /* indicate success                                                    */
1166     return cnt;
1167 }
1168 
MacMakeDire(patl)1169 int             MacMakeDire ( patl )
1170     char *              patl;
1171 {
1172     HFileParam          request;        /* structure describing request    */
1173     char                patp [256];     /* <patl> as a Pascal string       */
1174     int                 len;            /* length of <patp>                */
1175 
1176     /* convert <patl> from a C string to a Pascal string                   */
1177     len = strlen( patl );
1178     len = len < 256 ? len : 255;
1179     patp[0] = len;
1180     strncpy( patp+1, patl, len );
1181 
1182     /* set up the request                                                  */
1183     request.ioNamePtr = (unsigned char*)patp;
1184     request.ioVRefNum = 0;
1185     request.ioDirID   = 0;
1186     if (noErr == PBDirCreate( (HParmBlkPtr)&request, 0 ))
1187     /* return result                                                       */
1188 	    return (request.ioResult == 0);
1189 	else
1190 		return 0;
1191 }
1192 
1193 #endif
1194 
1195 
1196 /****************************************************************************
1197 **
1198 *F  MakeDirs(<pre>,<patu>)  . . . . . . . . . . . . . .  make all directories
1199 **
1200 **  'MakeDirs' tries  to  make all the directories   along the universal path
1201 **  name <patu> (i.e., with components separated by '/').   <pre> is a prefix
1202 **  that is prepended to all path names.
1203 */
1204 #ifdef  MAKE_DIRE
1205 
MakeDirs(pre,patu)1206 int             MakeDirs ( pre, patu )
1207     char *              pre;
1208     char *              patu;
1209 {
1210     char                patl [1024];    /* path name, local                */
1211     char                diru [256];     /* directory part of <patu>, univ. */
1212     char                dirl [256];     /* directory part of <patl>, local */
1213     char                namu [256];     /* file name part of <patu>, univ. */
1214     char                naml [256];     /* file name part of <patl>, local */
1215     char                * d,  * n;      /* loop variables                  */
1216 
1217     /* if <patu> is an absolute path, copy the slash '/'                   */
1218     d = diru;
1219     if ( *patu == '/' )  *d++ = *patu++;
1220 
1221     while ( *patu != '\0' ) {
1222 
1223         /* copy the file name part of <patu> into <namu>                   */
1224         for ( n = namu; *patu != '\0' && *patu != '/'; ) *n++ = *patu++;
1225         if ( *patu != '\0' )  patu++;
1226 
1227         /* convert the name into local format and make the directory       */
1228         *d = '\0';  *n = '\0';
1229         CONV_DIRE( dirl, diru );
1230         CONV_NAME( naml, namu );
1231         strcpy( patl, pre  );
1232         strcat( patl, dirl );
1233         strcat( patl, naml );
1234         /*N 1993/11/03 martin what should I do with the return code?       */
1235         /*N 1993/11/03 martin it could be 0 if the directory exists!       */
1236         MAKE_DIRE( patl );
1237 
1238         /* append the file name part to the directory part                 */
1239         if ( d != diru && d[-1] != '/' )  *d++ = '/';
1240         for ( n = namu; *n != '\0'; ) *d++ = *n++;
1241 
1242     }
1243 
1244     /* indicate success                                                    */
1245     return 1;
1246 }
1247 
1248 #endif
1249 
1250 
1251 /****************************************************************************
1252 **
1253 *F  IsMatchName(<pat>,<str>)  . . test if a string matches a wildcard pattern
1254 **
1255 **  'IsMatchName' return 1 if the pattern <pat>  matches the string <str> and
1256 **  0 otherwise.  A   '?' in <pat>  matches any  character  in <str>,  a  '*'
1257 **  matches any string  in <str>, other characters in   <pat> match the  same
1258 **  character in <str>.  Characters for which 'IsSpec[<ch>]' is true will not
1259 **  be matched by '?' and '*'.
1260 **
1261 **  Jeff Damens  wrote the name match code in 'booz' (originally for Kermit).
1262 */
1263 int             IsSpec [256];           /* nonzero for special characters  */
1264 
IsMatchName(pat,str)1265 int             IsMatchName ( pat, str )
1266     char *              pat;            /* pattern to match against        */
1267     char *              str;            /* string  to match                */
1268 {
1269     char *              pos = 0;        /* pos. after last '*' in pattern  */
1270     char *              tmp = 0;        /* corresponding match in string   */
1271 
1272     /* try to match the name part                                          */
1273     while ( *pat != '\0' || *str != '\0' ) {
1274         if      ( *pat==*str                  ) { pat++;       str++;       }
1275         else if ( *pat=='?' && ! IsSpec[(unsigned int)*str] )
1276             { pat++;       str++;       }
1277         else if ( *pat=='?' && *str != '\0'   ) { pat++;       str++;       }
1278         else if ( *pat=='*'                   ) { pos = ++pat; tmp =   str; }
1279         else if ( tmp != 0  && ! IsSpec[(unsigned int)*tmp] )
1280             { pat =   pos; str = ++tmp; }
1281         else                                    break;
1282     }
1283     return *pat == '\0' && *str == '\0';
1284 }
1285 
1286 
1287 /****************************************************************************
1288 **
1289 *F  OpenReadArch(<patl>)  . . . . . . . . . . . . . . try to open the archive
1290 *F  ClosReadArch()  . . . . . . . . . . . . . . . . . . . . close the archive
1291 *F  GotoReadArch(<pos>) . . . . . .  goto an absolute position in the archive
1292 *F  ByteReadArch()  . . . . . . . . . read a  8 bit unsigned from the archive
1293 *F  HalfReadArch()  . . . . . . . . . read a 16 bit unsigned from the archive
1294 *F  TripReadArch()  . . . . . . . . . read a 24 bit unsigned from the archive
1295 *F  WordReadArch()  . . . . . . . . . read a 32 bit unsigned from the archive
1296 *F  BlckReadArch(<blk>,<len>) . . . .  read a block of bytes from the archive
1297 *V  Descript  . . . . . . . . . . . . . . . . . . . . header from the archive
1298 *F  DescReadArch()  . . . . . . . . . . . .  read the header from the archive
1299 *V  Entry . . . . . . . . . . . . . . . . header of a member from the archive
1300 *F  EntrReadArch()  . . . . . .  read the header of a member from the archive
1301 **
1302 **  'OpenReadArch' tries to open the archive with  local path name <patl> (as
1303 **  specified by the user on the command  line) for reading  and returns 1 to
1304 **  indicate success or 0 to indicate that the file cannot be opened.
1305 **
1306 **  'ClosReadArch' closes the archive again.
1307 **
1308 **  'GotoReadArch'  positions the  archive  at the  position <pos>, i.e., the
1309 **  next call to 'ByteReadArch' will return the byte at position <pos>.  Note
1310 **  that 'GotoReadArch' does not use 'fseek', because 'fseek' is unreliable.
1311 **
1312 **  'ByteReadArch' returns the next   byte  unsigned  8 bit from the archive.
1313 **  'HalfReadArch' returns the next 2 bytes unsigned 16 bit from the archive.
1314 **  'TripReadArch' returns the next 3 bytes unsigned 24 bit from the archive.
1315 **  'WordReadArch' returns the next 4 bytes unsigned 32 bit from the archive.
1316 **  'BlckReadArch' reads <len> bytes into the buffer <blk>.
1317 **
1318 **  'Descript' is the description of the archive.
1319 **
1320 **  'DescReadArch' reads the description  of the archive  that starts at  the
1321 **  current position into the structure 'Descript'.  It should of course only
1322 **  be called at the start of the archive file.
1323 **
1324 **  'Entry' is the directory entry of the current member from the archive.
1325 **
1326 **  'EntrReadArch'  reads the directory entry of  a member that starts at the
1327 **  current position into the structure 'Entry'.
1328 */
1329 unsigned char   BufArch [64+4096];      /* buffer for the archive          */
1330 
1331 unsigned char * PtrArch;                /* pointer to the next byte        */
1332 
1333 unsigned char * EndArch;                /* pointer to the last byte        */
1334 
1335 unsigned long   PosArch;                /* position of 'BufArch[0]'        */
1336 
OpenReadArch(patl)1337 int             OpenReadArch ( patl )
1338     char *              patl;
1339 {
1340     PtrArch = EndArch = (BufArch+64);
1341     PosArch = 0;
1342     return OPEN_READ_ARCH( patl );
1343 }
1344 
ClosReadArch()1345 int     ClosReadArch ()
1346 {
1347     return CLOS_READ_ARCH();
1348 }
1349 
FillReadArch()1350 int             FillReadArch ()
1351 {
1352     unsigned char *     s;              /* loop variable                   */
1353     unsigned char *     d;              /* loop variable                   */
1354 
1355     /* copy the last characters to the beginning (for short backward seeks)*/
1356     d = BufArch;
1357     for ( s = EndArch-64; s < EndArch; s++ )
1358         *d++ = *s;
1359     PosArch += EndArch - (BufArch+64);
1360 
1361     /* read a block                                                        */
1362     PtrArch = BufArch+64;
1363     EndArch = PtrArch + BLCK_READ_ARCH( PtrArch, 4096 );
1364 
1365     /* return the first character                                          */
1366     return (PtrArch < EndArch ? *PtrArch++ : EOF);
1367 }
1368 
GotoReadArch(pos)1369 int             GotoReadArch ( pos )
1370     unsigned long       pos;
1371 {
1372     /* for long backward seeks goto the beginning of the file              */
1373     if ( pos+64 < PosArch ) {
1374 #ifdef SEEK_READ_ARCH
1375 		if (!SEEK_READ_ARCH(pos))
1376 			return 0;
1377         PtrArch = EndArch = BufArch+64;
1378         PosArch = pos;
1379 #else
1380         if ( ! RWND_READ_ARCH() )
1381             return 0;
1382         PtrArch = EndArch = BufArch+64;
1383         PosArch = 0;
1384 #endif
1385     }
1386 
1387     /* jump forward bufferwise                                             */
1388     while ( PosArch + (EndArch - (BufArch+64)) <= pos ) {
1389         if ( FillReadArch() == EOF )
1390             return 0;
1391     }
1392 
1393     /* and goto the position (which is now in the buffer)                  */
1394     PtrArch = (BufArch+64) + (pos - PosArch);
1395 
1396     /* indicate success                                                    */
1397     return 1;
1398 }
1399 
1400 #define ByteReadArch()          (PtrArch<EndArch?*PtrArch++:FillReadArch())
1401 
HalfReadArch()1402 unsigned long   HalfReadArch ()
1403 {
1404     unsigned long       result;
1405     result  = ((unsigned long)ByteReadArch());
1406     result += ((unsigned long)ByteReadArch()) << 8;
1407     return result;
1408 }
1409 
FlahReadArch()1410 unsigned long   FlahReadArch ()
1411 {
1412     unsigned long       result;
1413     result  = ((unsigned long)ByteReadArch()) << 8;
1414     result += ((unsigned long)ByteReadArch());
1415     return result;
1416 }
1417 
TripReadArch()1418 unsigned long   TripReadArch ()
1419 {
1420     unsigned long       result;
1421     result  = ((unsigned long)ByteReadArch());
1422     result += ((unsigned long)ByteReadArch()) << 8;
1423     result += ((unsigned long)ByteReadArch()) << 16;
1424     return result;
1425 }
1426 
WordReadArch()1427 unsigned long   WordReadArch ()
1428 {
1429     unsigned long       result;
1430     result  = ((unsigned long)ByteReadArch());
1431     result += ((unsigned long)ByteReadArch()) << 8;
1432     result += ((unsigned long)ByteReadArch()) << 16;
1433     result += ((unsigned long)ByteReadArch()) << 24;
1434     return result;
1435 }
1436 
BlckReadArch(blk,len)1437 unsigned long   BlckReadArch ( blk, len )
1438     char *              blk;
1439     unsigned long       len;
1440 {
1441     int                 ch;             /* character read                  */
1442     unsigned long       i;              /* loop variable                   */
1443     for ( i = 0; i < len; i++ ) {
1444         if ( (ch = ByteReadArch()) == EOF )
1445             return i;
1446         else
1447             *blk++ = ch;
1448     }
1449     return len;
1450 }
1451 
1452 struct {
1453     char                text[20];       /* "ZOO 2.10 Archive.<ctr>Z"       */
1454     unsigned long       magic;          /* magic word 0xfdc4a7dc           */
1455     unsigned long       posent;         /* position of first directory ent.*/
1456     unsigned long       klhvmh;         /* two's complement of posent      */
1457     unsigned char       majver;         /* major version needed to extract */
1458     unsigned char       minver;         /* minor version needed to extract */
1459     unsigned char       type;           /* type of current member (0,1)    */
1460     unsigned long       poscmt;         /* position of comment, 0 if none  */
1461     unsigned short      sizcmt;         /* length   of comment, 0 if none  */
1462     unsigned char       modgen;         /* gens. on, gen. limit            */
1463     /* the following are not in the archive file and are computed          */
1464     unsigned long       sizorg;         /* uncompressed size of members    */
1465     unsigned long       siznow;         /*   compressed size of members    */
1466     unsigned long       number;         /* number of members               */
1467 
1468 }               Descript;
1469 
DescReadArch()1470 int             DescReadArch ()
1471 {
1472     /* read the text at the beginning                                      */
1473     BlckReadArch(Descript.text,20L);  Descript.text[20] = '\0';
1474 
1475     /* try to read the magic words                                         */
1476     if ( (Descript.magic = WordReadArch()) != (unsigned long)0xfdc4a7dcL )
1477         return 0;
1478 
1479     /* read the old part of the description                                */
1480     Descript.posent = WordReadArch();
1481     Descript.klhvmh = WordReadArch();
1482     Descript.majver = ByteReadArch();
1483     Descript.minver = ByteReadArch();
1484 
1485     /* read the new part of the description if present                     */
1486     Descript.type   = (34 < Descript.posent ? ByteReadArch() : 0);
1487     Descript.poscmt = (34 < Descript.posent ? WordReadArch() : 0);
1488     Descript.sizcmt = (34 < Descript.posent ? HalfReadArch() : 0);
1489     Descript.modgen = (34 < Descript.posent ? ByteReadArch() : 0);
1490 
1491     /* initialize the fake entries                                         */
1492     Descript.sizorg = 0;
1493     Descript.siznow = 0;
1494     Descript.number = 0;
1495 
1496     /* indicate success                                                    */
1497     return 1;
1498 }
1499 
1500 struct {
1501     unsigned long       magic;          /* magic word 0xfdc4a7dc           */
1502     unsigned char       type;           /* type of current member (1)      */
1503     unsigned char       method;         /* packing method of member (0..2) */
1504     unsigned long       posnxt;         /* position of next member         */
1505     unsigned long       posdat;         /* position of data                */
1506     unsigned short      datdos;         /* date (in DOS format)            */
1507     unsigned short      timdos;         /* time (in DOS format)            */
1508     unsigned short      crcdat;         /* crc value of member             */
1509     unsigned long       sizorg;         /* uncompressed size of member     */
1510     unsigned long       siznow;         /*   compressed size of member     */
1511     unsigned char       majver;         /* major version needed to extract */
1512     unsigned char       minver;         /* minor version needed to extract */
1513     unsigned char       delete;         /* 1 if member is deleted, 0 else  */
1514     unsigned char       spared;         /* spare entry to pad entry        */
1515     unsigned long       poscmt;         /* position of comment, 0 if none  */
1516     unsigned short      sizcmt;         /* length   of comment, 0 if none  */
1517     char                nams [14];      /* short name of member or archive */
1518     unsigned short      lvar;           /* length of variable part         */
1519     unsigned char       timzon;         /* time zone                       */
1520     unsigned short      crcent;         /* crc value of entry              */
1521     unsigned char       lnamu;          /* length of long name             */
1522     unsigned char       ldiru;          /* length of directory             */
1523     char                namu [256];     /* univ. name of member of archive */
1524     char                diru [256];     /* univ. name of directory         */
1525     unsigned short      system;         /* system identifier               */
1526     unsigned long       permis;         /* file permissions                */
1527     unsigned char       modgen;         /* gens. on, last gen., gen. limit */
1528     unsigned short      ver;            /* version number of member        */
1529     /* the following are not in the archive file and are computed          */
1530     char                naml [256];     /* local name of member of archive */
1531     char                dirl [256];     /* local name of directory         */
1532     char                patl [512];     /* local path name of member       */
1533     char                patv [512];     /* ditto but with version number   */
1534     char *              patw;           /* name used by '-l'               */
1535     unsigned long       year;           /* years since 1900                */
1536     unsigned long       month;          /* month since January             */
1537     unsigned long       day;            /* day of month                    */
1538     unsigned long       hour;           /* hours since midnight            */
1539     unsigned long       min;            /* minutes after the hour          */
1540     unsigned long       sec;            /* seconds after the minutes       */
1541 }               Entry;
1542 
EntrReadArch()1543 int             EntrReadArch ()
1544 {
1545     unsigned long       l;              /* 'Entry.lnamu+Entry.ldiru'       */
1546     char *              p;              /* loop variable                   */
1547 
1548     /* try to read the magic words                                         */
1549     if ( (Entry.magic = WordReadArch()) != (unsigned long)0xfdc4a7dcL )
1550         return 0;
1551 
1552     /* read the fixed part of the directory entry                          */
1553     Entry.type   = ByteReadArch();
1554     Entry.method = ByteReadArch();
1555     Entry.posnxt = WordReadArch();
1556     Entry.posdat = WordReadArch();
1557     Entry.datdos = HalfReadArch();
1558     Entry.timdos = HalfReadArch();
1559     Entry.crcdat = HalfReadArch();
1560     Entry.sizorg = WordReadArch();
1561     Entry.siznow = WordReadArch();
1562     Entry.majver = ByteReadArch();
1563     Entry.minver = ByteReadArch();
1564     Entry.delete = ByteReadArch();
1565     Entry.spared = ByteReadArch();
1566     Entry.poscmt = WordReadArch();
1567     Entry.sizcmt = HalfReadArch();
1568     BlckReadArch(Entry.nams,13L);  Entry.nams[13] = '\0';
1569 
1570     /* handle the long name and the directory in the variable part         */
1571     Entry.lvar   = (Entry.type == 2  ? HalfReadArch() : 0);
1572     Entry.timzon = (Entry.type == 2  ? ByteReadArch() : 127);
1573     Entry.crcent = (Entry.type == 2  ? HalfReadArch() : 0);
1574     Entry.lnamu  = (0 < Entry.lvar   ? ByteReadArch() : 0);
1575     Entry.ldiru  = (1 < Entry.lvar   ? ByteReadArch() : 0);
1576     BlckReadArch(Entry.namu,(unsigned long)Entry.lnamu);
1577     Entry.namu[Entry.lnamu] = '\0';
1578     BlckReadArch(Entry.diru,(unsigned long)Entry.ldiru);
1579     Entry.diru[Entry.ldiru] = '\0';
1580     l = Entry.lnamu + Entry.ldiru;
1581     Entry.system = (l+2 < Entry.lvar ? HalfReadArch() : 0);
1582     Entry.permis = (l+4 < Entry.lvar ? TripReadArch() : 0);
1583     Entry.modgen = (l+7 < Entry.lvar ? ByteReadArch() : 0);
1584     Entry.ver    = (l+7 < Entry.lvar ? HalfReadArch() : 0);
1585 
1586     /* convert the names to local format                                   */
1587     if ( Entry.system == 0 || Entry.system == 2 ) {
1588         CONV_DIRE( Entry.dirl, Entry.diru );
1589         CONV_NAME( Entry.naml, (Entry.lnamu ? Entry.namu : Entry.nams) );
1590     }
1591     else {
1592         strcpy( Entry.dirl, Entry.diru );
1593         strcpy( Entry.naml, (Entry.lnamu ? Entry.namu : Entry.nams) );
1594     }
1595     strcpy( Entry.patl, Entry.dirl );
1596     strcat( Entry.patl, Entry.naml );
1597 
1598     /* create the name with the version appended                           */
1599     strcpy( Entry.patv, Entry.patl );
1600     p = Entry.patv;  while ( *p != '\0' )  p++;
1601     *p++ = ';';
1602     for ( l = 10000; 0 < l; l /= 10 )
1603         if ( l == 1 || l <= Entry.ver )
1604             *p++ = (Entry.ver / l) % 10 + '0';
1605     *p = '\0';
1606     Entry.patw = ((Entry.modgen&0xc0)!=0x80 ? Entry.patl : Entry.patv);
1607 
1608     /* convert the time                                                    */
1609     Entry.year  = ((Entry.datdos >>  9) & 0x7f) + 80;
1610     Entry.month = ((Entry.datdos >>  5) & 0x0f) - 1;
1611     Entry.day   = ((Entry.datdos      ) & 0x1f);
1612     Entry.hour  = ((Entry.timdos >> 11) & 0x1f);
1613     Entry.min   = ((Entry.timdos >>  5) & 0x3f);
1614     Entry.sec   = ((Entry.timdos      ) & 0x1f) * 2;
1615 
1616     /* indicate success                                                    */
1617     return 1;
1618 }
1619 
1620 
1621 /****************************************************************************
1622 **
1623 *F  OpenReadFile(<patl>,<bin>)  . . . . . . . . . . . open a file for reading
1624 *F  ClosReadFile()  . . . . . . . . . . . . . . . . . . .  close a file again
1625 *F  BlckReadFile(<blk>,<len>) . . . . . . .  write a block of bytes to a file
1626 *F  BufFile[] . . . . . . . . . . . . . . . . . . . . . . buffer for the file
1627 **
1628 **  'OpenReadFile' tries to open the archive  with local path name <patl> (as
1629 **  converted by 'CONV_NAME'  and 'CONV_DIRE') for reading  and returns 1  to
1630 **  indicate success  and 0 to  indicate that the file cannot  be opened.  If
1631 **  <bin> is  0, the file is opened   as a text file,   otherwise the file is
1632 **  opened as a binary file.
1633 **
1634 **  'ClosReadFile' closes the file again.
1635 **
1636 **  'BlckReadFile' reads <len>  bytes from the  file to the buffer  <blk> and
1637 **  returns the  number    of bytes actually   read.   If  no file    is open
1638 **  'BlckReadFile' only returns 0.
1639 **
1640 **  'BufFile'  is  a buffer for  the  file (which is not   used  by the above
1641 **  functions).
1642 */
1643 unsigned long   IsOpenReadFile;
1644 
OpenReadFile(patl,bin)1645 int             OpenReadFile ( patl, bin )
1646     char *              patl;
1647     unsigned long       bin;
1648 {
1649     if      ( bin == 0 && OPEN_READ_TEXT(patl) ) {
1650         IsOpenReadFile = 1;
1651         return 1;
1652     }
1653     else if ( bin == 1 && OPEN_READ_BINR(patl) ) {
1654         IsOpenReadFile = 2;
1655         return 1;
1656     }
1657     else {
1658         return 0;
1659     }
1660 }
1661 
ClosReadFile()1662 int             ClosReadFile ()
1663 {
1664     if      ( IsOpenReadFile == 1 ) {
1665         IsOpenReadFile = 0;
1666         return CLOS_READ_TEXT();
1667     }
1668     else if ( IsOpenReadFile == 2 ) {
1669         IsOpenReadFile = 0;
1670         return CLOS_READ_BINR();
1671     }
1672     else {
1673         return 0;
1674     }
1675 }
1676 
BlckReadFile(blk,len)1677 unsigned long   BlckReadFile ( blk, len )
1678     char *              blk;
1679     unsigned long       len;
1680 {
1681     if      ( IsOpenReadFile == 1 ) {
1682         return BLCK_READ_TEXT( blk, len );
1683     }
1684     else if ( IsOpenReadFile == 2 ) {
1685         return BLCK_READ_BINR( blk, len );
1686     }
1687     else {
1688         return 0;
1689     }
1690 }
1691 
1692 char            BufFile [8192];         /* at least MAX_OFF                */
1693 
1694 
1695 /****************************************************************************
1696 **
1697 *F  OpenWritFile(<patl>,<bin>)  . . . . . . . . . . . open a file for writing
1698 *F  ClosWritFile()  . . . . . . . . . . . . . . . . . . .  close a file again
1699 *F  BlckWritFile(<blk>,<len>) . . . . . . .  write a block of bytes to a file
1700 **
1701 **  'OpenWritFile' tries to open the archive  with local path name <patl> (as
1702 **  converted by 'CONV_NAME'  and 'CONV_DIRE') for writing  and returns  1 to
1703 **  indicate success  and 0 to indicate  that the file cannot  be opened.  If
1704 **  <bin> is  0, the file  is opened as a text   file, otherwise the  file is
1705 **  opened as a binary file.
1706 **
1707 **  'ClosWritFile' closes the file again.
1708 **
1709 **  'BlckWritFile' writes <len>  bytes from the  buffer <blk> to the file and
1710 **  returns the number  of bytes actually written,  which is less than  <len>
1711 **  only when a write error happened.  If no file is open 'BlckWritFile' only
1712 **  returns <len>.
1713 */
1714 unsigned long   IsOpenWritFile;
1715 
OpenWritFile(patl,bin)1716 int             OpenWritFile ( patl, bin )
1717     char *              patl;
1718     unsigned long       bin;
1719 {
1720     if ( patl == 0 ) {
1721         IsOpenWritFile = 1;
1722         return 1;
1723     }
1724     else if ( bin == 1 && OPEN_WRIT_TEXT(patl) ) {
1725         IsOpenWritFile = 2;
1726         return 1;
1727     }
1728     else if ( bin == 2 && OPEN_WRIT_BINR(patl) ) {
1729         IsOpenWritFile = 3;
1730         return 1;
1731     }
1732 #ifdef  SYS_IS_MAC_MPW
1733     else if ( bin == 3 && OPEN_WRIT_MACB(patl) ) {
1734         IsOpenWritFile = 4;
1735         return 1;
1736     }
1737 #endif
1738     else {
1739         return 0;
1740     }
1741 }
1742 
ClosWritFile()1743 int             ClosWritFile ()
1744 {
1745     if      ( IsOpenWritFile == 1 ) {
1746         return 1;
1747     }
1748     else if ( IsOpenWritFile == 2 ) {
1749         IsOpenWritFile = 0;
1750         return CLOS_WRIT_TEXT();
1751     }
1752     else if ( IsOpenWritFile == 3 ) {
1753         IsOpenWritFile = 0;
1754         return CLOS_WRIT_BINR();
1755     }
1756 #ifdef  SYS_IS_MAC_MPW
1757     else if ( IsOpenWritFile == 4 ) {
1758         IsOpenWritFile = 0;
1759         return CLOS_WRIT_MACB();
1760     }
1761 #endif
1762     else {
1763         return 0;
1764     }
1765 }
1766 
BlckWritFile(blk,len)1767 unsigned long   BlckWritFile ( blk, len )
1768     char *              blk;
1769     unsigned long       len;
1770 {
1771     unsigned long       i;              /* loop variable                   */
1772     if      ( IsOpenWritFile == 1 ) {
1773         for ( i = 0; i < len; i++ )
1774             putchar( blk[i] );
1775         return len;
1776     }
1777     else if ( IsOpenWritFile == 2 ) {
1778         return BLCK_WRIT_TEXT( blk, len );
1779     }
1780     else if ( IsOpenWritFile == 3 ) {
1781         return BLCK_WRIT_BINR( blk, len );
1782     }
1783 #ifdef  SYS_IS_MAC_MPW
1784     else if ( IsOpenWritFile == 4 ) {
1785 #ifdef SYS_IS_MAC_THC
1786         return BLCK_WRIT_MACB( (StringPtr)blk, len );
1787 #else
1788         return BLCK_WRIT_MACB( blk, len );
1789 #endif
1790     }
1791 #endif
1792     else {
1793         return len;
1794     }
1795 }
1796 
1797 
1798 /****************************************************************************
1799 **
1800 *V  Crc . . . . . . . . . . . . . . . . current cyclic redundancy check value
1801 *F  CRC_BYTE(<crc>,<byte>)  . . . . . cyclic redundancy check value of a byte
1802 *F  InitCrc() . . . . . . . . . . . . initialize cylic redundancy check table
1803 **
1804 **  'Crc'  is used by  the  decoding  functions to  communicate  the computed
1805 **  CRC-16 value to the calling function.
1806 **
1807 **  'CRC_BYTE' returns the new value that one gets by updating the old CRC-16
1808 **  value <crc> with the additional byte  <byte>.  It is  used to compute the
1809 **  ANSI CRC-16 value for  each member of the archive.   They idea is that if
1810 **  not  too many bits  of a member have corrupted,  then  the CRC-16 will be
1811 **  different, and so the corruption can be detected.
1812 **
1813 **  'InitCrc' initialize the table that 'CRC_BYTE' uses.   You must call this
1814 **  before using 'CRC_BYTE'.
1815 **
1816 **  The  ANSI CRC-16  value  for a sequence of    bits of lenght  <length> is
1817 **  computed by shifting the bits through the following shift register (where
1818 **  'O' are the latches and '+' denotes logical xor)
1819 **
1820 **                  bit          bit            ...  bit   bit   bit   -->-
1821 **                     <length>     <length>-1          3     2     1     |
1822 **                                                                        V
1823 **      -<-------<---------------------------------------------------<----+
1824 **      |       |                                                   |     ^
1825 **      V       V                                                   V     |
1826 **      ->O-->O-+>O-->O-->O-->O-->O-->O-->O-->O-->O-->O-->O-->O-->O-+>O-->-
1827 **       MSB                                                         LSB
1828 **
1829 **  Mathematically we compute in the polynomial ring $GF(2)[x]$ the remainder
1830 **
1831 **      $$\sum_{i=1}^{i=length}{bit_i x^{length+16-i}} mod crcpol$$
1832 **
1833 **  where  $crcpol = x^{16}  + x^{15}  +  x^2 +  1$.  Then  the  CRC-16 value
1834 **  consists  of the  coefficients   of  the remainder,  with    the constant
1835 **  coefficient being  the most significant bit (MSB)  and the coefficient of
1836 **  $x^{15}$ the least significant bit (LSB).
1837 **
1838 **  Changing  a  single bit will  always cause  the  CRC-16  value to change,
1839 **  because $x^{i} mod crcpol$ is never zero.
1840 **
1841 **  Changing two  bits  will cause the CRC-16   value to change,  unless  the
1842 **  distance between the bits is a multiple  of 32767, which  is the order of
1843 **  $x$ modulo $crcpol = (x+1)(x^{15} + x + 1)$ ($x^{15}+x+1$ is primitive).
1844 **
1845 **  Changing  16 adjacent  bits will always  cause the  CRC value  to change,
1846 **  because $x^{16}$ and $crcpol$ are relatively prime.
1847 **
1848 **  David Schwaderer provided the CRC-16 calculation in PC Tech Journal 4/85.
1849 */
1850 unsigned long   Crc;
1851 
1852 unsigned long   CrcTab [256];
1853 
1854 #define CRC_BYTE(crc,byte)      (((crc)>>8) ^ CrcTab[ ((crc)^(byte))&0xff ])
1855 
InitCrc()1856 int             InitCrc ()
1857 {
1858     unsigned long       i, k;           /* loop variables                  */
1859     for ( i = 0; i < 256; i++ ) {
1860         CrcTab[i] = i;
1861         for ( k = 0; k < 8; k++ )
1862             CrcTab[i] = (CrcTab[i]>>1) ^ ((CrcTab[i] & 1) ? 0xa001 : 0);
1863     }
1864     return 1;
1865 }
1866 
1867 
1868 /****************************************************************************
1869 **
1870 *V  ErrMsg  . . . . . . . . . . . . . . . . . . . . . . . . . . error message
1871 **
1872 **  'ErrMsg' is used by the  decode functions to communicate  the cause of an
1873 **  error to the calling function.
1874 */
1875 char *          ErrMsg;
1876 
1877 
1878 /****************************************************************************
1879 **
1880 *F  DecodeCopy(<size>). . . . . . . . . . . .  extract an uncompressed member
1881 **
1882 **  'DecodeCopy' simply  copies <size> bytes  from the  archive to the output
1883 **  file.
1884 */
DecodeCopy(size)1885 int             DecodeCopy ( size )
1886     unsigned long       size;
1887 {
1888     unsigned long       siz;            /* size of current block           */
1889     unsigned long       crc;            /* CRC-16 value                    */
1890     unsigned long       i;              /* loop variable                   */
1891 
1892     /* initialize the crc value                                            */
1893     crc = 0;
1894 
1895     /* loop until everything has been copied                               */
1896     while ( 0 < size ) {
1897 
1898         /* read as many bytes as possible in one go                        */
1899         siz = (sizeof(BufFile) < size ? sizeof(BufFile) : size);
1900         if ( BlckReadArch( BufFile, siz ) != siz ) {
1901             ErrMsg = "unexpected <eof> in the archive";
1902             return 0;
1903         }
1904 
1905         /* write them                                                      */
1906         if ( BlckWritFile( BufFile, siz ) != siz ) {
1907             ErrMsg = "cannot write output file";
1908             return 0;
1909         }
1910 
1911         /* compute the crc                                                 */
1912         for ( i = 0; i < siz; i++ )
1913             crc = CRC_BYTE( crc, BufFile[i] );
1914 
1915         /* on to the next block                                            */
1916         size -= siz;
1917     }
1918 
1919     /* store the crc and indicate success                                  */
1920     Crc = crc;
1921     return 1;
1922 }
1923 
1924 
1925 /****************************************************************************
1926 **
1927 *F  DecodeLzd() . . . . . . . . . . . . . . .  extract a LZ compressed member
1928 **
1929 *N  1993/10/21 martin add LZD.
1930 */
DecodeLzd()1931 int             DecodeLzd ()
1932 {
1933     ErrMsg = "LZD not yet implemented";
1934     return 0;
1935 }
1936 
1937 
1938 /****************************************************************************
1939 **
1940 *F  DecodeLzh() . . . . . . . . . . . . . . . extract a LZH compressed member
1941 **
1942 **  'DecodeLzh'  decodes  a LZH  (Lempel-Ziv 77  with dynamic Huffman coding)
1943 **  encoded member from the archive to the output file.
1944 **
1945 **  Each member is encoded as a  series of blocks.  Each  block starts with a
1946 **  16  bit field that contains the  number of codes  in this block <number>.
1947 **  The member is terminated by a block with 0 codes.
1948 **
1949 **  Next each block contains the  description of three Huffman codes,  called
1950 **  pre code, literal/length code, and log code.  The purpose of the pre code
1951 **  is to encode the description of  the literal/length code.  The purpose of
1952 **  the literal/length code and the  log code is   to encode the  appropriate
1953 **  fields in the LZ code.   I am too stupid to  understand the format of the
1954 **  description.
1955 **
1956 **  Then   each block contains  <number>  codewords.  There  are two kinds of
1957 **  codewords, *literals* and *copy instructions*.
1958 **
1959 **  A literal represents a certain byte.  For  the moment imaging the literal
1960 **  as having 9 bits.   The first bit  is zero, the other  8 bits contain the
1961 **  byte.
1962 **
1963 **      +--+----------------+
1964 **      | 0|     <byte>     |
1965 **      +--+----------------+
1966 **
1967 **  When a  literal is  encountered, the byte  <byte> that  it represents  is
1968 **  appended to the output.
1969 **
1970 **  A copy  instruction represents a certain  sequence of bytes that appeared
1971 **  already  earlier in the output.  The  copy instruction  consists of three
1972 **  parts, the length, the offset logarithm, and the offset mantissa.
1973 **
1974 **      +--+----------------+--------+--------------------+
1975 **      | 1|   <length>-3   |  <log> |     <mantissa>     |
1976 **      +--+----------------+--------+--------------------+
1977 **
1978 **  <length>  is  the  length  of the sequence   which  this copy instruction
1979 **  represents.  We store '<length>-3', because <length> is never 0, 1, or 2;
1980 **  such sequences are better represented by 0, 1, or  2 literals.  <log> and
1981 **  <mantissa>  together represent the offset at  which the sequence of bytes
1982 **  already  appeared.  '<log>-1'  is  the number of   bits in the <mantissa>
1983 **  field, and the offset is $2^{<log>-1} + <mantissa>$.  For example
1984 **
1985 **      +--+----------------+--------+----------+
1986 **      | 1|        9       |    6   | 0 1 1 0 1|
1987 **      +--+----------------+--------+----------+
1988 **
1989 **  represents the sequence of 12 bytes that appeared $2^5 + 8 + 4  + 1 = 45$
1990 **  bytes earlier in the output (so those 18 bits of input represent 12 bytes
1991 **  of output).
1992 **
1993 **  When a copy instruction  is encountered, the  sequence of  <length> bytes
1994 **  that appeared   <offset> bytes earlier  in the  output  is again appended
1995 **  (copied) to   the output.   For this  purpose  the last  <max>  bytes are
1996 **  remembered,  where  <max>  is the   maximal  used offset.   In 'zoo' this
1997 **  maximal offset is $2^{13} =  8192$.  The buffer in  which those bytes are
1998 **  remembered is  called   a sliding  window for   reasons  that  should  be
1999 **  obvious.
2000 **
2001 **  To save even  more space the first 9  bits of each code, which  represent
2002 **  the type of code and either the literal value or  the length, are encoded
2003 **  using  a Huffman code  called the literal/length  code.   Also the next 4
2004 **  bits in  copy instructions, which represent  the logarithm of the offset,
2005 **  are encoded using a second Huffman code called the log code.
2006 **
2007 **  Those  codes  are fixed, i.e.,  not  adaptive, but  may  vary between the
2008 **  blocks, i.e., in each block  literals/lengths and logs  may be encoded by
2009 **  different codes.  The codes are described at the beginning of each block.
2010 **
2011 **  Haruhiko Okumura  wrote the  LZH code (originally for his 'ar' archiver).
2012 */
2013 #define MAX_LIT                 255     /* maximal literal code            */
2014 #define MIN_LEN                 3       /* minimal length of match         */
2015 #define MAX_LEN                 256     /* maximal length of match         */
2016 #define MAX_CODE                (MAX_LIT+1 + MAX_LEN+1 - MIN_LEN)
2017 #define BITS_CODE               9       /* 2^BITS_CODE > MAX_CODE (+1?)    */
2018 #define MAX_OFF                 8192    /* 13 bit sliding directory        */
2019 #define MAX_LOG                 13      /* maximal log_2 of offset         */
2020 #define BITS_LOG                4       /* 2^BITS_LOG > MAX_LOG (+1?)      */
2021 #define MAX_PRE                 18      /* maximal pre code                */
2022 #define BITS_PRE                5       /* 2^BITS_PRE > MAX_PRE (+1?)      */
2023 
2024 unsigned short  TreeLeft [2*MAX_CODE+1];/* tree for codes   (upper half)   */
2025 unsigned short  TreeRight[2*MAX_CODE+1];/* and  for offsets (lower half)   */
2026 unsigned short  TabCode  [4096];        /* table for fast lookup of codes  */
2027 unsigned char   LenCode  [MAX_CODE+1];  /* number of bits used for code    */
2028 unsigned short  TabLog   [256];         /* table for fast lookup of logs   */
2029 unsigned char   LenLog   [MAX_LOG+1];   /* number of bits used for logs    */
2030 unsigned short  TabPre   [256];         /* table for fast lookup of pres   */
2031 unsigned char   LenPre   [MAX_PRE+1];   /* number of bits used for pres    */
2032 
MakeTablLzh(nchar,bitlen,tablebits,table)2033 int             MakeTablLzh ( nchar, bitlen, tablebits, table )
2034     int                 nchar;
2035     unsigned char       bitlen[];
2036     int                 tablebits;
2037     unsigned short      table[];
2038 {
2039     unsigned short      count[17], weight[17], start[18], *p;
2040     unsigned int        i, k, len, ch, jutbits, avail, mask;
2041 
2042     for (i = 1; i <= 16; i++) count[i] = 0;
2043     for (i = 0; i < nchar; i++) count[bitlen[i]]++;
2044 
2045     start[1] = 0;
2046     for (i = 1; i <= 16; i++)
2047         start[i + 1] = start[i] + (count[i] << (16 - i));
2048     if (start[17] != (unsigned short)((unsigned) 1 << 16))
2049         return 0;
2050 
2051     jutbits = 16 - tablebits;
2052     for (i = 1; i <= tablebits; i++) {
2053         start[i] >>= jutbits;
2054         weight[i] = (unsigned) 1 << (tablebits - i);
2055     }
2056     while (i <= 16) {
2057         weight[i] = (unsigned) 1 << (16 - i);
2058         i++;
2059     }
2060 
2061     i = start[tablebits + 1] >> jutbits;
2062     if (i != (unsigned short)((unsigned) 1 << 16)) {
2063         k = 1 << tablebits;
2064         while (i != k) table[i++] = 0;
2065     }
2066 
2067     avail = nchar;
2068     mask = (unsigned) 1 << (15 - tablebits);
2069     for (ch = 0; ch < nchar; ch++) {
2070         if ((len = bitlen[ch]) == 0) continue;
2071         if (len <= tablebits) {
2072             for ( i = 0; i < weight[len]; i++ )  table[i+start[len]] = ch;
2073         }
2074         else {
2075             k = start[len];
2076             p = &table[k >> jutbits];
2077             i = len - tablebits;
2078             while (i != 0) {
2079                 if (*p == 0) {
2080                     TreeRight[avail] = TreeLeft[avail] = 0;
2081                     *p = avail++;
2082                 }
2083                 if (k & mask) p = &TreeRight[*p];
2084                 else          p = &TreeLeft[*p];
2085                 k <<= 1;  i--;
2086             }
2087             *p = ch;
2088         }
2089         start[len] += weight[len];
2090     }
2091 
2092     /* indicate success                                                    */
2093     return 1;
2094 }
2095 
DecodeLzh()2096 int             DecodeLzh ()
2097 {
2098     unsigned long       cnt;            /* number of codes in block        */
2099     unsigned long       cnt2;           /* number of stuff in pre code     */
2100     unsigned long       code;           /* code from the Archive           */
2101     unsigned long       len;            /* length of match                 */
2102     unsigned long       log;            /* log_2 of offset of match        */
2103     unsigned long       off;            /* offset of match                 */
2104     unsigned long       pre;            /* pre code                        */
2105     char *              cur;            /* current position in BufFile     */
2106     char *              pos;            /* position of match               */
2107     char *              end;            /* pointer to the end of BufFile   */
2108     char *              stp;            /* stop pointer during copy        */
2109     unsigned long       crc;            /* cyclic redundancy check value   */
2110     unsigned long       i;              /* loop variable                   */
2111     unsigned long       bits;           /* the bits we are looking at      */
2112     unsigned long       bitc;           /* number of bits that are valid   */
2113 
2114 #define PEEK_BITS(N)            ((bits >> (bitc-(N))) & ((1L<<(N))-1))
2115 #define FLSH_BITS(N)            if ( (bitc -= (N)) < 16 ) { bits  = (bits<<16) + FlahReadArch(); bitc += 16; }
2116 
2117     /* initialize bit source, output pointer, and crc                      */
2118     bits = 0;  bitc = 0;  FLSH_BITS(0);
2119     cur = BufFile;  end = BufFile + MAX_OFF;
2120     crc = 0;
2121 
2122     /* loop until all blocks have been read                                */
2123     cnt = PEEK_BITS( 16 );  FLSH_BITS( 16 );
2124     while ( cnt != 0 ) {
2125 
2126         /* read the pre code                                               */
2127         cnt2 = PEEK_BITS( BITS_PRE );  FLSH_BITS( BITS_PRE );
2128         if ( cnt2 == 0 ) {
2129             pre = PEEK_BITS( BITS_PRE );  FLSH_BITS( BITS_PRE );
2130             for ( i = 0; i <      256; i++ )  TabPre[i] = pre;
2131             for ( i = 0; i <= MAX_PRE; i++ )  LenPre[i] = 0;
2132         }
2133         else {
2134             i = 0;
2135             while ( i < cnt2 ) {
2136                 len = PEEK_BITS( 3 );  FLSH_BITS( 3 );
2137                 if ( len == 7 ) {
2138                     while ( PEEK_BITS( 1 ) ) { len++; FLSH_BITS( 1 ); }
2139                     FLSH_BITS( 1 );
2140                 }
2141                 LenPre[i++] = len;
2142                 if ( i == 3 ) {
2143                     len = PEEK_BITS( 2 );  FLSH_BITS( 2 );
2144                     while ( 0 < len-- )  LenPre[i++] = 0;
2145                 }
2146             }
2147             while ( i <= MAX_PRE )  LenPre[i++] = 0;
2148             if ( ! MakeTablLzh( MAX_PRE+1, LenPre, 8, TabPre ) ) {
2149                 ErrMsg = "pre code description corrupted";
2150                 return 0;
2151             }
2152         }
2153 
2154         /* read the code (using the pre code)                              */
2155         cnt2 = PEEK_BITS( BITS_CODE );  FLSH_BITS( BITS_CODE );
2156         if ( cnt2 == 0 ) {
2157             code = PEEK_BITS( BITS_CODE );  FLSH_BITS( BITS_CODE );
2158             for ( i = 0; i <      4096; i++ )  TabCode[i] = code;
2159             for ( i = 0; i <= MAX_CODE; i++ )  LenCode[i] = 0;
2160         }
2161         else {
2162             i = 0;
2163             while ( i < cnt2 ) {
2164                 len = TabPre[ PEEK_BITS( 8 ) ];
2165                 if ( len <= MAX_PRE ) {
2166                     FLSH_BITS( LenPre[len] );
2167                 }
2168                 else {
2169                     FLSH_BITS( 8 );
2170                     do {
2171                         if ( PEEK_BITS( 1 ) )  len = TreeRight[len];
2172                         else                   len = TreeLeft [len];
2173                         FLSH_BITS( 1 );
2174                     } while ( MAX_PRE < len );
2175                 }
2176                 if ( len <= 2 ) {
2177                     if      ( len == 0 ) {
2178                         len = 1;
2179                     }
2180                     else if ( len == 1 ) {
2181                         len = PEEK_BITS(4)+3;  FLSH_BITS(4);
2182                     }
2183                     else {
2184                         len = PEEK_BITS(BITS_CODE)+20; FLSH_BITS(BITS_CODE);
2185                     }
2186                     while ( 0 < len-- )  LenCode[i++] = 0;
2187                 }
2188                 else {
2189                     LenCode[i++] = len - 2;
2190                 }
2191             }
2192             while ( i <= MAX_CODE )  LenCode[i++] = 0;
2193             if ( ! MakeTablLzh( MAX_CODE+1, LenCode, 12, TabCode ) ) {
2194                 ErrMsg = "literal/length code description corrupted";
2195                 return 0;
2196             }
2197         }
2198 
2199         /* read the log_2 of offsets                                       */
2200         cnt2 = PEEK_BITS( BITS_LOG );  FLSH_BITS( BITS_LOG );
2201         if ( cnt2 == 0 ) {
2202             log = PEEK_BITS( BITS_LOG );  FLSH_BITS( BITS_LOG );
2203             for ( i = 0; i <      256; i++ )  TabLog[i] = log;
2204             for ( i = 0; i <= MAX_LOG; i++ )  LenLog[i] = 0;
2205         }
2206         else {
2207             i = 0;
2208             while ( i < cnt2 ) {
2209                 len = PEEK_BITS( 3 );  FLSH_BITS( 3 );
2210                 if ( len == 7 ) {
2211                     while ( PEEK_BITS( 1 ) ) { len++; FLSH_BITS( 1 ); }
2212                     FLSH_BITS( 1 );
2213                 }
2214                 LenLog[i++] = len;
2215             }
2216             while ( i <= MAX_LOG )  LenLog[i++] = 0;
2217             if ( ! MakeTablLzh( MAX_LOG+1, LenLog, 8, TabLog ) ) {
2218                 ErrMsg = "log code description corrupted";
2219                 return 0;
2220             }
2221         }
2222 
2223         /* read the codes                                                  */
2224         while ( 0 < cnt-- ) {
2225 
2226             /* try to decode the code the fast way                         */
2227             code = TabCode[ PEEK_BITS( 12 ) ];
2228 
2229             /* if this code needs more than 12 bits look it up in the tree */
2230             if ( code <= MAX_CODE ) {
2231                 FLSH_BITS( LenCode[code] );
2232             }
2233             else {
2234                 FLSH_BITS( 12 );
2235                 do {
2236                     if ( PEEK_BITS( 1 ) )  code = TreeRight[code];
2237                     else                   code = TreeLeft [code];
2238                     FLSH_BITS( 1 );
2239                 } while ( MAX_CODE < code );
2240             }
2241 
2242             /* if the code is a literal, stuff it into the buffer          */
2243             if ( code <= MAX_LIT ) {
2244                 *cur++ = code;
2245                 crc = CRC_BYTE( crc, code );
2246                 if ( cur == end ) {
2247                     if ( BlckWritFile(BufFile,cur-BufFile) != cur-BufFile ) {
2248                         ErrMsg = "cannot write output file";
2249                         return 0;
2250                     }
2251                     cur = BufFile;
2252                 }
2253             }
2254 
2255             /* otherwise compute match length and offset and copy          */
2256             else {
2257                 len = code - (MAX_LIT+1) + MIN_LEN;
2258 
2259                 /* try to decodes the log_2 of the offset the fast way     */
2260                 log = TabLog[ PEEK_BITS( 8 ) ];
2261                 /* if this log_2 needs more than 8 bits look in the tree   */
2262                 if ( log <= MAX_LOG ) {
2263                     FLSH_BITS( LenLog[log] );
2264                 }
2265                 else {
2266                     FLSH_BITS( 8 );
2267                     do {
2268                         if ( PEEK_BITS( 1 ) )  log = TreeRight[log];
2269                         else                   log = TreeLeft [log];
2270                         FLSH_BITS( 1 );
2271                     } while ( MAX_LOG < log );
2272                 }
2273 
2274                 /* compute the offset                                      */
2275                 if ( log == 0 ) {
2276                     off = 0;
2277                 }
2278                 else {
2279                     off = ((unsigned)1 << (log-1)) + PEEK_BITS( log-1 );
2280                     FLSH_BITS( log-1 );
2281                 }
2282 
2283                 /* copy the match (this accounts for ~ 50% of the time)    */
2284                 pos = BufFile + (((cur-BufFile) - off - 1) & (MAX_OFF - 1));
2285                 if ( cur < end-len && pos < end-len ) {
2286                     stp = cur + len;
2287                     do {
2288                         code = *pos++;
2289                         crc = CRC_BYTE( crc, code );
2290                         *cur++ = code;
2291                     } while ( cur < stp );
2292                 }
2293                 else {
2294                     while ( 0 < len-- ) {
2295                         code = *pos++;
2296                         crc = CRC_BYTE( crc, code );
2297                         *cur++ = code;
2298                         if ( pos == end ) {
2299                             pos = BufFile;
2300                         }
2301                         if ( cur == end ) {
2302                             if ( BlckWritFile(BufFile,cur-BufFile)
2303                                  != cur-BufFile ) {
2304                                 ErrMsg = "cannot write output file";
2305                                 return 0;
2306                             }
2307                             cur = BufFile;
2308                         }
2309                     }
2310                 }
2311 
2312             }
2313 
2314         }
2315 
2316         cnt = PEEK_BITS( 16 );  FLSH_BITS( 16 );
2317     }
2318 
2319     /* write out the rest of the buffer                                    */
2320     if ( BlckWritFile(BufFile,cur-BufFile) != cur-BufFile ) {
2321         ErrMsg = "cannot write output file";
2322         return 0;
2323     }
2324 
2325     /* indicate success                                                    */
2326     Crc = crc;
2327     return 1;
2328 }
2329 
2330 
2331 /****************************************************************************
2332 **
2333 *F  ListArch(<ver>,<arc>,<filec>,<files>) . . list the members of the archive
2334 **
2335 **  'ListArch'  lists the members  of the  archive with  the name  <arc> that
2336 **  match one  of the file name  patterns '<files>[0] .. <files>[<filec>-1]'.
2337 **  If <ver> is 1, comments are also printed.
2338 */
2339 unsigned long   BeginMonth [12] = {
2340    0,    31,   59,   90,  120,  151,  181,  212,  243,  273,  304,  334
2341 };
2342 
2343 char            NameMonth [12] [4] = {
2344 "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
2345 };
2346 
ListArch(ver,arc,filec,files)2347 int             ListArch ( ver, arc, filec, files )
2348     unsigned long       ver;
2349     char *              arc;
2350     unsigned long       filec;
2351     char *              files [];
2352 {
2353     char                arczoo [256];   /* <arc> with '.zoo' tacked on     */
2354     int                 chr;            /* character from comment          */
2355     unsigned long       i;              /* loop variable                   */
2356 
2357     /* try to open the archive under various names                         */
2358     strcpy(arczoo,arc);  strcat(arczoo,".zoo");
2359     if ( OpenReadArch(arc) ) {
2360         if ( ! DescReadArch() ) {
2361             ClosReadArch();
2362             if ( ! OpenReadArch(arczoo) || ! DescReadArch() ) {
2363                 printf("unzoo: found bad description in archive '%s'\n",arc);
2364                 return 0;
2365             }
2366         }
2367     }
2368     else if ( OpenReadArch(arczoo) ) {
2369         if ( ! DescReadArch() ) {
2370             printf("unzoo: found bad description in archive '%s'\n",arczoo);
2371             return 0;
2372         }
2373     }
2374     else {
2375         printf("unzoo: could not open archive '%s'\n",arc);
2376         return 0;
2377     }
2378 
2379     /* if present, print the archive comment                               */
2380     if ( ver && Descript.sizcmt != 0 ) {
2381         if ( ! GotoReadArch( Descript.poscmt ) ) {
2382             printf("unzoo: cannot find comment in archive '%s'\n",arc);
2383             return 0;
2384         }
2385         chr = '\n';
2386         for ( i = 0; i < Descript.sizcmt; i++ ) {
2387             if ( chr == '\n' )  printf("# ");
2388             chr = ByteReadArch();
2389             if ( chr == '\012' )  chr = '\n';
2390             printf("%c",chr);
2391         }
2392         if ( chr != '\n' )  printf("\n");
2393         fflush( stdout );
2394     }
2395 
2396     /* print the header                                                    */
2397     printf("Length    CF  Size Now  Date      Time    \n");
2398     printf("--------  --- --------  --------- --------\n");
2399     fflush( stdout );
2400 
2401     /* loop over the members of the archive                                */
2402     Entry.posnxt = Descript.posent;
2403     while ( 1 ) {
2404 
2405         /* read the directory entry for the next member                    */
2406         if ( ! GotoReadArch( Entry.posnxt ) || ! EntrReadArch() ) {
2407             printf("unzoo: found bad directory entry in archive '%s'\n",arc);
2408             return 0;
2409         }
2410         if ( ! Entry.posnxt )  break;
2411 
2412         /* skip members we don't care about                                */
2413         if ( Entry.delete == 1 )
2414             continue;
2415         if ( filec == 0 && ! IsMatchName( "*", Entry.patw ) )
2416             continue;
2417         for ( i = 0; i < filec; i++ )
2418             if ( IsMatchName( files[i], Entry.patv )
2419               || IsMatchName( files[i], Entry.patw ) )
2420                 break;
2421         if ( filec != 0 && i == filec )
2422             continue;
2423 
2424         /* print the information about the member                          */
2425         printf("%8lu %3lu%% %8lu  %2lu %3s %02lu %02lu:%02lu:%02lu   %s\n",
2426                Entry.sizorg,
2427                (100*(Entry.sizorg-Entry.siznow)+Entry.sizorg/2)
2428                / (Entry.sizorg != 0 ? Entry.sizorg : 1),
2429                Entry.siznow,
2430                Entry.day, NameMonth[Entry.month], Entry.year % 100,
2431                Entry.hour, Entry.min, Entry.sec,
2432                (ver ? Entry.patv : Entry.patw) );
2433         fflush( stdout );
2434 
2435         /* update the counts for the whole archive                         */
2436         Descript.sizorg += Entry.sizorg;
2437         Descript.siznow += Entry.siznow;
2438         Descript.number += 1;
2439 
2440         /* if present print the file comment                               */
2441         if ( ver && Entry.sizcmt != 0 ) {
2442             if ( ! GotoReadArch( Entry.poscmt ) ) {
2443                 printf("unzoo: cannot find comment in archive '%s'\n",arc);
2444                 return 0;
2445             }
2446             chr = '\n';
2447             for ( i = 0; i < Entry.sizcmt; i++ ) {
2448                 if ( chr == '\n' )  printf("# ");
2449                 chr = ByteReadArch();
2450                 if ( chr == '\012' )  chr = '\n';
2451                 printf("%c",chr);
2452             }
2453             if ( chr != '\n' )  printf("\n");
2454         }
2455         fflush( stdout );
2456 
2457     }
2458 
2459     /* print the footer                                                    */
2460     printf("--------  --- --------  --------- --------\n");
2461     printf("%8lu %3lu%% %8lu  %4lu files\n",
2462            Descript.sizorg,
2463            (100*(Descript.sizorg-Descript.siznow)+Descript.sizorg/2)
2464            / (Descript.sizorg != 0 ? Descript.sizorg : 1),
2465            Descript.siznow,
2466            Descript.number );
2467     fflush( stdout );
2468 
2469     /* close the archive file                                              */
2470     if ( ! ClosReadArch() ) {
2471         printf("unzoo: could not close archive '%s'\n",arc);
2472         return 0;
2473     }
2474 
2475     /* indicate success                                                    */
2476     return 1;
2477 }
2478 
2479 
2480 /****************************************************************************
2481 **
2482 *F  ExtrArch(<bim>,<out>,<ovr>,<pre>,<frc>,<arc>,<filec>,<files>) . extract members
2483 **
2484 **  'ExtrArch' extracts the members  of the archive with  the name <arc> that
2485 **  match one  of the file name  patterns '<files>[0] .. <files>[<filec>-1]'.
2486 **  If <bim> is 0, members with comments starting with '!TEXT!' are extracted
2487 **  as text files and the other members are extracted as  binary files; if it
2488 **  is 1,  all members are extracted  as text files; if  it is 2, all members
2489 **  are  extracted as binary  files. If <out>  is 0, no members are extracted
2490 **  and only tested  for integrity; if it  is 1, the  members are printed  to
2491 **  stdout, i.e., to the screen.  and if it  is 2, the members are extracted.
2492 **  If <ovr> is 0, members will not overwrite  existing files; otherwise they
2493 **  will.  <pre> is a prefix that is prepended to all path names.
2494 **  <frc> is 1 if the user requested extraction of members even if this would
2495 ** result in a directory traversal.
2496 */
ExtrArch(bim,out,ovr,pre,frc,arc,filec,files)2497 int             ExtrArch ( bim, out, ovr, pre, frc, arc, filec, files )
2498     unsigned long       bim;
2499     unsigned long       out;
2500     unsigned long       ovr;
2501     char *              pre;
2502     unsigned long       frc;
2503     char *              arc;
2504     unsigned long       filec;
2505     char *              files [];
2506 {
2507     char                arczoo [256];   /* <arc> with '.zoo' tacked on     */
2508     char                ans [256];      /* to read the answer              */
2509     char                patl [1024];    /* local name with prefix          */
2510     unsigned long       bin;            /* extraction mode text/binary     */
2511     unsigned long       res;            /* status of decoding              */
2512     unsigned long       secs;           /* seconds since 70/01/01 00:00:00 */
2513     unsigned long       i;              /* loop variable                   */
2514 
2515     /* try to open the archive under various names                         */
2516     strcpy(arczoo,arc);  strcat(arczoo,".zoo");
2517     if ( OpenReadArch(arc) ) {
2518         if ( ! DescReadArch() ) {
2519             ClosReadArch();
2520             if ( ! OpenReadArch(arczoo) || ! DescReadArch() ) {
2521                 printf("unzoo: found bad description in archive '%s'\n",arc);
2522                 return 0;
2523             }
2524         }
2525     }
2526     else if ( OpenReadArch(arczoo) ) {
2527         if ( ! DescReadArch() ) {
2528             printf("unzoo: found bad description in archive '%s'\n",arczoo);
2529             return 0;
2530         }
2531     }
2532     else {
2533         printf("unzoo: could not open archive '%s'\n",arc);
2534         return 0;
2535     }
2536 
2537     /* test if the archive has a comment starting with '!TEXT!'            */
2538     if ( bim == 0
2539       && 6 <= Descript.sizcmt  && GotoReadArch( Descript.poscmt )
2540       && ByteReadArch() == '!' && ByteReadArch() == 'T'
2541       && ByteReadArch() == 'E' && ByteReadArch() == 'X'
2542       && ByteReadArch() == 'T' && ByteReadArch() == '!' )
2543         bim = 1;
2544 
2545     /* test if the archive has a comment starting with '!MACBINARY!'       */
2546 #ifdef  SYS_IS_MAC_MPW
2547     else if ( bim == 0
2548       && 11 <= Descript.sizcmt && GotoReadArch( Descript.poscmt )
2549       && ByteReadArch() == '!' && ByteReadArch() == 'M'
2550       && ByteReadArch() == 'A' && ByteReadArch() == 'C'
2551       && ByteReadArch() == 'B' && ByteReadArch() == 'I'
2552       && ByteReadArch() == 'N' && ByteReadArch() == 'A'
2553       && ByteReadArch() == 'R' && ByteReadArch() == 'Y'
2554       && ByteReadArch() == '!' )
2555         bim = 3;
2556 #endif
2557 
2558     /* loop over the members of the archive                                */
2559     Entry.posnxt = Descript.posent;
2560     while ( 1 ) {
2561 
2562         /* read the directory entry for the next member                    */
2563         if ( ! GotoReadArch( Entry.posnxt ) || ! EntrReadArch() ) {
2564             printf("unzoo: found bad directory entry in archive '%s'\n",arc);
2565             return 0;
2566         }
2567         if ( ! Entry.posnxt )  break;
2568 
2569         /* skip members we don't care about                                */
2570         if ( Entry.delete == 1 )
2571             continue;
2572         if ( filec == 0 && ! IsMatchName( "*", Entry.patw ) )
2573             continue;
2574         for ( i = 0; i < filec; i++ )
2575             if ( IsMatchName( files[i], Entry.patv )
2576               || IsMatchName( files[i], Entry.patw ) )
2577                 break;
2578         if ( filec != 0 && i == filec )
2579             continue;
2580 
2581         /* check that we can decode this file                              */
2582         if ( (2 < Entry.method) || (2 < Entry.majver)
2583           || (2 == Entry.majver && 1 < Entry.minver) ) {
2584             printf("unzoo: unknown method, you need a later version\n");
2585             continue;
2586         }
2587 
2588         /* check the path for directory traversal                          */
2589         if (frc != 1) {
2590             /* but only if the user did not request otherwise              */
2591 
2592             /* building the universal path of this member                  */
2593             int found_trav = 0;
2594             char patu [sizeof(Entry.diru) + sizeof(Entry.namu) + 2];
2595             strcpy( patu, Entry.diru );
2596             if ( strlen(patu) && patu[strlen(patu)-1] != '/') strcat( patu, "/" );
2597             strcat( patu, (Entry.lnamu ? Entry.namu : Entry.nams) );
2598 
2599 
2600             if ( strstr( patu, "/../" )) {
2601 
2602                 /* remove "/../" from the path                             */
2603                 char tmp [sizeof(patu)];
2604                 char *p;
2605                 char *q;
2606                 found_trav = 1;
2607                 memset(tmp, 0, sizeof(tmp));
2608                 q = patu;
2609 
2610                 while ( !strncmp(q, "/../", 4) ) {
2611                     q += 3;
2612                 }
2613                 if (q[0] == '/') q++;
2614 
2615                 while ((p = strstr( q, "/../" )) != NULL) {
2616                     if (q[0] == '/') q++;
2617                     if (p > q) strncat(tmp, q, p-q);
2618                     if (tmp[strlen(tmp)-1] != '/') strcat(tmp, "/");
2619                     p += 3;
2620                     q = p;
2621                 }
2622                 strncat(tmp, q+1, patu + strlen(patu) - q);
2623                 strcpy(patu, tmp);
2624 
2625                 printf("unzoo: skipped \"/../\" path component(s) in '%s'\n", Entry.patl);
2626             }
2627             if ( *patu == '/' && !strlen( pre ) ) {
2628 
2629                 char *p = malloc(sizeof(patu));
2630                 char *q = p;
2631                 found_trav = 1;
2632                 memset(p, 0, sizeof(patu));
2633                 strcpy(p, patu);
2634                 while ( q[0] == '/' ) q++;
2635                 strcpy(patu, q);
2636                 free(p);
2637 
2638                 printf("unzoo: skipped root directory path component in '%s'\n", patl);
2639             }
2640             if ( !strncmp( patu, "../", 3 )) {
2641 
2642                 char tmp [sizeof(patu)];
2643                 found_trav = 1;
2644                 memset(tmp, 0, sizeof(tmp));
2645                 strcpy(tmp, patu + 3);
2646                 strcpy(patu, tmp);
2647 
2648                 printf("unzoo: skipped \"../\" path component in '%s'\n", patl);
2649             }
2650 
2651             if (found_trav) {
2652                 /* patu contains the sanitized 'universal' path, i.e.      */
2653                 /* separated by '/' characters, including the file name.   */
2654 
2655                 char *f = strrchr( patu, '/' );
2656                 *f++ = '\0';
2657                 /* Now, patu points to the directory part, f to the file   */
2658 
2659                 memset( Entry.diru, 0, sizeof(Entry.diru) );
2660                 strncpy( Entry.diru, patu, sizeof(Entry.diru)-1 );
2661                 if ( Entry.lnamu > 0 ) {
2662                     memset( Entry.namu, 0, sizeof(Entry.namu) );
2663                     strncpy( Entry.namu, f, sizeof(Entry.namu)-1 );
2664                 } else {
2665                     memset( Entry.nams, 0, sizeof(Entry.nams) );
2666                     strncpy( Entry.nams, f, sizeof(Entry.nams)-1 );
2667                 }
2668 
2669                 /* convert the names to local format                       */
2670                 if ( Entry.system == 0 || Entry.system == 2 ) {
2671                     CONV_DIRE( Entry.dirl, Entry.diru );
2672                     CONV_NAME( Entry.naml, (Entry.lnamu ? Entry.namu : Entry.nams) );
2673                 }
2674                 else {
2675                     strcpy( Entry.dirl, Entry.diru );
2676                     strcpy( Entry.naml, (Entry.lnamu ? Entry.namu : Entry.nams) );
2677                 }
2678                 /* sizeof(patl)=512, sizeof({dirl|naml}=256} */
2679                 strcpy( Entry.patl, Entry.dirl );
2680                 strcat( Entry.patl, Entry.naml );
2681             }
2682         }
2683 
2684         /* check that such a file does not already exist                   */
2685         strcpy( patl, pre );  strcat( patl, Entry.patl );
2686         if ( out == 2 && ovr == 0 && OpenReadFile(patl,0L) ) {
2687             ClosReadFile();
2688             do {
2689                 printf("'%s' exists, overwrite it? (Yes/No/All/Ren): ",patl);
2690                 fflush( stdout );
2691                 if ( fgets( ans, sizeof(ans), stdin ) == (char*)0 )
2692                     return 0;
2693             } while ( *ans!='y' && *ans!='n' && *ans!='a' && *ans!='r'
2694                    && *ans!='Y' && *ans!='N' && *ans!='A' && *ans!='R' );
2695             if      ( *ans == 'n' || *ans == 'N' ) {
2696                 continue;
2697             }
2698             else if ( *ans == 'a' || *ans == 'A' ) {
2699                 ovr = 1;
2700             }
2701             else if ( *ans == 'r' || *ans == 'R' ) {
2702                 do {
2703                     printf("enter a new local path name: ");
2704                     fflush( stdout );
2705                     if ( fgets( patl, sizeof(patl), stdin ) == (char*)0 )
2706                         return 0;
2707                     for ( i = 0; patl[i] != '\0' && patl[i] != '\n'; i++ ) ;
2708                     patl[i] = '\0';
2709                 } while ( OpenReadFile(patl,0L) && ClosReadFile() );
2710             }
2711         }
2712 
2713         /* decide whether or not we want to open the file binary           */
2714         if ( bim == 0
2715           && 6 <= Entry.sizcmt     && GotoReadArch( Entry.poscmt )
2716           && ByteReadArch() == '!' && ByteReadArch() == 'T'
2717           && ByteReadArch() == 'E' && ByteReadArch() == 'X'
2718           && ByteReadArch() == 'T' && ByteReadArch() == '!' )
2719             bin = 1;
2720 #ifdef  SYS_IS_MAC_MPW
2721         else if ( bim == 0
2722           && 11 <= Entry.sizcmt    && GotoReadArch( Entry.poscmt )
2723           && ByteReadArch() == '!' && ByteReadArch() == 'M'
2724           && ByteReadArch() == 'A' && ByteReadArch() == 'C'
2725           && ByteReadArch() == 'B' && ByteReadArch() == 'I'
2726           && ByteReadArch() == 'N' && ByteReadArch() == 'A'
2727           && ByteReadArch() == 'R' && ByteReadArch() == 'Y'
2728           && ByteReadArch() == '!' )
2729             bin = 3;
2730 #endif
2731         else if ( bim == 0 )
2732             bin = 2;
2733         else
2734             bin = bim;
2735 
2736         /* open the file for creation                                      */
2737         if ( out == 2 && ! OpenWritFile(patl,bin)
2738 #ifdef  MAKE_DIRE
2739           && (! MakeDirs(pre,Entry.diru) || ! OpenWritFile(patl,bin))
2740 #endif
2741             ) {
2742             printf("unzoo: '%s' cannot be created, ",patl);
2743 #ifndef MAKE_DIRE
2744             if ( Entry.dirl[0] != '\0' )
2745                 printf("check that the directory '%s' exists\n",Entry.dirl);
2746             else
2747                 printf("check the permissions\n");
2748 #else
2749             printf("check the permissions\n");
2750 #endif
2751             continue;
2752         }
2753 
2754         /* or ``open'' stdout for printing                                 */
2755         if ( out == 1 )
2756             OpenWritFile( (char*)0, 0L );
2757 
2758         /* decode the file                                                 */
2759         if ( ! GotoReadArch( Entry.posdat ) ) {
2760             printf("unzoo: cannot find data in archive '%s'\n",arc);
2761             return 0;
2762         }
2763         res = 0;
2764         ErrMsg = "this should not happen";
2765         if ( out == 0 || out == 2 )
2766             printf("%s \t-- ",Entry.patl);
2767         else
2768             printf("********\n%s\n********\n",Entry.patl);
2769         fflush( stdout );
2770         if ( Entry.method == 0 )  res = DecodeCopy( Entry.siznow );
2771         if ( Entry.method == 1 )  res = DecodeLzd();
2772         if ( Entry.method == 2 )  res = DecodeLzh();
2773 
2774         /* check that everything went ok                                   */
2775         if      ( res == 0             )  printf("error, %s\n",ErrMsg);
2776         else if ( Crc != Entry.crcdat  )  printf("error, CRC failed\n");
2777         else if ( out == 2 && bin == 1 )  printf("extracted as text\n");
2778         else if ( out == 2 && bin == 2 )  printf("extracted as binary\n");
2779 #ifdef  SYS_IS_MAC_MPW
2780         else if ( out == 2 && bin == 3 )  printf("extracted as MacBinary\n");
2781 #endif
2782         else if ( out == 0             )  printf("tested\n");
2783         fflush( stdout );
2784 
2785         /* close the file after extraction                                 */
2786         if ( out == 1 || out == 2 )
2787             ClosWritFile();
2788 
2789         /* set the file time, evt. correct for timezone of packing system  */
2790         secs = 24*60*60L*(365*(Entry.year - 70)
2791                          + BeginMonth[Entry.month]
2792                          + Entry.day - 1
2793                          + (Entry.year -  69) / 4
2794                          + (Entry.year %   4 ==   0 && 1 < Entry.month)
2795                          - (Entry.year + 299) / 400
2796                          - (Entry.year % 400 == 100 && 1 < Entry.month))
2797                  +60*60L*Entry.hour + 60L*Entry.min + Entry.sec;
2798         if      ( Entry.timzon < 127 )  secs += 15*60*(Entry.timzon      );
2799         else if ( 127 < Entry.timzon )  secs += 15*60*(Entry.timzon - 256);
2800         if ( out == 2 ) {
2801             if ( ! SETF_TIME( patl, secs ) )
2802                 printf("unzoo: '%s' could not set the times\n",patl);
2803         }
2804 
2805         /* set the file permissions                                        */
2806         if ( out == 2 && (Entry.permis >> 22) == 1 ) {
2807             if ( ! SETF_PERM( patl, Entry.permis ) )
2808                 printf("unzoo: '%s' could not set the permissions\n",patl);
2809         }
2810 
2811     }
2812 
2813     /* close the archive file                                              */
2814     if ( ! ClosReadArch() ) {
2815         printf("unzoo: could not close the archive '%s'\n",arc);
2816         return 0;
2817     }
2818 
2819     /* indicate success                                                    */
2820     return 1;
2821 }
2822 
2823 
2824 /****************************************************************************
2825 **
2826 *F  HelpArch()  . . . . . . . . . . . . . . . . . . . . . . . print some help
2827 **
2828 **  'HelpArch' prints some help about 'unzoo'.
2829 */
HelpArch()2830 int             HelpArch ()
2831 {
2832     printf("unzoo -- a zoo archive extractor by Martin Schoenert\n");
2833     printf("  ($Id: unzoo.c,v 4.4 2000/05/29 08:56:57 sal Exp $)\n");
2834     printf("  based on 'booz' version 2.0 by Rahul Dhesi\n");
2835     printf("\n");
2836     printf("unzoo [-l] [-v] <archive>[.zoo] [<file>..]\n");
2837     printf("  list the members of the archive\n");
2838     printf("  -v:  list also the generation numbers and the comments\n");
2839     printf("  <file>: list only files matching at least one pattern,\n");
2840     printf("          '?' matches any char, '*' matches any string.\n");
2841     printf("\n");
2842     printf("unzoo -x [-abnpo] [-t] [-j <prefix>] <archive>[.zoo] [<file>..]\n");
2843     printf("  extract the members of the archive\n");
2844     printf("  -a:  extract all members as text files ");
2845     printf("(not only those with !TEXT! comments)\n");
2846     printf("  -b:  extract all members as binary files ");
2847     printf("(even those with !TEXT! comments)\n");
2848     printf("  -n:  extract no members, only test the integrity\n");
2849     printf("  -p:  extract to stdout\n");
2850     printf("  -o:  extract over existing files\n");
2851     printf("  -j:  extract to '<prefix><membername>'\n");
2852     printf("  -f:  force extraction of members to their original locations\n");
2853     printf("       even if this results in files extracted outside the\n");
2854     printf("       working directory. THIS COULD POTENTIALLY OVERWRITE\n");
2855     printf("       IMPORTANT FILES, SO USE WITH CARE!\n");
2856     printf("  <file>: extract only files matching at least one pattern,\n");
2857     printf("          '?' matches any char, '*' matches any string.\n");
2858     return 1;
2859 }
2860 
2861 
2862 /****************************************************************************
2863 **
2864 *F  main(<argc>,<argv>) . . . . . . . . . . . . . . . . . . . .  main program
2865 **
2866 **  'main' is the main program, it decodes the arguments  and then  calls the
2867 **  appropriate function.
2868 */
main(argc,argv)2869 int             main ( argc, argv )
2870     int                 argc;
2871     char *              argv [];
2872 {
2873     unsigned long       res;            /* result of command               */
2874     unsigned long       cmd;            /* command help/list/extract       */
2875     unsigned long       ver;            /* list verbose option             */
2876     unsigned long       bim;            /* extraction mode option          */
2877     unsigned long       out;            /* output destination option       */
2878     unsigned long       ovr;            /* overwrite file option           */
2879     unsigned long       frc;            /* force extraction option         */
2880     char *              pre;            /* prefix to prepend to path names */
2881     char                argl [256];     /* interactive command line        */
2882     int                 argd;           /* interactive command count       */
2883     char *              argw [256];     /* interactive command vector      */
2884     char *              p;              /* loop variable                   */
2885 
2886 
2887 #ifdef SYS_IS_MAC_THC
2888     SIOUXSettings.autocloseonquit = 1;
2889     SIOUXSettings.asktosaveonclose = 0;
2890 #endif
2891 
2892     /* repeat until the user enters an empty line                          */
2893     InitCrc();
2894     IsSpec['\0'] = 1;  IsSpec[';'] = 1;
2895     argd = 1;
2896     do {
2897 
2898         /* scan the command line arguments                                 */
2899         cmd = 1;  ver = 0;  bim = 0;  out = 2;  ovr = 0; frc = 0;
2900         pre = "";
2901         while ( 1 < argc && argv[1][0] == '-' ) {
2902             if ( argv[1][2] != '\0' )  cmd = 0;
2903             switch ( argv[1][1] ) {
2904             case 'l': case 'L': if ( cmd != 0 )  cmd = 1;            break;
2905             case 'v': case 'V': if ( cmd != 1 )  cmd = 0;  ver = 1;  break;
2906             case 'x': case 'X': if ( cmd != 0 )  cmd = 2;            break;
2907             case 'a': case 'A': if ( cmd != 2 )  cmd = 0;  bim = 1;  break;
2908             case 'b': case 'B': if ( cmd != 2 )  cmd = 0;  bim = 2;  break;
2909             case 'f': case 'F': if ( cmd != 2 )  cmd = 0;  frc = 1; break;
2910             case 'n': case 'N': if ( cmd != 2 )  cmd = 0;  out = 0;  break;
2911             case 'p': case 'P': if ( cmd != 2 )  cmd = 0;  out = 1;  break;
2912             case 'o': case 'O': if ( cmd != 2 )  cmd = 0;  ovr = 1;  break;
2913             case 'j': case 'J': if ( argc == 2 ) { cmd = 0;  break; }
2914                                 pre = argv[2];  argc--;  argv++;
2915                                 break;
2916             default:            cmd = 0;  break;
2917             }
2918             argc--;  argv++;
2919         }
2920 
2921         /* execute the command or print help                               */
2922         if      ( cmd == 1 && 1 < argc )
2923             res = ListArch( ver, argv[1],
2924                             (unsigned long)argc-2, argv+2 );
2925         else if ( cmd == 2 && 1 < argc )
2926             res = ExtrArch( bim, out, ovr, pre, frc, argv[1],
2927                             (unsigned long)argc-2, argv+2 );
2928         else
2929             res = HelpArch();
2930 
2931         /* in interactive mode read another line                           */
2932         if ( 1 < argd || argc <= 1 ) {
2933 
2934             /* read a command line                                         */
2935             printf("\nEnter a command line or an empty line to quit:\n");
2936             fflush( stdout );
2937             if ( fgets( argl, sizeof(argl), stdin ) == (char*)0 )  break;
2938 #ifdef SYS_IdfgdfsgasfgdsgfdS_MAC_THC
2939 			if ( *argl == '\n' && argl[1]=='\0') break;
2940 #endif
2941            /* parse the command line into argc                            */
2942             argd = 1;
2943             p = argl;
2944             while ( *p==' ' || *p=='\t' || *p=='\n' )  *p++ = '\0';
2945             while ( *p != '\0' ) {
2946                 argw[argd++] = p;
2947                 while ( *p!=' ' && *p!='\t' && *p!='\n' && *p!='\0' )  p++;
2948                 while ( *p==' ' || *p=='\t' || *p=='\n' )  *p++ = '\0';
2949             }
2950             argc = argd;  argv = argw;
2951 
2952         }
2953 
2954     } while ( 1 < argd );
2955 
2956     /* just to please lint                                                 */
2957     return ! res;
2958 }
2959 
2960 
2961 
2962