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>
8 * This software is provided 'as-is', without any express or implied
9 * warranty. In no event will the authors be held liable for any damages
10 * arising from the use of this software.
12 * Permission is granted to anyone to use this software for any purpose,
13 * including commercial applications, and to alter it and redistribute it
14 * freely, subject to the following restrictions:
16 * 1. The origin of this software must not be misrepresented; you must not
17 * claim that you wrote the original software. If you use this software
18 * in a product, an acknowledgment in the product documentation would be
19 * appreciated but is not required.
20 * 2. Altered source versions must be plainly marked as such, and must not be
21 * misrepresented as being the original software.
22 * 3. This notice may not be removed or altered from any source distribution.
40 # define mkdir(dirname,mode) _mkdir(dirname)
42 # define access(path,mode) _access(path,mode)
43 # define chmod(path,mode) _chmod(path,mode)
44 # define strdup(str) _strdup(str)
47 # include <sys/stat.h>
53 /* values used in typeflag field */
55 #define REGTYPE '0' /* regular file */
56 #define AREGTYPE '\0' /* regular file */
57 #define LNKTYPE '1' /* link */
58 #define SYMTYPE '2' /* reserved */
59 #define CHRTYPE '3' /* character special */
60 #define BLKTYPE '4' /* block special */
61 #define DIRTYPE '5' /* directory */
62 #define FIFOTYPE '6' /* FIFO special */
63 #define CONTTYPE '7' /* reserved */
65 /* GNU tar extensions */
67 #define GNUTYPE_DUMPDIR 'D' /* file names from dumped directory */
68 #define GNUTYPE_LONGLINK 'K' /* long link name */
69 #define GNUTYPE_LONGNAME 'L' /* long file name */
70 #define GNUTYPE_MULTIVOL 'M' /* continuation of file from another volume */
71 #define GNUTYPE_NAMES 'N' /* file name that does not fit into main hdr */
72 #define GNUTYPE_SPARSE 'S' /* sparse file */
73 #define GNUTYPE_VOLHDR 'V' /* tape/volume header */
79 #define SHORTNAMESIZE 100
83 char name[100]; /* 0 */
84 char mode[8]; /* 100 */
85 char uid[8]; /* 108 */
86 char gid[8]; /* 116 */
87 char size[12]; /* 124 */
88 char mtime[12]; /* 136 */
89 char chksum[8]; /* 148 */
90 char typeflag; /* 156 */
91 char linkname[100]; /* 157 */
92 char magic[6]; /* 257 */
93 char version[2]; /* 263 */
94 char uname[32]; /* 265 */
95 char gname[32]; /* 297 */
96 char devmajor[8]; /* 329 */
97 char devminor[8]; /* 337 */
98 char prefix[155]; /* 345 */
104 char buffer[BLOCKSIZE];
105 struct tar_header header;
110 struct attr_item *next;
116 enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID };
120 void error(const char *msg)
122 fprintf(stderr, "%s: %s\n", prog, msg);
126 const char *TGZsuffix[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL };
128 /* return the file name of the TGZ archive */
129 /* or NULL if it does not exist */
131 char *TGZfname (const char *arcname)
133 static char buffer[1024];
136 strcpy(buffer,arcname);
137 origlen = strlen(buffer);
139 for (i=0; TGZsuffix[i]; i++)
141 strcpy(buffer+origlen,TGZsuffix[i]);
142 if (access(buffer,F_OK) == 0)
149 /* error message for the filename */
151 void TGZnotfound (const char *arcname)
155 fprintf(stderr,"%s: Couldn't find ",prog);
156 for (i=0;TGZsuffix[i];i++)
157 fprintf(stderr,(TGZsuffix[i+1]) ? "%s%s, " : "or %s%s\n",
164 /* convert octal digits to int */
165 /* on error return -1 */
167 int getoct (char *p,int width)
179 if (c < '0' || c > '7')
181 result = result * 8 + (c - '0');
187 /* convert time_t to string */
188 /* use the "YYYY/MM/DD hh:mm:ss" format */
190 char *strtime (time_t *t)
193 static char result[32];
195 local = localtime(t);
196 sprintf(result,"%4d/%02d/%02d %02d:%02d:%02d",
197 local->tm_year+1900, local->tm_mon+1, local->tm_mday,
198 local->tm_hour, local->tm_min, local->tm_sec);
205 int setfiletime (char *fname,time_t ftime)
208 static int isWinNT = -1;
210 FILETIME locft, modft;
215 loctm = localtime(&ftime);
219 st.wYear = (WORD)loctm->tm_year + 1900;
220 st.wMonth = (WORD)loctm->tm_mon + 1;
221 st.wDayOfWeek = (WORD)loctm->tm_wday;
222 st.wDay = (WORD)loctm->tm_mday;
223 st.wHour = (WORD)loctm->tm_hour;
224 st.wMinute = (WORD)loctm->tm_min;
225 st.wSecond = (WORD)loctm->tm_sec;
226 st.wMilliseconds = 0;
227 if (!SystemTimeToFileTime(&st, &locft) ||
228 !LocalFileTimeToFileTime(&locft, &modft))
232 isWinNT = (GetVersion() < 0x80000000) ? 1 : 0;
233 hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
234 (isWinNT ? FILE_FLAG_BACKUP_SEMANTICS : 0),
236 if (hFile == INVALID_HANDLE_VALUE)
238 result = SetFileTime(hFile, NULL, NULL, &modft) ? 0 : -1;
242 struct utimbuf settime;
244 settime.actime = settime.modtime = ftime;
245 return utime(fname,&settime);
250 /* push file attributes */
252 void push_attr(struct attr_item **list,char *fname,int mode,time_t time)
254 struct attr_item *item;
256 item = (struct attr_item *)malloc(sizeof(struct attr_item));
258 error("Out of memory");
259 item->fname = strdup(fname);
267 /* restore file attributes */
269 void restore_attr(struct attr_item **list)
271 struct attr_item *item, *prev;
273 for (item = *list; item != NULL; )
275 setfiletime(item->fname,item->time);
276 chmod(item->fname,item->mode);
285 /* match regular expression */
287 #define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
289 int ExprMatch (char *string,char *expr)
293 if (ISSPECIAL(*expr))
297 if (*string != '\\' && *string != '/')
301 else if (*expr == '*')
305 while (*++string != *expr)
312 if (*string != *expr)
322 /* recursive mkdir */
323 /* abort on ENOENT; ignore other errors like "directory already exists" */
327 int makedir (char *newdir)
329 char *buffer = strdup(newdir);
331 int len = strlen(buffer);
337 if (buffer[len-1] == '/') {
338 buffer[len-1] = '\0';
340 if (mkdir(buffer, 0755) == 0)
351 while(*p && *p != '\\' && *p != '/')
355 if ((mkdir(buffer, 0755) == -1) && (errno == ENOENT))
357 fprintf(stderr,"%s: Couldn't create directory %s\n",prog,buffer);
370 int matchname (int arg,int argc,char **argv,char *fname)
372 if (arg == argc) /* no arguments given (untgz tgzarchive) */
376 if (ExprMatch(fname,argv[arg++]))
379 return 0; /* ignore this for the moment being */
383 /* tar file list or extract */
385 int tar (gzFile in,int action,int arg,int argc,char **argv)
387 union tar_buffer buffer;
392 FILE *outfile = NULL;
393 char fname[BLOCKSIZE];
396 struct attr_item *attributes = NULL;
398 if (action == TGZ_LIST)
399 printf(" date time size file\n"
400 " ---------- -------- --------- -------------------------------------\n");
403 len = gzread(in, &buffer, BLOCKSIZE);
405 error(gzerror(in, &err));
407 * Always expect complete blocks to process
408 * the tar information.
410 if (len != BLOCKSIZE)
412 action = TGZ_INVALID; /* force error exit */
413 remaining = 0; /* force I/O cleanup */
417 * If we have to get a tar header
422 * if we met the end of the tar
423 * or the end-of-tar block,
426 if (len == 0 || buffer.header.name[0] == 0)
429 tarmode = getoct(buffer.header.mode,8);
430 tartime = (time_t)getoct(buffer.header.mtime,12);
431 if (tarmode == -1 || tartime == (time_t)-1)
433 buffer.header.name[0] = 0;
434 action = TGZ_INVALID;
439 strncpy(fname,buffer.header.name,SHORTNAMESIZE);
440 if (fname[SHORTNAMESIZE-1] != 0)
441 fname[SHORTNAMESIZE] = 0;
446 * The file name is longer than SHORTNAMESIZE
448 if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1) != 0)
449 error("bad long name");
454 * Act according to the type flag
456 switch (buffer.header.typeflag)
459 if (action == TGZ_LIST)
460 printf(" %s <dir> %s\n",strtime(&tartime),fname);
461 if (action == TGZ_EXTRACT)
464 push_attr(&attributes,fname,tarmode,tartime);
469 remaining = getoct(buffer.header.size,12);
472 action = TGZ_INVALID;
475 if (action == TGZ_LIST)
476 printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
477 else if (action == TGZ_EXTRACT)
479 if (matchname(arg,argc,argv,fname))
481 outfile = fopen(fname,"wb");
482 if (outfile == NULL) {
483 /* try creating directory */
484 char *p = strrchr(fname, '/');
489 outfile = fopen(fname,"wb");
493 printf("Extracting %s\n",fname);
495 fprintf(stderr, "%s: Couldn't create %s",prog,fname);
502 case GNUTYPE_LONGLINK:
503 case GNUTYPE_LONGNAME:
504 remaining = getoct(buffer.header.size,12);
505 if (remaining < 0 || remaining >= BLOCKSIZE)
507 action = TGZ_INVALID;
510 len = gzread(in, fname, BLOCKSIZE);
512 error(gzerror(in, &err));
513 if (fname[BLOCKSIZE-1] != 0 || (int)strlen(fname) > remaining)
515 action = TGZ_INVALID;
521 if (action == TGZ_LIST)
522 printf(" %s <---> %s\n",strtime(&tartime),fname);
528 unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
532 if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
535 "%s: Error writing %s -- skipping\n",prog,fname);
551 if (action != TGZ_INVALID)
552 push_attr(&attributes,fname,tarmode,tartime);
557 * Abandon if errors are found
559 if (action == TGZ_INVALID)
561 error("broken archive");
567 * Restore file modes and time stamps
569 restore_attr(&attributes);
571 if (gzclose(in) != Z_OK)
572 error("failed gzclose");
578 /* ============================================================ */
580 void help(int exitval)
582 printf("untgz version 0.2.1\n"
583 " using zlib version %s\n\n",
585 printf("Usage: untgz file.tgz extract all files\n"
586 " untgz file.tgz fname ... extract selected files\n"
587 " untgz -l file.tgz list archive contents\n"
588 " untgz -h display this help\n");
593 /* ============================================================ */
595 #if defined(WIN32) && defined(__GNUC__)
596 int _CRT_glob = 0; /* disable argument globbing in MinGW */
599 int main(int argc,char **argv)
601 int action = TGZ_EXTRACT;
606 prog = strrchr(argv[0],'\\');
609 prog = strrchr(argv[0],'/');
612 prog = strrchr(argv[0],':');
627 if (strcmp(argv[arg],"-l") == 0)
633 else if (strcmp(argv[arg],"-h") == 0)
638 if ((TGZfile = TGZfname(argv[arg])) == NULL)
639 TGZnotfound(argv[arg]);
642 if ((action == TGZ_LIST) && (arg != argc))
646 * Process the TGZ file
652 f = gzopen(TGZfile,"rb");
655 fprintf(stderr,"%s: Couldn't gzopen %s\n",prog,TGZfile);
658 exit(tar(f, action, arg, argc, argv));
662 error("Unknown option");