Added SILC Thread Queue API
[silc.git] / lib / zlib / contrib / untgz / untgz.c
1 /*
2  * untgz.c -- Display contents and/or extract file from
3  * a gzip'd TAR file
4  * written by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
5  * adaptation to Unix by Jean-loup Gailly <jloup@gzip.org>
6  */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <time.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #ifdef unix
15 # include <unistd.h>
16 #else
17 # include <direct.h>
18 # include <io.h>
19 #endif
20
21 #include "zlib.h"
22
23 #ifdef WIN32
24 #  ifndef F_OK
25 #    define F_OK (0)
26 #  endif
27 #  ifdef _MSC_VER
28 #    define mkdir(dirname,mode) _mkdir(dirname)
29 #    define strdup(str)         _strdup(str)
30 #    define unlink(fn)          _unlink(fn)
31 #    define access(path,mode)   _access(path,mode)
32 #  else
33 #    define mkdir(dirname,mode) _mkdir(dirname)
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 #define BLOCKSIZE 512
53
54 struct tar_header
55 {                               /* byte offset */
56   char name[100];               /*   0 */
57   char mode[8];                 /* 100 */
58   char uid[8];                  /* 108 */
59   char gid[8];                  /* 116 */
60   char size[12];                /* 124 */
61   char mtime[12];               /* 136 */
62   char chksum[8];               /* 148 */
63   char typeflag;                /* 156 */
64   char linkname[100];           /* 157 */
65   char magic[6];                /* 257 */
66   char version[2];              /* 263 */
67   char uname[32];               /* 265 */
68   char gname[32];               /* 297 */
69   char devmajor[8];             /* 329 */
70   char devminor[8];             /* 337 */
71   char prefix[155];             /* 345 */
72                                 /* 500 */
73 };
74
75 union tar_buffer {
76   char               buffer[BLOCKSIZE];
77   struct tar_header  header;
78 };
79
80 enum { TGZ_EXTRACT = 0, TGZ_LIST };
81
82 static char *TGZfname   OF((const char *));
83 void TGZnotfound        OF((const char *));
84
85 int getoct              OF((char *, int));
86 char *strtime           OF((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 /* This will give a benign warning */
101
102 static char *TGZprefix[] = { "\0", ".tgz", ".tar.gz", ".tar", NULL };
103
104 /* Return the real name of the TGZ archive */
105 /* or NULL if it does not exist. */
106
107 static char *TGZfname OF((const char *fname))
108 {
109   static char buffer[1024];
110   int origlen,i;
111   
112   strcpy(buffer,fname);
113   origlen = strlen(buffer);
114
115   for (i=0; TGZprefix[i]; i++)
116     {
117        strcpy(buffer+origlen,TGZprefix[i]);
118        if (access(buffer,F_OK) == 0)
119          return buffer;
120     }
121   return NULL;
122 }
123
124 /* error message for the filename */
125
126 void TGZnotfound OF((const char *fname))
127 {
128   int i;
129
130   fprintf(stderr,"%s : couldn't find ",prog);
131   for (i=0;TGZprefix[i];i++)
132     fprintf(stderr,(TGZprefix[i+1]) ? "%s%s, " : "or %s%s\n",
133             fname,
134             TGZprefix[i]);
135   exit(1);
136 }
137
138
139 /* help functions */
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 == ' ')
150         continue;
151       if (c == 0)
152         break;
153       result = result * 8 + (c - '0');
154     }
155   return result;
156 }
157
158 char *strtime (time_t *t)
159 {
160   struct tm   *local;
161   static char result[32];
162
163   local = localtime(t);
164   sprintf(result,"%2d/%02d/%4d %02d:%02d:%02d",
165           local->tm_mday, local->tm_mon+1, local->tm_year+1900,
166           local->tm_hour, local->tm_min,   local->tm_sec);
167   return result;
168 }
169
170
171 /* regular expression matching */
172
173 #define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
174
175 int ExprMatch(char *string,char *expr)
176 {
177   while (1)
178     {
179       if (ISSPECIAL(*expr))
180         {
181           if (*expr == '/')
182             {
183               if (*string != '\\' && *string != '/')
184                 return 0;
185               string ++; expr++;
186             }
187           else if (*expr == '*')
188             {
189               if (*expr ++ == 0)
190                 return 1;
191               while (*++string != *expr)
192                 if (*string == 0)
193                   return 0;
194             }
195         }
196       else
197         {
198           if (*string != *expr)
199             return 0;
200           if (*expr++ == 0)
201             return 1;
202           string++;
203         }
204     }
205 }
206
207 /* recursive make directory */
208 /* abort if you get an ENOENT errno somewhere in the middle */
209 /* e.g. ignore error "mkdir on existing directory" */
210 /* */
211 /* return 1 if OK */
212 /*        0 on error */
213
214 int makedir (char *newdir)
215 {
216   char *buffer = strdup(newdir);
217   char *p;
218   int  len = strlen(buffer);
219   
220   if (len <= 0) {
221     free(buffer);
222     return 0;
223   }
224   if (buffer[len-1] == '/') {
225     buffer[len-1] = '\0';
226   }
227   if (mkdir(buffer, 0775) == 0)
228     {
229       free(buffer);
230       return 1;
231     }
232
233   p = buffer+1;
234   while (1)
235     {
236       char hold;
237       
238       while(*p && *p != '\\' && *p != '/')
239         p++;
240       hold = *p;
241       *p = 0;
242       if ((mkdir(buffer, 0775) == -1) && (errno == ENOENT))
243         {
244           fprintf(stderr,"%s: couldn't create directory %s\n",prog,buffer);
245           free(buffer);
246           return 0;
247         }
248       if (hold == 0)
249         break;
250       *p++ = hold;
251     }
252   free(buffer);
253   return 1;
254 }
255
256 int matchname (int arg,int argc,char **argv,char *fname)
257 {
258   if (arg == argc)              /* no arguments given (untgz tgzarchive) */
259     return 1;
260
261   while (arg < argc)
262     if (ExprMatch(fname,argv[arg++]))
263       return 1;
264
265   return 0; /* ignore this for the moment being */
266 }
267
268
269 /* Tar file list or extract */
270
271 int tar (gzFile in,int action,int arg,int argc,char **argv)
272 {
273   union  tar_buffer buffer;
274   int    len;
275   int    err;
276   int    getheader = 1;
277   int    remaining = 0;
278   FILE   *outfile = NULL;
279   char   fname[BLOCKSIZE];
280   time_t tartime;
281   
282   if (action == TGZ_LIST)
283     printf("     day      time     size                       file\n"
284            " ---------- -------- --------- -------------------------------------\n");
285   while (1)
286     {
287       len = gzread(in, &buffer, BLOCKSIZE);
288       if (len < 0)
289         error (gzerror(in, &err));
290       /*
291        * Always expect complete blocks to process
292        * the tar information.
293        */
294       if (len != BLOCKSIZE)
295         error("gzread: incomplete block read");
296       
297       /*
298        * If we have to get a tar header
299        */
300       if (getheader == 1)
301         {
302           /*
303            * if we met the end of the tar
304            * or the end-of-tar block,
305            * we are done
306            */
307           if ((len == 0)  || (buffer.header.name[0]== 0)) break;
308
309           tartime = (time_t)getoct(buffer.header.mtime,12);
310           strcpy(fname,buffer.header.name);
311           
312           switch (buffer.header.typeflag)
313             {
314             case DIRTYPE:
315               if (action == TGZ_LIST)
316                 printf(" %s     <dir> %s\n",strtime(&tartime),fname);
317               if (action == TGZ_EXTRACT)
318                 makedir(fname);
319               break;
320             case REGTYPE:
321             case AREGTYPE:
322               remaining = getoct(buffer.header.size,12);
323               if (action == TGZ_LIST)
324                 printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
325               if (action == TGZ_EXTRACT)
326                 {
327                   if ((remaining) && (matchname(arg,argc,argv,fname)))
328                     {
329                       outfile = fopen(fname,"wb");
330                       if (outfile == NULL) {
331                         /* try creating directory */
332                         char *p = strrchr(fname, '/');
333                         if (p != NULL) {
334                           *p = '\0';
335                           makedir(fname);
336                           *p = '/';
337                           outfile = fopen(fname,"wb");
338                         }
339                       }
340                       fprintf(stderr,
341                               "%s %s\n",
342                               (outfile) ? "Extracting" : "Couldn't create",
343                               fname);
344                     }
345                   else
346                     outfile = NULL;
347                 }
348               /*
349                * could have no contents
350                */
351               getheader = (remaining) ? 0 : 1;
352               break;
353             default:
354               if (action == TGZ_LIST)
355                 printf(" %s     <---> %s\n",strtime(&tartime),fname);
356               break;
357             }
358         }
359       else
360         {
361           unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
362
363           if ((action == TGZ_EXTRACT) && (outfile != NULL))
364             {
365               if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
366                 {
367                   fprintf(stderr,"%s : error writing %s skipping...\n",prog,fname);
368                   fclose(outfile);
369                   unlink(fname);
370                 }
371             }
372           remaining -= bytes;
373           if (remaining == 0)
374             {
375               getheader = 1;
376               if ((action == TGZ_EXTRACT) && (outfile != NULL))
377                 {
378 #ifdef WIN32
379                   HANDLE hFile;
380                   FILETIME ftm,ftLocal;
381                   SYSTEMTIME st;
382                   struct tm localt;
383  
384                   fclose(outfile);
385
386                   localt = *localtime(&tartime);
387
388                   hFile = CreateFile(fname, GENERIC_READ | GENERIC_WRITE,
389                                      0, NULL, OPEN_EXISTING, 0, NULL);
390                   
391                   st.wYear = (WORD)localt.tm_year+1900;
392                   st.wMonth = (WORD)localt.tm_mon;
393                   st.wDayOfWeek = (WORD)localt.tm_wday;
394                   st.wDay = (WORD)localt.tm_mday;
395                   st.wHour = (WORD)localt.tm_hour;
396                   st.wMinute = (WORD)localt.tm_min;
397                   st.wSecond = (WORD)localt.tm_sec;
398                   st.wMilliseconds = 0;
399                   SystemTimeToFileTime(&st,&ftLocal);
400                   LocalFileTimeToFileTime(&ftLocal,&ftm);
401                   SetFileTime(hFile,&ftm,NULL,&ftm);
402                   CloseHandle(hFile);
403
404                   outfile = NULL;
405 #else
406                   struct utimbuf settime;
407
408                   settime.actime = settime.modtime = tartime;
409
410                   fclose(outfile);
411                   outfile = NULL;
412                   utime(fname,&settime);
413 #endif
414                 }
415             }
416         }
417     }
418   
419   if (gzclose(in) != Z_OK)
420     error("failed gzclose");
421
422   return 0;
423 }
424
425
426 /* =========================================================== */
427
428 void help(int exitval)
429 {
430   fprintf(stderr,
431           "untgz v 0.1\n"
432           " an sample application of zlib 1.0.4\n\n"
433           "Usage : untgz TGZfile            to extract all files\n"
434           "        untgz TGZfile fname ...  to extract selected files\n"
435           "        untgz -l TGZfile         to list archive contents\n"
436           "        untgz -h                 to display this help\n\n");
437   exit(exitval);
438 }
439
440 void error(const char *msg)
441 {
442     fprintf(stderr, "%s: %s\n", prog, msg);
443     exit(1);
444 }
445
446
447 /* ====================================================================== */
448
449 int _CRT_glob = 0;      /* disable globbing of the arguments */
450
451 int main(int argc,char **argv)
452 {
453     int         action = TGZ_EXTRACT;
454     int         arg = 1;
455     char        *TGZfile;
456     gzFile      *f;
457     
458
459     prog = strrchr(argv[0],'\\');
460     if (prog == NULL)
461       {
462         prog = strrchr(argv[0],'/');
463         if (prog == NULL)
464           {
465             prog = strrchr(argv[0],':');
466             if (prog == NULL)
467               prog = argv[0];
468             else
469               prog++;
470           }
471         else
472           prog++;
473       }
474     else
475       prog++;
476     
477     if (argc == 1)
478       help(0);
479
480     if (strcmp(argv[arg],"-l") == 0)
481       {
482         action = TGZ_LIST;
483         if (argc == ++arg)
484           help(0);
485       }
486     else if (strcmp(argv[arg],"-h") == 0)
487       {
488         help(0);
489       }
490
491     if ((TGZfile = TGZfname(argv[arg])) == NULL)
492       TGZnotfound(argv[arg]);            
493
494     ++arg;
495     if ((action == TGZ_LIST) && (arg != argc))
496       help(1);
497
498 /*
499  *  Process the TGZ file
500  */
501     switch(action)
502       {
503       case TGZ_LIST:
504       case TGZ_EXTRACT:
505         f = gzopen(TGZfile,"rb");
506         if (f == NULL)
507           {
508             fprintf(stderr,"%s: Couldn't gzopen %s\n",
509                     prog,
510                     TGZfile);
511             return 1;
512           }
513         exit(tar(f, action, arg, argc, argv));
514       break;
515         
516       default:
517         error("Unknown option!");
518         exit(1);
519       }
520
521     return 0;
522 }