home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Amiga / Workbench / Archivers / DeTar.lha / DeTar / DeTar.c < prev    next >
C/C++ Source or Header  |  1998-02-15  |  11KB  |  518 lines

  1. /*
  2. **      $VER: DeTar.c 1.5 (15.2.98)
  3. **
  4. **      DeTar main module
  5. **
  6. **      written in 1995-98 by Andreas R. Kleinert, based on code by Steve Sampson
  7. **      Public Domain. (See DeTar.doc)
  8. */
  9.  
  10. /* ------------------------------------------------------------------------------ */
  11.  
  12. /*
  13.  *      detar.c
  14.  *
  15.  *      Version 1.1
  16.  *
  17.  *      Fixed bug in convert() wich produced errors on files without
  18.  *      a slash '/' in them.
  19.  *
  20.  *      Unix Tape Archive (TAR) Extractor for MSDOS
  21.  *
  22.  *      Public Domain January 1988, Steve Sampson
  23.  *      Based on the public domain TAR program by John Gilmore
  24.  *
  25.  *      Compiled with ECO-C88 V3.21
  26.  *
  27.  */
  28.  
  29. /* Includes */
  30.  
  31. #define __USE_SYSBASE
  32.  
  33. #include <exec/types.h>
  34.  
  35. #include <stdio.h>
  36. #include <string.h>
  37. #include <stdlib.h>
  38. #include <ctype.h>
  39. #include <time.h>
  40.  
  41. #include <proto/exec.h>
  42. #include <proto/dos.h>
  43.  
  44. #include "detar.h"
  45.  
  46. /* Defines */
  47.  
  48. #define isodigit(c)     ( ((c) >= '0') && ((c) <= '7') )
  49.  
  50. /* Globals */
  51.  
  52. FILE    *fdi,
  53.         *fdo;
  54.  
  55. union   record  *head;          /* the archive file header */
  56. struct  stat    hstat;          /* the decoded stat info   */
  57. struct  {
  58.         long    year;
  59.         long    month;
  60.         long    day;
  61.         long    hour;
  62.         long    min;
  63.         long    sec;
  64. } tm;
  65.  
  66. long    ugswidth = 11;
  67. long     dir_only = 0;
  68.  
  69. char    tempname[256];
  70.  
  71. char version [] = "\0$VER: detar 1.5 (15.2.98)";
  72.  
  73.  
  74. /* Forward References, Prototypes */
  75.  
  76. void dir_tar(void);
  77. void de_tar(void);
  78. void decode_header(union record *header, struct stat *st, long *stdp);
  79. void demode(unsigned mode, char *string);
  80. long from_oct(long digs, char *where);
  81.  
  82.  
  83. /* Main Program */
  84.  
  85. void main(long argc, char **argv)
  86. {
  87.  char *p;
  88.  long retval = 0;
  89.  long add    = FALSE, valid = FALSE;
  90.  
  91.  
  92.  if(!argc) exit(20);
  93.  
  94.  
  95.  /* The operator can ask for directory only or de-tar only. */
  96.  
  97.  argv++;
  98.  
  99.  if(argc == 3 && **argv == 'd')
  100.   {
  101.    dir_only++;
  102.    argc--;
  103.    argv++;
  104.   }
  105.  
  106.  if (argc != 2)
  107.   {
  108.    fprintf(stderr, &version[7]);
  109.  
  110.    fprintf(stderr, "\nAmiga Port by Andreas R. Kleinert in 1995-98. Public Domain."
  111.                    "\nPublic Domain January 1988, Steve Sampson"
  112.                    "\nBased on the public domain TAR program by John Gilmore");
  113.  
  114.    fprintf(stderr, "\nUsage: detar [d] filename[.tar]");
  115.    fprintf(stderr, "\nWhere d means 'list content only'\n");
  116.  
  117.    exit(1);
  118.   }
  119.  
  120.  for (p = *argv; *p; p++) *p = (char)toupper(*p);
  121.  
  122.  
  123.  /* parse the many possibilities, this stuff not needed with MSC */
  124.  
  125.        if ((p = strrchr(*argv, '.')) == (char *)NULL) { add =                    valid = TRUE; }
  126.   else if (strcmp(p, ".TAR") == NULL)                 { strcpy(tempname, *argv); valid = TRUE; }
  127.   else if (*(p+1) == '/' || *(p+1) == '\\')           { add = valid = TRUE;      valid = TRUE; }
  128.  
  129.  if(valid)
  130.   {
  131.    if(add)
  132.     {
  133.      strcpy(tempname, *argv);
  134.      strcat(tempname, ".TAR");
  135.     }
  136.  
  137.    fdi = fopen(tempname, "rb");
  138.    if(fdi)
  139.     {
  140.      setvbuf(fdi, NULL, _IOFBF, 8192);
  141.  
  142.      head = (union record *)malloc(512);
  143.      if(head)
  144.       {
  145.        if (dir_only) dir_tar();
  146.         else         de_tar();
  147.  
  148.        free(head);
  149.  
  150.       }else
  151.       {
  152.        fprintf(stderr, "Not enough memory !\n");
  153.        retval = 20;
  154.       }
  155.  
  156.      fclose(fdi);
  157.  
  158.     }else
  159.     {
  160.      fprintf(stderr, "Tar file '%s' not found\n", tempname);
  161.      retval = 20;
  162.     }
  163.  
  164.   }else
  165.   {
  166.    fprintf(stderr, "File '%s' not a TAR archive\n", *argv);
  167.    retval = 20;
  168.   }
  169.  
  170.  exit(retval);
  171. }
  172.  
  173.  
  174. /*
  175.  *      Produce a directory of the files contained on the TAR
  176.  */
  177.  
  178. void dir_tar(void)
  179. {
  180.  char   modes[11], *timestamp;
  181.  char   uform[11], gform[11];
  182.  char   *user, *group, size[24];
  183.  long   n, pad, header_std;
  184.  time_t timeval;
  185.  
  186.  for (;;)
  187.   {
  188.    if ((n = fread((char *)head, 1, 512, fdi)) == EOF)
  189.     {
  190.      return;
  191.     }
  192.  
  193.    decode_header(head, &hstat, &header_std);
  194.  
  195.    /* File type and modes */
  196.  
  197.    modes[0] = '?';
  198.  
  199.    switch (head->header.linkflag)
  200.     {
  201.      case LF_NORMAL:
  202.      case LF_OLDNORMAL:
  203.      case LF_LINK:
  204.       {
  205.        modes[0] = '-';
  206.        if ('/' == head->header.name[strlen(head->header.name)-1])
  207.        modes[0] = 'd';
  208.        break;
  209.       }
  210.      case LF_DIR:
  211.       {
  212.        modes[0] = 'd';
  213.        break;
  214.       }
  215.      case LF_SYMLINK:
  216.       {
  217.        modes[0] = 'l';
  218.        break;
  219.       }
  220.      case LF_BLK:
  221.       {
  222.        modes[0] = 'b';
  223.        break;
  224.       }
  225.      case LF_CHR:
  226.       {
  227.        modes[0] = 'c';
  228.        break;
  229.       }
  230.      case LF_FIFO:
  231.       {
  232.        modes[0] = 'f';
  233.        break;
  234.       }
  235.      case LF_CONTIG:
  236.       {
  237.        modes[0] = '=';
  238.        break;
  239.       }
  240.     }
  241.  
  242.    demode(hstat.st_mode, modes+1);
  243.  
  244.    /* Timestamp */
  245.  
  246.    timeval = hstat.st_mtime;
  247.    timestamp = ctime(&timeval);
  248.    timestamp[16] = '\0';
  249.    timestamp[24] = '\0';
  250.  
  251.    /* User and group names */
  252.  
  253.    if (*head->header.uname && header_std)
  254.     {
  255.      user  = head->header.uname;
  256.     }else
  257.     {
  258.      user = uform;
  259.      sprintf(uform, "%ld", (long)hstat.st_uid);
  260.     }
  261.  
  262.    if (*head->header.gname && header_std)
  263.     {
  264.      group = head->header.gname;
  265.     }else
  266.     {
  267.      group = gform;
  268.      sprintf(gform, "%ld", (long)hstat.st_gid);
  269.     }
  270.  
  271.    /* Format the file size or major/minor device numbers */
  272.  
  273.    switch (head->header.linkflag)
  274.     {
  275.      case LF_CHR:
  276.      case LF_BLK:
  277.       {
  278.        sprintf(size, "%ld, %ld",
  279.        (long)from_oct(8, head->header.devmajor),
  280.        (long)from_oct(8, head->header.devminor));
  281.        break;
  282.       }
  283.      default:
  284.       {
  285.        sprintf(size, "%ld", hstat.st_size);
  286.  
  287.        break;
  288.       }
  289.     }
  290.  
  291.  
  292.    /* Figure out padding and print the whole line. */
  293.  
  294.    pad = strlen(user) + strlen(group) + strlen(size) + 1;
  295.  
  296.    if (pad > ugswidth) ugswidth = pad;
  297.  
  298.     printf("%s %s/%s %*s%s %s %s %.*s",
  299.         modes,
  300.         user,
  301.         group,
  302.         ugswidth - pad,
  303.         "",
  304.         size,
  305.         timestamp+4, timestamp+20,
  306.         sizeof(head->header.name),
  307.         head->header.name);
  308.  
  309.     switch (head->header.linkflag)
  310.      {
  311.       case LF_SYMLINK:
  312.        {
  313.         printf(" -> %s\n", head->header.linkname);
  314.         break;
  315.        }
  316.       case LF_LINK:
  317.        {
  318.         printf(" link to %s\n", head->header.linkname);
  319.         break;
  320.        }
  321.  
  322.       default:
  323.        {
  324.         printf(" unknown file type '%c'\n", head->header.linkflag);
  325.         break;
  326.        }
  327.  
  328.       case LF_OLDNORMAL:
  329.       case LF_NORMAL:
  330.       case LF_CHR:
  331.       case LF_BLK:
  332.       case LF_DIR:
  333.       case LF_FIFO:
  334.       case LF_CONTIG:
  335.        {
  336.         putc('\n', stdout);
  337.         break;
  338.        }
  339.     }
  340.  
  341.    /* Seek to next file */
  342.  
  343.    fseek(fdi, hstat.st_size, SEEK_CUR);
  344.  
  345.    /* File starts on 512 byte boundary */
  346.  
  347.    fseek(fdi, 512L - (hstat.st_size % 512L), SEEK_CUR);
  348.   }
  349. }
  350.  
  351.  
  352. /*
  353.  *      Extract the files from the TAR archive
  354.  *
  355.  */
  356.  
  357. void de_tar(void)
  358. {
  359.         ULONG   size, filesize;
  360.         long    header_std, c, len;
  361.         char    *workfile;
  362.  
  363.         for (;!feof(fdi);)
  364.          {
  365.           if ( fread((char *)head, 1, 512, fdi) == (unsigned int) EOF) break;
  366.  
  367.                 decode_header(head, &hstat, &header_std);
  368.                 workfile = head->header.name;
  369.                 size = filesize = hstat.st_size;
  370.  
  371.                 len = strlen(workfile);
  372.                 if(len > 0)
  373.                  {
  374.                   if(    (workfile[len-1] == '/')
  375.                       || (workfile[len-1] == ':') )
  376.                    {
  377.                     BPTR lock;
  378.  
  379.                     printf("Creating: %s ...", workfile);
  380.  
  381.                     workfile[len-1] = (char) 0;
  382.  
  383.                     lock = CreateDir(workfile);
  384.                     if(lock)
  385.                      {
  386.                       printf(" OK\n");
  387.                       UnLock(lock);
  388.                      }else printf(" Failed\n");
  389.  
  390.                     fseek(fdi, filesize, SEEK_CUR);
  391.  
  392.                     continue;
  393.                    }
  394.  
  395.                   printf("Extracting: %s\n", workfile);
  396.  
  397.                   fdo = fopen(workfile, "wb");
  398.                   if(fdo)
  399.                    {
  400.                     setvbuf(fdo, NULL, _IOFBF, 8192);
  401.  
  402.                     while ((c = getc(fdi)) != EOF)
  403.                      {
  404.                       putc(c, fdo); size--;
  405.  
  406.                       if(!size) /* Get to next 512 byte boundary */
  407.                        {
  408.                         fseek(fdi, 512L - (filesize % 512L), SEEK_CUR);
  409.                         break;
  410.                        }
  411.                      }
  412.                     fclose(fdo);
  413.                    }else printf(" Failed\n");
  414.                  }else printf("Skipping empty entry\n");
  415.         }
  416. }
  417.  
  418.  
  419. /*
  420.  *      Break down the header info into stat info
  421.  *
  422.  *      Set "*stdp" to != 0 or == 0 depending whether
  423.  *      header record is "Unix Standard" or "old" tar format.
  424.  *
  425.  */
  426.  
  427. void decode_header(union record *header, struct stat *st, long *stdp)
  428. {
  429.         st->st_mode  = (long)from_oct( 8, header->header.mode);
  430.         st->st_mtime = from_oct(12, header->header.mtime);
  431.         st->st_size  = from_oct(12, header->header.size);
  432.  
  433.         if (0 == strcmp(header->header.magic, TMAGIC)) {
  434.  
  435.                 /* Unix Standard tar archive */
  436.  
  437.                 *stdp = 1;
  438.                 st->st_dev = 0;
  439.  
  440.         } else {
  441.  
  442.                 /* Old fashioned tar archive */
  443.  
  444.                 *stdp = 0;
  445.                 st->st_uid = (long)from_oct(8, header->header.uid);
  446.                 st->st_gid = (long)from_oct(8, header->header.gid);
  447.                 st->st_dev = 0;
  448.         }
  449. }
  450.  
  451. /*
  452.  *      Decode the mode string from a stat entry into a 9-char string
  453.  */
  454.  
  455. void demode(unsigned mode, char *string)
  456. {
  457.         register unsigned mask;
  458.         register char     *rwx = "rwxrwxrwx";
  459.  
  460.         for (mask = 0400; mask != 0; mask >>= 1) {
  461.                 if (mode & mask)
  462.                         *string++ = *rwx++;
  463.                 else {
  464.                         *string++ = '-';
  465.                         rwx++;
  466.                 }
  467.         }
  468.  
  469.         if (mode & S_ISUID)
  470.                 if (string[-7] == 'x')
  471.                         string[-7] = 's';
  472.                 else
  473.                         string[-7] = 'S';
  474.  
  475.         if (mode & S_ISGID)
  476.                 if (string[-4] == 'x')
  477.                         string[-4] = 's';
  478.                 else
  479.                         string[-4] = 'S';
  480.  
  481.         if (mode & S_ISVTX)
  482.                 if (string[-1] == 'x')
  483.                         string[-1] = 't';
  484.                 else
  485.                         string[-1] = 'T';
  486.  
  487.         *string = '\0';
  488. }
  489.  
  490.  
  491. /*
  492.  *      Quick and dirty octal conversion.
  493.  *
  494.  *      Result is -1 if the field is invalid (all blank, or nonoctal).
  495.  */
  496.  
  497. long from_oct(long digs, char *where)
  498. {
  499.         register long   value;
  500.  
  501.         while (isspace(*where)) {               /* Skip spaces */
  502.                 where++;
  503.                 if (--digs <= 0)
  504.                         return -1;              /* All blank field */
  505.         }
  506.  
  507.         value = 0;
  508.         while (digs > 0 && isodigit(*where)) {  /* Scan til nonoctal */
  509.                 value = (value << 3) | (*where++ - '0');
  510.                 --digs;
  511.         }
  512.  
  513.         if (digs > 0 && *where && !isspace(*where))
  514.                 return -1;                      /* Ended on non-space/nul */
  515.  
  516.         return value;
  517. }
  518.