home *** CD-ROM | disk | FTP | other *** search
- /*
- * Macintosh Tar
- *
- * Modified by Craig Ruff for use on the Macintosh.
- */
- /*
- * Extract files from a tar archive.
- *
- * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
- *
- * @(#) extract.c 1.17 86/10/29 Public Domain - gnu
- */
- #include "tar.h"
-
- extern union record *head; /* Points to current tape header */
- extern struct stat {
- long st_size;
- long st_mtime;
- } hstat; /* Fake stat struct for compat. */
-
- Boolean ExtractArchive();
- extern void PrintHeader();
- extern Boolean SkipFile();
-
- int MakeDirs(); /* Makes required directories */
-
- /*
- * Extract - extract the entire archive
- */
- Extract() {
- if (OpenArchive("\pExtract Archive:", true))
- return;
-
- if (GetDir("\pExtraction Directory:", true) == false)
- return;
-
- /*
- * Extract and print the files as found in the archive.
- */
- if (!WindInit()) {
- TextFace(underline);
- WPrintf(header);
- TextFace(0);
- ReadAnd(ExtractArchive);
- CloseArchive();
- WindEnd(pref.autoPage);
- }
-
- RlsDir();
- }
-
- /*
- * Extract a file from the archive.
- */
- Boolean
- ExtractArchive()
- {
- register char *data;
- register char *s, *d;
- union record *rec;
- int i, namelen;
- Boolean errFound = false;
- Boolean crSeen;
- long bytes, check, written;
- long size;
- OSErr err;
- char macName[256];
- HParamBlockRec dpb;
- HParamBlockRec fpb;
- char *routine = "\pExtractArchive";
-
- SaveRec(&head); /* Make sure it sticks around */
- UseRec(head); /* And go past it in the archive */
- DecodeHeader(head, &hstat, 1); /* Snarf fields */
-
- /* Print the record from 'head' and 'hstat' */
- PrintHeader();
-
- switch (head->header.linkflag) {
- default:
- WPrintf("Unknown file type %d for %s",
- head->header.linkflag, head->header.name);
- break;
-
- case LF_OLDNORMAL:
- case LF_NORMAL:
- /*
- * Appears to be a file.
- * See if it's really a directory.
- */
- namelen = strlen(head->header.name) - 1;
- if (head->header.name[namelen] == '/')
- goto really_dir;
-
- FixName(head->header.name, macName);
- again_file:
- memset(&fpb, 0, sizeof(fpb));
- fpb.fileParam.ioCompletion = nil;
- fpb.fileParam.ioNamePtr = macName;
- fpb.fileParam.ioVRefNum = dirVRefNum;
- fpb.fileParam.ioFVersNum = 0;
- fpb.fileParam.ioDirID = 0;
- err = PBHCreate(&fpb, false);
- if (err == noErr) {
- fpb.fileParam.ioCompletion = nil;
- fpb.fileParam.ioNamePtr = macName;
- fpb.fileParam.ioVRefNum = dirVRefNum;
- fpb.fileParam.ioDirID = 0;
- fpb.fileParam.ioFVersNum = 0;
- fpb.fileParam.ioFDirIndex = 0;
- if (PBHGetFInfo(&fpb, false)) {
- OSAlert(routine, "\pPBHGetFInfo", macName,
- fpb.fileParam.ioResult);
- goto doNext;
- }
-
- memcpy(&fpb.fileParam.ioFlFndrInfo.fdCreator,
- pref.creator, 4);
- memcpy(&fpb.fileParam.ioFlFndrInfo.fdType, pref.type, 4);
- fpb.fileParam.ioCompletion = nil;
- fpb.fileParam.ioNamePtr = macName;
- fpb.fileParam.ioVRefNum = dirVRefNum;
- fpb.fileParam.ioDirID = 0;
- fpb.fileParam.ioFVersNum = 0;
- if (PBHSetFInfo(&fpb, false)) {
- OSAlert(routine, "\pPBHSetFInfo", macName,
- fpb.fileParam.ioResult);
- goto doNext;
- }
-
- fpb.ioParam.ioPermssn = fsWrPerm;
- fpb.ioParam.ioMisc = nil;
- err = PBHOpen(&fpb, false);
- }
-
- if (err != noErr) {
- if (MakeDirs(macName))
- goto again_file;
-
- PgmAlert(routine, "\pCould not make file", macName);
- errFound = SkipFile((long)hstat.st_size);
- break;
- }
-
- /*
- * Note that this only extracts data forks!
- */
- if (pref.dosCvt)
- crSeen = false;
-
- for (size = hstat.st_size;
- size > 0;
- size -= written) {
- /*
- * Locate data, determine max length
- * writeable, write it, record that
- * we have used the data, then check
- * if the write worked.
- */
- if ((rec = FindRec()) == nil)
- return(true);
-
- data = rec->charptr;
- written = EndOfRecs()->charptr - data;
- if (written > size)
- written = size;
-
- bytes = written;
- if (pref.cvtNl) {
- /*
- * Convert newlines to return for Mac compatability.
- */
- for (i = bytes, d = data; --i >= 0; d++)
- if (*d == LF)
- *d = RETURN;
-
- } else if (pref.dosCvt) {
- /*
- * Convert DOS Style CR/LF to CR only.
- */
- for (s = d = data, i = bytes; i > 0; i--) {
- if ((*s != LF) || !crSeen)
- *d++ = *s;
-
- crSeen = *s++ == RETURN;
- }
-
- bytes -= s - d;
- }
-
- check = bytes;
- fpb.ioParam.ioBuffer = data;
- fpb.ioParam.ioReqCount = check;
- fpb.ioParam.ioPosMode = fsAtMark;
- fpb.ioParam.ioPosOffset = 0;
- err = PBWrite((ParmBlkPtr) &fpb, false);
- if (err != noErr) {
- OSAlert(routine, "\pPBWrite", macName, err);
- goto doNext;
- }
-
- check = fpb.ioParam.ioActCount;
- /*
- * The following is in violation of strict
- * typing, since the arg to userec
- * should be a struct rec *. FIXME.
- */
- UseRec(data + written - 1);
- if (check == bytes)
- continue;
-
- /*
- * Error in writing to file.
- * Print it, skip to next file in archive.
- */
- PgmAlert(routine, "\pWrite short", macName);
- doNext:
- PBClose((ParmBlkPtr) &fpb, false);
- errFound = SkipFile((long)(size - written));
- goto quit;
- }
-
- PBClose((ParmBlkPtr) &fpb, false);
- quit:
- break;
-
- case LF_DIR:
- /* Check for trailing / */
- namelen = strlen(head->header.name)-1;
- really_dir:
- while (namelen && head->header.name[namelen] == '/')
- head->header.name[namelen--] = '\0'; /* Zap / */
-
- FixName(head->header.name, macName);
- /* FALL THROUGH */
- again_dir:
- memset(&dpb, 0, sizeof(dpb));
- dpb.fileParam.ioCompletion = nil;
- dpb.fileParam.ioNamePtr = macName;
- dpb.fileParam.ioVRefNum = dirVRefNum;
- dpb.fileParam.ioFVersNum = 0;
- dpb.fileParam.ioDirID = 0;
- err = PBDirCreate(&dpb, false);
- if ((err != noErr) && (err != dupFNErr)) {
- if (MakeDirs(macName))
- goto again_dir;
-
- PgmAlert(routine, "\pCould not make directory", macName);
- }
-
- break;
- }
-
- /* We don't need to save it any longer. */
- SaveRec((union record **) 0);
- return(errFound);
- }
-
- /*
- * After a file/link/symlink/dir creation has failed, see if
- * it's because some required directory was not present, and if
- * so, create all required dirs.
- */
- int
- MakeDirs(pathname)
- char *pathname;
- {
- int madeone = 0; /* Did we do anything yet? */
- int i, savedLen;
- OSErr err;
- HParamBlockRec pb;
-
- savedLen = pathname[0] & 0xff;
- /*
- * skip initial ':'
- *
- * Note that the directory name has already been converted to Mac style.
- */
- for (i = 2; i < savedLen; i++) {
- while ((i < savedLen) && (pathname[i] != ':'))
- i++;
-
- if (i == savedLen)
- break;
-
- pathname[0] = i++ - 1;
- memset(&pb, 0, sizeof(pb));
- pb.fileParam.ioCompletion = nil;
- pb.fileParam.ioNamePtr = pathname;
- pb.fileParam.ioVRefNum = dirVRefNum;
- pb.fileParam.ioFVersNum = 0;
- pb.fileParam.ioDirID = 0;
- err = PBDirCreate(&pb, false);
- if (err == dupFNErr) {
- continue;
-
- } else if (err != noErr) {
- OSAlert("\pMakeDirs", "\pPBDirCreate", pathname,
- pb.fileParam.ioResult);
- return(0);
-
- } else {
- madeone++; /* Remember if we made one */
- continue;
- }
- }
-
- pathname[0] = savedLen;
- return(madeone); /* Tell them to retry if we made one */
- }
-
- /*
- * FixName - convert to a Mac style pathname
- *
- * Conversions:
- * . -> (Stay at this directory level)
- * .. -> :: (Up a directory level)
- * .xxxx -> _xxxx (Don't get in trouble with device names)
- * xx:xx -> xx/xx (Don't get in trouble with directory delims)
- */
- FixName(tar, mac)
- register char *tar;
- char *mac;
- {
- char *end = tar + strlen(tar);
- register char *p = mac + 1;
- register char *next;
-
- for (next = tar; tar < end; next++) {
- /*
- * Swallow up all contiguous '/' characters.
- */
- while (*next && (*next == '/'))
- next++;
-
- /*
- * Find the entire name up until the next '/'.
- */
- tar = next;
- while (*next && (*next != '/'))
- next++;
-
- *next = 0;
- *p++ = ':';
- if (*tar == '.')
- switch (*(tar + 1)) {
- case '\0':
- p--;
- continue;
-
- case '.':
- if (*(tar + 2) == 0)
- continue;
- /* else FALL THROUGH */
-
- default:
- *tar = '_';
- }
-
- while (tar < next) {
- if (*tar == ':')
- *tar = '/';
- *p++ = *tar++;
- }
- }
-
- *mac = p - mac - 1;
- }
-