home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 September / Simtel20_Sept92.cdr / msdos / filutl / detar11.arc / DETAR.C next >
Text File  |  1988-01-23  |  10KB  |  555 lines

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