home *** CD-ROM | disk | FTP | other *** search
- /*
- * Macintosh Tar
- *
- * Modifed by Craig Ruff for use on the Macintosh.
- */
- /*
- * Create a tar archive.
- *
- * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
- *
- * @(#)create.c 1.19 9/9/86 Public Domain - gnu
- */
-
- #include "tar.h"
-
- union record *StartHeader();
- extern union record *head;
-
- /*
- * Fake stat struct for use with existing code
- */
- extern struct stat {
- long st_size;
- long st_mtime;
- } hstat;
-
- void FinishHeader();
- void ToOct();
- Boolean DumpDir(), DumpFile(), FillName(), WriteEot();
-
- /*
- * Used to save pathname info while descending the directory hierarchy.
- */
- struct PathInfo {
- struct PathInfo *next;
- char name[32];
- };
- typedef struct PathInfo PathInfo;
- PathInfo pathHead;
-
- /*
- * ArCreate - manage the creation of an archive
- *
- * Asks for the archive name, creates the archive and then
- * loops asking for directories to add to the archive.
- */
- ArCreate() {
- Boolean errFound = false;
- Point where;
- SFReply reply;
- CInfoPBRec pb;
- CursHandle cursor;
- Str255 name;
-
- /*
- * Put up a standard file dialog asking for the archive file name.
- */
- where.h = where.v = 75;
- name[0] = 0;
- SFPutFile(where, "\pTar Archive:", name, nil, &reply);
- if (!reply.good)
- return;
-
- arVRefNum = reply.vRefNum;
- arName = reply.fName;
- if (OpenArchive(0)) /* Open for writing */
- return;
-
- /*
- * Ask for directories to add to the archive.
- * Note that this is WHOLE directories.
- */
- while (!errFound && GetDir("\pDirectory to Archive:", false)) {
- /*
- * Get the catalog info for the selected directory.
- */
- pathHead.next = nil;
- pb.hfileInfo.ioCompletion = nil;
- pb.hfileInfo.ioNamePtr = pathHead.name;
- pb.hfileInfo.ioVRefNum = dirVRefNum;
- pb.hfileInfo.ioDirID = dirDirID;
- pb.hfileInfo.ioFDirIndex = -1;
- if (PBGetCatInfo(&pb, false) != noErr) {
- OSAlert("\pArCreate", "\pPBGetCatInfo", pathHead.name,
- pb.hfileInfo.ioResult);
- break;
-
- } else {
- /*
- * Add the directory to the archive,
- * while printing the files being added.
- */
- if (WindInit())
- goto done;
-
- TextFace(underline);
- WPrintf(header);
- TextFace(0);
- if ((cursor = GetCursor(watchCursor)) != nil)
- SetCursor(*cursor);
-
- errFound = DumpDir(&pb, &pathHead);
- SetCursor(&qd.arrow);
- WindEnd(autoPage);
- FlushEvents(everyEvent, 0);
- }
- }
-
- WriteEot();
- done:
- CloseArchive();
- }
-
- /*
- * DumpDir - add a directory (possibly recursively) to the archive
- *
- * Exits via a longjmp on unrecoverable error
- * Returns normally otherwise
- */
- Boolean
- DumpDir(dir, path)
- CInfoPBRec *dir;
- PathInfo *path;
- {
- union record *header;
- int i;
- Boolean errFound = false;
- CInfoPBRec pb;
- PathInfo file;
- char *routine = "\pDumpDir";
- EventRecord e;
-
- /*
- * Output directory header record with permissions
- * FIXME, do this AFTER files, to avoid R/O dir problems?
- * If Unix Std format, don't put / on end of dir name
- * If old archive format, don't write record at all.
- */
- if (!oldArch) {
- /*
- * If people could really read standard archives,
- * this should be: (FIXME)
- * header = start_header(f_standard? p: namebuf, statbuf);
- * but since they'd interpret LF_DIR records as
- * regular files, we'd better put the / on the name.
- */
- if ((header = StartHeader(dir)) == nil)
- return(true);
-
- if (standard)
- header->header.linkflag = LF_DIR;
-
- FinishHeader(header); /* Done with directory header */
- head = header;
- PrintHeader();
- }
-
- file.next = nil;
- path->next = &file;
-
- /*
- * Check all entries in the directory.
- * Add regular files, recurse on subdirectories.
- */
- pb.hfileInfo.ioCompletion = nil;
- pb.hfileInfo.ioNamePtr = file.name;
- pb.hfileInfo.ioVRefNum = dir->dirInfo.ioVRefNum;
- for (i = 1; !errFound; i++) {
- SystemTask();
- if (EventAvail(keyDownMask, &e) && (e.what != nullEvent)) {
- if (
- (e.modifiers & cmdKey) &&
- ((e.message & charCodeMask) == '.')
- ) {
- GetNextEvent(keyDownMask, &e);
- break;
- }
- }
-
- pb.hfileInfo.ioDirID = dir->dirInfo.ioDrDirID;
- pb.hfileInfo.ioFDirIndex = i;
- if (PBGetCatInfo(&pb, false) != noErr) {
- if (pb.hfileInfo.ioResult == fnfErr)
- break;
-
- OSAlert(routine, "\pPBGetCatInfo", "\pDirectory search",
- pb.hfileInfo.ioResult);
- return(true);
- }
-
- if ((unsigned char) file.name[0] > 32) {
- /*
- * Sanity check, we have overwritten our stack!
- */
- PgmAlert(routine, "\pName too long", file.name);
- return(true);
- }
-
- if (DIRECTORY(pb)) {
- errFound = DumpDir(&pb, &file);
-
- } else {
- if (pb.hfileInfo.ioFRefNum == archive) {
- /*
- * DO NOT add the archive to itself!
- */
- ArSkipAlert();
- continue;
- }
-
- errFound = DumpFile(&pb);
- }
- }
-
- /*
- * Done with this directory, make sure we don't run out
- * of working directories.
- */
- path->next = nil;
- return(errFound);
- }
-
- /*
- * DumpFile - Dump a single file.
- *
- * Exits via longjmp on unrecoverable error.
- * Result is 1 for success, 0 for failure.
- */
- Boolean
- DumpFile(file)
- CInfoPBRec *file;
- {
- union record *header;
- register char *p;
- char *buf;
- HParamBlockRec fpb;
- long bufsize, count, i;
- register long sizeleft;
- register union record *start;
- char *routine = "\pDumpFile";
-
- if ((header = StartHeader(file)) == nil)
- return(true);
-
- FinishHeader(header);
- /*
- * Get the size of the file.
- * Don't bother opening it if it is zero length.
- */
- head = header;
- hstat.st_size = file->hfileInfo.ioFlLgLen;
- PrintHeader();
- if ((sizeleft = file->hfileInfo.ioFlLgLen) == 0)
- return(false);
-
- fpb.fileParam.ioCompletion = nil;
- fpb.fileParam.ioNamePtr = file->hfileInfo.ioNamePtr;
- fpb.fileParam.ioVRefNum = file->hfileInfo.ioVRefNum;
- fpb.fileParam.ioFVersNum = 0;
- fpb.fileParam.ioDirID = file->hfileInfo.ioFlParID;
- fpb.ioParam.ioPermssn = fsRdPerm;
- fpb.ioParam.ioMisc = nil;
- if (PBHOpen(&fpb, false) != noErr) {
- OSAlert(routine, "\pPBHOpen", file->hfileInfo.ioNamePtr,
- fpb.fileParam.ioResult);
- return(true);
- }
-
- /*
- * Dump the file to the archive.
- * Note: this only dumps the data fork!
- */
- while (sizeleft > 0) {
- if ((start = FindRec()) == nil)
- return(true);
-
- bufsize = EndOfRecs()->charptr - start->charptr;
- buf = start->charptr;
- again:
- count = (sizeleft < bufsize) ? sizeleft : bufsize;
- fpb.ioParam.ioBuffer = buf;
- fpb.ioParam.ioReqCount = count;
- fpb.ioParam.ioPosMode = fsAtMark;
- fpb.ioParam.ioPosOffset = 0;
- if (PBRead((ParmBlkPtr) &fpb, false) != noErr) {
- OSAlert(routine, "\pPBRead", file->hfileInfo.ioNamePtr,
- fpb.ioParam.ioResult);
- return(true);
- }
-
- count = fpb.ioParam.ioActCount;
- if (cvtNl) {
- /*
- * Convert returns to newlines for Unix compat.
- */
- for (i = count, p = buf; --i >= 0; p++)
- if (*p == RETURN)
- *p = LF;
- }
-
- sizeleft -= count;
- UseRec(start + (count - 1) / RECORDSIZE);
- }
-
- PBClose((ParmBlkPtr) &fpb, false);
-
- /* Clear last block garbage to zeros, FIXME */
- return(false);
- }
-
-
- /*
- * Make a header block for the file name whose stat info is st .
- * Return header pointer for success, NULL if the name is too long.
- */
- union record *
- StartHeader(pb)
- CInfoPBRec *pb;
- {
- register union record *header;
- Boolean directory = DIRECTORY(*pb);
-
- if ((header = (union record *) FindRec()) == nil)
- return(nil);
-
- bzero(header->charptr, sizeof(union record)); /* XXX speed up */
- /*
- * Generate the pathname, make sure we don't overflow
- * the field in the tar header.
- */
- if (FillName(header, directory)) {
- char buf[NAMSIZ + 1];
-
- buf[0] = NAMSIZ;
- bcopy(header->header.name, &buf[1], NAMSIZ);
- PgmAlert("\pStartHeader", "\pName too long", buf);
- return(nil);
- }
-
- /*
- * Fake the file mode, uid, gid.
- * Convert from Mac based time to Unix based time.
- */
- ToOct((directory) ? 0755L : 0644L, 8, header->header.mode);
- ToOct(0L, 8, header->header.uid);
- ToOct(0L, 8, header->header.gid);
- ToOct((directory) ? 0L : pb->hfileInfo.ioFlLgLen, 1+12,
- header->header.size);
- ToOct((directory) ? pb->dirInfo.ioDrMdDat - TIMEDIFF :
- pb->hfileInfo.ioFlMdDat - TIMEDIFF,
- 1+12, header->header.mtime);
- /* header->header.linkflag is left as null */
- return(header);
- }
-
- /*
- * Finish off a filled-in header block and write it out.
- */
- void
- FinishHeader(header)
- register union record *header;
- {
- register int i;
- register long sum;
- register char *p;
-
- bcopy(CHKBLANKS, header->header.chksum, sizeof(header->header.chksum));
-
- sum = 0;
- p = header->charptr;
- for (i = sizeof(union record); --i >= 0; ) {
- /*
- * We can't use unsigned char here because of old compilers,
- * e.g. V7.
- */
- sum += 0xFF & *p++;
- }
-
- /*
- * Fill in the checksum field. It's formatted differently
- * from the other fields: it has [6] digits, a null, then a
- * space -- rather than digits, a space, then a null.
- * We use to_oct then write the null in over to_oct's space.
- * The final space is already there, from checksumming, and
- * to_oct doesn't modify it.
- *
- * This is a fast way to do:
- * (void) sprintf(header->header.chksum, "%6o", sum);
- */
- ToOct((long) sum, 8, header->header.chksum);
- header->header.chksum[6] = '\0'; /* Zap the space */
- UseRec(header);
- return;
- }
-
-
- /*
- * Quick and dirty octal conversion.
- * Converts long "value" into a "digs"-digit field at "where",
- * including a trailing space and room for a null. "digs"==3 means
- * 1 digit, a space, and room for a null.
- *
- * We assume the trailing null is already there and don't fill it in.
- * This fact is used by start_header and finish_header, so don't change it!
- *
- * This should be equivalent to:
- * (void) sprintf(where, "%*lo ", digs-2, value);
- * except that sprintf fills in the trailing null and we don't.
- */
- void
- ToOct(value, digs, where)
- register long value;
- register int digs;
- register char *where;
- {
- --digs; /* Trailing null slot is left alone */
- where[--digs] = ' '; /* Put in the space, though */
-
- /* Produce the digits -- at least one */
- do {
- where[--digs] = '0' + (value & 7); /* one octal digit */
- value >>= 3;
- } while (digs > 0 && value != 0);
-
- /* Leading spaces, if necessary */
- while (digs > 0)
- where[--digs] = ' ';
-
- }
-
- /*
- * Write the EOT block(s).
- */
- Boolean
- WriteEot()
- {
- union record *p;
-
- if ((p = FindRec()) == nil)
- return(true);
-
- bzero(p->charptr, RECORDSIZE);
- UseRec(p);
- /* FIXME, only one EOT block should be needed. */
- if ((p = FindRec()) == nil)
- return(true);
-
- bzero(p->charptr, RECORDSIZE);
- UseRec(p);
- return(false);
- }
-
- /*
- * FillName - generate the file or directory pathname
- *
- * Converts to Unix style pathnames.
- * Appends a '/' for a directory.
- */
- Boolean
- FillName(header, directory)
- register union record *header;
- Boolean directory;
- {
- register PathInfo *p;
- register char *d, *s;
- char c;
- int i;
-
- d = header->header.name;
- for (p = &pathHead; p != nil; p = p->next) {
- s = &p->name[1];
- for (i = p->name[0]; i > 0; i--) {
- c = *s++;
- if (c == '/')
- *d++ = ':';
-
- else if ((c <= ' ') || (c >= 0x7f))
- *d++ = '_';
-
- else
- *d++ = c;
- }
-
- *d++ = (p->next == nil) ? '\0' : '/';
- }
-
- if (directory) {
- *(d - 1) = '/';
- *d = '\0';
- }
-
- return((d - header->header.name) > NAMSIZ);
- }
-