🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.sources.mac
Article #45

Tar 1.1 Source (part 2 of 3)

#45
From: Craig Ruff
Date: Fri, 13 May 1988 05:52
1405 lines
34213 bytes
[Tar 1.1 Source - part 2 of 3]

---
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	create.c
#	dialog.c
#	dir.c
# This archive created: Wed May 11 07:24:05 1988
# By:	Roger L. Long (bytebug@dhw68k.cts.com)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'create.c'" '(10693 characters)'
if test -f 'create.c'
then
	echo shar: will not over-write existing file "'create.c'"
else
sed 's/^X//' << \SHAR_EOF > 'create.c'
X/*
X * Macintosh Tar
X *
X * Modifed by Craig Ruff for use on the Macintosh.
X */
X/*
X * Create a tar archive.
X *
X * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
X *
X * @(#)create.c 1.19 9/9/86 Public Domain - gnu
X */
X
X#include "tar.h"
X
Xunion record		*StartHeader();
Xextern union record	*head;
X
X/*
X * Fake stat struct for use with existing code
X */
Xextern struct stat {
X	long	st_size;
X	long	st_mtime;
X} hstat;
X
Xvoid	FinishHeader();
Xvoid	ToOct();
XBoolean	FillName();
X
X/*
X * Used to save pathname info while descending the directory hierarchy.
X */
Xstruct PathInfo {
X	struct PathInfo	*next;
X	char		name[32];
X};
Xtypedef struct PathInfo PathInfo;
XPathInfo	pathHead;
X
X/*
X * ArCreate - manage the creation of an archive
X *
X *	Asks for the archive name, creates the archive and then
X *	loops asking for directories to add to the archive.
X */
XArCreate() {
X	Point		where;
X	SFReply		reply;
X	CInfoPBRec	pb;
X	CursHandle	cursor;
X
X	/*
X	 * Put up a standard file dialog asking for the archive file name.
X	 */
X	where.h = where.v = 75;
X	SFPutFile(pass(where), "\PName of TAR file", NIL, NIL, &reply);
X	if (!reply.good)
X		return;
X
X	arVRefNum = reply.vRefNum;
X	arName = reply.fName;
X	if (OpenArchive(0))	/* Open for writing */
X		return;
X
X	if (setjmp(errJmp))
X		goto done;
X
X	/*
X	 * Ask for directories to add to the archive.
X	 * Note that this is WHOLE directories.
X	 */
X	while (GetDir("\Pto archive")) {
X		/*
X		 * Get the catalog info for the selected directory.
X		 */
X		pathHead.next = NIL;
X		pb.ioCompletion = NIL;
X		pb.ioNamePtr = pathHead.name;
X		pb.ioVRefNum = dirVRefNum;
X		pb.u.hfi.ioDirID = dirDirID;
X		pb.ioFDirIndex = -1;
X		if (PBGetCatInfo(&pb, FALSE) != noErr) {
X			OSAlert("\PArCreate", "\PPBGetCatInfo", pathHead.name,
X					pb.ioResult);
X			break;
X
X		} else {
X			/*
X			 * Add the directory to the archive,
X			 * while printing the files being added.
X			 */
X			if (WindInit())
X				goto done;
X
X			TextFace(underlineStyle);
X			WPrintf(header);
X			TextFace(0);
X			if ((cursor = GetCursor(watchCursor)) != NIL)
X				SetCursor(*cursor);
X
X			DumpDir(&pb, &pathHead);
X			SetCursor(&arrow);
X			WindEnd(autoPage);
X			FlushEvents(everyEvent, 0);
X		}
X
X	}
X
X	WriteEot();
Xdone:
X	CloseArchive();
X}
X
X/*
X * DumpDir - add a directory (possibly recursively) to the archive
X *
X *	Exits via a longjmp on unrecoverable error
X *	Returns normally otherwise
X */
XDumpDir(dir, path)
XCInfoPBRec	*dir;
XPathInfo	*path;
X{
X	union record	*header;
X	int		i;
X	CInfoPBRec	pb;
X	WDPBRec		wdpb;
X	PathInfo	file;
X	char		*routine = "\PDumpDir";
X	EventRecord	e;
X
X	/*
X	 * Output directory header record with permissions
X	 * FIXME, do this AFTER files, to avoid R/O dir problems?
X	 * If Unix Std format, don't put / on end of dir name
X	 * If old archive format, don't write record at all.
X	 */
X	if (!oldArch) {
X		/*
X		 * If people could really read standard archives,
X		 * this should be:		(FIXME)
X		 * header = start_header(f_standard? p: namebuf, statbuf);
X		 * but since they'd interpret LF_DIR records as
X		 * regular files, we'd better put the / on the name.
X		 */
X		header = StartHeader(dir);
X		if (header == NIL) {
X			longjmp(errJmp, 1);
X		}
X
X		if (standard) {
X			header->header.linkflag = LF_DIR;
X		}
X
X		FinishHeader(header);	/* Done with directory header */
X		head = header;
X		PrintHeader();
X	}
X
X	/*
X	 * Open this directory as a working directory so we can
X	 * access files without the whole pathname (which might be too long).
X	 */
X	file.next = NIL;
X	path->next = &file;
X	wdpb.ioCompletion = NIL;
X	wdpb.ioNamePtr = NIL;
X	wdpb.ioVRefNum = dir->ioVRefNum;
X	wdpb.ioWDProcID = 'TAR ';
X	wdpb.ioWDDirID = dir->u.di.ioDrDirID;
X	if (PBOpenWD(&wdpb, FALSE) != noErr) {
X		OSAlert(routine, "\PPBOpenWD", NIL, wdpb.ioResult);
X		longjmp(errJmp, 1);
X	}
X
X	/*
X	 * Check all entries in the directory.
X	 * Add regular files, recurse on subdirectories.
X	 */
X	pb.ioCompletion = NIL;
X	pb.ioNamePtr = file.name;
X	pb.ioVRefNum = wdpb.ioVRefNum;
X	for (i = 1; ; i++) {
X		SystemTask();
X		if (EventAvail(keyDownMask, &e) && (e.what != nullEvent)) {
X			if (
X				(e.modifiers & cmdKey) &&
X				((e.message & charCodeMask) == '.')
X			) {
X				GetNextEvent(keyDownMask, &e);
X				break;
X			}
X		}
X
X		pb.u.hfi.ioDirID = 0;
X		pb.ioFDirIndex = i;
X		if (PBGetCatInfo(&pb, FALSE) != noErr) {
X			if (pb.ioResult == fnfErr)
X				break;
X
X			OSAlert(routine, "\PPBGetCatInfo", NIL, pb.ioResult);
X			longjmp(errJmp, 1);
X		}
X
X		if ((unsigned char) file.name[0] > 32) {
X			/*
X			 * Sanity check, we have overwritten our stack!
X			 */
X			PgmAlert(routine, "\PName too long", file.name, NIL);
X			longjmp(errJmp, 1);
X		}
X
X		if (DIRECTORY(pb)) {
X			DumpDir(&pb, &file);
X
X		} else {
X			if (pb.ioFRefNum == archive) {
X				/*
X				 * DO NOT add the archive to itself!
X				 */
X				ArSkipAlert();
X				continue;
X			}
X
X			DumpFile(&pb, &file);
X		}
X	}
X
X	/*
X	 * Done with this directory, make sure we don't run out
X	 * of working directories.
X	 */
X	PBCloseWD(&wdpb, FALSE);
X	path->next = NIL;
X}
X
X/*
X * DumpFile - Dump a single file.
X *
X *	Exits via longjmp on unrecoverable error.
X *	Result is 1 for success, 0 for failure.
X */
XDumpFile(file, path)
XCInfoPBRec	*file;
XPathInfo	*path;
X{
X	union record	*header;
X	register char	*p;
X	char		*buf;
X	short		f;		/* File descriptor */
X	HPrmBlkRec	fpb;
X	long		bufsize, count, i;
X	register long	sizeleft;
X	register union record 	*start;
X	OSErr		err;
X	char		*routine = "\PDumpFile";
X
X	if ((header = StartHeader(file)) == NIL)
X		longjmp(errJmp, 1);
X
X	FinishHeader(header);
X	/*
X	 * Get the size of the file.
X	 * Don't bother opening it if it is zero length.
X	 */
X	head = header;
X	hstat.st_size = file->u.hfi.ioFlLgLen;
X	PrintHeader();
X	if ((sizeleft = file->u.hfi.ioFlLgLen) == 0)
X		return;
X
X	fpb.ioCompletion = NIL;
X	fpb.ioNamePtr = file->ioNamePtr;
X	fpb.ioVRefNum = file->ioVRefNum;
X	fpb.u.hfp.ioDirID = 0;
X	fpb.u.iop.ioPermssn = fsRdPerm;
X	fpb.u.iop.ioMisc = NIL;
X	if (PBHOpen(&fpb, FALSE) != noErr) {
X		OSAlert(routine, "\PPBHOpen", file->ioNamePtr, fpb.ioResult);
X		longjmp(errJmp, 1);
X	}
X
X	/*
X	 * Dump the file to the archive.
X	 * Note: this only dumps the data fork!
X	 */
X	while (sizeleft > 0) {
X		start = FindRec();
X		bufsize = EndOfRecs()->charptr - start->charptr;
X		buf = start->charptr;
X	again:
X		count = (sizeleft < bufsize) ? sizeleft : bufsize;
X		fpb.u.iop.ioBuffer = buf;
X		fpb.u.iop.ioReqCount = count;
X		fpb.u.iop.ioPosMode = fsAtMark;
X		fpb.u.iop.ioPosOffset = 0;
X		if (PBRead(&fpb, FALSE) != noErr) {
X			OSAlert(routine, "\PPBRead", file->ioNamePtr, fpb.ioResult);
X			longjmp(errJmp, 1);
X		}
X
X		count = fpb.u.iop.ioActCount;
X		if (cvtNl) {
X			/*
X			 * Convert returns to newlines for Unix compat.
X			 */
X			for (i = count, p = buf; --i >= 0; p++)
X				if (*p == '\r')
X					*p = '\n';
X		}
X
X		sizeleft -= count;
X		UseRec(start + (count - 1) / RECORDSIZE);
X	}
X	PBClose(&fpb, FALSE);
X
X	/* Clear last block garbage to zeros, FIXME */
X}
X
X
X/*
X * Make a header block for the file  name  whose stat info is  st .
X * Return header pointer for success, NULL if the name is too long.
X */
Xunion record *
XStartHeader(pb)
XCInfoPBRec	*pb;
X{
X	register union record *header;
X	Boolean	directory = DIRECTORY(*pb);
X
X	header = (union record *) FindRec();
X	bzero(header->charptr, sizeof(union record)); /* XXX speed up */
X	/*
X	 * Generate the pathname, make sure we don't overflow
X	 * the field in the tar header.
X	 */
X	if (FillName(header, directory)) {
X		char	buf[NAMSIZ + 1];
X
X		buf[0] = NAMSIZ;
X		bcopy(header->header.name, &buf[1], NAMSIZ);
X		PgmAlert("\PStartHeader", "\PName too long", buf, NIL);
X		return(NIL);
X	}
X
X	/*
X	 * Fake the file mode, uid, gid.
X	 * Convert from Mac based time to Unix based time.
X	 */
X	ToOct((directory) ? 0755L : 0644L, 8,  header->header.mode);
X	ToOct(0L, 8,  header->header.uid);
X	ToOct(0L, 8,  header->header.gid);
X	ToOct((directory) ? 0L : pb->u.hfi.ioFlLgLen, 1+12,
X			header->header.size);
X	ToOct((directory) ? pb->u.di.ioDrMdDat - TIMEDIFF :
X			    pb->u.hfi.ioFlMdDat - TIMEDIFF,
X			1+12, header->header.mtime);
X	/* header->header.linkflag is left as null */
X	return(header);
X}
X
X/* 
X * Finish off a filled-in header block and write it out.
X */
Xvoid
XFinishHeader(header)
Xregister union record *header;
X{
X	register int	i;
X	register long	sum;
X	register char	*p;
X
X	bcopy(CHKBLANKS, header->header.chksum, sizeof(header->header.chksum));
X
X	sum = 0;
X	p = header->charptr;
X	for (i = sizeof(union record); --i >= 0; ) {
X		/*
X		 * We can't use unsigned char here because of old compilers,
X		 * e.g. V7.
X		 */
X		sum += 0xFF & *p++;
X	}
X
X	/*
X	 * Fill in the checksum field.  It's formatted differently
X	 * from the other fields:  it has [6] digits, a null, then a
X	 * space -- rather than digits, a space, then a null.
X	 * We use to_oct then write the null in over to_oct's space.
X	 * The final space is already there, from checksumming, and
X	 * to_oct doesn't modify it.
X	 *
X	 * This is a fast way to do:
X	 * (void) sprintf(header->header.chksum, "%6o", sum);
X	 */
X	ToOct((long) sum, 8, header->header.chksum);
X	header->header.chksum[6] = '\0';	/* Zap the space */
X	UseRec(header);
X	return;
X}
X
X
X/*
X * Quick and dirty octal conversion.
X * Converts long "value" into a "digs"-digit field at "where",
X * including a trailing space and room for a null.  "digs"==3 means
X * 1 digit, a space, and room for a null.
X *
X * We assume the trailing null is already there and don't fill it in.
X * This fact is used by start_header and finish_header, so don't change it!
X *
X * This should be equivalent to:
X *	(void) sprintf(where, "%*lo ", digs-2, value);
X * except that sprintf fills in the trailing null and we don't.
X */
Xvoid
XToOct(value, digs, where)
Xregister long	value;
Xregister int	digs;
Xregister char	*where;
X{
X	--digs;				/* Trailing null slot is left alone */
X	where[--digs] = ' ';		/* Put in the space, though */
X
X	/* Produce the digits -- at least one */
X	do {
X		where[--digs] = '0' + (value & 7);	/* one octal digit */
X		value >>= 3;
X	} while (digs > 0 && value != 0);
X
X	/* Leading spaces, if necessary */
X	while (digs > 0)
X		where[--digs] = ' ';
X
X}
X
X/*
X * Write the EOT block(s).
X */
XWriteEot()
X{
X	union record *p;
X
X	p = FindRec();
X	bzero(p->charptr, RECORDSIZE);
X	UseRec(p);
X	/* FIXME, only one EOT block should be needed. */
X	p = FindRec();
X	bzero(p->charptr, RECORDSIZE);
X	UseRec(p);
X}
X
X/*
X * FillName - generate the file or directory pathname
X *
X *	Converts to Unix style pathnames.
X *	Appends a '/' for a directory.
X */
XBoolean
XFillName(header, directory)
Xregister union record *header;
XBoolean	directory;
X{
X	register PathInfo *p;
X	char *q;
X
X	q = header->header.name;
X	for (p = &pathHead; p != NIL; p = p->next) {
X		sprintf(q, "%.*s%c", p->name[0], &p->name[1],
X				 (p->next == NIL) ? '\0' : '/');
X
X		q += p->name[0] + 1;
X	}
X
X	if (directory) {
X		*(q - 1) = '/';
X		*q = '\0';
X	}
X
X	return((q - header->header.name) > NAMSIZ);
X}
SHAR_EOF
if test 10693 -ne "`wc -c < 'create.c'`"
then
	echo shar: error transmitting "'create.c'" '(should have been 10693 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'dialog.c'" '(7050 characters)'
