home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.mactech.com 2010
/
ftp.mactech.com.tar
/
ftp.mactech.com
/
online
/
source
/
c
/
compilers
/
Tickle-4.0.sit.hqx
/
Tickle-4.0
/
src
/
tar_create.c
< prev
next >
Wrap
Text File
|
1993-11-20
|
19KB
|
846 lines
#pragma segment TAR
/*
* 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"
#include "stat.h"
#include <string.h>
union record *StartHeader();
extern union record *head;
extern struct
{
long st_size;
long st_mtime;
} hstat; /* Fake stat struct for compat. */
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.
*/
#ifdef TCLAPPL
ArCreate()
{
Boolean errFound = false;
Point where;
SFReply reply;
CInfoPBRec pb;
CursHandle cursor;
Str255 name;
/*TGE*/ extern WindowPtr theFeedbackWindow;
/*TGE*/ extern short feedback_showing;
/*TGE*/ extern short in_back_ground;
/*
* Put up a standard file dialog asking for the archive file name.
*/
where.h = where.v = 75;
name[0] = 0;
MyPutFile(where, "\pTar Archive:", name, nil, &reply);
if (!reply.good)
return;
arName = reply.fName;
WDDirVRef(reply.vRefNum, &arVRefNum, &arDirID);
if (OpenArchive(0))
{
/* Open for writing */
WPrintf("Open Archive Failed.");
return;
}
/*TGE*/ UBegYield();
/*
* Ask for directories to add to the archive.
* Note that this is WHOLE directories.
*/
while (!errFound)
{
/*TGE*/ if (in_back_ground)
while (in_back_ground)
/*TGE*/ pausing();
if (! GetFolderPathName("Directory To Archive:", name, &dirVRefNum, &dirDirID))
break;
#ifdef NEVER_DEFINED
if (! GetDir("\pDirectory to Archive:", false))
break;
#endif
/*TGE*/ ShowFeedback();
/*
* Get the catalog info for the selected directory.
*/
pathHead.next = nil;
pathHead.name[0] = '\0';
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;
/*TGE*/ SetPort(theFeedbackWindow);
TextFace(underline);
WPrintf(header);
/*TGE*/ SetPort(theFeedbackWindow);
TextFace(0);
if ((cursor = GetCursor(watchCursor)) != nil)
SetCursor(*cursor);
errFound = DumpDir(&pb, &pathHead);
SetCursor(&qd.arrow);
FlushEvents(everyEvent, 0);
}
/*TGE*/ /*HideFeedback();*/
}
/*TGE*/ ShowFeedback();
WriteEot();
done:
CloseArchive();
WPrintf("--- tar creation completed.");
/*TGE*/
UEndYield();
UInitCursor();
}
#endif /* TCLAPPL */
Cmd_Archive(clientData, interp, argc, argv)
char *clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
Boolean errFound = false, save_cvtNl;
int tarArgc, arg_index, myerr, result = TCL_OK;
char **tarArgv, *name;
Tcl_DString tildeBuf;
CInfoPBRec pb;
CursHandle cursor;
char *ptr, *default_pathhead = NULL, *archive_arg;
Str255 arname, itemname;
PathInfo subPath, *dirPathPtr, filepath;
struct stat statbuf;
#pragma unused (clientData)
extern WindowPtr theFeedbackWindow;
extern short feedback_showing;
extern short in_back_ground;
if (argc < 3)
{
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ?-p prefix? archive_filename filelist\"", (char *) NULL);
result = TCL_ERROR;
}
save_cvtNl = cvtNl;
cvtNl = false;
default_pathhead = NULL;
for ( arg_index = 1 ; arg_index < argc && ! errFound ; ++arg_index )
{
if (argv[arg_index][0] != '-')
break;
if (argv[arg_index][1] == '-' && argv[arg_index][2] == '\0')
break;
if (argv[arg_index][1] == 'p' && argv[arg_index][2] == '\0')
{
default_pathhead = argv[arg_index+1];
++arg_index;
}
else if (argv[arg_index][1] == 'a' && argv[arg_index][2] == '\0')
{
cvtNl = true;
}
else
break;
}
if (arg_index >= argc)
{
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ?-p prefix? archive_filename filelist\"", (char *) NULL);
result = TCL_ERROR;
}
archive_arg = argv[arg_index++];
ptr = strrchr( archive_arg, ':' );
if ( *archive_arg == ':' || ptr == NULL )
{
arDirID = TclMac_CWDDirID();
arVRefNum = TclMac_CWDVRefNum();
strcpy(arname, (ptr == NULL ? archive_arg : ptr + 1));
}
else
{
*ptr = '\0';
myerr = stat( archive_arg, &statbuf );
if ( myerr < 0 )
{
Tcl_AppendResult(interp, "could not locate directory \"", archive_arg,
"\" to create archive - ", Tcl_PosixError(interp), NULL);
*ptr = ':';
result = TCL_ERROR;
}
if ( ! S_ISDIR(statbuf.st_mode) )
{
Tcl_AppendResult(interp, "\"", archive_arg, "\" is not a directory", NULL);
*ptr = ':';
result = TCL_ERROR;
}
*ptr = ':';
arDirID = statbuf.st_ino;
arVRefNum = statbuf.st_dev;
strcpy(arname, ptr + 1);
}
c2pstr(arname);
arName = arname;
if (arg_index >= argc)
{
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ?-p prefix? archive_filename filelist\"", (char *) NULL);
result = TCL_ERROR;
}
if ( Tcl_SplitList(interp, argv[arg_index], &tarArgc, &tarArgv) != TCL_OK )
{
cvtNl = save_cvtNl;
return TCL_ERROR;
}
tar_scripting = 1;
tar_interp = interp;
if (OpenArchive(0)) /* Open for writing */
{
Tcl_AppendResult(interp, "could not open \"", argv[arg_index-1],
"\" to write archive into", (char *) NULL);
tar_scripting = 0;
tar_interp = NULL;
cvtNl = save_cvtNl;
return TCL_ERROR;
}
UBegYield();
pathHead.next = NULL;
pathHead.name[0] = 1;
pathHead.name[1] = '.';
if (default_pathhead != NULL)
{
if (*default_pathhead == '/' && *(default_pathhead + 1) == '\0')
{
pathHead.name[0] = '\0';
}
else
{
strcpy(pathHead.name, default_pathhead);
c2pstr(pathHead.name);
}
}
/*
* Ask for directories to add to the archive.
* Note that this is WHOLE directories.
*/
for ( arg_index = 0 ; arg_index < tarArgc && result == TCL_OK ; ++arg_index )
{
if (in_back_ground)
while (in_back_ground)
pausing();
Tcl_DStringInit (&tildeBuf);
name = Tcl_TildeSubst(interp, tarArgv[arg_index], &tildeBuf);
if (name == NULL)
{
Tcl_AppendResult(interp, "could not substitute for directory \"",
tarArgv[arg_index], "\" ", (char *) NULL);
continue;
}
if (tcl_path_to_dir(name, &dirVRefNum, &dirDirID) != noErr)
{
Tcl_AppendResult(interp, "could not locate \"", name,
"\" for archival", (char *) NULL);
continue;
}
ShowFeedback();
/*
* Get the catalog info for the selected directory.
*/
itemname[0] = '\0';
subPath.next = NULL;
ptr = strrchr(name, ':');
if (ptr != NULL)
{
/* Has path elements */
if ( *(ptr + 1) == '\0' )
{
/* Directory only -> ":dir:dir:dir:" */
}
else
{
/* Directory or File -> ":dir:dir:item" */
strcpy(itemname, ptr + 1);
c2pstr(itemname);
}
}
else
{
/* Has NO path elements */
/* Directory or File -> "item" */
strcpy(itemname, name);
c2pstr(itemname);
}
pb.hFileInfo.ioCompletion = 0;
pb.hFileInfo.ioVRefNum = dirVRefNum;
pb.hFileInfo.ioDirID = dirDirID;
pb.hFileInfo.ioNamePtr = itemname;
pb.hFileInfo.ioFDirIndex = itemname[0] ? 0 : -1;
if (PBGetCatInfo(&pb, false) != noErr)
{
Tcl_AppendResult(interp, "could not get info for \"", name,
"\"", (char *) NULL);
}
else
{
/*
** Add the item to the archive,
** while printing the files being added.
*/
//if (WindInit())
// goto done;
SetPort(theFeedbackWindow);
TextFace(underline);
WPrintf(header);
SetPort(theFeedbackWindow);
TextFace(0);
if ( (cursor = GetCursor(watchCursor)) != NULL )
SetCursor(*cursor);
if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
{
dirPathPtr = &pathHead;
if (itemname[0] != '\0')
{
BlockMove(itemname, subPath.name, itemname[0]+1);
pathHead.next = &subPath;
dirPathPtr = &subPath;
}
errFound = DumpDir(&pb, dirPathPtr);
}
else
{
BlockMove(itemname, subPath.name, itemname[0]+1);
pathHead.next = &subPath;
errFound = DumpFile(&pb);
pathHead.next = nil;
}
if (errFound)
{
Tcl_AppendResult(interp, "error could not archive ",
( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 ? "directory" : "file" ),
" \"", name, "\"", (char *) NULL);
result = TCL_ERROR;
}
SetCursor(&qd.arrow);
FlushEvents(everyEvent, 0);
}
Tcl_DStringFree (&tildeBuf);
pathHead.next = NULL;
}
ShowFeedback();
if (result == TCL_OK)
WriteEot();
CloseArchive();
ckfree ((char *) tarArgv);
cvtNl = save_cvtNl;
tar_scripting = 0;
tar_interp = NULL;
WPrintf("--- tar creation completed.");
UEndYield();
UInitCursor();
return result;
}
/*
* 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";
extern int cancel_current_op;
extern short pause_op;
/*
WPrintf("DumpDir(%d, %d, <%.*s>) ENTER",
dir->dirInfo.ioVRefNum, dir->dirInfo.ioDrDirID,
dir->dirInfo.ioNamePtr[0], &dir->dirInfo.ioNamePtr[1]);
*/
/*
* 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.
*/
file.name[0] = '\0';
pb.hFileInfo.ioCompletion = nil;
pb.hFileInfo.ioNamePtr = file.name;
pb.hFileInfo.ioVRefNum = dir->dirInfo.ioVRefNum;
for (i = 1; !errFound; i++) {
/*TGE*/ DoYield();
if (pause_op)
while (pause_op)
pausing();
if (cancel_current_op)
/*TGE*/ break;
pb.hFileInfo.ioCompletion = nil;
pb.hFileInfo.ioDirID = dir->dirInfo.ioDrDirID;
pb.hFileInfo.ioFDirIndex = i;
pb.hFileInfo.ioVRefNum = dir->dirInfo.ioVRefNum;
if (PBGetCatInfo(&pb, false) != noErr) {
if (pb.hFileInfo.ioResult == fnfErr)
break;
/*TGE*/ WPrintf("DumpDir pPBGetCatInfo(%d, %d, #%d) result = %d.",
dir->dirInfo.ioVRefNum, dir->dirInfo.ioDrDirID, i, pb.hFileInfo.ioResult);
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);
}
}
/*
WPrintf("DumpDir(%d, %d, <%.*s>) EXIT",
dir->dirInfo.ioVRefNum, dir->dirInfo.ioDrDirID,
dir->dirInfo.ioNamePtr[0], &dir->dirInfo.ioNamePtr[1]);
*/
/*
* 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;
memcpy(&buf[1], header->header.name, 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;
memcpy(header->header.chksum, CHKBLANKS, 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 > '~'))
*d++ = '_';
else
*d++ = c;
}
*d++ = (p->next == nil) ? '\0' : '/';
}
if (directory) {
*(d - 1) = '/';
*d = '\0';
}
return((d - header->header.name) > NAMSIZ);
}