home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / alde_c / misc / comm / detar / detar.c next >
Encoding:
C/C++ Source or Header  |  1988-12-29  |  9.9 KB  |  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    cv_time(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:
  162.         case LF_LINK:
  163.             modes[0] = '-'; 
  164.             if ('/' == head->header.name[strlen(head->header.name)-1])
  165.                 modes[0] = 'd';
  166.             break;
  167.         case LF_DIR:
  168.             modes[0] = 'd';
  169.             break;
  170.         case LF_SYMLINK:
  171.             modes[0] = 'l';
  172.             break;
  173.         case LF_BLK:
  174.             modes[0] = 'b';
  175.             break;
  176.         case LF_CHR:
  177.             modes[0] = 'c';
  178.             break;
  179.         case LF_FIFO:
  180.             modes[0] = 'f';
  181.             break;
  182.         case LF_CONTIG:
  183.             modes[0] = '=';
  184.             break;
  185.         }
  186.  
  187.         demode(hstat.st_mode, modes+1);
  188.  
  189.         /* Timestamp */
  190.  
  191.         longie = hstat.st_mtime;
  192.         timestamp = ctime(&longie);
  193.         timestamp[16] = '\0';
  194.         timestamp[24] = '\0';
  195.  
  196.         /* User and group names */
  197.  
  198.         if (*head->header.uname && header_std) {
  199.             user  = head->header.uname;
  200.         } else {
  201.             user = uform;
  202.             sprintf(uform, "%d", hstat.st_uid);
  203.         }
  204.  
  205.         if (*head->header.gname && header_std) {
  206.             group = head->header.gname;
  207.         } else {
  208.             group = gform;
  209.             sprintf(gform, "%d", hstat.st_gid);
  210.         }
  211.  
  212.         /* Format the file size or major/minor device numbers */
  213.  
  214.         switch (head->header.linkflag) {
  215.         case LF_CHR:
  216.         case LF_BLK:
  217.             sprintf(size, "%d, %d",
  218.                (int)from_oct(8, head->header.devmajor),
  219.                (int)from_oct(8, head->header.devminor));
  220.             break;
  221.         default:
  222.             sprintf(size, "%ld", hstat.st_size);
  223.         }
  224.  
  225.         /* Figure out padding and print the whole line. */
  226.  
  227.         pad = strlen(user) + strlen(group) + strlen(size) + 1;
  228.  
  229.         if (pad > ugswidth)
  230.             ugswidth = pad;
  231.  
  232.         printf("%s %s/%s %*s%s %s %s %.*s",
  233.             modes,
  234.             user,
  235.             group,
  236.             ugswidth - pad,
  237.             "",
  238.             size,
  239.             timestamp+4, timestamp+20,
  240.             sizeof(head->header.name),
  241.             head->header.name);
  242.  
  243.         switch (head->header.linkflag) {
  244.         case LF_SYMLINK:
  245.             printf(" -> %s\n", head->header.linkname);
  246.             break;
  247.  
  248.         case LF_LINK:
  249.             printf(" link to %s\n", head->header.linkname);
  250.             break;
  251.  
  252.         default:
  253.             printf(" unknown file type '%c'\n", head->header.linkflag);
  254.             break;
  255.  
  256.         case LF_OLDNORMAL:
  257.         case LF_NORMAL:
  258.         case LF_CHR:
  259.         case LF_BLK:
  260.         case LF_DIR:
  261.         case LF_FIFO:
  262.         case LF_CONTIG:
  263.             putc('\n', stdout);
  264.             break;
  265.         }
  266.  
  267.         /*
  268.          *    Seek to next file
  269.          */
  270.  
  271.         fseek(fdi, hstat.st_size, 1);
  272.  
  273.         /*
  274.          *    File starts on 512 byte boundary
  275.          */
  276.  
  277.         fseek(fdi, 512L - (hstat.st_size % 512L), 1);
  278.     }
  279. }
  280.  
  281.  
  282. /*
  283.  *    Extract the files from the TAR archive
  284.  *
  285.  */
  286.  
  287. void de_tar()
  288. {
  289.     long    size;
  290.     int    header_std, c;
  291.     char    *workfile;
  292.  
  293.     for (;;)  {
  294.         if ( fread((char *)head, 1, 512, fdi) == EOF)  {
  295.             fclose(fdi);
  296.             return;
  297.         }
  298.  
  299.         decode_header(head, &hstat, &header_std);
  300.         workfile = convert(head->header.name);
  301.  
  302.         printf("Extracting: %s\n", workfile);
  303.  
  304.         fdo = fopen(workfile, "w");
  305.         size = hstat.st_size;
  306.  
  307.         while ((c = getc(fdi)) != EOF)  {
  308.  
  309.             /*
  310.              *    Convert Linefeed to MSDOS
  311.              *    Carriage Return, Linefeed pair
  312.              *
  313.              */
  314.  
  315.             if (c == 0x0A)
  316.                 putc('\n', fdo);
  317.             else
  318.                 putc(c, fdo);
  319.  
  320.             if (--size == 0L)  {
  321.  
  322.                /*
  323.                 *    Get to next 512 byte boundary
  324.                 */
  325.  
  326.                fseek(fdi, 512L - (hstat.st_size % 512L), 1);
  327.                break;
  328.             }
  329.         }
  330.  
  331.         fclose(fdo);
  332.     }
  333. }
  334.  
  335.  
  336. /* 
  337.  *    Break down the header info into stat info
  338.  *
  339.  *    Set "*stdp" to != 0 or == 0 depending whether
  340.  *    header record is "Unix Standard" or "old" tar format.
  341.  *
  342.  */
  343.  
  344. void decode_header(header, st, stdp)
  345. register union record    *header;
  346. register struct stat    *st;
  347. register int        *stdp;
  348. {
  349.     st->st_mode  = (int)from_oct( 8, header->header.mode);
  350.     st->st_mtime = from_oct(12, header->header.mtime);
  351.     st->st_size  = from_oct(12, header->header.size);
  352.     
  353.     if (0 == strcmp(header->header.magic, TMAGIC)) {
  354.  
  355.         /* Unix Standard tar archive */
  356.  
  357.         *stdp = 1;
  358.         st->st_dev = 0;
  359.  
  360.     } else {
  361.  
  362.         /* Old fashioned tar archive */
  363.  
  364.         *stdp = 0;
  365.         st->st_uid = (int)from_oct(8, header->header.uid);
  366.         st->st_gid = (int)from_oct(8, header->header.gid);
  367.         st->st_dev = 0;
  368.     }
  369. }
  370.  
  371.  
  372. /*
  373.  *    Decode the mode string from a stat entry into a 9-char string
  374.  */
  375.  
  376. void demode(mode, string)
  377. register unsigned mode;
  378. register char      *string;
  379. {
  380.     register unsigned mask;
  381.     register char      *rwx = "rwxrwxrwx";
  382.  
  383.     for (mask = 0400; mask != 0; mask >>= 1) {
  384.         if (mode & mask)
  385.             *string++ = *rwx++;
  386.         else {
  387.             *string++ = '-';
  388.             rwx++;
  389.         }
  390.     }
  391.  
  392.     if (mode & S_ISUID)
  393.         if (string[-7] == 'x')
  394.             string[-7] = 's';
  395.         else
  396.             string[-7] = 'S';
  397.  
  398.     if (mode & S_ISGID)
  399.         if (string[-4] == 'x')
  400.             string[-4] = 's';
  401.         else
  402.             string[-4] = 'S';
  403.  
  404.     if (mode & S_ISVTX)
  405.         if (string[-1] == 'x')
  406.             string[-1] = 't';
  407.         else
  408.             string[-1] = 'T';
  409.  
  410.     *string = '\0';
  411. }
  412.  
  413.  
  414. /*
  415.  *    Quick and dirty octal conversion.
  416.  *
  417.  *    Result is -1 if the field is invalid (all blank, or nonoctal).
  418.  */
  419.  
  420. long from_oct(digs, where)
  421. register int    digs;
  422. register char    *where;
  423. {
  424.     register long    value;
  425.  
  426.     while (isspace(*where)) {        /* Skip spaces */
  427.         where++;
  428.         if (--digs <= 0)
  429.             return -1;        /* All blank field */
  430.     }
  431.  
  432.     value = 0;
  433.     while (digs > 0 && isodigit(*where)) {    /* Scan til nonoctal */
  434.         value = (value << 3) | (*where++ - '0');
  435.         --digs;
  436.     }
  437.  
  438.     if (digs > 0 && *where && !isspace(*where))
  439.         return -1;            /* Ended on non-space/nul */
  440.  
  441.     return value;
  442. }
  443.  
  444.  
  445. /*
  446.  *    Borrowed from comp.os.minix
  447.  */
  448.  
  449. char *ctime(time) 
  450. long    *time;
  451. {
  452.     long    tt;
  453.  
  454.     s_p_min  = 60L;
  455.     s_p_hour = 60L * 60L;
  456.     s_p_day  = 60L * 60L * 24L;
  457.     s_p_year = 60L * 60L * 24L * 365L;
  458.  
  459.     tt = *time;
  460.     cv_time(tt);
  461.  
  462.     sprintf(t_buf, "%s %s %02d %02d:%02d:%02d %04d\n",
  463.        days[(tt / s_p_day) % 7], months[tm.month], tm.day, tm.hour,
  464.          tm.min, tm.sec, tm.year); 
  465.  
  466.     return(t_buf);
  467. }
  468.  
  469.  
  470. void cv_time(time)
  471. long    time;
  472. {
  473.     tm.year = 0;
  474.     tm.month = 0;
  475.     tm.day = 1;
  476.     tm.hour = 0;
  477.     tm.min = 0;
  478.     tm.sec = 0;
  479.  
  480.     while (time >= s_p_year)  {
  481.         if (((tm.year + 2) % 4) == 0)
  482.             time -= s_p_day;
  483.  
  484.         tm.year += 1;
  485.         time -= s_p_year;
  486.     }
  487.  
  488.     if (((tm.year + 2) % 4) == 0)
  489.         days_per_month[1]++;
  490.  
  491.     tm.year += 1970;
  492.  
  493.     while ( time >= (days_per_month[tm.month] * s_p_day))
  494.         time -= days_per_month[tm.month++] * s_p_day;
  495.  
  496.     while (time >= s_p_day) {
  497.         time -= s_p_day;
  498.         tm.day++;
  499.     }
  500.  
  501.     while (time >= s_p_hour) {
  502.         time -= s_p_hour;
  503.         tm.hour++;
  504.     }
  505.  
  506.     while (time >= s_p_min) {
  507.         time -= s_p_min;
  508.         tm.min++;
  509.     }
  510.  
  511.     tm.sec = (int)time;
  512. }
  513.  
  514. char *convert(unixname)
  515. register char *unixname;
  516. {
  517.     static   char    temp[16];
  518.     register char    *p;
  519.     register int    have_dot;
  520.  
  521.     /* First get rid of directory specs */
  522.  
  523.     if ((p = strrchr(unixname, '/')) != (char *)NULL)
  524.         strcpy(temp, p + 1);
  525.     else
  526.         strcpy(temp, unixname);
  527.  
  528.     /* Then make a basic MSDOS filename */
  529.  
  530.     if (p = strchr(temp, '.'))  {
  531.         if (strlen(p) > 4)
  532.             p[4] = '\0';
  533.  
  534.         if ((p - temp) > 8)  {
  535.             temp[8] = '\0';
  536.             strcat(temp, p);
  537.         }
  538.     }
  539.     else
  540.         temp[8] = '\0';
  541.  
  542.     /* Now look for two dots in filename, only one allowed in MSDOS */
  543.     /* substitute a dash for second dot */
  544.  
  545.     for (have_dot = 0, p = temp; *p; p++)  {
  546.         if (*p == '.')  {
  547.             if (have_dot)
  548.                 *p = '-';
  549.             else
  550.                 have_dot = 1;
  551.         }
  552.     }
  553.  
  554.     return temp;
  555. }
  556.  
  557. /* EOF */
  558.