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