git subrepo pull (merge) --force deps/libchdr
[pcsx_rearmed.git] / deps / libchdr / deps / zlib-1.3.1 / contrib / untgz / untgz.c
CommitLineData
9e052883 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>
648db22b 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.
9e052883 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
648db22b 33#ifdef _WIN32
9e052883 34# include <direct.h>
35# include <io.h>
648db22b 36# include <windows.h>
9e052883 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
648db22b 47# include <sys/stat.h>
48# include <unistd.h>
9e052883 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
81struct 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
102union tar_buffer
103{
104 char buffer[BLOCKSIZE];
105 struct tar_header header;
106};
107
108struct attr_item
109{
110 struct attr_item *next;
111 char *fname;
112 int mode;
113 time_t time;
114};
115
116enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID };
117
9e052883 118char *prog;
119
648db22b 120void error(const char *msg)
121{
122 fprintf(stderr, "%s: %s\n", prog, msg);
123 exit(1);
124}
125
9e052883 126const 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
131char *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
151void 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
167int 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
190char *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
205int setfiletime (char *fname,time_t ftime)
206{
648db22b 207#ifdef _WIN32
9e052883 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
252void 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
269void 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
289int 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
327int 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
370int 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
385int 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
580void 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
9e052883 592
593/* ============================================================ */
594
595#if defined(WIN32) && defined(__GNUC__)
596int _CRT_glob = 0; /* disable argument globbing in MinGW */
597#endif
598
599int main(int argc,char **argv)
600{
601 int action = TGZ_EXTRACT;
602 int arg = 1;
603 char *TGZfile;
648db22b 604 gzFile f;
9e052883 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}