You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
522 lines
11 KiB
522 lines
11 KiB
/* |
|
* untgz.c -- Display contents and/or extract file from |
|
* a gzip'd TAR file |
|
* written by "Pedro A. Aranda Guti\irrez" <paag@tid.es> |
|
* adaptation to Unix by Jean-loup Gailly <jloup@gzip.org> |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <time.h> |
|
#include <errno.h> |
|
#include <fcntl.h> |
|
#ifdef unix |
|
# include <unistd.h> |
|
#else |
|
# include <direct.h> |
|
# include <io.h> |
|
#endif |
|
|
|
#include "zlib.h" |
|
|
|
#ifdef WIN32 |
|
# ifndef F_OK |
|
# define F_OK (0) |
|
# endif |
|
# ifdef _MSC_VER |
|
# define mkdir(dirname,mode) _mkdir(dirname) |
|
# define strdup(str) _strdup(str) |
|
# define unlink(fn) _unlink(fn) |
|
# define access(path,mode) _access(path,mode) |
|
# else |
|
# define mkdir(dirname,mode) _mkdir(dirname) |
|
# endif |
|
#else |
|
# include <utime.h> |
|
#endif |
|
|
|
|
|
/* 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 }; |
|
|
|
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 **)); |
|
|
|
char *prog; |
|
|
|
/* This will give a benign warning */ |
|
|
|
static char *TGZprefix[] = { "\0", ".tgz", ".tar.gz", ".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 */ |
|
|
|
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 */ |
|
|
|
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; |
|
} |
|
|
|
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) == '/')) |
|
|
|
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 */ |
|
|
|
int makedir (char *newdir) |
|
{ |
|
char *buffer = strdup(newdir); |
|
char *p; |
|
int len = strlen(buffer); |
|
|
|
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; |
|
} |
|
|
|
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 */ |
|
|
|
int tar (gzFile in,int action,int arg,int argc,char **argv) |
|
{ |
|
union tar_buffer buffer; |
|
int len; |
|
int err; |
|
int getheader = 1; |
|
int remaining = 0; |
|
FILE *outfile = NULL; |
|
char fname[BLOCKSIZE]; |
|
time_t tartime; |
|
|
|
if (action == TGZ_LIST) |
|
printf(" day time size file\n" |
|
" ---------- -------- --------- -------------------------------------\n"); |
|
while (1) |
|
{ |
|
len = gzread(in, &buffer, BLOCKSIZE); |
|
if (len < 0) |
|
error (gzerror(in, &err)); |
|
/* |
|
* Always expect complete blocks to process |
|
* the tar information. |
|
*/ |
|
if (len != BLOCKSIZE) |
|
error("gzread: 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"); |
|
} |
|
} |
|
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 (gzclose(in) != Z_OK) |
|
error("failed gzclose"); |
|
|
|
return 0; |
|
} |
|
|
|
|
|
/* =========================================================== */ |
|
|
|
void help(int exitval) |
|
{ |
|
fprintf(stderr, |
|
"untgz v 0.1\n" |
|
" an sample application of zlib 1.0.4\n\n" |
|
"Usage : untgz TGZfile to extract all files\n" |
|
" untgz TGZfile fname ... to extract selected files\n" |
|
" untgz -l TGZfile to list archive contents\n" |
|
" untgz -h to display this help\n\n"); |
|
exit(exitval); |
|
} |
|
|
|
void error(const char *msg) |
|
{ |
|
fprintf(stderr, "%s: %s\n", prog, msg); |
|
exit(1); |
|
} |
|
|
|
|
|
/* ====================================================================== */ |
|
|
|
int _CRT_glob = 0; /* disable globbing of the arguments */ |
|
|
|
int main(int argc,char **argv) |
|
{ |
|
int action = TGZ_EXTRACT; |
|
int arg = 1; |
|
char *TGZfile; |
|
gzFile *f; |
|
|
|
|
|
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) |
|
{ |
|
action = TGZ_LIST; |
|
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: |
|
f = gzopen(TGZfile,"rb"); |
|
if (f == NULL) |
|
{ |
|
fprintf(stderr,"%s: Couldn't gzopen %s\n", |
|
prog, |
|
TGZfile); |
|
return 1; |
|
} |
|
exit(tar(f, action, arg, argc, argv)); |
|
break; |
|
|
|
default: |
|
error("Unknown option!"); |
|
exit(1); |
|
} |
|
|
|
return 0; |
|
}
|
|
|