Path: 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) [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 X#include 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 ---