sl@0: /* sl@0: * tclMacExit.c -- sl@0: * sl@0: * This file contains routines that deal with cleaning up various state sl@0: * when Tcl/Tk applications quit. Unfortunantly, not all state is cleaned sl@0: * up by the process when an application quites or crashes. Also you sl@0: * need to do different things depending on wether you are running as sl@0: * 68k code, PowerPC, or a code resource. The Exit handler code was sl@0: * adapted from code posted on alt.sources.mac by Dave Nebinger. sl@0: * sl@0: * Copyright (c) 1995 Dave Nebinger. sl@0: * Copyright (c) 1995-1996 Sun Microsystems, Inc. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: tclMacExit.c,v 1.4 1999/04/16 00:47:19 stanton Exp $ sl@0: */ sl@0: sl@0: #include "tclInt.h" sl@0: #include "tclMacInt.h" sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: /* sl@0: * Various typedefs and defines needed to patch ExitToShell. sl@0: */ sl@0: sl@0: enum { sl@0: uppExitToShellProcInfo = kPascalStackBased sl@0: }; sl@0: sl@0: #if GENERATINGCFM sl@0: typedef UniversalProcPtr ExitToShellUPP; sl@0: sl@0: #define CallExitToShellProc(userRoutine) \ sl@0: CallUniversalProc((UniversalProcPtr)(userRoutine),uppExitToShellProcInfo) sl@0: #define NewExitToShellProc(userRoutine) \ sl@0: (ExitToShellUPP)NewRoutineDescriptor((ProcPtr)(userRoutine), \ sl@0: uppExitToShellProcInfo, GetCurrentArchitecture()) sl@0: sl@0: #else sl@0: typedef ExitToShellProcPtr ExitToShellUPP; sl@0: sl@0: #define CallExitToShellProc(userRoutine) \ sl@0: (*(userRoutine))() sl@0: #define NewExitToShellProc(userRoutine) \ sl@0: (ExitToShellUPP)(userRoutine) sl@0: #endif sl@0: sl@0: #define DisposeExitToShellProc(userRoutine) \ sl@0: DisposeRoutineDescriptor(userRoutine) sl@0: sl@0: #if defined(powerc)||defined(__powerc) sl@0: #pragma options align=mac68k sl@0: #endif sl@0: struct ExitToShellUPPList{ sl@0: struct ExitToShellUPPList* nextProc; sl@0: ExitToShellUPP userProc; sl@0: }; sl@0: #if defined(powerc)||defined(__powerc) sl@0: #pragma options align=reset sl@0: #endif sl@0: sl@0: typedef struct ExitToShellDataStruct ExitToShellDataRec,* ExitToShellDataPtr,** ExitToShellDataHdl; sl@0: sl@0: typedef struct ExitToShellUPPList ExitToShellUPPList,* ExitToShellUPPListPtr,** ExitToShellUPPHdl; sl@0: sl@0: #if defined(powerc)||defined(__powerc) sl@0: #pragma options align=mac68k sl@0: #endif sl@0: struct ExitToShellDataStruct{ sl@0: unsigned long a5; sl@0: ExitToShellUPPList* userProcs; sl@0: ExitToShellUPP oldProc; sl@0: }; sl@0: #if defined(powerc)||defined(__powerc) sl@0: #pragma options align=reset sl@0: #endif sl@0: sl@0: /* sl@0: * Static globals used within this file. sl@0: */ sl@0: static ExitToShellDataPtr gExitToShellData = (ExitToShellDataPtr) NULL; sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclPlatformExit -- sl@0: * sl@0: * This procedure implements the Macintosh specific exit routine. sl@0: * We explicitly callthe ExitHandler function to do various clean sl@0: * up. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * We exit the process. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpExit( sl@0: int status) /* Ignored. */ sl@0: { sl@0: TclMacExitHandler(); sl@0: sl@0: /* sl@0: * If we are using the Metrowerks Standard Library, then we will call its exit so that it sl@0: * will get a chance to clean up temp files, and so forth. It always calls the standard sl@0: * ExitToShell, so the Tcl handlers will also get called. sl@0: * sl@0: * If you have another exit, make sure that it does not patch ExitToShell, and does sl@0: * call it. If so, it will probably work as well. sl@0: * sl@0: */ sl@0: sl@0: #ifdef __MSL__ sl@0: exit(status); sl@0: #else sl@0: ExitToShell(); sl@0: #endif sl@0: sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclMacExitHandler -- sl@0: * sl@0: * This procedure is invoked after Tcl at the last possible moment sl@0: * to clean up any state Tcl has left around that may cause other sl@0: * applications to crash. For example, this function can be used sl@0: * as the termination routine for CFM applications. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Various cleanup occurs. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclMacExitHandler() sl@0: { sl@0: ExitToShellUPPListPtr curProc; sl@0: sl@0: /* sl@0: * Loop through all installed Exit handlers sl@0: * and call them. Always make sure we are in sl@0: * a clean state in case we are recursivly called. sl@0: */ sl@0: if ((gExitToShellData) != NULL && (gExitToShellData->userProcs != NULL)){ sl@0: sl@0: /* sl@0: * Call the installed exit to shell routines. sl@0: */ sl@0: curProc = gExitToShellData->userProcs; sl@0: do { sl@0: gExitToShellData->userProcs = curProc->nextProc; sl@0: CallExitToShellProc(curProc->userProc); sl@0: DisposeExitToShellProc(curProc->userProc); sl@0: DisposePtr((Ptr) curProc); sl@0: curProc = gExitToShellData->userProcs; sl@0: } while (curProc != (ExitToShellUPPListPtr) NULL); sl@0: } sl@0: sl@0: return; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclMacInstallExitToShellPatch -- sl@0: * sl@0: * This procedure installs a way to clean up state at the latest sl@0: * possible moment before we exit. These are things that must sl@0: * be cleaned up or the system will crash. The exact way in which sl@0: * this is implemented depends on the architecture in which we are sl@0: * running. For 68k applications we patch the ExitToShell call. sl@0: * For PowerPC applications we just create a list of procs to call. sl@0: * The function ExitHandler should be installed in the Code sl@0: * Fragments terminiation routine. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Installs the new routine. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: OSErr sl@0: TclMacInstallExitToShellPatch( sl@0: ExitToShellProcPtr newProc) /* Function pointer. */ sl@0: { sl@0: ExitToShellUPP exitHandler; sl@0: ExitToShellUPPListPtr listPtr; sl@0: sl@0: if (gExitToShellData == (ExitToShellDataPtr) NULL){ sl@0: TclMacInitExitToShell(true); sl@0: } sl@0: sl@0: /* sl@0: * Add the passed in function pointer to the list of functions sl@0: * to be called when ExitToShell is called. sl@0: */ sl@0: exitHandler = NewExitToShellProc(newProc); sl@0: listPtr = (ExitToShellUPPListPtr) NewPtrClear(sizeof(ExitToShellUPPList)); sl@0: listPtr->userProc = exitHandler; sl@0: listPtr->nextProc = gExitToShellData->userProcs; sl@0: gExitToShellData->userProcs = listPtr; sl@0: sl@0: return noErr; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ExitToShellPatchRoutine -- sl@0: * sl@0: * This procedure is invoked when someone calls ExitToShell for sl@0: * this application. This function performs some last miniute sl@0: * clean up and then calls the real ExitToShell routine. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Various cleanup occurs. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static pascal void sl@0: ExitToShellPatchRoutine() sl@0: { sl@0: ExitToShellUPP oldETS; sl@0: long oldA5; sl@0: sl@0: /* sl@0: * Set up our A5 world. This allows us to have sl@0: * access to our global variables in the 68k world. sl@0: */ sl@0: oldA5 = SetCurrentA5(); sl@0: SetA5(gExitToShellData->a5); sl@0: sl@0: /* sl@0: * Call the function that invokes all sl@0: * of the handlers. sl@0: */ sl@0: TclMacExitHandler(); sl@0: sl@0: /* sl@0: * Call the origional ExitToShell routine. sl@0: */ sl@0: oldETS = gExitToShellData->oldProc; sl@0: DisposePtr((Ptr) gExitToShellData); sl@0: SetA5(oldA5); sl@0: CallExitToShellProc(oldETS); sl@0: return; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclMacInitExitToShell -- sl@0: * sl@0: * This procedure initializes the ExitToShell clean up machanism. sl@0: * Generally, this is handled automatically when users make a call sl@0: * to InstallExitToShellPatch. However, it can be called sl@0: * explicitly at startup time to turn off the patching mechanism. sl@0: * This can be used by code resources which could be removed from sl@0: * the application before ExitToShell is called. sl@0: * sl@0: * Note, if we are running from CFM code we never install the sl@0: * patch. Instead, the function ExitHandler should be installed sl@0: * as the terminiation routine for the code fragment. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Creates global state. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclMacInitExitToShell( sl@0: int usePatch) /* True if on 68k. */ sl@0: { sl@0: if (gExitToShellData == (ExitToShellDataPtr) NULL){ sl@0: #if GENERATINGCFM sl@0: gExitToShellData = (ExitToShellDataPtr) sl@0: NewPtr(sizeof(ExitToShellDataRec)); sl@0: gExitToShellData->a5 = SetCurrentA5(); sl@0: gExitToShellData->userProcs = (ExitToShellUPPList*) NULL; sl@0: #else sl@0: ExitToShellUPP oldExitToShell, newExitToShellPatch; sl@0: short exitToShellTrap; sl@0: sl@0: /* sl@0: * Initialize patch mechanism. sl@0: */ sl@0: sl@0: gExitToShellData = (ExitToShellDataPtr) NewPtr(sizeof(ExitToShellDataRec)); sl@0: gExitToShellData->a5 = SetCurrentA5(); sl@0: gExitToShellData->userProcs = (ExitToShellUPPList*) NULL; sl@0: sl@0: /* sl@0: * Save state needed to call origional ExitToShell routine. Install sl@0: * the new ExitToShell code in it's place. sl@0: */ sl@0: if (usePatch) { sl@0: exitToShellTrap = _ExitToShell & 0x3ff; sl@0: newExitToShellPatch = NewExitToShellProc(ExitToShellPatchRoutine); sl@0: oldExitToShell = (ExitToShellUPP) sl@0: NGetTrapAddress(exitToShellTrap, ToolTrap); sl@0: NSetTrapAddress((UniversalProcPtr) newExitToShellPatch, sl@0: exitToShellTrap, ToolTrap); sl@0: gExitToShellData->oldProc = oldExitToShell; sl@0: } sl@0: #endif sl@0: } sl@0: }