home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / tarsrc30.sit / list.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-04  |  6.8 KB  |  349 lines

  1. /*
  2.  * Macintosh Tar
  3.  *
  4.  * Modified by Craig Ruff for use on the Macintosh.
  5.  */
  6. /*
  7.  * List a tar archive.
  8.  *
  9.  * Also includes support routines for reading a tar archive.
  10.  *
  11.  * Pubic Domain version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
  12.  *
  13.  * @(#)list.c 1.18 9/23/86 Public Domain - gnu
  14.  */
  15. #include "tar.h"
  16.  
  17. char *ctime();                /* From libc.a */
  18.  
  19. #define    isodigit(c)    ( ((c) >= '0') && ((c) <= '7') )
  20. #define isspace(c)    ((c) == ' ')
  21.  
  22. long FromOct();            /* Decode octal number */
  23.  
  24. union record *head;        /* Points to current archive header */
  25. struct stat {
  26.     long    st_size;
  27.     long    st_mtime;
  28. } hstat;            /* Fake stat struct for compat. */
  29.  
  30. void    PrintHeader();
  31. void    ReadAnd();
  32. Boolean    ListArchive(), SkipFile();
  33.  
  34. /*
  35.  * List - list an archive file
  36.  */
  37. List() {
  38.     Boolean        oldAutoPage = pref.autoPage;
  39.  
  40.     if (OpenArchive("\pList Archive:", true))
  41.         return;
  42.         
  43.     if (WindInit())
  44.         return;
  45.  
  46.     pref.autoPage = true;
  47.     TextFace(underline);
  48.     WPrintf(header);
  49.     TextFace(0);
  50.     ReadAnd(ListArchive);
  51.     CloseArchive();
  52.     WindEnd(true);
  53.     pref.autoPage = oldAutoPage;
  54. }
  55.  
  56. /*
  57.  * Main loop for reading an archive.
  58.  */
  59. void
  60. ReadAnd(doSomething)
  61. Boolean (*doSomething)();
  62. {
  63.     int         status = 1;
  64.     int         prevStatus;
  65.     Boolean        errFound = false;
  66.     EventRecord    e;
  67.     CursHandle    cursor;
  68.  
  69.     if ((cursor = GetCursor(watchCursor)) != nil)
  70.         SetCursor(*cursor);
  71.  
  72.     while (!errFound) {
  73.         if (EventAvail(keyDownMask, &e) && (e.what != nullEvent)) {
  74.             if (
  75.                 (e.modifiers & cmdKey) &&
  76.                 ((e.message & charCodeMask) == '.')
  77.             ) {
  78.                 WaitNextEvent(keyDownMask, &e, 0L, nil);
  79.                 break;
  80.             }
  81.         }
  82.  
  83.         prevStatus = status;
  84.         status = ReadHeader();
  85.         switch (status) {
  86.         case 1:            /* Valid header */
  87.             /* We should decode next field (mode) first... */
  88.             /* Ensure incoming names are null terminated. */
  89.             head->header.name[NAMSIZ-1] = '\0';
  90.             errFound = (*doSomething)();
  91.             continue;
  92.  
  93.             /*
  94.              * If the previous header was good, tell them
  95.              * that we are skipping bad ones.
  96.              */
  97.         case 0:            /* Invalid header */
  98.         case0:
  99.             UseRec(head);
  100.             if (prevStatus == 1) {
  101.                 PgmAlert("\pReadAnd",
  102.                     "\pSkipping to next file header...",
  103.                     nil);
  104.             }
  105.             continue;
  106.  
  107.         case 2:            /* Block of zeroes */
  108.             if (ignorez)    
  109.                 goto case0;    /* Just skip if asked */
  110.             /* FALL THRU */
  111.         case (int) EOF:        /* End of archive */
  112.             break;
  113.         }
  114.         break;
  115.     }
  116.  
  117.     CloseArchive();
  118.     SetCursor(&qd.arrow);
  119. }        
  120.  
  121.  
  122. /*
  123.  * Print a header record, based on tar options.
  124.  */
  125. Boolean
  126. ListArchive()
  127. {
  128.     long t;
  129.  
  130.     /* Save the record */
  131.     SaveRec(&head);
  132.  
  133.     /*
  134.      * Print the header record.
  135.      * Don't sling the names too fast!
  136.      */
  137.     PrintHeader();
  138.     if (!pref.autoPage)
  139.         Delay(60L, &t);
  140.  
  141.     /* Skip past it in the archive */
  142.     SaveRec((union record **) 0);    /* Unsave it */
  143.     UseRec(head);
  144.  
  145.     /* Skip to the next header on the archive */
  146.     return(SkipFile((long)hstat.st_size));
  147. }
  148.  
  149.  
  150. /*
  151.  * Read a record that's supposed to be a header record.
  152.  * Return its address in "head", and if it is good, the file's
  153.  * size in hstat.st_size.
  154.  *
  155.  * Return 1 for success, 0 if the checksum is bad, EOF on eof,
  156.  * 2 for a block full of zeros (EOF marker).
  157.  *
  158.  * You must always userec(head) to skip past the header which this
  159.  * routine reads.
  160.  */
  161. int
  162. ReadHeader()
  163. {
  164.     register int    i;
  165.     register long    sum, recsum;
  166.     register char    *p;
  167.     register union record *header;
  168.  
  169.     header = FindRec();
  170.     head = header;        /* This is our current header */
  171.     if (header == nil)
  172.         return(EOF);
  173.  
  174.     recsum = FromOct(8,  header->header.chksum);
  175.     sum = 0;
  176.     p = header->charptr;
  177.     for (i = sizeof(*header); --i >= 0;) {
  178.         /*
  179.          * We can't use unsigned char here because of old compilers,
  180.          * e.g. V7.
  181.          */
  182.         sum += 0xFF & *p++;
  183.     }
  184.  
  185.     /* Adjust checksum to count the "chksum" field as blanks. */
  186.     for (i = sizeof(header->header.chksum); --i >= 0;)
  187.         sum -= 0xFF & header->header.chksum[i];
  188.     sum += ' ' * sizeof(header->header.chksum);
  189.  
  190.     if (sum == recsum) {
  191.         /*
  192.          * Good record.  Decode file size and return.
  193.          */
  194.         hstat.st_size = FromOct(1+12, header->header.size);
  195.         return(1);
  196.     }
  197.  
  198.     if (sum == 8 * ' ') {
  199.         /*
  200.          * This is a zeroed block...whole block is 0's except
  201.          * for the 8 blanks we faked for the checksum field.
  202.          */
  203.         return(2);
  204.     }
  205.  
  206.     return(0);
  207. }
  208.  
  209. /* 
  210.  * Decode things from a file header record into a "struct stat".
  211.  *
  212.  * read_header() has already decoded the checksum and length, so we don't.
  213.  *
  214.  * If wantug != 0, we want the uid/group info decoded from Unix Standard
  215.  * tapes (for extraction).  If == 0, we are just printing anyway, so save time.
  216.  */
  217. DecodeHeader(header, st, wantug)
  218. register union record    *header;
  219. register struct stat    *st;
  220. int    wantug;
  221. {
  222. #pragma unused(wantug)
  223.  
  224.     st->st_mtime = FromOct(1+12, header->header.mtime);
  225. }
  226.  
  227. /*
  228.  * Quick and dirty octal conversion.
  229.  *
  230.  * Result is -1 if the field is invalid (all blank, or nonoctal).
  231.  */
  232. long
  233. FromOct(digs, where)
  234. register int    digs;
  235. register char    *where;
  236. {
  237.     register long    value;
  238.  
  239.     while (isspace(*where)) {        /* Skip spaces */
  240.         where++;
  241.         if (--digs <= 0)
  242.             return(-1);        /* All blank field */
  243.     }
  244.  
  245.     value = 0;
  246.     while (digs > 0 && isodigit(*where)) {    /* Scan til nonoctal */
  247.         value = (value << 3) | (*where++ - '0');
  248.         --digs;
  249.     }
  250.  
  251.     if (digs > 0 && *where && !isspace(*where))
  252.         return(-1);            /* Ended on non-space/nul */
  253.  
  254.     return(value);
  255. }
  256.  
  257. /*
  258.  * Actually print it.
  259.  */
  260. #define    UGSWIDTH    9    /* min width of User, group, size */
  261. #define    DATEWIDTH    19    /* Last mod date */
  262. static int ugswidth = UGSWIDTH;    /* Max width encountered so far */
  263.  
  264. void
  265. PrintHeader()
  266. {
  267.     char mode;
  268.     char *timestamp;
  269.     char size[12];        /* Holds a formatted long */
  270.     long longie;        /* To make ctime() call portable */
  271.     int    pad;
  272.  
  273.     DecodeHeader(head, &hstat, 0);
  274.     /* File type and mode */
  275.     mode = '?';
  276.     switch (head->header.linkflag) {
  277.     case LF_NORMAL:
  278.     case LF_OLDNORMAL:
  279.         mode = 'F'; 
  280.         if ('/' == head->header.name[strlen(head->header.name)-1])
  281.             mode = 'D';
  282.         break;
  283.  
  284.     case LF_DIR:
  285.         mode = 'D';
  286.         break;
  287.     }
  288.  
  289.     /* 
  290.      * Convert to Mac based time from Unix based time.
  291.      */
  292.     longie = hstat.st_mtime + TIMEDIFF;
  293.  
  294.     timestamp = ctime(&longie);
  295.     timestamp[16] = '\0';
  296.     timestamp[24] = '\0';
  297.  
  298.     /* Format the file size or major/minor device numbers */
  299.     switch (head->header.linkflag) {
  300.     default:
  301.         (void) sprintf(size, "?????");
  302.         break;
  303.  
  304.     case LF_DIR:
  305.         (void) sprintf(size, "%.*s", UGSWIDTH, "");
  306.         break;
  307.  
  308.     case LF_OLDNORMAL:
  309.     case LF_NORMAL:
  310.         (void) sprintf(size, "%ld", hstat.st_size);
  311.         break;
  312.     }
  313.  
  314.  
  315.     /* Figure out padding and print the whole line. */
  316.     pad = strlen(size) + 1;
  317.     if (pad > ugswidth)
  318.         ugswidth = pad;
  319.  
  320.     WPrintf("%c %*s%s %s %s %.*s", mode, ugswidth - pad, "", size,
  321.         timestamp+4, timestamp+20, sizeof(head->header.name),
  322.                   head->header.name);
  323. }
  324.  
  325. /*
  326.  * Skip over <size> bytes of data in records in the archive.
  327.  */
  328. Boolean
  329. SkipFile(size)
  330. register long size;
  331. {
  332.     union record *x;
  333.  
  334.     while (size > 0) {
  335.         x = FindRec();
  336.         if (x == nil) {    /* Check it... */
  337.             PgmAlert("\pSkipFile",
  338.                 "\pUnexpected EOF on archive file",
  339.                 nil);
  340.             return(true);
  341.         }
  342.         
  343.         UseRec(x);
  344.         size -= RECORDSIZE;
  345.     }
  346.     
  347.     return(false);
  348. }
  349.