mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-29 20:49:19 +00:00
1928 lines
64 KiB
C
1928 lines
64 KiB
C
|
/*
|
|||
|
File: Processes.c
|
|||
|
|
|||
|
Contains: Process Mgr creation/deletion routines.
|
|||
|
|
|||
|
Written by: Phil Goldman and Erich Ringewald
|
|||
|
|
|||
|
Copyright: © 1986-1993 by Apple Computer, Inc., all rights reserved.
|
|||
|
|
|||
|
Change History (most recent first):
|
|||
|
|
|||
|
<SM55> 7/21/93 joe Back out <SM54>.
|
|||
|
<55> 12/4/92 DRF Only support stub 'cfri' resource consisting of a single 32-bit
|
|||
|
zero. This special case indicates launch a PowerPC main code
|
|||
|
fragment living in the datafork. In the future, we will support
|
|||
|
more complicated 'cfri' structures indicated by a non-zero value
|
|||
|
in the first 32-bit word.
|
|||
|
<54> 11/25/92 DRF Roll-in changes for PsychicTV. CreateProcess() now calls CFM to
|
|||
|
create a native runtime context for every process. Native
|
|||
|
applications are also detected via the presence of a 'cfri' (0)
|
|||
|
resource, and launched with a mini-A5 world. In ExitToShell(),
|
|||
|
any connections to system fragments (DLLs), or native resources
|
|||
|
are released. All changes are conditionalized under "PsychicTV".
|
|||
|
<53> 10/28/92 DTY Use new Get/Set macros to access ExpandMem.
|
|||
|
<52> 8/26/92 DTY Roll in latest changes for A/UX.
|
|||
|
<51> 5/29/92 DCL Included Script.h. iuSystemScript moved there for the New Inside
|
|||
|
Mac.
|
|||
|
<50> 3/23/92 JSM OSEvents.h is obsolete, use Events.h.
|
|||
|
<49> 2/18/92 DTY #1021226: Switch emMessageManagerGlobals again.
|
|||
|
<48> 1/14/92 YK Take out <46> since we found a better way. But <47> is still
|
|||
|
there.
|
|||
|
<47> 1/11/92 YK Make tsmLayerOwner nil if the dying process is it. (for TSM)
|
|||
|
<46> 1/10/92 YK Allocate enough stack space to the Service Window Manager app
|
|||
|
(for TSM) even though it is the background only app. (because it
|
|||
|
owns windows.)
|
|||
|
<45> 11/22/91 DTY Set the allocateGMBlkInSysHeap semaphore around the
|
|||
|
PostHighLevelEvent in SendDeathNotice to force the event to be
|
|||
|
allocated in the system heap instead of the Process Manager
|
|||
|
heap. This is a fix from 7-Up.
|
|||
|
<44> 10/23/91 DTY Use new _GetOverrideMap call instead of calling
|
|||
|
GetTopSystemOverrideMap.
|
|||
|
<43> 10/20/91 DTY New resource chains should start at the topmost System Override
|
|||
|
map, not SysMapHndl. Call GetTopSystemOverrideMap to get this
|
|||
|
map.
|
|||
|
<42> 10/9/91 YK Added code to dispose float layer in c_exittoshell.
|
|||
|
<41> 10/4/91 JSM Change PsychoticFarmerOrLater to TheFuture.
|
|||
|
<40> 9/22/91 DTY Change PsychoticFarmerAndLater to PsychoticFarmerOrLater.
|
|||
|
<39> 9/20/91 DFH Conditionalize initialization of emMessageManagerGlobals based
|
|||
|
on PsychoticFarmerAndLater.
|
|||
|
<38> 9/16/91 DFH Changed CreateProcess to initialize new process' SR from the SR
|
|||
|
we found at startup, rather than a hard-coded 0x2000. Needed for
|
|||
|
NuKernel.
|
|||
|
<37> 7/26/91 stb replace HOpenResFileTrap with HOpenResFile. No glue is used
|
|||
|
when SystemSevenOrLater.
|
|||
|
<36> 6/4/91 DFH Zero emMessageManagerGlobals in InitLowMemory.
|
|||
|
<35> 5/23/91 dba do a style of coercion that MPW 3.2 C likes
|
|||
|
<34> 4/9/91 DFH VL, WS#BB-KA-034 : ExitToShell now checks QDExist before calling
|
|||
|
InitCursor. Caused system crash when running application that
|
|||
|
quits before InitGraf (such as Giffer). application on a MacIIci
|
|||
|
or MacIIfx.
|
|||
|
<33> 4/2/91 DFH (with John Iarocci) Added version check for MicroSoft Word
|
|||
|
workaround. NOTE: This only affects the code when it is built
|
|||
|
with the HAS_AUX_PROCESSMGR condition defined.
|
|||
|
<32> 4/2/91 DFH VL,#86163: Fixed CreateProcess to not be vulnerable to
|
|||
|
purgeability of the DA Handler CODE 0 resource. If purged,
|
|||
|
LaunchDeskAccessory gets appMemFullErr, since we get garbage
|
|||
|
when trying to fetch a5 world info from the resource.
|
|||
|
<31> 3/27/91 DFH JWM,#84862: Fixed CreateProcess to look at result of
|
|||
|
HOpenResFile, rather than at RESERR, to deceide whether the call
|
|||
|
succeeded. RESERR can be set even if file got opened (e.g. when
|
|||
|
there is not enough room for the preload resources).
|
|||
|
<30> 3/25/91 DFH JSM,#DFH-910325a: Fix CreateProcess to deal with the fact that
|
|||
|
fabricated Apple Event is in an unlocked handle.
|
|||
|
<29> 3/21/91 DFH tes,#84989: Fixed pointer math in FMKill change #28.
|
|||
|
<28> 3/20/91 DFH gbm,#84989: Added MakeSynStrikesPurgeable.
|
|||
|
<27> 3/1/91 DFH KST,WS#DFH-910301a: Fixed GetFinderInfoForFile to extract script
|
|||
|
code correctly. Was setting the high byte to 0xFF.
|
|||
|
<26> 2/26/91 DFH JWM,WS#DFH-910226c: Fixed NewProcess to make sure pBabyProc is
|
|||
|
set before jumping to ErrorRecovery after failed
|
|||
|
NewProcessEntryClear.
|
|||
|
<25> 2/21/91 DFH dba,#82504, #82681, #83168, #83182, #83207: HiNewHandle rolled
|
|||
|
into ProcessMgrHiNewHandle. CreateProcess now calls the latter
|
|||
|
instead of the former.
|
|||
|
<24> 2/18/91 DFH BAC,#83089, #82873:Fix CreateProcess so that DA Handler's CODE 0
|
|||
|
can remain unlocked, but is not susceptible to movement.
|
|||
|
Currently, HOpenResFile is called after we deref the handle for
|
|||
|
later use.
|
|||
|
<23> 2/1/91 DFH BAC,WS#DFH-910131c:aeApplicationDied keyword parameter with the
|
|||
|
OSErr is now the 'errn', rather than 'errv'.
|
|||
|
<22> 1/31/91 DFH NGK,WS#DFH-910131b:Changed ExitToShell to adjust the application
|
|||
|
menu for the incoming front process. Copes with applications
|
|||
|
that call DrawMenuBar before we fix up the menu at the end of
|
|||
|
the foreground switch.
|
|||
|
<21> 1/28/91 DFH JSM,#81425: Removed inhibit_DAs mechanism.
|
|||
|
<20> 1/21/91 DFH (KSM) Process Menu is now called Application Menu.
|
|||
|
<19> 1/14/91 DFH (dba) Make system mode per process.
|
|||
|
<18> 1/14/91 DFH (JDR) Conditionalize out AUX support.
|
|||
|
<17> 1/10/91 DFH (JDR) Add hack to prevent HyperCard 1.2.5 patch to SysBeep.
|
|||
|
<16> 1/8/91 DFH (rdd) Got rid of pErrantProcess since it is no longer needed.
|
|||
|
<15> 12/18/90 DFH Removed fgAppsUnexecuted mechanism, since front switch queue
|
|||
|
works better.
|
|||
|
<14> 12/17/90 DFH Change NewProcess to always initialize the SCRAPHANDLE to an
|
|||
|
zero length handle, and let CS_INIT give the process the
|
|||
|
correct, up-to-date scrap.
|
|||
|
<13> 12/15/90 DFH OrphanScrapIntoPartition no longer has a parameter.
|
|||
|
<12> 12/14/90 DFH (SMC) Remove PMgrExit workaround in ExitToShell. Was stuffing
|
|||
|
HEAPEND around CleanupApplication, since PMgrExit used it to
|
|||
|
figure out which palettes to dispose. Now, PMgrExit uses
|
|||
|
ApplZone->bkLim like it should.
|
|||
|
<11> 12/10/90 DFH Adding support for custom icons (i.e. the ones the user can
|
|||
|
specify).
|
|||
|
<10> 12/5/90 DFH Integrated AUX support.
|
|||
|
<9> 11/27/90 DFH Removed unneeded parameter from cpu_resched.
|
|||
|
<8> 11/26/90 DFH Change trespass() to dbmsg().
|
|||
|
<7> 11/21/90 DFH Changed InitHeapZone to use ROUTINE_ADDR of MyGrowZone so that
|
|||
|
jump table is bypassed.
|
|||
|
<6> 11/9/90 DFH Added CancelSleep() to c_exittoshell in case ExitToShell was
|
|||
|
called when process was sleeping, e.g. by dsForcedQuit handler
|
|||
|
or MacsBug. Otherwise, sleep queue is hosed.
|
|||
|
<5> 11/6/90 DFH Renamed emAppleEventsGlobal to emAppleEvents.
|
|||
|
<4> 11/1/90 DFH Put workaround in ExitToShell for PMgrExit using HEAPEND for
|
|||
|
limit check. Remove when PMgrExit fixed. Unconditionalized
|
|||
|
edition mgr lomem switch.
|
|||
|
<2> 10/30/90 csd Fixed some identifiers to match the newest interfaces.
|
|||
|
<0> 9/2/86 PYG New Today.
|
|||
|
|
|||
|
*/
|
|||
|
|
|||
|
#pragma segment kernel_segment
|
|||
|
|
|||
|
#include <types.h>
|
|||
|
#include <memory.h>
|
|||
|
#include <files.h>
|
|||
|
#include <quickdraw.h>
|
|||
|
#include <windows.h>
|
|||
|
#include <menus.h>
|
|||
|
#include <events.h>
|
|||
|
#include <resources.h>
|
|||
|
#include <osutils.h>
|
|||
|
#include <script.h>
|
|||
|
#include <Notification.h>
|
|||
|
#include <retrace.h>
|
|||
|
#include <segload.h>
|
|||
|
#include <desk.h>
|
|||
|
#include <dialogs.h>
|
|||
|
#include <errors.h>
|
|||
|
#include <shutdown.h>
|
|||
|
#include <EPPC.h>
|
|||
|
#include <MFPrivate.h>
|
|||
|
#include <Icons.h>
|
|||
|
#include <AppleEvents.h>
|
|||
|
#include <AppleEventsInternal.h>
|
|||
|
#include <ExpandMemPriv.h>
|
|||
|
#include <ResourceMgrPriv.h>
|
|||
|
|
|||
|
#include "Glue.h"
|
|||
|
#include "Lomem.h"
|
|||
|
#include "Data.h"
|
|||
|
#include "SysMisc.h"
|
|||
|
#include "Patches.h"
|
|||
|
#include "HList.h"
|
|||
|
#include "Aux.h"
|
|||
|
#include "Puppet.h"
|
|||
|
#include "Zone.h"
|
|||
|
#include "OSDispatchPrivate.h"
|
|||
|
#include "AppleEventExtensions.h"
|
|||
|
|
|||
|
#if PsychicTV
|
|||
|
#include "MixedMode.h" /* <54> Some new interfaces from NativeProj */
|
|||
|
#include "CodeFragments.h"
|
|||
|
#include "CodeFragmentsTrap.h"
|
|||
|
#endif
|
|||
|
|
|||
|
/* Some function prototypes that should be in (yet another) header file */
|
|||
|
void GetProcessIconCache(PEntryPtr);
|
|||
|
void AdjustApplicationMenu(PEntryPtr pTitleProc);
|
|||
|
void DeleteAppFromApplicationMenu(PEntryPtr);
|
|||
|
void AppleMenuCleanup(PEntryPtr);
|
|||
|
Handle GetMapHdlFromRefNum(short, Handle);
|
|||
|
OSErr GetDrvrResourceAndInfo(StringPtr, Handle *, short *, Str255 *);
|
|||
|
OSErr CheckUnitTableEntry(short);
|
|||
|
OSErr MakeHidingString(PEntryPtr, StringPtr);
|
|||
|
void InitPuppets(PEntryProc);
|
|||
|
void SetAppParameters(PCB **, unsigned long, InternalLaunchPBPtr, Boolean);
|
|||
|
void kill_state(PEntryPtr);
|
|||
|
void PuppetKill(void), QDKill(void), VNKill(void);
|
|||
|
void save_lomem(PEntryPtr);
|
|||
|
|
|||
|
/* Function prototypes internal to this file */
|
|||
|
void InitLowMemory(FSSpecPtr);
|
|||
|
Ptr GetCeilingPtr(PEntryPtr pPEntry);
|
|||
|
OSErr NewProcessEntryClear(PEntryPtr *);
|
|||
|
void DisposeProcessEntry(PEntryPtr);
|
|||
|
OSErr GetProcessMemory(PEntryPtr, Ptr);
|
|||
|
void DisposeProcessMemory(PEntryPtr);
|
|||
|
OSErr CreateProcess(InternalLaunchPBPtr, PEntryPtr, PEntryPtr, u_long *);
|
|||
|
OSErr InitHeapZone(PEntryPtr);
|
|||
|
void GetFinderInfoForFile(FSSpec *, PEntryPtr);
|
|||
|
void HandleShellDeath(PEntryPtr, OSErr, Boolean);
|
|||
|
void MenuKill(void), FMKill(Boolean), NMKill(void);
|
|||
|
|
|||
|
#if PsychicTV
|
|||
|
/* <54> Function Prototypes for CodeFragmentMgr/MixedMode Specific Stuff */
|
|||
|
|
|||
|
Boolean FindFragmentEntryPoint(FSSpec *theAppFSSpec,
|
|||
|
FragmentLocatorPtr mainFragment,
|
|||
|
OSType *architecture);
|
|||
|
OSErr SetupPsychicTVProcess(PEntryPtr pNewProc,
|
|||
|
Boolean launchingFragment,
|
|||
|
FragmentLocatorPtr mainFragment,
|
|||
|
OSType archictecture,
|
|||
|
RoutineDescriptor *nativeEntryPoint);
|
|||
|
void TearDownPsychicTVProcess(PEntryPtr theProc);
|
|||
|
#endif
|
|||
|
|
|||
|
/*---------------------------------------------------------------------------------*
|
|||
|
Process Creation Routines
|
|||
|
*---------------------------------------------------------------------------------*/
|
|||
|
|
|||
|
/* Process heap and stack control */
|
|||
|
#define TOPROOM (0x0400) /* APPLLIMIT during launch */
|
|||
|
#define APPZONESIZE (0x1800) /* minimum app heap size at launch */
|
|||
|
#define DAEMON_APPZONESIZE (0x1000) /* minimum BG only app heap size at launch */
|
|||
|
#define DAEMON_STACKSIZE (0x800) /* max stack size for BG only app */
|
|||
|
|
|||
|
/* NewProcessEntryClear. Make a new process desriptor. Zeroes out the entire entry, then
|
|||
|
* allocates a ProcessID and ProcessSerialNumber.
|
|||
|
*/
|
|||
|
OSErr
|
|||
|
NewProcessEntryClear(PEntryPtr *ppProc)
|
|||
|
{
|
|||
|
register ProcessID pid;
|
|||
|
PEntryPtr retProc, *hProc;
|
|||
|
|
|||
|
/* safety first! */
|
|||
|
*ppProc = nil;
|
|||
|
|
|||
|
/* Allocate the PEntry high and lock it down */
|
|||
|
if ((hProc = ProcessMgrHiNewHandle(sizeof(PEntry), nil)) == nil)
|
|||
|
return(memFullErr);
|
|||
|
HLock(hProc);
|
|||
|
|
|||
|
/* Clear PEntry, then fill in preliminary information */
|
|||
|
retProc = (char *) StripAddress(*hProc);
|
|||
|
MemClear(retProc, sizeof(PEntry));
|
|||
|
retProc->p_HandleForThisPEntry = hProc;
|
|||
|
|
|||
|
/* Allocate a ProcessID (handles wrap-around gracefully). */
|
|||
|
pid = ProcessIDSource;
|
|||
|
do
|
|||
|
{
|
|||
|
if ((--pid) <= 0)
|
|||
|
pid = MAX_PROCESSID;
|
|||
|
}
|
|||
|
while (PEntryFromPID(pid) != nil);
|
|||
|
|
|||
|
retProc->p_mypid = ProcessIDSource = pid;
|
|||
|
retProc->p_serialNumber.lowLongOfPSN = ProcessNumberSource++;
|
|||
|
retProc->p_serialNumber.highLongOfPSN = 0;
|
|||
|
#ifdef DEBUG
|
|||
|
retProc->p_tattoo = BRAND;
|
|||
|
#endif DEBUG
|
|||
|
|
|||
|
/* Set output param and return value */
|
|||
|
*ppProc = retProc;
|
|||
|
return(noErr);
|
|||
|
}
|
|||
|
|
|||
|
/* DisposeProcessEntry. Opposite of NewProcessEntryClear. */
|
|||
|
void
|
|||
|
DisposeProcessEntry(PEntryPtr pDeadProc)
|
|||
|
{
|
|||
|
#ifdef DEBUG
|
|||
|
pDeadProc->p_tattoo = 0;
|
|||
|
#endif DEBUG
|
|||
|
DisposHandle(pDeadProc->p_HandleForThisPEntry);
|
|||
|
}
|
|||
|
|
|||
|
/* Flag to disable app gz proc of launcher while launchees lomem is current */
|
|||
|
Boolean inLauncheesWorld = false;
|
|||
|
|
|||
|
/* NewProcess. Create the new process with a world size of reqSize bytes, and a
|
|||
|
* stack size of ssize bytes.
|
|||
|
*/
|
|||
|
OSErr
|
|||
|
NewProcess(InternalLaunchPBPtr pParams)
|
|||
|
{
|
|||
|
register PEntryPtr pBabyProc, pLauncherProc;
|
|||
|
FSSpecPtr pFileSpec;
|
|||
|
PEntryPtr pProcOnStack;
|
|||
|
PCB *pPCB, **hOldPCB;
|
|||
|
Handle rsrcHdl;
|
|||
|
short err;
|
|||
|
u_long init_appzonesize, currenta5;
|
|||
|
AppParametersPtr pAEParm;
|
|||
|
Ptr msgAddr;
|
|||
|
|
|||
|
#ifdef HAS_AUX_PROCESSMGR
|
|||
|
/* Sanity check */
|
|||
|
if ((pParams->ilTaskMode & COFF_BINARY) != 0)
|
|||
|
assert(AUXIsPresent);
|
|||
|
#endif HAS_AUX_PROCESSMGR
|
|||
|
|
|||
|
/* Don't call gz proc of launcher (pCurrentProcess) */
|
|||
|
inLauncheesWorld = true;
|
|||
|
pLauncherProc = pCurrentProcess;
|
|||
|
|
|||
|
/* allocate a process list entry */
|
|||
|
err = NewProcessEntryClear(&pProcOnStack);
|
|||
|
if ((pBabyProc = pProcOnStack) == nil)
|
|||
|
goto ErrorRecovery;
|
|||
|
|
|||
|
/* Determine process dimensions */
|
|||
|
pBabyProc->p_size = pParams->ilPartitionSize;
|
|||
|
LONG_ALIGN(pBabyProc->p_size);
|
|||
|
if ((pParams->ilTaskMode & modeOnlyBackground) != 0)
|
|||
|
{
|
|||
|
init_appzonesize = DAEMON_APPZONESIZE;
|
|||
|
pBabyProc->p_ssize = DAEMON_STACKSIZE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
init_appzonesize = APPZONESIZE;
|
|||
|
pBabyProc->p_ssize = pParams->ilStackSize;
|
|||
|
}
|
|||
|
|
|||
|
pBabyProc->p_taskmode = pParams->ilTaskMode;
|
|||
|
pBabyProc->p_haseditcmdkeys = (-1); /* Have not yet gone through CS_INIT */
|
|||
|
pBabyProc->p_wakeuptime = TICKS; /* Reset the wakeup placeholder */
|
|||
|
|
|||
|
#ifdef HAS_AUX_PROCESSMGR
|
|||
|
/* Initialize osInfo to prevent random switches for real Macintosh applications */
|
|||
|
if (AUXIsPresent)
|
|||
|
pBabyProc->aux_realpid = AUX_ProcessMgrTask();
|
|||
|
#endif HAS_AUX_PROCESSMGR
|
|||
|
|
|||
|
/* Set signature, type */
|
|||
|
pFileSpec = pParams->ilAppSpecPtr;
|
|||
|
if ((pBabyProc->p_taskmode & modeDeskAccessory) == 0)
|
|||
|
GetFinderInfoForFile(pFileSpec, pBabyProc);
|
|||
|
|
|||
|
/* Save the launcher's state so we can restore after generating launchee's state */
|
|||
|
save_state(pLauncherProc);
|
|||
|
|
|||
|
/* Go get Excel's version number to determine whether we have to kludge
|
|||
|
* the partition allocation.
|
|||
|
* NOTE: Sets THEZONE = SYSZONE so map goes into impartial heap.
|
|||
|
* NOTE: Assumes that the PEntry was zeroed, since we haven't set the p_signature
|
|||
|
* (or p_type) if we're launching a DA.
|
|||
|
* NOTE: Assumes that the resource chain changes resulting from HOpenResFile and
|
|||
|
* CloseResFile will not be reflected in the caller's state, since our save_state
|
|||
|
* (which just happened) and restore_state will take care of the relevant info.
|
|||
|
*/
|
|||
|
if (pBabyProc->p_signature == 'XCEL' || pBabyProc->p_signature == 'nX^n')
|
|||
|
{
|
|||
|
THEZONE = SYSZONE;
|
|||
|
SetResLoad(false);
|
|||
|
if (HOpenResFile(pFileSpec->vRefNum, pFileSpec->parID, &pFileSpec->name, fsRdPerm) != (-1))
|
|||
|
{
|
|||
|
SetResLoad(true);
|
|||
|
if ((rsrcHdl = Get1Resource('VERS', 0)) != nil)
|
|||
|
pBabyProc->p_version = **((unsigned long **) rsrcHdl);
|
|||
|
CloseResFile(CurResFile());
|
|||
|
}
|
|||
|
SetResLoad(true);
|
|||
|
/* At this point THEZONE == SYSZONE */
|
|||
|
}
|
|||
|
|
|||
|
/* Big enough zone requested? */
|
|||
|
if (pParams->ilPartitionSize < (init_appzonesize + pBabyProc->p_ssize))
|
|||
|
{
|
|||
|
err = appMemFullErr;
|
|||
|
goto ErrorRecovery;
|
|||
|
}
|
|||
|
|
|||
|
/* try allocating the physical memory partition for this process
|
|||
|
* NOTE: Side effect is to set THEZONE to ProcessMgrZone.
|
|||
|
*/
|
|||
|
if ((err = GetProcessMemory(pBabyProc, GetCeilingPtr(pBabyProc)) ) != noErr)
|
|||
|
goto ErrorRecovery;
|
|||
|
|
|||
|
/* At this point THEZONE == ProcessMgrZone */
|
|||
|
|
|||
|
/* initialize the portions of low memory which need it.
|
|||
|
* NOTE: CURAPNAME for a DA will be set later when we know for sure what the
|
|||
|
* the driver name is.
|
|||
|
*/
|
|||
|
InitLowMemory(pFileSpec);
|
|||
|
|
|||
|
/* create the application heap zone low in the partition (sets THEZONE) */
|
|||
|
if ((err = InitHeapZone(pBabyProc)) != noErr)
|
|||
|
goto ErrorRecovery;
|
|||
|
|
|||
|
/* At this point THEZONE == pBabyProc->p_zone */
|
|||
|
|
|||
|
/* Set the top of the resource chain back down to the system file. This causes
|
|||
|
* subsequent resource file opens to create a new chain.
|
|||
|
*/
|
|||
|
TOPMAPHANDLE = GetOverrideMap(SYSMAPHANDLE);
|
|||
|
|
|||
|
/* At this point, an ExitToShell should (safely) cleanup launchee, not launcher. So,
|
|||
|
* set pCurrentProcess to new PEntry. This means that any tracking will be against
|
|||
|
* launchee (such as the FCB for CURAPREFNUM).
|
|||
|
*/
|
|||
|
pCurrentProcess = pBabyProc;
|
|||
|
|
|||
|
InitAllPacks();
|
|||
|
|
|||
|
/* Initialize auxiliary information in the PCB */
|
|||
|
pPCB = *pBabyProc->p_pcb;
|
|||
|
pPCB->p_vrefnum = 0;
|
|||
|
pPCB->launchDate = TICKS;
|
|||
|
|
|||
|
InitApplPatchTable(&pPCB->applPatchTable, &pPCB->applPatchCount);
|
|||
|
if ((err = MEMERROR) != noErr)
|
|||
|
goto ErrorRecovery;
|
|||
|
|
|||
|
/* load resources, set up A5 world and stack, and fill in the PEntry some more. */
|
|||
|
if ((err = CreateProcess(pParams, pBabyProc, pLauncherProc, ¤ta5)) != noErr)
|
|||
|
goto ErrorRecovery;
|
|||
|
|
|||
|
/* Create custom string for hide item of Application Menu */
|
|||
|
if ((err = MakeHidingString(pBabyProc, CURAPNAME)) != noErr)
|
|||
|
goto ErrorRecovery;
|
|||
|
|
|||
|
/********************************************************************************
|
|||
|
* NOTE: There is no error handling from here on out.
|
|||
|
********************************************************************************/
|
|||
|
|
|||
|
/* Get the application icon */
|
|||
|
GetProcessIconCache(pBabyProc);
|
|||
|
|
|||
|
/* Set up initial scrap info. The correct scrap will get moved in later during
|
|||
|
* CS_INIT in Coercion_State_Engine when the process is brought to the front.
|
|||
|
* NOTE: This HandToHand's the current SCRAPHANDLE from the launcher to the
|
|||
|
* launchee because there any app combos that depend on this direct inheritance
|
|||
|
* as a covert means of communication. However, they don't need it after the
|
|||
|
* launchee's first event call (since the scrap can change there anyway). Strictly
|
|||
|
* a compatibility thing.
|
|||
|
*/
|
|||
|
if ((pBabyProc->p_taskmode & modeDeskAccessory) == 0)
|
|||
|
{
|
|||
|
if (SCRAPHANDLE != nil)
|
|||
|
(void) HandToHand(&SCRAPHANDLE);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
SCRAPINFO = 0;
|
|||
|
SCRAPSTATE = 1;
|
|||
|
SCRAPHANDLE = NewHandleSys(0);
|
|||
|
}
|
|||
|
|
|||
|
/* Get version longword and release it. This non-standard version resource is
|
|||
|
* a special deal for MicroSoft applications so they can call TWGetProcInfo.
|
|||
|
*/
|
|||
|
if ((rsrcHdl = (long **) GetResource('VERS', 0)) != nil)
|
|||
|
{
|
|||
|
pBabyProc->p_version = **((long **) rsrcHdl);
|
|||
|
ReleaseResource(rsrcHdl);
|
|||
|
}
|
|||
|
else
|
|||
|
pBabyProc->p_version = 0L;
|
|||
|
|
|||
|
/* Check whether this application is HyperCard 1.2.5. If so, we need to prevent
|
|||
|
* it from patching SysBeep, since its patch breaks horribly under System 7.0 and
|
|||
|
* later. Remove this when HyperCard 1.2.5 is no longer supported.
|
|||
|
*/
|
|||
|
if ((pBabyProc->p_signature == 'WILD') &&
|
|||
|
((rsrcHdl = (long **) Get1Resource('vers', 1)) != nil))
|
|||
|
{
|
|||
|
if (**((short **) rsrcHdl) <= 0x125)
|
|||
|
pBabyProc->p_preventFirstSysBeepPatch = true;
|
|||
|
ReleaseResource(rsrcHdl);
|
|||
|
}
|
|||
|
|
|||
|
/* Set up puppet string handling */
|
|||
|
InitPuppets(pBabyProc);
|
|||
|
|
|||
|
/* Get Event PPC resource and create message queues */
|
|||
|
CreateMsgQ(pBabyProc);
|
|||
|
|
|||
|
/* Handle any necessary APPPARMHANDLE/ilAppParameters conversion. */
|
|||
|
SetAppParameters(pBabyProc->p_pcb, currenta5, pParams, pBabyProc->eppcBlk.portID != 0);
|
|||
|
|
|||
|
#ifdef HAS_AUX_PROCESSMGR
|
|||
|
/* Fix bug in Microsoft Word. If A/UX is running they don't try to grow
|
|||
|
* the zone and then don't believe they have any memory available. Historical
|
|||
|
* braindamage that's really because they don't use the MaxApplZone, but
|
|||
|
* instead manually scribble a 24-bit style heap block at the end to artificially
|
|||
|
* grow the zone. That doesn't work on A/UX.
|
|||
|
*/
|
|||
|
if (AUXIsPresent && pBabyProc->p_signature == 'MSWD' && pBabyProc->p_version <= '0400')
|
|||
|
MaxApplZone();
|
|||
|
#endif HAS_AUX_PROCESSMGR
|
|||
|
|
|||
|
/* fork caller's low memory into createe's PCB. */
|
|||
|
save_lomem(pBabyProc);
|
|||
|
|
|||
|
/* Copy the following info into PCB directly, rather than through low memory, so
|
|||
|
* that VBL tasks of the calling process will not ever access the wrong values.
|
|||
|
* Stklowpt is set with knowledge that return address INITRET is already on the stack.
|
|||
|
* NOTE: The PMSPHOOK lomem is NIL'd by the call to save_state(pLauncherProc), to
|
|||
|
* make sure the hook is not used while lomem is in transition. For the launchee to
|
|||
|
* correctly inherit the launcher's value, we must copy the saved value from the
|
|||
|
* launcher's PCB. This is normally fine. It gets tricky when the caller has no
|
|||
|
* PCB (!), such as pNullProcess launching Finder. The solution is to leave PMSPHOOK
|
|||
|
* alone in this case. Since there is no PCB, the context save didn't have to alter
|
|||
|
* the lomem. Therefore, the save_lomem(pBabyProc) just above copied the correct value.
|
|||
|
*/
|
|||
|
pPCB = *pBabyProc->p_pcb;
|
|||
|
pPCB->currenta5 = currenta5;
|
|||
|
pPCB->stklowpt = CURSTACKBASE - sizeof(Ptr);
|
|||
|
pPCB->fsqueuehook = (void (**)())initFSQueueHook;
|
|||
|
if ((hOldPCB = pLauncherProc->p_pcb) != nil)
|
|||
|
{
|
|||
|
assert(pLauncherProc != pNullProcess);
|
|||
|
pPCB->pmsphook = (*hOldPCB)->pmsphook;
|
|||
|
}
|
|||
|
|
|||
|
/* Restore caller's state */
|
|||
|
pCurrentProcess = nil;
|
|||
|
(void)restore_state(pLauncherProc, true);
|
|||
|
pCurrentProcess = pLauncherProc;
|
|||
|
inLauncheesWorld = false;
|
|||
|
|
|||
|
/* Send any necessary AppleEvents. Free the message if SetAppParameters made it. */
|
|||
|
pAEParm = pParams->ilAppParameters;
|
|||
|
if (pParams->ilAppParamHdl != nil)
|
|||
|
{
|
|||
|
HLock(pParams->ilAppParamHdl);
|
|||
|
pAEParm = *pParams->ilAppParamHdl;
|
|||
|
}
|
|||
|
if (pAEParm != nil)
|
|||
|
{
|
|||
|
msgAddr = (pAEParm->messageLength == 0) ? nil : ((Ptr) pAEParm + sizeof(AppParameters));
|
|||
|
(void) BeginSystemMode();
|
|||
|
(void) PostHighLevelEvent(&(pAEParm->theMsgEvent), (u_long) &pBabyProc->p_serialNumber, pAEParm->eventRefCon, msgAddr, pAEParm->messageLength, receiverIDisPSN);
|
|||
|
(void) EndSystemMode();
|
|||
|
}
|
|||
|
if (pParams->ilAppParamHdl != nil)
|
|||
|
DisposHandle(pParams->ilAppParamHdl);
|
|||
|
|
|||
|
/* Return output parameters to caller */
|
|||
|
pParams->ilResultProc = pBabyProc;
|
|||
|
return(noErr);
|
|||
|
|
|||
|
/* Common code to do the cleanup after a fatal error occurred (err==reason) */
|
|||
|
ErrorRecovery:
|
|||
|
|
|||
|
if (pBabyProc != nil)
|
|||
|
{
|
|||
|
pCurrentProcess = nil;
|
|||
|
DisposeProcessMemory(pBabyProc);
|
|||
|
(void)restore_state(pLauncherProc, true);
|
|||
|
pCurrentProcess = pLauncherProc;
|
|||
|
DisposeProcessEntry(pBabyProc);
|
|||
|
}
|
|||
|
|
|||
|
inLauncheesWorld = false;
|
|||
|
pParams->ilResultProc = nil;
|
|||
|
return(err);
|
|||
|
}
|
|||
|
|
|||
|
/* userret. The address of this routine is placed on the process' stack as the return
|
|||
|
* address of the main routine, so it is entered when a process exits that routine.
|
|||
|
*/
|
|||
|
void
|
|||
|
userret(void)
|
|||
|
{
|
|||
|
ExitToShell();
|
|||
|
}
|
|||
|
|
|||
|
/* CreateProcess. Given a name and a PEntryPtr describing a valid address space, make
|
|||
|
* the given process ready to run.
|
|||
|
* NOTE: Assumes that PEntry and PCB were zeroed when created, so fields that are
|
|||
|
* supposed to be init'd to zero need no attention.
|
|||
|
*/
|
|||
|
OSErr
|
|||
|
CreateProcess(InternalLaunchPBPtr pParams, PEntryPtr pNewProc, PEntryPtr pLauncherProc, u_long *pCurrenta5)
|
|||
|
{
|
|||
|
OSErr err;
|
|||
|
u_long currenta5;
|
|||
|
Ptr curStackEnd, saveApplLimit;
|
|||
|
u_size minappheapsize;
|
|||
|
struct code0 *pCode0;
|
|||
|
FSSpecPtr pFileSpec;
|
|||
|
Handle drvrHdl;
|
|||
|
short resourceID;
|
|||
|
short daFileRefNum;
|
|||
|
#ifdef HAS_AUX_PROCESSMGR
|
|||
|
static struct code0 coffSegInfo =
|
|||
|
{
|
|||
|
32, /* above a5 */
|
|||
|
sizeof(qd), /* below a5 */
|
|||
|
0, /* jtSize */
|
|||
|
32 /* jtOffset */
|
|||
|
};
|
|||
|
#endif HAS_AUX_PROCESSMGR
|
|||
|
#if PsychicTV
|
|||
|
static struct code0 fakeCode0 = /* <54> This should be combined with A/UX stuff, */
|
|||
|
{ /* but the #ifdefs don’t seem to be working out… */
|
|||
|
32, /* above a5 */
|
|||
|
sizeof(qd), /* below a5 */
|
|||
|
0, /* jtSize */
|
|||
|
32 /* jtOffset */
|
|||
|
};
|
|||
|
Boolean launchingFragment = false;
|
|||
|
FragmentLocator mainFragLocation;
|
|||
|
OSType architecture;
|
|||
|
RoutineDescriptor nativeEntryPoint;
|
|||
|
#endif
|
|||
|
|
|||
|
if ((pNewProc->p_taskmode & modeDeskAccessory) != 0)
|
|||
|
{
|
|||
|
/* Get the DA resource file.
|
|||
|
* NOTE: We turn off ResLoad because we want the DA Handler to load the
|
|||
|
* resource. We set THEZONE to SYSZONE so that pre-load resources will
|
|||
|
* get empty handles in the SYSZONE.
|
|||
|
*/
|
|||
|
SetResLoad(false);
|
|||
|
THEZONE = SYSZONE;
|
|||
|
pFileSpec = (pParams->ilDAInfoPtr)->daFileSpec;
|
|||
|
(*pNewProc->p_pcb)->p_fileRefNum = daFileRefNum = HOpenResFile(pFileSpec->vRefNum,
|
|||
|
pFileSpec->parID, &pFileSpec->name, fsCurPerm);
|
|||
|
|
|||
|
/* Fix up CURAPNAME, since we know it now. Make sure table entry is OK to use. */
|
|||
|
err = RESERR;
|
|||
|
if (daFileRefNum != (-1))
|
|||
|
if ((err = GetDrvrResourceAndInfo((pParams->ilDAInfoPtr)->daDrvrName, &drvrHdl, &resourceID, CURAPNAME)) == noErr)
|
|||
|
err = CheckUnitTableEntry(resourceID);
|
|||
|
|
|||
|
THEZONE = APPLZONE;
|
|||
|
SetResLoad(true);
|
|||
|
UseResFile(0);
|
|||
|
|
|||
|
/* Try for DAHandler's segment loader information resource */
|
|||
|
if (err == noErr)
|
|||
|
{
|
|||
|
CURAPREFNUM = SYSMAP;
|
|||
|
if ((SAVESEGHANDLE = GetResource(SYS_SEGMENT_TYPE, DAH_SEGMENT_ZERO)) == nil)
|
|||
|
if ((err = RESERR) == noErr)
|
|||
|
err = resNotFound;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle error opening the file, getting the driver, or checking the DCE. We
|
|||
|
* can close the file only if it was opened above the system file.
|
|||
|
*/
|
|||
|
if (err != noErr)
|
|||
|
{
|
|||
|
if ( (daFileRefNum != (-1)) && (GetMapHdlFromRefNum(daFileRefNum, SYSMAPHANDLE) == nil) )
|
|||
|
CloseResFile(daFileRefNum);
|
|||
|
return(err);
|
|||
|
}
|
|||
|
|
|||
|
/* Get address of segment loader info, since we got it. */
|
|||
|
pCode0 = *SAVESEGHANDLE;
|
|||
|
|
|||
|
/* Set signature, type, and name script code */
|
|||
|
GetFinderInfoForFile(pFileSpec, pNewProc);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Open the app file (read-only if the app is being multilaunched) */
|
|||
|
pFileSpec = pParams->ilAppSpecPtr;
|
|||
|
CURAPREFNUM = HOpenResFile(pFileSpec->vRefNum, pFileSpec->parID, &pFileSpec->name,
|
|||
|
((pNewProc->p_taskmode & modeMultiLaunch) != 0) ? fsRdPerm : fsCurPerm);
|
|||
|
if (((*pNewProc->p_pcb)->p_fileRefNum = CURAPREFNUM) == (-1))
|
|||
|
return(RESERR);
|
|||
|
|
|||
|
#ifdef HAS_AUX_PROCESSMGR
|
|||
|
/* Try to load the initial code segment. Not available or needed for coff binaries. */
|
|||
|
if (AUXIsPresent && (pNewProc->p_taskmode & COFF_BINARY) != 0)
|
|||
|
{
|
|||
|
pCode0 = &coffSegInfo;
|
|||
|
SAVESEGHANDLE = nil;
|
|||
|
}
|
|||
|
else
|
|||
|
#endif HAS_AUX_PROCESSMGR
|
|||
|
|
|||
|
#if PsychicTV
|
|||
|
/* <54> Find out if there is a main code fragment to launch */
|
|||
|
if ((launchingFragment = FindFragmentEntryPoint(pFileSpec,&mainFragLocation,&architecture)))
|
|||
|
{
|
|||
|
pCode0 = &fakeCode0;
|
|||
|
SAVESEGHANDLE = nil;
|
|||
|
}
|
|||
|
else
|
|||
|
#endif
|
|||
|
{
|
|||
|
if ((SAVESEGHANDLE = GetResource('CODE', 0)) == nil)
|
|||
|
{
|
|||
|
if ((err = RESERR) == noErr)
|
|||
|
err = resNotFound;
|
|||
|
CloseResFile(CURAPREFNUM);
|
|||
|
return(err);
|
|||
|
}
|
|||
|
|
|||
|
pCode0 = *SAVESEGHANDLE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* really enough room for min heap, a5 world, and stack, at least? */
|
|||
|
minappheapsize = ((pNewProc->p_taskmode & modeOnlyBackground) != 0) ? DAEMON_APPZONESIZE : APPZONESIZE;
|
|||
|
if (pNewProc->p_size < (minappheapsize + pCode0->abovea5 + pCode0->belowa5 + pNewProc->p_ssize) )
|
|||
|
{
|
|||
|
if (CURAPREFNUM != SYSMAP)
|
|||
|
CloseResFile(CURAPREFNUM);
|
|||
|
if ((pNewProc->p_taskmode & modeDeskAccessory) != 0)
|
|||
|
CloseResFile(daFileRefNum);
|
|||
|
return(appMemFullErr);
|
|||
|
}
|
|||
|
|
|||
|
#if PsychicTV
|
|||
|
if ((err = SetupPsychicTVProcess(pNewProc,launchingFragment,&mainFragLocation,architecture,&nativeEntryPoint)) != noErr)
|
|||
|
{
|
|||
|
DebugStr("\pSetup Failed");
|
|||
|
CloseResFile(CURAPREFNUM);
|
|||
|
return(err);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
/* Setup a5, curjtoffset, stackbase, and lowpt based on CODE 0 info. */
|
|||
|
currenta5 = (unsigned long)pNewProc->p_zone + pNewProc->p_size - pCode0->abovea5;
|
|||
|
CURJTOFFSET = (unsigned short)pCode0->jtoffset;
|
|||
|
CURSTACKBASE = curStackEnd = (Ptr) ((unsigned long)currenta5 - pCode0->belowa5);
|
|||
|
|
|||
|
/* Push return address onto his stack a la JSR, so that an RTS from his main
|
|||
|
* routine dumps him into our handler. Then, push the real first routine, so
|
|||
|
* that InitializeProcess() returns to it.
|
|||
|
*/
|
|||
|
*--(Ptr *)curStackEnd = INITRET;
|
|||
|
#if PsychicTV
|
|||
|
/* <54> If we’re launching a fragment, push it’s main entry point instead of jumptable entry */
|
|||
|
|
|||
|
if (launchingFragment)
|
|||
|
*--(Ptr *)curStackEnd = nativeEntryPoint; // a pointer to a private_RD that points to the native entry point
|
|||
|
else
|
|||
|
#endif
|
|||
|
*--(Ptr *)curStackEnd = (Ptr)(currenta5 + pCode0->jtoffset + 2); // first jump table entry
|
|||
|
|
|||
|
/* copy jump table from resource to its home in the app's a5 world */
|
|||
|
BlockMove(pCode0->jt, (Ptr)currenta5 + pCode0->jtoffset, pCode0->jtsize);
|
|||
|
|
|||
|
/* Now that things are set up, we can release segment 0 */
|
|||
|
if (SAVESEGHANDLE != nil)
|
|||
|
ReleaseResource(SAVESEGHANDLE);
|
|||
|
|
|||
|
/* Try to set APPLLIMIT to ssize below current stack. We get an error if this
|
|||
|
* would be below the current HEAPEND. This can happen if the application heap
|
|||
|
* had to be expanded after we initialized it. Essentially, it means that the
|
|||
|
* specified partition was not big enough. We could give the guy a substandard
|
|||
|
* stack size (i.e. a higher APPLLIMIT), but prefer not to.
|
|||
|
* NOTE: SetApplLimit has a bug wherein APPLLIMIT is set even if an error is
|
|||
|
* returned, causing the heap globals to be bad. We therefore try to restore the
|
|||
|
* value if the set failed. That should always work. Even if it doesn't we can
|
|||
|
* probably still back out OK, since all we'll do in the heap is dispose blocks.
|
|||
|
*/
|
|||
|
saveApplLimit = APPLLIMIT;
|
|||
|
SetApplLimit((Ptr) ((u_long) curStackEnd - pNewProc->p_ssize));
|
|||
|
if (MEMERROR != noErr)
|
|||
|
{
|
|||
|
SetApplLimit(saveApplLimit);
|
|||
|
if (CURAPREFNUM != SYSMAP)
|
|||
|
CloseResFile(CURAPREFNUM);
|
|||
|
return(appMemFullErr);
|
|||
|
}
|
|||
|
|
|||
|
/* Set up registers for first context switch in */
|
|||
|
{
|
|||
|
curStackEnd -= sizeof(SaveRegs); /* To leave room for regs on the stack */
|
|||
|
|
|||
|
((SaveRegs *)curStackEnd)->d2 = 0; /* Because MS Works assumes this to be so */
|
|||
|
((SaveRegs *)curStackEnd)->a6 = (unsigned long) SCRATCH20;
|
|||
|
/* NOTE:
|
|||
|
* This is always supposed to be a safe value.
|
|||
|
* MacWrite has the following code at init:
|
|||
|
* link a6,#$fff0 ; make room for buffer
|
|||
|
* move.l (a6),a0 ; should be move.l a6,a0
|
|||
|
* ...
|
|||
|
* _SysEnvirons ; a0 supposed to point to buffer
|
|||
|
*/
|
|||
|
((SaveRegs *)curStackEnd)->a5 = currenta5;
|
|||
|
((SaveRegs *)curStackEnd)->sr = initialProcessSR;
|
|||
|
((SaveRegs *)curStackEnd)->pc = InitializeProcess;
|
|||
|
pNewProc->p_sp = (unsigned long)curStackEnd;
|
|||
|
}
|
|||
|
|
|||
|
pNewProc->p_dadProcess = pLauncherProc;
|
|||
|
if ((pNewProc->p_taskmode & modeCanBackground) != 0)
|
|||
|
{
|
|||
|
if ((pNewProc->p_taskmode & (modeNeedSuspendResume | modeDoesActivateOnFGSwitch))
|
|||
|
== (modeNeedSuspendResume | modeDoesActivateOnFGSwitch))
|
|||
|
pNewProc->p_isfirstbackevt = true;
|
|||
|
|
|||
|
if (nbacks++ == 0)
|
|||
|
pLastBGProcess = pNewProc;
|
|||
|
}
|
|||
|
|
|||
|
/* Now that the entire thing is set up, claim the PEntry */
|
|||
|
pNewProc->p_state = PRREADY;
|
|||
|
pNewProc->p_NextProcess = pProcessList;
|
|||
|
pProcessList = pNewProc;
|
|||
|
|
|||
|
DSERRCODE = noErr;
|
|||
|
|
|||
|
/* Success! Set the output parameter and leave. */
|
|||
|
*pCurrenta5 = currenta5;
|
|||
|
return(noErr);
|
|||
|
}
|
|||
|
|
|||
|
/* InitHeapZone. This routine will initialize the heap zone for the given process.
|
|||
|
* NOTE: A side effect is to set THEZONE == pProc->p_zone.
|
|||
|
*/
|
|||
|
OSErr
|
|||
|
InitHeapZone(PEntryPtr pProc)
|
|||
|
{
|
|||
|
/* turn his allocated world into a zone. */
|
|||
|
InitZone(ROUTINE_ADDR(MyGrowZone), 64, (Ptr)pProc->p_zone + (((pProc->p_taskmode & modeOnlyBackground) != 0) ? DAEMON_APPZONESIZE : APPZONESIZE), pProc->p_zone);
|
|||
|
if (MEMERROR != noErr)
|
|||
|
return(MEMERROR);
|
|||
|
|
|||
|
/* Mark heap delimiters in lomem, set applzone there. Our new zone will be
|
|||
|
* the `application zone,' so we can perform memory manager calls which
|
|||
|
* manipulate it.
|
|||
|
*/
|
|||
|
HIHEAPMARK = HEAPEND = (APPLZONE = THEZONE)->bkLim;
|
|||
|
|
|||
|
/* Heap end is now the nominal APPZONESIZE (approx 6K, 4k for daemons).
|
|||
|
* Set a reasonable appl limit (Top of world less 'TOPROOM'). This
|
|||
|
* gives us a provisional heap until we get the CODE 0 resource loaded
|
|||
|
* to determine where the real APPLLIMIT should go.
|
|||
|
* NOTE: Launch will fail if our allocations in the app heap cause the
|
|||
|
* HEAPEND to move higher than the real APPLLIMIT we set later.
|
|||
|
*/
|
|||
|
SetApplLimit((Ptr)pProc->p_zone + pProc->p_size - TOPROOM);
|
|||
|
if (MEMERROR != noErr)
|
|||
|
return (MEMERROR);
|
|||
|
|
|||
|
/* Set application's memtop as if partition were located low in memory
|
|||
|
* NOTE: Since Process Mgr makes SYSZONE->bklim a *variable*, the exact
|
|||
|
* value we give MEMTOP is somewhat irrelevant.
|
|||
|
*/
|
|||
|
MEMTOP = SYSZONE->bkLim + pProc->p_size;
|
|||
|
|
|||
|
return(noErr);
|
|||
|
}
|
|||
|
|
|||
|
/* InitLowMemory. This routine initializes the low memory for a fledgling process for
|
|||
|
* those portions that cannot be inherited from one's parent.
|
|||
|
*/
|
|||
|
void
|
|||
|
InitLowMemory(FSSpecPtr pAppFileSpec)
|
|||
|
{
|
|||
|
register long zero = 0, minusOne = (-1);
|
|||
|
|
|||
|
/* turn off stack sniffing whilst hacking on heaps... */
|
|||
|
STKLOWPT = (char *)zero;
|
|||
|
|
|||
|
SEVTENB = minusOne;
|
|||
|
|
|||
|
DSDRAWPROC = (void (**)())minusOne;
|
|||
|
EJECTNOTIFY = (void (**)())zero;
|
|||
|
IAZNOTIFY = (void (**)())zero;
|
|||
|
|
|||
|
/* Set current layer using inherited window mgr and qd state */
|
|||
|
SetCurLayer(MFLayer);
|
|||
|
WWEXIST = minusOne;
|
|||
|
QDEXIST = minusOne;
|
|||
|
|
|||
|
/* Set CURAPNAME, but make sure to limit the size (MFS names can be bigger) */
|
|||
|
if (pAppFileSpec != nil)
|
|||
|
{
|
|||
|
BlockMove(&pAppFileSpec->name, CURAPNAME, sizeof(Str31));
|
|||
|
if (Length(CURAPNAME) > sizeof(Str31) - 1)
|
|||
|
Length(CURAPNAME) = sizeof(Str31) - 1;
|
|||
|
}
|
|||
|
|
|||
|
SetExpandMemAppleEvents(zero);
|
|||
|
SetExpandMemScriptAppGlobals((Ptr) zero);
|
|||
|
SetExpandMemEditionMgrPerApp((Ptr) zero);
|
|||
|
SetExpandMemMessageManagerGlobals(0, zero);
|
|||
|
|
|||
|
DESKHOOK = (void (**)())zero;
|
|||
|
CLOSEORNHOOK = (void (**)())zero;
|
|||
|
DRAGHOOK = (void (**)())zero;
|
|||
|
|
|||
|
RESUMEPROC = (void (**)())zero;
|
|||
|
|
|||
|
TASKLOCK = zero;
|
|||
|
FSCALEDISABLE = zero;
|
|||
|
|
|||
|
RESERRPROC = (void (**)())zero;
|
|||
|
MENULIST = (Handle)zero;
|
|||
|
|
|||
|
LASTSPEXTRA = minusOne;
|
|||
|
WIDTHTABHANDLE = (Handle)zero;
|
|||
|
WIDTHLISTHAND = (Handle)zero;
|
|||
|
if (Colorized)
|
|||
|
{
|
|||
|
FMEXIST = minusOne;
|
|||
|
SYNLISTHANDLE = (Handle)zero;
|
|||
|
MENUCINFO = (Handle)minusOne;
|
|||
|
LAYERPALETTE = (Handle)zero;
|
|||
|
}
|
|||
|
|
|||
|
MACPGM = zero; /* Don't confuse the runtime systems */
|
|||
|
SWITCHDATA = (Ptr)zero; /* Convince every one that switcher isn't running */
|
|||
|
SWITCHEDBITS = (char)minusOne;
|
|||
|
}
|
|||
|
|
|||
|
#define ONE_K (1024)
|
|||
|
#define ONE_MEG_PTR ((Ptr)0x100000)
|
|||
|
#define MIN_EXCEL_K_ABOVE_1MEG (100) /* In K units */
|
|||
|
#define MAX_EXCEL_K_ABOVE_1MEG (525) /* In K units */
|
|||
|
#define MIN_EXCEL_K_PARTITION_SIZE (224) /* In K units */
|
|||
|
#define MAX_EXCEL_K_PARTITION_SIZE (1385) /* In K units */
|
|||
|
#define MAX_REASONABLE_SYS_HEAP_TOP ((Ptr)(167*ONE_K)) /* In bytes */
|
|||
|
|
|||
|
/* GetCeilingPtr. Return the address that bounds the process from above. This is
|
|||
|
* necessary because certain applications make assumptions about loading in the 1st
|
|||
|
* meg. In particular, make sure that WriteNow 1.00 never reaches higher than
|
|||
|
* 1M and calculate the ceiling for Excel 1.00-1.999... based on the empirical given
|
|||
|
* below (linear interpolation based on partition size).
|
|||
|
* NOTE: We don't care if p_size > MAX_EXCEL_K_PARTITION_SIZE: it won't be gotten and
|
|||
|
* GetProcessMemory will return memFragErr.
|
|||
|
* NOTE: We should actually check for the case in which the user has set the Excel
|
|||
|
* size so large that the multiplication overflows. However, as long as the constants
|
|||
|
* stay hardwired this could only happen if the size is set to 2**31/(525-127) > 10M.
|
|||
|
* It's not worth the extra code to handle this. If the constants get bigger, just
|
|||
|
* change the units to make the product smaller. The goal here is to waste as little
|
|||
|
* code as possible on this.
|
|||
|
*/
|
|||
|
Ptr
|
|||
|
GetCeilingPtr(PEntryPtr pPEntry)
|
|||
|
{
|
|||
|
long cbDiffActualFromMin, cbAboveAsk;
|
|||
|
Ptr minCeilingPtr, ceilingPtr;
|
|||
|
|
|||
|
ceilingPtr = nil;
|
|||
|
if (pPEntry->p_signature == 'nX^n' && pPEntry->p_version <= '0100')
|
|||
|
ceilingPtr = ONE_MEG_PTR;
|
|||
|
else if (pPEntry->p_signature == 'XCEL' && pPEntry->p_version < '0200')
|
|||
|
{
|
|||
|
/* Find difference between requested and minimum partitions (might be negative). */
|
|||
|
cbDiffActualFromMin = pPEntry->p_size - (MIN_EXCEL_K_PARTITION_SIZE * ONE_K);
|
|||
|
|
|||
|
/* Adjust ceiling pointer. Use default if requested size was too small. */
|
|||
|
ceilingPtr = ONE_MEG_PTR;
|
|||
|
if (cbDiffActualFromMin > 0)
|
|||
|
{
|
|||
|
cbAboveAsk = (cbDiffActualFromMin
|
|||
|
* (MAX_EXCEL_K_ABOVE_1MEG - MIN_EXCEL_K_ABOVE_1MEG)
|
|||
|
/ (MAX_EXCEL_K_PARTITION_SIZE - MIN_EXCEL_K_PARTITION_SIZE))
|
|||
|
+ (MIN_EXCEL_K_ABOVE_1MEG * ONE_K);
|
|||
|
|
|||
|
ceilingPtr += cbAboveAsk;
|
|||
|
}
|
|||
|
|
|||
|
/* Now bound ceiling from the bottom */
|
|||
|
minCeilingPtr = MAX_REASONABLE_SYS_HEAP_TOP + (Ptr) pPEntry->p_size;
|
|||
|
if (ceilingPtr < minCeilingPtr)
|
|||
|
ceilingPtr = minCeilingPtr;
|
|||
|
}
|
|||
|
|
|||
|
return ceilingPtr;
|
|||
|
}
|
|||
|
|
|||
|
/* GetProcessMemory. Get all of the physical memory required to run this process.
|
|||
|
* NOTE: Has unique ability to force application to be loaded completely
|
|||
|
* below a given ceiling. The hokey method is to allocate a specially-marked
|
|||
|
* block at the ceiling point, then mangle the ProcessMgrHiNewHandle to not look higher
|
|||
|
* than said block. This is to kow tow to Microsoft Excel 1.5's and WriteNow 1.0's
|
|||
|
* crippled designs.
|
|||
|
* NOTE: Assumes a5 == PROCESSMGRGLOBALS
|
|||
|
* NOTE: Side effect is to set THEZONE to ProcessMgrZone.
|
|||
|
*/
|
|||
|
static Ptr myRoverPtr = nil;
|
|||
|
|
|||
|
OSErr
|
|||
|
GetProcessMemory(PEntryPtr pProc, Ptr ceilingPtr)
|
|||
|
{
|
|||
|
Ptr lockPtr;
|
|||
|
OSErr retVal;
|
|||
|
PCB *pPCB;
|
|||
|
|
|||
|
lockPtr = nil;
|
|||
|
retVal = memFullErr;
|
|||
|
|
|||
|
THEZONE = ProcessMgrZone;
|
|||
|
assert(APPLZONE != THEZONE || STKLOWPT == nil);
|
|||
|
|
|||
|
/* If a ceiling-application in then load on top of it */
|
|||
|
if (myRoverPtr != nil)
|
|||
|
THEZONE->allocPtr = myRoverPtr;
|
|||
|
|
|||
|
/* Punt if the ceiling is below the Process Mgr zone, because we can't
|
|||
|
* allocate a "lock block" there. On the other hand, a ceiling above
|
|||
|
* the Process Mgr zone is taken care of naturally (because we never go
|
|||
|
* higher anyway).
|
|||
|
*/
|
|||
|
if (ceilingPtr != nil)
|
|||
|
{
|
|||
|
retVal = memFragErr;
|
|||
|
if ( (ceilingPtr >= &THEZONE->heapData) && (ceilingPtr < THEZONE->bkLim) )
|
|||
|
lockPtr = GetLockPtr(ceilingPtr);
|
|||
|
if (lockPtr == nil)
|
|||
|
goto ReturnRetVal;
|
|||
|
}
|
|||
|
|
|||
|
/* Allocate the application partition. The error code for failure should
|
|||
|
* be memFragErr if ceilingPtr != nil, memFullErr if ceilingPtr == nil.
|
|||
|
*/
|
|||
|
if ( (pProc->p_zoneh = ProcessMgrHiNewHandle(pProc->p_size, lockPtr)) == nil)
|
|||
|
goto ReturnRetVal;
|
|||
|
|
|||
|
/* Switch back to plain error once ceiling-application partition has been made */
|
|||
|
retVal = memFullErr;
|
|||
|
|
|||
|
/* lock the physical zone, and strip its tags. */
|
|||
|
HLock(pProc->p_zoneh);
|
|||
|
pProc->p_zone = (char *)(StripAddress(*pProc->p_zoneh));
|
|||
|
|
|||
|
/* Set rover for next guy */
|
|||
|
if (lockPtr != nil)
|
|||
|
{
|
|||
|
THEZONE->allocPtr = myRoverPtr = (Ptr)pProc->p_zone + pProc->p_size;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the block in which to record lomem context */
|
|||
|
if ( (pProc->p_lmemtool = NewHandle(lmemToolDataSize)) == nil)
|
|||
|
goto ReturnRetVal;
|
|||
|
|
|||
|
/* NOTE: PCB must be initialized to 0's, because much of the data
|
|||
|
* needs to be initialized to zero, so this is a fast, convenient way.
|
|||
|
*/
|
|||
|
if ( (pProc->p_pcb = (PCB **) NewHandleClear(sizeof(PCB))) == nil)
|
|||
|
goto ReturnRetVal;
|
|||
|
|
|||
|
/* Well. Now we have a process control block, and can continue to build
|
|||
|
* the rest of the dynamic data structures for this process in the Process Mgr
|
|||
|
* heap. When all are allocated, we make sure all really exist and have one
|
|||
|
* last chance to back out.
|
|||
|
*/
|
|||
|
|
|||
|
if ( ((*pProc->p_pcb)->vblvars = NewHandle(0)) == nil)
|
|||
|
goto ReturnRetVal;
|
|||
|
if ( ((*pProc->p_pcb)->dces = NewHandle(0)) == nil)
|
|||
|
goto ReturnRetVal;
|
|||
|
if ( ((*pProc->p_pcb)->tempMemHListHdl = CreateHList(0)) == nil)
|
|||
|
goto ReturnRetVal;
|
|||
|
|
|||
|
/* Set up the PCB. */
|
|||
|
pPCB = *pProc->p_pcb;
|
|||
|
pPCB->sysevtmask = everyEvent - keyUpMask;
|
|||
|
pPCB->enablePrTypeChanges = PRTYPE_CHANGESTATUS_ENABLED;
|
|||
|
retVal = noErr;
|
|||
|
|
|||
|
ReturnRetVal:
|
|||
|
if (lockPtr != nil)
|
|||
|
DisposPtr(lockPtr);
|
|||
|
THEZONE->allocPtr = nil;
|
|||
|
return(retVal);
|
|||
|
}
|
|||
|
|
|||
|
/* GetFinderInfoForFile. Handy routine to set a bunch of app file info we need. */
|
|||
|
void
|
|||
|
GetFinderInfoForFile(FSSpec *pFileSpec, PEntryPtr pProc)
|
|||
|
{
|
|||
|
register ScriptCode scriptCode;
|
|||
|
CInfoPBRec ioPB;
|
|||
|
|
|||
|
pProc->p_nameScript = iuSystemScript;
|
|||
|
ioPB.hFileInfo.ioNamePtr = &pFileSpec->name;
|
|||
|
ioPB.hFileInfo.ioVRefNum = pFileSpec->vRefNum;
|
|||
|
ioPB.hFileInfo.ioDirID = pFileSpec->parID;
|
|||
|
ioPB.hFileInfo.ioFDirIndex = 0;
|
|||
|
ioPB.hFileInfo.ioCompletion = nil;
|
|||
|
if (PBGetCatInfo(&ioPB.hFileInfo, SyncHFS) == noErr)
|
|||
|
{
|
|||
|
pProc->p_type = ioPB.hFileInfo.ioFlFndrInfo.fdType;
|
|||
|
pProc->p_signature = ioPB.hFileInfo.ioFlFndrInfo.fdCreator;
|
|||
|
scriptCode = (ScriptCode) ((FXInfo *) &ioPB.hFileInfo.ioFlXFndrInfo)->fdScript;
|
|||
|
if ((scriptCode & (1 << kScriptValidBit)) != 0)
|
|||
|
pProc->p_nameScript = (scriptCode & xfiScriptCodeMask);
|
|||
|
if ((((FInfo *) &ioPB.hFileInfo.ioFlFndrInfo)->fdFlags & FILE_FLAG_HAS_CUSTOM_ICON) != 0)
|
|||
|
pProc->iconResourceID = kCustomIconID;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*---------------------------------------------------------------------------------*
|
|||
|
Process Liquidation Routines
|
|||
|
*---------------------------------------------------------------------------------*/
|
|||
|
|
|||
|
/* HandleShellDeath. Determine the significance of the current death. Relaunch
|
|||
|
* Finder if we Deep Shat from the it or else tried to _ExitToShell from the last
|
|||
|
* layer.
|
|||
|
* NOTE: What we really want to do is switch into pNullProcess and have it just
|
|||
|
* constantly spawn new Finders. The problem is, we can't really switch to it because
|
|||
|
* right now pNullProcess has no context other than registers (and not even these
|
|||
|
* if we don't change switch_task()).
|
|||
|
* NOTE: Assumes a5 == PROCESSMGRGLOBALS
|
|||
|
*/
|
|||
|
void
|
|||
|
HandleShellDeath(PEntryPtr pDeadProc, OSErr status, Boolean isLastLayer)
|
|||
|
{
|
|||
|
register PEntryPtr pProc;
|
|||
|
Boolean relaunch;
|
|||
|
FSSpec finderSpec;
|
|||
|
LaunchResults finderLaunchResults;
|
|||
|
|
|||
|
/* Set likely result */
|
|||
|
relaunch = false;
|
|||
|
|
|||
|
/* Weed out case of crash during LaunchFacelessTasks() */
|
|||
|
if (pShellProcess == ((Ptr) -1))
|
|||
|
return;
|
|||
|
|
|||
|
/* At the very least, relaunch Finder if it crashed */
|
|||
|
if (pDeadProc == pShellProcess)
|
|||
|
{
|
|||
|
pShellProcess = nil;
|
|||
|
if ((ShellWasFinderType) && (status != noErr))
|
|||
|
relaunch = true;
|
|||
|
}
|
|||
|
|
|||
|
/* Handle death of last app on the system. If process we launched from FINDERNAME
|
|||
|
* is Finder or Finder-replacement, relaunch it. Otherwise, call for a restart as
|
|||
|
* soon as the corpse is buried.
|
|||
|
* NOTE: The SysError handler needs the fonts set up still.
|
|||
|
*/
|
|||
|
if (pProcessList == nil)
|
|||
|
{
|
|||
|
if (ShellWasFinderType)
|
|||
|
relaunch = true;
|
|||
|
else
|
|||
|
ShutDwnUserChoice();
|
|||
|
}
|
|||
|
|
|||
|
/* Handle last faceful app. If process we launched from FINDERNAME is Finder or a
|
|||
|
* Finder-replacement, relaunch it. Otherwise, kill all the remaining apps (i.e.
|
|||
|
* daemons). When the last of them dies, we'll be called again and say to shutdown.
|
|||
|
*/
|
|||
|
else if (isLastLayer)
|
|||
|
{
|
|||
|
if (ShellWasFinderType)
|
|||
|
relaunch = true;
|
|||
|
else
|
|||
|
{
|
|||
|
pProc = pProcessList;
|
|||
|
while (pProc != nil)
|
|||
|
{
|
|||
|
if (pProc != pDeadProc)
|
|||
|
KillProcess(&pProc->p_serialNumber);
|
|||
|
pProc = pProc->p_NextProcess;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Well, do we take the initiative about Finder? */
|
|||
|
if (relaunch)
|
|||
|
{
|
|||
|
if ((finderLaunchResults.LaunchError = FSMakeFSSpec(BOOTVOL, 0, FINDERNAME, &finderSpec)) == noErr)
|
|||
|
StandardLaunch(&finderSpec, launchUseMinimum, 0, nil, &finderLaunchResults, nil);
|
|||
|
|
|||
|
if (finderLaunchResults.LaunchError != noErr)
|
|||
|
SysError(dsFinderErr);
|
|||
|
|
|||
|
/* Wait to do this 'til we're sure. */
|
|||
|
pShellProcess = finderLaunchResults.pNewProcess;
|
|||
|
pShellProcess->p_dadProcess = nil;
|
|||
|
ShellWasFinderType = IS_FINDER(pShellProcess);
|
|||
|
(void) SetFrontProcess(&pShellProcess->p_serialNumber);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* DisposeProcessMemory. Dispose of the physical memory occupied by the specified process.
|
|||
|
* Doesn't assume structures exist, because this may be called as error recovery
|
|||
|
* from launch.
|
|||
|
* NOTE: Does not explicitly dispose things that are known to reside in the application
|
|||
|
* heap, since these are implicitly freed by freeing or resizing the app partition.
|
|||
|
*/
|
|||
|
void
|
|||
|
DisposeProcessMemory(PEntryPtr pProc)
|
|||
|
{
|
|||
|
PCB *pPCB, **hPCB;
|
|||
|
|
|||
|
/* Must invalidate rover if it was set for this app's partition */
|
|||
|
if (myRoverPtr == ((Ptr)pProc->p_zone + pProc->p_size))
|
|||
|
myRoverPtr = nil;
|
|||
|
|
|||
|
/* Free the partition */
|
|||
|
if (pProc->p_zoneh != nil)
|
|||
|
{
|
|||
|
DisposHandle(pProc->p_zoneh);
|
|||
|
pProc->p_zoneh = nil;
|
|||
|
}
|
|||
|
|
|||
|
/* free structures hanging off of PEntry proper */
|
|||
|
if (pProc->p_lmemtool != nil)
|
|||
|
{
|
|||
|
DisposHandle(pProc->p_lmemtool);
|
|||
|
pProc->p_lmemtool = nil;
|
|||
|
}
|
|||
|
|
|||
|
/* now free structures hanging off PCB, then PCB itself */
|
|||
|
if ((hPCB = pProc->p_pcb) == nil)
|
|||
|
return;
|
|||
|
|
|||
|
pProc->p_pcb = nil;
|
|||
|
HLock(hPCB);
|
|||
|
pPCB = *hPCB;
|
|||
|
|
|||
|
if (pPCB->vblvars != nil)
|
|||
|
DisposHandle(pPCB->vblvars);
|
|||
|
|
|||
|
if (pPCB->dces != nil)
|
|||
|
DisposHandle(pPCB->dces);
|
|||
|
|
|||
|
if (HListExists(pPCB->p_PseudoEvtQHList))
|
|||
|
DestroyHList(pPCB->p_PseudoEvtQHList);
|
|||
|
|
|||
|
if (HListExists(pPCB->tempMemHListHdl))
|
|||
|
DestroyHList(pPCB->tempMemHListHdl);
|
|||
|
|
|||
|
/* puppetstringhdl is either a) in app heap (dispose is not needed), or b) a shared
|
|||
|
* resource (dispose would be bad for others). Don't dispose it.
|
|||
|
*/
|
|||
|
if (pPCB->puppetstringhdl != nil)
|
|||
|
{
|
|||
|
/* DisposHandle(pPCB->puppetstringhdl); */
|
|||
|
}
|
|||
|
|
|||
|
DisposHandle(hPCB);
|
|||
|
}
|
|||
|
|
|||
|
/* MenuKill. Delete Apple menu items defined by this app, then delete application
|
|||
|
* name itself. */
|
|||
|
void
|
|||
|
MenuKill(void)
|
|||
|
{
|
|||
|
/* Nuke MENULIST because app resource files will be closed, which releases all the
|
|||
|
* menu resources, leaving disposed handles in the menulist.
|
|||
|
*/
|
|||
|
MENULIST = nil;
|
|||
|
|
|||
|
/* Remove our record of this app having an Apple menu */
|
|||
|
AppleMenuCleanup(pCurrentProcess);
|
|||
|
|
|||
|
/* Update the Application Menu. Must reset the enable state of the hiding items if
|
|||
|
* this process is dying in the background.
|
|||
|
*/
|
|||
|
DeleteAppFromApplicationMenu(pCurrentProcess);
|
|||
|
if (pCurrentProcess != LayerOwner(GetFrontAppLayer()))
|
|||
|
AdjustApplicationMenu(nil);
|
|||
|
}
|
|||
|
|
|||
|
/* NMKill. Remove all notification manager requests for the current process.
|
|||
|
* Criteria is that request memory lies within the application's partition.
|
|||
|
* NOTE: This should be a trap call to NM mgr.
|
|||
|
*/
|
|||
|
void
|
|||
|
NMKill(void)
|
|||
|
{
|
|||
|
register NMRecPtr curNMRec;
|
|||
|
NMRecPtr oldNMRec;
|
|||
|
Ptr base, bound;
|
|||
|
unsigned short ps;
|
|||
|
|
|||
|
/* Bail out if NM doesn't exist or there are no outstanding requests */
|
|||
|
if (NMQHDRPTR == nil || NMQHDRPTR == ((Ptr) -1))
|
|||
|
return;
|
|||
|
|
|||
|
/* Cache the application boundaries */
|
|||
|
base = pCurrentProcess->p_zone;
|
|||
|
bound = base + pCurrentProcess->p_size;
|
|||
|
|
|||
|
/* Scan the list. Disables interrupts to avoid collisions. */
|
|||
|
curNMRec = NMQHDRPTR->qHead;
|
|||
|
ps = disable();
|
|||
|
while (curNMRec != nil)
|
|||
|
{
|
|||
|
oldNMRec = curNMRec; curNMRec = curNMRec->qLink;
|
|||
|
if ((oldNMRec >= base) && (oldNMRec <= bound))
|
|||
|
(void) NMRemove(oldNMRec);
|
|||
|
}
|
|||
|
spl(ps);
|
|||
|
}
|
|||
|
|
|||
|
/* FMKill. Remove all font handles for the current process. If the location of these
|
|||
|
* handles ever changes to another zone, so should this.
|
|||
|
* NOTE: WIDTHTABHANDLE, WIDTHLISTHAND, and SYNLISTHANDLE are nil'd out in case the
|
|||
|
* lomem may be used again (esp. for sys startup caller).
|
|||
|
* NOTE: Assumes THEZONE != ProcessMgrZone, in that it restores THEZONE unconditionally,
|
|||
|
* instead of using SafeSetZone and RestoreZone.
|
|||
|
*/
|
|||
|
void
|
|||
|
FMKill(Boolean disposeSynStrikes)
|
|||
|
{
|
|||
|
short iTable;
|
|||
|
Ptr sysZoneEnd, tableEnd;
|
|||
|
Handle widthTabHdl, *widthTabHdlPtr;
|
|||
|
SynEntryPtr synEntryPtr;
|
|||
|
THz saveZone;
|
|||
|
|
|||
|
/* All the action here occurs in the system heap */
|
|||
|
saveZone = THEZONE;
|
|||
|
THEZONE = SYSZONE;
|
|||
|
sysZoneEnd = SYSZONE->bkLim;
|
|||
|
|
|||
|
/* Deallocate the current WIDTHTABHANDLE */
|
|||
|
if (WIDTHTABHANDLE != nil)
|
|||
|
{
|
|||
|
DisposHandle(WIDTHTABHANDLE);
|
|||
|
WIDTHTABHANDLE = nil;
|
|||
|
}
|
|||
|
|
|||
|
/* Get rid of any system handle in WIDTHLISTHAND, then dispose the list itself */
|
|||
|
if (WIDTHLISTHAND != nil)
|
|||
|
{
|
|||
|
assert(*WIDTHLISTHAND != nil);
|
|||
|
HLock(WIDTHLISTHAND);
|
|||
|
|
|||
|
widthTabHdlPtr = *WIDTHLISTHAND;
|
|||
|
iTable = NUMITABLES - 1;
|
|||
|
do
|
|||
|
{
|
|||
|
/* Table may prematurely end with a 0 */
|
|||
|
if ((widthTabHdl = (Handle) StripAddress(*widthTabHdlPtr++)) == nil)
|
|||
|
break;
|
|||
|
|
|||
|
/* Get rid of sys heap handles */
|
|||
|
if (widthTabHdl < sysZoneEnd)
|
|||
|
DisposHandle(widthTabHdl);
|
|||
|
}
|
|||
|
while (--iTable >= 0);
|
|||
|
|
|||
|
/* Dispose of the list itself */
|
|||
|
DisposHandle(WIDTHLISTHAND);
|
|||
|
WIDTHLISTHAND = nil;
|
|||
|
}
|
|||
|
|
|||
|
/* Get rid of any system handle in SYNLISTHANDLE, then dispose the list itself.
|
|||
|
* NOTE: We are guaranteed that when we are called by InitializeEnvironment(), that
|
|||
|
* only the first entry could be in the sys heap. Worse yet, a following entry
|
|||
|
* might appear to be in the sys heap because it was from a MP block at the
|
|||
|
* bottom of the app heap, and a subsequent _SetAppBase has raised the top of
|
|||
|
* the sys heap over it. We therefore check that entry is below sysZoneEnd.
|
|||
|
*/
|
|||
|
if (Colorized && (SYNLISTHANDLE != nil) && ((synEntryPtr = StripAddress(*SYNLISTHANDLE)) != nil))
|
|||
|
{
|
|||
|
if (disposeSynStrikes)
|
|||
|
{
|
|||
|
HLock(SYNLISTHANDLE);
|
|||
|
tableEnd = (Ptr) synEntryPtr + GetHandleSize(SYNLISTHANDLE);
|
|||
|
|
|||
|
/* Get rid of sys heap handles */
|
|||
|
while ((Ptr) synEntryPtr < tableEnd)
|
|||
|
{
|
|||
|
if ( (synEntryPtr->synNFNT != (-1)) &&
|
|||
|
(StripAddress(synEntryPtr->synStrikeHdl) < sysZoneEnd) )
|
|||
|
DisposHandle(synEntryPtr->synStrikeHdl);
|
|||
|
synEntryPtr += 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Dispose of the list itself */
|
|||
|
DisposHandle(SYNLISTHANDLE);
|
|||
|
SYNLISTHANDLE = nil;
|
|||
|
}
|
|||
|
|
|||
|
THEZONE = saveZone;
|
|||
|
}
|
|||
|
|
|||
|
/* MakeSynStrikesPurgeable. The Font Manager (spec. _FMSwapFont) always leaves one of
|
|||
|
* the synthetic strikes non-purgeable. It stays that way indefinitely, even though the
|
|||
|
* Font Manager can easily cope with a purged strike. Since the memory for the strike
|
|||
|
* (10's of Kb) comes from the system heap, we would like to see the strike purgeable.
|
|||
|
* There is no great time in QuickDraw or the Font Manager we can do this, since the
|
|||
|
* caller of _FMSwapFont is a public call (app may assume strike is non-purgeable in
|
|||
|
* places the system doesn't). We figure, tho, that by the time the app is making a
|
|||
|
* ToolBox event call it can cope with a purgeable strike.
|
|||
|
*/
|
|||
|
void
|
|||
|
MakeSynStrikesPurgeable(void)
|
|||
|
{
|
|||
|
SynEntryPtr synEntryPtr;
|
|||
|
Ptr tableEnd;
|
|||
|
|
|||
|
if (Colorized && (SYNLISTHANDLE != nil) && ((synEntryPtr = StripAddress(*SYNLISTHANDLE)) != nil))
|
|||
|
{
|
|||
|
tableEnd = (Ptr) synEntryPtr + GetHandleSize(SYNLISTHANDLE);
|
|||
|
while ((Ptr) synEntryPtr < tableEnd)
|
|||
|
{
|
|||
|
if (synEntryPtr->synNFNT != (-1))
|
|||
|
HPurge(synEntryPtr->synStrikeHdl);
|
|||
|
synEntryPtr += 1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Message format for aeApplicationDied we send */
|
|||
|
typedef struct ApplicationDiedMsg {
|
|||
|
AETFHeader messageHeader;
|
|||
|
KeyWord metaDataMark;
|
|||
|
AETFParameter appDiedErrorHdr;
|
|||
|
long appDiedErrorNumber;
|
|||
|
AETFParameter appDiedProcessHdr;
|
|||
|
ProcessSerialNumber appDiedProcessNumber;
|
|||
|
} ApplicationDiedMsg;
|
|||
|
|
|||
|
/* SendDeathNotice. Notify the specified process that its pal is dying. */
|
|||
|
void
|
|||
|
SendDeathNotice(PEntryPtr pTargetProc, PEntryPtr pProc, OSErr status)
|
|||
|
{
|
|||
|
ApplicationDiedMsg msgStorage;
|
|||
|
HighLevelEventRecord theEvent;
|
|||
|
|
|||
|
/* Do nothing if not there, or doesn't want the notification */
|
|||
|
if ((pTargetProc == nil) || ((pTargetProc->p_taskmode & modeGetAppDiedMsg) == 0))
|
|||
|
return;
|
|||
|
|
|||
|
/* Set up the high-level event record */
|
|||
|
SetAppleEvent(aeApplicationDied, &theEvent);
|
|||
|
|
|||
|
/* Set up the message buffer */
|
|||
|
GenericAEMessage(&msgStorage.messageHeader);
|
|||
|
msgStorage.metaDataMark = aeEndOfMetaDataKeyword;
|
|||
|
SetAEParmHeader(&msgStorage.appDiedErrorHdr, keyErrorNumber, aeLongintParamType, sizeof(long));
|
|||
|
msgStorage.appDiedErrorNumber = (long) status;
|
|||
|
|
|||
|
SetAEParmHeader(&msgStorage.appDiedProcessHdr, aeProcessKeyword, aeProcessParamType, sizeof(ProcessSerialNumber));
|
|||
|
msgStorage.appDiedProcessNumber = pProc->p_serialNumber;
|
|||
|
|
|||
|
/* Post the event. Do it in system mode since the dying process is incapable of EPPC. */
|
|||
|
(void) BeginSystemMode();
|
|||
|
allocateGMBlkInSysHeap = true; // <45> Force event to be created in the System heap
|
|||
|
(void) PostHighLevelEvent((EventRecord *) &theEvent, (u_long) &pTargetProc->p_serialNumber, 0, &msgStorage, sizeof(ApplicationDiedMsg), receiverIDisPSN);
|
|||
|
allocateGMBlkInSysHeap = false; // <45> Go back to trying to allocate from the PM heap first
|
|||
|
(void) EndSystemMode();
|
|||
|
}
|
|||
|
|
|||
|
/* c_exittoshell. Here is our patch for ExitToShell(). We don't want this to run as a
|
|||
|
* kernel routine!!! Either the application called this in the normal course of events,
|
|||
|
* or else some type of exception was fielded by the debugger and the user did an
|
|||
|
* exit (e.g. ES in MacsBug) from there, thus setting the DSErrCode. This status is
|
|||
|
* passed on to the bereaved parent.
|
|||
|
* This is the last subroutine a process ever calls.
|
|||
|
*/
|
|||
|
pascal void
|
|||
|
c_exittoshell(void)
|
|||
|
{
|
|||
|
PEntryPtr pDeadProc, pOtherProc;
|
|||
|
LayerPtr pLayer;
|
|||
|
Boolean frontmost, isLastLayer, deathByQuitAll;
|
|||
|
PCB **pcbHdl;
|
|||
|
OSErr exitStatus;
|
|||
|
unsigned long oldA5;
|
|||
|
|
|||
|
/* Use our A5, but swipe the graphics globals from CURRENTA5 as long as InitGraf has
|
|||
|
* been called (i.e. QDEXIST == 0).
|
|||
|
*/
|
|||
|
if (QDEXIST == (char) 0)
|
|||
|
{
|
|||
|
A5SimpleRestore(CURRENTA5);
|
|||
|
oldA5 = ProcessMgrA5Setup();
|
|||
|
}
|
|||
|
else
|
|||
|
oldA5 = ProcessMgrA5SimpleSetup();
|
|||
|
|
|||
|
/* Disk switches don't count as sys errors */
|
|||
|
if ((exitStatus = (OSErr) DSERRCODE) == dsReinsert)
|
|||
|
exitStatus = noErr;
|
|||
|
|
|||
|
/* Switch to a neutral zone, in case we call something that allocates memory */
|
|||
|
THEZONE = SYSZONE;
|
|||
|
|
|||
|
#if PsychicTV
|
|||
|
/* <54> Tear down any CFM-loaded system DLLs or fat resources */
|
|||
|
|
|||
|
TearDownPsychicTVProcess(pCurrentProcess);
|
|||
|
#endif
|
|||
|
|
|||
|
/* Collect some useful information for later */
|
|||
|
pDeadProc = pCurrentProcess;
|
|||
|
pcbHdl = pDeadProc->p_pcb;
|
|||
|
|
|||
|
/* Reset this so that cleanup won't wipe out files/data acquired in system mode */
|
|||
|
pDeadProc->p_systemModeLevel = 0;
|
|||
|
|
|||
|
/* Clean up executable stuff so it doesn't interfere with our cleanup (for example,
|
|||
|
* the dead app might have patches to traps we call).
|
|||
|
*/
|
|||
|
kill_state(pDeadProc);
|
|||
|
|
|||
|
/* Dump any pending events if app crashed.
|
|||
|
* NOTE: Do this only for the foreground process?
|
|||
|
*/
|
|||
|
if (exitStatus != noErr)
|
|||
|
FlushEvents(everyEvent, 0);
|
|||
|
|
|||
|
/* See if process died in its sleep. Something other than the app code may be
|
|||
|
* calling _ExitToShell (like MacsBug or the dsForcedQuit handler).
|
|||
|
*/
|
|||
|
CancelSleep(pDeadProc);
|
|||
|
|
|||
|
/* Reset other process' entries */
|
|||
|
pOtherProc = pProcessList;
|
|||
|
while (pOtherProc != nil)
|
|||
|
{
|
|||
|
if (pOtherProc->p_dadProcess == pDeadProc)
|
|||
|
pOtherProc->p_dadProcess = nil;
|
|||
|
pOtherProc = pOtherProc->p_NextProcess;
|
|||
|
}
|
|||
|
|
|||
|
/* Remove from St. Peter's waiting list */
|
|||
|
if (pDeadProc->p_condemned)
|
|||
|
{
|
|||
|
RemoveFromStateList(pDeadProc, &pDyingStateList);
|
|||
|
pDeadProc->p_condemned = wFalse;
|
|||
|
}
|
|||
|
|
|||
|
/* Make sure the registered debugger checks out (and takes his debuggees with him) */
|
|||
|
if (pDeadProc == pDebugProcess)
|
|||
|
{
|
|||
|
ProcessSerialNumber noProcess;
|
|||
|
|
|||
|
SetPSN(kNoProcess, &noProcess);
|
|||
|
RegisterDebugger(&noProcess, nil, nil, nil, 0);
|
|||
|
}
|
|||
|
|
|||
|
/* Cancel system quit if the dead app is the one that started the whole thing. */
|
|||
|
frontmost = (pDeadProc == pFrontProcess);
|
|||
|
deathByQuitAll = false;
|
|||
|
if (pSystemQuitAllMsg != nil)
|
|||
|
{
|
|||
|
if (EqualPSN(&(pSystemQuitAllMsg->sendermfid.localPSN), &pDeadProc->p_serialNumber))
|
|||
|
CancelQuitAll(nil);
|
|||
|
else
|
|||
|
deathByQuitAll = (EqualPSN(&(pSystemQuitAllMsg->targetmfid.localPSN), &pDeadProc->p_serialNumber));
|
|||
|
}
|
|||
|
|
|||
|
/* Cancel puppet stringing. */
|
|||
|
PuppetKill();
|
|||
|
|
|||
|
/* Nuke EPPC and PPC stuff. */
|
|||
|
DestroyMsgQ(&pDeadProc->eppcBlk);
|
|||
|
|
|||
|
/* Cleanup volume notification */
|
|||
|
VNKill();
|
|||
|
|
|||
|
/* Make sure cursor is visible */
|
|||
|
if ((frontmost) && (QDEXIST == (char) 0))
|
|||
|
InitCursor();
|
|||
|
|
|||
|
/* Menu manager cleanup */
|
|||
|
MenuKill();
|
|||
|
|
|||
|
/* Re-enable CloseWD for home working directory (p_vrefnum) */
|
|||
|
(*pcbHdl)->p_vrefnum = 0;
|
|||
|
|
|||
|
/* Get system and toolbox to cleanup. Set the zone and A5 since some need them
|
|||
|
* to know which structures to wipe.
|
|||
|
*/
|
|||
|
THEZONE = APPLZONE;
|
|||
|
A5SimpleRestore(oldA5);
|
|||
|
CleanupApplication();
|
|||
|
(void) ProcessMgrA5SimpleSetup();
|
|||
|
THEZONE = SYSZONE;
|
|||
|
|
|||
|
/* Close down the toolbox for the dead process (needs our help) */
|
|||
|
if ((pLayer = pDeadProc->p_layer) != nil)
|
|||
|
LayerClose(pLayer);
|
|||
|
if (pDeadProc->p_floatLayer != nil)
|
|||
|
DisposeWindow(pDeadProc->p_floatLayer);
|
|||
|
if (pDeadProc == tsmLayerOwner)
|
|||
|
tsmLayerOwner = nil;
|
|||
|
QDKill();
|
|||
|
NMKill();
|
|||
|
|
|||
|
/* Remember whether this is the last visible layer going away */
|
|||
|
isLastLayer = (GetFrontAppLayer() == nil);
|
|||
|
|
|||
|
/* Reallow switching in case he died in the middle. */
|
|||
|
while (dont_switch > 0)
|
|||
|
EnableSwitching();
|
|||
|
|
|||
|
/* Remove from count of background apps, if he is one */
|
|||
|
if ((pDeadProc->p_taskmode & modeCanBackground) != 0)
|
|||
|
nbacks--;
|
|||
|
|
|||
|
{
|
|||
|
/* unlink pDeadProc from list of active entries */
|
|||
|
PEntryPtr pPrev, pNext;
|
|||
|
|
|||
|
pPrev = pNullProcess;
|
|||
|
while ((pNext = pPrev->p_NextProcess) != pDeadProc)
|
|||
|
{
|
|||
|
assert(pNext != nil);
|
|||
|
pPrev = pNext;
|
|||
|
}
|
|||
|
pPrev->p_NextProcess = pDeadProc->p_NextProcess;
|
|||
|
}
|
|||
|
|
|||
|
/* Adjust BG round-robin placeholder */
|
|||
|
if (pDeadProc == pLastBGProcess)
|
|||
|
{
|
|||
|
if ((pLastBGProcess = pDeadProc->p_NextProcess) == nil)
|
|||
|
pLastBGProcess = pProcessList;
|
|||
|
}
|
|||
|
|
|||
|
/* Take out of queue if dead guy was waiting to come to the front */
|
|||
|
RemoveFromFrontProcessQueue(pDeadProc);
|
|||
|
|
|||
|
/* Reset global so we don't end up switching to same guy! */
|
|||
|
if (pDeadProc == pNewFrontProcess)
|
|||
|
pNewFrontProcess = nil;
|
|||
|
|
|||
|
/* Let the world know of the tragedy that has beset us */
|
|||
|
SendDeathNotice(pDeadProc->p_dadProcess, pDeadProc, exitStatus);
|
|||
|
if (pDebugProcess != pDeadProc->p_dadProcess)
|
|||
|
SendDeathNotice(pDebugProcess, pDeadProc, exitStatus);
|
|||
|
|
|||
|
/* Handle shell quit or crash, or death of the last layer.
|
|||
|
* NOTE: We wait to FMKill until after HandleShellDeath, because fonts will be
|
|||
|
* needed if a SysError is generated (like dsFinderErr, or inside ShutDwnUserChoice).
|
|||
|
*/
|
|||
|
HandleShellDeath(pDeadProc, exitStatus, isLastLayer);
|
|||
|
FMKill(true);
|
|||
|
|
|||
|
/* Remove the process' memory. Be very careful for awhile, since some of the basic
|
|||
|
* structures are about to become invalid!
|
|||
|
* NOTE: OrphanScrapIntoPartition commandeers the partition to hold the scrap until
|
|||
|
* it coercion is satisfied. This means we'd better be done with everything we need
|
|||
|
* in the partition!
|
|||
|
*/
|
|||
|
OrphanScrapIntoPartition();
|
|||
|
pCurrentProcess = nil;
|
|||
|
DisposeProcessMemory(pDeadProc);
|
|||
|
DisposeProcessEntry(pDeadProc);
|
|||
|
|
|||
|
/* Set pFrontProcess to new front process now that current guy is dead (in the
|
|||
|
* event we don't call cpu_resched() below).
|
|||
|
* Candidates:
|
|||
|
* • current frontmost if that isn't the dead app (pFrontProcess)
|
|||
|
* • the process from SetFrontProcess() or canceled switch (pNewFrontProcess)
|
|||
|
* • process waiting to come to the front (PopFrontProcessQueue())
|
|||
|
* • process that initiated a system quit (from pSystemQuitAllMsg)
|
|||
|
* • process controlling the top layer
|
|||
|
*/
|
|||
|
|
|||
|
/* See if we need to select new foreground app */
|
|||
|
if (frontmost)
|
|||
|
{
|
|||
|
if ( ((pOtherProc = pNewFrontProcess) == nil) &&
|
|||
|
((pOtherProc = PopFrontProcessQueue()) == nil) )
|
|||
|
{
|
|||
|
if (pSystemQuitAllMsg != nil)
|
|||
|
pOtherProc = PEntryFromPSN(&(pSystemQuitAllMsg->sendermfid.localPSN));
|
|||
|
else
|
|||
|
{
|
|||
|
if ((pLayer = LGetNextLayer(nil, true)) == nil)
|
|||
|
pLayer = LGetNextLayer(nil, false);
|
|||
|
assert(pLayer != nil);
|
|||
|
pOtherProc = LayerOwner(pLayer);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
assert(IsProcessEntry(pOtherProc));
|
|||
|
|
|||
|
pFrontProcess = pOtherProc;
|
|||
|
}
|
|||
|
|
|||
|
/* User deaths require full scrap-in switches. Background deaths cause control to
|
|||
|
* go the frontmost, unless generalized rescheduling was requested. Otherwise, it
|
|||
|
* was a simple murder of a non-frontmost process.
|
|||
|
*/
|
|||
|
|
|||
|
/* Check for death of foreground process with other facefuls available */
|
|||
|
if (frontmost && (isLastLayer == false) && (pFrontProcess != nil))
|
|||
|
{
|
|||
|
/* Jump into end of coercion (scrap info set by OrphanScrapIntoPartition) */
|
|||
|
coercionState = CS_ENTERING;
|
|||
|
|
|||
|
/* So that new front process sees valid menu bar */
|
|||
|
AdjustApplicationMenu(pFrontProcess);
|
|||
|
|
|||
|
/* Switch to front process we located earlier */
|
|||
|
pNewFrontProcess = nil;
|
|||
|
CancelSleep(pFrontProcess);
|
|||
|
pFrontProcess->p_state = PRMOVING;
|
|||
|
|
|||
|
/* Reset OSDispatch depth indicator. This is positioned such that we have
|
|||
|
* already made all the OSDispatch calls we need before the switch.
|
|||
|
*/
|
|||
|
kernelbusy = 0;
|
|||
|
|
|||
|
switch_task(pFrontProcess);
|
|||
|
}
|
|||
|
|
|||
|
/* Death of BG process or last foreground process */
|
|||
|
else
|
|||
|
{
|
|||
|
/* Continue system quit, if it's happening */
|
|||
|
if (deathByQuitAll)
|
|||
|
ContinueQuitAll();
|
|||
|
|
|||
|
/* Reset OSDispatch depth indicator. This is positioned such that we have
|
|||
|
* already made all the OSDispatch calls we need before the switch.
|
|||
|
*/
|
|||
|
kernelbusy = 0;
|
|||
|
|
|||
|
cpu_resched();
|
|||
|
}
|
|||
|
|
|||
|
dbmsg("ExitToShell failed to schedule a different application.");
|
|||
|
}
|
|||
|
|
|||
|
/*----------------------------------------------------------------------------------*
|
|||
|
* Process Access Routines *
|
|||
|
*----------------------------------------------------------------------------------*/
|
|||
|
|
|||
|
/* PEntryFromPID. Locate existing process having given ProcessID. */
|
|||
|
PEntryPtr
|
|||
|
PEntryFromPID(ProcessID pid)
|
|||
|
{
|
|||
|
register PEntryPtr pProc;
|
|||
|
unsigned long oldA5;
|
|||
|
|
|||
|
/* Loop until ProcessID matches or end of list */
|
|||
|
oldA5 = ProcessMgrA5SimpleSetup();
|
|||
|
pProc = pProcessList;
|
|||
|
while (pProc != nil)
|
|||
|
{
|
|||
|
if ((pProc->p_mypid) == pid)
|
|||
|
break;
|
|||
|
|
|||
|
pProc = pProc->p_NextProcess;
|
|||
|
}
|
|||
|
|
|||
|
A5SimpleRestore(oldA5);
|
|||
|
return(pProc);
|
|||
|
}
|
|||
|
|
|||
|
/* EqualPSN. Return whether the two PSNs are identical. */
|
|||
|
Boolean
|
|||
|
EqualPSN(ProcessSerialNumberPtr pFirstPSN, ProcessSerialNumberPtr pSecondPSN)
|
|||
|
{
|
|||
|
return( (pFirstPSN->lowLongOfPSN == pSecondPSN->lowLongOfPSN) &&
|
|||
|
(pFirstPSN->highLongOfPSN == pSecondPSN->highLongOfPSN) );
|
|||
|
}
|
|||
|
|
|||
|
/* PEntryFromPSN. Locate existing process having given ProcessSerialNumber.
|
|||
|
* NOTE: Caller may be passing in PSN address from heap block, so we should
|
|||
|
* be careful to not move memory in this call.
|
|||
|
*/
|
|||
|
PEntryPtr
|
|||
|
PEntryFromPSN(ProcessSerialNumberPtr pPSN)
|
|||
|
{
|
|||
|
register PEntryPtr pProc;
|
|||
|
unsigned long oldA5;
|
|||
|
|
|||
|
/* Weed out the junkies */
|
|||
|
if (pPSN == nil)
|
|||
|
return(nil);
|
|||
|
|
|||
|
/* Look first for special value */
|
|||
|
oldA5 = ProcessMgrA5SimpleSetup();
|
|||
|
if ((pPSN->lowLongOfPSN == kCurrentProcess) && (pPSN->highLongOfPSN == 0))
|
|||
|
pProc = pCurrentProcess;
|
|||
|
else
|
|||
|
{
|
|||
|
/* Loop until ProcessSerialNumber matches or end of list */
|
|||
|
pProc = pProcessList;
|
|||
|
while (pProc != nil)
|
|||
|
{
|
|||
|
if (EqualPSN(&pProc->p_serialNumber, pPSN))
|
|||
|
break;
|
|||
|
|
|||
|
pProc = pProc->p_NextProcess;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
A5SimpleRestore(oldA5);
|
|||
|
return(pProc);
|
|||
|
}
|
|||
|
|
|||
|
/* PEntryFromFileSpec. See if we can find a process that is running off the
|
|||
|
* the specified file. Include the pResName if you're looking for a DA.
|
|||
|
* NOTE: Does PBGetFCBInfo return appropriate ioFCBParID on MFS volume?
|
|||
|
*/
|
|||
|
PEntryPtr
|
|||
|
PEntryFromFileSpec(FSSpecPtr pSpec, StringPtr pResName)
|
|||
|
{
|
|||
|
PEntryPtr pProc;
|
|||
|
FCBPBRec fcbInfoRec;
|
|||
|
Str255 rsrcFileName;
|
|||
|
|
|||
|
/* Iterate through the process list */
|
|||
|
pProc = pProcessList;
|
|||
|
fcbInfoRec.ioCompletion = nil;
|
|||
|
while (pProc != nil)
|
|||
|
{
|
|||
|
/* Get resource file spec for this process */
|
|||
|
fcbInfoRec.ioFCBIndx = 0;
|
|||
|
fcbInfoRec.ioRefNum = (*pProc->p_pcb)->p_fileRefNum;
|
|||
|
fcbInfoRec.ioNamePtr = &rsrcFileName;
|
|||
|
if (PBGetFCBInfo(&fcbInfoRec, SyncHFS) == noErr)
|
|||
|
{
|
|||
|
/* See if spec matches */
|
|||
|
if ((pSpec->vRefNum == fcbInfoRec.ioFCBVRefNum) && (pSpec->parID == fcbInfoRec.ioFCBParID))
|
|||
|
{
|
|||
|
if ( (MyRelString(&pSpec->name, &rsrcFileName) == sortsEqual) &&
|
|||
|
((pResName == nil) || (MyRelString(pResName, &((*pProc->p_pcb)->curapname)) == sortsEqual)) )
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
pProc = pProc->p_NextProcess;
|
|||
|
}
|
|||
|
|
|||
|
return (pProc);
|
|||
|
}
|
|||
|
|
|||
|
#if PsychicTV
|
|||
|
|
|||
|
/*----------------------------------------------------------------------------------*
|
|||
|
* <54> PsychicTV Support Routines *
|
|||
|
*----------------------------------------------------------------------------------*/
|
|||
|
|
|||
|
/* Routines added to support launching of Native applications under RISC System Software */
|
|||
|
/* These routines can also be generalized to support Code Fragment Manager on 68K */
|
|||
|
|
|||
|
|
|||
|
Boolean
|
|||
|
FindFragmentEntryPoint(FSSpec *theAppFSSpec,FragmentLocatorPtr mainFragment,OSType *architecture)
|
|||
|
{
|
|||
|
// <55> we’re looking for a longword version for now, and we only support version 0.
|
|||
|
// (PowerPC code in the datafork).
|
|||
|
|
|||
|
long **codeFragIndex;
|
|||
|
|
|||
|
if ((codeFragIndex = (long **) Get1Resource('cfri',0)) && (**codeFragIndex == 0))
|
|||
|
{
|
|||
|
mainFragment->where = kOnDiskFlat; // In the data fork
|
|||
|
mainFragment->u.onDisk.fileSpec = theAppFSSpec;
|
|||
|
mainFragment->u.onDisk.offset = 0; // the whole data fork is the container
|
|||
|
mainFragment->u.onDisk.length = 0; // another for the whole data fork
|
|||
|
|
|||
|
*architecture = kPowerPC; // this is all we have now…
|
|||
|
|
|||
|
ReleaseResource(codeFragIndex);
|
|||
|
return(true);
|
|||
|
}
|
|||
|
else
|
|||
|
return(false);
|
|||
|
}
|
|||
|
|
|||
|
OSErr
|
|||
|
SetupPsychicTVProcess(PEntryPtr pNewProc,Boolean launchingFragment,FragmentLocatorPtr mainFragment,
|
|||
|
OSType architecture,RoutineDescriptor *nativeEntryPoint)
|
|||
|
{
|
|||
|
OSErr err;
|
|||
|
FragContextID fragContext;
|
|||
|
FragConnectionID fragConnection;
|
|||
|
LogicalAddress entryPoint;
|
|||
|
Str255 badLibName;
|
|||
|
|
|||
|
err = FragCreateContext(&fragContext,
|
|||
|
pNewProc->p_zone,
|
|||
|
kDefaultAddrSpaceID,
|
|||
|
kDefaultTeamID,
|
|||
|
kDefaultTaskID,
|
|||
|
0);
|
|||
|
if (err != noErr)
|
|||
|
return(err);
|
|||
|
|
|||
|
(*pNewProc->p_pcb)->fragContext = fragContext; // Save contextID for later use
|
|||
|
|
|||
|
if (launchingFragment)
|
|||
|
{
|
|||
|
if (architecture != kPowerPC)
|
|||
|
return(-1);
|
|||
|
|
|||
|
err = FragPrepare( fragContext,
|
|||
|
mainFragment,
|
|||
|
kNoLibName,
|
|||
|
kAttachStatic,
|
|||
|
architecture, // usually PowerPC
|
|||
|
&fragConnection,
|
|||
|
&entryPoint,
|
|||
|
badLibName); // Is the errorString useful to "Joe User"?
|
|||
|
if (err == noErr)
|
|||
|
*nativeEntryPoint = NewRoutineDescriptor((ProcPtr) entryPoint,kCStackBased+kNoReturnValue,kCodeTypePower);
|
|||
|
else
|
|||
|
{
|
|||
|
// We couldn’t load a fragment, so destroy the context, and return the
|
|||
|
// error code. We could pass the badLibName up a level, but I'm not sure
|
|||
|
// this is useful to the average user. Maybe a debugging version could…
|
|||
|
|
|||
|
DebugStr("\pPrepare Failed!");
|
|||
|
|
|||
|
(void) FragRelease(fragContext,kAllConnections,kDefaultRelease,kNormalTerm);
|
|||
|
(*pNewProc->p_pcb)->fragContext = 0; // so we don’t double-release
|
|||
|
return (err);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return(noErr);
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
TearDownPsychicTVProcess(PEntryPtr pProc)
|
|||
|
{
|
|||
|
FragErr err;
|
|||
|
FragContextID fragContext;
|
|||
|
|
|||
|
fragContext = (*pProc->p_pcb)->fragContext;
|
|||
|
|
|||
|
if (fragContext)
|
|||
|
{
|
|||
|
err = FragRelease(fragContext,kAllConnections,kDefaultRelease,kNormalTerm);
|
|||
|
if (err != noErr)
|
|||
|
FragRelease(fragContext,kAllConnections,kDefaultRelease,kAbnormalTerm);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endif PsychicTV
|