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