--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define DUMMY \
+ WFLAGS="-ansi -pedantic -W -Wall -Wstrict-prototypes -Wtraditional -Wnested-externs -Winline -Wpointer-arith -Wbad-function-cast -Wcast-qual -Wmissing-prototypes -Wmissing-declarations -Wunused"; \
+ set -ex; \
+ gcc -s -O2 $WFLAGS -DHAVE_ZLIB -DHAVE_BZ2LIB untarka.c -o untarka -lz -lbz2; \
+ exit 0
+/*
+ * untarka.c -- Display contents and/or extract file from a tar file (possibly
+ * gzip'd, bzip'd or compress'd)
+ * by pts@fazekas.hu Wed Jan 29 00:38:31 CET 2003
+ * Z compiles fine at Wed Jan 29 14:20:32 CET 2003
+ * all run file at Wed Jan 29 15:50:39 CET 2003
+ * 0.34 Win32 runs fine at Wed Jan 29 16:02:57 GMT 2003
+ *
+ * Untarka extracts TAR (UNIX Tape ARchive) files without calling any
+ * external programs. It supports and autodetects multiple compression
+ * methods (.tar, .tar.Z, .tar.gz, .tar.bz2, .Z, .gz and .bz2). UNIX
+ * devices, sockets, hard links, symlinks, owners and permissions are
+ * ignored during extraction.
+ *
+ * written by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
+ * adaptation to Unix by Jean-loup Gailly <jloup@gzip.org>
+ * adaptation to bzip2 by José Fonseca <em96115@fe.up.pt>
+ * merged comressors by Szabó Péter <pts@fazekas.hu> at Tue Jan 28 23:40:23 CET 2003
+ * compiles on Win32 under MinGW, Linux with gcc.
+ *
+ * To get gzip (zlib) support, pass to gcc: -DHAVE_ZLIB -lz
+ * To get bzip2 (bzlib2) support, pass to gcc: -DHAVE_BZ2LIB -lbz2
+ * To get compress (lz) support, relax, since it's the default.
+ *
+ * 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
+ */
+/* Imp: symlink()
+ * Imp: mknod() device, socket
+ * Imp: hard link()
+ * Imp: chmod()
+ * Imp: chown()
+ * Imp: allow force non-tar gzipped file
+ * Imp: command line options
+ * Imp: uncompress ZIP files
+ * Dat: zlib is able to read uncompressed files, libbz2 isn't
+ * Dat: magic numbers:
+ * 257 string ustar\0 POSIX tar archive
+ * 257 string ustar\040\040\0 GNU tar archive
+ * 0 string BZh bzip2 compressed data
+ * 0 string \037\213 gzip compressed data
+ * 0 string \037\235 compress'd data
+ * is_tar.c() from file(1)
+ */
+
+#undef DOSISH
+#if defined(WIN32) || defined(__MINGW32__) || defined(__CYGWIN__) || MSC_VER > 1000
+# include <windows.h>
+# define DOSISH 1
+# undef __STRICT_ANSI__
+#endif
+#ifdef MSDOS
+# define DOSISH 1
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef DOSISH
+# include <direct.h>
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+
+#if DOSISH
+# ifndef F_OK
+# define F_OK (0)
+# endif
+# ifdef _MSC_VER
+# define mkdir(dirname,mode) _mkdir(dirname)
+# define unlink(fn) _unlink(fn)
+# define access(path,mode) _access(path,mode)
+# else
+# define mkdir(dirname,mode) _mkdir(dirname)
+# endif
+#else
+# include <utime.h>
+# include <sys/stat.h> /* mkdir() */
+#endif
+
+#ifndef OF /* function prototypes */
+# if defined(__STDC__) || defined(__PROTOTYPES__) || defined(__cplusplus)
+# define OF(args) args
+# else
+# define OF(args) args
+# endif
+#endif
+
+static void error OF((const char *msg));
+
+/* #define TAR_GZ 1 */
+
+typedef struct Readable {
+ /*private*/ void *f;
+ /** @return 0 on success, 1 on failure */
+ int (*xOpen4Read)(struct Readable* self, char const* filename);
+ /** @return 0 on success */
+ int (*xClose)(struct Readable* self);
+ /** @return (unsigned)-1 on error */
+ unsigned (*xRead)(struct Readable* self, void* buf, unsigned len);
+ char const* (*xError)(struct Readable* self, int *errnum_ret);
+} Readable;
+
+enum { FMT_U=1, FMT_Z=2, FMT_GZ=3, FMT_BZ2=4 };
+
+/* #define xFILE FILE* */
+static int xU_Open4Read(struct Readable* self, char const* filename) { return NULL==(self->f=fopen(filename,"rb")); }
+static int xU_Close(struct Readable* self) { return fclose((FILE*)self->f); }
+static unsigned xU_Read(struct Readable* self, void* buf, unsigned len) {
+ unsigned got=fread(buf,1,len,(FILE*)self->f);
+ return got>0 ? got : ferror((FILE*)self->f) ? 0U-1 : 0;
+}
+static char const* xU_Error(struct Readable* self, int *errnum_ret) { return (*errnum_ret=ferror((FILE*)self->f))?"I/O error":"OK"; }
+
+/* #define xFILE struct lz_reader_t* */
+struct lz_reader_t;
+static struct lz_reader_t* lz_read_new(FILE* inf);
+static int lz_read_init(struct lz_reader_t* reader, FILE* inf);
+static int lz_close(struct lz_reader_t* reader);
+static unsigned lz_read(struct lz_reader_t* reader, char*buf, unsigned len);
+
+static int xZ_Open4Read(struct Readable* self, char const* filename) {
+ FILE *f=fopen(filename,"rb");
+ if (NULL==f) return 1;
+ return NULL==(self->f=lz_read_new(f));
+}
+static int xZ_Close(struct Readable* self) {
+ int ret=lz_close((struct lz_reader_t*)self->f);
+ free((struct lz_reader_t*)self->f);
+ return ret;
+}
+static unsigned xZ_Read(struct Readable* self, void* buf, unsigned len) {
+ return lz_read((struct lz_reader_t*)self->f, buf, len);
+}
+static char const* xZ_Error(struct Readable* self, int *errnum_ret) {
+ (void)self;
+ *errnum_ret=0;
+ return "lzw error";
+}
+
+#if HAVE_ZLIB
+#include "zlib.h"
+/* #define xFILE gzFile */
+static int xGZ_Open4Read(struct Readable* self, char const* filename) { return NULL==(self->f=gzopen(filename,"rb")); }
+static int xGZ_Close(struct Readable* self) { return gzclose((gzFile)self->f); }
+static unsigned xGZ_Read(struct Readable* self, void* buf, unsigned len) { return gzread((gzFile)self->f,buf,len); }
+static char const* xGZ_Error(struct Readable* self, int *errnum_ret) { return gzerror((gzFile)self->f, errnum_ret); }
+#endif
+
+#if HAVE_BZ2LIB
+#include "bzlib.h"
+/* #define xFILE BZFILE* */
+static int xBZ2_Open4Read(struct Readable* self, char const* filename) { return NULL==(self->f=BZ2_bzopen(filename,"rb")); }
+static int xBZ2_Close(struct Readable* self) { BZ2_bzclose((BZFILE*)self->f); return 0; }
+static unsigned xBZ2_Read(struct Readable* self, void* buf, unsigned len) { return BZ2_bzread((BZFILE*)self->f,buf,len); }
+static char const* xBZ2_Error(struct Readable* self, int *errnum_ret) { return BZ2_bzerror((BZFILE*)self->f, errnum_ret); }
+#endif
+
+/* -- is_tar() */
+
+/*
+ * is_tar() -- figure out whether file is a tar archive.
+ *
+ * Stolen (by the author!) from the public domain tar program:
+ * Public Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu).
+ *
+ * @(#)list.c 1.18 9/23/86 Public Domain - gnu
+ * $Id$
+ *
+ * Comments changed and some code/comments reformatted
+ * for file command by Ian Darwin.
+ */
+
+/* The magic field is filled with this if uname and gname are valid. */
+#define TMAGIC "ustar " /* 7 chars and a null */
+
+/*
+ * Header block on tape.
+ *
+ * I'm going to use traditional DP naming conventions here.
+ * A "block" is a big chunk of stuff that we do I/O on.
+ * A "record" is a piece of info that we care about.
+ * Typically many "record"s fit into a "block".
+ */
+#define RECORDSIZE 512
+#define NAMSIZ 100
+#define TUNMLEN 32
+#define TGNMLEN 32
+
+union tar_record {
+ char charptr[RECORDSIZE];
+ struct header {
+ char name[NAMSIZ];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char linkflag;
+ char linkname[NAMSIZ];
+ char magic[8];
+ char uname[TUNMLEN];
+ char gname[TGNMLEN];
+ char devmajor[8];
+ char devminor[8];
+ } header;
+};
+
+#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
+
+static int from_oct(int, char *); /* Decode octal number */
+
+/*
+ * Return
+ * 0 if the checksum is bad (i.e., probably not a tar archive),
+ * 1 for old UNIX tar file,
+ * 2 for Unix Std (POSIX) tar file.
+ */
+static int
+is_tar(char *buf, unsigned nbytes)
+{
+ union tar_record *header = (union tar_record *)buf;
+ int i;
+ int sum, recsum;
+ char *p;
+
+ if (nbytes < sizeof(union tar_record))
+ return 0;
+
+ recsum = from_oct(8, header->header.chksum);
+
+ sum = 0;
+ p = header->charptr;
+ for (i = sizeof(union tar_record); --i >= 0;) {
+ /*
+ * We can't use unsigned char here because of old compilers,
+ * e.g. V7.
+ */
+ sum += 0xFF & *p++;
+ }
+
+ /* Adjust checksum to count the "chksum" field as blanks. */
+ for (i = sizeof(header->header.chksum); --i >= 0;)
+ sum -= 0xFF & header->header.chksum[i];
+ sum += ' '* sizeof header->header.chksum;
+
+ if (sum != recsum)
+ return 0; /* Not a tar archive */
+
+ if (0==strcmp(header->header.magic, TMAGIC))
+ return 2; /* Unix Standard tar archive */
+
+ return 1; /* Old fashioned tar archive */
+}
+
+
+/*
+ * Quick and dirty octal conversion.
+ *
+ * Result is -1 if the field is invalid (all blank, or nonoctal).
+ */
+static int
+from_oct(int digs, char *where)
+{
+ int value;
+
+ while (*where==' ' || *where=='\f' || *where=='\n' || *where=='\r' || *where=='\t' || *where=='\v') {
+ where++;
+ if (--digs <= 0)
+ return -1; /* All blank field */
+ }
+ value = 0;
+ while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */
+ value = (value << 3) | (*where++ - '0');
+ --digs;
+ }
+
+ if (digs > 0 && *where!='\0' && *where!=' ' && *where!='\f' && *where!='\n' && *where!='\r' && *where!='\t' && *where!='\v')
+ return -1; /* Ended on non-space/nul */
+
+ return value;
+}
+
+
+/* --- */
+
+/** Constructor.
+ * @return 0 on success, positive for various failure reasons
+ */
+static int xOpen4Read(struct Readable *self, char const* filename) {
+ unsigned i;
+ char buf[RECORDSIZE];
+ FILE *f=fopen(filename, "rb");
+ if (f==NULL) return 4;
+ i=sizeof(buf); while (i--!=0) buf[i]='\0';
+ if (5>fread(buf, 1, sizeof(buf), f)) return 2;
+ if (0) {
+ } else if (buf[0]==0102 && buf[1]==0132 && buf[2]==0150) {
+#if HAVE_BZ2LIB
+ fclose(f);
+ self->xOpen4Read=xBZ2_Open4Read;
+ self->xClose=xBZ2_Close;
+ self->xRead=xBZ2_Read;
+ self->xError=xBZ2_Error;
+ if (xBZ2_Open4Read(self, filename)!=0) return 1;
+#else
+ error("bzip2 compression not compiled in");
+#endif
+ } else if (buf[0]==0037 && (buf[1]&255)==0213 && (buf[2]&255)<=8) {
+#if HAVE_ZLIB
+ fclose(f);
+ self->xOpen4Read=xGZ_Open4Read;
+ self->xClose=xGZ_Close;
+ self->xRead=xGZ_Read;
+ self->xError=xGZ_Error;
+ if (xGZ_Open4Read(self, filename)!=0) return 1;
+#else
+ error("gzip compression not compiled in");
+#endif
+ } else if (buf[0]==0037 && (buf[1]&255)==0235) {
+ fclose(f);
+ self->xOpen4Read=xZ_Open4Read;
+ self->xClose=xZ_Close;
+ self->xRead=xZ_Read;
+ self->xError=xZ_Error;
+ if (xZ_Open4Read(self, filename)!=0) return 1;
+ } else if ((buf[257]==0165 && buf[258]==0163 && buf[259]==0164
+ && buf[260]==0141 && buf[261]==0162
+ && (buf[262]==0000 || (buf[262]==0040 && buf[263]==0040 && buf[264]==0))
+ ) || is_tar(buf, sizeof(buf))
+ ) {
+ rewind(f);
+ self->f=f; /* shortcut */
+ self->xOpen4Read=xU_Open4Read;
+ self->xClose=xU_Close;
+ self->xRead=xU_Read;
+ self->xError=xU_Error;
+ } else return 3;
+ return 0;
+}
+
+/* --- .Z LZ uncompression */
+
+/* code derived from
+ * (N)compress42.c - File compression ala IEEE Computer, Mar 1992.
+ * Modified by pts@fazekas.hu at Wed Jul 18 17:25:38 CEST 2001.
+ *
+ * Authors:
+ * Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
+ * Jim McKie (decvax!mcvax!jim)
+ * Steve Davies (decvax!vax135!petsd!peora!srd)
+ * Ken Turkowski (decvax!decwrl!turtlevax!ken)
+ * James A. Woods (decvax!ihnp4!ames!jaw)
+ * Joe Orost (decvax!vax135!petsd!joe)
+ * Dave Mack (csu@alembic.acs.com)
+ * Peter Jannesen, Network Communication Systems
+ * (peter@ncs.nl)
+ *
+ * Revision 4.2.3 92/03/14 peter@ncs.nl
+ * Optimise compress and decompress function and a lot of cleanups.
+ * New fast hash algoritme added (if more than 800Kb available).
+ */
+#define IBUFSIZ 4096
+
+#define NOFUNCDEF
+#undef DOS
+#undef COMPATIBLE
+#undef DONTPTS /* `#define DONTPTS 1' means original uncompress */
+#undef DIRENT
+#undef RECURSIVE
+#undef SYSDIR
+#undef UTIME_H
+#undef BYTEORDER
+#define BITS 16 /* _must_ be able to handle this many bits, anyway */
+#define FAST
+#if 1 /* !! */
+# define ASSERT(x) do { if (!(x)) abort(); } while(0)
+#else
+# define ASSERT(x)
+#endif
+
+#include <stdio.h>
+#include <string.h> /* memcpy(), memset() */
+#include <stdlib.h> /* abort(), exit() */
+
+/* #define fx_write(a,b,c) fwrite((b),1,(c),(a)) */
+#define fx_read(a,b,c) fread( (b),1,(c),(a))
+#define fx_stdin stdin
+#define fx_stdout stdout
+#define fx_type FILE*
+
+#ifndef NOFUNCDEF
+#define LARGS(a) () /* Relay on include files for libary func defs. */
+ extern void *malloc LARGS((int));
+ extern void free LARGS((void *));
+#ifndef _IBMR2
+ extern int open LARGS((char const *,int,...));
+#endif
+ extern int close LARGS((int));
+ extern int read LARGS((int,void *,int));
+ extern int write LARGS((int,void const *,int));
+ extern int chmod LARGS((char const *,int));
+ extern int unlink LARGS((char const *));
+ extern int chown LARGS((char const *,int,int));
+ extern int utime LARGS((char const *,struct utimbuf const *));
+ extern char *strcpy LARGS((char *,char const *));
+ extern char *strcat LARGS((char *,char const *));
+ extern int strcmp LARGS((char const *,char const *));
+ extern unsigned strlen LARGS((char const *));
+ extern void *memset LARGS((void *,char,unsigned int));
+ extern void *memcpy LARGS((void *,void const *,unsigned int));
+ extern int atoi LARGS((char const *));
+ extern void exit LARGS((int));
+ extern int isatty LARGS((int));
+#endif
+
+#ifdef DEF_ERRNO
+ extern int errno;
+#endif
+
+#undef min
+#define min(a,b) ((a>b) ? b : a)
+
+#define MAGIC_1 (char_type)'\037'/* First byte of compressed file */
+#define MAGIC_2 (char_type)'\235'/* Second byte of compressed file */
+#define BIT_MASK 0x1f /* Mask for 'number of compresssion bits' */
+ /* Masks 0x20 and 0x40 are free. */
+ /* I think 0x20 should mean that there is */
+ /* a fourth header byte (for expansion). */
+#define BLOCK_MODE 0x80 /* Block compresssion if table is full and */
+ /* compression rate is dropping flush tables */
+
+ /* the next two codes should not be changed lightly, as they must not */
+ /* lie within the contiguous general code space. */
+#define FIRST 257 /* first free entry */
+#define CLEAR 256 /* table clear output code */
+
+#define INIT_BITS 9 /* initial number of bits/code */
+
+#ifndef BYTEORDER
+# define BYTEORDER 0000
+#endif
+
+#ifndef NOALLIGN
+# define NOALLIGN 0
+#endif
+
+/*
+ * machine variants which require cc -Dmachine: pdp11, z8000, DOS
+ */
+
+#ifdef interdata /* Perkin-Elmer */
+# define SIGNED_COMPARE_SLOW /* signed compare is slower than unsigned */
+#endif
+
+#ifdef MSDOS /* PC/XT/AT (8088) processor */
+# undef BYTEORDER
+# define BYTEORDER 4321
+# undef NOALLIGN
+# define NOALLIGN 1
+#endif /* DOS */
+
+#ifndef O_BINARY
+# define O_BINARY 0 /* System has no binary mode */
+#endif
+
+typedef long int code_int;
+
+#ifdef SIGNED_COMPARE_SLOW
+ typedef unsigned short int count_short;
+ typedef unsigned long int cmp_code_int; /* Cast to make compare faster */
+#else
+ typedef long int cmp_code_int;
+#endif
+
+typedef unsigned char char_type;
+
+#define MAXCODE(n) (1L << (n))
+
+#ifndef REGISTERS
+# define REGISTERS 20
+#endif
+#define REG1
+#define REG2
+#define REG3
+#define REG4
+#define REG5
+#define REG6
+#define REG7
+#define REG8
+#define REG9
+#define REG10
+#define REG11
+#define REG12
+#define REG13
+#define REG14
+#define REG15
+#define REG16
+#if REGISTERS >= 1
+# undef REG1
+# define REG1 register
+#endif
+#if REGISTERS >= 2
+# undef REG2
+# define REG2 register
+#endif
+#if REGISTERS >= 3
+# undef REG3
+# define REG3 register
+#endif
+#if REGISTERS >= 4
+# undef REG4
+# define REG4 register
+#endif
+#if REGISTERS >= 5
+# undef REG5
+# define REG5 register
+#endif
+#if REGISTERS >= 6
+# undef REG6
+# define REG6 register
+#endif
+#if REGISTERS >= 7
+# undef REG7
+# define REG7 register
+#endif
+#if REGISTERS >= 8
+# undef REG8
+# define REG8 register
+#endif
+#if REGISTERS >= 9
+# undef REG9
+# define REG9 register
+#endif
+#if REGISTERS >= 10
+# undef REG10
+# define REG10 register
+#endif
+#if REGISTERS >= 11
+# undef REG11
+# define REG11 register
+#endif
+#if REGISTERS >= 12
+# undef REG12
+# define REG12 register
+#endif
+#if REGISTERS >= 13
+# undef REG13
+# define REG13 register
+#endif
+#if REGISTERS >= 14
+# undef REG14
+# define REG14 register
+#endif
+#if REGISTERS >= 15
+# undef REG15
+# define REG15 register
+#endif
+#if REGISTERS >= 16
+# undef REG16
+# define REG16 register
+#endif
+
+#if BYTEORDER == 4321 && NOALLIGN == 1
+#define input(b,o,c,n,m){ \
+ (c) = (*(long *)(&(b)[(o)>>3])>>((o)&0x7))&(m); \
+ (o) += (n); \
+ }
+#else
+#define input(b,o,c,n,m){ REG1 char_type *p = &(b)[(o)>>3]; \
+ (c) = ((((long)(p[0]))|((long)(p[1])<<8)| \
+ ((long)(p[2])<<16))>>((o)&0x7))&(m); \
+ (o) += (n); \
+ }
+#endif
+
+/*
+ * 8086 & 80286 Has a problem with array bigger than 64K so fake the array
+ * For processors with a limited address space and segments.
+ */
+/*
+ * To save much memory, we overlay the table used by compress() with those
+ * used by lz_decompress(). The tab_prefix table is the same size and type
+ * as the codetab. The tab_suffix table needs 2**BITS characters. We
+ * get this from the beginning of htab. The output stack uses the rest
+ * of htab, and contains characters. There is plenty of room for any
+ * possible stack (stack used to be 8000 characters).
+ */
+#ifdef MAXSEG_64K
+ #error removed MAXSEG_64K
+#else /* Normal machine */
+# define HSIZE (1<<16) /* (1<<15) is few, (1<<16) seems to be OK, (1<<17) is proven safe OK */
+#if 0
+ char_type lz_htab[2*HSIZE]; /* Dat: HSIZE for the codes and HSIZE for de_stack */
+ unsigned short lz_codetab[HSIZE];
+#endif
+# define tab_prefixof(i) codetab[i]
+# define tab_suffixof(i) htab[i]
+# define de_stack (htab+(sizeof(htab[0])*(2*HSIZE-1)))
+# define clear_tab_prefixof() memset(codetab, 0, 256*sizeof(codetab[0]));
+#endif /* MAXSEG_64K */
+
+typedef struct lz_reader_t {
+ /* constants (don't change during decompression) */
+ fx_type fdin;
+ char_type* inbuf; /* [IBUFSIZ+64]; Input buffer */
+ char_type* htab; /* [2*HSIZE]; Dat: HSIZE for the codes and HSIZE for de_stack */
+ unsigned short* codetab; /* [HSIZE]; */
+ int blkmode;
+ code_int maycode;
+
+ /* variables that have to be saved */
+ char_type *stackp;
+ code_int code;
+ int finchar;
+ code_int oldcode;
+ code_int incode;
+ int inbits;
+ int posbits;
+ int insize;
+ int bitmask;
+ code_int freeent;
+ code_int maxcode;
+ int n_bits;
+ int rsize;
+ int maxbits;
+} lz_reader_t;
+
+static struct lz_reader_t* lz_read_new(FILE *inf) {
+ lz_reader_t* reader=(lz_reader_t*)malloc(sizeof(lz_reader_t));
+ reader->fdin=NULL;
+ if (reader!=NULL) {
+ if (lz_read_init(reader, inf)!=0) {
+ lz_close(reader);
+ free(reader); reader=NULL;
+ }
+ }
+ return reader;
+}
+static int lz_close(struct lz_reader_t* reader) {
+ FILE *fin=reader->fdin;
+ reader->rsize=-1;
+ if (reader->inbuf!=NULL) { free(reader->inbuf ); reader->inbuf =NULL; }
+ if (reader->htab!=NULL) { free(reader->htab ); reader->htab =NULL; }
+ if (reader->codetab!=NULL) { free(reader->codetab); reader->codetab=NULL; }
+ if (fin==NULL) return 0;
+ reader->fdin=NULL;
+ return fclose(fin);
+}
+/** @param Zreader uninitialized
+ * @return 0 on success
+ */
+static int lz_read_init(struct lz_reader_t* reader, FILE *inf) {
+ char_type* htab;
+ unsigned short* codetab;
+ code_int codex;
+ reader->fdin=inf;
+ if (NULL==(reader->inbuf=malloc(IBUFSIZ+67))) return 1;
+ if (NULL==(htab=reader->htab=malloc(2*HSIZE))) return 1; /* Dat: HSIZE for the codes and HSIZE for de_stack */
+ if (NULL==(codetab=reader->codetab=malloc(sizeof(reader->codetab[0])*HSIZE))) return 1;
+ reader->insize=0;
+ reader->rsize=0;
+ while (reader->insize < 3 && (reader->rsize = fx_read(reader->fdin, reader->inbuf+reader->insize, IBUFSIZ)) > 0)
+ reader->insize += reader->rsize;
+
+ if (reader->insize < 3
+ || reader->inbuf[0] != (char_type)'\037' /* First byte of compressed file */
+ || reader->inbuf[1] != (char_type)'\235' /* Second byte of compressed file */
+ ) return reader->insize >= 0 ? 2 : 3;
+
+ reader->maxbits = reader->inbuf[2] & BIT_MASK;
+ reader->blkmode = reader->inbuf[2] & BLOCK_MODE;
+ reader->maycode = MAXCODE(reader->maxbits);
+
+ if (reader->maxbits > BITS) return 4;
+#if 0
+ fprintf(stderr,
+ "%s: compressed with %d bits, can only handle %d bits\n",
+ ("stdin"), maxbits, BITS);
+ return 4;
+#endif
+
+ reader->maxcode = MAXCODE(reader->n_bits = INIT_BITS)-1;
+ reader->bitmask = (1<<reader->n_bits)-1;
+ reader->oldcode = -1;
+ reader->finchar = 0;
+ reader->posbits = 3<<3;
+ reader->stackp = NULL;
+ reader->code = -1;
+ reader->incode = -1; /* fake */
+ reader->inbits = 0; /* fake */
+
+ reader->freeent = ((reader->blkmode) ? FIRST : 256);
+
+ clear_tab_prefixof(); /* As above, initialize the first 256 entries in the table. */
+
+ for (codex = 255 ; codex >= 0 ; --codex)
+ tab_suffixof(codex) = (char_type)codex;
+ return 0; /* success */
+}
+
+/*
+ * Decompress stdin to stdout. This routine adapts to the codes in the
+ * file building the "string" table on-the-fly; requiring no table to
+ * be stored in the compressed file. The tables used herein are shared
+ * with those of the compress() routine. See the definitions above.
+ */
+static unsigned lz_read(struct lz_reader_t* reader, char* outbuf, unsigned obufsize) {
+/* static int lz_decompress(fx_type fdin, fx_type fdout) */
+ REG2 char_type *stackp;
+ REG3 code_int code;
+ REG4 int finchar;
+ REG5 code_int oldcode;
+ REG6 code_int incode;
+ REG7 int inbits;
+ REG8 int posbits;
+ REG10 int insize;
+ REG11 int bitmask;
+ REG12 code_int freeent;
+ REG13 code_int maxcode;
+ REG14 code_int maycode;
+ REG15 int n_bits;
+ REG16 int rsize;
+ int blkmode;
+ int maxbits;
+ char_type* inbuf;
+ char_type* htab;
+ unsigned short* codetab;
+ fx_type fdin;
+ REG9 unsigned outpos=0; /* not restored */
+
+#if 0
+ fprintf(stderr, ":: call rsize=%d oldcode=%d\n", reader->rsize, (int)reader->oldcode);
+#endif
+ if (reader->rsize<1) return reader->rsize; /* already EOF or error */
+
+ stackp =reader->stackp;
+ code =reader->code;
+ finchar=reader->finchar;
+ oldcode=reader->oldcode;
+ incode =reader->incode;
+ inbits =reader->inbits;
+ posbits=reader->posbits;
+ insize =reader->insize;
+ bitmask=reader->bitmask;
+ freeent=reader->freeent;
+ maxcode=reader->maxcode;
+ maycode=reader->maycode;
+ n_bits =reader->n_bits;
+ rsize =reader->rsize;
+ blkmode=reader->blkmode;
+ maxbits=reader->maxbits;
+ htab =reader->htab;
+ codetab=reader->codetab;
+ fdin =reader->fdin;
+ inbuf =reader->inbuf;
+ if (oldcode!=-1) goto try_fit; /* lz_read() called again */
+
+ do {
+ resetbuf: ;
+ {
+ REG1 int i;
+ int e;
+ int o;
+
+ e = insize-(o = (posbits>>3));
+
+ for (i = 0 ; i < e ; ++i)
+ inbuf[i] = inbuf[i+o];
+
+ insize = e;
+ posbits = 0;
+ }
+
+ if ((unsigned)insize < /*sizeof(inbuf)-IBUFSIZ*/ 64) {
+#if 0
+ fprintf(stderr, ":: try read insize=%d\n", insize);
+#endif
+ if ((rsize = fx_read(fdin, inbuf+insize, IBUFSIZ)) < 0 || ferror(fdin)) return reader->rsize=-1;
+#if 0
+ fprintf(stderr, ":: got rsize=%d\n", rsize);
+#endif
+ insize += rsize;
+ inbuf[insize]=inbuf[insize+1]=inbuf[insize+2]=0;
+ /* ^^^ sentinel, so input() won't produce ``read uninitialized byte(s) in a block.'' */
+ }
+
+ inbits = ((rsize > 0) ? (insize - insize%n_bits)<<3 :
+ (insize<<3)-(n_bits-1));
+
+ while (inbits > posbits) {
+ if (freeent > maxcode) {
+ posbits = ((posbits-1) + ((n_bits<<3) -
+ (posbits-1+(n_bits<<3))%(n_bits<<3)));
+ ++n_bits;
+ if (n_bits == maxbits)
+ maxcode = maycode;
+ else
+ maxcode = MAXCODE(n_bits)-1;
+
+ bitmask = (1<<n_bits)-1;
+ goto resetbuf;
+ }
+
+#if 0
+ fputc('#',stderr);
+#endif
+ input(inbuf,posbits,code,n_bits,bitmask);
+
+ if (oldcode == -1) {
+ outbuf[outpos++] = (char_type)(finchar = (int)(oldcode = code));
+ continue;
+ }
+ /* Dat: oldcode will never be -1 again */
+
+ if (code == CLEAR && blkmode) {
+ clear_tab_prefixof();
+ freeent = FIRST - 1;
+ posbits = ((posbits-1) + ((n_bits<<3) -
+ (posbits-1+(n_bits<<3))%(n_bits<<3)));
+ maxcode = MAXCODE(n_bits = INIT_BITS)-1;
+ bitmask = (1<<n_bits)-1;
+ goto resetbuf;
+ }
+
+ incode = code;
+ stackp = de_stack;
+
+ if (code >= freeent) { /* Special case for KwKwK string. */
+ if (code > freeent) {
+ REG1 char_type *p;
+
+ posbits -= n_bits;
+ p = &inbuf[posbits>>3];
+
+#if 0
+ fprintf(stderr, "uncompress: insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)\n", insize, posbits,
+ p[-1],p[0],p[1],p[2],p[3], (posbits&07));
+ fprintf(stderr, "uncompress: corrupt input\n");
+ abort();
+#else
+ return reader->rsize=-1;
+#endif
+ }
+
+ *--stackp = (char_type)finchar;
+ code = oldcode;
+ }
+
+ while ((cmp_code_int)code >= (cmp_code_int)256) {
+ /* Generate output characters in reverse order */
+ *--stackp = tab_suffixof(code);
+ code = tab_prefixof(code);
+ }
+#if 0
+ fprintf(stderr, ":: to stack code=%d\n", (int)code);
+#endif
+ *--stackp = (char_type)(finchar = tab_suffixof(code));
+ ASSERT(outpos<=obufsize);
+
+ /* And put them out in forward order */
+ {
+ REG1 int i;
+
+ try_fit: if (outpos+(i = (de_stack-stackp)) >= obufsize) {
+ /* The entire stack doesn't fit into the outbuf */
+ i=obufsize-outpos;
+ memcpy(outbuf+outpos, stackp, i);
+ stackp+=i;
+ /* vvv Dat: blkmode, maycode, inbuf, htab, codetab, fdin need not be saved */
+ reader->stackp =stackp;
+ reader->code =code;
+ reader->finchar=finchar;
+ reader->oldcode=oldcode;
+ reader->incode =incode;
+ reader->inbits =inbits;
+ reader->posbits=posbits;
+ reader->insize =insize;
+ reader->bitmask=bitmask;
+ reader->freeent=freeent;
+ reader->maxcode=maxcode;
+ reader->maycode=maycode;
+ reader->n_bits =n_bits;
+ reader->rsize =rsize;
+ reader->blkmode=blkmode;
+ reader->maxbits=maxbits;
+#if 0
+ fprintf(stderr, ":: return obufsize=%d oldcode=%d\n", obufsize, (int)oldcode);
+#endif
+ return obufsize;
+ /* Dat: co-routine return back to try_fit, with outpos==0, outbuf=... obufsize=... */
+ } else {
+ memcpy(outbuf+outpos, stackp, i);
+ outpos += i;
+ }
+ }
+ /* Dat: ignore stackp from now */
+
+ if ((code = freeent) < maycode) { /* Generate the new entry. */
+#if 0
+ fprintf(stderr, ":: new entry code=(%d)\n", (int)code);
+#endif
+ tab_prefixof(code) = (unsigned short)oldcode;
+ tab_suffixof(code) = (char_type)finchar;
+ freeent = code+1;
+ }
+ oldcode = incode; /* Remember previous code. */
+ }
+ } while (rsize > 0);
+ reader->rsize=0;
+ return outpos;
+}
+
+/* --- */
+
+/* Values used in typeflag field. */
+
+#define REGTYPE '0' /* regular file */
+#define AREGTYPE '\0' /* regular file */
+#define LNKTYPE '1' /* link */
+#define SYMTYPE '2' /* reserved */
+#define CHRTYPE '3' /* character special */
+#define BLKTYPE '4' /* block special */
+#define DIRTYPE '5' /* directory */
+#define FIFOTYPE '6' /* FIFO special */
+#define CONTTYPE '7' /* reserved */
+
+#define BLOCKSIZE 512
+
+struct tar_header
+{ /* byte offset */
+ char name[100]; /* 0 */
+ char mode[8]; /* 100 */
+ char uid[8]; /* 108 */
+ char gid[8]; /* 116 */
+ char size[12]; /* 124 */
+ char mtime[12]; /* 136 */
+ char chksum[8]; /* 148 */
+ char typeflag; /* 156 */
+ char linkname[100]; /* 157 */
+ char magic[6]; /* 257 */
+ char version[2]; /* 263 */
+ char uname[32]; /* 265 */
+ char gname[32]; /* 297 */
+ char devmajor[8]; /* 329 */
+ char devminor[8]; /* 337 */
+ char prefix[155]; /* 345 */
+ /* 500 */
+};
+
+union tar_buffer {
+ char buffer[BLOCKSIZE];
+ struct tar_header header;
+};
+
+enum { TGZ_EXTRACT = 0, TGZ_LIST };
+
+#if 0
+static char *TGZfname OF((const char *));
+void TGZnotfound OF((const char *));
+
+int getoct OF((char *, int));
+char *strtime OF((time_t *));
+int ExprMatch OF((char *,char *));
+
+int makedir OF((char *));
+int matchname OF((int,int,char **,char *));
+
+void error OF((const char *));
+int tar OF((gzFile, int, int, int, char **));
+
+void help OF((int));
+int main OF((int, char **));
+#endif
+
+static char *prog;
+
+static void error(const char *msg) {
+ fprintf(stderr, "%s: %s\n", prog, msg);
+ exit(1);
+}
+
+/* This will give a benign warning */
+
+static char *TGZprefix[] = { "\0", ".tgz", ".tar.gz", ".tar.bz2", ".tar.Z", ".tar", NULL };
+
+/* Return the real name of the TGZ archive */
+/* or NULL if it does not exist. */
+
+static char *TGZfname OF((const char *fname))
+{
+ static char buffer[1024];
+ int origlen,i;
+
+ strcpy(buffer,fname);
+ origlen = strlen(buffer);
+
+ for (i=0; TGZprefix[i]; i++)
+ {
+ strcpy(buffer+origlen,TGZprefix[i]);
+ if (access(buffer,F_OK) == 0)
+ return buffer;
+ }
+ return NULL;
+}
+
+/* error message for the filename */
+
+static void TGZnotfound OF((const char *fname))
+{
+ int i;
+
+ fprintf(stderr,"%s : couldn't find ",prog);
+ for (i=0;TGZprefix[i];i++)
+ fprintf(stderr,(TGZprefix[i+1]) ? "%s%s, " : "or %s%s\n",
+ fname,
+ TGZprefix[i]);
+ exit(1);
+}
+
+
+/* help functions */
+
+static int getoct(char *p,int width)
+{
+ int result = 0;
+ char c;
+
+ while (width --)
+ {
+ c = *p++;
+ if (c == ' ')
+ continue;
+ if (c == 0)
+ break;
+ result = result * 8 + (c - '0');
+ }
+ return result;
+}
+
+static char *strtime (time_t *t)
+{
+ struct tm *local;
+ static char result[32];
+
+ local = localtime(t);
+ sprintf(result,"%2d/%02d/%4d %02d:%02d:%02d",
+ local->tm_mday, local->tm_mon+1, local->tm_year+1900,
+ local->tm_hour, local->tm_min, local->tm_sec);
+ return result;
+}
+
+
+/* regular expression matching */
+
+#define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
+
+static int ExprMatch(char *string,char *expr)
+{
+ while (1)
+ {
+ if (ISSPECIAL(*expr))
+ {
+ if (*expr == '/')
+ {
+ if (*string != '\\' && *string != '/')
+ return 0;
+ string ++; expr++;
+ }
+ else if (*expr == '*')
+ {
+ if (*expr ++ == 0)
+ return 1;
+ while (*++string != *expr)
+ if (*string == 0)
+ return 0;
+ }
+ }
+ else
+ {
+ if (*string != *expr)
+ return 0;
+ if (*expr++ == 0)
+ return 1;
+ string++;
+ }
+ }
+}
+
+/* recursive make directory */
+/* abort if you get an ENOENT errno somewhere in the middle */
+/* e.g. ignore error "mkdir on existing directory" */
+/* */
+/* return 1 if OK */
+/* 0 on error */
+
+static int makedir (char *newdir) {
+ /* avoid calling strdup, since it's not ansi C */
+ int len = strlen(newdir);
+ char *p, *buffer = malloc(len+1);
+ if (buffer==NULL) error("out of memory");
+ memcpy(buffer,newdir,len+1);
+
+ if (len <= 0) {
+ free(buffer);
+ return 0;
+ }
+ if (buffer[len-1] == '/') {
+ buffer[len-1] = '\0';
+ }
+ if (mkdir(buffer, 0775) == 0)
+ {
+ free(buffer);
+ return 1;
+ }
+
+ p = buffer+1;
+ while (1)
+ {
+ char hold;
+
+ while(*p && *p != '\\' && *p != '/')
+ p++;
+ hold = *p;
+ *p = 0;
+ if ((mkdir(buffer, 0775) == -1) && (errno == ENOENT))
+ {
+ fprintf(stderr,"%s: couldn't create directory %s\n",prog,buffer);
+ free(buffer);
+ return 0;
+ }
+ if (hold == 0)
+ break;
+ *p++ = hold;
+ }
+ free(buffer);
+ return 1;
+}
+
+static int matchname (int arg,int argc,char **argv,char *fname)
+{
+ if (arg == argc) /* no arguments given (untgz tgzarchive) */
+ return 1;
+
+ while (arg < argc)
+ if (ExprMatch(fname,argv[arg++]))
+ return 1;
+
+ return 0; /* ignore this for the moment being */
+}
+
+
+/* Tar file list or extract */
+
+static int tar (Readable* rin,int action,int arg,int argc,char **argv, char const* TGZfile, int verbose, void (*step)(void)) {
+ union tar_buffer buffer;
+ int is_tar_ok=0;
+ int len;
+ int err;
+ int getheader = 1;
+ int remaining = 0;
+ FILE *outfile = NULL;
+ char fname[BLOCKSIZE];
+ time_t tartime;
+
+#if 0
+ while (0<(len=rin->xRead(rin, &buffer, BLOCKSIZE))) {
+ fwrite(buffer.buffer, 1, len, stdout);
+ }
+ exit(0);
+#endif
+
+ if (action == TGZ_LIST)
+ printf(" day time size file\n"
+ " ---------- -------- --------- -------------------------------------\n");
+ while (1) {
+ len = rin->xRead(rin, &buffer, BLOCKSIZE);
+ if (len+1 == 0)
+ error (rin->xError(rin, &err));
+ if (!is_tar_ok && !(is_tar_ok=is_tar(buffer.buffer, len))) {
+ fprintf(stderr, "%s: compressed file not tared: %s\n", prog, TGZfile);
+ if (action == TGZ_EXTRACT) {
+ FILE *of;
+ unsigned len0=strlen(TGZfile), len1=len0;
+ char *ofn=malloc(len1+5);
+ if (ofn==NULL) return 1;
+ memcpy(ofn, TGZfile, len1+1);
+ while (len1>0 && ofn[len1]!='/' && ofn[len1]!='\\' && ofn[len1]!='.') len1--;
+ if (ofn[len1]=='.') ofn[len1]='\0'; else strcpy(ofn+len0, ".unc");
+ /* ^^^ remove last extension, or add `.unc' */
+ fprintf(stderr, "%s: extracting as: %s\n", prog, ofn);
+ if (NULL==(of=fopen(ofn, "wb"))) error("fopen write error");
+ do {
+ fwrite(buffer.buffer, 1, len, of);
+ if (ferror(of)) error("write error");
+ } while (0<(len=rin->xRead(rin, &buffer, BLOCKSIZE))); /* Imp: larger blocks */
+ len=ferror(of);
+ if (0!=(len|fclose(of))) error("fclose write error");
+ free(ofn);
+ return 0;
+ }
+ return 1;
+ /* !! */
+ }
+ /*
+ * Always expect complete blocks to process
+ * the tar information.
+ */
+ if (len != BLOCKSIZE)
+ error("read: incomplete block read");
+
+ /*
+ * If we have to get a tar header
+ */
+ if (getheader == 1)
+ {
+ /*
+ * if we met the end of the tar
+ * or the end-of-tar block,
+ * we are done
+ */
+ if ((len == 0) || (buffer.header.name[0]== 0)) break;
+
+ tartime = (time_t)getoct(buffer.header.mtime,12);
+ strcpy(fname,buffer.header.name);
+
+ switch (buffer.header.typeflag)
+ {
+ case DIRTYPE:
+ if (action == TGZ_LIST)
+ printf(" %s <dir> %s\n",strtime(&tartime),fname);
+ if (action == TGZ_EXTRACT)
+ makedir(fname);
+ break;
+ case REGTYPE:
+ case AREGTYPE:
+ remaining = getoct(buffer.header.size,12);
+ if (action == TGZ_LIST)
+ printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
+ if (action == TGZ_EXTRACT)
+ {
+ if ((remaining) && (matchname(arg,argc,argv,fname)))
+ {
+ outfile = fopen(fname,"wb");
+ if (outfile == NULL) {
+ /* try creating directory */
+ char *p = strrchr(fname, '/');
+ if (p != NULL) {
+ *p = '\0';
+ makedir(fname);
+ *p = '/';
+ outfile = fopen(fname,"wb");
+ }
+ }
+ if (verbose)
+ fprintf(stderr,
+ "%s %s\n",
+ (outfile) ? "Extracting" : "Couldn't create",
+ fname);
+ }
+ else
+ outfile = NULL;
+ }
+ /*
+ * could have no contents
+ */
+ getheader = (remaining) ? 0 : 1;
+ break;
+ default:
+ if (action == TGZ_LIST)
+ printf(" %s <---> %s\n",strtime(&tartime),fname);
+ break;
+ }
+ }
+ else
+ {
+ unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
+
+ if ((action == TGZ_EXTRACT) && (outfile != NULL))
+ {
+ if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
+ {
+ fprintf(stderr,"%s : error writing %s skipping...\n",prog,fname);
+ fclose(outfile);
+ unlink(fname);
+ }
+ }
+ remaining -= bytes;
+ if (remaining == 0)
+ {
+ getheader = 1;
+ if ((action == TGZ_EXTRACT) && (outfile != NULL))
+ {
+#ifdef WIN32
+ HANDLE hFile;
+ FILETIME ftm,ftLocal;
+ SYSTEMTIME st;
+ struct tm localt;
+
+ fclose(outfile);
+
+ localt = *localtime(&tartime);
+
+ hFile = CreateFile(fname, GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, OPEN_EXISTING, 0, NULL);
+
+ st.wYear = (WORD)localt.tm_year+1900;
+ st.wMonth = (WORD)localt.tm_mon;
+ st.wDayOfWeek = (WORD)localt.tm_wday;
+ st.wDay = (WORD)localt.tm_mday;
+ st.wHour = (WORD)localt.tm_hour;
+ st.wMinute = (WORD)localt.tm_min;
+ st.wSecond = (WORD)localt.tm_sec;
+ st.wMilliseconds = 0;
+ SystemTimeToFileTime(&st,&ftLocal);
+ LocalFileTimeToFileTime(&ftLocal,&ftm);
+ SetFileTime(hFile,&ftm,NULL,&ftm);
+ CloseHandle(hFile);
+
+ outfile = NULL;
+#else
+ struct utimbuf settime;
+
+ settime.actime = settime.modtime = tartime;
+
+ fclose(outfile);
+ outfile = NULL;
+ utime(fname,&settime);
+#endif
+ if (step) step();
+ }
+ }
+ }
+ }
+
+ if (rin->xClose(rin))
+ error("failed gzclose");
+
+ return 0;
+}
+
+
+/* =========================================================== */
+
+static void help(int exitval)
+{
+ fprintf(stderr,
+ "untarka v0.34: super untar + untar.Z"
+#ifdef HAVE_ZLIB
+ " + untar.gz"
+#endif
+#ifdef HAVE_BZ2LIB
+ " + untar.bz2"
+#endif
+ "\ncontains code (C) by pts@fazekas.hu since 2003.01.28\n"
+ "This program is under GPL >=2.0. There is NO WARRANTY. Use at your own risk!\n\n"
+ "Usage: untarka [-x] tarfile to extract all files\n"
+ " untarka -x tarfile fname ... to extract selected files\n"
+ " untarka -l tarfile to list archive contents\n"
+ " untarka -h to display this help\n");
+ exit(exitval);
+}
+
+/* ====================================================================== */
+
+#ifdef DOSISH
+extern int _CRT_glob;
+int _CRT_glob = 0; /* disable globbing of the arguments */
+#endif
+#ifdef COMPILE_MAIN
+int main(int argc,char **argv) {
+ int action = TGZ_EXTRACT;
+ int arg = 1;
+ char *TGZfile;
+ Readable r;
+
+#if DOSISH
+ setmode(0, O_BINARY);
+#endif
+ prog = strrchr(argv[0],'\\');
+ if (prog == NULL)
+ {
+ prog = strrchr(argv[0],'/');
+ if (prog == NULL)
+ {
+ prog = strrchr(argv[0],':');
+ if (prog == NULL)
+ prog = argv[0];
+ else
+ prog++;
+ }
+ else
+ prog++;
+ }
+ else
+ prog++;
+
+ if (argc == 1)
+ help(0);
+
+ if (strcmp(argv[arg],"-l") == 0 || strcmp(argv[arg],"-v") == 0) {
+ action = TGZ_LIST;
+ if (argc == ++arg) help(0);
+ } else if (strcmp(argv[arg],"-x") == 0) {
+ action = TGZ_EXTRACT;
+ if (argc == ++arg) help(0);
+ } else if (strcmp(argv[arg],"-h") == 0) help(0);
+
+ if ((TGZfile = TGZfname(argv[arg])) == NULL)
+ TGZnotfound(argv[arg]);
+
+ ++arg;
+ if ((action == TGZ_LIST) && (arg != argc))
+ help(1);
+
+ /*
+ * Process the TGZ file
+ */
+ switch (action) {
+ case TGZ_LIST:
+ case TGZ_EXTRACT:
+ switch (xOpen4Read(&r,TGZfile)) {
+ case 0: break;
+ case 1: fprintf(stderr, "%s: cannot decode compressed tar file: %s\n", prog, TGZfile); return 1;
+ case 2: fprintf(stderr, "%s: tar file too short: %s\n", prog, TGZfile); return 1;
+ case 3: fprintf(stderr, "%s: not a tar file: %s\n", prog, TGZfile); return 1;
+ case 4: fprintf(stderr, "%s: cannot open tar file: %s\n", prog, TGZfile); return 1;
+ }
+ return tar(&r, action, arg, argc, argv, TGZfile, 1, 0);
+ default: error("Unknown option!");
+ }
+ return 0;
+}
+#endif
+
+void
+tarextract(char *TGZfile,char *dest,int verbose, void (*step)(void))
+{
+ Readable r;
+ if (xOpen4Read(&r,TGZfile) == 0)
+ {
+ int arg = 2,
+ argc = 2;
+ char *argv[2];
+ chdir( dest );
+ argv[0] = "fgadmin";
+ argv[1] = TGZfile;
+ tar(&r, TGZ_EXTRACT, arg, argc, argv, TGZfile, 0, step);
+ }
+}