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