update from zlib 1.1.4 to 1.2.3
[reactos.git] / reactos / lib / 3rdparty / zlib / contrib / untgz / untgz.c
index 4a431ff..2c391e5 100644 (file)
@@ -1,8 +1,9 @@
 /*
- * untgz.c -- Display contents and/or extract file from
- * a gzip'd TAR file
- * written by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
+ * untgz.c -- Display contents and extract files from a gzip'd TAR file
+ *
+ * written by Pedro A. Aranda Gutierrez <paag@tid.es>
  * adaptation to Unix by Jean-loup Gailly <jloup@gzip.org>
+ * various fixes by Cosmin Truta <cosmint@cs.ubbcluj.ro>
  */
 
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
 #include <errno.h>
-#include <fcntl.h>
+
+#include "zlib.h"
+
 #ifdef unix
-# include <unistd.h>
+#  include <unistd.h>
 #else
-# include <direct.h>
-# include <io.h>
+#  include <direct.h>
+#  include <io.h>
 #endif
 
-#include "zlib.h"
-
 #ifdef WIN32
+#include <windows.h>
 #  ifndef F_OK
-#    define F_OK (0)
+#    define F_OK  0
 #  endif
+#  define mkdir(dirname,mode)   _mkdir(dirname)
 #  ifdef _MSC_VER
-#    define mkdir(dirname,mode) _mkdir(dirname)
-#    define strdup(str)         _strdup(str)
-#    define unlink(fn)          _unlink(fn)
 #    define access(path,mode)   _access(path,mode)
-#  else
-#    define mkdir(dirname,mode) _mkdir(dirname)
+#    define chmod(path,mode)    _chmod(path,mode)
+#    define strdup(str)         _strdup(str)
 #  endif
 #else
 #  include <utime.h>
 #endif
 
 
-/* Values used in typeflag field.  */
+/* values used in typeflag field */
+
+#define REGTYPE  '0'            /* regular file */
+#define AREGTYPE '\0'           /* regular file */
+#define LNKTYPE  '1'            /* link */
+#define SYMTYPE  '2'            /* reserved */
+#define CHRTYPE  '3'            /* character special */
+#define BLKTYPE  '4'            /* block special */
+#define DIRTYPE  '5'            /* directory */
+#define FIFOTYPE '6'            /* FIFO special */
+#define CONTTYPE '7'            /* reserved */
+
+/* GNU tar extensions */
 
-#define REGTYPE         '0'            /* regular file */
-#define AREGTYPE '\0'          /* regular file */
-#define LNKTYPE  '1'           /* link */
-#define SYMTYPE  '2'           /* reserved */
-#define CHRTYPE  '3'           /* character special */
-#define BLKTYPE  '4'           /* block special */
-#define DIRTYPE  '5'           /* directory */
-#define FIFOTYPE '6'           /* FIFO special */
-#define CONTTYPE '7'           /* reserved */
+#define GNUTYPE_DUMPDIR  'D'    /* file names from dumped directory */
+#define GNUTYPE_LONGLINK 'K'    /* long link name */
+#define GNUTYPE_LONGNAME 'L'    /* long file name */
+#define GNUTYPE_MULTIVOL 'M'    /* continuation of file from another volume */
+#define GNUTYPE_NAMES    'N'    /* file name that does not fit into main hdr */
+#define GNUTYPE_SPARSE   'S'    /* sparse file */
+#define GNUTYPE_VOLHDR   'V'    /* tape/volume header */
 
-#define BLOCKSIZE 512
+
+/* tar header */
+
+#define BLOCKSIZE     512
+#define SHORTNAMESIZE 100
 
 struct tar_header
