136de6bdf3
(rpm-checker was unhappy about our outdated FSF address :) )
1472 lines
41 KiB
C
1472 lines
41 KiB
C
#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
|
|
#ifdef WIN32
|
|
# 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
|
|
|
|
#include "untarka.h"
|
|
|
|
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 <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) {
|
|
posbits -= n_bits;
|
|
#if 0
|
|
{
|
|
REG1 char_type *p;
|
|
p = &inbuf[posbits>>3];
|
|
|
|
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);
|
|
}
|
|
|
|
#ifdef DOSISH
|
|
/* 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);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* 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 = 0;
|
|
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 <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 (rin->xClose(rin))
|
|
error("failed gzclose");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef DOSISH
|
|
/* =========================================================== */
|
|
|
|
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);
|
|
}
|
|
|
|
/* ====================================================================== */
|
|
|
|
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 *,int), 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);
|
|
}
|
|
}
|