untgz.c 16 KB

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