-{                              /* byte offset */
-  char name[100];              /*   0 */
-  char mode[8];                        /* 100 */
-  char uid[8];                 /* 108 */
-  char gid[8];                 /* 116 */
-  char size[12];               /* 124 */
-  char mtime[12];              /* 136 */
-  char chksum[8];              /* 148 */
-  char typeflag;               /* 156 */
-  char linkname[100];          /* 157 */
-  char magic[6];               /* 257 */
-  char version[2];             /* 263 */
-  char uname[32];              /* 265 */
-  char gname[32];              /* 297 */
-  char devmajor[8];            /* 329 */
-  char devminor[8];            /* 337 */
-  char prefix[155];            /* 345 */
-                               /* 500 */
+{                               /* byte offset */
+  char name[100];               /*   0 */
+  char mode[8];                 /* 100 */
+  char uid[8];                  /* 108 */
+  char gid[8];                  /* 116 */
+  char size[12];                /* 124 */
+  char mtime[12];               /* 136 */
+  char chksum[8];               /* 148 */
+  char typeflag;                /* 156 */
+  char linkname[100];           /* 157 */
+  char magic[6];                /* 257 */
+  char version[2];              /* 263 */
+  char uname[32];               /* 265 */
+  char gname[32];               /* 297 */
+  char devmajor[8];             /* 329 */
+  char devminor[8];             /* 337 */
+  char prefix[155];             /* 345 */
+                                /* 500 */
 };
 
-union tar_buffer {
+union tar_buffer
+{
   char               buffer[BLOCKSIZE];
   struct tar_header  header;
 };
 
-enum { TGZ_EXTRACT = 0, TGZ_LIST };
+struct attr_item
+{
+  struct attr_item  *next;
+  char              *fname;
+  int                mode;
+  time_t             time;
+};
+
+enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID };
 
-static char *TGZfname  OF((const char *));
-void TGZnotfound       OF((const char *));
+char *TGZfname          OF((const char *));
+void TGZnotfound        OF((const char *));
 
-int getoct             OF((char *, int));
-char *strtime          OF((time_t *));
-int ExprMatch          OF((char *,char *));
+int getoct              OF((char *, int));
+char *strtime           OF((time_t *));
+int setfiletime         OF((char *, time_t));
+void push_attr          OF((struct attr_item **, char *, int, time_t));
+void restore_attr       OF((struct attr_item **));
 
-int makedir            OF((char *));
-int matchname          OF((int,int,char **,char *));
+int ExprMatch           OF((char *, char *));
 
-void error             OF((const char *));
-int  tar               OF((gzFile, int, int, int, char **));
+int makedir             OF((char *));
+int matchname           OF((int, int, char **, char *));
 
-void help              OF((int));
-int main               OF((int, char **));
+void error              OF((const char *));
+int tar                 OF((gzFile, int, int, int, char **));
 
-char *prog;
+void help               OF((int));
+int main                OF((int, char **));
 
-/* This will give a benign warning */
+char *prog;
 
-static char *TGZprefix[] = { "\0", ".tgz", ".tar.gz", ".tar", NULL };
+const char *TGZsuffix[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL };
 
-/* Return the real name of the TGZ archive */
-/* or NULL if it does not exist. */
+/* return the file name of the TGZ archive */
+/* or NULL if it does not exist */
 
-static char *TGZfname OF((const char *fname))
+char *TGZfname (const char *arcname)
 {
   static char buffer[1024];
   int origlen,i;
-  
-  strcpy(buffer,fname);
+
+  strcpy(buffer,arcname);
   origlen = strlen(buffer);
 
-  for (i=0; TGZprefix[i]; i++)
+  for (i=0; TGZsuffix[i]; i++)
     {
-       strcpy(buffer+origlen,TGZprefix[i]);
+       strcpy(buffer+origlen,TGZsuffix[i]);
        if (access(buffer,F_OK) == 0)
          return buffer;
     }
   return NULL;
 }
 
+
 /* error message for the filename */
 
-void TGZnotfound OF((const char *fname))
+void TGZnotfound (const char *arcname)
 {
   int i;
 
-  fprintf(stderr,"%s : couldn't find ",prog);
-  for (i=0;TGZprefix[i];i++)
-    fprintf(stderr,(TGZprefix[i+1]) ? "%s%s, " : "or %s%s\n",
-            fname,
-            TGZprefix[i]);
+  fprintf(stderr,"%s: Couldn't find ",prog);
+  for (i=0;TGZsuffix[i];i++)
+    fprintf(stderr,(TGZsuffix[i+1]) ? "%s%s, " : "or %s%s\n",
+            arcname,
+            TGZsuffix[i]);
   exit(1);
 }
 
 
-/* help functions */
+/* convert octal digits to int */
+/* on error return -1 */
 
-int getoct(char *p,int width)
+int getoct (char *p,int width)
 {
   int result = 0;
   char c;
-  
-  while (width --)
+
+  while (width--)
     {
       c = *p++;
-      if (c == ' ')
-       continue;
       if (c == 0)
-       break;
+        break;
+      if (c == ' ')
+        continue;
+      if (c < '0' || c > '7')
+        return -1;
       result = result * 8 + (c - '0');
     }
   return result;
 }
 
+
+/* convert time_t to string */
+/* use the "YYYY/MM/DD hh:mm:ss" format */
+
 char *strtime (time_t *t)
 {
   struct tm   *local;
   static char result[32];
 
   local = localtime(t);
-  sprintf(result,"%2d/%02d/%4d %02d:%02d:%02d",
-         local->tm_mday, local->tm_mon+1, local->tm_year+1900,
-         local->tm_hour, local->tm_min,   local->tm_sec);
+  sprintf(result,"%4d/%02d/%02d %02d:%02d:%02d",
+          local->tm_year+1900, local->tm_mon+1, local->tm_mday,
+          local->tm_hour, local->tm_min, local->tm_sec);
   return result;
 }
 
 
-/* regular expression matching */
+/* set file time */
+
+int setfiletime (char *fname,time_t ftime)
+{
+#ifdef WIN32
+  static int isWinNT = -1;
+  SYSTEMTIME st;
+  FILETIME locft, modft;
+  struct tm *loctm;
+  HANDLE hFile;
+  int result;
+
+  loctm = localtime(&ftime);
+  if (loctm == NULL)
+    return -1;
+
+  st.wYear         = (WORD)loctm->tm_year + 1900;
+  st.wMonth        = (WORD)loctm->tm_mon + 1;
+  st.wDayOfWeek    = (WORD)loctm->tm_wday;
+  st.wDay          = (WORD)loctm->tm_mday;
+  st.wHour         = (WORD)loctm->tm_hour;
+  st.wMinute       = (WORD)loctm->tm_min;
+  st.wSecond       = (WORD)loctm->tm_sec;
+  st.wMilliseconds = 0;
+  if (!SystemTimeToFileTime(&st, &locft) ||
+      !LocalFileTimeToFileTime(&locft, &modft))
+    return -1;
+
+  if (isWinNT < 0)
+    isWinNT = (GetVersion() < 0x80000000) ? 1 : 0;
+  hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+                     (isWinNT ? FILE_FLAG_BACKUP_SEMANTICS : 0),
+                     NULL);
+  if (hFile == INVALID_HANDLE_VALUE)
+    return -1;
+  result = SetFileTime(hFile, NULL, NULL, &modft) ? 0 : -1;
+  CloseHandle(hFile);
+  return result;
+#else
+  struct utimbuf settime;
+
+  settime.actime = settime.modtime = ftime;
+  return utime(fname,&settime);
+#endif
+}
+
+
+/* push file attributes */
+
+void push_attr(struct attr_item **list,char *fname,int mode,time_t time)
+{
+  struct attr_item *item;
+
+  item = (struct attr_item *)malloc(sizeof(struct attr_item));
+  if (item == NULL)
+    error("Out of memory");
+  item->fname = strdup(fname);
+  item->mode  = mode;
+  item->time  = time;
+  item->next  = *list;
+  *list       = item;
+}
+
+
+/* restore file attributes */
+
+void restore_attr(struct attr_item **list)
+{
+  struct attr_item *item, *prev;
+
+  for (item = *list; item != NULL; )
+    {
+      setfiletime(item->fname,item->time);
+      chmod(item->fname,item->mode);
+      prev = item;
+      item = item->next;
+      free(prev);
+    }
+  *list = NULL;
+}
+
+
+/* match regular expression */
 
 #define ISSPECIAL(c) (((c) == '*') || ((c) == '/'))
 
