home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Garbo
/
Garbo.cdr
/
mac
/
source
/
tarsrc.sit
/
create.c
< prev
next >
Wrap
Text File
|
1989-09-14
|
11KB
|
494 lines
/*
* 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);
}