2
* untgz.c -- Display contents and extract files from a gzip'd TAR file
4
* written by Pedro A. Aranda Gutierrez <paag@tid.es>
5
* adaptation to Unix by Jean-loup Gailly <jloup@gzip.org>
6
* various fixes by Cosmin Truta <cosmint@cs.ubbcluj.ro>
29
# define mkdir(dirname,mode) _mkdir(dirname)
31
# define access(path,mode) _access(path,mode)
32
# define chmod(path,mode) _chmod(path,mode)
33
# define strdup(str) _strdup(str)
40
/* values used in typeflag field */
42
#define REGTYPE '0' /* regular file */
43
#define AREGTYPE '\0' /* regular file */
44
#define LNKTYPE '1' /* link */
45
#define SYMTYPE '2' /* reserved */
46
#define CHRTYPE '3' /* character special */
47
#define BLKTYPE '4' /* block special */
48
#define DIRTYPE '5' /* directory */
49
#define FIFOTYPE '6' /* FIFO special */
50
#define CONTTYPE '7' /* reserved */
52
/* GNU tar extensions */
54
#define GNUTYPE_DUMPDIR 'D' /* file names from dumped directory */
55
#define GNUTYPE_LONGLINK 'K' /* long link name */
56
#define GNUTYPE_LONGNAME 'L' /* long file name */
57
#define GNUTYPE_MULTIVOL 'M' /* continuation of file from another volume */
58
#define GNUTYPE_NAMES 'N' /* file name that does not fit into main hdr */
59
#define GNUTYPE_SPARSE 'S' /* sparse file */
60
#define GNUTYPE_VOLHDR 'V' /* tape/volume header */
66
#define SHORTNAMESIZE 100
70
char name[100]; /* 0 */
71
char mode[8]; /* 100 */
72
char uid[8]; /* 108 */
73
char gid[8]; /* 116 */
74
char size[12]; /* 124 */
75
char mtime[12]; /* 136 */
76
char chksum[8]; /* 148 */
77
char typeflag; /* 156 */
78
char linkname[100]; /* 157 */
79
char magic[6]; /* 257 */
80
char version[2]; /* 263 */
81
char uname[32]; /* 265 */
82
char gname[32]; /* 297 */
83
char devmajor[8]; /* 329 */
84
char devminor[8]; /* 337 */
85
char prefix[155]; /* 345 */
91
char buffer[BLOCKSIZE];
92
struct tar_header header;
97
struct attr_item *next;
103
enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID };
105
char *TGZfname OF((const char *));
106
void TGZnotfound OF((const char *));
108
int getoct OF((char *, int));
109
char *strtime OF((time_t *));
110
int setfiletime OF((char *, time_t));
111
void push_attr OF((struct attr_item **, char *, int, time_t));
112
void restore_attr OF((struct attr_item **));
114
int ExprMatch OF((char *, char *));
116
int makedir OF((char *));
117
int matchname OF((int, int, char **, char *));
119
void error OF((const char *));
120
int tar OF((gzFile, int, int, int, char **));
123
int main OF((int, char **));
127
const char *TGZsuffix[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL };
129
/* return the file name of the TGZ archive */
130
/* or NULL if it does not exist */
132
char *TGZfname (const char *arcname)
134
static char buffer[1024];
137
strcpy(buffer,arcname);
138
origlen = strlen(buffer);
140
for (i=0; TGZsuffix[i]; i++)
142
strcpy(buffer+origlen,TGZsuffix[i]);
143
if (access(buffer,F_OK) == 0)
150
/* error message for the filename */
152
void TGZnotfound (const char *arcname)
156
fprintf(stderr,"%s: Couldn't find ",prog);
157
for (i=0;TGZsuffix[i];i++)
158
fprintf(stderr,(TGZsuffix[i+1]) ? "%s%s, " : "or %s%s\n",
165
/* convert octal digits to int */
166
/* on error return -1 */
168
int getoct (char *p,int width)
180
if (c < '0' || c > '7')
182
result = result * 8 + (c - '0');
188
/* convert time_t to string */
189
/* use the "YYYY/MM/DD hh:mm:ss" format */
191
char *strtime (time_t *t)
194
static char result[32];
196
local = localtime(t);
197
sprintf(result,"%4d/%02d/%02d %02d:%02d:%02d",
198
local->tm_year+1900, local->tm_mon+1, local->tm_mday,
199
local->tm_hour, local->tm_min, local->tm_sec);
206
int setfiletime (char *fname,time_t ftime)
209
static int isWinNT = -1;
211
FILETIME locft, modft;
216
loctm = localtime(&ftime);
220
st.wYear = (WORD)loctm->tm_year + 1900;
221
st.wMonth = (WORD)loctm->tm_mon + 1;
222
st.wDayOfWeek = (WORD)loctm->tm_wday;
223
st.wDay = (WORD)loctm->tm_mday;
224
st.wHour = (WORD)loctm->tm_hour;
225
st.wMinute = (WORD)loctm->tm_min;
226
st.wSecond = (WORD)loctm->tm_sec;
227
st.wMilliseconds = 0;
228
if (!SystemTimeToFileTime(&st, &locft) ||
229
!LocalFileTimeToFileTime(&locft, &modft))
233
isWinNT = (GetVersion() < 0x80000000) ? 1 : 0;
234
hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
235
(isWinNT ? FILE_FLAG_BACKUP_SEMANTICS : 0),
237
if (hFile == INVALID_HANDLE_VALUE)
239
result = SetFileTime(hFile, NULL, NULL, &modft) ? 0 : -1;
243
struct utimbuf settime;
245
settime.actime = settime.modtime = ftime;
246
return utime(fname,&settime);
251
/* push file attributes */
253
void push_attr(struct attr_item **list,char *fname,int mode,time_t time)
255
struct attr_item *item;
257
item = (struct attr_item *)malloc(sizeof(struct attr_item));
259
error("Out of memory");
260
item->fname = strdup(fname);
268
/* restore file attributes */
270
void restore_attr(struct attr_item **list)
272
struct attr_item *item, *prev;
274
for (item = *list; item != NULL; )
276
setfiletime(item->fname,item->time);
277
chmod(item->fname,item->mode);
286
/* match regular expression */
288
#define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
290
int ExprMatch (char *string,char *expr)
294
if (ISSPECIAL(*expr))
298
if (*string != '\\' && *string != '/')
302
else if (*expr == '*')
306
while (*++string != *expr)
313
if (*string != *expr)
323
/* recursive mkdir */
324
/* abort on ENOENT; ignore other errors like "directory already exists" */
328
int makedir (char *newdir)
330
char *buffer = strdup(newdir);
332
int len = strlen(buffer);
338
if (buffer[len-1] == '/') {
339
buffer[len-1] = '\0';
341
if (mkdir(buffer, 0755) == 0)
352
while(*p && *p != '\\' && *p != '/')
356
if ((mkdir(buffer, 0755) == -1) && (errno == ENOENT))
358
fprintf(stderr,"%s: Couldn't create directory %s\n",prog,buffer);
371
int matchname (int arg,int argc,char **argv,char *fname)
373
if (arg == argc) /* no arguments given (untgz tgzarchive) */
377
if (ExprMatch(fname,argv[arg++]))
380
return 0; /* ignore this for the moment being */
384
/* tar file list or extract */
386
int tar (gzFile in,int action,int arg,int argc,char **argv)
388
union tar_buffer buffer;
393
FILE *outfile = NULL;
394
char fname[BLOCKSIZE];
397
struct attr_item *attributes = NULL;
399
if (action == TGZ_LIST)
400
printf(" date time size file\n"
401
" ---------- -------- --------- -------------------------------------\n");
404
len = gzread(in, &buffer, BLOCKSIZE);
406
error(gzerror(in, &err));
408
* Always expect complete blocks to process
409
* the tar information.
411
if (len != BLOCKSIZE)
413
action = TGZ_INVALID; /* force error exit */
414
remaining = 0; /* force I/O cleanup */
418
* If we have to get a tar header
423
* if we met the end of the tar
424
* or the end-of-tar block,
427
if (len == 0 || buffer.header.name[0] == 0)
430
tarmode = getoct(buffer.header.mode,8);
431
tartime = (time_t)getoct(buffer.header.mtime,12);
432
if (tarmode == -1 || tartime == (time_t)-1)
434
buffer.header.name[0] = 0;
435
action = TGZ_INVALID;
440
strncpy(fname,buffer.header.name,SHORTNAMESIZE);
441
if (fname[SHORTNAMESIZE-1] != 0)
442
fname[SHORTNAMESIZE] = 0;
447
* The file name is longer than SHORTNAMESIZE
449
if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1) != 0)
450
error("bad long name");
455
* Act according to the type flag
457
switch (buffer.header.typeflag)
460
if (action == TGZ_LIST)
461
printf(" %s <dir> %s\n",strtime(&tartime),fname);
462
if (action == TGZ_EXTRACT)
465
push_attr(&attributes,fname,tarmode,tartime);
470
remaining = getoct(buffer.header.size,12);
473
action = TGZ_INVALID;
476
if (action == TGZ_LIST)
477
printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
478
else if (action == TGZ_EXTRACT)
480
if (matchname(arg,argc,argv,fname))
482
outfile = fopen(fname,"wb");
483
if (outfile == NULL) {
484
/* try creating directory */
485
char *p = strrchr(fname, '/');
490
outfile = fopen(fname,"wb");
494
printf("Extracting %s\n",fname);
496
fprintf(stderr, "%s: Couldn't create %s",prog,fname);
503
case GNUTYPE_LONGLINK:
504
case GNUTYPE_LONGNAME:
505
remaining = getoct(buffer.header.size,12);
506
if (remaining < 0 || remaining >= BLOCKSIZE)
508
action = TGZ_INVALID;
511
len = gzread(in, fname, BLOCKSIZE);
513
error(gzerror(in, &err));
514
if (fname[BLOCKSIZE-1] != 0 || (int)strlen(fname) > remaining)
516
action = TGZ_INVALID;
522
if (action == TGZ_LIST)
523
printf(" %s <---> %s\n",strtime(&tartime),fname);
529
unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
533
if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
536
"%s: Error writing %s -- skipping\n",prog,fname);
552
if (action != TGZ_INVALID)
553
push_attr(&attributes,fname,tarmode,tartime);
558
* Abandon if errors are found
560
if (action == TGZ_INVALID)
562
error("broken archive");
568
* Restore file modes and time stamps
570
restore_attr(&attributes);
572
if (gzclose(in) != Z_OK)
573
error("failed gzclose");
579
/* ============================================================ */
581
void help(int exitval)
583
printf("untgz version 0.2.1\n"
584
" using zlib version %s\n\n",
586
printf("Usage: untgz file.tgz extract all files\n"
587
" untgz file.tgz fname ... extract selected files\n"
588
" untgz -l file.tgz list archive contents\n"
589
" untgz -h display this help\n");
593
void error(const char *msg)
595
fprintf(stderr, "%s: %s\n", prog, msg);
600
/* ============================================================ */
602
#if defined(WIN32) && defined(__GNUC__)
603
int _CRT_glob = 0; /* disable argument globbing in MinGW */
606
int main(int argc,char **argv)
608
int action = TGZ_EXTRACT;
613
prog = strrchr(argv[0],'\\');
616
prog = strrchr(argv[0],'/');
619
prog = strrchr(argv[0],':');
634
if (strcmp(argv[arg],"-l") == 0)
640
else if (strcmp(argv[arg],"-h") == 0)
645
if ((TGZfile = TGZfname(argv[arg])) == NULL)
646
TGZnotfound(argv[arg]);
649
if ((action == TGZ_LIST) && (arg != argc))
653
* Process the TGZ file
659
f = gzopen(TGZfile,"rb");
662
fprintf(stderr,"%s: Couldn't gzopen %s\n",prog,TGZfile);
665
exit(tar(f, action, arg, argc, argv));
669
error("Unknown option");