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