]> git.mxchange.org Git - flightgear.git/blob - utils/fgadmin/src/untarka.c
commradio: improvements for atis speech
[flightgear.git] / utils / fgadmin / src / untarka.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #define DUMMY \
6   WFLAGS="-ansi -pedantic -W -Wall -Wstrict-prototypes -Wtraditional -Wnested-externs -Winline -Wpointer-arith -Wbad-function-cast -Wcast-qual -Wmissing-prototypes -Wmissing-declarations -Wunused"; \
7   set -ex; \
8   gcc -s -O2 $WFLAGS -DHAVE_ZLIB -DHAVE_BZ2LIB untarka.c -o untarka -lz -lbz2; \
9   exit 0
10 /*
11  * untarka.c -- Display contents and/or extract file from a tar file (possibly
12  * gzip'd, bzip'd or compress'd)
13  * by pts@fazekas.hu Wed Jan 29 00:38:31 CET 2003
14  * Z compiles fine at Wed Jan 29 14:20:32 CET 2003
15  * all run file at Wed Jan 29 15:50:39 CET 2003
16  * 0.34 Win32 runs fine at Wed Jan 29 16:02:57 GMT 2003
17  *
18  * Untarka extracts TAR (UNIX Tape ARchive) files without calling any
19  * external programs. It supports and autodetects multiple compression
20  * methods (.tar, .tar.Z, .tar.gz, .tar.bz2, .Z, .gz and .bz2). UNIX
21  * devices, sockets, hard links, symlinks, owners and permissions are
22  * ignored during extraction.
23  *
24  * written by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
25  * adaptation to Unix by Jean-loup Gailly <jloup@gzip.org>
26  * adaptation to bzip2 by José Fonseca <em96115@fe.up.pt>
27  * merged comressors by Szabó Péter <pts@fazekas.hu> at Tue Jan 28 23:40:23 CET 2003
28  * compiles on Win32 under MinGW, Linux with gcc.
29  *
30  * To get gzip (zlib) support, pass to gcc: -DHAVE_ZLIB -lz
31  * To get bzip2 (bzlib2) support, pass to gcc: -DHAVE_BZ2LIB -lbz2
32  * To get compress (lz) support, relax, since it's the default.
33  *
34  * Compile memory-checking with checkergcc -g -ansi -pedantic -W -Wall -Wstrict-prototypes -Wtraditional -Wnested-externs -Winline -Wpointer-arith -Wbad-function-cast -Wcast-qual -Wmissing-prototypes -Wmissing-declarations -Wunused untarka.c -o untarka
35  */
36 /* Imp: symlink()
37  * Imp: mknod() device, socket
38  * Imp: hard link()
39  * Imp: chmod()
40  * Imp: chown()
41  * Imp: allow force non-tar gzipped file
42  * Imp: command line options
43  * Imp: uncompress ZIP files
44  * Dat: zlib is able to read uncompressed files, libbz2 isn't
45  * Dat: magic numbers:
46  *  257     string          ustar\0         POSIX tar archive
47  *  257     string          ustar\040\040\0 GNU tar archive
48  *  0       string          BZh             bzip2 compressed data
49  *  0       string          \037\213        gzip compressed data
50  *  0       string          \037\235        compress'd data
51  *  is_tar.c() from file(1)
52  */
53
54 #undef DOSISH
55 #ifdef WIN32
56 #  include <windows.h>
57 #  define DOSISH 1
58 #  undef __STRICT_ANSI__
59 #endif
60 #ifdef MSDOS
61 #  define DOSISH 1
62 #endif
63
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <time.h>
68 #include <errno.h>
69 #include <fcntl.h>
70
71 #ifdef DOSISH
72 # include <direct.h>
73 # include <io.h>
74 #else
75 # include <unistd.h>
76 #endif
77
78 #if DOSISH
79 #  ifndef F_OK
80 #    define F_OK (0)
81 #  endif
82 #  ifdef _MSC_VER
83 #    define mkdir(dirname,mode) _mkdir(dirname)
84 #    define unlink(fn)          _unlink(fn)
85 #    define access(path,mode)   _access(path,mode)
86 #  else
87 #    define mkdir(dirname,mode) _mkdir(dirname)
88 #  endif
89 #else
90 #  include <utime.h>
91 #  include <sys/stat.h> /* mkdir() */
92 #endif
93
94 #ifndef OF /* function prototypes */
95 #  if defined(__STDC__) || defined(__PROTOTYPES__) || defined(__cplusplus)
96 #    define OF(args)  args
97 #  else
98 #    define OF(args)  args
99 #  endif
100 #endif
101
102 #include "untarka.h"
103
104 static void error OF((const char *msg));
105
106 static unsigned long total_read;
107
108 /* #define TAR_GZ 1 */
109
110 typedef struct Readable {
111   /*private*/ void *f;
112   /** @return 0 on success, 1 on failure */
113   int (*xOpen4Read)(struct Readable* self, char const* filename);
114   /** @return 0 on success */
115   int (*xClose)(struct Readable* self);
116   /** @return (unsigned)-1 on error */
117   unsigned (*xRead)(struct Readable* self, void* buf, unsigned len);
118   char const* (*xError)(struct Readable* self, int *errnum_ret);
119 } Readable;
120
121 enum { FMT_U=1, FMT_Z=2, FMT_GZ=3, FMT_BZ2=4 };
122
123 /* #define xFILE FILE* */
124 static int xU_Open4Read(struct Readable* self, char const* filename) { total_read=0; return NULL==(self->f=fopen(filename,"rb")); }
125 static int xU_Close(struct Readable* self) { return fclose((FILE*)self->f); }
126 static unsigned xU_Read(struct Readable* self, void* buf, unsigned len) {
127   unsigned got=fread(buf,1,len,(FILE*)self->f);
128   total_read+=got;
129   return got>0 ? got : ferror((FILE*)self->f) ? 0U-1 : 0;
130 }
131 static char const* xU_Error(struct Readable* self, int *errnum_ret) { return (*errnum_ret=ferror((FILE*)self->f))?"I/O error":"OK"; }
132
133 /* #define xFILE struct lz_reader_t* */
134 struct lz_reader_t;
135 static struct lz_reader_t* lz_read_new(FILE* inf);
136 static int lz_read_init(struct lz_reader_t* reader, FILE* inf);
137 static int lz_close(struct lz_reader_t* reader);
138 static unsigned lz_read(struct lz_reader_t* reader, char*buf, unsigned len);
139
140 static int xZ_Open4Read(struct Readable* self, char const* filename) {
141   FILE *f=fopen(filename,"rb");
142   if (NULL==f) return 1;
143   return NULL==(self->f=lz_read_new(f));
144 }
145 static int xZ_Close(struct Readable* self) {
146   int ret=lz_close((struct lz_reader_t*)self->f);
147   free((struct lz_reader_t*)self->f);
148   return ret;
149 }
150 static unsigned xZ_Read(struct Readable* self, void* buf, unsigned len) {
151   return lz_read((struct lz_reader_t*)self->f, buf, len);
152 }
153 static char const* xZ_Error(struct Readable* self, int *errnum_ret) {
154   (void)self;
155   *errnum_ret=0;
156   return "lzw error";
157 }
158
159 #if HAVE_ZLIB
160 #include "zlib.h"
161 /* #define xFILE gzFile */
162 static int xGZ_Open4Read(struct Readable* self, char const* filename) { total_read=0; return NULL==(self->f=gzopen(filename,"rb")); }
163 static int xGZ_Close(struct Readable* self) { return gzclose((gzFile)self->f); }
164 static unsigned xGZ_Read(struct Readable* self, void* buf, unsigned len) { unsigned l=gzread((gzFile)self->f,buf,len); total_read=((z_streamp)self->f)->total_in; return l; }
165 static char const* xGZ_Error(struct Readable* self, int *errnum_ret) { return gzerror((gzFile)self->f, errnum_ret); }
166 #endif
167
168 #if HAVE_BZ2LIB
169 #include "bzlib.h"
170 /* #define xFILE BZFILE* */
171 static int xBZ2_Open4Read(struct Readable* self, char const* filename) { return NULL==(self->f=BZ2_bzopen(filename,"rb")); }
172 static int xBZ2_Close(struct Readable* self) { BZ2_bzclose((BZFILE*)self->f); return 0; }
173 static unsigned xBZ2_Read(struct Readable* self, void* buf, unsigned len) { return BZ2_bzread((BZFILE*)self->f,buf,len); }
174 static char const* xBZ2_Error(struct Readable* self, int *errnum_ret) { return BZ2_bzerror((BZFILE*)self->f, errnum_ret); }
175 #endif
176
177 /* -- is_tar() */
178
179 /*
180  * is_tar() -- figure out whether file is a tar archive.
181  *
182  * Stolen (by the author!) from the public domain tar program:
183  * Public Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu).
184  *
185  * @(#)list.c 1.18 9/23/86 Public Domain - gnu
186  * $Id$
187  *
188  * Comments changed and some code/comments reformatted
189  * for file command by Ian Darwin.
190  */
191
192 /* The magic field is filled with this if uname and gname are valid. */
193 #define TMAGIC          "ustar  "       /* 7 chars and a null */
194
195 /*
196  * Header block on tape.
197  *
198  * I'm going to use traditional DP naming conventions here.
199  * A "block" is a big chunk of stuff that we do I/O on.
200  * A "record" is a piece of info that we care about.
201  * Typically many "record"s fit into a "block".
202  */
203 #define RECORDSIZE      512
204 #define NAMSIZ  100
205 #define TUNMLEN 32
206 #define TGNMLEN 32
207
208 union tar_record {
209         char            charptr[RECORDSIZE];
210         struct header {
211                 char    name[NAMSIZ];
212                 char    mode[8];
213                 char    uid[8];
214                 char    gid[8];
215                 char    size[12];
216                 char    mtime[12];
217                 char    chksum[8];
218                 char    linkflag;
219                 char    linkname[NAMSIZ];
220                 char    magic[8];
221                 char    uname[TUNMLEN];
222                 char    gname[TGNMLEN];
223                 char    devmajor[8];
224                 char    devminor[8];
225         } header;
226 };
227
228 #define isodigit(c)     ( ((c) >= '0') && ((c) <= '7') )
229
230 static int from_oct(int, char *);       /* Decode octal number */
231
232 /*
233  * Return
234  *      0 if the checksum is bad (i.e., probably not a tar archive),
235  *      1 for old UNIX tar file,
236  *      2 for Unix Std (POSIX) tar file.
237  */
238 static int
239 is_tar(char *buf, unsigned nbytes)
240 {
241         union tar_record *header = (union tar_record *)buf;
242         int     i;
243         int     sum, recsum;
244         char    *p;
245
246         if (nbytes < sizeof(union tar_record))
247                 return 0;
248
249         recsum = from_oct(8,  header->header.chksum);
250
251         sum = 0;
252         p = header->charptr;
253         for (i = sizeof(union tar_record); --i >= 0;) {
254                 /*
255                  * We can't use unsigned char here because of old compilers,
256                  * e.g. V7.
257                  */
258                 sum += 0xFF & *p++;
259         }
260
261         /* Adjust checksum to count the "chksum" field as blanks. */
262         for (i = sizeof(header->header.chksum); --i >= 0;)
263                 sum -= 0xFF & header->header.chksum[i];
264         sum += ' '* sizeof header->header.chksum;
265
266         if (sum != recsum)
267                 return 0;       /* Not a tar archive */
268
269         if (0==strcmp(header->header.magic, TMAGIC))
270                 return 2;               /* Unix Standard tar archive */
271
272         return 1;                       /* Old fashioned tar archive */
273 }
274
275
276 /*
277  * Quick and dirty octal conversion.
278  *
279  * Result is -1 if the field is invalid (all blank, or nonoctal).
280  */
281 static int
282 from_oct(int digs, char *where)
283 {
284         int     value;
285
286         while (*where==' ' || *where=='\f' || *where=='\n' || *where=='\r' || *where=='\t' || *where=='\v') {
287                 where++;
288                 if (--digs <= 0)
289                         return -1;              /* All blank field */
290         }
291         value = 0;
292         while (digs > 0 && isodigit(*where)) {  /* Scan til nonoctal */
293                 value = (value << 3) | (*where++ - '0');
294                 --digs;
295         }
296
297         if (digs > 0 && *where!='\0' && *where!=' ' && *where!='\f' && *where!='\n' && *where!='\r' && *where!='\t' && *where!='\v')
298                 return -1;                      /* Ended on non-space/nul */
299
300         return value;
301 }
302
303
304 /* --- */
305
306 /** Constructor.
307  * @return 0 on success, positive for various failure reasons
308  */
309 static int xOpen4Read(struct Readable *self, char const* filename) {
310   unsigned i;
311   char buf[RECORDSIZE];
312   FILE *f=fopen(filename, "rb");
313   if (f==NULL) return 4;
314   i=sizeof(buf); while (i--!=0) buf[i]='\0';
315   if (5>fread(buf, 1, sizeof(buf), f)) return 2;
316   if (0) {
317   } else if (buf[0]==0102 && buf[1]==0132 && buf[2]==0150) {
318 #if HAVE_BZ2LIB
319     fclose(f);
320     self->xOpen4Read=xBZ2_Open4Read;
321     self->xClose=xBZ2_Close;
322     self->xRead=xBZ2_Read;
323     self->xError=xBZ2_Error;
324     if (xBZ2_Open4Read(self, filename)!=0) return 1;
325 #else
326     error("bzip2 compression not compiled in");
327 #endif
328   } else if (buf[0]==0037 && (buf[1]&255)==0213 && (buf[2]&255)<=8) {
329 #if HAVE_ZLIB
330     fclose(f);
331     self->xOpen4Read=xGZ_Open4Read;
332     self->xClose=xGZ_Close;
333     self->xRead=xGZ_Read;
334     self->xError=xGZ_Error;
335     if (xGZ_Open4Read(self, filename)!=0) return 1;
336 #else
337     error("gzip compression not compiled in");
338 #endif
339   } else if (buf[0]==0037 && (buf[1]&255)==0235) {
340     fclose(f);
341     self->xOpen4Read=xZ_Open4Read;
342     self->xClose=xZ_Close;
343     self->xRead=xZ_Read;
344     self->xError=xZ_Error;
345     if (xZ_Open4Read(self, filename)!=0) return 1;
346   } else if ((buf[257]==0165 && buf[258]==0163 && buf[259]==0164
347            && buf[260]==0141 && buf[261]==0162
348            && (buf[262]==0000 || (buf[262]==0040 && buf[263]==0040 && buf[264]==0))
349              ) || is_tar(buf, sizeof(buf))
350             ) {
351     rewind(f);
352     self->f=f; /* shortcut */
353     self->xOpen4Read=xU_Open4Read;
354     self->xClose=xU_Close;
355     self->xRead=xU_Read;
356     self->xError=xU_Error;
357   } else return 3;
358   return 0;
359 }
360
361 /* --- .Z LZ uncompression */
362
363 /* code derived from
364  * (N)compress42.c - File compression ala IEEE Computer, Mar 1992.
365  * Modified by pts@fazekas.hu at Wed Jul 18 17:25:38 CEST 2001.
366  *
367  * Authors:
368  *   Spencer W. Thomas   (decvax!harpo!utah-cs!utah-gr!thomas)
369  *   Jim McKie           (decvax!mcvax!jim)
370  *   Steve Davies        (decvax!vax135!petsd!peora!srd)
371  *   Ken Turkowski       (decvax!decwrl!turtlevax!ken)
372  *   James A. Woods      (decvax!ihnp4!ames!jaw)
373  *   Joe Orost           (decvax!vax135!petsd!joe)
374  *   Dave Mack           (csu@alembic.acs.com)
375  *   Peter Jannesen, Network Communication Systems
376  *                       (peter@ncs.nl)
377  *
378  * Revision 4.2.3  92/03/14 peter@ncs.nl
379  *   Optimise compress and decompress function and a lot of cleanups.
380  *   New fast hash algoritme added (if more than 800Kb available).
381  */
382 #define IBUFSIZ 4096
383
384 #define NOFUNCDEF
385 #undef DOS
386 #undef COMPATIBLE
387 #undef DONTPTS /* `#define DONTPTS 1' means original uncompress */
388 #undef DIRENT
389 #undef RECURSIVE
390 #undef SYSDIR
391 #undef UTIME_H
392 #undef BYTEORDER
393 #define BITS 16 /* _must_ be able to handle this many bits, anyway */
394 #define FAST
395 #if 1 /* !! */
396 #  define ASSERT(x) do { if (!(x)) abort(); } while(0)
397 #else
398 #  define ASSERT(x)
399 #endif
400
401 #include <stdio.h>
402 #include <string.h> /* memcpy(), memset() */
403 #include <stdlib.h> /* abort(), exit() */
404
405 /* #define fx_write(a,b,c) fwrite((b),1,(c),(a)) */
406 #define fx_read(a,b,c)  fread( (b),1,(c),(a))
407 #define fx_stdin        stdin
408 #define fx_stdout       stdout
409 #define fx_type         FILE*
410
411 #ifndef NOFUNCDEF
412 #define    LARGS(a)    ()    /* Relay on include files for libary func defs. */
413     extern    void    *malloc LARGS((int));
414     extern    void    free    LARGS((void *));
415 #ifndef _IBMR2
416     extern    int        open    LARGS((char const *,int,...));
417 #endif
418     extern    int        close    LARGS((int));
419     extern    int        read    LARGS((int,void *,int));
420     extern    int        write    LARGS((int,void const *,int));
421     extern    int        chmod    LARGS((char const *,int));
422     extern    int        unlink    LARGS((char const *));
423     extern    int        chown    LARGS((char const *,int,int));
424     extern    int        utime    LARGS((char const *,struct utimbuf const *));
425     extern    char    *strcpy    LARGS((char *,char const *));
426     extern    char    *strcat    LARGS((char *,char const *));
427     extern    int        strcmp    LARGS((char const *,char const *));
428     extern    unsigned strlen    LARGS((char const *));
429     extern    void    *memset    LARGS((void *,char,unsigned int));
430     extern    void    *memcpy    LARGS((void *,void const *,unsigned int));
431     extern    int        atoi    LARGS((char const *));
432     extern    void    exit    LARGS((int));
433     extern    int        isatty    LARGS((int));
434 #endif
435
436 #ifdef    DEF_ERRNO
437     extern int    errno;
438 #endif
439
440 #undef    min
441 #define   min(a,b)    ((a>b) ? b : a)
442
443 #define    MAGIC_1        (char_type)'\037'/* First byte of compressed file                */
444 #define    MAGIC_2        (char_type)'\235'/* Second byte of compressed file                */
445 #define BIT_MASK    0x1f            /* Mask for 'number of compresssion bits'        */
446                                     /* Masks 0x20 and 0x40 are free.                  */
447                                     /* I think 0x20 should mean that there is        */
448                                     /* a fourth header byte (for expansion).        */
449 #define BLOCK_MODE    0x80          /* Block compresssion if table is full and        */
450                                     /* compression rate is dropping flush tables    */
451
452             /* the next two codes should not be changed lightly, as they must not   */
453             /* lie within the contiguous general code space.                        */
454 #define FIRST    257                    /* first free entry                             */
455 #define CLEAR    256                    /* table clear output code                         */
456
457 #define INIT_BITS 9            /* initial number of bits/code */
458
459 #ifndef    BYTEORDER
460 #    define    BYTEORDER    0000
461 #endif
462
463 #ifndef    NOALLIGN
464 #    define    NOALLIGN    0
465 #endif
466
467 /*
468  * machine variants which require cc -Dmachine:  pdp11, z8000, DOS
469  */
470
471 #ifdef interdata    /* Perkin-Elmer                                                    */
472 #    define SIGNED_COMPARE_SLOW    /* signed compare is slower than unsigned             */
473 #endif
474
475 #ifdef MSDOS            /* PC/XT/AT (8088) processor                                    */
476 #    undef    BYTEORDER
477 #    define    BYTEORDER     4321
478 #    undef    NOALLIGN
479 #    define    NOALLIGN    1
480 #endif /* DOS */
481
482 #ifndef    O_BINARY
483 #    define    O_BINARY    0    /* System has no binary mode                            */
484 #endif
485
486 typedef long int code_int;
487
488 #ifdef SIGNED_COMPARE_SLOW
489     typedef unsigned short int   count_short;
490     typedef unsigned long int    cmp_code_int;    /* Cast to make compare faster    */
491 #else
492     typedef long int             cmp_code_int;
493 #endif
494
495 typedef    unsigned char    char_type;
496
497 #define MAXCODE(n)    (1L << (n))
498
499 #ifndef    REGISTERS
500 #    define    REGISTERS    20
501 #endif
502 #define    REG1
503 #define    REG2
504 #define    REG3
505 #define    REG4
506 #define    REG5
507 #define    REG6
508 #define    REG7
509 #define    REG8
510 #define    REG9
511 #define    REG10
512 #define    REG11
513 #define    REG12
514 #define    REG13
515 #define    REG14
516 #define    REG15
517 #define    REG16
518 #if REGISTERS >= 1
519 #    undef    REG1
520 #    define    REG1    register
521 #endif
522 #if REGISTERS >= 2
523 #    undef    REG2
524 #    define    REG2    register
525 #endif
526 #if REGISTERS >= 3
527 #    undef    REG3
528 #    define    REG3    register
529 #endif
530 #if REGISTERS >= 4
531 #    undef    REG4
532 #    define    REG4    register
533 #endif
534 #if REGISTERS >= 5
535 #    undef    REG5
536 #    define    REG5    register
537 #endif
538 #if REGISTERS >= 6
539 #    undef    REG6
540 #    define    REG6    register
541 #endif
542 #if REGISTERS >= 7
543 #    undef    REG7
544 #    define    REG7    register
545 #endif
546 #if REGISTERS >= 8
547 #    undef    REG8
548 #    define    REG8    register
549 #endif
550 #if REGISTERS >= 9
551 #    undef    REG9
552 #    define    REG9    register
553 #endif
554 #if REGISTERS >= 10
555 #    undef    REG10
556 #    define    REG10    register
557 #endif
558 #if REGISTERS >= 11
559 #    undef    REG11
560 #    define    REG11    register
561 #endif
562 #if REGISTERS >= 12
563 #    undef    REG12
564 #    define    REG12    register
565 #endif
566 #if REGISTERS >= 13
567 #    undef    REG13
568 #    define    REG13    register
569 #endif
570 #if REGISTERS >= 14
571 #    undef    REG14
572 #    define    REG14    register
573 #endif
574 #if REGISTERS >= 15
575 #    undef    REG15
576 #    define    REG15    register
577 #endif
578 #if REGISTERS >= 16
579 #    undef    REG16
580 #    define    REG16    register
581 #endif
582
583 #if BYTEORDER == 4321 && NOALLIGN == 1
584 #define    input(b,o,c,n,m){                                                 \
585                             (c) = (*(long *)(&(b)[(o)>>3])>>((o)&0x7))&(m);  \
586                             (o) += (n);                                      \
587                         }
588 #else
589 #define    input(b,o,c,n,m){    REG1 char_type         *p = &(b)[(o)>>3];    \
590                             (c) = ((((long)(p[0]))|((long)(p[1])<<8)|        \
591                                      ((long)(p[2])<<16))>>((o)&0x7))&(m);    \
592                             (o) += (n);                                      \
593                         }
594 #endif
595
596 /*
597  * 8086 & 80286 Has a problem with array bigger than 64K so fake the array
598  * For processors with a limited address space and segments.
599  */
600 /*
601  * To save much memory, we overlay the table used by compress() with those
602  * used by lz_decompress().  The tab_prefix table is the same size and type
603  * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
604  * get this from the beginning of htab.  The output stack uses the rest
605  * of htab, and contains characters.  There is plenty of room for any
606  * possible stack (stack used to be 8000 characters).
607  */
608 #ifdef MAXSEG_64K
609  #error removed MAXSEG_64K
610 #else    /* Normal machine */
611 #    define HSIZE (1<<16) /* (1<<15) is few, (1<<16) seems to be OK, (1<<17) is proven safe OK */
612 #if 0
613     char_type lz_htab[2*HSIZE]; /* Dat: HSIZE for the codes and HSIZE for de_stack */
614     unsigned short lz_codetab[HSIZE];
615 #endif
616 #    define    tab_prefixof(i)         codetab[i]
617 #    define    tab_suffixof(i)         htab[i]
618 #    define    de_stack                (htab+(sizeof(htab[0])*(2*HSIZE-1)))
619 #    define    clear_tab_prefixof()    memset(codetab, 0, 256*sizeof(codetab[0]));
620 #endif    /* MAXSEG_64K */
621
622 typedef struct lz_reader_t {
623   /* constants (don't change during decompression) */
624   fx_type fdin;
625   char_type* inbuf; /* [IBUFSIZ+64];  Input buffer */
626   char_type* htab; /* [2*HSIZE];  Dat: HSIZE for the codes and HSIZE for de_stack */
627   unsigned short* codetab; /* [HSIZE]; */
628   int blkmode;
629   code_int maycode;
630
631   /* variables that have to be saved */
632   char_type        *stackp;
633   code_int         code;
634   int              finchar;
635   code_int         oldcode;
636   code_int         incode;
637   int              inbits;
638   int              posbits;
639   int              insize;
640   int              bitmask;
641   code_int         freeent;
642   code_int         maxcode;
643   int              n_bits;
644   int              rsize;
645   int              maxbits;
646 } lz_reader_t;
647
648 static struct lz_reader_t* lz_read_new(FILE *inf) {
649   lz_reader_t* reader=(lz_reader_t*)malloc(sizeof(lz_reader_t));
650   reader->fdin=NULL;
651   if (reader!=NULL) {
652     if (lz_read_init(reader, inf)!=0) {
653       lz_close(reader);
654       free(reader); reader=NULL;
655     }
656   }
657   return reader;
658 }
659 static int lz_close(struct lz_reader_t* reader) {
660   FILE *fin=reader->fdin;
661   reader->rsize=-1;
662   if (reader->inbuf!=NULL)   { free(reader->inbuf  ); reader->inbuf  =NULL; }
663   if (reader->htab!=NULL)    { free(reader->htab   ); reader->htab   =NULL; }
664   if (reader->codetab!=NULL) { free(reader->codetab); reader->codetab=NULL; }
665   if (fin==NULL) return 0;
666   reader->fdin=NULL;
667   return fclose(fin);
668 }
669 /** @param Zreader uninitialized
670  * @return 0 on success
671  */
672 static int lz_read_init(struct lz_reader_t* reader, FILE *inf) {
673   char_type* htab;
674   unsigned short* codetab;
675   code_int codex;
676   reader->fdin=inf;
677   if (NULL==(reader->inbuf=malloc(IBUFSIZ+67))) return 1;
678   if (NULL==(htab=reader->htab=malloc(2*HSIZE))) return 1; /* Dat: HSIZE for the codes and HSIZE for de_stack */
679   if (NULL==(codetab=reader->codetab=malloc(sizeof(reader->codetab[0])*HSIZE))) return 1;
680   reader->insize=0;
681   reader->rsize=0;
682   while (reader->insize < 3 && (reader->rsize = fx_read(reader->fdin, reader->inbuf+reader->insize, IBUFSIZ)) > 0)
683       reader->insize += reader->rsize;
684
685   if (reader->insize < 3
686    || reader->inbuf[0] != (char_type)'\037' /* First byte of compressed file */
687    || reader->inbuf[1] != (char_type)'\235' /* Second byte of compressed file */
688   ) return reader->insize >= 0 ? 2 : 3;
689
690   reader->maxbits = reader->inbuf[2] & BIT_MASK;
691   reader->blkmode = reader->inbuf[2] & BLOCK_MODE;
692   reader->maycode = MAXCODE(reader->maxbits);
693
694   if (reader->maxbits > BITS) return 4;
695 #if 0
696       fprintf(stderr,
697               "%s: compressed with %d bits, can only handle %d bits\n",
698               ("stdin"), maxbits, BITS);
699       return 4;
700 #endif
701
702   reader->maxcode = MAXCODE(reader->n_bits = INIT_BITS)-1;
703   reader->bitmask = (1<<reader->n_bits)-1;
704   reader->oldcode = -1;
705   reader->finchar = 0;
706   reader->posbits = 3<<3;
707   reader->stackp  = NULL;
708   reader->code    = -1;
709   reader->incode  = -1; /* fake */
710   reader->inbits  = 0;  /* fake */
711
712   reader->freeent = ((reader->blkmode) ? FIRST : 256);
713
714   clear_tab_prefixof();    /* As above, initialize the first 256 entries in the table. */
715
716   for (codex = 255 ; codex >= 0 ; --codex)
717       tab_suffixof(codex) = (char_type)codex;
718   return 0; /* success */
719 }
720
721 /*
722  * Decompress stdin to stdout.  This routine adapts to the codes in the
723  * file building the "string" table on-the-fly; requiring no table to
724  * be stored in the compressed file.  The tables used herein are shared
725  * with those of the compress() routine.  See the definitions above.
726  */
727 static unsigned lz_read(struct lz_reader_t* reader, char* outbuf, unsigned obufsize) {
728 /* static int lz_decompress(fx_type fdin, fx_type fdout) */
729   REG2    char_type        *stackp;
730   REG3    code_int         code;
731   REG4    int              finchar;
732   REG5    code_int         oldcode;
733   REG6    code_int         incode;
734   REG7    int              inbits;
735   REG8    int              posbits;
736   REG10   int              insize;
737   REG11   int              bitmask;
738   REG12   code_int         freeent;
739   REG13   code_int         maxcode;
740   REG14   code_int         maycode;
741   REG15   int              n_bits;
742   REG16   int              rsize;
743           int              blkmode;
744           int              maxbits;
745           char_type* inbuf;
746           char_type* htab;
747           unsigned short* codetab;
748           fx_type fdin;
749   REG9    unsigned outpos=0; /* not restored */
750
751 #if 0
752   fprintf(stderr, ":: call rsize=%d oldcode=%d\n", reader->rsize, (int)reader->oldcode);
753 #endif
754   if (reader->rsize<1) return reader->rsize; /* already EOF or error */
755
756   stackp =reader->stackp;
757   code   =reader->code;
758   finchar=reader->finchar;
759   oldcode=reader->oldcode;
760   incode =reader->incode;
761   inbits =reader->inbits;
762   posbits=reader->posbits;
763   insize =reader->insize;
764   bitmask=reader->bitmask;
765   freeent=reader->freeent;
766   maxcode=reader->maxcode;
767   maycode=reader->maycode;
768   n_bits =reader->n_bits;
769   rsize  =reader->rsize;
770   blkmode=reader->blkmode;
771   maxbits=reader->maxbits;
772   htab   =reader->htab;
773   codetab=reader->codetab;
774   fdin   =reader->fdin;
775   inbuf  =reader->inbuf;
776   if (oldcode!=-1) goto try_fit; /* lz_read() called again */
777
778   do {
779     resetbuf: ;
780     {
781       REG1     int    i;
782       int                e;
783       int                o;
784
785       e = insize-(o = (posbits>>3));
786
787       for (i = 0 ; i < e ; ++i)
788           inbuf[i] = inbuf[i+o];
789
790       insize = e;
791       posbits = 0;
792     }
793
794     if ((unsigned)insize < /*sizeof(inbuf)-IBUFSIZ*/ 64) {
795 #if 0
796       fprintf(stderr, ":: try read insize=%d\n", insize);
797 #endif
798       if ((rsize = fx_read(fdin, inbuf+insize, IBUFSIZ)) < 0 || ferror(fdin)) return reader->rsize=-1;
799 #if 0
800       fprintf(stderr, ":: got rsize=%d\n", rsize);
801 #endif
802       insize += rsize;
803       inbuf[insize]=inbuf[insize+1]=inbuf[insize+2]=0;
804       /* ^^^ sentinel, so input() won't produce ``read uninitialized byte(s) in a block.'' */
805     }
806
807     inbits = ((rsize > 0) ? (insize - insize%n_bits)<<3 :
808                             (insize<<3)-(n_bits-1));
809
810     while (inbits > posbits) {
811       if (freeent > maxcode) {
812         posbits = ((posbits-1) + ((n_bits<<3) -
813                          (posbits-1+(n_bits<<3))%(n_bits<<3)));
814         ++n_bits;
815         if (n_bits == maxbits)
816             maxcode = maycode;
817         else
818             maxcode = MAXCODE(n_bits)-1;
819
820         bitmask = (1<<n_bits)-1;
821         goto resetbuf;
822       }
823
824 #if 0
825       fputc('#',stderr);
826 #endif
827       input(inbuf,posbits,code,n_bits,bitmask);
828
829       if (oldcode == -1) {
830         outbuf[outpos++] = (char_type)(finchar = (int)(oldcode = code));
831         continue;
832       }
833       /* Dat: oldcode will never be -1 again */
834
835       if (code == CLEAR && blkmode) {
836         clear_tab_prefixof();
837         freeent = FIRST - 1;
838         posbits = ((posbits-1) + ((n_bits<<3) -
839                     (posbits-1+(n_bits<<3))%(n_bits<<3)));
840         maxcode = MAXCODE(n_bits = INIT_BITS)-1;
841         bitmask = (1<<n_bits)-1;
842         goto resetbuf;
843       }
844
845       incode = code;
846       stackp = de_stack;
847
848       if (code >= freeent) {    /* Special case for KwKwK string.    */
849         if (code > freeent) {
850             posbits -= n_bits;
851 #if 0
852             {
853                 REG1 char_type         *p;
854                 p = &inbuf[posbits>>3];
855
856                 fprintf(stderr, "uncompress: insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)\n", insize, posbits,
857                         p[-1],p[0],p[1],p[2],p[3], (posbits&07));
858                 fprintf(stderr, "uncompress: corrupt input\n");
859                 abort();
860             }
861 #else
862             return reader->rsize=-1;
863 #endif
864         }
865
866         *--stackp = (char_type)finchar;
867         code = oldcode;
868       }
869
870       while ((cmp_code_int)code >= (cmp_code_int)256) {
871         /* Generate output characters in reverse order */
872         *--stackp = tab_suffixof(code);
873         code = tab_prefixof(code);
874       }
875 #if 0
876       fprintf(stderr, ":: to stack code=%d\n", (int)code);
877 #endif
878       *--stackp =    (char_type)(finchar = tab_suffixof(code));
879       ASSERT(outpos<=obufsize);
880
881       /* And put them out in forward order */
882       {
883         REG1 int    i;
884
885         try_fit: if (outpos+(i = (de_stack-stackp)) >= obufsize) {
886           /* The entire stack doesn't fit into the outbuf */
887           i=obufsize-outpos;
888           memcpy(outbuf+outpos, stackp, i);
889           stackp+=i;
890           /* vvv Dat: blkmode, maycode, inbuf, htab, codetab, fdin need not be saved */
891           reader->stackp =stackp;
892           reader->code   =code;
893           reader->finchar=finchar;
894           reader->oldcode=oldcode;
895           reader->incode =incode;
896           reader->inbits =inbits;
897           reader->posbits=posbits;
898           reader->insize =insize;
899           reader->bitmask=bitmask;
900           reader->freeent=freeent;
901           reader->maxcode=maxcode;
902           reader->maycode=maycode;
903           reader->n_bits =n_bits;
904           reader->rsize  =rsize;
905           reader->blkmode=blkmode;
906           reader->maxbits=maxbits;
907 #if 0
908           fprintf(stderr, ":: return obufsize=%d oldcode=%d\n", obufsize, (int)oldcode);
909 #endif
910           return obufsize;
911           /* Dat: co-routine return back to try_fit, with outpos==0, outbuf=... obufsize=... */
912         } else {
913           memcpy(outbuf+outpos, stackp, i);
914           outpos += i;
915         }
916       }
917       /* Dat: ignore stackp from now */
918
919       if ((code = freeent) < maycode) { /* Generate the new entry. */
920 #if 0
921         fprintf(stderr, ":: new entry code=(%d)\n", (int)code);
922 #endif
923         tab_prefixof(code) = (unsigned short)oldcode;
924         tab_suffixof(code) = (char_type)finchar;
925         freeent = code+1;
926       }
927       oldcode = incode;    /* Remember previous code.    */
928     }
929   } while (rsize > 0);
930   reader->rsize=0;
931   return outpos;
932 }
933
934 /* --- */
935
936 /* Values used in typeflag field.  */
937
938 #define REGTYPE  '0'            /* regular file */
939 #define AREGTYPE '\0'           /* regular file */
940 #define LNKTYPE  '1'            /* link */
941 #define SYMTYPE  '2'            /* reserved */
942 #define CHRTYPE  '3'            /* character special */
943 #define BLKTYPE  '4'            /* block special */
944 #define DIRTYPE  '5'            /* directory */
945 #define FIFOTYPE '6'            /* FIFO special */
946 #define CONTTYPE '7'            /* reserved */
947
948 #define BLOCKSIZE 512
949
950 struct tar_header
951 {                               /* byte offset */
952   char name[100];               /*   0 */
953   char mode[8];                 /* 100 */
954   char uid[8];                  /* 108 */
955   char gid[8];                  /* 116 */
956   char size[12];                /* 124 */
957   char mtime[12];               /* 136 */
958   char chksum[8];               /* 148 */
959   char typeflag;                /* 156 */
960   char linkname[100];           /* 157 */
961   char magic[6];                /* 257 */
962   char version[2];              /* 263 */
963   char uname[32];               /* 265 */
964   char gname[32];               /* 297 */
965   char devmajor[8];             /* 329 */
966   char devminor[8];             /* 337 */
967   char prefix[155];             /* 345 */
968                                 /* 500 */
969 };
970
971 union tar_buffer {
972   char               buffer[BLOCKSIZE];
973   struct tar_header  header;
974 };
975
976 enum { TGZ_EXTRACT = 0, TGZ_LIST };
977
978 #if 0
979 static char *TGZfname   OF((const char *));
980 void TGZnotfound        OF((const char *));
981
982 int getoct              OF((char *, int));
983 char *strtime           OF((time_t *));
984 int ExprMatch           OF((char *,char *));
985
986 int makedir             OF((char *));
987 int matchname           OF((int,int,char **,char *));
988
989 void error              OF((const char *));
990 int  tar                OF((gzFile, int, int, int, char **));
991
992 void help               OF((int));
993 int main                OF((int, char **));
994 #endif
995
996 static char *prog;
997
998 static void error(const char *msg) {
999   fprintf(stderr, "%s: %s\n", prog, msg);
1000   exit(1);
1001 }
1002
1003 #ifdef DOSISH
1004 /* This will give a benign warning */
1005 static char *TGZprefix[] = { "\0", ".tgz", ".tar.gz", ".tar.bz2", ".tar.Z", ".tar", NULL };
1006
1007 /* Return the real name of the TGZ archive */
1008 /* or NULL if it does not exist. */
1009 static char *TGZfname OF((const char *fname))
1010 {
1011   static char buffer[1024];
1012   int origlen,i;
1013
1014   strcpy(buffer,fname);
1015   origlen = strlen(buffer);
1016
1017   for (i=0; TGZprefix[i]; i++)
1018     {
1019        strcpy(buffer+origlen,TGZprefix[i]);
1020        if (access(buffer,F_OK) == 0)
1021          return buffer;
1022     }
1023   return NULL;
1024 }
1025
1026 /* error message for the filename */
1027
1028 static void TGZnotfound OF((const char *fname))
1029 {
1030   int i;
1031
1032   fprintf(stderr,"%s : couldn't find ",prog);
1033   for (i=0;TGZprefix[i];i++)
1034     fprintf(stderr,(TGZprefix[i+1]) ? "%s%s, " : "or %s%s\n",
1035             fname,
1036             TGZprefix[i]);
1037   exit(1);
1038 }
1039 #endif
1040
1041
1042 /* help functions */
1043
1044 static int getoct(char *p,int width)
1045 {
1046   int result = 0;
1047   char c;
1048
1049   while (width --)
1050     {
1051       c = *p++;
1052       if (c == ' ')
1053         continue;
1054       if (c == 0)
1055         break;
1056       result = result * 8 + (c - '0');
1057     }
1058   return result;
1059 }
1060
1061 static char *strtime (time_t *t)
1062 {
1063   struct tm   *local;
1064   static char result[32];
1065
1066   local = localtime(t);
1067   sprintf(result,"%2d/%02d/%4d %02d:%02d:%02d",
1068           local->tm_mday, local->tm_mon+1, local->tm_year+1900,
1069           local->tm_hour, local->tm_min,   local->tm_sec);
1070   return result;
1071 }
1072
1073
1074 /* regular expression matching */
1075
1076 #define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
1077
1078 static int ExprMatch(char *string,char *expr)
1079 {
1080   while (1)
1081     {
1082       if (ISSPECIAL(*expr))
1083         {
1084           if (*expr == '/')
1085             {
1086               if (*string != '\\' && *string != '/')
1087                 return 0;
1088               string ++; expr++;
1089             }
1090           else if (*expr == '*')
1091             {
1092               if (*expr ++ == 0)
1093                 return 1;
1094               while (*++string != *expr)
1095                 if (*string == 0)
1096                   return 0;
1097             }
1098         }
1099       else
1100         {
1101           if (*string != *expr)
1102             return 0;
1103           if (*expr++ == 0)
1104             return 1;
1105           string++;
1106         }
1107     }
1108 }
1109
1110 /* recursive make directory */
1111 /* abort if you get an ENOENT errno somewhere in the middle */
1112 /* e.g. ignore error "mkdir on existing directory" */
1113 /* */
1114 /* return 1 if OK */
1115 /*        0 on error */
1116
1117 static int makedir (char *newdir) {
1118   /* avoid calling strdup, since it's not ansi C */
1119   int  len = strlen(newdir);
1120   char *p, *buffer = malloc(len+1);
1121   if (buffer==NULL) error("out of memory");
1122   memcpy(buffer,newdir,len+1);
1123
1124   if (len <= 0) {
1125     free(buffer);
1126     return 0;
1127   }
1128   if (buffer[len-1] == '/') {
1129     buffer[len-1] = '\0';
1130   }
1131   if (mkdir(buffer, 0775) == 0)
1132     {
1133       free(buffer);
1134       return 1;
1135     }
1136
1137   p = buffer+1;
1138   while (1)
1139     {
1140       char hold;
1141
1142       while(*p && *p != '\\' && *p != '/')
1143         p++;
1144       hold = *p;
1145       *p = 0;
1146       if ((mkdir(buffer, 0775) == -1) && (errno == ENOENT))
1147         {
1148           fprintf(stderr,"%s: couldn't create directory %s\n",prog,buffer);
1149           free(buffer);
1150           return 0;
1151         }
1152       if (hold == 0)
1153         break;
1154       *p++ = hold;
1155     }
1156   free(buffer);
1157   return 1;
1158 }
1159
1160 static int matchname (int arg,int argc,char **argv,char *fname)
1161 {
1162   if (arg == argc)              /* no arguments given (untgz tgzarchive) */
1163     return 1;
1164
1165   while (arg < argc)
1166     if (ExprMatch(fname,argv[arg++]))
1167       return 1;
1168
1169   return 0; /* ignore this for the moment being */
1170 }
1171
1172
1173 /* Tar file list or extract */
1174
1175 static int tar (Readable* rin,int action,int arg,int argc,char **argv, char const* TGZfile, int verbose, void (*step)(void *,int), void *data) {
1176   union  tar_buffer buffer;
1177   int    is_tar_ok=0;
1178   int    len;
1179   int    err;
1180   int    getheader = 1;
1181   int    remaining = 0;
1182   FILE   *outfile = NULL;
1183   char   fname[BLOCKSIZE];
1184   time_t tartime = 0;
1185   unsigned long last_read;
1186
1187 #if 0
1188   while (0<(len=rin->xRead(rin, &buffer, BLOCKSIZE))) {
1189     fwrite(buffer.buffer, 1, len, stdout);
1190   }
1191   exit(0);
1192 #endif
1193
1194   last_read = 0;
1195   if (action == TGZ_LIST)
1196     printf("     day      time     size                       file\n"
1197            " ---------- -------- --------- -------------------------------------\n");
1198   while (1) {
1199       len = rin->xRead(rin, &buffer, BLOCKSIZE);
1200       if (len+1 == 0)
1201         error (rin->xError(rin, &err));
1202       if (step) { step(data,total_read - last_read); last_read = total_read; }
1203       if (!is_tar_ok && !(is_tar_ok=is_tar(buffer.buffer, len))) {
1204         fprintf(stderr, "%s: compressed file not tared: %s\n", prog, TGZfile);
1205         if (action == TGZ_EXTRACT) {
1206           FILE *of;
1207           unsigned len0=strlen(TGZfile), len1=len0;
1208           char *ofn=malloc(len1+5);
1209           if (ofn==NULL) return 1;
1210           memcpy(ofn, TGZfile, len1+1);
1211           while (len1>0 && ofn[len1]!='/' && ofn[len1]!='\\' && ofn[len1]!='.') len1--;
1212           if (ofn[len1]=='.') ofn[len1]='\0'; else strcpy(ofn+len0, ".unc");
1213           /* ^^^ remove last extension, or add `.unc' */
1214           fprintf(stderr, "%s: extracting as: %s\n", prog, ofn);
1215           if (NULL==(of=fopen(ofn, "wb"))) error("fopen write error");
1216           do {
1217             fwrite(buffer.buffer, 1, len, of);
1218             if (ferror(of)) error("write error");
1219           } while (0<(len=rin->xRead(rin, &buffer, BLOCKSIZE))); /* Imp: larger blocks */
1220           len=ferror(of);
1221           if (0!=(len|fclose(of))) error("fclose write error");
1222           free(ofn);
1223           return 0;
1224         }
1225         return 1;
1226         /* !! */
1227       }
1228       /*
1229        * Always expect complete blocks to process
1230        * the tar information.
1231        */
1232       if (len != BLOCKSIZE)
1233         error("read: incomplete block read");
1234
1235       /*
1236        * If we have to get a tar header
1237        */
1238       if (getheader == 1)
1239         {
1240           /*
1241            * if we met the end of the tar
1242            * or the end-of-tar block,
1243            * we are done
1244            */
1245           if ((len == 0)  || (buffer.header.name[0]== 0)) break;
1246
1247           tartime = (time_t)getoct(buffer.header.mtime,12);
1248           strcpy(fname,buffer.header.name);
1249
1250           switch (buffer.header.typeflag)
1251             {
1252             case DIRTYPE:
1253               if (action == TGZ_LIST)
1254                 printf(" %s     <dir> %s\n",strtime(&tartime),fname);
1255               if (action == TGZ_EXTRACT)
1256                 makedir(fname);
1257               break;
1258             case REGTYPE:
1259             case AREGTYPE:
1260               remaining = getoct(buffer.header.size,12);
1261               if (action == TGZ_LIST)
1262                 printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
1263               if (action == TGZ_EXTRACT)
1264                 {
1265                   if ((remaining) && (matchname(arg,argc,argv,fname)))
1266                     {
1267                       outfile = fopen(fname,"wb");
1268                       if (outfile == NULL) {
1269                         /* try creating directory */
1270                         char *p = strrchr(fname, '/');
1271                         if (p != NULL) {
1272                           *p = '\0';
1273                           makedir(fname);
1274                           *p = '/';
1275                           outfile = fopen(fname,"wb");
1276                         }
1277                       }
1278                       if (verbose)
1279                         fprintf(stderr,
1280                                 "%s %s\n",
1281                                 (outfile) ? "Extracting" : "Couldn't create",
1282                                 fname);
1283                     }
1284                   else
1285                     outfile = NULL;
1286                 }
1287               /*
1288                * could have no contents
1289                */
1290               getheader = (remaining) ? 0 : 1;
1291               break;
1292             default:
1293               if (action == TGZ_LIST)
1294                 printf(" %s     <---> %s\n",strtime(&tartime),fname);
1295               break;
1296             }
1297         }
1298       else
1299         {
1300           unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
1301
1302           if ((action == TGZ_EXTRACT) && (outfile != NULL))
1303             {
1304               if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
1305                 {
1306                   fprintf(stderr,"%s : error writing %s skipping...\n",prog,fname);
1307                   fclose(outfile);
1308                   unlink(fname);
1309                 }
1310             }
1311           remaining -= bytes;
1312           if (remaining == 0)
1313             {
1314               getheader = 1;
1315               if ((action == TGZ_EXTRACT) && (outfile != NULL))
1316                 {
1317 #ifdef WIN32
1318                   HANDLE hFile;
1319                   FILETIME ftm,ftLocal;
1320                   SYSTEMTIME st;
1321                   struct tm localt;
1322
1323                   fclose(outfile);
1324
1325                   localt = *localtime(&tartime);
1326
1327                   hFile = CreateFile(fname, GENERIC_READ | GENERIC_WRITE,
1328                                      0, NULL, OPEN_EXISTING, 0, NULL);
1329
1330                   st.wYear = (WORD)localt.tm_year+1900;
1331                   st.wMonth = (WORD)localt.tm_mon;
1332                   st.wDayOfWeek = (WORD)localt.tm_wday;
1333                   st.wDay = (WORD)localt.tm_mday;
1334                   st.wHour = (WORD)localt.tm_hour;
1335                   st.wMinute = (WORD)localt.tm_min;
1336                   st.wSecond = (WORD)localt.tm_sec;
1337                   st.wMilliseconds = 0;
1338                   SystemTimeToFileTime(&st,&ftLocal);
1339                   LocalFileTimeToFileTime(&ftLocal,&ftm);
1340                   SetFileTime(hFile,&ftm,NULL,&ftm);
1341                   CloseHandle(hFile);
1342
1343                   outfile = NULL;
1344 #else
1345                   struct utimbuf settime;
1346
1347                   settime.actime = settime.modtime = tartime;
1348
1349                   fclose(outfile);
1350                   outfile = NULL;
1351                   utime(fname,&settime);
1352 #endif
1353                 }
1354             }
1355         }
1356     }
1357
1358   if (rin->xClose(rin))
1359     error("failed gzclose");
1360
1361   return 0;
1362 }
1363
1364
1365 #ifdef DOSISH
1366 /* =========================================================== */
1367
1368 static void help(int exitval)
1369 {
1370   fprintf(stderr,
1371           "untarka v0.34: super untar + untar.Z"
1372 #ifdef HAVE_ZLIB
1373           " + untar.gz"
1374 #endif
1375 #ifdef HAVE_BZ2LIB
1376           " + untar.bz2"
1377 #endif
1378           "\ncontains code (C) by pts@fazekas.hu since 2003.01.28\n"
1379           "This program is under GPL >=2.0. There is NO WARRANTY. Use at your own risk!\n\n"
1380           "Usage: untarka [-x] tarfile          to extract all files\n"
1381           "       untarka -x tarfile fname ...  to extract selected files\n"
1382           "       untarka -l tarfile            to list archive contents\n"
1383           "       untarka -h                    to display this help\n");
1384   exit(exitval);
1385 }
1386
1387 /* ====================================================================== */
1388
1389 extern int _CRT_glob;
1390 int _CRT_glob = 0;      /* disable globbing of the arguments */
1391 #endif
1392 #ifdef COMPILE_MAIN
1393 int main(int argc,char **argv) {
1394     int         action = TGZ_EXTRACT;
1395     int         arg = 1;
1396     char        *TGZfile;
1397     Readable    r;
1398
1399 #if DOSISH
1400     setmode(0, O_BINARY);
1401 #endif
1402     prog = strrchr(argv[0],'\\');
1403     if (prog == NULL)
1404       {
1405         prog = strrchr(argv[0],'/');
1406         if (prog == NULL)
1407           {
1408             prog = strrchr(argv[0],':');
1409             if (prog == NULL)
1410               prog = argv[0];
1411             else
1412               prog++;
1413           }
1414         else
1415           prog++;
1416       }
1417     else
1418       prog++;
1419
1420     if (argc == 1)
1421       help(0);
1422
1423     if (strcmp(argv[arg],"-l") == 0 || strcmp(argv[arg],"-v") == 0) {
1424       action = TGZ_LIST;
1425       if (argc == ++arg) help(0);
1426     } else if (strcmp(argv[arg],"-x") == 0) {
1427       action = TGZ_EXTRACT;
1428       if (argc == ++arg) help(0);
1429     } else if (strcmp(argv[arg],"-h") == 0) help(0);
1430
1431     if ((TGZfile = TGZfname(argv[arg])) == NULL)
1432       TGZnotfound(argv[arg]);
1433
1434     ++arg;
1435     if ((action == TGZ_LIST) && (arg != argc))
1436       help(1);
1437
1438     /*
1439      *  Process the TGZ file
1440      */
1441     switch (action) {
1442      case TGZ_LIST:
1443      case TGZ_EXTRACT:
1444       switch (xOpen4Read(&r,TGZfile)) {
1445        case 0: break;
1446        case 1: fprintf(stderr, "%s: cannot decode compressed tar file: %s\n", prog, TGZfile); return 1;
1447        case 2: fprintf(stderr, "%s: tar file too short: %s\n", prog, TGZfile); return 1;
1448        case 3: fprintf(stderr, "%s: not a tar file: %s\n", prog, TGZfile); return 1;
1449        case 4: fprintf(stderr, "%s: cannot open tar file: %s\n", prog, TGZfile); return 1;
1450       }
1451       return tar(&r, action, arg, argc, argv, TGZfile, 1, 0, 0);
1452       default: error("Unknown option!");
1453     }
1454     return 0;
1455 }
1456 #endif
1457
1458 void
1459 tarextract(char *TGZfile,char *dest,int verbose, void (*step)(void *,int), void *data)
1460 {
1461    Readable    r;
1462    if (xOpen4Read(&r,TGZfile) == 0)
1463       {
1464       int arg = 2,
1465           argc = 2;
1466       char *argv[2];
1467       chdir( dest );
1468       argv[0] = "fgadmin";
1469       argv[1] = TGZfile;
1470       tar(&r, TGZ_EXTRACT, arg, argc, argv, TGZfile, 0, step, data);
1471       }
1472 }