if test -f 'dialog.c'
then
	echo shar: will not over-write existing file "'dialog.c'"
else
sed 's/^X//' << \SHAR_EOF > 'dialog.c'
X/*
X * Macintosh Tar
X *
X * The routines in this file deal with the dialogs presented to the user.
X *
X * Written by Craig Ruff.
X *
X * Note that the dialog dealing with directory selection is in dir.c.
X */
X
X#include "tar.h"
X#include <resource.h>
X#include <segment.h>
X
X/*
X * Dialog definitions
X */
X#define aboutID		128		/* The "About ..." dialog */
X
X#define blockID		129		/* The block size dialog */
X#define sizeItem	3
X
X#define typeID		131		/* Creator/Type dialog */
X#define creatorItem	3
X#define typeItem	4
X
X/*
X * Alert Definitions
X */
X#define badBlockID	129		/* Block size incorrect alert */
X#define osErrID		130		/* Generic OS Error alert */
X#define pgmErrID	131		/* Program logic error alert */
X#define hfsID		132		/* Must run under HFS alert */
X#define askipID		133		/* Skipping archive file note */
X#define stkErrID	134		/* Stack error alert */
X
X/*
X * AboutFilter - manage the "About ..." dialog
X *
X *	Returns when the mouse or a key is pressed anywhere.
X */
Xpascal Boolean
XAboutFilter(theDialog, theEvent, itemHit)
XDialogPtr	theDialog;
XEventRecord	*theEvent;
Xshort		*itemHit;
X{
X	switch (theEvent->what) {
X	case keyDown:
X	case mouseDown:
X		*itemHit = 1;
X		return(TRUE);
X		break;
X
X	default:
X		return(FALSE);
X		break;
X	}
X}
X
X/*
X * DoAboutBox - put the "About ..." dialog on the screen
X */
XDoAboutBox() {
X	int		itemHit;
X	WindowPtr	myWindow;
X
X	myWindow = GetNewDialog(aboutID, NIL, (WindowPtr) -1);
X	do {
X		ModalDialog(AboutFilter, &itemHit);
X	} while (itemHit != 1);
X	DisposDialog(myWindow);
X}
X
X/*
X * ValidBlockSize - checks the user entered blocksize for validity.
X *
X *	Returns TRUE if ok, FALSE if bad.
X */
XBoolean
XValidBlockSize(theDialog)
XDialogPtr	theDialog;
X{
X	long	n;
X	short	type;
X	Handle	hdl;
X	Rect	box;
X	char	text[256];
X
X	GetDItem(theDialog, sizeItem, &type, &hdl, &box);
X	GetIText(hdl, text);
X	StringToNum(text, &n);
X
X	/*
X	 * These limits are somewhat arbitrary.
X	 */
X	if ((n < 1) || (n > 128)) {
X		NoteAlert(badBlockID, NIL);
X		SelIText(theDialog, sizeItem, 0, 32767);
X		return(FALSE);
X	}
X
X	blocking = (int) n;
X	blockSize = blocking * RECORDSIZE;
X	return(TRUE);
X}
X
XRect		okBox;		/* ok Box location (gotten once for speed) */
XControlHandle	okHdl;		/* ok Box handle */
X
X/*
X * BlockFilter - manage user events during the block size dialog
X *
X *	Allows numeric entry and editing, validates size.
X *	Returns TRUE if the dialog should exit, FALSE to continue.
X */
Xpascal Boolean
XBlockFilter(theDialog, theEvent, itemHit)
XDialogPtr	theDialog;
XEventRecord	*theEvent;
Xshort		*itemHit;
X{
X	long	t;
X	Point	lpt;
X	GrafPtr	savedPort;
X
X	switch (theEvent->what) {
X	case keyDown:
X		switch ((char) (theEvent->message & charCodeMask)) {
X		case RETURN:
X		case ENTER:
X			goto validate;
X			break;
X
X		case '0': case '1': case '2': case '3': case '4':
X		case '5': case '6': case '7': case '8': case '9':
X		case TAB: case  BS:
X			break;
X
X		default:
X			SysBeep(5);
X			theEvent->what = nullEvent;
X			break;
X		}
X		break;
X
X	case mouseDown:
X		pass(lpt) = pass(theEvent->where);
X		GetPort(&savedPort);
X		SetPort((GrafPtr) theDialog);
X		GlobalToLocal(&lpt);
X		SetPort(savedPort);
X		if (PtInRect(pass(lpt), &okBox)) {
Xvalidate:		if (ValidBlockSize(theDialog)) {
X				*itemHit = OK;
X				HiliteControl(okHdl, 1);
X				Delay(8L, &t);
X				HiliteControl(okHdl, 0);
X				return(TRUE);
X			} else
X				theEvent->what = nullEvent;
X		}
X		break;
X	}
X
X	return(FALSE);
X}
X
X/*
X * CreatorTypeFilter - manage user events during the Creator/Type dialog
X *
X */
Xpascal Boolean
XCreatorTypeFilter(theDialog, theEvent, itemHit)
XDialogPtr	theDialog;
XEventRecord	*theEvent;
Xshort		*itemHit;
X{
X	short	type;
X	Handle	hdl;
X	Rect	box;
X	char	text[256];
X	int	n;
X	long	t;
X	Point	lpt;
X	GrafPtr	savedPort;
X
X	switch (theEvent->what) {
X	case keyDown:
X		switch ((char) (theEvent->message & charCodeMask)) {
X		case RETURN:
X		case ENTER:
X			goto done;
X			break;
X
X		default:
X			break;
X		}
X		break;
X
X	case mouseDown:
X		pass(lpt) = pass(theEvent->where);
X		GetPort(&savedPort);
X		SetPort((GrafPtr) theDialog);
X		GlobalToLocal(&lpt);
X		SetPort(savedPort);
X		if (PtInRect(pass(lpt), &okBox)) {
Xdone:
X			GetDItem(theDialog, creatorItem, &type, &hdl, &box);
X			GetIText(hdl, text);
X			n = (unsigned char) text[0];
X			if (n > 4)
X				n = 4;
X
X			strncpy(fdCreator, &text[1], n);
X			GetDItem(theDialog, typeItem, &type, &hdl, &box);
X			GetIText(hdl, text);
X			n = (unsigned char) text[0];
X			if (n > 4)
X				n = 4;
X
X			strncpy(fdType, &text[1], n);
X			*itemHit = OK;
X			HiliteControl(okHdl, 1);
X			Delay(8L, &t);
X			HiliteControl(okHdl, 0);
X			return(TRUE);
X		}
X		break;
X	}
X
X	return(FALSE);
X}
X
X/*
X * DoBlockSize - put the block size dialog on the screen
X *
X *	Puts the current block size into the edit field.
X */
XDoBlockSize() {
X	int		itemHit;
X	DialogPtr	theDialog;
X	Handle		hdl;
X	short		type;
X	Rect		box;
X	char		text[256];
X
X	theDialog = GetNewDialog(blockID, NIL, (WindowPtr) -1);
X	GetDItem(theDialog, sizeItem, &type, &hdl, &box);
X	NumToString((long) blocking, text);
X	SetIText(hdl, text);
X	SelIText(theDialog, sizeItem, 0, 32767);
X	GetDItem(theDialog, OK, &type, &okHdl, &okBox);
X	do {
X		ModalDialog(BlockFilter, &itemHit);
X	} while ((itemHit != OK) && (itemHit != Cancel));
X	DisposDialog(theDialog);
X}
X
X/*
X * DoCreatorType - put the set Creator/Type dialog on the screen
X *
X *	Puts the current Creator and Type into the edit field.
X */
XDoCreatorType() {
X	int		itemHit;
X	DialogPtr	theDialog;
X	Handle		hdl;
X	short		type;
X	Rect		box;
X	char		text[256];
X
X	theDialog = GetNewDialog(typeID, NIL, (WindowPtr) -1);
X	GetDItem(theDialog, creatorItem, &type, &hdl, &box);
X	text[0] = 4;
X	strncpy(&text[1], fdCreator, 4);
X	SetIText(hdl, text);
X	GetDItem(theDialog, typeItem, &type, &hdl, &box);
X	text[0] = 4;
X	strncpy(&text[1], fdType, 4);
X	SetIText(hdl, text);
X	SelIText(theDialog, creatorItem, 0, 32767);
X	GetDItem(theDialog, OK, &type, &okHdl, &okBox);
X	do {
X		ModalDialog(CreatorTypeFilter, &itemHit);
X	} while ((itemHit != OK) && (itemHit != Cancel));
X
X	DisposDialog(theDialog);
X}
X
X/*
X * OSAlert - put a generic OS Error Alert on the screen
X *
X *	Used for errors not handled in another way (yet).
X */
XOSAlert(p0, p1, p2, err)
Xchar	*p0, *p1, *p2;
XOSErr	err;
X{
X	char	buf[256];
X
X	/*
X	 * Manage the contingency of being called for resources missing!
X	 */
X	if (GetResource('ALRT', osErrID) == NIL) {
X		GrafPtr	wmPort;
X		long	t;
X		Rect	r;
X
X		GetWMgrPort(&wmPort);
X		SetPort(wmPort);
X		SetRect(&r, 140, 150, 360, 180);
X		EraseRect(&r);
X		MoveTo(150,170);
X		DrawString("\PPANIC! Resources Missing!");
X		Delay(600L, &t);
X		ExitToShell();
X	}
X
X	NumToString((long) err, buf);
X	ParamText(p0, p1, p2, buf);
X	StopAlert(osErrID, NIL);
X}
X
X/*
X * PgmAlert - put a program logic alert on the screen
X */
XPgmAlert(p0, p1, p2, p3)
Xchar	*p0, *p1, *p2, *p3;
X{
X	ParamText(p0, p1, p2, p3);
X	StopAlert(pgmErrID, NIL);
X}
X
X/*
X * HFSAlert - put a "HFS only" alert on the screen
X */
XHFSAlert() {
X	StopAlert(hfsID, NIL);
X}
X
X/*
X * ArSkipAlert - put a "skipping archive file" note on the screen
X */
XArSkipAlert() {
X	NoteAlert(askipID, NIL);
X}
X
X/*
X * StkErrAlert - put a stack corrupted alert on the screen
X */
XStkErrAlert() {
X	StopAlert(stkErrID, NIL);
X	ExitToShell();
X}
SHAR_EOF
if test 7050 -ne "`wc -c < 'dialog.c'`"
then
	echo shar: error transmitting "'dialog.c'" '(should have been 7050 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'dir.c'" '(11757 characters)'
