1 /* $NetBSD: untgz.c,v 1.1.1.1 2006/01/14 20:11:02 christos Exp $ */
4 * untgz.c -- Display contents and extract files from a gzip'd TAR file
6 * written by Pedro A. Aranda Gutierrez <paag@tid.es>
7 * adaptation to Unix by Jean-loup Gailly <jloup@gzip.org>
8 * various fixes by Cosmin Truta <cosmint@cs.ubbcluj.ro>
31 # define mkdir(dirname,mode) _mkdir(dirname)
33 # define access(path,mode) _access(path,mode)
34 # define chmod(path,mode) _chmod(path,mode)
35 # define strdup(str) _strdup(str)
42 /* values used in typeflag field */
44 #define REGTYPE '0' /* regular file */
45 #define AREGTYPE '\0' /* regular file */
46 #define LNKTYPE '1' /* link */
47 #define SYMTYPE '2' /* reserved */
48 #define CHRTYPE '3' /* character special */
49 #define BLKTYPE '4' /* block special */
50 #define DIRTYPE '5' /* directory */
51 #define FIFOTYPE '6' /* FIFO special */
52 #define CONTTYPE '7' /* reserved */
54 /* GNU tar extensions */
56 #define GNUTYPE_DUMPDIR 'D' /* file names from dumped directory */
57 #define GNUTYPE_LONGLINK 'K' /* long link name */
58 #define GNUTYPE_LONGNAME 'L' /* long file name */
59 #define GNUTYPE_MULTIVOL 'M' /* continuation of file from another volume */
60 #define GNUTYPE_NAMES 'N' /* file name that does not fit into main hdr */
61 #define GNUTYPE_SPARSE 'S' /* sparse file */
62 #define GNUTYPE_VOLHDR 'V' /* tape/volume header */
68 #define SHORTNAMESIZE 100
72 char name
[100]; /* 0 */
73 char mode
[8]; /* 100 */
74 char uid
[8]; /* 108 */
75 char gid
[8]; /* 116 */
76 char size
[12]; /* 124 */
77 char mtime
[12]; /* 136 */
78 char chksum
[8]; /* 148 */
79 char typeflag
; /* 156 */
80 char linkname
[100]; /* 157 */
81 char magic
[6]; /* 257 */
82 char version
[2]; /* 263 */
83 char uname
[32]; /* 265 */
84 char gname
[32]; /* 297 */
85 char devmajor
[8]; /* 329 */
86 char devminor
[8]; /* 337 */
87 char prefix
[155]; /* 345 */
93 char buffer
[BLOCKSIZE
];
94 struct tar_header header
;
99 struct attr_item
*next
;
105 enum { TGZ_EXTRACT
, TGZ_LIST
, TGZ_INVALID
};
107 char *TGZfname
OF((const char *));
108 void TGZnotfound
OF((const char *));
110 int getoct
OF((char *, int));
111 char *strtime
OF((time_t *));
112 int setfiletime
OF((char *, time_t));
113 void push_attr
OF((struct attr_item
**, char *, int, time_t));
114 void restore_attr
OF((struct attr_item
**));
116 int ExprMatch
OF((char *, char *));
118 int makedir
OF((char *));
119 int matchname
OF((int, int, char **, char *));
121 void error
OF((const char *));
122 int tar
OF((gzFile
, int, int, int, char **));
125 int main
OF((int, char **));
129 const char *TGZsuffix
[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL
};
131 /* return the file name of the TGZ archive */
132 /* or NULL if it does not exist */
134 char *TGZfname (const char *arcname
)
136 static char buffer
[1024];
139 strcpy(buffer
,arcname
);
140 origlen
= strlen(buffer
);
142 for (i
=0; TGZsuffix
[i
]; i
++)
144 strcpy(buffer
+origlen
,TGZsuffix
[i
]);
145 if (access(buffer
,F_OK
) == 0)
152 /* error message for the filename */
154 void TGZnotfound (const char *arcname
)
158 fprintf(stderr
,"%s: Couldn't find ",prog
);
159 for (i
=0;TGZsuffix
[i
];i
++)
160 fprintf(stderr
,(TGZsuffix
[i
+1]) ? "%s%s, " : "or %s%s\n",
167 /* convert octal digits to int */
168 /* on error return -1 */
170 int getoct (char *p
,int width
)
182 if (c
< '0' || c
> '7')
184 result
= result
* 8 + (c
- '0');
190 /* convert time_t to string */
191 /* use the "YYYY/MM/DD hh:mm:ss" format */
193 char *strtime (time_t *t
)
196 static char result
[32];
198 local
= localtime(t
);
199 sprintf(result
,"%4d/%02d/%02d %02d:%02d:%02d",
200 local
->tm_year
+1900, local
->tm_mon
+1, local
->tm_mday
,
201 local
->tm_hour
, local
->tm_min
, local
->tm_sec
);
208 int setfiletime (char *fname
,time_t ftime
)
211 static int isWinNT
= -1;
213 FILETIME locft
, modft
;
218 loctm
= localtime(&ftime
);
222 st
.wYear
= (WORD
)loctm
->tm_year
+ 1900;
223 st
.wMonth
= (WORD
)loctm
->tm_mon
+ 1;
224 st
.wDayOfWeek
= (WORD
)loctm
->tm_wday
;
225 st
.wDay
= (WORD
)loctm
->tm_mday
;
226 st
.wHour
= (WORD
)loctm
->tm_hour
;
227 st
.wMinute
= (WORD
)loctm
->tm_min
;
228 st
.wSecond
= (WORD
)loctm
->tm_sec
;
229 st
.wMilliseconds
= 0;
230 if (!SystemTimeToFileTime(&st
, &locft
) ||
231 !LocalFileTimeToFileTime(&locft
, &modft
))
235 isWinNT
= (GetVersion() < 0x80000000) ? 1 : 0;
236 hFile
= CreateFile(fname
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
,
237 (isWinNT
? FILE_FLAG_BACKUP_SEMANTICS
: 0),
239 if (hFile
== INVALID_HANDLE_VALUE
)
241 result
= SetFileTime(hFile
, NULL
, NULL
, &modft
) ? 0 : -1;
245 struct utimbuf settime
;
247 settime
.actime
= settime
.modtime
= ftime
;
248 return utime(fname
,&settime
);
253 /* push file attributes */
255 void push_attr(struct attr_item
**list
,char *fname
,int mode
,time_t time
)
257 struct attr_item
*item
;
259 item
= (struct attr_item
*)malloc(sizeof(struct attr_item
));
261 error("Out of memory");
262 item
->fname
= strdup(fname
);
270 /* restore file attributes */
272 void restore_attr(struct attr_item
**list
)
274 struct attr_item
*item
, *prev
;
276 for (item
= *list
; item
!= NULL
; )
278 setfiletime(item
->fname
,item
->time
);
279 chmod(item
->fname
,item
->mode
);
288 /* match regular expression */
290 #define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
292 int ExprMatch (char *string
,char *expr
)
296 if (ISSPECIAL(*expr
))
300 if (*string
!= '\\' && *string
!= '/')
304 else if (*expr
== '*')
308 while (*++string
!= *expr
)
315 if (*string
!= *expr
)
325 /* recursive mkdir */
326 /* abort on ENOENT; ignore other errors like "directory already exists" */
330 int makedir (char *newdir
)
332 char *buffer
= strdup(newdir
);
334 int len
= strlen(buffer
);
340 if (buffer
[len
-1] == '/') {
341 buffer
[len
-1] = '\0';
343 if (mkdir(buffer
, 0755) == 0)
354 while(*p
&& *p
!= '\\' && *p
!= '/')
358 if ((mkdir(buffer
, 0755) == -1) && (errno
== ENOENT
))
360 fprintf(stderr
,"%s: Couldn't create directory %s\n",prog
,buffer
);
373 int matchname (int arg
,int argc
,char **argv
,char *fname
)
375 if (arg
== argc
) /* no arguments given (untgz tgzarchive) */
379 if (ExprMatch(fname
,argv
[arg
++]))
382 return 0; /* ignore this for the moment being */
386 /* tar file list or extract */
388 int tar (gzFile in
,int action
,int arg
,int argc
,char **argv
)
390 union tar_buffer buffer
;
395 FILE *outfile
= NULL
;
396 char fname
[BLOCKSIZE
];
399 struct attr_item
*attributes
= NULL
;
401 if (action
== TGZ_LIST
)
402 printf(" date time size file\n"
403 " ---------- -------- --------- -------------------------------------\n");
406 len
= gzread(in
, &buffer
, BLOCKSIZE
);
408 error(gzerror(in
, &err
));
410 * Always expect complete blocks to process
411 * the tar information.
413 if (len
!= BLOCKSIZE
)
415 action
= TGZ_INVALID
; /* force error exit */
416 remaining
= 0; /* force I/O cleanup */
420 * If we have to get a tar header
425 * if we met the end of the tar
426 * or the end-of-tar block,
429 if (len
== 0 || buffer
.header
.name
[0] == 0)
432 tarmode
= getoct(buffer
.header
.mode
,8);
433 tartime
= (time_t)getoct(buffer
.header
.mtime
,12);
434 if (tarmode
== -1 || tartime
== (time_t)-1)
436 buffer
.header
.name
[0] = 0;
437 action
= TGZ_INVALID
;
442 strncpy(fname
,buffer
.header
.name
,SHORTNAMESIZE
);
443 if (fname
[SHORTNAMESIZE
-1] != 0)
444 fname
[SHORTNAMESIZE
] = 0;
449 * The file name is longer than SHORTNAMESIZE
451 if (strncmp(fname
,buffer
.header
.name
,SHORTNAMESIZE
-1) != 0)
452 error("bad long name");
457 * Act according to the type flag
459 switch (buffer
.header
.typeflag
)
462 if (action
== TGZ_LIST
)
463 printf(" %s <dir> %s\n",strtime(&tartime
),fname
);
464 if (action
== TGZ_EXTRACT
)
467 push_attr(&attributes
,fname
,tarmode
,tartime
);
472 remaining
= getoct(buffer
.header
.size
,12);
475 action
= TGZ_INVALID
;
478 if (action
== TGZ_LIST
)
479 printf(" %s %9d %s\n",strtime(&tartime
),remaining
,fname
);
480 else if (action
== TGZ_EXTRACT
)
482 if (matchname(arg
,argc
,argv
,fname
))
484 outfile
= fopen(fname
,"wb");
485 if (outfile
== NULL
) {
486 /* try creating directory */
487 char *p
= strrchr(fname
, '/');
492 outfile
= fopen(fname
,"wb");
496 printf("Extracting %s\n",fname
);
498 fprintf(stderr
, "%s: Couldn't create %s",prog
,fname
);
505 case GNUTYPE_LONGLINK
:
506 case GNUTYPE_LONGNAME
:
507 remaining
= getoct(buffer
.header
.size
,12);
508 if (remaining
< 0 || remaining
>= BLOCKSIZE
)
510 action
= TGZ_INVALID
;
513 len
= gzread(in
, fname
, BLOCKSIZE
);
515 error(gzerror(in
, &err
));
516 if (fname
[BLOCKSIZE
-1] != 0 || (int)strlen(fname
) > remaining
)
518 action
= TGZ_INVALID
;
524 if (action
== TGZ_LIST
)
525 printf(" %s <---> %s\n",strtime(&tartime
),fname
);
531 unsigned int bytes
= (remaining
> BLOCKSIZE
) ? BLOCKSIZE
: remaining
;
535 if (fwrite(&buffer
,sizeof(char),bytes
,outfile
) != bytes
)
538 "%s: Error writing %s -- skipping\n",prog
,fname
);
554 if (action
!= TGZ_INVALID
)
555 push_attr(&attributes
,fname
,tarmode
,tartime
);
560 * Abandon if errors are found
562 if (action
== TGZ_INVALID
)
564 error("broken archive");
570 * Restore file modes and time stamps
572 restore_attr(&attributes
);
574 if (gzclose(in
) != Z_OK
)
575 error("failed gzclose");
581 /* ============================================================ */
583 void help(int exitval
)
585 printf("untgz version 0.2.1\n"
586 " using zlib version %s\n\n",
588 printf("Usage: untgz file.tgz extract all files\n"
589 " untgz file.tgz fname ... extract selected files\n"
590 " untgz -l file.tgz list archive contents\n"
591 " untgz -h display this help\n");
595 void error(const char *msg
)
597 fprintf(stderr
, "%s: %s\n", prog
, msg
);
602 /* ============================================================ */
604 #if defined(WIN32) && defined(__GNUC__)
605 int _CRT_glob
= 0; /* disable argument globbing in MinGW */
608 int main(int argc
,char **argv
)
610 int action
= TGZ_EXTRACT
;
615 prog
= strrchr(argv
[0],'\\');
618 prog
= strrchr(argv
[0],'/');
621 prog
= strrchr(argv
[0],':');
636 if (strcmp(argv
[arg
],"-l") == 0)
642 else if (strcmp(argv
[arg
],"-h") == 0)
647 if ((TGZfile
= TGZfname(argv
[arg
])) == NULL
)
648 TGZnotfound(argv
[arg
]);
651 if ((action
== TGZ_LIST
) && (arg
!= argc
))
655 * Process the TGZ file
661 f
= gzopen(TGZfile
,"rb");
664 fprintf(stderr
,"%s: Couldn't gzopen %s\n",prog
,TGZfile
);
667 exit(tar(f
, action
, arg
, argc
, argv
));
671 error("Unknown option");