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");