-int ExprMatch(char *string,char *expr)
+int ExprMatch (char *string,char *expr)
 {
   while (1)
     {
       if (ISSPECIAL(*expr))
-       {
-         if (*expr == '/')
-           {
-             if (*string != '\\' && *string != '/')
-               return 0;
-             string ++; expr++;
-           }
-         else if (*expr == '*')
-           {
-             if (*expr ++ == 0)
-               return 1;
-             while (*++string != *expr)
-               if (*string == 0)
-                 return 0;
-           }
-       }
+        {
+          if (*expr == '/')
+            {
+              if (*string != '\\' && *string != '/')
+                return 0;
+              string ++; expr++;
+            }
+          else if (*expr == '*')
+            {
+              if (*expr ++ == 0)
+                return 1;
+              while (*++string != *expr)
+                if (*string == 0)
+                  return 0;
+            }
+        }
       else
-       {
-         if (*string != *expr)
-           return 0;
-         if (*expr++ == 0)
-           return 1;
-         string++;
-       }
+        {
+          if (*string != *expr)
+            return 0;
+          if (*expr++ == 0)
+            return 1;
+          string++;
+        }
     }
 }
 
-/* recursive make directory */
-/* abort if you get an ENOENT errno somewhere in the middle */
-/* e.g. ignore error "mkdir on existing directory" */
-/* */
+
+/* recursive mkdir */
+/* abort on ENOENT; ignore other errors like "directory already exists" */
 /* return 1 if OK */
 /*        0 on error */
 
