OSDN Git Service

2003-07-09 Chris Demetriou <cgd@broadcom.com>
[pf3gnuchains/pf3gnuchains3x.git] / tcl / mac / tclMacExit.c
1 /* 
2  * tclMacExit.c --
3  *
4  *      This file contains routines that deal with cleaning up various state
5  *      when Tcl/Tk applications quit.  Unfortunantly, not all state is cleaned
6  *      up by the process when an application quites or crashes.  Also you
7  *      need to do different things depending on wether you are running as
8  *      68k code, PowerPC, or a code resource.  The Exit handler code was 
9  *      adapted from code posted on alt.sources.mac by Dave Nebinger.
10  *
11  * Copyright (c) 1995 Dave Nebinger.
12  * Copyright (c) 1995-1996 Sun Microsystems, Inc.
13  *
14  * See the file "license.terms" for information on usage and redistribution
15  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16  *
17  * RCS: @(#) $Id$
18  */
19
20 #include "tclInt.h"
21 #include "tclMacInt.h"
22 #include <SegLoad.h>
23 #include <Traps.h>
24 #include <Processes.h>
25
26 /*
27  * Various typedefs and defines needed to patch ExitToShell.
28  */
29  
30 enum {
31         uppExitToShellProcInfo = kPascalStackBased
32 };
33
34 #if GENERATINGCFM
35 typedef UniversalProcPtr ExitToShellUPP;
36
37 #define CallExitToShellProc(userRoutine)        \
38         CallUniversalProc((UniversalProcPtr)(userRoutine),uppExitToShellProcInfo)
39 #define NewExitToShellProc(userRoutine) \
40         (ExitToShellUPP)NewRoutineDescriptor((ProcPtr)(userRoutine), \
41                 uppExitToShellProcInfo, GetCurrentArchitecture())
42
43 #else
44 typedef ExitToShellProcPtr ExitToShellUPP;
45
46 #define CallExitToShellProc(userRoutine)        \
47         (*(userRoutine))()
48 #define NewExitToShellProc(userRoutine) \
49         (ExitToShellUPP)(userRoutine)
50 #endif
51
52 #define DisposeExitToShellProc(userRoutine) \
53         DisposeRoutineDescriptor(userRoutine)
54
55 #if defined(powerc)||defined(__powerc)
56 #pragma options align=mac68k
57 #endif
58 struct ExitToShellUPPList{
59         struct ExitToShellUPPList* nextProc;
60         ExitToShellUPP userProc;
61 };
62 #if defined(powerc)||defined(__powerc)
63 #pragma options align=reset
64 #endif
65
66 typedef struct ExitToShellDataStruct ExitToShellDataRec,* ExitToShellDataPtr,** ExitToShellDataHdl;
67
68 typedef struct ExitToShellUPPList ExitToShellUPPList,* ExitToShellUPPListPtr,** ExitToShellUPPHdl;
69
70 #if defined(powerc)||defined(__powerc)
71 #pragma options align=mac68k
72 #endif
73 struct ExitToShellDataStruct{
74     unsigned long a5;
75     ExitToShellUPPList* userProcs;
76     ExitToShellUPP oldProc;
77 };
78 #if defined(powerc)||defined(__powerc)
79 #pragma options align=reset
80 #endif
81
82 /*
83  * Static globals used within this file.
84  */
85 static ExitToShellDataPtr gExitToShellData = (ExitToShellDataPtr) NULL;
86
87 \f
88 /*
89  *----------------------------------------------------------------------
90  *
91  * TclPlatformExit --
92  *
93  *      This procedure implements the Macintosh specific exit routine.
94  *      We explicitly callthe ExitHandler function to do various clean
95  *      up.  
96  *
97  * Results:
98  *      None.
99  *
100  * Side effects:
101  *      We exit the process.
102  *
103  *----------------------------------------------------------------------
104  */
105
106 void
107 TclpExit(
108     int status)         /* Ignored. */
109 {
110     TclMacExitHandler();
111
112 /* 
113  * If we are using the Metrowerks Standard Library, then we will call its exit so that it
114  * will get a chance to clean up temp files, and so forth.  It always calls the standard 
115  * ExitToShell, so the Tcl handlers will also get called.
116  *   
117  * If you have another exit, make sure that it does not patch ExitToShell, and does
118  * call it.  If so, it will probably work as well.
119  *
120  */
121  
122 #ifdef __MSL__    
123     exit(status);
124 #else
125     ExitToShell();
126 #endif
127
128 }
129 \f
130 /*
131  *----------------------------------------------------------------------
132  *
133  * TclMacExitHandler --
134  *
135  *      This procedure is invoked after Tcl at the last possible moment
136  *      to clean up any state Tcl has left around that may cause other
137  *      applications to crash.  For example, this function can be used
138  *      as the termination routine for CFM applications.
139  *
140  * Results:
141  *      None.
142  *
143  * Side effects:
144  *      Various cleanup occurs.
145  *
146  *----------------------------------------------------------------------
147  */
148
149 void
150 TclMacExitHandler()
151 {
152     ExitToShellUPPListPtr curProc;
153
154     /*
155      * Loop through all installed Exit handlers
156      * and call them.  Always make sure we are in
157      * a clean state in case we are recursivly called.
158      */
159     if ((gExitToShellData) != NULL && (gExitToShellData->userProcs != NULL)){
160     
161         /*
162          * Call the installed exit to shell routines.
163          */
164         curProc = gExitToShellData->userProcs;
165         do {
166             gExitToShellData->userProcs = curProc->nextProc;
167             CallExitToShellProc(curProc->userProc);
168             DisposeExitToShellProc(curProc->userProc);
169             DisposePtr((Ptr) curProc);
170             curProc = gExitToShellData->userProcs;
171         } while (curProc != (ExitToShellUPPListPtr) NULL);
172     }
173
174     return;
175 }
176 \f
177 /*
178  *----------------------------------------------------------------------
179  *
180  * TclMacInstallExitToShellPatch --
181  *
182  *      This procedure installs a way to clean up state at the latest
183  *      possible moment before we exit.  These are things that must
184  *      be cleaned up or the system will crash.  The exact way in which
185  *      this is implemented depends on the architecture in which we are
186  *      running.  For 68k applications we patch the ExitToShell call.
187  *      For PowerPC applications we just create a list of procs to call.
188  *      The function ExitHandler should be installed in the Code 
189  *      Fragments terminiation routine.
190  *
191  * Results:
192  *      None.
193  *
194  * Side effects:
195  *      Installs the new routine.
196  *
197  *----------------------------------------------------------------------
198  */
199
200 OSErr 
201 TclMacInstallExitToShellPatch(
202     ExitToShellProcPtr newProc)         /* Function pointer. */
203 {
204     ExitToShellUPP exitHandler;
205     ExitToShellUPPListPtr listPtr;
206
207     if (gExitToShellData == (ExitToShellDataPtr) NULL){
208         TclMacInitExitToShell(true);
209     }
210
211     /*
212      * Add the passed in function pointer to the list of functions
213      * to be called when ExitToShell is called.
214      */
215     exitHandler = NewExitToShellProc(newProc);
216     listPtr = (ExitToShellUPPListPtr) NewPtrClear(sizeof(ExitToShellUPPList));
217     listPtr->userProc = exitHandler;
218     listPtr->nextProc = gExitToShellData->userProcs;
219     gExitToShellData->userProcs = listPtr;
220
221     return noErr;
222 }
223 \f
224 /*
225  *----------------------------------------------------------------------
226  *
227  * ExitToShellPatchRoutine --
228  *
229  *      This procedure is invoked when someone calls ExitToShell for
230  *      this application.  This function performs some last miniute
231  *      clean up and then calls the real ExitToShell routine.
232  *
233  * Results:
234  *      None.
235  *
236  * Side effects:
237  *      Various cleanup occurs.
238  *
239  *----------------------------------------------------------------------
240  */
241
242 static pascal void
243 ExitToShellPatchRoutine()
244 {
245     ExitToShellUPP oldETS;
246     long oldA5;
247
248     /*
249      * Set up our A5 world.  This allows us to have
250      * access to our global variables in the 68k world.
251      */
252     oldA5 = SetCurrentA5();
253     SetA5(gExitToShellData->a5);
254
255     /*
256      * Call the function that invokes all
257      * of the handlers.
258      */
259     TclMacExitHandler();
260
261     /*
262      * Call the origional ExitToShell routine.
263      */
264     oldETS = gExitToShellData->oldProc;
265     DisposePtr((Ptr) gExitToShellData);
266     SetA5(oldA5);
267     CallExitToShellProc(oldETS);
268     return;
269 }
270 \f
271 /*
272  *----------------------------------------------------------------------
273  *
274  * TclMacInitExitToShell --
275  *
276  *      This procedure initializes the ExitToShell clean up machanism.
277  *      Generally, this is handled automatically when users make a call
278  *      to InstallExitToShellPatch.  However, it can be called 
279  *      explicitly at startup time to turn off the patching mechanism.
280  *      This can be used by code resources which could be removed from
281  *      the application before ExitToShell is called.
282  *
283  *      Note, if we are running from CFM code we never install the
284  *      patch.  Instead, the function ExitHandler should be installed
285  *      as the terminiation routine for the code fragment.
286  *
287  * Results:
288  *      None.
289  *
290  * Side effects:
291  *      Creates global state.
292  *
293  *----------------------------------------------------------------------
294  */
295
296 void 
297 TclMacInitExitToShell(
298     int usePatch)       /* True if on 68k. */
299 {
300     if (gExitToShellData == (ExitToShellDataPtr) NULL){
301 #if GENERATINGCFM
302         gExitToShellData = (ExitToShellDataPtr)
303           NewPtr(sizeof(ExitToShellDataRec));
304         gExitToShellData->a5 = SetCurrentA5();
305         gExitToShellData->userProcs = (ExitToShellUPPList*) NULL;
306 #else
307         ExitToShellUPP oldExitToShell, newExitToShellPatch;
308         short exitToShellTrap;
309         
310         /*
311          * Initialize patch mechanism.
312          */
313          
314         gExitToShellData = (ExitToShellDataPtr) NewPtr(sizeof(ExitToShellDataRec));
315         gExitToShellData->a5 = SetCurrentA5();
316         gExitToShellData->userProcs = (ExitToShellUPPList*) NULL;
317
318         /*
319          * Save state needed to call origional ExitToShell routine.  Install
320          * the new ExitToShell code in it's place.
321          */
322         if (usePatch) {
323             exitToShellTrap = _ExitToShell & 0x3ff;
324             newExitToShellPatch = NewExitToShellProc(ExitToShellPatchRoutine);
325             oldExitToShell = (ExitToShellUPP)
326               NGetTrapAddress(exitToShellTrap, ToolTrap);
327             NSetTrapAddress((UniversalProcPtr) newExitToShellPatch,
328                     exitToShellTrap, ToolTrap);
329             gExitToShellData->oldProc = oldExitToShell;
330         }
331 #endif
332     }
333 }