Back to index

lightning-sunbird  0.9+nobinonly
mdmac.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include <Types.h>
00039 #include <Timer.h>
00040 #include <Files.h>
00041 #include <Errors.h>
00042 #include <Folders.h>
00043 #include <Gestalt.h>
00044 #include <Events.h>
00045 #include <Processes.h>
00046 #include <TextUtils.h>
00047 #include <MixedMode.h>
00048 #include <LowMem.h>
00049 
00050 #include <fcntl.h>
00051 #include <string.h>
00052 #include <stdio.h>
00053 #include <stdlib.h>
00054 #include <stat.h>
00055 #include <stdarg.h>
00056 #include <unix.h>
00057 
00058 #include "MacErrorHandling.h"
00059 
00060 #include "primpl.h"
00061 #include "prgc.h"
00062 
00063 #include "mactime.h"
00064 
00065 #include "mdmac.h"
00066 
00067 // undefine getenv, so that _MD_GetEnv can call the version in NSStdLib::nsEnvironment.cpp.
00068 #undef getenv
00069 
00070 //
00071 // Local routines
00072 //
00073 unsigned char GarbageCollectorCacheFlusher(PRUint32 size);
00074 
00075 extern PRThread *gPrimaryThread;
00076 extern ProcessSerialNumber gApplicationProcess;     // in macthr.c
00077 
00078 
00079 //##############################################################################
00080 //##############################################################################
00081 #pragma mark -
00082 #pragma mark CREATING MACINTOSH THREAD STACKS
00083 
00084 
00085 enum {
00086        uppExitToShellProcInfo                           = kPascalStackBased,
00087        uppStackSpaceProcInfo                            = kRegisterBased 
00088                                                                         | RESULT_SIZE(SIZE_CODE(sizeof(long)))
00089                                                                         | REGISTER_RESULT_LOCATION(kRegisterD0)
00090                                                                         | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16)))
00091 };
00092 
00093 typedef CALLBACK_API( long , StackSpacePatchPtr )(UInt16 trapNo);
00094 typedef REGISTER_UPP_TYPE(StackSpacePatchPtr)    StackSpacePatchUPP;
00095 
00096 StackSpacePatchUPP     gStackSpacePatchUPP = NULL;
00097 UniversalProcPtr       gStackSpacePatchCallThru = NULL;
00098 long                        (*gCallOSTrapUniversalProc)(UniversalProcPtr,ProcInfoType,...) = NULL;
00099 
00100 
00101 pascal long StackSpacePatch(UInt16 trapNo)
00102 {
00103        char          tos;
00104        PRThread      *thisThread;
00105        
00106        thisThread = PR_CurrentThread();
00107        
00108        //     If we are the primary thread, then call through to the
00109        //     good ol' fashion stack space implementation.  Otherwise,
00110        //     compute it by hand.
00111        if ((thisThread == gPrimaryThread) ||     
00112               (&tos < thisThread->stack->stackBottom) || 
00113               (&tos > thisThread->stack->stackTop)) {
00114               return gCallOSTrapUniversalProc(gStackSpacePatchCallThru, uppStackSpaceProcInfo, trapNo);
00115        }
00116        else {
00117               return &tos - thisThread->stack->stackBottom;
00118        }
00119 }
00120 
00121 
00122 static void InstallStackSpacePatch(void)
00123 {
00124        long                        systemVersion;
00125        OSErr                       err;
00126        CFragConnectionID    connID;
00127        Str255                      errMessage;
00128        Ptr                                interfaceLibAddr;
00129        CFragSymbolClass     symClass;
00130        UniversalProcPtr     (*getOSTrapAddressProc)(UInt16);
00131        void                        (*setOSTrapAddressProc)(StackSpacePatchUPP, UInt16);
00132        UniversalProcPtr     (*newRoutineDescriptorProc)(ProcPtr,ProcInfoType,ISAType);
00133        
00134 
00135        err = Gestalt(gestaltSystemVersion,&systemVersion);
00136        if (systemVersion >= 0x00000A00)   // we don't need to patch StackSpace()
00137               return;
00138 
00139        // open connection to "InterfaceLib"
00140        err = GetSharedLibrary("\pInterfaceLib", kPowerPCCFragArch, kFindCFrag,
00141                                                                              &connID, &interfaceLibAddr, errMessage);
00142        PR_ASSERT(err == noErr);
00143        if (err != noErr)
00144               return;
00145 
00146        // get symbol GetOSTrapAddress
00147        err = FindSymbol(connID, "\pGetOSTrapAddress", &(Ptr)getOSTrapAddressProc, &symClass);
00148        if (err != noErr)
00149               return;
00150 
00151        // get symbol SetOSTrapAddress
00152        err = FindSymbol(connID, "\pSetOSTrapAddress", &(Ptr)setOSTrapAddressProc, &symClass);
00153        if (err != noErr)
00154               return;
00155        
00156        // get symbol NewRoutineDescriptor
00157        err = FindSymbol(connID, "\pNewRoutineDescriptor", &(Ptr)newRoutineDescriptorProc, &symClass);
00158        if (err != noErr)
00159               return;
00160        
00161        // get symbol CallOSTrapUniversalProc
00162        err = FindSymbol(connID, "\pCallOSTrapUniversalProc", &(Ptr)gCallOSTrapUniversalProc, &symClass);
00163        if (err != noErr)
00164               return;
00165 
00166        // get and set trap address for StackSpace (A065)
00167        gStackSpacePatchCallThru = getOSTrapAddressProc(0x0065);
00168        if (gStackSpacePatchCallThru)
00169        {
00170               gStackSpacePatchUPP =
00171                      (StackSpacePatchUPP)newRoutineDescriptorProc((ProcPtr)(StackSpacePatch), uppStackSpaceProcInfo, GetCurrentArchitecture());
00172               setOSTrapAddressProc(gStackSpacePatchUPP, 0x0065);
00173        }
00174 
00175 #if DEBUG
00176        StackSpace();
00177 #endif
00178 }
00179 
00180 
00181 //##############################################################################
00182 //##############################################################################
00183 #pragma mark -
00184 #pragma mark ENVIRONMENT VARIABLES
00185 
00186 
00187 typedef struct EnvVariable EnvVariable;
00188 
00189 struct EnvVariable {
00190        char                 *variable;
00191        char                 *value;
00192        EnvVariable          *next;
00193 };
00194 
00195 EnvVariable          *gEnvironmentVariables = NULL;
00196 
00197 char *_MD_GetEnv(const char *name)
00198 {
00199        EnvVariable   *currentVariable = gEnvironmentVariables;
00200 
00201        while (currentVariable) {
00202               if (!strcmp(currentVariable->variable, name))
00203                      return currentVariable->value;
00204                      
00205               currentVariable = currentVariable->next;
00206        }
00207 
00208        return getenv(name);
00209 }
00210 
00211 PR_IMPLEMENT(int) 
00212 _MD_PutEnv(const char *string)
00213 {
00214        EnvVariable   *currentVariable = gEnvironmentVariables;
00215        char                 *variableCopy,
00216                                 *value,
00217                                    *current;
00218                                    
00219        variableCopy = strdup(string);
00220        PR_ASSERT(variableCopy != NULL);
00221 
00222        current = variableCopy;
00223        while (*current != '=')
00224               current++;
00225 
00226        *current = 0;
00227        current++;
00228 
00229        value = current;
00230 
00231        while (currentVariable) {
00232               if (!strcmp(currentVariable->variable, variableCopy))
00233                      break;
00234               
00235               currentVariable = currentVariable->next;
00236        }
00237 
00238        if (currentVariable == NULL) {
00239               currentVariable = PR_NEW(EnvVariable);
00240               
00241               if (currentVariable == NULL) {
00242                      PR_DELETE(variableCopy);
00243                      return -1;
00244               }
00245               
00246               currentVariable->variable = strdup(variableCopy);
00247               currentVariable->value = strdup(value);
00248               currentVariable->next = gEnvironmentVariables;
00249               gEnvironmentVariables = currentVariable;
00250        }
00251        
00252        else {
00253               PR_DELETE(currentVariable->value);
00254               currentVariable->value = strdup(current);
00255 
00256               /* This is a temporary hack.  Working on a real fix, remove this when done. */
00257               /* OK, there are two ways to access the  */
00258               /* library path, getenv() and PR_GetLibraryPath().  Take a look at PR_GetLibraryPath(). */
00259               /* You'll see that we keep the path in a global which is intialized at startup from */
00260               /* a call to getenv().  From then on, they have nothing in common. */
00261               /* We need to keep them in synch.  */
00262               if (strcmp(currentVariable->variable, "LD_LIBRARY_PATH") == 0)
00263                      PR_SetLibraryPath(currentVariable->value);
00264        }
00265        
00266        PR_DELETE(variableCopy);
00267        return 0;
00268 }
00269 
00270 
00271 
00272 //##############################################################################
00273 //##############################################################################
00274 #pragma mark -
00275 #pragma mark MISCELLANEOUS
00276 
00277 PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
00278 {
00279     if (isCurrent) {
00280        (void) setjmp(t->md.jb);
00281     }
00282     *np = sizeof(t->md.jb) / sizeof(PRUint32);
00283     return (PRWord*) (t->md.jb);
00284 }
00285 
00286 void _MD_GetRegisters(PRUint32 *to)
00287 {
00288   (void) setjmp((void*) to);
00289 }
00290 
00291 void _MD_EarlyInit()
00292 {
00293        Handle                      environmentVariables;
00294 
00295        GetCurrentProcess(&gApplicationProcess);
00296 
00297        INIT_CRITICAL_REGION();
00298        InitIdleSemaphore();
00299 
00300 #if !defined(MAC_NSPR_STANDALONE)
00301        // MacintoshInitializeMemory();  Moved to mdmacmem.c: AllocateRawMemory(Size blockSize)
00302 #else
00303        MacintoshInitializeMemory();
00304 #endif
00305        MacintoshInitializeTime();
00306        
00307        //     Install resource-controlled environment variables.
00308        
00309        environmentVariables = GetResource('Envi', 128);
00310        if (environmentVariables != NULL) {
00311        
00312               Size   resourceSize;
00313               char   *currentPutEnvString = (char *)*environmentVariables,
00314                             *currentScanChar = currentPutEnvString;
00315                             
00316               resourceSize = GetHandleSize(environmentVariables);                   
00317               DetachResource(environmentVariables);
00318               HLock(environmentVariables);
00319               
00320               while (resourceSize--) {
00321               
00322                      if ((*currentScanChar == '\n') || (*currentScanChar == '\r')) {
00323                             *currentScanChar = 0;
00324                             _MD_PutEnv (currentPutEnvString);
00325                             currentPutEnvString = currentScanChar + 1;
00326                      }
00327               
00328                      currentScanChar++;
00329               
00330               }
00331               
00332               DisposeHandle(environmentVariables);
00333 
00334        }
00335 
00336 #ifdef PR_INTERNAL_LOGGING
00337        _MD_PutEnv ("NSPR_LOG_MODULES=clock:6,cmon:6,io:6,mon:6,linker:6,cvar:6,sched:6,thread:6");
00338 #endif
00339 
00340        InstallStackSpacePatch();
00341 }
00342 
00343 void _MD_FinalInit()
00344 {
00345        _MD_InitNetAccess();
00346 }
00347 
00348 void PR_InitMemory(void) {
00349 #ifndef NSPR_AS_SHARED_LIB
00350        //     Needed for Mac browsers without Java.  We don't want them calling PR_INIT, since it
00351        //     brings in all of the thread support.  But we do need to allow them to initialize
00352        //     the NSPR memory package.
00353        //     This should go away when all clients of the NSPR want threads AND memory.
00354        MacintoshInitializeMemory();
00355 #endif
00356 }
00357 
00358 //##############################################################################
00359 //##############################################################################
00360 #pragma mark -
00361 #pragma mark TERMINATION
00362 
00363 
00364 //     THIS IS *** VERY *** IMPORTANT... our CFM Termination proc.
00365 //     This allows us to deactivate our Time Mananger task even
00366 //     if we are not totally gracefully exited.  If this is not
00367 //     done then we will randomly crash at later times when the
00368 //     task is called after the app heap is gone.
00369 
00370 #if TARGET_CARBON
00371 extern OTClientContextPtr   clientContext;
00372 #define CLOSE_OPEN_TRANSPORT()     CloseOpenTransportInContext(clientContext)
00373 
00374 #else
00375 
00376 #define CLOSE_OPEN_TRANSPORT()     CloseOpenTransport()
00377 #endif /* TARGET_CARBON */
00378 
00379 extern pascal void __NSTerminate(void);
00380 
00381 void CleanupTermProc(void)
00382 {
00383        _MD_StopInterrupts();       // deactive Time Manager task
00384 
00385        CLOSE_OPEN_TRANSPORT();
00386        TermIdleSemaphore();
00387        TERM_CRITICAL_REGION();
00388        
00389        __NSTerminate();
00390 }
00391 
00392 
00393 
00394 //##############################################################################
00395 //##############################################################################
00396 #pragma mark -
00397 #pragma mark STRING OPERATIONS
00398 
00399 #if !defined(MAC_NSPR_STANDALONE)
00400 
00401 //     PStrFromCStr converts the source C string to a destination
00402 //     pascal string as it copies. The dest string will
00403 //     be truncated to fit into an Str255 if necessary.
00404 //  If the C String pointer is NULL, the pascal string's length is set to zero
00405 //
00406 void 
00407 PStrFromCStr(const char* src, Str255 dst)
00408 {
00409        short  length  = 0;
00410        
00411        // handle case of overlapping strings
00412        if ( (void*)src == (void*)dst )
00413        {
00414               unsigned char*              curdst = &dst[1];
00415               unsigned char        thisChar;
00416                             
00417               thisChar = *(const unsigned char*)src++;
00418               while ( thisChar != '\0' ) 
00419               {
00420                      unsigned char nextChar;
00421                      
00422                      // use nextChar so we don't overwrite what we are about to read
00423                      nextChar = *(const unsigned char*)src++;
00424                      *curdst++ = thisChar;
00425                      thisChar = nextChar;
00426                      
00427                      if ( ++length >= 255 )
00428                             break;
00429               }
00430        }
00431        else if ( src != NULL )
00432        {
00433               unsigned char*              curdst = &dst[1];
00434               short                       overflow = 255;             // count down so test it loop is faster
00435               register char        temp;
00436        
00437               // Can't do the K&R C thing of "while (*s++ = *t++)" because it will copy trailing zero
00438               // which might overrun pascal buffer.  Instead we use a temp variable.
00439               while ( (temp = *src++) != 0 ) 
00440               {
00441                      *(char*)curdst++ = temp;
00442                             
00443                      if ( --overflow <= 0 )
00444                             break;
00445               }
00446               length = 255 - overflow;
00447        }
00448        dst[0] = length;
00449 }
00450 
00451 
00452 void CStrFromPStr(ConstStr255Param pString, char **cString)
00453 {
00454        // Allocates a cString and copies a Pascal string into it.
00455        unsigned int  len;
00456        
00457        len = pString[0];
00458        *cString = malloc(len+1);
00459        
00460        if (*cString != NULL) {
00461               strncpy(*cString, (char *)&pString[1], len);
00462               (*cString)[len] = NULL;
00463        }
00464 }
00465 
00466 
00467 void dprintf(const char *format, ...)
00468 {
00469 #if DEBUG
00470     va_list ap;
00471        Str255 buffer;
00472        
00473        va_start(ap, format);
00474        buffer[0] = PR_vsnprintf((char *)buffer + 1, sizeof(buffer) - 1, format, ap);
00475        va_end(ap);
00476        
00477        DebugStr(buffer);
00478 #endif /* DEBUG */
00479 }
00480 
00481 #else
00482 
00483 void debugstr(const char *debuggerMsg)
00484 {
00485        Str255        pStr;
00486        
00487        PStrFromCStr(debuggerMsg, pStr);
00488        DebugStr(pStr);
00489 }
00490 
00491 
00492 char *strdup(const char *source)
00493 {
00494        char   *newAllocation;
00495        size_t stringLength;
00496 
00497        PR_ASSERT(source);
00498        
00499        stringLength = strlen(source) + 1;
00500        
00501        newAllocation = (char *)PR_MALLOC(stringLength);
00502        if (newAllocation == NULL)
00503               return NULL;
00504        BlockMoveData(source, newAllocation, stringLength);
00505        return newAllocation;
00506 }
00507 
00508 //     PStrFromCStr converts the source C string to a destination
00509 //     pascal string as it copies. The dest string will
00510 //     be truncated to fit into an Str255 if necessary.
00511 //  If the C String pointer is NULL, the pascal string's length is set to zero
00512 //
00513 void PStrFromCStr(const char* src, Str255 dst)
00514 {
00515        short  length  = 0;
00516        
00517        // handle case of overlapping strings
00518        if ( (void*)src == (void*)dst )
00519        {
00520               unsigned char*              curdst = &dst[1];
00521               unsigned char        thisChar;
00522                             
00523               thisChar = *(const unsigned char*)src++;
00524               while ( thisChar != '\0' ) 
00525               {
00526                      unsigned char nextChar;
00527                      
00528                      // use nextChar so we don't overwrite what we are about to read
00529                      nextChar = *(const unsigned char*)src++;
00530                      *curdst++ = thisChar;
00531                      thisChar = nextChar;
00532                      
00533                      if ( ++length >= 255 )
00534                             break;
00535               }
00536        }
00537        else if ( src != NULL )
00538        {
00539               unsigned char*              curdst = &dst[1];
00540               short                       overflow = 255;             // count down so test it loop is faster
00541               register char        temp;
00542        
00543               // Can't do the K&R C thing of "while (*s++ = *t++)" because it will copy trailing zero
00544               // which might overrun pascal buffer.  Instead we use a temp variable.
00545               while ( (temp = *src++) != 0 ) 
00546               {
00547                      *(char*)curdst++ = temp;
00548                             
00549                      if ( --overflow <= 0 )
00550                             break;
00551               }
00552               length = 255 - overflow;
00553        }
00554        dst[0] = length;
00555 }
00556 
00557 
00558 void CStrFromPStr(ConstStr255Param pString, char **cString)
00559 {
00560        // Allocates a cString and copies a Pascal string into it.
00561        unsigned int  len;
00562        
00563        len = pString[0];
00564        *cString = PR_MALLOC(len+1);
00565        
00566        if (*cString != NULL) {
00567               strncpy(*cString, (char *)&pString[1], len);
00568               (*cString)[len] = NULL;
00569        }
00570 }
00571 
00572 
00573 size_t strlen(const char *source)
00574 {
00575        size_t currentLength = 0;
00576        
00577        if (source == NULL)
00578               return currentLength;
00579                      
00580        while (*source++ != '\0')
00581               currentLength++;
00582               
00583        return currentLength;
00584 }
00585 
00586 int strcmpcore(const char *str1, const char *str2, int caseSensitive)
00587 {
00588        char   currentChar1, currentChar2;
00589 
00590        while (1) {
00591        
00592               currentChar1 = *str1;
00593               currentChar2 = *str2;
00594               
00595               if (!caseSensitive) {
00596                      
00597                      if ((currentChar1 >= 'a') && (currentChar1 <= 'z'))
00598                             currentChar1 += ('A' - 'a');
00599               
00600                      if ((currentChar2 >= 'a') && (currentChar2 <= 'z'))
00601                             currentChar2 += ('A' - 'a');
00602               
00603               }
00604        
00605               if (currentChar1 == '\0')
00606                      break;
00607        
00608               if (currentChar1 != currentChar2)
00609                      return currentChar1 - currentChar2;
00610                      
00611               str1++;
00612               str2++;
00613        
00614        }
00615        
00616        return currentChar1 - currentChar2;
00617 }
00618 
00619 int strcmp(const char *str1, const char *str2)
00620 {
00621        return strcmpcore(str1, str2, true);
00622 }
00623 
00624 int strcasecmp(const char *str1, const char *str2)
00625 {
00626        return strcmpcore(str1, str2, false);
00627 }
00628 
00629 
00630 void *memcpy(void *to, const void *from, size_t size)
00631 {
00632        if (size != 0) {
00633 #if DEBUG
00634               if ((UInt32)to < 0x1000)
00635                      DebugStr("\pmemcpy has illegal to argument");
00636               if ((UInt32)from < 0x1000)
00637                      DebugStr("\pmemcpy has illegal from argument");
00638 #endif
00639               BlockMoveData(from, to, size);
00640        }
00641        return to;
00642 }
00643 
00644 void dprintf(const char *format, ...)
00645 {
00646     va_list ap;
00647        char   *buffer;
00648        
00649        va_start(ap, format);
00650        buffer = (char *)PR_vsmprintf(format, ap);
00651        va_end(ap);
00652        
00653        debugstr(buffer);
00654        PR_DELETE(buffer);
00655 }
00656 
00657 void
00658 exit(int result)
00659 {
00660 #pragma unused (result)
00661 
00662               ExitToShell();
00663 }
00664 
00665 void abort(void)
00666 {
00667        exit(-1);
00668 }
00669 
00670 #endif
00671 
00672 //##############################################################################
00673 //##############################################################################
00674 #pragma mark -
00675 #pragma mark FLUSHING THE GARBAGE COLLECTOR
00676 
00677 #if !defined(MAC_NSPR_STANDALONE)
00678 
00679 unsigned char GarbageCollectorCacheFlusher(PRUint32)
00680 {
00681 
00682     PRIntn is;
00683 
00684        UInt32        oldPriority;
00685 
00686        //     If java wasn't completely initialized, then bail 
00687        //     harmlessly.
00688        
00689        if (PR_GetGCInfo()->lock == NULL)
00690               return false;
00691 
00692 #if DEBUG
00693        if (_MD_GET_INTSOFF() == 1)
00694               DebugStr("\pGarbageCollectorCacheFlusher at interrupt time!");
00695 #endif
00696 
00697        //     The synchronization here is very tricky.  We really
00698        //     don't want any other threads to run while we are 
00699        //     cleaning up the gc heap... they could call malloc,
00700        //     and then we would be in trouble in a big way.  So,
00701        //     we jack up our priority and that of the finalizer
00702        //     so that we won't yield to other threads.
00703        //     dkc 5/17/96
00704 
00705        oldPriority = PR_GetThreadPriority(PR_GetCurrentThread());
00706        _PR_INTSOFF(is);
00707        _PR_SetThreadPriority(PR_GetCurrentThread(), (PRThreadPriority)30);
00708        _PR_INTSON(is);
00709 
00710        //     Garbage collect twice.  This will finalize any
00711        //     dangling AWT resources (images, components), and
00712        //     then free up their GC space, too.
00713        //     dkc 2/15/96
00714        //  interrupts must be on during PR_GC
00715        
00716        PR_GC();
00717        
00718        //     By setting the finalizer priority to 31, then we 
00719        //     ensure it will run before us.  When it finishes
00720        //     its list of finalizations, it returns to us
00721        //     for the second garbage collection.
00722        
00723        PR_Yield();
00724 
00725        PR_GC();
00726        
00727        //     Restore our old priorities.
00728        
00729        _PR_INTSOFF(is);
00730        _PR_SetThreadPriority(PR_GetCurrentThread(), (PRThreadPriority)oldPriority);
00731        _PR_INTSON(is);
00732 
00733        return false;
00734 }
00735 
00736 #endif
00737 
00738 //##############################################################################
00739 //##############################################################################
00740 #pragma mark -
00741 #pragma mark MISCELLANEOUS-HACKS
00742 
00743 
00744 //
00745 //            ***** HACK  FIX THESE ****
00746 //
00747 extern long _MD_GetOSName(char *buf, long count)
00748 {
00749        long   len;
00750        
00751        len = PR_snprintf(buf, count, "Mac OS");
00752        
00753        return 0;
00754 }
00755 
00756 extern long _MD_GetOSVersion(char *buf, long count)
00757 {
00758        long   len;
00759        
00760        len = PR_snprintf(buf, count, "7.5");
00761 
00762        return 0;
00763 }
00764 
00765 extern long _MD_GetArchitecture(char *buf, long count)
00766 {
00767        long   len;
00768        
00769 #if defined(TARGET_CPU_PPC) && TARGET_CPU_PPC    
00770        len = PR_snprintf(buf, count, "PowerPC");
00771 #else
00772        len = PR_snprintf(buf, count, "Motorola68k");
00773 #endif
00774 
00775        return 0;
00776 }