getifaddrs: add un-namespaced alias
[minix.git] / common / dist / zlib / contrib / untgz / untgz.c
blobb39ede37fab79ee1eafde773b9343185ac4ac1f4
1 /* $NetBSD: untgz.c,v 1.1.1.1 2006/01/14 20:11:02 christos Exp $ */
3 /*
4 * untgz.c -- Display contents and extract files from a gzip'd TAR file
6 * written by Pedro A. Aranda Gutierrez <paag@tid.es>
7 * adaptation to Unix by Jean-loup Gailly <jloup@gzip.org>
8 * various fixes by Cosmin Truta <cosmint@cs.ubbcluj.ro>
9 */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <time.h>
15 #include <errno.h>
17 #include "zlib.h"
19 #ifdef unix
20 # include <unistd.h>
21 #else
22 # include <direct.h>
23 # include <io.h>
24 #endif
26 #ifdef WIN32
27 #include <windows.h>
28 # ifndef F_OK
29 # define F_OK 0
30 # endif
31 # define mkdir(dirname,mode) _mkdir(dirname)
32 # ifdef _MSC_VER
33 # define access(path,mode) _access(path,mode)
34 # define chmod(path,mode) _chmod(path,mode)
35 # define strdup(str) _strdup(str)
36 # endif
37 #else
38 # include <utime.h>
39 #endif
42 /* values used in typeflag field */
44 #define REGTYPE '0' /* regular file */
45 #define AREGTYPE '\0' /* regular file */
46 #define LNKTYPE '1' /* link */
47 #define SYMTYPE '2' /* reserved */
48 #define CHRTYPE '3' /* character special */
49 #define BLKTYPE '4' /* block special */
50 #define DIRTYPE '5' /* directory */
51 #define FIFOTYPE '6' /* FIFO special */
52 #define CONTTYPE '7' /* reserved */
54 /* GNU tar extensions */
56 #define GNUTYPE_DUMPDIR 'D' /* file names from dumped directory */
57 #define GNUTYPE_LONGLINK 'K' /* long link name */
58 #define GNUTYPE_LONGNAME 'L' /* long file name */
59 #define GNUTYPE_MULTIVOL 'M' /* continuation of file from another volume */
60 #define GNUTYPE_NAMES 'N' /* file name that does not fit into main hdr */
61 #define GNUTYPE_SPARSE 'S' /* sparse file */
62 #define GNUTYPE_VOLHDR 'V' /* tape/volume header */
65 /* tar header */
67 #define BLOCKSIZE 512
68 #define SHORTNAMESIZE 100
70 struct tar_header
71 { /* byte offset */
72 char name[100]; /* 0 */
73 char mode[8]; /* 100 */
74 char uid[8]; /* 108 */
75 char gid[8]; /* 116 */
76 char size[12]; /* 124 */
77 char mtime[12]; /* 136 */
78 char chksum[8]; /* 148 */
79 char typeflag; /* 156 */
80 char linkname[100]; /* 157 */
81 char magic[6]; /* 257 */
82 char version[2]; /* 263 */
83 char uname[32]; /* 265 */
84 char gname[32]; /* 297 */
85 char devmajor[8]; /* 329 */
86 char devminor[8]; /* 337 */
87 char prefix[155]; /* 345 */
88 /* 500 */
91 union tar_buffer
93 char buffer[BLOCKSIZE];
94 struct tar_header header;
97 struct attr_item
99 struct attr_item *next;
100 char *fname;
101 int mode;
102 time_t time;
105 enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID };
107 char *TGZfname OF((const char *));
108 void TGZnotfound OF((const char *));
110 int getoct OF((char *, int));
111 char *strtime OF((time_t *));
112 int setfiletime OF((char *, time_t));
113 void push_attr OF((struct attr_item **, char *, int, time_t));
114 void restore_attr OF((struct attr_item **));
116 int ExprMatch OF((char *, char *));
118 int makedir OF((char *));
119 int matchname OF((int, int, char **, char *));
121 void error OF((const char *));
122 int tar OF((gzFile, int, int, int, char **));
124 void help OF((int));
125 int main OF((int, char **));
127 char *prog;
129 const char *TGZsuffix[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL };
131 /* return the file name of the TGZ archive */
132 /* or NULL if it does not exist */
134 char *TGZfname (const char *arcname)
136 static char buffer[1024];
137 int origlen,i;
139 strcpy(buffer,arcname);
140 origlen = strlen(buffer);
142 for (i=0; TGZsuffix[i]; i++)
144 strcpy(buffer+origlen,TGZsuffix[i]);
145 if (access(buffer,F_OK) == 0)
146 return buffer;
148 return NULL;
152 /* error message for the filename */
154 void TGZnotfound (const char *arcname)
156 int i;
158 fprintf(stderr,"%s: Couldn't find ",prog);
159 for (i=0;TGZsuffix[i];i++)
160 fprintf(stderr,(TGZsuffix[i+1]) ? "%s%s, " : "or %s%s\n",
161 arcname,
162 TGZsuffix[i]);
163 exit(1);
167 /* convert octal digits to int */
168 /* on error return -1 */
170 int getoct (char *p,int width)
172 int result = 0;
173 char c;
175 while (width--)
177 c = *p++;
178 if (c == 0)
179 break;
180 if (c == ' ')
181 continue;
182 if (c < '0' || c > '7')
183 return -1;
184 result = result * 8 + (c - '0');
186 return result;
190 /* convert time_t to string */
191 /* use the "YYYY/MM/DD hh:mm:ss" format */
193 char *strtime (time_t *t)
195 struct tm *local;
196 static char result[32];
198 local = localtime(t);
199 sprintf(result,"%4d/%02d/%02d %02d:%02d:%02d",
200 local->tm_year+1900, local->tm_mon+1, local->tm_mday,
201 local->tm_hour, local->tm_min, local->tm_sec);
202 return result;
206 /* set file time */
208 int setfiletime (char *fname,time_t ftime)
210 #ifdef WIN32
211 static int isWinNT = -1;
212 SYSTEMTIME st;
213 FILETIME locft, modft;
214 struct tm *loctm;
215 HANDLE hFile;
216 int result;
218 loctm = localtime(&ftime);
219 if (loctm == NULL)
220 return -1;
222 st.wYear = (WORD)loctm->tm_year + 1900;
223 st.wMonth = (WORD)loctm->tm_mon + 1;
224 st.wDayOfWeek = (WORD)loctm->tm_wday;
225 st.wDay = (WORD)loctm->tm_mday;
226 st.wHour = (WORD)loctm->tm_hour;
227 st.wMinute = (WORD)loctm->tm_min;
228 st.wSecond = (WORD)loctm->tm_sec;
229 st.wMilliseconds = 0;
230 if (!SystemTimeToFileTime(&st, &locft) ||
231 !LocalFileTimeToFileTime(&locft, &modft))
232 return -1;
234 if (isWinNT < 0)
235 isWinNT = (GetVersion() < 0x80000000) ? 1 : 0;
236 hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
237 (isWinNT ? FILE_FLAG_BACKUP_SEMANTICS : 0),
238 NULL);
239 if (hFile == INVALID_HANDLE_VALUE)
240 return -1;
241 result = SetFileTime(hFile, NULL, NULL, &modft) ? 0 : -1;
242 CloseHandle(hFile);
243 return result;
244 #else
245 struct utimbuf settime;
247 settime.actime = settime.modtime = ftime;
248 return utime(fname,&settime);
249 #endif
253 /* push file attributes */
255 void push_attr(struct attr_item **list,char *fname,int mode,time_t time)
257 struct attr_item *item;
259 item = (struct attr_item *)malloc(sizeof(struct attr_item));
260 if (item == NULL)
261 error("Out of memory");
262 item->fname = strdup(fname);
263 item->mode = mode;
264 item->time = time;
265 item->next = *list;
266 *list = item;
270 /* restore file attributes */
272 void restore_attr(struct attr_item **list)
274 struct attr_item *item, *prev;
276 for (item = *list; item != NULL; )
278 setfiletime(item->fname,item->time);
279 chmod(item->fname,item->mode);
280 prev = item;
281 item = item->next;
282 free(prev);
284 *list = NULL;
288 /* match regular expression */
290 #define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
292 int ExprMatch (char *string,char *expr)
294 while (1)
296 if (ISSPECIAL(*expr))
298 if (*expr == '/')
300 if (*string != '\\' && *string != '/')
301 return 0;
302 string ++; expr++;
304 else if (*expr == '*')
306 if (*expr ++ == 0)
307 return 1;
308 while (*++string != *expr)
309 if (*string == 0)
310 return 0;
313 else
315 if (*string != *expr)
316 return 0;
317 if (*expr++ == 0)
318 return 1;
319 string++;
325 /* recursive mkdir */
326 /* abort on ENOENT; ignore other errors like "directory already exists" */
327 /* return 1 if OK */
328 /* 0 on error */
330 int makedir (char *newdir)
332 char *buffer = strdup(newdir);
333 char *p;
334 int len = strlen(buffer);
336 if (len <= 0) {
337 free(buffer);
338 return 0;
340 if (buffer[len-1] == '/') {
341 buffer[len-1] = '\0';
343 if (mkdir(buffer, 0755) == 0)
345 free(buffer);
346 return 1;
349 p = buffer+1;
350 while (1)
352 char hold;
354 while(*p && *p != '\\' && *p != '/')
355 p++;
356 hold = *p;
357 *p = 0;
358 if ((mkdir(buffer, 0755) == -1) && (errno == ENOENT))
360 fprintf(stderr,"%s: Couldn't create directory %s\n",prog,buffer);
361 free(buffer);
362 return 0;
364 if (hold == 0)
365 break;
366 *p++ = hold;
368 free(buffer);
369 return 1;
373 int matchname (int arg,int argc,char **argv,char *fname)
375 if (arg == argc) /* no arguments given (untgz tgzarchive) */
376 return 1;
378 while (arg < argc)
379 if (ExprMatch(fname,argv[arg++]))
380 return 1;
382 return 0; /* ignore this for the moment being */
386 /* tar file list or extract */
388 int tar (gzFile in,int action,int arg,int argc,char **argv)
390 union tar_buffer buffer;
391 int len;
392 int err;
393 int getheader = 1;
394 int remaining = 0;
395 FILE *outfile = NULL;
396 char fname[BLOCKSIZE];
397 int tarmode;
398 time_t tartime;
399 struct attr_item *attributes = NULL;
401 if (action == TGZ_LIST)
402 printf(" date time size file\n"
403 " ---------- -------- --------- -------------------------------------\n");
404 while (1)
406 len = gzread(in, &buffer, BLOCKSIZE);
407 if (len < 0)
408 error(gzerror(in, &err));
410 * Always expect complete blocks to process
411 * the tar information.
413 if (len != BLOCKSIZE)
415 action = TGZ_INVALID; /* force error exit */
416 remaining = 0; /* force I/O cleanup */
420 * If we have to get a tar header
422 if (getheader >= 1)
425 * if we met the end of the tar
426 * or the end-of-tar block,
427 * we are done
429 if (len == 0 || buffer.header.name[0] == 0)
430 break;
432 tarmode = getoct(buffer.header.mode,8);
433 tartime = (time_t)getoct(buffer.header.mtime,12);
434 if (tarmode == -1 || tartime == (time_t)-1)
436 buffer.header.name[0] = 0;
437 action = TGZ_INVALID;
440 if (getheader == 1)
442 strncpy(fname,buffer.header.name,SHORTNAMESIZE);
443 if (fname[SHORTNAMESIZE-1] != 0)
444 fname[SHORTNAMESIZE] = 0;
446 else
449 * The file name is longer than SHORTNAMESIZE
451 if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1) != 0)
452 error("bad long name");
453 getheader = 1;
457 * Act according to the type flag
459 switch (buffer.header.typeflag)
461 case DIRTYPE:
462 if (action == TGZ_LIST)
463 printf(" %s <dir> %s\n",strtime(&tartime),fname);
464 if (action == TGZ_EXTRACT)
466 makedir(fname);
467 push_attr(&attributes,fname,tarmode,tartime);
469 break;
470 case REGTYPE:
471 case AREGTYPE:
472 remaining = getoct(buffer.header.size,12);
473 if (remaining == -1)
475 action = TGZ_INVALID;
476 break;
478 if (action == TGZ_LIST)
479 printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
480 else if (action == TGZ_EXTRACT)
482 if (matchname(arg,argc,argv,fname))
484 outfile = fopen(fname,"wb");
485 if (outfile == NULL) {
486 /* try creating directory */
487 char *p = strrchr(fname, '/');
488 if (p != NULL) {
489 *p = '\0';
490 makedir(fname);
491 *p = '/';
492 outfile = fopen(fname,"wb");
495 if (outfile != NULL)
496 printf("Extracting %s\n",fname);
497 else
498 fprintf(stderr, "%s: Couldn't create %s",prog,fname);
500 else
501 outfile = NULL;
503 getheader = 0;
504 break;
505 case GNUTYPE_LONGLINK:
506 case GNUTYPE_LONGNAME:
507 remaining = getoct(buffer.header.size,12);
508 if (remaining < 0 || remaining >= BLOCKSIZE)
510 action = TGZ_INVALID;
511 break;
513 len = gzread(in, fname, BLOCKSIZE);
514 if (len < 0)
515 error(gzerror(in, &err));
516 if (fname[BLOCKSIZE-1] != 0 || (int)strlen(fname) > remaining)
518 action = TGZ_INVALID;
519 break;
521 getheader = 2;
522 break;
523 default:
524 if (action == TGZ_LIST)
525 printf(" %s <---> %s\n",strtime(&tartime),fname);
526 break;
529 else
531 unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
533 if (outfile != NULL)
535 if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
537 fprintf(stderr,
538 "%s: Error writing %s -- skipping\n",prog,fname);
539 fclose(outfile);
540 outfile = NULL;
541 remove(fname);
544 remaining -= bytes;
547 if (remaining == 0)
549 getheader = 1;
550 if (outfile != NULL)
552 fclose(outfile);
553 outfile = NULL;
554 if (action != TGZ_INVALID)
555 push_attr(&attributes,fname,tarmode,tartime);
560 * Abandon if errors are found
562 if (action == TGZ_INVALID)
564 error("broken archive");
565 break;
570 * Restore file modes and time stamps
572 restore_attr(&attributes);
574 if (gzclose(in) != Z_OK)
575 error("failed gzclose");
577 return 0;
581 /* ============================================================ */
583 void help(int exitval)
585 printf("untgz version 0.2.1\n"
586 " using zlib version %s\n\n",
587 zlibVersion());
588 printf("Usage: untgz file.tgz extract all files\n"
589 " untgz file.tgz fname ... extract selected files\n"
590 " untgz -l file.tgz list archive contents\n"
591 " untgz -h display this help\n");
592 exit(exitval);
595 void error(const char *msg)
597 fprintf(stderr, "%s: %s\n", prog, msg);
598 exit(1);
602 /* ============================================================ */
604 #if defined(WIN32) && defined(__GNUC__)
605 int _CRT_glob = 0; /* disable argument globbing in MinGW */
606 #endif
608 int main(int argc,char **argv)
610 int action = TGZ_EXTRACT;
611 int arg = 1;
612 char *TGZfile;
613 gzFile *f;
615 prog = strrchr(argv[0],'\\');
616 if (prog == NULL)
618 prog = strrchr(argv[0],'/');
619 if (prog == NULL)
621 prog = strrchr(argv[0],':');
622 if (prog == NULL)
623 prog = argv[0];
624 else
625 prog++;
627 else
628 prog++;
630 else
631 prog++;
633 if (argc == 1)
634 help(0);
636 if (strcmp(argv[arg],"-l") == 0)
638 action = TGZ_LIST;
639 if (argc == ++arg)
640 help(0);
642 else if (strcmp(argv[arg],"-h") == 0)
644 help(0);
647 if ((TGZfile = TGZfname(argv[arg])) == NULL)
648 TGZnotfound(argv[arg]);
650 ++arg;
651 if ((action == TGZ_LIST) && (arg != argc))
652 help(1);
655 * Process the TGZ file
657 switch(action)
659 case TGZ_LIST:
660 case TGZ_EXTRACT:
661 f = gzopen(TGZfile,"rb");
662 if (f == NULL)
664 fprintf(stderr,"%s: Couldn't gzopen %s\n",prog,TGZfile);
665 return 1;
667 exit(tar(f, action, arg, argc, argv));
668 break;
670 default:
671 error("Unknown option");
672 exit(1);
675 return 0;