2 * untgz.c -- Display contents and extract files from a gzip'd TAR file
4 * written by "Pedro A. Aranda Guti\irrez" <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 strdup(str) _strdup(str)
32 # define access(path,mode) _access(path,mode)
39 /* values used in typeflag field */
41 #define REGTYPE '0' /* regular file */
42 #define AREGTYPE '\0' /* regular file */
43 #define LNKTYPE '1' /* link */
44 #define SYMTYPE '2' /* reserved */
45 #define CHRTYPE '3' /* character special */
46 #define BLKTYPE '4' /* block special */
47 #define DIRTYPE '5' /* directory */
48 #define FIFOTYPE '6' /* FIFO special */
49 #define CONTTYPE '7' /* reserved */
55 char name
[100]; /* 0 */
56 char mode
[8]; /* 100 */
57 char uid
[8]; /* 108 */
58 char gid
[8]; /* 116 */
59 char size
[12]; /* 124 */
60 char mtime
[12]; /* 136 */
61 char chksum
[8]; /* 148 */
62 char typeflag
; /* 156 */
63 char linkname
[100]; /* 157 */
64 char magic
[6]; /* 257 */
65 char version
[2]; /* 263 */
66 char uname
[32]; /* 265 */
67 char gname
[32]; /* 297 */
68 char devmajor
[8]; /* 329 */
69 char devminor
[8]; /* 337 */
70 char prefix
[155]; /* 345 */
75 char buffer
[BLOCKSIZE
];
76 struct tar_header header
;
79 enum { TGZ_EXTRACT
, TGZ_LIST
, TGZ_INVALID
};
81 char *TGZfname
OF((const char *));
82 void TGZnotfound
OF((const char *));
84 int getoct
OF((char *, int));
85 char *strtime
OF((time_t *));
86 int setfiletime
OF((char *, time_t));
87 int ExprMatch
OF((char *, char *));
89 int makedir
OF((char *));
90 int matchname
OF((int, int, char **, char *));
92 void error
OF((const char *));
93 int tar
OF((gzFile
, int, int, int, char **));
96 int main
OF((int, char **));
100 const char *TGZsuffix
[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL
};
102 /* return the file name of the TGZ archive */
103 /* or NULL if it does not exist */
105 char *TGZfname (const char *arcname
)
107 static char buffer
[1024];
110 strcpy(buffer
,arcname
);
111 origlen
= strlen(buffer
);
113 for (i
=0; TGZsuffix
[i
]; i
++)
115 strcpy(buffer
+origlen
,TGZsuffix
[i
]);
116 if (access(buffer
,F_OK
) == 0)
123 /* error message for the filename */
125 void TGZnotfound (const char *arcname
)
129 fprintf(stderr
,"%s: Couldn't find ",prog
);
130 for (i
=0;TGZsuffix
[i
];i
++)
131 fprintf(stderr
,(TGZsuffix
[i
+1]) ? "%s%s, " : "or %s%s\n",
138 /* convert octal digits to int */
139 /* on error return -1 */
141 int getoct (char *p
,int width
)
153 if (c
< '0' || c
> '7')
155 result
= result
* 8 + (c
- '0');
161 /* convert time_t to string */
162 /* use the "YYYY/MM/DD hh:mm:ss" format */
164 char *strtime (time_t *t
)
167 static char result
[32];
169 local
= localtime(t
);
170 sprintf(result
,"%4d/%02d/%02d %02d:%02d:%02d",
171 local
->tm_year
+1900, local
->tm_mon
+1, local
->tm_mday
,
172 local
->tm_hour
, local
->tm_min
, local
->tm_sec
);
179 int setfiletime (char *fname
,time_t ftime
)
182 static int isWinNT
= -1;
184 FILETIME locft
, modft
;
189 loctm
= localtime(&ftime
);
193 st
.wYear
= (WORD
)loctm
->tm_year
+ 1900;
194 st
.wMonth
= (WORD
)loctm
->tm_mon
+ 1;
195 st
.wDayOfWeek
= (WORD
)loctm
->tm_wday
;
196 st
.wDay
= (WORD
)loctm
->tm_mday
;
197 st
.wHour
= (WORD
)loctm
->tm_hour
;
198 st
.wMinute
= (WORD
)loctm
->tm_min
;
199 st
.wSecond
= (WORD
)loctm
->tm_sec
;
200 st
.wMilliseconds
= 0;
201 if (!SystemTimeToFileTime(&st
, &locft
) ||
202 !LocalFileTimeToFileTime(&locft
, &modft
))
206 isWinNT
= (GetVersion() < 0x80000000) ? 1 : 0;
207 hFile
= CreateFile(fname
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
,
208 (isWinNT
? FILE_FLAG_BACKUP_SEMANTICS
: 0),
210 if (hFile
== INVALID_HANDLE_VALUE
)
212 result
= SetFileTime(hFile
, NULL
, NULL
, &modft
) ? 0 : -1;
216 struct utimbuf settime
;
218 settime
.actime
= settime
.modtime
= ftime
;
219 return utime(fname
,&settime
);
224 /* regular expression matching */
226 #define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
228 int ExprMatch (char *string
,char *expr
)
232 if (ISSPECIAL(*expr
))
236 if (*string
!= '\\' && *string
!= '/')
240 else if (*expr
== '*')
244 while (*++string
!= *expr
)
251 if (*string
!= *expr
)
261 /* recursive mkdir */
262 /* abort on ENOENT; ignore other errors like "directory already exists" */
266 int makedir (char *newdir
)
268 char *buffer
= strdup(newdir
);
270 int len
= strlen(buffer
);
276 if (buffer
[len
-1] == '/') {
277 buffer
[len
-1] = '\0';
279 if (mkdir(buffer
, 0755) == 0)
290 while(*p
&& *p
!= '\\' && *p
!= '/')
294 if ((mkdir(buffer
, 0755) == -1) && (errno
== ENOENT
))
296 fprintf(stderr
,"%s: Couldn't create directory %s\n",prog
,buffer
);
309 int matchname (int arg
,int argc
,char **argv
,char *fname
)
311 if (arg
== argc
) /* no arguments given (untgz tgzarchive) */
315 if (ExprMatch(fname
,argv
[arg
++]))
318 return 0; /* ignore this for the moment being */
322 /* tar file list or extract */
324 int tar (gzFile in
,int action
,int arg
,int argc
,char **argv
)
326 union tar_buffer buffer
;
331 FILE *outfile
= NULL
;
332 char fname
[BLOCKSIZE
];
336 if (action
== TGZ_LIST
)
337 printf(" date time size file\n"
338 " ---------- -------- --------- -------------------------------------\n");
341 len
= gzread(in
, &buffer
, BLOCKSIZE
);
343 error(gzerror(in
, &err
));
345 * Always expect complete blocks to process
346 * the tar information.
348 if (len
!= BLOCKSIZE
)
350 action
= TGZ_INVALID
; /* force error exit */
351 remaining
= 0; /* force I/O cleanup */
355 * If we have to get a tar header
360 * if we met the end of the tar
361 * or the end-of-tar block,
364 if ((len
== 0) || (buffer
.header
.name
[0] == 0)) break;
366 tarmode
= getoct(buffer
.header
.mode
,8);
367 tartime
= (time_t)getoct(buffer
.header
.mtime
,12);
368 if (tarmode
== -1 || tartime
== (time_t)-1)
370 buffer
.header
.name
[0] = 0;
371 action
= TGZ_INVALID
;
374 strcpy(fname
,buffer
.header
.name
);
376 switch (buffer
.header
.typeflag
)
379 if (action
== TGZ_LIST
)
380 printf(" %s <dir> %s\n",strtime(&tartime
),fname
);
381 if (action
== TGZ_EXTRACT
)
384 setfiletime(fname
,tartime
);
389 remaining
= getoct(buffer
.header
.size
,12);
392 action
= TGZ_INVALID
;
395 if (action
== TGZ_LIST
)
396 printf(" %s %9d %s\n",strtime(&tartime
),remaining
,fname
);
397 else if (action
== TGZ_EXTRACT
)
399 if (matchname(arg
,argc
,argv
,fname
))
401 outfile
= fopen(fname
,"wb");
402 if (outfile
== NULL
) {
403 /* try creating directory */
404 char *p
= strrchr(fname
, '/');
409 outfile
= fopen(fname
,"wb");
413 printf("Extracting %s\n",fname
);
415 fprintf(stderr
, "%s: Couldn't create %s",prog
,fname
);
423 if (action
== TGZ_LIST
)
424 printf(" %s <---> %s\n",strtime(&tartime
),fname
);
430 unsigned int bytes
= (remaining
> BLOCKSIZE
) ? BLOCKSIZE
: remaining
;
434 if (fwrite(&buffer
,sizeof(char),bytes
,outfile
) != bytes
)
436 fprintf(stderr
,"%s: Error writing %s -- skipping\n",prog
,fname
);
452 if (action
!= TGZ_INVALID
)
453 setfiletime(fname
,tartime
);
458 * Abandon if errors are found
460 if (action
== TGZ_INVALID
)
462 error("broken archive");
467 if (gzclose(in
) != Z_OK
)
468 error("failed gzclose");
474 /* ============================================================ */
476 void help(int exitval
)
478 printf("untgz version 0.2\n"
479 " using zlib version %s\n\n",
481 printf("Usage: untgz file.tgz extract all files\n"
482 " untgz file.tgz fname ... extract selected files\n"
483 " untgz -l file.tgz list archive contents\n"
484 " untgz -h display this help\n");
488 void error(const char *msg
)
490 fprintf(stderr
, "%s: %s\n", prog
, msg
);
495 /* ============================================================ */
497 #if defined(WIN32) && defined(__GNUC__)
498 int _CRT_glob
= 0; /* disable argument globbing in MinGW */
501 int main(int argc
,char **argv
)
503 int action
= TGZ_EXTRACT
;
508 prog
= strrchr(argv
[0],'\\');
511 prog
= strrchr(argv
[0],'/');
514 prog
= strrchr(argv
[0],':');
529 if (strcmp(argv
[arg
],"-l") == 0)
535 else if (strcmp(argv
[arg
],"-h") == 0)
540 if ((TGZfile
= TGZfname(argv
[arg
])) == NULL
)
541 TGZnotfound(argv
[arg
]);
544 if ((action
== TGZ_LIST
) && (arg
!= argc
))
548 * Process the TGZ file
554 f
= gzopen(TGZfile
,"rb");
557 fprintf(stderr
,"%s: Couldn't gzopen %s\n",prog
,TGZfile
);
560 exit(tar(f
, action
, arg
, argc
, argv
));
564 error("Unknown option");