00001
00002
00003
00004
00005
00006
00007
00008
00009 #include <stdio.h>
00010 #include <stdlib.h>
00011 #include <string.h>
00012 #include <time.h>
00013 #include <errno.h>
00014
00015 #include "zlib.h"
00016
00017 #ifdef unix
00018 # include <unistd.h>
00019 #else
00020 # include <direct.h>
00021 # include <io.h>
00022 #endif
00023
00024 #ifdef WIN32
00025 #include <windows.h>
00026 # ifndef F_OK
00027 # define F_OK 0
00028 # endif
00029 # define mkdir(dirname,mode) _mkdir(dirname)
00030 # ifdef _MSC_VER
00031 # define access(path,mode) _access(path,mode)
00032 # define chmod(path,mode) _chmod(path,mode)
00033 # define strdup(str) _strdup(str)
00034 # endif
00035 #else
00036 # include <utime.h>
00037 #endif
00038
00039
00040
00041
00042 #define REGTYPE '0'
00043 #define AREGTYPE '\0'
00044 #define LNKTYPE '1'
00045 #define SYMTYPE '2'
00046 #define CHRTYPE '3'
00047 #define BLKTYPE '4'
00048 #define DIRTYPE '5'
00049 #define FIFOTYPE '6'
00050 #define CONTTYPE '7'
00051
00052
00053
00054 #define GNUTYPE_DUMPDIR 'D'
00055 #define GNUTYPE_LONGLINK 'K'
00056 #define GNUTYPE_LONGNAME 'L'
00057 #define GNUTYPE_MULTIVOL 'M'
00058 #define GNUTYPE_NAMES 'N'
00059 #define GNUTYPE_SPARSE 'S'
00060 #define GNUTYPE_VOLHDR 'V'
00061
00062
00063
00064
00065 #define BLOCKSIZE 512
00066 #define SHORTNAMESIZE 100
00067
00068 struct tar_header
00069 {
00070 char name[100];
00071 char mode[8];
00072 char uid[8];
00073 char gid[8];
00074 char size[12];
00075 char mtime[12];
00076 char chksum[8];
00077 char typeflag;
00078 char linkname[100];
00079 char magic[6];
00080 char version[2];
00081 char uname[32];
00082 char gname[32];
00083 char devmajor[8];
00084 char devminor[8];
00085 char prefix[155];
00086
00087 };
00088
00089 union tar_buffer
00090 {
00091 char buffer[BLOCKSIZE];
00092 struct tar_header header;
00093 };
00094
00095 struct attr_item
00096 {
00097 struct attr_item *next;
00098 char *fname;
00099 int mode;
00100 time_t time;
00101 };
00102
00103 enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID };
00104
00105 char *TGZfname OF((const char *));
00106 void TGZnotfound OF((const char *));
00107
00108 int getoct OF((char *, int));
00109 char *strtime OF((time_t *));
00110 int setfiletime OF((char *, time_t));
00111 void push_attr OF((struct attr_item **, char *, int, time_t));
00112 void restore_attr OF((struct attr_item **));
00113
00114 int ExprMatch OF((char *, char *));
00115
00116 int makedir OF((char *));
00117 int matchname OF((int, int, char **, char *));
00118
00119 void error OF((const char *));
00120 int tar OF((gzFile, int, int, int, char **));
00121
00122 void help OF((int));
00123 int main OF((int, char **));
00124
00125 char *prog;
00126
00127 const char *TGZsuffix[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL };
00128
00129
00130
00131
00132 char *TGZfname (const char *arcname)
00133 {
00134 static char buffer[1024];
00135 int origlen,i;
00136
00137 strcpy(buffer,arcname);
00138 origlen = strlen(buffer);
00139
00140 for (i=0; TGZsuffix[i]; i++)
00141 {
00142 strcpy(buffer+origlen,TGZsuffix[i]);
00143 if (access(buffer,F_OK) == 0)
00144 return buffer;
00145 }
00146 return NULL;
00147 }
00148
00149
00150
00151
00152 void TGZnotfound (const char *arcname)
00153 {
00154 int i;
00155
00156 fprintf(stderr,"%s: Couldn't find ",prog);
00157 for (i=0;TGZsuffix[i];i++)
00158 fprintf(stderr,(TGZsuffix[i+1]) ? "%s%s, " : "or %s%s\n",
00159 arcname,
00160 TGZsuffix[i]);
00161 exit(1);
00162 }
00163
00164
00165
00166
00167
00168 int getoct (char *p,int width)
00169 {
00170 int result = 0;
00171 char c;
00172
00173 while (width--)
00174 {
00175 c = *p++;
00176 if (c == 0)
00177 break;
00178 if (c == ' ')
00179 continue;
00180 if (c < '0' || c > '7')
00181 return -1;
00182 result = result * 8 + (c - '0');
00183 }
00184 return result;
00185 }
00186
00187
00188
00189
00190
00191 char *strtime (time_t *t)
00192 {
00193 struct tm *local;
00194 static char result[32];
00195
00196 local = localtime(t);
00197 sprintf(result,"%4d/%02d/%02d %02d:%02d:%02d",
00198 local->tm_year+1900, local->tm_mon+1, local->tm_mday,
00199 local->tm_hour, local->tm_min, local->tm_sec);
00200 return result;
00201 }
00202
00203
00204
00205
00206 int setfiletime (char *fname,time_t ftime)
00207 {
00208 #ifdef WIN32
00209 static int isWinNT = -1;
00210 SYSTEMTIME st;
00211 FILETIME locft, modft;
00212 struct tm *loctm;
00213 HANDLE hFile;
00214 int result;
00215
00216 loctm = localtime(&ftime);
00217 if (loctm == NULL)
00218 return -1;
00219
00220 st.wYear = (WORD)loctm->tm_year + 1900;
00221 st.wMonth = (WORD)loctm->tm_mon + 1;
00222 st.wDayOfWeek = (WORD)loctm->tm_wday;
00223 st.wDay = (WORD)loctm->tm_mday;
00224 st.wHour = (WORD)loctm->tm_hour;
00225 st.wMinute = (WORD)loctm->tm_min;
00226 st.wSecond = (WORD)loctm->tm_sec;
00227 st.wMilliseconds = 0;
00228 if (!SystemTimeToFileTime(&st, &locft) ||
00229 !LocalFileTimeToFileTime(&locft, &modft))
00230 return -1;
00231
00232 if (isWinNT < 0)
00233 isWinNT = (GetVersion() < 0x80000000) ? 1 : 0;
00234 hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
00235 (isWinNT ? FILE_FLAG_BACKUP_SEMANTICS : 0),
00236 NULL);
00237 if (hFile == INVALID_HANDLE_VALUE)
00238 return -1;
00239 result = SetFileTime(hFile, NULL, NULL, &modft) ? 0 : -1;
00240 CloseHandle(hFile);
00241 return result;
00242 #else
00243 struct utimbuf settime;
00244
00245 settime.actime = settime.modtime = ftime;
00246 return utime(fname,&settime);
00247 #endif
00248 }
00249
00250
00251
00252
00253 void push_attr(struct attr_item **list,char *fname,int mode,time_t time)
00254 {
00255 struct attr_item *item;
00256
00257 item = (struct attr_item *)malloc(sizeof(struct attr_item));
00258 if (item == NULL)
00259 error("Out of memory");
00260 item->fname = strdup(fname);
00261 item->mode = mode;
00262 item->time = time;
00263 item->next = *list;
00264 *list = item;
00265 }
00266
00267
00268
00269
00270 void restore_attr(struct attr_item **list)
00271 {
00272 struct attr_item *item, *prev;
00273
00274 for (item = *list; item != NULL; )
00275 {
00276 setfiletime(item->fname,item->time);
00277 chmod(item->fname,item->mode);
00278 prev = item;
00279 item = item->next;
00280 free(prev);
00281 }
00282 *list = NULL;
00283 }
00284
00285
00286
00287
00288 #define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
00289
00290 int ExprMatch (char *string,char *expr)
00291 {
00292 while (1)
00293 {
00294 if (ISSPECIAL(*expr))
00295 {
00296 if (*expr == '/')
00297 {
00298 if (*string != '\\' && *string != '/')
00299 return 0;
00300 string ++; expr++;
00301 }
00302 else if (*expr == '*')
00303 {
00304 if (*expr ++ == 0)
00305 return 1;
00306 while (*++string != *expr)
00307 if (*string == 0)
00308 return 0;
00309 }
00310 }
00311 else
00312 {
00313 if (*string != *expr)
00314 return 0;
00315 if (*expr++ == 0)
00316 return 1;
00317 string++;
00318 }
00319 }
00320 }
00321
00322
00323
00324
00325
00326
00327
00328 int makedir (char *newdir)
00329 {
00330 char *buffer = strdup(newdir);
00331 char *p;
00332 int len = strlen(buffer);
00333
00334 if (len <= 0) {
00335 free(buffer);
00336 return 0;
00337 }
00338 if (buffer[len-1] == '/') {
00339 buffer[len-1] = '\0';
00340 }
00341 if (mkdir(buffer, 0755) == 0)
00342 {
00343 free(buffer);
00344 return 1;
00345 }
00346
00347 p = buffer+1;
00348 while (1)
00349 {
00350 char hold;
00351
00352 while(*p && *p != '\\' && *p != '/')
00353 p++;
00354 hold = *p;
00355 *p = 0;
00356 if ((mkdir(buffer, 0755) == -1) && (errno == ENOENT))
00357 {
00358 fprintf(stderr,"%s: Couldn't create directory %s\n",prog,buffer);
00359 free(buffer);
00360 return 0;
00361 }
00362 if (hold == 0)
00363 break;
00364 *p++ = hold;
00365 }
00366 free(buffer);
00367 return 1;
00368 }
00369
00370
00371 int matchname (int arg,int argc,char **argv,char *fname)
00372 {
00373 if (arg == argc)
00374 return 1;
00375
00376 while (arg < argc)
00377 if (ExprMatch(fname,argv[arg++]))
00378 return 1;
00379
00380 return 0;
00381 }
00382
00383
00384
00385
00386 int tar (gzFile in,int action,int arg,int argc,char **argv)
00387 {
00388 union tar_buffer buffer;
00389 int len;
00390 int err;
00391 int getheader = 1;
00392 int remaining = 0;
00393 FILE *outfile = NULL;
00394 char fname[BLOCKSIZE];
00395 int tarmode;
00396 time_t tartime;
00397 struct attr_item *attributes = NULL;
00398
00399 if (action == TGZ_LIST)
00400 printf(" date time size file\n"
00401 " ---------- -------- --------- -------------------------------------\n");
00402 while (1)
00403 {
00404 len = gzread(in, &buffer, BLOCKSIZE);
00405 if (len < 0)
00406 error(gzerror(in, &err));
00407
00408
00409
00410
00411 if (len != BLOCKSIZE)
00412 {
00413 action = TGZ_INVALID;
00414 remaining = 0;
00415 }
00416
00417
00418
00419
00420 if (getheader >= 1)
00421 {
00422
00423
00424
00425
00426
00427 if (len == 0 || buffer.header.name[0] == 0)
00428 break;
00429
00430 tarmode = getoct(buffer.header.mode,8);
00431 tartime = (time_t)getoct(buffer.header.mtime,12);
00432 if (tarmode == -1 || tartime == (time_t)-1)
00433 {
00434 buffer.header.name[0] = 0;
00435 action = TGZ_INVALID;
00436 }
00437
00438 if (getheader == 1)
00439 {
00440 strncpy(fname,buffer.header.name,SHORTNAMESIZE);
00441 if (fname[SHORTNAMESIZE-1] != 0)
00442 fname[SHORTNAMESIZE] = 0;
00443 }
00444 else
00445 {
00446
00447
00448
00449 if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1) != 0)
00450 error("bad long name");
00451 getheader = 1;
00452 }
00453
00454
00455
00456
00457 switch (buffer.header.typeflag)
00458 {
00459 case DIRTYPE:
00460 if (action == TGZ_LIST)
00461 printf(" %s <dir> %s\n",strtime(&tartime),fname);
00462 if (action == TGZ_EXTRACT)
00463 {
00464 makedir(fname);
00465 push_attr(&attributes,fname,tarmode,tartime);
00466 }
00467 break;
00468 case REGTYPE:
00469 case AREGTYPE:
00470 remaining = getoct(buffer.header.size,12);
00471 if (remaining == -1)
00472 {
00473 action = TGZ_INVALID;
00474 break;
00475 }
00476 if (action == TGZ_LIST)
00477 printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
00478 else if (action == TGZ_EXTRACT)
00479 {
00480 if (matchname(arg,argc,argv,fname))
00481 {
00482 outfile = fopen(fname,"wb");
00483 if (outfile == NULL) {
00484
00485 char *p = strrchr(fname, '/');
00486 if (p != NULL) {
00487 *p = '\0';
00488 makedir(fname);
00489 *p = '/';
00490 outfile = fopen(fname,"wb");
00491 }
00492 }
00493 if (outfile != NULL)
00494 printf("Extracting %s\n",fname);
00495 else
00496 fprintf(stderr, "%s: Couldn't create %s",prog,fname);
00497 }
00498 else
00499 outfile = NULL;
00500 }
00501 getheader = 0;
00502 break;
00503 case GNUTYPE_LONGLINK:
00504 case GNUTYPE_LONGNAME:
00505 remaining = getoct(buffer.header.size,12);
00506 if (remaining < 0 || remaining >= BLOCKSIZE)
00507 {
00508 action = TGZ_INVALID;
00509 break;
00510 }
00511 len = gzread(in, fname, BLOCKSIZE);
00512 if (len < 0)
00513 error(gzerror(in, &err));
00514 if (fname[BLOCKSIZE-1] != 0 || (int)strlen(fname) > remaining)
00515 {
00516 action = TGZ_INVALID;
00517 break;
00518 }
00519 getheader = 2;
00520 break;
00521 default:
00522 if (action == TGZ_LIST)
00523 printf(" %s <---> %s\n",strtime(&tartime),fname);
00524 break;
00525 }
00526 }
00527 else
00528 {
00529 unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
00530
00531 if (outfile != NULL)
00532 {
00533 if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
00534 {
00535 fprintf(stderr,
00536 "%s: Error writing %s -- skipping\n",prog,fname);
00537 fclose(outfile);
00538 outfile = NULL;
00539 remove(fname);
00540 }
00541 }
00542 remaining -= bytes;
00543 }
00544
00545 if (remaining == 0)
00546 {
00547 getheader = 1;
00548 if (outfile != NULL)
00549 {
00550 fclose(outfile);
00551 outfile = NULL;
00552 if (action != TGZ_INVALID)
00553 push_attr(&attributes,fname,tarmode,tartime);
00554 }
00555 }
00556
00557
00558
00559
00560 if (action == TGZ_INVALID)
00561 {
00562 error("broken archive");
00563 break;
00564 }
00565 }
00566
00567
00568
00569
00570 restore_attr(&attributes);
00571
00572 if (gzclose(in) != Z_OK)
00573 error("failed gzclose");
00574
00575 return 0;
00576 }
00577
00578
00579
00580
00581 void help(int exitval)
00582 {
00583 printf("untgz version 0.2.1\n"
00584 " using zlib version %s\n\n",
00585 zlibVersion());
00586 printf("Usage: untgz file.tgz extract all files\n"
00587 " untgz file.tgz fname ... extract selected files\n"
00588 " untgz -l file.tgz list archive contents\n"
00589 " untgz -h display this help\n");
00590 exit(exitval);
00591 }
00592
00593 void error(const char *msg)
00594 {
00595 fprintf(stderr, "%s: %s\n", prog, msg);
00596 exit(1);
00597 }
00598
00599
00600
00601
00602 #if defined(WIN32) && defined(__GNUC__)
00603 int _CRT_glob = 0;
00604 #endif
00605
00606 int main(int argc,char **argv)
00607 {
00608 int action = TGZ_EXTRACT;
00609 int arg = 1;
00610 char *TGZfile;
00611 gzFile *f;
00612
00613 prog = strrchr(argv[0],'\\');
00614 if (prog == NULL)
00615 {
00616 prog = strrchr(argv[0],'/');
00617 if (prog == NULL)
00618 {
00619 prog = strrchr(argv[0],':');
00620 if (prog == NULL)
00621 prog = argv[0];
00622 else
00623 prog++;
00624 }
00625 else
00626 prog++;
00627 }
00628 else
00629 prog++;
00630
00631 if (argc == 1)
00632 help(0);
00633
00634 if (strcmp(argv[arg],"-l") == 0)
00635 {
00636 action = TGZ_LIST;
00637 if (argc == ++arg)
00638 help(0);
00639 }
00640 else if (strcmp(argv[arg],"-h") == 0)
00641 {
00642 help(0);
00643 }
00644
00645 if ((TGZfile = TGZfname(argv[arg])) == NULL)
00646 TGZnotfound(argv[arg]);
00647
00648 ++arg;
00649 if ((action == TGZ_LIST) && (arg != argc))
00650 help(1);
00651
00652
00653
00654
00655 switch(action)
00656 {
00657 case TGZ_LIST:
00658 case TGZ_EXTRACT:
00659 f = gzopen(TGZfile,"rb");
00660 if (f == NULL)
00661 {
00662 fprintf(stderr,"%s: Couldn't gzopen %s\n",prog,TGZfile);
00663 return 1;
00664 }
00665 exit(tar(f, action, arg, argc, argv));
00666 break;
00667
00668 default:
00669 error("Unknown option");
00670 exit(1);
00671 }
00672
00673 return 0;
00674 }