• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

contrib/zlib/contrib/untgz/untgz.c

00001 /*
00002  * untgz.c -- Display contents and extract files from a gzip'd TAR file
00003  *
00004  * written by Pedro A. Aranda Gutierrez <paag@tid.es>
00005  * adaptation to Unix by Jean-loup Gailly <jloup@gzip.org>
00006  * various fixes by Cosmin Truta <cosmint@cs.ubbcluj.ro>
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 /* values used in typeflag field */
00041 
00042 #define REGTYPE  '0'            /* regular file */
00043 #define AREGTYPE '\0'           /* regular file */
00044 #define LNKTYPE  '1'            /* link */
00045 #define SYMTYPE  '2'            /* reserved */
00046 #define CHRTYPE  '3'            /* character special */
00047 #define BLKTYPE  '4'            /* block special */
00048 #define DIRTYPE  '5'            /* directory */
00049 #define FIFOTYPE '6'            /* FIFO special */
00050 #define CONTTYPE '7'            /* reserved */
00051 
00052 /* GNU tar extensions */
00053 
00054 #define GNUTYPE_DUMPDIR  'D'    /* file names from dumped directory */
00055 #define GNUTYPE_LONGLINK 'K'    /* long link name */
00056 #define GNUTYPE_LONGNAME 'L'    /* long file name */
00057 #define GNUTYPE_MULTIVOL 'M'    /* continuation of file from another volume */
00058 #define GNUTYPE_NAMES    'N'    /* file name that does not fit into main hdr */
00059 #define GNUTYPE_SPARSE   'S'    /* sparse file */
00060 #define GNUTYPE_VOLHDR   'V'    /* tape/volume header */
00061 
00062 
00063 /* tar header */
00064 
00065 #define BLOCKSIZE     512
00066 #define SHORTNAMESIZE 100
00067 
00068 struct tar_header
00069 {                               /* byte offset */
00070   char name[100];               /*   0 */
00071   char mode[8];                 /* 100 */
00072   char uid[8];                  /* 108 */
00073   char gid[8];                  /* 116 */
00074   char size[12];                /* 124 */
00075   char mtime[12];               /* 136 */
00076   char chksum[8];               /* 148 */
00077   char typeflag;                /* 156 */
00078   char linkname[100];           /* 157 */
00079   char magic[6];                /* 257 */
00080   char version[2];              /* 263 */
00081   char uname[32];               /* 265 */
00082   char gname[32];               /* 297 */
00083   char devmajor[8];             /* 329 */
00084   char devminor[8];             /* 337 */
00085   char prefix[155];             /* 345 */
00086                                 /* 500 */
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 /* return the file name of the TGZ archive */
00130 /* or NULL if it does not exist */
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 /* error message for the filename */
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 /* convert octal digits to int */
00166 /* on error return -1 */
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 /* convert time_t to string */
00189 /* use the "YYYY/MM/DD hh:mm:ss" format */
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 /* set file time */
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 /* push file attributes */
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 /* restore file attributes */
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 /* match regular expression */
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 /* recursive mkdir */
00324 /* abort on ENOENT; ignore other errors like "directory already exists" */
00325 /* return 1 if OK */
00326 /*        0 on error */
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)      /* no arguments given (untgz tgzarchive) */
00374     return 1;
00375 
00376   while (arg < argc)
00377     if (ExprMatch(fname,argv[arg++]))
00378       return 1;
00379 
00380   return 0; /* ignore this for the moment being */
00381 }
00382 
00383 
00384 /* tar file list or extract */
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        * Always expect complete blocks to process
00409        * the tar information.
00410        */
00411       if (len != BLOCKSIZE)
00412         {
00413           action = TGZ_INVALID; /* force error exit */
00414           remaining = 0;        /* force I/O cleanup */
00415         }
00416 
00417       /*
00418        * If we have to get a tar header
00419        */
00420       if (getheader >= 1)
00421         {
00422           /*
00423            * if we met the end of the tar
00424            * or the end-of-tar block,
00425            * we are done
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                * The file name is longer than SHORTNAMESIZE
00448                */
00449               if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1) != 0)
00450                   error("bad long name");
00451               getheader = 1;
00452             }
00453 
00454           /*
00455            * Act according to the type flag
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                         /* try creating directory */
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        * Abandon if errors are found
00559        */
00560       if (action == TGZ_INVALID)
00561         {
00562           error("broken archive");
00563           break;
00564         }
00565     }
00566 
00567   /*
00568    * Restore file modes and time stamps
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;      /* disable argument globbing in MinGW */
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  *  Process the TGZ file
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 }

Generated on Wed Oct 20 2010 11:12:17 for APBS by  doxygen 1.7.2