if test -f 'dir.c'
then
	echo shar: will not over-write existing file "'dir.c'"
else
sed 's/^X//' << \SHAR_EOF > 'dir.c'
X/*
X * Macintosh Tar
X *
X * dir.c - routines dealing with directory selection
X *
X * Written by Craig Ruff
X */
X
X#include "tar.h"
X
X#define ROOTDIR		2		/* WATCH OUT!  Subject to change? */
X
X#define dirID		130		/* Directory selection dialog */
X#define selectItem	1		/* Use this directory */
X#define vnameItem	3		/* Volume name */
X#define boxItem		6		/* Directory list box */
X#define lineItem	7		/* User line item */
X#define upItem		8		/* Go to parent directory */
X#define	openItem	9		/* Open a subdirectory */
X#define driveItem	11		/* Select drive item */
X#define ejectItem	12		/* Eject disk item */
X
XListHandle	dirList;		/* Current directory list */
XRect		dirListRect;		/* Where is the list displayed? */
Xshort		dirVRefNum;		/* Selected directory VRefNum */
Xlong		dirDirID;		/* Selected directory DirID */
XRect		selectRect;		/* Rectangle of selectItem */
Xshort		volIndex;		/* Current volume index (for driveItem) */
XBoolean		ejectable;		/* Is the current volume ejectable? */
XPoint		lineLeft;
XPoint		lineRight;
X
X/*
X * DirFilter - handle events for the select directory dialog
X *
X *	Returns TRUE if button pressed, FALSE if to continue
X */
Xpascal Boolean
XDirFilter(theDialog, theEvent, itemHit)
XDialogPtr	theDialog;
XEventRecord	*theEvent;
Xshort		*itemHit;
X{
X	long	t;
X	Point	lpt;
X	GrafPtr	savedPort;
X
X	switch (theEvent->what) {
X	case keyDown:
X		/*
X		 * Default button for return or enter is Select
X		 */
X		if (((t = theEvent->message & charCodeMask) == RETURN) ||
X				(t == ENTER)) {
X			*itemHit = selectItem;
X			return(TRUE);
X		}
X		break;
X
X	case mouseDown:
X		/*
X		 * If we get a click in the directory list, send
X		 * it to the list manager, then pretend we got an
X		 * open button press.
X		 */
X		GetPort(&savedPort);
X		SetPort(theDialog);
X		pass(lpt) = pass(theEvent->where);
X		GlobalToLocal(&lpt);
X		SetPort(savedPort);
X		if (PtInRect(pass(lpt), &dirListRect)) {
X			if (LClick(pass(lpt), theEvent->modifiers, dirList)) {
X				*itemHit = openItem;
X				return(TRUE);
X			}
X			theEvent->what = nullEvent;
X		}
X		break;
X	}
X
X	return(FALSE);
X}
X
X/*
X * DirListProc - handle user defined redrawing of the dialog
X */
Xpascal
XDirListProc(theDialog, itemNo)
XDialogPtr	theDialog;
Xshort		itemNo;
X{
X	Rect		r;
X	PenState	pn;
X
X	/*
X	 * Highlight the default choice for return or enter, "Select".
X	 */
X	GetPenState(&pn);
X	PenSize(3, 3);
X	SetRect(&r, selectRect.left - 4, selectRect.top - 4,
X			selectRect.right + 4, selectRect.bottom + 4);
X	FrameRoundRect(&r, 16, 16);
X
X	/*
X	 * Draw the line separating Drive and Eject from Cancel and Open
X	 */
X	PenSize(1,1);
X	MoveTo(lineLeft.h, lineLeft.v);
X	LineTo(lineRight.h, lineRight.v);
X	SetPenState(&pn);
X
X	/*
X	 * Put a box around the directory list.
X	 * Do not erase the scroll bar on the right.
X	 * Then tell the list manager to update.
X	 */
X	SetRect(&r, dirListRect.left - 1, dirListRect.top - 1,
X			dirListRect.right + 1, dirListRect.bottom + 1);
X	FrameRect(&r);
X	SetRect(&r, dirListRect.left, dirListRect.top,
X			dirListRect.right - 15, dirListRect.bottom);
X	EraseRect(&r);
X	LUpdate(theDialog->visRgn, dirList);
X}
X
X/*
X * FillDirList - get all of the subdirectory names in this directory
X *
X *	Returns TRUE if an error was found, FALSE otherwise.
X */
XBoolean
XFillDirList(dir, parent, dname)
Xlong		dir;
Xlong		*parent;
Xchar		*dname;
X{
X	short		i, index;
X	CInfoPBRec	pb;
X	WDPBRec		wdpb;
X	char		name[64];
X	Point		cell;
X	char		*routine = "\PFillDirList";
X
X	/*
X	 * Open this directory to get the catalog info.
X	 */
X	wdpb.ioCompletion = NIL;
X	wdpb.ioNamePtr = NIL;
X	wdpb.ioVRefNum = dirVRefNum;
X	wdpb.ioWDProcID = 'TAR ';
X	wdpb.ioWDDirID = dir;
X	if (PBOpenWD(&wdpb, FALSE) != noErr) {
X		OSAlert(routine, "\PPBOpenWD", NIL, wdpb.ioResult);
X		return(TRUE);
X	}
X
X	/*
X	 * We will get this directory's name for the select button.
X	 */
X	pb.ioCompletion = NIL;
X	pb.ioNamePtr = dname;
X	pb.ioVRefNum = dirVRefNum;
X	pb.u.hfi.ioDirID = dir;
X	pb.ioFDirIndex = -1;
X	if (PBGetCatInfo(&pb, FALSE) != noErr) {
X		OSAlert(routine, "\PPBGetCatInfo(1)", NIL, pb.ioResult);
X		return(TRUE);
X	}
X
X	/*
X	 * Save the parent's DirID.
X	 * Loop until all directories have been found.
X	 */
X	*parent = pb.u.di.ioDrParID;
X	pb.ioNamePtr = name;
X	pb.ioVRefNum = wdpb.ioVRefNum;
X	for (i = 1, index = 0; ; i++) {
X		pb.u.hfi.ioDirID = 0;
X		pb.ioFDirIndex = i;
X		if (PBGetCatInfo(&pb, FALSE) != noErr) {
X			if (pb.ioResult == fnfErr)
X				break;
X
X			OSAlert(routine, "\PPBGetCatInfo(2)", NIL, pb.ioResult);
X			return(TRUE);
X		}
X
X		if (!DIRECTORY(pb))
X			continue;
X
X		/*
X		 * Add another row to the list if we are beyond the end
X		 * of the current list.
X		 * Remember the DirID for possible later selection.
X		 */
X		if (index >= (**dirList).dataBounds.bottom)
X			(void) LAddRow(1, index, dirList);
X
X		cell.h = 0;
X		cell.v = index++;
X		LSetCell(&name[1], (unsigned char) name[0], pass(cell), dirList);
X		cell.h = 1;
X		LSetCell(&pb.u.di.ioDrDirID, sizeof(long), pass(cell), dirList);
X	}
X
X	/*
X	 * Make sure we're not left in never-never land when
X	 * the directory list is redisplayed.
X	 * Free up any unused list rows.
X	 */
X	LScroll(-9999, -9999, dirList);
X	if (index < (**dirList).dataBounds.bottom)
X		LDelRow((**dirList).dataBounds.bottom - index, index, dirList);
X
X	PBCloseWD(&wdpb, FALSE);
X}
X
X/*
X * IsEjectable - check if a volume is ejectable
X *
X *	Returns TRUE or FALSE.
X *
X *	I think there should be an easier way to do this!  Like an additional
X *	flag in the GetVInfo call or something.
X */
XBoolean
XIsEjectable(volpb)
XHPrmBlkPtr	volpb;
X{
X	int		drvnum = volpb->u.hvp.ioVDrvInfo;
X	QHdrPtr		drvq;
X	struct DrvQEl	*drv;
X
X	/*
X	 * This volume is not in a drive!  Therefore is already ejected!
X	 */
X	if (drvnum == 0)
X		return(FALSE);
X
X	/*
X	 * Search the drive queue for our volume's drive.
X	 * Check the hidden flags.  Talk about hidden!
X	 */
X	drvq = GetDrvQHdr();
X	for (drv = (struct DrvQEl *) drvq->qHead;
X			drv != (struct DrvQEl *) drvq->qTail;
X			drv = drv->qLink)
X		if (drv->dQDrive == drvnum)
X			break;
X
X	if ((drv == (struct DrvQEl *) drvq->qTail) && (drv->dQDrive != drvnum))
X		return(FALSE);
X
X	return ((((long *) drv)[-1] & 0x80000) ? FALSE : TRUE);
X}
X
X#define SysMap	(*(short *) 0xa58)
X
X/*
X * DirInit - set up some stuff for the dialog
X *
X *	Returns TRUE if an error was found, FALSE otherwise.
X */
XDirInit(vName)
Xchar	*vName;
X{
X	char		*routine = "\PDirInit";
X	WDPBRec		pb;
X	HPrmBlkRec	volpb;
X	OSErr		err;
X
X	/*
X	 * Find out where we are currently.
X	 */
X	pb.ioCompletion = NIL;
X	pb.ioNamePtr = NIL;
X	if (PBHGetVol(&pb, FALSE) != noErr) {
X		OSAlert(routine, "\PPBHGetVol", NIL, pb.ioResult);
X		return(TRUE);
X	}
X
X	/*
X	 * Remember that, and figure out which volume index is ours.
X	 */
X	dirVRefNum = pb.ioWDVRefNum;
X	dirDirID = pb.ioWDDirID;
X	for (volIndex = 1; ; volIndex++) {
X		volpb.ioCompletion = NIL;
X		volpb.ioNamePtr = vName;
X		volpb.ioVRefNum = NIL;
X		volpb.u.hvp.ioVolIndex = volIndex;
X		if ((err = PBHGetVInfo(&volpb, FALSE)) != noErr) {
X			OSAlert(routine, "\PPBHGetVInfo", 
X				(err == nsvErr) ?
X					"\PCould not find default volume" :
X					NIL,
X				volpb.ioResult);
X			return(TRUE);
X		}
X
X		if (volpb.ioVRefNum == pb.ioWDVRefNum) {
X			ejectable = IsEjectable(&volpb);
X			break;
X		}
X	}
X
X	return(FALSE);
X}
X
X/*
X * GetNextVol - get the next volume in the volume list (for the drive button)
X *
X *	Returns the volume reference number.
X */
Xshort
XGetNextVol(vName)
Xchar	*vName;
X{
X	HPrmBlkRec	volpb;
X	int		found = FALSE;
X
X	do {
X		volpb.ioCompletion = NIL;
X		volpb.ioNamePtr = vName;
X		volpb.ioVRefNum = NIL;
X		volpb.u.hvp.ioVolIndex = ++volIndex;
X		PBHGetVInfo(&volpb, FALSE);
X		if (volpb.ioResult == nsvErr) {
X			/*
X			 * Reached the end of the list, start again.
X			 */
X			volIndex = 0;
X			continue;
X
X		}
X		
X		if (volpb.ioResult != noErr) {
X			OSAlert("\PGetNextVol", "\PPBHGetVInfo", NIL,
X					volpb.ioResult);
X			return(-32768);
X		}
X
X		if (volpb.u.hvp.ioVSigWord != 0x4244)
X			continue;	/* Ignore non-HFS volumes */
X
X		found = TRUE;
X		ejectable = IsEjectable(&volpb);
X	} while (!found);
X
X	return(volpb.ioVRefNum);
X}
X
X/*
X * GetDir - manage the directory selection dialog
X */
XBoolean
XGetDir(text)
Xchar	*text;
X{
X	short		itemHit;
X	DialogPtr	theDialog;
X	Handle		dhdl, vhdl;
X	ControlHandle	ehdl;
X	short		len, type;
X	Rect		dataBounds, r;
X	Point		cell, size;
X	long		parent;
X	GrafPtr		savedPort;
X	char		dirName[32], volName[32];
X	Boolean		selection;
X	char		*routine = "\PGetDir";
X	ParamBlkRec	pb;
X
X	/*
X	 * Read in the selection dialog.
X	 * Set the function specific message.
X	 */
X	theDialog = GetNewDialog(dirID, NIL, (WindowPtr) -1);
X	ParamText(text, NIL, NIL, NIL);
X
X	/*
X	 * Get the directory list information.
X	 * We have all the user item drawing done when the list is updated too.
X	 * The list boundaries are setup.  Leave room for the scroll bar.
X	 * Only one column of the list is displayed.  The other is used
X	 * to save the DirID.
X	 */
X	GetDItem(theDialog, boxItem, &type, &dhdl, &dirListRect);
X	SetDItem(theDialog, boxItem, type, (Handle) DirListProc, &dirListRect);
X	dataBounds.top = dataBounds.left = 0;
X	dataBounds.bottom = 0;
X	dataBounds.right = 2;
X	dirListRect.right -= 15;
X	size.h = dirListRect.right - dirListRect.left;
X	size.v = 0;
X	dirList = LNew(&dirListRect, &dataBounds, pass(size), 0, theDialog,
X			FALSE, FALSE, FALSE, TRUE);
X	if (dirList == NIL) {
X		PgmAlert(routine, "\PLNew NIL return", NIL, NIL);
X		itemHit = 0;
X		goto done;
X	}
X
X	/*
X	 * Ok, now include the scroll bar when the mouse is pressed (later).
X	 * Only allow a single directory to be selected at a time.
X	 */
X	dirListRect.right += 15;
X	(**dirList).selFlags = lOnlyOne;
X	if (DirInit(volName)) {
X		itemHit = 0;
X		goto done;
X	}
X
X	/*
X	 * Save the control handles for "Eject" and the volume name
X	 * for either enabling/disabling and name setting respectively.
X	 * Save the control handle for the "Select" button so the
X	 * current directory's name can be put into it.
X	 */
X	GetDItem(theDialog, lineItem, &type, &ehdl, &r);
X	lineLeft.h = r.left;
X	lineRight.v = lineLeft.v = r.top;
X	lineRight.h = r.right;
X	GetDItem(theDialog, ejectItem, &type, &ehdl, &r);
X	GetDItem(theDialog, vnameItem, &type, &vhdl, &r);
X	SetIText(vhdl, volName);
X	GetDItem(theDialog, selectItem, &type, &dhdl, &selectRect);
X	ShowWindow(theDialog);
X	cell.v = 0;
X
X	/*
X	 * Wait until either the user selects a directory or cancels.
X	 */
X	do {
X		/*
X		 * Don't update the list on the screen while filling it.
X		 * This looks sloppy.
X		 */
X		LDoDraw(FALSE, dirList);
X		if (FillDirList(dirDirID, &parent, dirName)) {
X			itemHit = 0;
X			goto done;
X		}
X
X		LDoDraw(TRUE, dirList);
X		SetCTitle((ControlHandle) dhdl, dirName);
X		HiliteControl(ehdl, ejectable ? 0 : 255);
X
X		/*
X		 * Make sure the directory list IS redrawn.
X		 */
X		GetPort(&savedPort);
X		SetPort(theDialog);
X		InvalRect(&theDialog->portRect);
X		SetPort(savedPort);
X
X		/*
X		 * No directory is selected initially.
X		 */
X		cell.h = 0;
X		LSetSelect(FALSE, pass(cell), dirList);
X		ModalDialog(DirFilter, &itemHit);
X		cell.v = 0;
X
X		/*
X		 * See if anything was really selected.
X		 */
X		selection =  LGetSelect(TRUE, &cell, dirList);
X		switch (itemHit) {
X		case openItem:
X			if (!selection)
X				break;
X
X			/*
X			 * Ok, use this directory now.
X			 */
X			cell.h = 1;
X			len = sizeof(dirDirID);
X			LGetCell(&dirDirID, &len, pass(cell), dirList);
X			break;
X
X		case upItem:
X			if (dirDirID == ROOTDIR)
X				break;
X
X			dirDirID = parent;
X			break;
X
X		case driveItem:
X		case ejectItem:
X			dirDirID = ROOTDIR;
X			if ((dirVRefNum = GetNextVol(volName)) == 0)
X				return(FALSE);
X
X			SetIText(vhdl, volName);
X			if (itemHit == driveItem)
X				break;
X
X			pb.ioCompletion = NIL;
X			pb.ioNamePtr = NIL;
X			pb.ioVRefNum = dirVRefNum;
X			if (PBEject(&pb, FALSE) != noErr)
X				OSAlert(routine, "\PPBEject", NIL, pb.ioResult);
X
X			break;
X		}
X	} while ((itemHit != selectItem) && (itemHit != Cancel));
X
Xdone:
X	LDispose(dirList);
X	DisposDialog(theDialog);
X	return((itemHit == selectItem) ? TRUE : FALSE);
X}
SHAR_EOF
if test 11757 -ne "`wc -c < 'dir.c'`"
then
	echo shar: error transmitting "'dir.c'" '(should have been 11757 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
--- end of part 2 ---
Technical Details
Message-ID: <7925@dhw68k.cts.com>
Path: arc1-ca.pugleaf.net!utzoo!attcan!uunet!seismo!sundc!pitstop!sun!decwrl!ucbvax!hplabs!felix!dhw68k!macintosh
References: <7889@dhw68k.cts.com>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!attcan!uunet!seismo!sundc!pitstop!sun!decwrl!ucbvax!hplabs!felix!dhw68k!macintosh
From: cruff@ncar.ucar.edu (Craig Ruff)
Newsgroups: comp.sources.mac
Subject: Tar 1.1 Source (part 2 of 3)
Message-ID: <7925@dhw68k.cts.com>
Date: 13 May 88 05:52:56 GMT
References: <7889@dhw68k.cts.com>
Sender: macintosh@dhw68k.cts.com
Organization: Scientific Computing Division/NCAR, Boulder CO
Lines: 1405
Approved: bytebug@dhw68k.cts.com (Roger L. Long)