@@ -216,7 +330,7 @@ int makedir (char *newdir)
   char *buffer = strdup(newdir);
   char *p;
   int  len = strlen(buffer);
-  
+
   if (len <= 0) {
     free(buffer);
     return 0;
@@ -224,7 +338,7 @@ int makedir (char *newdir)
   if (buffer[len-1] == '/') {
     buffer[len-1] = '\0';
   }
-  if (mkdir(buffer, 0775) == 0)
+  if (mkdir(buffer, 0755) == 0)
     {
       free(buffer);
       return 1;
@@ -234,28 +348,29 @@ int makedir (char *newdir)
   while (1)
     {
       char hold;
-      
+
       while(*p && *p != '\\' && *p != '/')
-       p++;
+        p++;
       hold = *p;
       *p = 0;
-      if ((mkdir(buffer, 0775) == -1) && (errno == ENOENT))
-       {
-         fprintf(stderr,"%s: couldn't create directory %s\n",prog,buffer);
-         free(buffer);
-         return 0;
-       }
+      if ((mkdir(buffer, 0755) == -1) && (errno == ENOENT))
+        {
+          fprintf(stderr,"%s: Couldn't create directory %s\n",prog,buffer);
+          free(buffer);
+          return 0;
+        }
       if (hold == 0)
-       break;
+        break;
       *p++ = hold;
     }
   free(buffer);
   return 1;
 }
 
+
 int matchname (int arg,int argc,char **argv,char *fname)
 {
-  if (arg == argc)             /* no arguments given (untgz tgzarchive) */
+  if (arg == argc)      /* no arguments given (untgz tgzarchive) */
     return 1;
 
   while (arg < argc)
@@ -266,7 +381,7 @@ int matchname (int arg,int argc,char **argv,char *fname)
 }
 
 
-/* Tar file list or extract */
+/* tar file list or extract */
 
 int tar (gzFile in,int action,int arg,int argc,char **argv)
 {
@@ -277,145 +392,183 @@ int tar (gzFile in,int action,int arg,int argc,char **argv)
   int    remaining = 0;
   FILE   *outfile = NULL;
   char   fname[BLOCKSIZE];
+  int    tarmode;
   time_t tartime;
-  
+  struct attr_item *attributes = NULL;
+
   if (action == TGZ_LIST)
-    printf("     day      time     size                       file\n"
-          " ---------- -------- --------- -------------------------------------\n");
+    printf("    date      time     size                       file\n"
+           " ---------- -------- --------- -------------------------------------\n");
   while (1)
     {
       len = gzread(in, &buffer, BLOCKSIZE);
       if (len < 0)
-       error (gzerror(in, &err));
+        error(gzerror(in, &err));
       /*
        * Always expect complete blocks to process
        * the tar information.
        */
       if (len != BLOCKSIZE)
-       error("gzread: incomplete block read");
-      
+        {
+          action = TGZ_INVALID; /* force error exit */
+          remaining = 0;        /* force I/O cleanup */
+        }
+
       /*
        * If we have to get a tar header
        */
-      if (getheader == 1)
-       {
-         /*
-          * if we met the end of the tar
-          * or the end-of-tar block,
-          * we are done
-          */
-         if ((len == 0)  || (buffer.header.name[0]== 0)) break;
-
-         tartime = (time_t)getoct(buffer.header.mtime,12);
-         strcpy(fname,buffer.header.name);
-         
-         switch (buffer.header.typeflag)
-           {
-           case DIRTYPE:
-             if (action == TGZ_LIST)
-               printf(" %s     <dir> %s\n",strtime(&tartime),fname);
-             if (action == TGZ_EXTRACT)
-               makedir(fname);
-             break;
-           case REGTYPE:
-           case AREGTYPE:
-             remaining = getoct(buffer.header.size,12);
-             if (action == TGZ_LIST)
-               printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
-             if (action == TGZ_EXTRACT)
-               {
-                 if ((remaining) && (matchname(arg,argc,argv,fname)))
-                   {
-                     outfile = fopen(fname,"wb");
-                     if (outfile == NULL) {
-                       /* try creating directory */
-                       char *p = strrchr(fname, '/');
-                       if (p != NULL) {
-                         *p = '\0';
-                         makedir(fname);
-                         *p = '/';
-                         outfile = fopen(fname,"wb");
-                       }
-                     }
-                     fprintf(stderr,
-                             "%s %s\n",
-                             (outfile) ? "Extracting" : "Couldn't create",
-                             fname);
-                   }
-                 else
-                   outfile = NULL;
-               }
-             /*
-              * could have no contents
-              */
-             getheader = (remaining) ? 0 : 1;
-             break;
-           default:
-             if (action == TGZ_LIST)
-               printf(" %s     <---> %s\n",strtime(&tartime),fname);
-             break;
-           }
-       }
+      if (getheader >= 1)
+        {
+          /*
+           * if we met the end of the tar
+           * or the end-of-tar block,
+           * we are done
+           */
+          if (len == 0 || buffer.header.name[0] == 0)
+            break;
+
+          tarmode = getoct(buffer.header.mode,8);
+          tartime = (time_t)getoct(buffer.header.mtime,12);
+          if (tarmode == -1 || tartime == (time_t)-1)
+            {
+              buffer.header.name[0] = 0;
+              action = TGZ_INVALID;
+            }
+
+          if (getheader == 1)
+            {
+              strncpy(fname,buffer.header.name,SHORTNAMESIZE);
+              if (fname[SHORTNAMESIZE-1] != 0)
+                  fname[SHORTNAMESIZE] = 0;
+            }
+          else
+            {
+              /*
+               * The file name is longer than SHORTNAMESIZE
+               */
+              if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1) != 0)
+                  error("bad long name");
+              getheader = 1;
+            }
+
+          /*
+           * Act according to the type flag
+           */
+          switch (buffer.header.typeflag)
+            {
+            case DIRTYPE:
+              if (action == TGZ_LIST)
+                printf(" %s     <dir> %s\n",strtime(&tartime),fname);
+              if (action == TGZ_EXTRACT)
+                {
+                  makedir(fname);
+                  push_attr(&attributes,fname,tarmode,tartime);
+                }
+              break;
+            case REGTYPE:
+            case AREGTYPE:
+              remaining = getoct(buffer.header.size,12);
+              if (remaining == -1)
+                {
+                  action = TGZ_INVALID;
+                  break;
+                }
+              if (action == TGZ_LIST)
+                printf(" %s %9d %s\n",strtime(&tartime),remaining,fname);
+              else if (action == TGZ_EXTRACT)
+                {
+                  if (matchname(arg,argc,argv,fname))
+                    {
+                      outfile = fopen(fname,"wb");
+                      if (outfile == NULL) {
+                        /* try creating directory */
+                        char *p = strrchr(fname, '/');
+                        if (p != NULL) {
+                          *p = '\0';
+                          makedir(fname);
+                          *p = '/';
+                          outfile = fopen(fname,"wb");
+                        }
+                      }
+                      if (outfile != NULL)
+                        printf("Extracting %s\n",fname);
+                      else
+                        fprintf(stderr, "%s: Couldn't create %s",prog,fname);
+                    }
+                  else
+                    outfile = NULL;
+                }
+              getheader = 0;
+              break;
+            case GNUTYPE_LONGLINK:
+            case GNUTYPE_LONGNAME:
+              remaining = getoct(buffer.header.size,12);
+              if (remaining < 0 || remaining >= BLOCKSIZE)
+                {
+                  action = TGZ_INVALID;
+                  break;
+                }
+              len = gzread(in, fname, BLOCKSIZE);
+              if (len < 0)
+                error(gzerror(in, &err));
+              if (fname[BLOCKSIZE-1] != 0 || (int)strlen(fname) > remaining)
+                {
+                  action = TGZ_INVALID;
+                  break;
+                }
+              getheader = 2;
+              break;
+            default:
+              if (action == TGZ_LIST)
+                printf(" %s     <---> %s\n",strtime(&tartime),fname);
+              break;
+            }
+        }
       else
-       {
-         unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
-
-         if ((action == TGZ_EXTRACT) && (outfile != NULL))
-           {
-             if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
-               {
-                 fprintf(stderr,"%s : error writing %s skipping...\n",prog,fname);
-                 fclose(outfile);
-                 unlink(fname);
-               }
-           }
-         remaining -= bytes;
-         if (remaining == 0)
-           {
-             getheader = 1;
-             if ((action == TGZ_EXTRACT) && (outfile != NULL))
-               {
-#ifdef WIN32
-                 HANDLE hFile;
-                 FILETIME ftm,ftLocal;
-                 SYSTEMTIME st;
-                 struct tm localt;
-                 fclose(outfile);
-
-                 localt = *localtime(&tartime);
-
-                 hFile = CreateFile(fname, GENERIC_READ | GENERIC_WRITE,
-                                    0, NULL, OPEN_EXISTING, 0, NULL);
-                 
-                 st.wYear = (WORD)localt.tm_year+1900;
-                 st.wMonth = (WORD)localt.tm_mon;
-                 st.wDayOfWeek = (WORD)localt.tm_wday;
-                 st.wDay = (WORD)localt.tm_mday;
-                 st.wHour = (WORD)localt.tm_hour;
-                 st.wMinute = (WORD)localt.tm_min;
-                 st.wSecond = (WORD)localt.tm_sec;
-                 st.wMilliseconds = 0;
-                 SystemTimeToFileTime(&st,&ftLocal);
-                 LocalFileTimeToFileTime(&ftLocal,&ftm);
-                 SetFileTime(hFile,&ftm,NULL,&ftm);
-                 CloseHandle(hFile);
-
-                 outfile = NULL;
-#else
-                 struct utimbuf settime;
+        {
+          unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining;
+
+          if (outfile != NULL)
+            {
+              if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes)
+                {
+                  fprintf(stderr,
+                    "%s: Error writing %s -- skipping\n",prog,fname);
+                  fclose(outfile);
+                  outfile = NULL;
+                  remove(fname);
+                }
+            }
+          remaining -= bytes;
+        }
+
+      if (remaining == 0)
+        {
+          getheader = 1;
+          if (outfile != NULL)
+            {
+              fclose(outfile);
+              outfile = NULL;
+              if (action != TGZ_INVALID)
+                push_attr(&attributes,fname,tarmode,tartime);
+            }
+        }
 
-                 settime.actime = settime.modtime = tartime;
-
-                 fclose(outfile);
-                 outfile = NULL;
-                 utime(fname,&settime);
-#endif
-               }
-           }
-       }
+      /*
+       * Abandon if errors are found
+       */
+      if (action == TGZ_INVALID)
+        {
+          error("broken archive");
+          break;
+        }
     }
