OSDN Git Service

d748b6905bbfe435b157d6d7ae56f85529766d48
[pf3gnuchains/gcc-fork.git] / zlib / contrib / untgz / untgz.c
1 /*
2  * untgz.c -- Display contents and extract files from a gzip'd TAR file
3  *
4  * written by "Pedro A. Aranda Guti\irrez" <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 strdup(str)         _strdup(str)
32 #    define access(path,mode)   _access(path,mode)
33 #  endif
34 #else
35 #  include <utime.h>
36 #endif
37
38
39 /* values used in typeflag field */
40
41 #define REGTYPE  '0'            /* regular file */
42 #define AREGTYPE '\0'           /* regular file */
43 #define LNKTYPE  '1'            /* link */
44 #define SYMTYPE  '2'            /* reserved */
45 #define CHRTYPE  '3'            /* character special */
46 #define BLKTYPE  '4'            /* block special */
47 #define DIRTYPE  '5'            /* directory */
48 #define FIFOTYPE '6'            /* FIFO special */
49 #define CONTTYPE '7'            /* reserved */
50
51 #define BLOCKSIZE 512
52
53 struct tar_header
54 {                               /* byte offset */
55   char name[100];               /*   0 */
56   char mode[8];                 /* 100 */
57   char uid[8];                  /* 108 */
58   char gid[8];                  /* 116 */
59   char size[12];                /* 124 */
60   char mtime[12];               /* 136 */
61   char chksum[8];               /* 148 */
62   char typeflag;                /* 156 */
63   char linkname[100];           /* 157 */
64   char magic[6];                /* 257 */
65   char version[2];              /* 263 */
66   char uname[32];               /* 265 */
67   char gname[32];               /* 297 */
68   char devmajor[8];             /* 329 */
69   char devminor[8];             /* 337 */
70   char prefix[155];             /* 345 */
71                                 /* 500 */
72 };
73
74 union tar_buffer {
75   char               buffer[BLOCKSIZE];
76   struct tar_header  header;
77 };
78
79 enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID };
80
81 char *TGZfname          OF((const char *));
82 void TGZnotfound        OF((const char *));
83
84 int getoct              OF((char *, int));
85 char *strtime           OF((time_t *));
86 int setfiletime         OF((char *, time_t));
87 int ExprMatch           OF((char *, char *));
88
89 int makedir             OF((char *));
90 int matchname           OF((int, int, char **, char *));
91
92 void error              OF((const char *));
93 int tar                 OF((gzFile, int, int, int, char **));
94
95 void help               OF((int));
96 int main                OF((int, char **));
97
98 char *prog;
99
100 const char *TGZsuffix[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL };
101
102 /* return the file name of the TGZ archive */
103 /* or NULL if it does not exist */
104
105 char *TGZfname (const char *arcname)
106 {
107   static char buffer[1024];
108   int origlen,i;
109
110   strcpy(buffer,arcname);
111   origlen = strlen(buffer);
112
113   for (i=0; TGZsuffix[i]; i++)
114     {
115        strcpy(buffer+origlen,TGZsuffix[i]);
116        if (access(buffer,F_OK) == 0)
117          return buffer;
118     }
119   return NULL;
120 }
121
122
123 /* error message for the filename */
124
125 void TGZnotfound (const char *arcname)
126 {
127   int i;
128
129   fprintf(stderr,"%s: Couldn't find ",prog);
130   for (i=0;TGZsuffix[i];i++)
131     fprintf(stderr,(TGZsuffix[i+1]) ? "%s%s, " : "or %s%s\n",
132             arcname,
133             TGZsuffix[i]);
134   exit(1);
135 }
136
137
138 /* convert octal digits to int */
139 /* on error return -1 */
140
141 int getoct (char *p,int width)
142 {
143   int result = 0;
144   char c;
145
146   while (width--)
147     {
148       c = *p++;
149       if (c == 0)
150         break;
151       if (c == ' ')
152         continue;
153       if (c < '0' || c > '7')
154         return -1;
155       result = result * 8 + (c - '0');
156     }
157   return result;
158 }
159
160
161 /* convert time_t to string */
162 /* use the "YYYY/MM/DD hh:mm:ss" format */
163
164 char *strtime (time_t *t)
165 {
166   struct tm   *local;
167   static char result[32];
168
169   local = localtime(t);
170   sprintf(result,"%4d/%02d/%02d %02d:%02d:%02d",
171           local->tm_year+1900, local->tm_mon+1, local->tm_mday,
172           local->tm_hour, local->tm_min, local->tm_sec);
173   return result;
174 }
175
176
177 /* set file time */
178
179 int setfiletime (char *fname,time_t ftime)
180 {
181 #ifdef WIN32
182   static int isWinNT = -1;
183   SYSTEMTIME st;
184   FILETIME locft, modft;
185   struct tm *loctm;
186   HANDLE hFile;
187   int result;
188
189   loctm = localtime(&ftime);
190   if (loctm == NULL)
191     return -1;
192
193   st.wYear         = (WORD)loctm->tm_year + 1900;
194   st.wMonth        = (WORD)loctm->tm_mon + 1;
195   st.wDayOfWeek    = (WORD)loctm->tm_wday;
196   st.wDay          = (WORD)loctm->tm_mday;
197   st.wHour         = (WORD)loctm->tm_hour;
198   st.wMinute       = (WORD)loctm->tm_min;
199   st.wSecond       = (WORD)loctm->tm_sec;
200   st.wMilliseconds = 0;
201   if (!SystemTimeToFileTime(&st, &locft) ||
202       !LocalFileTimeToFileTime(&locft, &modft))
203     return -1;
204
205   if (isWinNT < 0)
206     isWinNT = (GetVersion() < 0x80000000) ? 1 : 0;
207   hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
208                      (isWinNT ? FILE_FLAG_BACKUP_SEMANTICS : 0),
209                      NULL);
210   if (hFile == INVALID_HANDLE_VALUE)
211     return -1;
212   result = SetFileTime(hFile, NULL, NULL, &modft) ? 0 : -1;
213   CloseHandle(hFile);
214   return result;
215 #else
216   struct utimbuf settime;
217
218   settime.actime = settime.modtime = ftime;
219   return utime(fname,&settime);
220 #endif
221 }
222
223
224 /* regular expression matching */
225
226 #define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
227
228 int ExprMatch (char *string,char *expr)
229 {
230   while (1)
231     {
232       if (ISSPECIAL(*expr))
233         {
234           if (*expr == '/')
235             {
236               if (*string != '\\' && *string != '/')
237                 return 0;
238               string ++; expr++;
239             }
240           else if (*expr == '*')
241             {
242               if (*expr ++ == 0)
243                 return 1;
244               while (*++string != *expr)
245                 if (*string == 0)
246                   return 0;
247             }
248         }
249       else
250         {
251           if (*string != *expr)
252             return 0;
253           if (*expr++ == 0)
254             return 1;
255           string++;
256         }
257     }
258 }
259
260
261 /* recursive mkdir */
262 /* abort on ENOENT; ignore other errors like "directory already exists" */
263 /* return 1 if OK */
264 /*        0 on error */
265
266 int makedir (char *newdir)
267 {
268   char *buffer = strdup(newdir);
269   char *p;
270   int  len = strlen(buffer);
271
272   if (len <= 0) {
273     free(buffer);
274     return 0;
275   }
276   if (buffer[len-1] == '/') {
277     buffer[len-1] = '\0';
278   }
279   if (mkdir(buffer, 0755) == 0)
280     {
281       free(buffer);
282       return 1;
283     }
284
285   p = buffer+1;
286   while (1)
287     {
288       char hold;
289
290       while(*p && *p != '\\' && *p != '/')
291         p++;
292       hold = *p;
293       *p = 0;
294       if ((mkdir(buffer, 0755) == -1) && (errno == ENOENT))
295         {
296           fprintf(stderr,"%s: Couldn't create directory %s\n",prog,buffer);
297           free(buffer);
298           return 0;
299         }
300       if (hold == 0)
301         break;
302       *p++ = hold;
303     }
304   free(buffer);
305   return 1;
306 }
307
308
309 int matchname (int arg,int argc,char **argv,char *fname)
310 {
311   if (arg == argc)      /* no arguments given (untgz tgzarchive) */
312     return 1;
313
314   while (arg < argc)
315     if (ExprMatch(fname,argv[arg++]))
316       return 1;
317
318   return 0; /* ignore this for the moment being */
319 }
320
321
322 /* tar file list or extract */
323
324 int tar (gzFile in,int action,int arg,int argc,char **argv)
325 {
326   union  tar_buffer buffer;
327   int    len;
328   int    err;
329   int    getheader = 1;
330   int    remaining = 0;
331   FILE   *outfile = NULL;
332   char   fname[BLOCKSIZE];
333   int    tarmode;
334   time_t tartime;
335
336   if (action == TGZ_LIST)
337     printf("    date      time     size                       file\n"
338            " ---------- -------- --------- -------------------------------------\n");
339   while (1)
340     {
341       len = gzread(in, &buffer, BLOCKSIZE);
342       if (len < 0)
343         error(gzerror(in, &err));
344       /*
345        * Always expect complete blocks to process
346        * the tar information.
347        */
348       if (len != BLOCKSIZE)
349         {
350           action = TGZ_INVALID; /* force error exit */
351           remaining = 0;        /* force I/O cleanup */
352         }
353
354       /*
355        * If we have to get a tar header
356        */
357       if (getheader == 1)
358         {
359           /*
360            * if we met the end of the tar
361            * or the end-of-tar block,
362            * we are done
363            */
364           if ((len == 0) || (buffer.header.name[0] == 0)) break;
365
366           tarmode = getoct(buffer.header.mode,8);
367           tartime = (time_t)getoct(buffer.header.mtime,12);
368           if (tarmode == -1 || tartime == (time_t)-1)
369             {
370               buffer.header.name[0] = 0;
371               action = TGZ_INVALID;
372             }
373
374           strcpy(fname,buffer.header.name);
375
376           switch (buffer.header.typeflag)
377             {
378             case DIRTYPE:
379               if (action == TGZ_LIST)
380                 printf(" %s     <dir> %s\n",strtime(&tartime),fname);
381               if (action == TGZ_EXTRACT)
382                 {
383                   makedir(fname);
384                   setfiletime(fname,tartime);
385                 }
386               break;
387             case REGTYPE:
388             case AREGTYPE:
389               remaining = getoct(buffer.header.size,12);
390               if (remaining == -1)
391                 {
392                   action = TGZ_INVALID;
393                   break;
394                 }
395               if (action == TGZ_LIST)
396                 printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
397               else if (action == TGZ_EXTRACT)
398                 {
399                   if (matchname(arg,argc,argv,fname))
400                     {
401                       outfile = fopen(fname,"wb");
402                       if (outfile == NULL) {
403                         /* try creating directory */
404                         char *p = strrchr(fname, '/');
405                         if (p != NULL) {
406                           *p = '\0';
407                           makedir(fname);
408                           *p = '/';
409                           outfile = fopen(fname,"wb");
410                         }
411                       }
412                       if (outfile != NULL)
413                         printf("Extracting %s\n",fname);
414                       else
415                         fprintf(stderr, "%s: Couldn't create %s",prog,fname);
416                     }
417                   else
418                     outfile = NULL;
419                 }
420               getheader = 0;
421               break;
422             default:
423               if (action == TGZ_LIST)
424                 printf(" %s     <---> %s\n",strtime(&tartime),fname);
425               break;
426             }
427         }
428       else
429         {
430           unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
431
432           if (outfile != NULL)
433             {
434               if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
435                 {
436                   fprintf(stderr,"%s: Error writing %s -- skipping\n",prog,fname);
437                   fclose(outfile);
438                   outfile = NULL;
439                   remove(fname);
440                 }
441             }
442           remaining -= bytes;
443         }
444
445       if (remaining == 0)
446         {
447           getheader = 1;
448           if (outfile != NULL)
449             {
450               fclose(outfile);
451               outfile = NULL;
452               if (action != TGZ_INVALID)
453                 setfiletime(fname,tartime);
454             }
455         }
456
457       /*
458        * Abandon if errors are found
459        */
460       if (action == TGZ_INVALID)
461         {
462           error("broken archive");
463           break;
464         }
465     }
466
467   if (gzclose(in) != Z_OK)
468     error("failed gzclose");
469
470   return 0;
471 }
472
473
474 /* ============================================================ */
475
476 void help(int exitval)
477 {
478   printf("untgz version 0.2\n"
479          "  using zlib version %s\n\n",
480          zlibVersion());
481   printf("Usage: untgz file.tgz            extract all files\n"
482          "       untgz file.tgz fname ...  extract selected files\n"
483          "       untgz -l file.tgz         list archive contents\n"
484          "       untgz -h                  display this help\n");
485   exit(exitval);
486 }
487
488 void error(const char *msg)
489 {
490   fprintf(stderr, "%s: %s\n", prog, msg);
491   exit(1);
492 }
493
494
495 /* ============================================================ */
496
497 #if defined(WIN32) && defined(__GNUC__)
498 int _CRT_glob = 0;      /* disable argument globbing in MinGW */
499 #endif
500
501 int main(int argc,char **argv)
502 {
503     int         action = TGZ_EXTRACT;
504     int         arg = 1;
505     char        *TGZfile;
506     gzFile      *f;
507
508     prog = strrchr(argv[0],'\\');
509     if (prog == NULL)
510       {
511         prog = strrchr(argv[0],'/');
512         if (prog == NULL)
513           {
514             prog = strrchr(argv[0],':');
515             if (prog == NULL)
516               prog = argv[0];
517             else
518               prog++;
519           }
520         else
521           prog++;
522       }
523     else
524       prog++;
525
526     if (argc == 1)
527       help(0);
528
529     if (strcmp(argv[arg],"-l") == 0)
530       {
531         action = TGZ_LIST;
532         if (argc == ++arg)
533           help(0);
534       }
535     else if (strcmp(argv[arg],"-h") == 0)
536       {
537         help(0);
538       }
539
540     if ((TGZfile = TGZfname(argv[arg])) == NULL)
541       TGZnotfound(argv[arg]);
542
543     ++arg;
544     if ((action == TGZ_LIST) && (arg != argc))
545       help(1);
546
547 /*
548  *  Process the TGZ file
549  */
550     switch(action)
551       {
552       case TGZ_LIST:
553       case TGZ_EXTRACT:
554         f = gzopen(TGZfile,"rb");
555         if (f == NULL)
556           {
557             fprintf(stderr,"%s: Couldn't gzopen %s\n",prog,TGZfile);
558             return 1;
559           }
560         exit(tar(f, action, arg, argc, argv));
561       break;
562
563       default:
564         error("Unknown option");
565         exit(1);
566       }
567
568     return 0;
569 }