/* File: PackageMgrPatches.c Contains: Routines which patch the Package Manager traps. Written by: Erich Ringewald Copyright: © 1986-1992 by Apple Computer, Inc., all rights reserved. Change History (most recent first): <3> 3/31/92 DCL Include StandardFile. Because it was removed from Packages.h <0> x/xx/86 ELR New Today. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Glue.h" #include "Lomem.h" #include "Data.h" #include "SysMisc.h" #include "Patches.h" #include "Puppet.h" /************************************************************************************ * Patch to SFGetFile/SFPGetFile (PACK3 selectors) for puppet string Open. This patch * is reached after we have told the application that there was a mousedown in the menu * bar, and then that the user selected the "Open…" item from the menu. The application * calls PACK3, which we patch to come here. Instead of the usual modal dialog, we * just return a specification for the file being puppet Open'd. Of course, we have to * go through the normal hoops, like checking the type list and calling the file filter. * NOTE: This only works with the older-style SFGetFiles that employ a Working Directory. * The newer FSSpec-based ones are not supported, since callers using them should also * be AppleEvent-aware, and therefore never require puppet-stringing. ************************************************************************************/ /* Some function prototypes that should be in (yet another) header file */ OSErr FileIDToAppFile(AETFDescriptor *, AppFile *, short *, long *); void NextListParam(AETFDescriptor **); /* Function prototypes for the application routines called by the SFGetFile handler. */ typedef pascal short (*DialogHookPtr) (short item, Ptr theDialog); typedef pascal Boolean (*FileFilterFncPtr) (FileParam *paramBlockPtr); /* We truncate the file names we hand back, since they are generated by puppet master. */ #define MAX_SF_FNAME_LEN (63) /* InTypeList. Check whether the puppet file is in the typelist given to SFGetFile. */ Boolean InTypeList(long fType, SFTypeList *pSFTypeList, short numTypes) { /* count of zero means all types are OK */ if (numTypes == (-1)) return(true); /* traverse the array until type matches or the count is exhausted */ while (--numTypes >= 0) if ((*pSFTypeList)[numTypes] == fType) return(true); /* Ze count he eez tired */ return(false); } /* AcceptedByFileFilter. Call the filter, and return whether the file is OK. */ short AcceptedByFileFilter(AppFile *pActiveDoc, Ptr fileFilterFnc) { CInfoPBRec myCInfoPBRec; unsigned long olda5; Boolean retval; if (fileFilterFnc == nil) return true; /* Get catalog info required by the filter */ myCInfoPBRec.dirInfo.ioNamePtr = &pActiveDoc->fName; myCInfoPBRec.dirInfo.ioVRefNum = pActiveDoc->vRefNum; myCInfoPBRec.dirInfo.ioFDirIndex = 0; myCInfoPBRec.dirInfo.ioDrDirID = 0; if (PBGetCatInfo(&myCInfoPBRec, SyncHFS) != noErr) return(false); /* Filter returns whether should be filtered! Our return value has inverse sense. */ olda5 = CurrentA5SimpleSetup(); retval = (CALL_FNC_PTR(FileFilterFncPtr, fileFilterFnc, (&myCInfoPBRec)) == false); A5SimpleRestore(olda5); return(retval); } /* CopyWDToCurProc. Given an existing Working Directory refnum, open a new WD for the * same directory. The new copy is needed because a) the puppet master might close the * original, and b) WDs are tagged by HFS to the process that opened them, and * closes them when the process goes away. */ void CopyWDToCurProc(short wdRefNumSrc, WDPBRec *pWDPB) { pWDPB->ioVRefNum = wdRefNumSrc; pWDPB->ioWDIndex = 0; pWDPB->ioWDProcID = 0; pWDPB->ioNamePtr = nil; pWDPB->ioCompletion = nil; PBGetWDInfo(pWDPB, SyncHFS); pWDPB->ioVRefNum = pWDPB->ioWDVRefNum; pWDPB->ioWDProcID = 0; pWDPB->ioNamePtr = nil; pWDPB->ioCompletion = nil; (void) PBOpenWD(pWDPB, SyncHFS); } /* IsForcedOpen. The main patch to SFGetFile. If the call to SFGetFile is to get a * puppet file opened, set up the puppet file specification. Returns whether this * was done. */ Boolean IsForcedOpen(Ptr fileFilterFnc, short numTypes, Ptr dlgHook, SFTypeList *pSFTypeList, SFReply *pSFReply, short dlgID) { AppFile *pActiveDoc; EventListPtr activeInstrsPtr; EventListHdl activeInstrsHdl; EventRecord *pActiveEvent; Boolean isForcedOpen; PuppetVars *myPuppetVarsPtr; MFmsgBlkPtr msgBlk; WDPBRec wdpbRec; AppFile appFile; short theVol; long theDir; long strLengthPlus1; DialogPtr pDialog; unsigned long olda5; isForcedOpen = false; olda5 = ProcessMgrA5SimpleSetup(); if (InstrsQueueIsEmpty() == false) if ((activeInstrsHdl = GetActiveInstrsHdl()) != nil) { HLock(activeInstrsHdl); activeInstrsPtr = *activeInstrsHdl; /* Since we really look at the instruction preceding the current one */ assert((unsigned long)activeInstrsPtr->offset >= (SIZEOF_EVENTLIST_HDR + sizeof(EventRecord))); pActiveEvent = ((Ptr) activeInstrsPtr) + (unsigned long)activeInstrsPtr->offset; pActiveEvent--; if (*((char *)&pActiveEvent->what + 1) == app4Evt && (*((char *)&pActiveEvent->message) == instrOpen)) { isForcedOpen = true; /* Make pActiveDoc->vRefNum a valid WD for the current process. Copy * it from current WD, or open it from translation vrefnum/dirid. */ myPuppetVarsPtr = &pCurrentProcess->p_puppetvars; if ((msgBlk = myPuppetVarsPtr->puppetEppcMsgBlk) == nil) { pActiveDoc = ((Ptr) activeInstrsPtr) + (pActiveEvent->message & LO3BYTES); CopyWDToCurProc(pActiveDoc->vRefNum, &wdpbRec); } else { pActiveDoc = nil; if (FileIDToAppFile((AETFDescriptor *) (msgBlk->addrOfMsg), &appFile, &theVol, &theDir) == noErr) { NextListParam((AETFDescriptor **) &msgBlk->addrOfMsg); wdpbRec.ioNamePtr = nil; wdpbRec.ioVRefNum = theVol; wdpbRec.ioWDProcID = 0; wdpbRec.ioWDDirID = theDir; wdpbRec.ioCompletion = nil; (void) PBOpenWD(&wdpbRec, SyncHFS); appFile.vRefNum = wdpbRec.ioVRefNum; pActiveDoc = &appFile; } } pSFReply->vRefNum = wdpbRec.ioVRefNum; if ( (pActiveDoc != nil) && (InTypeList(pActiveDoc->fType, pSFTypeList, numTypes) && AcceptedByFileFilter(pActiveDoc, fileFilterFnc)) ) { /* Call word dialog hook to clear r/o flag */ if (dlgHook != nil) { /* Fake out word <= 3.01 to not make the doc r/o by giving it an extra init call */ pDialog = GetNewDialog(dlgID, nil, (Ptr)(-1)); if (pDialog) { (void) CurrentA5SimpleSetup(); CALL_FNC_PTR(DialogHookPtr, dlgHook, (-1, pDialog)); (void) ProcessMgrA5SimpleSetup(); DisposDialog(pDialog); } } pSFReply->good = true; pSFReply->fType = pActiveDoc->fType; pSFReply->version = pActiveDoc->versNum; if ((strLengthPlus1 = Length(&(pActiveDoc->fName)) + 1) > (MAX_SF_FNAME_LEN + 1)) strLengthPlus1 = MAX_SF_FNAME_LEN + 1; BlockMove(&pActiveDoc->fName, &pSFReply->fName, strLengthPlus1); } else { /* Tell sawdust-head that the PACK 3 failed */ pSFReply->good = false; /* Shouldn't cancel, and needn't close WD, if the file translation failed */ if (pActiveDoc != nil) { /* Close the WD, if we opened it */ if (wdpbRec.ioWDCreated != 0) PBCloseWD(&wdpbRec, SyncHFS); /* Force back to offending instruction, then cancel */ activeInstrsPtr->offset -= sizeof(EventRecord); CancelSwitchWithError(); } } } HUnlock(activeInstrsHdl); } A5SimpleRestore(olda5); return(isForcedOpen); }