-  
+
+  /*
+   * Restore file modes and time stamps
+   */
+  restore_attr(&attributes);
+
   if (gzclose(in) != Z_OK)
     error("failed gzclose");
 
@@ -423,73 +576,74 @@ int tar (gzFile in,int action,int arg,int argc,char **argv)
 }
 
 
-/* =========================================================== */
+/* ============================================================ */
 
 void help(int exitval)
 {
-  fprintf(stderr,
-         "untgz v 0.1\n"
-         " an sample application of zlib 1.0.4\n\n"
-          "Usage : untgz TGZfile            to extract all files\n"
-          "        untgz TGZfile fname ...  to extract selected files\n"
-          "        untgz -l TGZfile         to list archive contents\n"
-          "        untgz -h                 to display this help\n\n");
+  printf("untgz version 0.2.1\n"
+         "  using zlib version %s\n\n",
+         zlibVersion());
+  printf("Usage: untgz file.tgz            extract all files\n"
+         "       untgz file.tgz fname ...  extract selected files\n"
+         "       untgz -l file.tgz         list archive contents\n"
+         "       untgz -h                  display this help\n");
   exit(exitval);
 }
 
 void error(const char *msg)
 {
-    fprintf(stderr, "%s: %s\n", prog, msg);
-    exit(1);
+  fprintf(stderr, "%s: %s\n", prog, msg);
+  exit(1);
 }
 
 
