Chris@4
|
1 /*
|
Chris@4
|
2 * untgz.c -- Display contents and extract files from a gzip'd TAR file
|
Chris@4
|
3 *
|
Chris@4
|
4 * written by Pedro A. Aranda Gutierrez <paag@tid.es>
|
Chris@4
|
5 * adaptation to Unix by Jean-loup Gailly <jloup@gzip.org>
|
Chris@4
|
6 * various fixes by Cosmin Truta <cosmint@cs.ubbcluj.ro>
|
Chris@4
|
7 */
|
Chris@4
|
8
|
Chris@4
|
9 #include <stdio.h>
|
Chris@4
|
10 #include <stdlib.h>
|
Chris@4
|
11 #include <string.h>
|
Chris@4
|
12 #include <time.h>
|
Chris@4
|
13 #include <errno.h>
|
Chris@4
|
14
|
Chris@4
|
15 #include "zlib.h"
|
Chris@4
|
16
|
Chris@4
|
17 #ifdef unix
|
Chris@4
|
18 # include <unistd.h>
|
Chris@4
|
19 #else
|
Chris@4
|
20 # include <direct.h>
|
Chris@4
|
21 # include <io.h>
|
Chris@4
|
22 #endif
|
Chris@4
|
23
|
Chris@4
|
24 #ifdef WIN32
|
Chris@4
|
25 #include <windows.h>
|
Chris@4
|
26 # ifndef F_OK
|
Chris@4
|
27 # define F_OK 0
|
Chris@4
|
28 # endif
|
Chris@4
|
29 # define mkdir(dirname,mode) _mkdir(dirname)
|
Chris@4
|
30 # ifdef _MSC_VER
|
Chris@4
|
31 # define access(path,mode) _access(path,mode)
|
Chris@4
|
32 # define chmod(path,mode) _chmod(path,mode)
|
Chris@4
|
33 # define strdup(str) _strdup(str)
|
Chris@4
|
34 # endif
|
Chris@4
|
35 #else
|
Chris@4
|
36 # include <utime.h>
|
Chris@4
|
37 #endif
|
Chris@4
|
38
|
Chris@4
|
39
|
Chris@4
|
40 /* values used in typeflag field */
|
Chris@4
|
41
|
Chris@4
|
42 #define REGTYPE '0' /* regular file */
|
Chris@4
|
43 #define AREGTYPE '\0' /* regular file */
|
Chris@4
|
44 #define LNKTYPE '1' /* link */
|
Chris@4
|
45 #define SYMTYPE '2' /* reserved */
|
Chris@4
|
46 #define CHRTYPE '3' /* character special */
|
Chris@4
|
47 #define BLKTYPE '4' /* block special */
|
Chris@4
|
48 #define DIRTYPE '5' /* directory */
|
Chris@4
|
49 #define FIFOTYPE '6' /* FIFO special */
|
Chris@4
|
50 #define CONTTYPE '7' /* reserved */
|
Chris@4
|
51
|
Chris@4
|
52 /* GNU tar extensions */
|
Chris@4
|
53
|
Chris@4
|
54 #define GNUTYPE_DUMPDIR 'D' /* file names from dumped directory */
|
Chris@4
|
55 #define GNUTYPE_LONGLINK 'K' /* long link name */
|
Chris@4
|
56 #define GNUTYPE_LONGNAME 'L' /* long file name */
|
Chris@4
|
57 #define GNUTYPE_MULTIVOL 'M' /* continuation of file from another volume */
|
Chris@4
|
58 #define GNUTYPE_NAMES 'N' /* file name that does not fit into main hdr */
|
Chris@4
|
59 #define GNUTYPE_SPARSE 'S' /* sparse file */
|
Chris@4
|
60 #define GNUTYPE_VOLHDR 'V' /* tape/volume header */
|
Chris@4
|
61
|
Chris@4
|
62
|
Chris@4
|
63 /* tar header */
|
Chris@4
|
64
|
Chris@4
|
65 #define BLOCKSIZE 512
|
Chris@4
|
66 #define SHORTNAMESIZE 100
|
Chris@4
|
67
|
Chris@4
|
68 struct tar_header
|
Chris@4
|
69 { /* byte offset */
|
Chris@4
|
70 char name[100]; /* 0 */
|
Chris@4
|
71 char mode[8]; /* 100 */
|
Chris@4
|
72 char uid[8]; /* 108 */
|
Chris@4
|
73 char gid[8]; /* 116 */
|
Chris@4
|
74 char size[12]; /* 124 */
|
Chris@4
|
75 char mtime[12]; /* 136 */
|
Chris@4
|
76 char chksum[8]; /* 148 */
|
Chris@4
|
77 char typeflag; /* 156 */
|
Chris@4
|
78 char linkname[100]; /* 157 */
|
Chris@4
|
79 char magic[6]; /* 257 */
|
Chris@4
|
80 char version[2]; /* 263 */
|
Chris@4
|
81 char uname[32]; /* 265 */
|
Chris@4
|
82 char gname[32]; /* 297 */
|
Chris@4
|
83 char devmajor[8]; /* 329 */
|
Chris@4
|
84 char devminor[8]; /* 337 */
|
Chris@4
|
85 char prefix[155]; /* 345 */
|
Chris@4
|
86 /* 500 */
|
Chris@4
|
87 };
|
Chris@4
|
88
|
Chris@4
|
89 union tar_buffer
|
Chris@4
|
90 {
|
Chris@4
|
91 char buffer[BLOCKSIZE];
|
Chris@4
|
92 struct tar_header header;
|
Chris@4
|
93 };
|
Chris@4
|
94
|
Chris@4
|
95 struct attr_item
|
Chris@4
|
96 {
|
Chris@4
|
97 struct attr_item *next;
|
Chris@4
|
98 char *fname;
|
Chris@4
|
99 int mode;
|
Chris@4
|
100 time_t time;
|
Chris@4
|
101 };
|
Chris@4
|
102
|
Chris@4
|
103 enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID };
|
Chris@4
|
104
|
Chris@4
|
105 char *TGZfname OF((const char *));
|
Chris@4
|
106 void TGZnotfound OF((const char *));
|
Chris@4
|
107
|
Chris@4
|
108 int getoct OF((char *, int));
|
Chris@4
|
109 char *strtime OF((time_t *));
|
Chris@4
|
110 int setfiletime OF((char *, time_t));
|
Chris@4
|
111 void push_attr OF((struct attr_item **, char *, int, time_t));
|
Chris@4
|
112 void restore_attr OF((struct attr_item **));
|
Chris@4
|
113
|
Chris@4
|
114 int ExprMatch OF((char *, char *));
|
Chris@4
|
115
|
Chris@4
|
116 int makedir OF((char *));
|
Chris@4
|
117 int matchname OF((int, int, char **, char *));
|
Chris@4
|
118
|
Chris@4
|
119 void error OF((const char *));
|
Chris@4
|
120 int tar OF((gzFile, int, int, int, char **));
|
Chris@4
|
121
|
Chris@4
|
122 void help OF((int));
|
Chris@4
|
123 int main OF((int, char **));
|
Chris@4
|
124
|
Chris@4
|
125 char *prog;
|
Chris@4
|
126
|
Chris@4
|
127 const char *TGZsuffix[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL };
|
Chris@4
|
128
|
Chris@4
|
129 /* return the file name of the TGZ archive */
|
Chris@4
|
130 /* or NULL if it does not exist */
|
Chris@4
|
131
|
Chris@4
|
132 char *TGZfname (const char *arcname)
|
Chris@4
|
133 {
|
Chris@4
|
134 static char buffer[1024];
|
Chris@4
|
135 int origlen,i;
|
Chris@4
|
136
|
Chris@4
|
137 strcpy(buffer,arcname);
|
Chris@4
|
138 origlen = strlen(buffer);
|
Chris@4
|
139
|
Chris@4
|
140 for (i=0; TGZsuffix[i]; i++)
|
Chris@4
|
141 {
|
Chris@4
|
142 strcpy(buffer+origlen,TGZsuffix[i]);
|
Chris@4
|
143 if (access(buffer,F_OK) == 0)
|
Chris@4
|
144 return buffer;
|
Chris@4
|
145 }
|
Chris@4
|
146 return NULL;
|
Chris@4
|
147 }
|
Chris@4
|
148
|
Chris@4
|
149
|
Chris@4
|
150 /* error message for the filename */
|
Chris@4
|
151
|
Chris@4
|
152 void TGZnotfound (const char *arcname)
|
Chris@4
|
153 {
|
Chris@4
|
154 int i;
|
Chris@4
|
155
|
Chris@4
|
156 fprintf(stderr,"%s: Couldn't find ",prog);
|
Chris@4
|
157 for (i=0;TGZsuffix[i];i++)
|
Chris@4
|
158 fprintf(stderr,(TGZsuffix[i+1]) ? "%s%s, " : "or %s%s\n",
|
Chris@4
|
159 arcname,
|
Chris@4
|
160 TGZsuffix[i]);
|
Chris@4
|
161 exit(1);
|
Chris@4
|
162 }
|
Chris@4
|
163
|
Chris@4
|
164
|
Chris@4
|
165 /* convert octal digits to int */
|
Chris@4
|
166 /* on error return -1 */
|
Chris@4
|
167
|
Chris@4
|
168 int getoct (char *p,int width)
|
Chris@4
|
169 {
|
Chris@4
|
170 int result = 0;
|
Chris@4
|
171 char c;
|
Chris@4
|
172
|
Chris@4
|
173 while (width--)
|
Chris@4
|
174 {
|
Chris@4
|
175 c = *p++;
|
Chris@4
|
176 if (c == 0)
|
Chris@4
|
177 break;
|
Chris@4
|
178 if (c == ' ')
|
Chris@4
|
179 continue;
|
Chris@4
|
180 if (c < '0' || c > '7')
|
Chris@4
|
181 return -1;
|
Chris@4
|
182 result = result * 8 + (c - '0');
|
Chris@4
|
183 }
|
Chris@4
|
184 return result;
|
Chris@4
|
185 }
|
Chris@4
|
186
|
Chris@4
|
187
|
Chris@4
|
188 /* convert time_t to string */
|
Chris@4
|
189 /* use the "YYYY/MM/DD hh:mm:ss" format */
|
Chris@4
|
190
|
Chris@4
|
191 char *strtime (time_t *t)
|
Chris@4
|
192 {
|
Chris@4
|
193 struct tm *local;
|
Chris@4
|
194 static char result[32];
|
Chris@4
|
195
|
Chris@4
|
196 local = localtime(t);
|
Chris@4
|
197 sprintf(result,"%4d/%02d/%02d %02d:%02d:%02d",
|
Chris@4
|
198 local->tm_year+1900, local->tm_mon+1, local->tm_mday,
|
Chris@4
|
199 local->tm_hour, local->tm_min, local->tm_sec);
|
Chris@4
|
200 return result;
|
Chris@4
|
201 }
|
Chris@4
|
202
|
Chris@4
|
203
|
Chris@4
|
204 /* set file time */
|
Chris@4
|
205
|
Chris@4
|
206 int setfiletime (char *fname,time_t ftime)
|
Chris@4
|
207 {
|
Chris@4
|
208 #ifdef WIN32
|
Chris@4
|
209 static int isWinNT = -1;
|
Chris@4
|
210 SYSTEMTIME st;
|
Chris@4
|
211 FILETIME locft, modft;
|
Chris@4
|
212 struct tm *loctm;
|
Chris@4
|
213 HANDLE hFile;
|
Chris@4
|
214 int result;
|
Chris@4
|
215
|
Chris@4
|
216 loctm = localtime(&ftime);
|
Chris@4
|
217 if (loctm == NULL)
|
Chris@4
|
218 return -1;
|
Chris@4
|
219
|
Chris@4
|
220 st.wYear = (WORD)loctm->tm_year + 1900;
|
Chris@4
|
221 st.wMonth = (WORD)loctm->tm_mon + 1;
|
Chris@4
|
222 st.wDayOfWeek = (WORD)loctm->tm_wday;
|
Chris@4
|
223 st.wDay = (WORD)loctm->tm_mday;
|
Chris@4
|
224 st.wHour = (WORD)loctm->tm_hour;
|
Chris@4
|
225 st.wMinute = (WORD)loctm->tm_min;
|
Chris@4
|
226 st.wSecond = (WORD)loctm->tm_sec;
|
Chris@4
|
227 st.wMilliseconds = 0;
|
Chris@4
|
228 if (!SystemTimeToFileTime(&st, &locft) ||
|
Chris@4
|
229 !LocalFileTimeToFileTime(&locft, &modft))
|
Chris@4
|
230 return -1;
|
Chris@4
|
231
|
Chris@4
|
232 if (isWinNT < 0)
|
Chris@4
|
233 isWinNT = (GetVersion() < 0x80000000) ? 1 : 0;
|
Chris@4
|
234 hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
|
Chris@4
|
235 (isWinNT ? FILE_FLAG_BACKUP_SEMANTICS : 0),
|
Chris@4
|
236 NULL);
|
Chris@4
|
237 if (hFile == INVALID_HANDLE_VALUE)
|
Chris@4
|
238 return -1;
|
Chris@4
|
239 result = SetFileTime(hFile, NULL, NULL, &modft) ? 0 : -1;
|
Chris@4
|
240 CloseHandle(hFile);
|
Chris@4
|
241 return result;
|
Chris@4
|
242 #else
|
Chris@4
|
243 struct utimbuf settime;
|
Chris@4
|
244
|
Chris@4
|
245 settime.actime = settime.modtime = ftime;
|
Chris@4
|
246 return utime(fname,&settime);
|
Chris@4
|
247 #endif
|
Chris@4
|
248 }
|
Chris@4
|
249
|
Chris@4
|
250
|
Chris@4
|
251 /* push file attributes */
|
Chris@4
|
252
|
Chris@4
|
253 void push_attr(struct attr_item **list,char *fname,int mode,time_t time)
|
Chris@4
|
254 {
|
Chris@4
|
255 struct attr_item *item;
|
Chris@4
|
256
|
Chris@4
|
257 item = (struct attr_item *)malloc(sizeof(struct attr_item));
|
Chris@4
|
258 if (item == NULL)
|
Chris@4
|
259 error("Out of memory");
|
Chris@4
|
260 item->fname = strdup(fname);
|
Chris@4
|
261 item->mode = mode;
|
Chris@4
|
262 item->time = time;
|
Chris@4
|
263 item->next = *list;
|
Chris@4
|
264 *list = item;
|
Chris@4
|
265 }
|
Chris@4
|
266
|
Chris@4
|
267
|
Chris@4
|
268 /* restore file attributes */
|
Chris@4
|
269
|
Chris@4
|
270 void restore_attr(struct attr_item **list)
|
Chris@4
|
271 {
|
Chris@4
|
272 struct attr_item *item, *prev;
|
Chris@4
|
273
|
Chris@4
|
274 for (item = *list; item != NULL; )
|
Chris@4
|
275 {
|
Chris@4
|
276 setfiletime(item->fname,item->time);
|
Chris@4
|
277 chmod(item->fname,item->mode);
|
Chris@4
|
278 prev = item;
|
Chris@4
|
279 item = item->next;
|
Chris@4
|
280 free(prev);
|
Chris@4
|
281 }
|
Chris@4
|
282 *list = NULL;
|
Chris@4
|
283 }
|
Chris@4
|
284
|
Chris@4
|
285
|
Chris@4
|
286 /* match regular expression */
|
Chris@4
|
287
|
Chris@4
|
288 #define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
|
Chris@4
|
289
|
Chris@4
|
290 int ExprMatch (char *string,char *expr)
|
Chris@4
|
291 {
|
Chris@4
|
292 while (1)
|
Chris@4
|
293 {
|
Chris@4
|
294 if (ISSPECIAL(*expr))
|
Chris@4
|
295 {
|
Chris@4
|
296 if (*expr == '/')
|
Chris@4
|
297 {
|
Chris@4
|
298 if (*string != '\\' && *string != '/')
|
Chris@4
|
299 return 0;
|
Chris@4
|
300 string ++; expr++;
|
Chris@4
|
301 }
|
Chris@4
|
302 else if (*expr == '*')
|
Chris@4
|
303 {
|
Chris@4
|
304 if (*expr ++ == 0)
|
Chris@4
|
305 return 1;
|
Chris@4
|
306 while (*++string != *expr)
|
Chris@4
|
307 if (*string == 0)
|
Chris@4
|
308 return 0;
|
Chris@4
|
309 }
|
Chris@4
|
310 }
|
Chris@4
|
311 else
|
Chris@4
|
312 {
|
Chris@4
|
313 if (*string != *expr)
|
Chris@4
|
314 return 0;
|
Chris@4
|
315 if (*expr++ == 0)
|
Chris@4
|
316 return 1;
|
Chris@4
|
317 string++;
|
Chris@4
|
318 }
|
Chris@4
|
319 }
|
Chris@4
|
320 }
|
Chris@4
|
321
|
Chris@4
|
322
|
Chris@4
|
323 /* recursive mkdir */
|
Chris@4
|
324 /* abort on ENOENT; ignore other errors like "directory already exists" */
|
Chris@4
|
325 /* return 1 if OK */
|
Chris@4
|
326 /* 0 on error */
|
Chris@4
|
327
|
Chris@4
|
328 int makedir (char *newdir)
|
Chris@4
|
329 {
|
Chris@4
|
330 char *buffer = strdup(newdir);
|
Chris@4
|
331 char *p;
|
Chris@4
|
332 int len = strlen(buffer);
|
Chris@4
|
333
|
Chris@4
|
334 if (len <= 0) {
|
Chris@4
|
335 free(buffer);
|
Chris@4
|
336 return 0;
|
Chris@4
|
337 }
|
Chris@4
|
338 if (buffer[len-1] == '/') {
|
Chris@4
|
339 buffer[len-1] = '\0';
|
Chris@4
|
340 }
|
Chris@4
|
341 if (mkdir(buffer, 0755) == 0)
|
Chris@4
|
342 {
|
Chris@4
|
343 free(buffer);
|
Chris@4
|
344 return 1;
|
Chris@4
|
345 }
|
Chris@4
|
346
|
Chris@4
|
347 p = buffer+1;
|
Chris@4
|
348 while (1)
|
Chris@4
|
349 {
|
Chris@4
|
350 char hold;
|
Chris@4
|
351
|
Chris@4
|
352 while(*p && *p != '\\' && *p != '/')
|
Chris@4
|
353 p++;
|
Chris@4
|
354 hold = *p;
|
Chris@4
|
355 *p = 0;
|
Chris@4
|
356 if ((mkdir(buffer, 0755) == -1) && (errno == ENOENT))
|
Chris@4
|
357 {
|
Chris@4
|
358 fprintf(stderr,"%s: Couldn't create directory %s\n",prog,buffer);
|
Chris@4
|
359 free(buffer);
|
Chris@4
|
360 return 0;
|
Chris@4
|
361 }
|
Chris@4
|
362 if (hold == 0)
|
Chris@4
|
363 break;
|
Chris@4
|
364 *p++ = hold;
|
Chris@4
|
365 }
|
Chris@4
|
366 free(buffer);
|
Chris@4
|
367 return 1;
|
Chris@4
|
368 }
|
Chris@4
|
369
|
Chris@4
|
370
|
Chris@4
|
371 int matchname (int arg,int argc,char **argv,char *fname)
|
Chris@4
|
372 {
|
Chris@4
|
373 if (arg == argc) /* no arguments given (untgz tgzarchive) */
|
Chris@4
|
374 return 1;
|
Chris@4
|
375
|
Chris@4
|
376 while (arg < argc)
|
Chris@4
|
377 if (ExprMatch(fname,argv[arg++]))
|
Chris@4
|
378 return 1;
|
Chris@4
|
379
|
Chris@4
|
380 return 0; /* ignore this for the moment being */
|
Chris@4
|
381 }
|
Chris@4
|
382
|
Chris@4
|
383
|
Chris@4
|
384 /* tar file list or extract */
|
Chris@4
|
385
|
Chris@4
|
386 int tar (gzFile in,int action,int arg,int argc,char **argv)
|
Chris@4
|
387 {
|
Chris@4
|
388 union tar_buffer buffer;
|
Chris@4
|
389 int len;
|
Chris@4
|
390 int err;
|
Chris@4
|
391 int getheader = 1;
|
Chris@4
|
392 int remaining = 0;
|
Chris@4
|
393 FILE *outfile = NULL;
|
Chris@4
|
394 char fname[BLOCKSIZE];
|
Chris@4
|
395 int tarmode;
|
Chris@4
|
396 time_t tartime;
|
Chris@4
|
397 struct attr_item *attributes = NULL;
|
Chris@4
|
398
|
Chris@4
|
399 if (action == TGZ_LIST)
|
Chris@4
|
400 printf(" date time size file\n"
|
Chris@4
|
401 " ---------- -------- --------- -------------------------------------\n");
|
Chris@4
|
402 while (1)
|
Chris@4
|
403 {
|
Chris@4
|
404 len = gzread(in, &buffer, BLOCKSIZE);
|
Chris@4
|
405 if (len < 0)
|
Chris@4
|
406 error(gzerror(in, &err));
|
Chris@4
|
407 /*
|
Chris@4
|
408 * Always expect complete blocks to process
|
Chris@4
|
409 * the tar information.
|
Chris@4
|
410 */
|
Chris@4
|
411 if (len != BLOCKSIZE)
|
Chris@4
|
412 {
|
Chris@4
|
413 action = TGZ_INVALID; /* force error exit */
|
Chris@4
|
414 remaining = 0; /* force I/O cleanup */
|
Chris@4
|
415 }
|
Chris@4
|
416
|
Chris@4
|
417 /*
|
Chris@4
|
418 * If we have to get a tar header
|
Chris@4
|
419 */
|
Chris@4
|
420 if (getheader >= 1)
|
Chris@4
|
421 {
|
Chris@4
|
422 /*
|
Chris@4
|
423 * if we met the end of the tar
|
Chris@4
|
424 * or the end-of-tar block,
|
Chris@4
|
425 * we are done
|
Chris@4
|
426 */
|
Chris@4
|
427 if (len == 0 || buffer.header.name[0] == 0)
|
Chris@4
|
428 break;
|
Chris@4
|
429
|
Chris@4
|
430 tarmode = getoct(buffer.header.mode,8);
|
Chris@4
|
431 tartime = (time_t)getoct(buffer.header.mtime,12);
|
Chris@4
|
432 if (tarmode == -1 || tartime == (time_t)-1)
|
Chris@4
|
433 {
|
Chris@4
|
434 buffer.header.name[0] = 0;
|
Chris@4
|
435 action = TGZ_INVALID;
|
Chris@4
|
436 }
|
Chris@4
|
437
|
Chris@4
|
438 if (getheader == 1)
|
Chris@4
|
439 {
|
Chris@4
|
440 strncpy(fname,buffer.header.name,SHORTNAMESIZE);
|
Chris@4
|
441 if (fname[SHORTNAMESIZE-1] != 0)
|
Chris@4
|
442 fname[SHORTNAMESIZE] = 0;
|
Chris@4
|
443 }
|
Chris@4
|
444 else
|
Chris@4
|
445 {
|
Chris@4
|
446 /*
|
Chris@4
|
447 * The file name is longer than SHORTNAMESIZE
|
Chris@4
|
448 */
|
Chris@4
|
449 if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1) != 0)
|
Chris@4
|
450 error("bad long name");
|
Chris@4
|
451 getheader = 1;
|
Chris@4
|
452 }
|
Chris@4
|
453
|
Chris@4
|
454 /*
|
Chris@4
|
455 * Act according to the type flag
|
Chris@4
|
456 */
|
Chris@4
|
457 switch (buffer.header.typeflag)
|
Chris@4
|
458 {
|
Chris@4
|
459 case DIRTYPE:
|
Chris@4
|
460 if (action == TGZ_LIST)
|
Chris@4
|
461 printf(" %s <dir> %s\n",strtime(&tartime),fname);
|
Chris@4
|
462 if (action == TGZ_EXTRACT)
|
Chris@4
|
463 {
|
Chris@4
|
464 makedir(fname);
|
Chris@4
|
465 push_attr(&attributes,fname,tarmode,tartime);
|
Chris@4
|
466 }
|
Chris@4
|
467 break;
|
Chris@4
|
468 case REGTYPE:
|
Chris@4
|
469 case AREGTYPE:
|
Chris@4
|
470 remaining = getoct(buffer.header.size,12);
|
Chris@4
|
471 if (remaining == -1)
|
Chris@4
|
472 {
|
Chris@4
|
473 action = TGZ_INVALID;
|
Chris@4
|
474 break;
|
Chris@4
|
475 }
|
Chris@4
|
476 if (action == TGZ_LIST)
|
Chris@4
|
477 printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
|
Chris@4
|
478 else if (action == TGZ_EXTRACT)
|
Chris@4
|
479 {
|
Chris@4
|
480 if (matchname(arg,argc,argv,fname))
|
Chris@4
|
481 {
|
Chris@4
|
482 outfile = fopen(fname,"wb");
|
Chris@4
|
483 if (outfile == NULL) {
|
Chris@4
|
484 /* try creating directory */
|
Chris@4
|
485 char *p = strrchr(fname, '/');
|
Chris@4
|
486 if (p != NULL) {
|
Chris@4
|
487 *p = '\0';
|
Chris@4
|
488 makedir(fname);
|
Chris@4
|
489 *p = '/';
|
Chris@4
|
490 outfile = fopen(fname,"wb");
|
Chris@4
|
491 }
|
Chris@4
|
492 }
|
Chris@4
|
493 if (outfile != NULL)
|
Chris@4
|
494 printf("Extracting %s\n",fname);
|
Chris@4
|
495 else
|
Chris@4
|
496 fprintf(stderr, "%s: Couldn't create %s",prog,fname);
|
Chris@4
|
497 }
|
Chris@4
|
498 else
|
Chris@4
|
499 outfile = NULL;
|
Chris@4
|
500 }
|
Chris@4
|
501 getheader = 0;
|
Chris@4
|
502 break;
|
Chris@4
|
503 case GNUTYPE_LONGLINK:
|
Chris@4
|
504 case GNUTYPE_LONGNAME:
|
Chris@4
|
505 remaining = getoct(buffer.header.size,12);
|
Chris@4
|
506 if (remaining < 0 || remaining >= BLOCKSIZE)
|
Chris@4
|
507 {
|
Chris@4
|
508 action = TGZ_INVALID;
|
Chris@4
|
509 break;
|
Chris@4
|
510 }
|
Chris@4
|
511 len = gzread(in, fname, BLOCKSIZE);
|
Chris@4
|
512 if (len < 0)
|
Chris@4
|
513 error(gzerror(in, &err));
|
Chris@4
|
514 if (fname[BLOCKSIZE-1] != 0 || (int)strlen(fname) > remaining)
|
Chris@4
|
515 {
|
Chris@4
|
516 action = TGZ_INVALID;
|
Chris@4
|
517 break;
|
Chris@4
|
518 }
|
Chris@4
|
519 getheader = 2;
|
Chris@4
|
520 break;
|
Chris@4
|
521 default:
|
Chris@4
|
522 if (action == TGZ_LIST)
|
Chris@4
|
523 printf(" %s <---> %s\n",strtime(&tartime),fname);
|
Chris@4
|
524 break;
|
Chris@4
|
525 }
|
Chris@4
|
526 }
|
Chris@4
|
527 else
|
Chris@4
|
528 {
|
Chris@4
|
529 unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
|
Chris@4
|
530
|
Chris@4
|
531 if (outfile != NULL)
|
Chris@4
|
532 {
|
Chris@4
|
533 if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
|
Chris@4
|
534 {
|
Chris@4
|
535 fprintf(stderr,
|
Chris@4
|
536 "%s: Error writing %s -- skipping\n",prog,fname);
|
Chris@4
|
537 fclose(outfile);
|
Chris@4
|
538 outfile = NULL;
|
Chris@4
|
539 remove(fname);
|
Chris@4
|
540 }
|
Chris@4
|
541 }
|
Chris@4
|
542 remaining -= bytes;
|
Chris@4
|
543 }
|
Chris@4
|
544
|
Chris@4
|
545 if (remaining == 0)
|
Chris@4
|
546 {
|
Chris@4
|
547 getheader = 1;
|
Chris@4
|
548 if (outfile != NULL)
|
Chris@4
|
549 {
|
Chris@4
|
550 fclose(outfile);
|
Chris@4
|
551 outfile = NULL;
|
Chris@4
|
552 if (action != TGZ_INVALID)
|
Chris@4
|
553 push_attr(&attributes,fname,tarmode,tartime);
|
Chris@4
|
554 }
|
Chris@4
|
555 }
|
Chris@4
|
556
|
Chris@4
|
557 /*
|
Chris@4
|
558 * Abandon if errors are found
|
Chris@4
|
559 */
|
Chris@4
|
560 if (action == TGZ_INVALID)
|
Chris@4
|
561 {
|
Chris@4
|
562 error("broken archive");
|
Chris@4
|
563 break;
|
Chris@4
|
564 }
|
Chris@4
|
565 }
|
Chris@4
|
566
|
Chris@4
|
567 /*
|
Chris@4
|
568 * Restore file modes and time stamps
|
Chris@4
|
569 */
|
Chris@4
|
570 restore_attr(&attributes);
|
Chris@4
|
571
|
Chris@4
|
572 if (gzclose(in) != Z_OK)
|
Chris@4
|
573 error("failed gzclose");
|
Chris@4
|
574
|
Chris@4
|
575 return 0;
|
Chris@4
|
576 }
|
Chris@4
|
577
|
Chris@4
|
578
|
Chris@4
|
579 /* ============================================================ */
|
Chris@4
|
580
|
Chris@4
|
581 void help(int exitval)
|
Chris@4
|
582 {
|
Chris@4
|
583 printf("untgz version 0.2.1\n"
|
Chris@4
|
584 " using zlib version %s\n\n",
|
Chris@4
|
585 zlibVersion());
|
Chris@4
|
586 printf("Usage: untgz file.tgz extract all files\n"
|
Chris@4
|
587 " untgz file.tgz fname ... extract selected files\n"
|
Chris@4
|
588 " untgz -l file.tgz list archive contents\n"
|
Chris@4
|
589 " untgz -h display this help\n");
|
Chris@4
|
590 exit(exitval);
|
Chris@4
|
591 }
|
Chris@4
|
592
|
Chris@4
|
593 void error(const char *msg)
|
Chris@4
|
594 {
|
Chris@4
|
595 fprintf(stderr, "%s: %s\n", prog, msg);
|
Chris@4
|
596 exit(1);
|
Chris@4
|
597 }
|
Chris@4
|
598
|
Chris@4
|
599
|
Chris@4
|
600 /* ============================================================ */
|
Chris@4
|
601
|
Chris@4
|
602 #if defined(WIN32) && defined(__GNUC__)
|
Chris@4
|
603 int _CRT_glob = 0; /* disable argument globbing in MinGW */
|
Chris@4
|
604 #endif
|
Chris@4
|
605
|
Chris@4
|
606 int main(int argc,char **argv)
|
Chris@4
|
607 {
|
Chris@4
|
608 int action = TGZ_EXTRACT;
|
Chris@4
|
609 int arg = 1;
|
Chris@4
|
610 char *TGZfile;
|
Chris@4
|
611 gzFile *f;
|
Chris@4
|
612
|
Chris@4
|
613 prog = strrchr(argv[0],'\\');
|
Chris@4
|
614 if (prog == NULL)
|
Chris@4
|
615 {
|
Chris@4
|
616 prog = strrchr(argv[0],'/');
|
Chris@4
|
617 if (prog == NULL)
|
Chris@4
|
618 {
|
Chris@4
|
619 prog = strrchr(argv[0],':');
|
Chris@4
|
620 if (prog == NULL)
|
Chris@4
|
621 prog = argv[0];
|
Chris@4
|
622 else
|
Chris@4
|
623 prog++;
|
Chris@4
|
624 }
|
Chris@4
|
625 else
|
Chris@4
|
626 prog++;
|
Chris@4
|
627 }
|
Chris@4
|
628 else
|
Chris@4
|
629 prog++;
|
Chris@4
|
630
|
Chris@4
|
631 if (argc == 1)
|
Chris@4
|
632 help(0);
|
Chris@4
|
633
|
Chris@4
|
634 if (strcmp(argv[arg],"-l") == 0)
|
Chris@4
|
635 {
|
Chris@4
|
636 action = TGZ_LIST;
|
Chris@4
|
637 if (argc == ++arg)
|
Chris@4
|
638 help(0);
|
Chris@4
|
639 }
|
Chris@4
|
640 else if (strcmp(argv[arg],"-h") == 0)
|
Chris@4
|
641 {
|
Chris@4
|
642 help(0);
|
Chris@4
|
643 }
|
Chris@4
|
644
|
Chris@4
|
645 if ((TGZfile = TGZfname(argv[arg])) == NULL)
|
Chris@4
|
646 TGZnotfound(argv[arg]);
|
Chris@4
|
647
|
Chris@4
|
648 ++arg;
|
Chris@4
|
649 if ((action == TGZ_LIST) && (arg != argc))
|
Chris@4
|
650 help(1);
|
Chris@4
|
651
|
Chris@4
|
652 /*
|
Chris@4
|
653 * Process the TGZ file
|
Chris@4
|
654 */
|
Chris@4
|
655 switch(action)
|
Chris@4
|
656 {
|
Chris@4
|
657 case TGZ_LIST:
|
Chris@4
|
658 case TGZ_EXTRACT:
|
Chris@4
|
659 f = gzopen(TGZfile,"rb");
|
Chris@4
|
660 if (f == NULL)
|
Chris@4
|
661 {
|
Chris@4
|
662 fprintf(stderr,"%s: Couldn't gzopen %s\n",prog,TGZfile);
|
Chris@4
|
663 return 1;
|
Chris@4
|
664 }
|
Chris@4
|
665 exit(tar(f, action, arg, argc, argv));
|
Chris@4
|
666 break;
|
Chris@4
|
667
|
Chris@4
|
668 default:
|
Chris@4
|
669 error("Unknown option");
|
Chris@4
|
670 exit(1);
|
Chris@4
|
671 }
|
Chris@4
|
672
|
Chris@4
|
673 return 0;
|
Chris@4
|
674 }
|