#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" * adaptation to Unix by Jean-loup Gailly * adaptation to bzip2 by José Fonseca * merged comressors by Szabó Péter 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 # define DOSISH 1 # undef __STRICT_ANSI__ #endif #ifdef MSDOS # define DOSISH 1 #endif #include #include #include #include #include #include #ifdef DOSISH # include # include #else # include #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 # include /* 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)); static unsigned long total_read; /* #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) { total_read=0; 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); total_read+=got; 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) { total_read=0; 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) { unsigned l=gzread((gzFile)self->f,buf,len); total_read=((z_streamp)self->f)->total_in; return l; } 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 #include /* memcpy(), memset() */ #include /* 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<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<= 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 *,int), void *data) { 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; unsigned long last_read; #if 0 while (0<(len=rin->xRead(rin, &buffer, BLOCKSIZE))) { fwrite(buffer.buffer, 1, len, stdout); } exit(0); #endif last_read = 0; 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 (step) { step(data,total_read - last_read); last_read = total_read; } 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 %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 (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, 0); default: error("Unknown option!"); } return 0; } #endif void tarextract(char *TGZfile,char *dest,int verbose, void (*step)(void *), void *data) { 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, data); } }