-/* ====================================================================== */
+/* ============================================================ */
 
-int _CRT_glob = 0;     /* disable globbing of the arguments */
+#if defined(WIN32) && defined(__GNUC__)
+int _CRT_glob = 0;      /* disable argument globbing in MinGW */
+#endif
 
 int main(int argc,char **argv)
 {
-    int        action = TGZ_EXTRACT;
-    int        arg = 1;
-    char       *TGZfile;
-    gzFile     *f;
-    
+    int         action = TGZ_EXTRACT;
+    int         arg = 1;
+    char        *TGZfile;
+    gzFile      *f;
 
     prog = strrchr(argv[0],'\\');
     if (prog == NULL)
       {
-       prog = strrchr(argv[0],'/');
-       if (prog == NULL)
-         {
-           prog = strrchr(argv[0],':');
-           if (prog == NULL)
-             prog = argv[0];
-           else
-             prog++;
-         }
-       else
-         prog++;
+        prog = strrchr(argv[0],'/');
+        if (prog == NULL)
+          {
+            prog = strrchr(argv[0],':');
+            if (prog == NULL)
+              prog = argv[0];
+            else
+              prog++;
+          }
+        else
+          prog++;
       }
     else
       prog++;
-    
+
     if (argc == 1)
       help(0);
 
     if (strcmp(argv[arg],"-l") == 0)
       {
-       action = TGZ_LIST;
-       if (argc == ++arg)
-         help(0);
+        action = TGZ_LIST;
+        if (argc == ++arg)
+          help(0);
       }
     else if (strcmp(argv[arg],"-h") == 0)
       {
-       help(0);
+        help(0);
       }
 
     if ((TGZfile = TGZfname(argv[arg])) == NULL)
-      TGZnotfound(argv[arg]);            
+      TGZnotfound(argv[arg]);
 
     ++arg;
     if ((action == TGZ_LIST) && (arg != argc))
@@ -502,20 +656,18 @@ int main(int argc,char **argv)
       {
       case TGZ_LIST:
       case TGZ_EXTRACT:
-       f = gzopen(TGZfile,"rb");
-       if (f == NULL)
-         {
-           fprintf(stderr,"%s: Couldn't gzopen %s\n",
-                   prog,
-                   TGZfile);
-           return 1;
-         }
-       exit(tar(f, action, arg, argc, argv));
+        f = gzopen(TGZfile,"rb");
+        if (f == NULL)
+          {
+            fprintf(stderr,"%s: Couldn't gzopen %s\n",prog,TGZfile);
+            return 1;
+          }
+        exit(tar(f, action, arg, argc, argv));
       break;
-       
+
       default:
-       error("Unknown option!");
-       exit(1);
+        error("Unknown option");
+        exit(1);
       }
 
     return 0;