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