home *** CD-ROM | disk | FTP | other *** search
- /*
- * Macintosh Tar
- *
- * Modified by Craig Ruff for use on the Macintosh.
- */
- /*
- * List a tar archive.
- *
- * Also includes support routines for reading a tar archive.
- *
- * Pubic Domain version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
- *
- * @(#)list.c 1.18 9/23/86 Public Domain - gnu
- */
- #include "tar.h"
-
- char *ctime(); /* From libc.a */
-
- #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
- #define isspace(c) ((c) == ' ')
-
- long FromOct(); /* Decode octal number */
-
- union record *head; /* Points to current archive header */
- struct stat {
- long st_size;
- long st_mtime;
- } hstat; /* Fake stat struct for compat. */
-
- void PrintHeader();
- void ReadAnd();
- Boolean ListArchive(), SkipFile();
-
- /*
- * List - list an archive file
- */
- List() {
- Point where;
- SFReply reply;
- Boolean oldAutoPage = autoPage;
-
- /*
- * Use standard file to get the archive file.
- * Always do a screen at a time if to the screen.
- */
- where.h = where.v = 75;
- SFGetFile(where, "\pName of TAR file:", nil, -1, nil, nil, &reply);
- if (!reply.good)
- return;
-
- arVRefNum = reply.vRefNum;
- arName = reply.fName;
- if (WindInit())
- return;
-
- autoPage = true;
- TextFace(underline);
- WPrintf(header);
- TextFace(0);
-
- ReadAnd(ListArchive);
- CloseArchive();
- WindEnd(true);
- autoPage = oldAutoPage;
- }
-
- /*
- * Main loop for reading an archive.
- */
- void
- ReadAnd(doSomething)
- Boolean (*doSomething)();
- {
- int status = 1;
- int prevStatus;
- Boolean errFound = false;
- EventRecord e;
- CursHandle cursor;
-
- if ((cursor = GetCursor(watchCursor)) != nil)
- SetCursor(*cursor);
-
- OpenArchive(1); /* Open for reading */
-
- while (!errFound) {
- SystemTask();
- if (EventAvail(keyDownMask, &e) && (e.what != nullEvent)) {
- if (
- (e.modifiers & cmdKey) &&
- ((e.message & charCodeMask) == '.')
- ) {
- GetNextEvent(keyDownMask, &e);
- break;
- }
- }
-
- prevStatus = status;
- status = ReadHeader();
- switch (status) {
- case 1: /* Valid header */
- /* We should decode next field (mode) first... */
- /* Ensure incoming names are null terminated. */
- head->header.name[NAMSIZ-1] = '\0';
- errFound = (*doSomething)();
- continue;
-
- /*
- * If the previous header was good, tell them
- * that we are skipping bad ones.
- */
- case 0: /* Invalid header */
- case0:
- UseRec(head);
- if (prevStatus == 1) {
- PgmAlert("\pReadAnd",
- "\pSkipping to next file header...",
- nil);
- }
- continue;
-
- case 2: /* Block of zeroes */
- if (ignorez)
- goto case0; /* Just skip if asked */
- /* FALL THRU */
- case (int) EOF: /* End of archive */
- break;
- }
- break;
- }
-
- CloseArchive();
- SetCursor(&qd.arrow);
- }
-
-
- /*
- * Print a header record, based on tar options.
- */
- Boolean
- ListArchive()
- {
- long t;
-
- /* Save the record */
- SaveRec(&head);
-
- /*
- * Print the header record.
- * Don't sling the names too fast!
- */
- PrintHeader();
- if (!autoPage)
- Delay(60L, &t);
-
- /* Skip past it in the archive */
- SaveRec((union record **) 0); /* Unsave it */
- UseRec(head);
-
- /* Skip to the next header on the archive */
- return(SkipFile((long)hstat.st_size));
- }
-
-
- /*
- * Read a record that's supposed to be a header record.
- * Return its address in "head", and if it is good, the file's
- * size in hstat.st_size.
- *
- * Return 1 for success, 0 if the checksum is bad, EOF on eof,
- * 2 for a block full of zeros (EOF marker).
- *
- * You must always userec(head) to skip past the header which this
- * routine reads.
- */
- int
- ReadHeader()
- {
- register int i;
- register long sum, recsum;
- register char *p;
- register union record *header;
-
- header = FindRec();
- head = header; /* This is our current header */
- if (header == nil)
- return(EOF);
-
- recsum = FromOct(8, header->header.chksum);
- sum = 0;
- p = header->charptr;
- for (i = sizeof(*header); --i >= 0;) {
- /*
- * We can't use unsigned char here because of old compilers,
- * e.g. V7.
- */
- sum += 0xFF & *p++;
- }
-
- /* Adjust checksum to count the "chksum" field as blanks. */
- for (i = sizeof(header->header.chksum); --i >= 0;)
- sum -= 0xFF & header->header.chksum[i];
- sum += ' ' * sizeof(header->header.chksum);
-
- if (sum == recsum) {
- /*
- * Good record. Decode file size and return.
- */
- hstat.st_size = FromOct(1+12, header->header.size);
- return(1);
- }
-
- if (sum == 8 * ' ') {
- /*
- * This is a zeroed block...whole block is 0's except
- * for the 8 blanks we faked for the checksum field.
- */
- return(2);
- }
-
- return(0);
- }
-
- /*
- * Decode things from a file header record into a "struct stat".
- *
- * read_header() has already decoded the checksum and length, so we don't.
- *
- * If wantug != 0, we want the uid/group info decoded from Unix Standard
- * tapes (for extraction). If == 0, we are just printing anyway, so save time.
- */
- DecodeHeader(header, st, wantug)
- register union record *header;
- register struct stat *st;
- int wantug;
- {
- #pragma unused(wantug)
-
- st->st_mtime = FromOct(1+12, header->header.mtime);
- }
-
- /*
- * Quick and dirty octal conversion.
- *
- * Result is -1 if the field is invalid (all blank, or nonoctal).
- */
- long
- FromOct(digs, where)
- register int digs;
- register char *where;
- {
- register long value;
-
- while (isspace(*where)) { /* Skip spaces */
- where++;
- if (--digs <= 0)
- return(-1); /* All blank field */
- }
-
- value = 0;
- while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */
- value = (value << 3) | (*where++ - '0');
- --digs;
- }
-
- if (digs > 0 && *where && !isspace(*where))
- return(-1); /* Ended on non-space/nul */
-
- return(value);
- }
-
- /*
- * Actually print it.
- */
- #define UGSWIDTH 9 /* min width of User, group, size */
- #define DATEWIDTH 19 /* Last mod date */
- static int ugswidth = UGSWIDTH; /* Max width encountered so far */
-
- void
- PrintHeader()
- {
- char mode;
- char *timestamp;
- char size[12]; /* Holds a formatted long */
- long longie; /* To make ctime() call portable */
- int pad;
-
- DecodeHeader(head, &hstat, 0);
- /* File type and mode */
- mode = '?';
- switch (head->header.linkflag) {
- case LF_NORMAL:
- case LF_OLDNORMAL:
- mode = 'F';
- if ('/' == head->header.name[strlen(head->header.name)-1])
- mode = 'D';
- break;
-
- case LF_DIR:
- mode = 'D';
- break;
- }
-
- /*
- * Convert to Mac based time from Unix based time.
- */
- longie = hstat.st_mtime + TIMEDIFF;
-
- timestamp = ctime(&longie);
- timestamp[16] = '\0';
- timestamp[24] = '\0';
-
- /* Format the file size or major/minor device numbers */
- switch (head->header.linkflag) {
- default:
- (void) sprintf(size, "?????");
- break;
-
- case LF_DIR:
- (void) sprintf(size, "%.*s", UGSWIDTH, "");
- break;
-
- case LF_OLDNORMAL:
- case LF_NORMAL:
- (void) sprintf(size, "%ld", hstat.st_size);
- break;
- }
-
-
- /* Figure out padding and print the whole line. */
- pad = strlen(size) + 1;
- if (pad > ugswidth)
- ugswidth = pad;
-
- WPrintf("%c %*s%s %s %s %.*s", mode, ugswidth - pad, "", size,
- timestamp+4, timestamp+20, sizeof(head->header.name),
- head->header.name);
- }
-
- /*
- * Skip over <size> bytes of data in records in the archive.
- */
- Boolean
- SkipFile(size)
- register long size;
- {
- union record *x;
-
- while (size > 0) {
- x = FindRec();
- if (x == nil) { /* Check it... */
- PgmAlert("\pSkipFile",
- "\pUnexpected EOF on archive file",
- nil);
- return(true);
- }
-
- UseRec(x);
- size -= RECORDSIZE;
- }
-
- return(false);
- }
-