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