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