/*
	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, &currenta5)) != 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
