home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / filutl / pdtar.arc / LIST.C < prev    next >
C/C++ Source or Header  |  1988-05-15  |  13KB  |  577 lines

  1. /*
  2.  * List a tar archive.
  3.  *
  4.  * Also includes support routines for reading a tar archive.
  5.  *
  6.  * Pubic Domain version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
  7.  * MS-DOS port 2/87 by Eric Roskos.
  8.  * Minix  port 3/88 by Eric Roskos.
  9.  *
  10.  * @(#)list.c 1.18 9/23/86 Public Domain - gnu
  11.  */
  12. #include <stdio.h>
  13. #include <ctype.h>
  14. #include <sys/types.h>
  15. #include <sys/stat.h>
  16. #ifndef MSDOS
  17. #ifndef V7
  18. #include <sys/file.h>
  19. #endif /* V7 */
  20. #else                            /* ifdef MSDOS */
  21. #include <fcntl.h>
  22. #endif
  23.  
  24. char           *ctime();        /* From libc.a */
  25.  
  26. #define    isodigit(c)    ( ((c) >= '0') && ((c) <= '7') )
  27.  
  28. #include "tar.h"
  29.  
  30. long            from_oct();        /* Decode octal number */
  31. void            demode();        /* Print file mode */
  32.  
  33. union record   *head;            /* Points to current archive header */
  34. struct stat     hstat[1];        /* Stat struct corresponding */
  35. struct stat    *phstat = hstat;        /* to overcome construct ACK C can't handle */
  36.  
  37. void            print_header();
  38. void            skip_file();
  39.  
  40.  
  41. /*
  42.  * Main loop for reading an archive.
  43.  */
  44. void
  45. read_and(do_something)
  46. void            (*do_something) ();
  47.  
  48. {
  49.     int             status = 1;
  50.     int             prev_status;
  51.     char           *xname;
  52.     char           *fixname();
  53.  
  54.     name_gather();                /* Gather all the names */
  55.     open_archive(1);            /* Open for reading */
  56.  
  57.     for (;;)
  58.     {
  59.         prev_status = status;
  60.         status = read_header();
  61.         switch (status)
  62.         {
  63.  
  64.         case 1:                /* Valid header */
  65.             /* We should decode next field (mode) first... */
  66.             /* Ensure incoming names are null terminated. */
  67.             head->header.name[NAMSIZ - 1] = '\0';
  68.             /* make a valid filename for this OS */
  69.             xname = fixname(head->header.name);
  70.  
  71.             if (!name_match(head->header.name))
  72.             {
  73.                 /* Skip past it in the archive */
  74.                 userec(head);
  75.                 /* Skip to the next header on the archive */
  76.                 if (head->header.linkflag != LF_BLK &&
  77.                     head->header.linkflag != LF_CHR)
  78.                     skip_file((long) phstat->st_size);
  79.                 continue;
  80.             }
  81.  
  82.             (*do_something) (xname);
  83.             continue;
  84.  
  85.             /*
  86.              * If the previous header was good, tell them that we are
  87.              * skipping bad ones. 
  88.              */
  89.         case 0:                /* Invalid header */
  90.     case0:
  91.             userec(head);
  92.             if (prev_status == 1)
  93.             {
  94.                 annorec(stderr, tar);
  95.                 fprintf(stderr,
  96.                     "Skipping to next file header...\n");
  97.             }
  98.             continue;
  99.  
  100.         case 2:                /* Block of zeroes */
  101.             if (f_ignorez)
  102.                 goto case0;        /* Just skip if asked */
  103.             /* FALL THRU */
  104.         case EOF:                /* End of archive */
  105.             break;
  106.         }
  107.         break;
  108.     };
  109.  
  110.     close_archive();
  111.     names_notfound();            /* Print names not found */
  112. }
  113.  
  114.  
  115. /*
  116.  * Print a header record, based on tar options.
  117.  */
  118. void
  119. list_archive(xname)
  120. char           *xname;
  121. {
  122.  
  123.     /* Save the record */
  124.     saverec(&head);
  125.  
  126.     /* Print the header record */
  127.     print_header(xname);
  128.  
  129.     /* Skip past it in the archive */
  130.     saverec((union record **) 0);        /* Unsave it */
  131.     userec(head);
  132.  
  133.     /* Skip to the next header on the archive */
  134.     if (head->header.linkflag != LF_BLK &&
  135.         head->header.linkflag != LF_CHR) /* no skip if special file - JER */
  136.         skip_file((long) phstat->st_size);
  137. }
  138.  
  139.  
  140. /*
  141.  * Read a record that's supposed to be a header record.
  142.  * Return its address in "head", and if it is good, the file's
  143.  * size in hstat->st_size.
  144.  *
  145.  * Return 1 for success, 0 if the checksum is bad, EOF on eof,
  146.  * 2 for a block full of zeros (EOF marker).
  147.  *
  148.  * You must always userec(head) to skip past the header which this
  149.  * routine reads.
  150.  */
  151. int
  152. read_header()
  153. {
  154.     register int    i;
  155.     register long   sum, recsum;
  156.     register char  *p;
  157.     register union record *header;
  158.  
  159.     header = findrec();
  160.     head = header;                /* This is our current header */
  161.     if (NULL == header)
  162.         return EOF;
  163.  
  164.     recsum = from_oct(8, header->header.chksum);
  165.  
  166.     sum = 0;
  167.     p = header->charptr;
  168.     for (i = sizeof(*header); --i >= 0;)
  169.     {
  170.  
  171.         /*
  172.          * We can't use unsigned char here because of old compilers, e.g. V7. 
  173.          */
  174.         sum += 0xFF & *p++;
  175.     }
  176.  
  177.     /* Adjust checksum to count the "chksum" field as blanks. */
  178.     for (i = sizeof(header->header.chksum); --i >= 0;)
  179.         sum -= 0xFF & header->header.chksum[i];
  180.     sum += ' ' * sizeof header->header.chksum;
  181.  
  182.     if (sum == recsum)
  183.     {
  184.  
  185.         /*
  186.          * Good record.  Decode file size and return. 
  187.          */
  188.         if (header->header.linkflag == LF_LINK)
  189.             phstat->st_size = 0;    /* Links 0 size on tape */
  190.         else
  191.             phstat->st_size = from_oct(1 + 12, header->header.size);
  192.         return 1;
  193.     }
  194.  
  195.     if (sum == 8 * ' ')
  196.     {
  197.  
  198.         /*
  199.          * This is a zeroed block...whole block is 0's except for the 8
  200.          * blanks we faked for the checksum field. 
  201.          */
  202.         return 2;
  203.     }
  204.  
  205.     return 0;
  206. }
  207.  
  208.  
  209. /* 
  210.  * Decode things from a file header record into a "struct stat".
  211.  * Also set "*stdp" to !=0 or ==0 depending whether header record is "Unix
  212.  * Standard" tar format or regular old tar format.
  213.  *
  214.  * read_header() has already decoded the checksum and length, so we don't.
  215.  *
  216.  * If wantug != 0, we want the uid/group info decoded from Unix Standard
  217.  * tapes (for extraction).  If == 0, we are just printing anyway, so save time.
  218.  */
  219. decode_header(header, st, stdp, wantug)
  220. register union record *header;
  221. register struct stat *st;
  222. int            *stdp;
  223. int             wantug;
  224. {
  225.     st->st_mode = from_oct(8, header->header.mode);
  226.     st->st_mtime = from_oct(1 + 12, header->header.mtime);
  227.  
  228.     if (0 == strcmp(header->header.magic, TMAGIC))
  229.     {
  230.         /* Unix Standard tar archive */
  231.         *stdp = 1;
  232.         if (wantug)
  233.         {
  234. #ifndef MSDOS
  235. #ifndef NONAMES    /* if we have names, still support noname tapes - JER */
  236.             if (header->header.uname[0]) /* JER */
  237.                 st->st_uid = finduid(header->header.uname);
  238.             else
  239. #endif /* NONAMES */                
  240.                 st->st_uid = from_oct(8, header->header.uid);
  241. #ifndef NONAMES
  242.             if (header->header.gname[0]) /* JER */
  243.                 st->st_gid = findgid(header->header.gname);
  244.             else
  245. #endif /* NONAMES */                
  246.                 st->st_gid = from_oct(8, header->header.gid);
  247. #else /* MSDOS */
  248.             st->st_uid = st->st_gid = 0;        /* unsupported in DOS */
  249. #endif /* MSDOS */
  250.         }
  251. #ifndef MSDOS
  252.         switch (header->header.linkflag)
  253.         case LF_BLK:
  254.         case LF_CHR:
  255.             st->st_dev = makedev(from_oct(8, header->header.devmajor),
  256.                 from_oct(8, header->header.devminor));
  257. #endif
  258.     }
  259.     else
  260.     {
  261.         /* Old fashioned tar archive */
  262.         *stdp = 0;
  263.         st->st_uid = from_oct(8, header->header.uid);
  264.         st->st_gid = from_oct(8, header->header.gid);
  265.         st->st_dev = 0;
  266.     }
  267. }
  268.  
  269.  
  270. /*
  271.  * Quick and dirty octal conversion.
  272.  *
  273.  * Result is -1 if the field is invalid (all blank, or nonoctal).
  274.  */
  275. long
  276. from_oct(digs, where)
  277. register int    digs;
  278. register char  *where;
  279. {
  280.     register long   value;
  281.  
  282.     while (isspace(*where))
  283.     {                            /* Skip spaces */
  284.         where++;
  285.         if (--digs <= 0)
  286.             return -1;            /* All blank field */
  287.     }
  288.     value = 0;
  289.     while (digs > 0 && isodigit(*where))
  290.     {                            /* Scan til nonoctal */
  291.         value = (value << 3) | (*where++ - '0');
  292.         --digs;
  293.     }
  294.  
  295.     if (digs > 0 && *where && !isspace(*where))
  296.         return -1;                /* Ended on non-space/nul */
  297.  
  298.     return value;
  299. }
  300.  
  301.  
  302. /*
  303.  * Actually print it.
  304.  */
  305. #define    UGSWIDTH    11            /* min width of User, group, size */
  306. #define    DATEWIDTH    19            /* Last mod date */
  307. static int      ugswidth = UGSWIDTH;    /* Max width encountered so far */
  308.  
  309. void
  310. print_header(xname)
  311. char           *xname;
  312. {
  313.     char            modes[11];
  314.     char           *timestamp;
  315.     char            uform[11], gform[11];        /* These hold formatted ints */
  316.     char           *user, *group;
  317.     char            size[24];    /* Holds a formatted long or maj, min */
  318.     long            longie;        /* To make ctime() call portable */
  319.     int             pad;
  320.     int             header_std;    /* Is header standard or not? */
  321.     register int    i;
  322. #ifdef MSDOS
  323.     char        blanks[26];
  324. #endif
  325.  
  326.     annofile(stdout, (char *) NULL);
  327.  
  328.     if (f_verbose)
  329.     {
  330.         decode_header(head, hstat, &header_std, 0);
  331.  
  332.         /* File type and modes */
  333.         modes[0] = '?';
  334.         switch (head->header.linkflag)
  335.         {
  336.         case LF_NORMAL:
  337.         case LF_OLDNORMAL:
  338.         case LF_LINK:
  339.             modes[0] = '-';
  340.             if ('/' == head->header.name[strlen(head->header.name) - 1])
  341.                 modes[0] = 'd';
  342.             break;
  343.         case LF_DIR:
  344.             modes[0] = 'd';
  345.             break;
  346.         case LF_SYMLINK:
  347.             modes[0] = 'l';
  348.             break;
  349.         case LF_BLK:
  350.             modes[0] = 'b';
  351.             break;
  352.         case LF_CHR:
  353.             modes[0] = 'c';
  354.             break;
  355.         case LF_FIFO:
  356.             modes[0] = 'f';
  357.             break;
  358.         case LF_CONTIG:
  359.             modes[0] = '=';
  360.             break;
  361.         }
  362. #ifdef MSDOS
  363.         if (convmode(xname) & O_TEXT)
  364.             modes[0] = 'a';
  365. #endif
  366.  
  367.         demode((unsigned) phstat->st_mode, modes + 1);
  368.  
  369.         /* Timestamp */
  370.         longie = phstat->st_mtime;
  371. #ifdef MSDOS
  372.         /* following is due to a bug in MSDOS's ctime() */
  373.         if (longie < 0x10000000L)
  374.         {
  375.             memset(blanks, ' ', sizeof(blanks)-1);
  376.             blanks[sizeof(blanks)-1] = '\0';
  377.             timestamp = blanks;
  378.         }
  379.         else
  380. #endif
  381.             timestamp = ctime(&longie);
  382.         timestamp[16] = '\0';
  383.         timestamp[24] = '\0';
  384.  
  385.         /* User and group names */
  386.         if (*head->header.uname && header_std)
  387.         {
  388.             user = head->header.uname;
  389.         }
  390.         else
  391.         {
  392.             user = uform;
  393.             (void) sprintf(uform, "%d", (int) phstat->st_uid);
  394.         }
  395.         if (*head->header.gname && header_std)
  396.         {
  397.             group = head->header.gname;
  398.         }
  399.         else
  400.         {
  401.             group = gform;
  402.             (void) sprintf(gform, "%d", (int) phstat->st_gid);
  403.         }
  404.  
  405.         /* Format the file size or major/minor device numbers */
  406.         switch (head->header.linkflag)
  407.         {
  408.         case LF_CHR:
  409.         case LF_BLK:
  410. #ifdef V7
  411.             (void) sprintf(size, "(%d, %d) %D",
  412. #else
  413.             (void) sprintf(size, "%d, %d",
  414. #endif
  415.                 major(phstat->st_dev),
  416.                 minor(phstat->st_dev),
  417.                 /* size has meaning for Minix - JER */
  418.                 (long)phstat->st_size);
  419.             break;
  420.  
  421.         default:
  422. #ifdef V7
  423.             (void) sprintf(size, "%D", (long) phstat->st_size);
  424. #else
  425.             (void) sprintf(size, "%ld", (long) phstat->st_size);
  426. #endif
  427.         }
  428.  
  429.         /* Figure out padding and print the whole line. */
  430.         pad = strlen(user) + strlen(group) + strlen(size) + 1;
  431.         if (pad > ugswidth)
  432.             ugswidth = pad;
  433.  
  434.             printf("%s %s/%s ", modes, user, group);
  435.             for (i = ugswidth - pad; i > 0; i--) putchar(' ');
  436.             printf("%s %s %s %s", size, timestamp+4,
  437.                 timestamp+20, head->header.name);
  438.     }
  439.     else
  440.     {
  441.         printf("%s", head->header.name);
  442.     }
  443.     if (strcmp(head->header.name, xname))
  444.         printf(" (%s)", xname);
  445.  
  446.     if (f_verbose)
  447.         switch (head->header.linkflag)
  448.         {
  449.         case LF_SYMLINK:
  450.             printf(" -> %s\n", head->header.linkname);
  451.             break;
  452.  
  453.         case LF_LINK:
  454.             printf(" link to %s\n", head->header.linkname);
  455.             break;
  456.  
  457.         default:
  458.             printf(" unknown file type '%c'\n", head->header.linkflag);
  459.             break;
  460.  
  461.         case LF_OLDNORMAL:
  462.         case LF_NORMAL:
  463.         case LF_CHR:
  464.         case LF_BLK:
  465.         case LF_DIR:
  466.         case LF_FIFO:
  467.         case LF_CONTIG:
  468.             putc('\n', stdout);
  469.             break;
  470.         }
  471.     else
  472.     {
  473.         putc('\n', stdout);
  474.     }
  475.  
  476.     /* FIXME: we don't print major/minor device numbers */
  477. }
  478.  
  479. /*
  480.  * Print a similar line when we make a directory automatically.
  481.  */
  482. void
  483. pr_mkdir(pathname, length, mode)
  484. char           *pathname;
  485. int             length;
  486. int             mode;
  487. {
  488.     char            modes[11];
  489.  
  490.     if (f_verbose)
  491.     {
  492.         /* File type and modes */
  493.         modes[0] = 'd';
  494.         demode((unsigned) mode, modes + 1);
  495.  
  496.         annofile(stdout, (char *) NULL);
  497.         printf("%s %*s %.*s\n",
  498.             modes,
  499.             ugswidth + DATEWIDTH,
  500.             "Creating directory:",
  501.             length,
  502.             pathname);
  503.     }
  504. }
  505.  
  506.  
  507. /*
  508.  * Skip over <size> bytes of data in records in the archive.
  509.  */
  510. void
  511. skip_file(size)
  512. register long   size;
  513. {
  514.     union record   *x;
  515.  
  516.     while (size > 0)
  517.     {
  518.         x = findrec();
  519.         if (x == NULL)
  520.         {                        /* Check it... */
  521.             annorec(stderr, tar);
  522.             fprintf(stderr, "Unexpected EOF on archive file\n");
  523.             exit(EX_BADARCH);
  524.         }
  525.         userec(x);
  526.         size -= RECORDSIZE;
  527.     }
  528. }
  529.  
  530.  
  531. /*
  532.  * Decode the mode string from a stat entry into a 9-char string and a null.
  533.  * This is good, portable coding, John!
  534.  */
  535. void
  536. demode(mode, string)
  537. register unsigned mode;
  538. register char  *string;
  539. {
  540.     register unsigned mask;
  541.     register char  *rwx = "rwxrwxrwx";
  542.  
  543.     for (mask = 0400; mask != 0; mask >>= 1)
  544.     {
  545.         if (mode & mask)
  546.             *string++ = *rwx++;
  547.         else
  548.         {
  549.             *string++ = '-';
  550.             rwx++;
  551.         }
  552.     }
  553.  
  554. #ifdef S_ISUID
  555.     if (mode & S_ISUID)
  556.         if (string[-7] == 'x')
  557.             string[-7] = 's';
  558.         else
  559.             string[-7] = 'S';
  560. #endif
  561. #ifdef S_ISGID
  562.     if (mode & S_ISGID)
  563.         if (string[-4] == 'x')
  564.             string[-4] = 's';
  565.         else
  566.             string[-4] = 'S';
  567. #endif
  568. #ifdef S_ISVTX
  569.     if (mode & S_ISVTX)
  570.         if (string[-1] == 'x')
  571.             string[-1] = 't';
  572.         else
  573.             string[-1] = 'T';
  574. #endif
  575.     *string = '\0';
  576. }
  577.