Article View: comp.sources.mac
Article #45Tar 1.1 Source (part 2 of 3)
From: Craig Ruff
Date: Fri, 13 May 1988 05:52
Date: Fri, 13 May 1988 05:52
1405 lines
34213 bytes
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!macintoshReferences:
<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)