//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/***
*crt0dat.c - 32-bit C run-time initialization/termination routines
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*       This module contains the routines _cinit, exit, and _exit
*       for C run-time startup and termination.  _cinit and exit
*       are called from the _astart code in crt0.asm.
*       This module also defines several data variables used by the
*       runtime.
*
*       [NOTE: Lock segment definitions are at end of module.]
*
*Revision History:
*       06-28-89  PHG   Module created, based on asm version
*       04-09-90  GJF   Added #include <cruntime.h>. Made calling type
*                       explicit (_CALLTYPE1 or _CALLTYPE4). Also, fixed
*                       the copyright.
*       04-10-90  GJF   Fixed compiler warnings (-W3).
*       05-21-90  GJF   Added #undef _NFILE_ (temporary hack) and fixed the
*                       indents.
*       08-31-90  GJF   Removed 32 from API names.
*       09-25-90  GJF   Merged tree version with local (8-31 and 5-21 changes).
*       10-08-90  GJF   New-style function declarators.
*       10-12-90  GJF   Removed divide by 0 stuff.
*       10-18-90  GJF   Added _pipech[] array.
*       11-05-90  GJF   Added _umaskval.
*       12-04-90  GJF   Added _osfinfo[] definition for Win32 target. Note that
*                       the Win32 support is still incomplete!
*       12-04-90  SRW   Changed to include <oscalls.h> instead of <doscalls.h>
*       12-04-90  SRW   Added _osfile back for win32.  Changed _osfinfo from
*                       an array of structures to an array of 32-bit handles
*                       (_osfhnd)
*       12-28-90  SRW   Added _CRUISER_ conditional around pack pragmas
*       01-29-91  GJF   ANSI naming.
*       01-29-91  SRW   Added call to GetFileType [_WIN32_]
*       02-18-91  SRW   Removed duplicate defintion of _NFILE_ (see mtdll.h)
*       04-04-91  GJF   Added definitions for _base[version|major|minor]
*                       (_WIN32_).
*       04-08-91  GJF   Temporary hack for Win32/DOS folks - added HeapDestroy
*                       call to doexit to tear down the OS heap used by C
*                       heap.
*       04-09-91  PNT   Added _MAC_ conditional
*       04-26-91  SRW   Removed level 3 warnings
*       07-16-91  GJF   Added fp initialization test-and-call [_WIN32_].
*       07-26-91  GJF   Revised initialization and termination stuff. In
*                       particular, removed need for win32ini.c [_WIN32_].
*       08-07-91  GJF   Added init. for FORTRAN runtime, if present [_WIN32_].
*       08-21-91  GJF   Test _prmtmp against NULL, not _prmtmp().
*       08-21-91  JCR   Added _exitflag, _endstdio, _cpumode, etc.
*       09-09-91  GJF   Revised _doinitterm for C++ init. support and to make
*                       _onexit/atexit compatible with C++ needs.
*       09-16-91  GJF   Must test __onexitend before calling _doinitterm.
*       10-29-91  GJF   Force in floating point initialization for MIPS
*                       compiler [_WIN32_].
*       11-13-91  GJF   FORTRAN needs _onexit/atexit init. before call thru
*                       _pFFinit.
*       12-29-91  RID   MAC module created, based on OS2 version
*       01-10-92  GJF   Merged. Also, added _C_Termination_Done [_WIN32_].
*       02-13-92  GJF   Moved all lowio initialization to ioinit.c for Win32.
*       03-12-92  SKS   Major changes to initialization/termination scheme
*       04-01-92  XY    implemented new intialization/termination schema (MAC)
*       04-16-92  DJM   POSIX support.
*       04-17-92  SKS   Export _initterm() for CRTDLL model
*       05-07-92  DJM   Removed _exit() from POSIX build.
*       06-03-92  GJF   Temporarily restored call to FORTRAN init.
*       08-26-92  SKS   Add _osver, _winver, _winmajor, _winminor
*       08-28-92  GJF   Use unistd.h for POSIX build.
*       09-02-92  SKS   Fix _onexit table traversal to be LIFO.
*                       Since table is built forward (my changes 03-12-92)
*                       the table must be traversed in reverse order.
*       11-12-92  SKS   Remove hard-coded call to FORTRAN initializer
*       03-20-93  SKS   Remove obsolete variables _osmode, _cpumode, etc.
*       04-06-93  SKS   Replace _CRTAPI* with __cdecl
*       04-07-93  SKS   Change to __declspec(dllexport) for CRT DLL model
*       04-19-93  SKS   Remove obsolete variable _child.
*       04-20-93  SKS   _C_Termination_Done is now used by DLLs in LIBC/LIBCMT
*                       models, not just in MSVCRT10.DLL.
*       07-16-93  SRW   ALPHA Merge
*       09-21-93  CFW   Move _initmbctable call to _cinit().
*       10-19-93  GJF   Merged NT and Cuda versions. Cleaned out a lot of old
*                       Cruiser and Dosx32 support. Replaced MTHREAD with
*                       _MT, _MIPS_ with _M_MRX000, _ALPHA_ with _M_ALPHA.
*       11-09-93  GJF   Moved _initmbctable call back into crt0.c.
*       11-09-93  GJF   Replaced PF with _PVFV (defined in internal.h).
*       11-19-93  CFW   Add _wargv, _wpgmptr.
*       11-29-93  CFW   Add _wenviron.
*       12-15-93  CFW   Set _pgmptr, _wpgmptr to NULL (MIPS compiler bug).
*       02-04-94  CFW   Add _[w]initenv.
*       03-25-94  GJF   Made definitions of:
*                           __argc,
*                           __argv,     __wargv,
*                           _C_Termination_Done,
*                           _environ,   _wenviron,
*                           _exitflag,
*                           __initenv,  __winitenv,
*                           __onexitbegin, __onexitend,
*                           _osver,
*                           _pgmptr,    _wpgmptr,
*                           _winmajor,
*                           _winminor
*                           _winver
*                       conditional on ndef DLL_FOR_WIN32S.
*       10-02-94  BWT   Add PPC changes
*       12-03-94  SKS   Clean up OS/2 references
*       02-11-95  CFW   PPC -> _M_MPPC.
*       02-16-95  JWM   Spliced _WIN32 & Mac versions.
*       02-24-95  CFW   Call _CrtDumpMemoryLeaks.
*       02-27-95  CFW   Make _CrtDumpMemoryLeaks call conditional
*       04-06-95  CFW   Only check for static libs, avoid infinite loop.
*       07-24-95  CFW   Call _CrtDumpMemoryLeaks for PMac.
*       12-18-95  JWM   doexit() can no longer be called recursively.
*       08-01-96  RDK   For PMac, define _osflagfiles, cleaned up Gestalt test,
*                       and make termination parallel x86 functionality.
*       07-24-97  GJF   Added __env_initialized flag.
*       08-06-97  GJF   Moved __mbctype_initialized flag here from crt0.c.
*       09-26-97  BWT   Fix POSIX
*       10-02-98  GJF   Added _osplatform.
*       11-13-98  KBF   Only do an atexit(RTC_Terminate) - moved Init to after
*                       _heap_init
*       02-01-99  GJF   Slight change to terminator execution loop to allow
*                       terminators to register more terminators.
*       04-28-99  PML   Wrap __declspec(allocate()) in _CRTALLOC macro.
*       05-11-99  KBF   Wrap RTC support in #ifdef.
*       05-17-99  PML   Remove all Macintosh support.
*       03-06-00  PML   Add __crtExitProcess for COM+ exit processing.
*       04-28-00  BWT   Fix Posix
*       08-04-00  PML   COM+ -> managed (VS7#117746).
*       03-27-01  PML   .CRT$XI funcs now return an error status (VS7#231220).
*       05-01-01  BWT   Remove TerminateProcess call.  It's not necessary when
*                       simply exiting would serve the same purpose.
*       07-15-01  PML   Remove all ALPHA, MIPS, and PPC code
*       10-02-01  BWT   Release exit critical section before calling __crtExitProcess
*                       It's possible another thread may be calling doexit() while holding
*                       the loader lock (static object construction on dllattach)
*                       __crtExitProcess() needs to call GetModuleHandle to finish.
*       11-06-01  GB    Added __freeCRTMemory for debug build to minimize leaks
*       04-29-02  GB    Added try-finally arounds lock-unlock.
*       06-14-02  MSL   _cinit now takes an int to indicate FP initialisation
*                       VS7 550455
*       11-18-02  GB    Added __crtdll_callstaticterminators to be used in dllmain
*                       of crtdll.
*       10-29-03  AC    Added accessors for global variables
*       02-13-04  PML   Add dynamic TLS support
*       09-24-04  MSL   Added initialisation checks
*       01-12-05  DJT   Use OS-encoded global function pointers
*       04-05-05  AC    exit() needs to call CorExitProcess before shutting down native
*                       VSW#467254
*       05-24-05  PML   Make sure unencoded global func-ptrs are in .rdata,
*                       encode __onexitbegin/end (vsw#191114)
*       06-15-05  AEI   Fix in function doexit: __crtCorExitProcess(code) is called only when
*                       CRTDLL is defined (and ofcourse when !retcaller). 
*                       This is to cover the following scenario: an application linked statically
*                       is loading the CLR later in the execution. If CRTDLL is not checked to be defined,
*                       __crtCorExitProcess(code) will start executing shut-down code from CLR crt,
*                       (CLR loaded msvcr80.dll) and native crt buffers will never be flushed.
*       02-16-06  SSM   Encode function pointers stored in atexit list
*       11-08-06  PMB   Remove support for Win9x and _osplatform and relatives.
*                       DDB#11325
*       01-09-07  WL    Recalculate onexitend/onexitbegin each time we call the the clean-up function.
*                       DDB#55120
*       05-07-07  GM    Avoid resetting onexitend/onexitbegin unless boundaries change.
*
*******************************************************************************/

#include <cruntime.h>
#ifdef  _POSIX_
#include <unistd.h>
#else
#include <msdos.h>
#include <rtcapi.h>
#endif
#include <dos.h>
#include <oscalls.h>
#include <mtdll.h>
#include <awint.h>
#include <internal.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <dbgint.h>
#include <sect_attribs.h>
#include <mbctype.h>
#include <mbdata.h>


/* define umask */
int _umaskval = 0;


/* argument vector and environment */

_CRTIMP int __argc = 0;
_CRTIMP char **__argv = NULL;
_CRTIMP wchar_t **__wargv = NULL;
#ifdef  _POSIX_
char **environ = NULL;
#else
_CRTIMP char **_environ = NULL;
_CRTIMP char **__initenv = NULL;
_CRTIMP wchar_t **_wenviron = NULL;
_CRTIMP wchar_t **__winitenv = NULL;
#endif
_CRTIMP char *_pgmptr = NULL;           /* ptr to program name */
_CRTIMP wchar_t *_wpgmptr = NULL;       /* ptr to wide program name */
extern void *_stdbuf[2];

/* callable exit flag */
char _exitflag = 0;

/*
 * flag indicating if C runtime termination has been done. set if exit,
 * _exit, _cexit or _c_exit has been called. checked when _CRTDLL_INIT
 * is called with DLL_PROCESS_DETACH.
 */
int _C_Termination_Done = FALSE;
int _C_Exit_Done = FALSE;

#ifndef CRTDLL
/*
 * Flag checked by getenv() and _putenv() to determine if the environment has
 * been initialized.
 */
int __env_initialized;

#endif

#ifdef  _MBCS
/*
 * Flag to ensure multibyte ctype table is only initialized once
 */
int __mbctype_initialized;
#endif  /* _MBCS */


/*
 * NOTE: THE USE OF THE POINTERS DECLARED BELOW DEPENDS ON THE PROPERTIES
 * OF C COMMUNAL VARIABLES. SPECIFICALLY, THEY ARE NON-NULL IFF THERE EXISTS
 * A DEFINITION ELSEWHERE INITIALIZING THEM TO NON-NULL VALUES.
 */

/*
 * pointers to initialization functions
 */

#ifndef CRTDLL
const _PVFI _FPinit;                            /* floating point init. */
#endif

/*
 * pointers to initialization sections
 */

extern _CRTALLOC(".CRT$XIA") _PIFV __xi_a[];
extern _CRTALLOC(".CRT$XIZ") _PIFV __xi_z[];    /* C initializers */
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[];    /* C++ initializers */
extern _CRTALLOC(".CRT$XPA") _PVFV __xp_a[];
extern _CRTALLOC(".CRT$XPZ") _PVFV __xp_z[];    /* C pre-terminators */
extern _CRTALLOC(".CRT$XTA") _PVFV __xt_a[];
extern _CRTALLOC(".CRT$XTZ") _PVFV __xt_z[];    /* C terminators */

/*
 * pointers to the start and finish of the _onexit/atexit table
 * NOTE - the pointers are stored encoded.
 */
_PVFV *__onexitbegin;
_PVFV *__onexitend;

/*
 * Pointer to callback function to initialize any dynamically initialized
 * __declspec(thread) variables.  This relies on a subtle aspect of C.
 * The pointer is defined here uninitialized.  It is defined initialized in
 * tlsdyn.c.  If user code uses dynamically initialized __declspec(thread)
 * variables, then compiler-injected dependencies cause tlsdyn.obj to be
 * linked.  In that case, the non-zero definition of __dyn_tls_init_callback
 * in tlsdyn.obj will take precedence, and the startup code will execute the
 * callback.  This use of multiple definitions is only legal in C, not C++.
 */

#ifndef CRTDLL
const PIMAGE_TLS_CALLBACK __dyn_tls_init_callback;
#endif  /* !CRTDLL */

/*
 * static (internal) functions that walk a table of function pointers,
 * calling each entry between the two pointers, skipping NULL entries
 *
 * _initterm needs to be exported for CRT DLL so that C++ initializers in the
 * client EXE / DLLs can be initialized.
 *
 * _initterm_e calls function pointers that return a nonzero error code to
 * indicate an initialization failed fatally.
 */
#ifdef  CRTDLL
void __cdecl _initterm(_PVFV *, _PVFV *);
#else
static void __cdecl _initterm(_PVFV *, _PVFV *);
#endif
int  __cdecl _initterm_e(_PIFV *, _PIFV *);


#ifdef CRTDLL

/* 
    Copied from dllstuff\crtexe.c.
*/

/***
*check_managed_app() - Check for a managed executable
*
*Purpose:
*       Determine if the EXE the startup code is linked into is a managed app
*       by looking for the COM Runtime Descriptor in the Image Data Directory
*       of the PE or PE+ header.
*
*Entry:
*       None
*
*Exit:
*       1 if managed app, 0 if not.
*
*Exceptions:
*
*******************************************************************************/

static int __cdecl check_managed_app (
        void
        )
{
        PIMAGE_DOS_HEADER pDOSHeader;
        PIMAGE_NT_HEADERS pPEHeader;

        pDOSHeader = (PIMAGE_DOS_HEADER) GetModuleHandleW(NULL);

        if (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE)
        {
            return 0;
        }

        pPEHeader = (PIMAGE_NT_HEADERS) ((BYTE *) pDOSHeader + pDOSHeader->e_lfanew);

        if (pPEHeader->Signature != IMAGE_NT_SIGNATURE)
        {
            return 0;
        }

        switch (pPEHeader->OptionalHeader.Magic) {
        PIMAGE_OPTIONAL_HEADER32 pOptHeader32;
#ifdef  _WIN64
        PIMAGE_OPTIONAL_HEADER64 pOptHeader64;
#endif

        case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
            /* PE header */
            pOptHeader32 = (PIMAGE_OPTIONAL_HEADER32) &pPEHeader->OptionalHeader;

            if (pOptHeader32->NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
            {
                return 0;
            }

            return pOptHeader32->DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress != 0;

#ifdef  _WIN64
        case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
            /* PE+ header */
            pOptHeader64 = (PIMAGE_OPTIONAL_HEADER64) &pPEHeader->OptionalHeader;

            if (pOptHeader64->NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
            {
                return 0;
            }

            return pOptHeader64->DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress != 0;
#endif
        }

        /* Not PE or PE+, so not managed */
        return 0;
}

#endif

/***
*_cinit - C initialization
*
*Purpose:
*       This routine performs the shared DOS and Windows initialization.
*       The following order of initialization must be preserved -
*
*       1.  Check for devices for file handles 0 - 2
*       2.  Integer divide interrupt vector setup
*       3.  General C initializer routines
*
*Entry:
*       No parameters: Called from __crtstart and assumes data
*       set up correctly there.
*
*Exit:
*       Initializes C runtime data.
*       Returns 0 if all .CRT$XI internal initializations succeeded, else
*       the _RT_* fatal error code encountered.
*
*Exceptions:
*
*******************************************************************************/

#ifndef CRTDLL
extern void __cdecl _initp_misc_cfltcvt_tab();
#endif

int __cdecl _cinit (
        int initFloatingPrecision
        )
{
        int initret;

        /*
         * initialize floating point package, if present
         */
#ifdef  CRTDLL
        _fpmath(initFloatingPrecision);
#else
#ifndef _WIN32_WCE
        if (_FPinit != NULL &&
            _IsNonwritableInCurrentImage((PBYTE)&_FPinit))
#else
        if (_FPinit != NULL)
#endif
        {
            (*_FPinit)(initFloatingPrecision);
        }
        _initp_misc_cfltcvt_tab();
#endif

        /*
         * do initializations
         */
        initret = _initterm_e( __xi_a, __xi_z );
        if ( initret != 0 )
            return initret;

#ifdef  _RTC
        atexit(_RTC_Terminate);
#endif
        /*
         * do C++ initializations
         */
        _initterm( __xc_a, __xc_z );

#ifndef CRTDLL
        /*
         * If we have any dynamically initialized __declspec(thread)
         * variables, then invoke their initialization for the thread on
         * which the DLL is being loaded, by calling __dyn_tls_init through
         * a callback defined in tlsdyn.obj.  We can't rely on the OS
         * calling __dyn_tls_init with DLL_PROCESS_ATTACH because, on
         * Win2K3 and before, that call happens before the CRT is
         * initialized.
         */
#ifndef _WIN32_WCE
        if (__dyn_tls_init_callback != NULL &&
            _IsNonwritableInCurrentImage((PBYTE)&__dyn_tls_init_callback))
#else
        if (__dyn_tls_init_callback != NULL)
#endif
        {
            __dyn_tls_init_callback(NULL, DLL_THREAD_ATTACH, NULL);
        }
#endif  /* !CRTDLL */

        return 0;
}


/***
*exit(status), _exit(status), _cexit(void), _c_exit(void) - C termination
*
*Purpose:
*
*       Entry points:
*
*           exit(code):  Performs all the C termination functions
*               and terminates the process with the return code
*               supplied by the user.
*
*           _exit(code):  Performs a quick exit routine that does not
*               do certain 'high-level' exit processing.  The _exit
*               routine terminates the process with the return code
*               supplied by the user.
*
*           _cexit():  Performs the same C lib termination processing
*               as exit(code) but returns control to the caller
*               when done (i.e., does NOT terminate the process).
*
*           _c_exit():  Performs the same C lib termination processing
*               as _exit(code) but returns control to the caller
*               when done (i.e., does NOT terminate the process).
*
*       Termination actions:
*
*           exit(), _cexit():
*
*           1.  call user's terminator routines
*           2.  call C runtime preterminators
*
*           _exit(), _c_exit():
*
*           3.  call C runtime terminators
*           4.  return to DOS or caller
*
*       Notes:
*
*       The termination sequence is complicated due to the multiple entry
*       points sharing the common code body while having different entry/exit
*       sequences.
*
*       Multi-thread notes:
*
*       1. exit() should NEVER be called when mthread locks are held.
*          The exit() routine can make calls that try to get mthread locks.
*
*       2. _exit()/_c_exit() can be called from anywhere, with or without locks held.
*          Thus, _exit() can NEVER try to get locks (otherwise, deadlock
*          may occur).  _exit() should always 'work' (i.e., the process
*          should always terminate successfully).
*
*       3. Only one thread is allowed into the exit code (see _lockexit()
*          and _unlockexit() routines).
*
*Entry:
*       exit(), _exit()
*           int status - exit status (0-255)
*
*       _cexit(), _c_exit()
*           <no input>
*
*Exit:
*       exit(), _exit()
*           <EXIT to DOS>
*
*       _cexit(), _c_exit()
*           Return to caller
*
*Uses:
*
*Exceptions:
*
*******************************************************************************/

/* worker routine prototype */
static void __cdecl doexit (int code, int quick, int retcaller);

void __cdecl exit (
        int code
        )
{
        doexit(code, 0, 0); /* full term, kill process */
}

#ifndef _POSIX_

void __cdecl _exit (
        int code
        )
{
        doexit(code, 1, 0); /* quick term, kill process */
}

void __cdecl _cexit (
        void
        )
{
        doexit(0, 0, 1);    /* full term, return to caller */
}

void __cdecl _c_exit (
        void
        )
{
        doexit(0, 1, 1);    /* quick term, return to caller */
}

#endif  /* _POSIX_ */

/***
*_amsg_exit(rterrnum) - Fast exit fatal errors
*
*Purpose:
*       Exit the program with error code of 255 and appropriate error
*       message.
*
*Entry:
*       int rterrnum - error message number (amsg_exit only).
*
*Exit:
*       Calls _exit()
*
*Exceptions:
*
*******************************************************************************/

void __cdecl _amsg_exit (
        int rterrnum
        )
{
        _FF_MSGBANNER();    /* write run-time error banner */

        _NMSG_WRITE(rterrnum);  /* write message */
        _exit(255);
}


/***
* __freeCrtMemory()
*
* Purpose:
*       To free CRT allocated memory preventing memory leaks when dll is unloaded.
*
*******************************************************************************/
void __cdecl __freeCrtMemory()
{
    _PVFV * onexitbegin = (_PVFV *) DecodePointer(__onexitbegin);
    void **pptr;

    for (pptr = _wenviron; pptr != NULL && *pptr != NULL; ++pptr)
        _free_crt(*pptr);

    _free_crt(_wenviron);
    _wenviron = NULL;

    for (pptr = _environ; pptr != NULL && *pptr != NULL; ++pptr)
        _free_crt(*pptr);

    _free_crt(_environ);
    _environ = NULL;

    _free_crt(__wargv);
    _free_crt(__argv);
    __wargv = NULL;
    __argv = NULL;

    if (onexitbegin != (_PVFV *)-1)
        _free_crt(onexitbegin);

    __onexitbegin = (_PVFV *)EncodePointer(NULL);

    if (_stdbuf[0])
    {
         _free_crt(_stdbuf[0]);
         _stdbuf[0] = NULL;
    }

    if (_stdbuf[1])
    {
         _free_crt(_stdbuf[1]);
         _stdbuf[1] = NULL;
    }

    if (InterlockedDecrement(&(__ptmbcinfo->refcount)) == 0 && __ptmbcinfo != &__initialmbcinfo)
    {
        _free_crt(__ptmbcinfo);
        __ptmbcinfo = &__initialmbcinfo;
    }
}

static void __cdecl doexit (
        int code,
        int quick,
        int retcaller
        )
{
#ifdef  _DEBUG
        static int fExit = 0;
#endif /* _DEBUG */

#ifdef CRTDLL
#ifndef _WINCE_WCE
        if (!retcaller && check_managed_app())
        {
            /* 
               Only if the EXE is managed then we call CorExitProcess.
               Native cleanup is done in .cctor of the EXE
               If the Exe is Native then native clean up should be done
               before calling (Cor)ExitProcess.
            */
            __crtCorExitProcess(code);
        }
#endif // _WIN32_WCE
#endif

        _lockexit();        /* assure only 1 thread in exit path */
        __TRY

#ifndef _POSIX_
        if (_C_Exit_Done != TRUE) {
#endif
            _C_Termination_Done = TRUE;

            /* save callable exit flag (for use by terminators) */
            _exitflag = (char) retcaller;  /* 0 = term, !0 = callable exit */

            if (!quick) {

                /*
                 * do _onexit/atexit() terminators
                 * (if there are any)
                 *
                 * These terminators MUST be executed in reverse order (LIFO)!
                 *
                 * NOTE:
                 *  This code assumes that __onexitbegin points
                 *  to the first valid onexit() entry and that
                 *  __onexitend points past the last valid entry.
                 *  If __onexitbegin == __onexitend, the table
                 *  is empty and there are no routines to call.
                 */

                _PVFV * onexitbegin = (_PVFV *) DecodePointer(__onexitbegin);
                if (onexitbegin) {
                    _PVFV * onexitend = (_PVFV *) DecodePointer(__onexitend);
                    _PVFV function_to_call = NULL;

                    /* save the start and end for later comparison */
                    _PVFV * onexitbegin_saved = onexitbegin;
                    _PVFV * onexitend_saved = onexitend;

                    while (1)
                    {
                        _PVFV * onexitbegin_new = NULL;
                        _PVFV * onexitend_new = NULL;

                        /* find the last valid function pointer to call. */
                        while (--onexitend >= onexitbegin && *onexitend == EncodePointer(NULL))
                        {
                            /* keep going backwards. */
                        }

                        if (onexitend < onexitbegin)
                        {
                            /* there are no more valid entries in the list, we are done. */
                            break;
                        }

                        /* cache the function to call. */
                        function_to_call = (_PVFV) DecodePointer(*onexitend);

                        /* mark the function pointer as visited. */
                        *onexitend = (_PVFV)EncodePointer(NULL);

                        /* call the function, which can eventually change __onexitbegin and __onexitend */
                        (*function_to_call)();

                        onexitbegin_new = (_PVFV *) DecodePointer(__onexitbegin);
                        onexitend_new = (_PVFV *) DecodePointer(__onexitend);

                        if ( ( onexitbegin_saved != onexitbegin_new ) || ( onexitend_saved != onexitend_new ) )
                        {
                            /* reset only if either start or end has changed */
                            onexitbegin = onexitbegin_saved = onexitbegin_new;
                            onexitend = onexitend_saved = onexitend_new;
                        }
                    }
                }
#ifndef CRTDLL
                /*
                 * do pre-terminators
                 */
                _initterm(__xp_a, __xp_z);
#endif
            }

#ifndef CRTDLL
            /*
             * do terminators
             */
            _initterm(__xt_a, __xt_z);
#endif

#ifdef  _DEBUG
            /* Dump all memory leaks */
            if (!fExit && _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_LEAK_CHECK_DF)
            {
                fExit = 1;
#ifndef CRTDLL
                __freeCrtMemory();
                _CrtDumpMemoryLeaks();
#endif
            }
#endif

#ifndef _POSIX_
        }
#endif
        /* return to OS or to caller */

        __FINALLY
            if (retcaller)
                _unlockexit();      /* unlock the exit code path */
        __END_TRY_FINALLY

        if (retcaller)
            return;

#ifdef  _POSIX_

        _exit(code);
}

#else   /* ndef _POSIX_ */

        _C_Exit_Done = TRUE;

        _unlockexit();      /* unlock the exit code path */

        __crtExitProcess(code);
}

#ifdef CRTDLL
/***
* __crtdll_callstaticterminators
*
*Purpose:
*       call terminators. This is called from CRT_INIT when dll entrypoint is
*       called with DLL_PROCESS_DETACH. We can't call the terminators from exit
*       as there may be some dll that may need crt functionality during
*       DLL_PROCESS_DETACH
*
*******************************************************************************/
void __crtdll_callstaticterminators(void) {
    /*
     * do pre-terminators
     */
    _initterm(__xp_a, __xp_z);

    /*
     * do terminators
     */
    _initterm(__xt_a, __xt_z);
}
#endif
/***
* __crtCorExitProcess - CRT wrapper for CorExitProcess
* __crtExitProcess - CRT wrapper for ExitProcess
*
*Purpose:
*       If we're part of a managed app, then call the CorExitProcess,
*       otherwise call ExitProcess.  For managed apps, calling ExitProcess can
*       be problematic, because it doesn't give the managed FinalizerThread a
*       chance to clean up.
*
*       To determine if we're a managed app, we check if mscoree.dll is loaded.
*       Then, if CorExitProcess is available, we call it.
*
*Entry:
*       int status - exit code
*
*Exit:
*       Does not return
*
*Exceptions:
*
*******************************************************************************/

#ifndef _WIN32_WCE // CE's __crtExitProcess in buildbtcrt\exitproc.c
typedef void (WINAPI * PFN_EXIT_PROCESS)(UINT uExitCode);

void __cdecl __crtCorExitProcess (
        int status
        )
{
        HMODULE hmod;
        PFN_EXIT_PROCESS pfn;

        if (GetModuleHandleExW(0, L"mscoree.dll", &hmod)) {
            pfn = (PFN_EXIT_PROCESS)GetProcAddress(hmod, "CorExitProcess");
            if (pfn != NULL) {
                pfn(status);
            }
        }

        /*
         * Either mscoree.dll isn't loaded,
         * or CorExitProcess isn't exported from mscoree.dll,
         * or CorExitProcess returned (should never happen).
         * Just call return.
         */
}

void __cdecl __crtExitProcess (
        int status
        )
{
        __crtCorExitProcess(status);

        /*
         * Either mscoree.dll isn't loaded,
         * or CorExitProcess isn't exported from mscoree.dll,
         * or CorExitProcess returned (should never happen).
         * Just call ExitProcess.
         */

        ExitProcess(status);
}
#endif // _WIN32_WCE

#endif  /* _POSIX_ */

/***
* _lockexit - Aquire the exit code lock
*
*Purpose:
*       Makes sure only one thread is in the exit code at a time.
*       If a thread is already in the exit code, it must be allowed
*       to continue.  All other threads must pend.
*
*       Notes:
*
*       (1) It is legal for a thread that already has the lock to
*       try and get it again(!).  That is, consider the following
*       sequence:
*
*           (a) program calls exit()
*           (b) thread locks exit code
*           (c) user onexit() routine calls _exit()
*           (d) same thread tries to lock exit code
*
*       Since _exit() must ALWAYS be able to work (i.e., can be called
*       from anywhere with no regard for locking), we must make sure the
*       program does not deadlock at step (d) above.
*
*       (2) If a thread executing exit() or _exit() aquires the exit lock,
*       other threads trying to get the lock will pend forever.  That is,
*       since exit() and _exit() terminate the process, there is not need
*       for them to unlock the exit code path.
*
*       (3) Note that onexit()/atexit() routines call _lockexit/_unlockexit
*       to protect mthread access to the onexit table.
*
*       (4) The 32-bit OS semaphore calls DO allow a single thread to acquire
*       the same lock multiple times* thus, this version is straight forward.
*
*Entry: <none>
*
*Exit:
*       Calling thread has exit code path locked on return.
*
*Exceptions:
*
*******************************************************************************/

void __cdecl _lockexit (
        void
        )
{
        _mlock(_EXIT_LOCK1);
}

/***
* _unlockexit - Release exit code lock
*
*Purpose:
*       [See _lockexit() description above.]
*
*       This routine is called by _cexit(), _c_exit(), and onexit()/atexit().
*       The exit() and _exit() routines never unlock the exit code path since
*       they are terminating the process.
*
*Entry:
*       Exit code path is unlocked.
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

void __cdecl _unlockexit (
        void
        )
{
        _munlock(_EXIT_LOCK1);
}


/***
*_init_pointers() - initialize global function pointers with OS encoded values
*
*Purpose:
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

extern void __cdecl _initp_heap_handler(void*);
extern void __cdecl _initp_misc_invarg(void*);
extern void __cdecl _initp_misc_purevirt(void*);
#ifndef _WIN32_WCE
extern void __cdecl _initp_misc_rand_s(void*);
extern void __cdecl _initp_misc_winsig(void*);
extern void __cdecl _initp_eh_hooks(void*);
#endif // _WIN32_WCE

void __cdecl _init_pointers() {
        void *enull = EncodePointer(NULL);

        _initp_heap_handler(enull);
        _initp_misc_invarg(enull);
        _initp_misc_purevirt(enull);
#ifndef _WIN32_WCE
        _initp_misc_rand_s(enull);
        _initp_misc_winsig(enull);
        _initp_eh_hooks(enull);
#endif // _WIN32_WCE
}

/***
* static void _initterm(_PVFV * pfbegin, _PVFV * pfend) - call entries in
*       function pointer table
*
*Purpose:
*       Walk a table of function pointers, calling each entry, as follows:
*
*           1. walk from beginning to end, pfunctbl is assumed to point
*              to the beginning of the table, which is currently a null entry,
*              as is the end entry.
*           2. skip NULL entries
*           3. stop walking when the end of the table is encountered
*
*Entry:
*       _PVFV *pfbegin  - pointer to the beginning of the table (first
*                         valid entry).
*       _PVFV *pfend    - pointer to the end of the table (after last
*                         valid entry).
*
*Exit:
*       No return value
*
*Notes:
*       This routine must be exported in the CRT DLL model so that the client
*       EXE and client DLL(s) can call it to initialize their C++ constructors.
*
*Exceptions:
*       If either pfbegin or pfend is NULL, or invalid, all bets are off!
*
*******************************************************************************/

#ifdef  CRTDLL
void __cdecl _initterm (
#else
static void __cdecl _initterm (
#endif
        _PVFV * pfbegin,
        _PVFV * pfend
        )
{
        /*
         * walk the table of function pointers from the bottom up, until
         * the end is encountered.  Do not skip the first entry.  The initial
         * value of pfbegin points to the first valid entry.  Do not try to
         * execute what pfend points to.  Only entries before pfend are valid.
         */
        while ( pfbegin < pfend )
        {
            /*
             * if current table entry is non-NULL, call thru it.
             */
            if ( *pfbegin != NULL )
                (**pfbegin)();
            ++pfbegin;
        }
}

/***
* static int  _initterm_e(_PIFV * pfbegin, _PIFV * pfend) - call entries in
*       function pointer table, return error code on any failure
*
*Purpose:
*       Walk a table of function pointers in the same way as _initterm, but
*       here the functions return an error code.  If an error is returned, it
*       will be a nonzero value equal to one of the _RT_* codes.
*
*Entry:
*       _PIFV *pfbegin  - pointer to the beginning of the table (first
*                         valid entry).
*       _PIFV *pfend    - pointer to the end of the table (after last
*                         valid entry).
*
*Exit:
*       No return value
*
*Notes:
*       This routine must be exported in the CRT DLL model so that the client
*       EXE and client DLL(s) can call it.
*
*Exceptions:
*       If either pfbegin or pfend is NULL, or invalid, all bets are off!
*
*******************************************************************************/

int __cdecl _initterm_e (
        _PIFV * pfbegin,
        _PIFV * pfend
        )
{
        int ret = 0;

        /*
         * walk the table of function pointers from the bottom up, until
         * the end is encountered.  Do not skip the first entry.  The initial
         * value of pfbegin points to the first valid entry.  Do not try to
         * execute what pfend points to.  Only entries before pfend are valid.
         */
        while ( pfbegin < pfend  && ret == 0)
        {
            /*
             * if current table entry is non-NULL, call thru it.
             */
            if ( *pfbegin != NULL )
                ret = (**pfbegin)();
            ++pfbegin;
        }

        return ret;
}

/***
*errno_t _get_wpgmptr() - get _wpgmptr, i.e. the module (EXE) wide name
*
*Purpose:
*       Get the value of _wpgmptr
*
*Entry:
*       wchar_t **pValue - pointer where to store the value
*
*Exit:
*       The error code
*
*Exceptions:
*       Input parameters are validated. Refer to the validation section of the function.
*
*******************************************************************************/
errno_t _get_wpgmptr(wchar_t **pValue)
{
    /* validation section */
    _VALIDATE_RETURN_ERRCODE(pValue != NULL, EINVAL);

_BEGIN_SECURE_CRT_DEPRECATION_DISABLE

    _VALIDATE_RETURN_ERRCODE(_wpgmptr != NULL, EINVAL);

    *pValue = _wpgmptr;
_END_SECURE_CRT_DEPRECATION_DISABLE
    return 0;
}

/***
*errno_t _get_pgmptr() - get _pgmptr, i.e. the module (EXE) name
*
*Purpose:
*       Get the value of _pgmptr
*
*Entry:
*       char **pValue - pointer where to store the value
*
*Exit:
*       The error code
*
*Exceptions:
*       Input parameters are validated. Refer to the validation section of the function.
*
*******************************************************************************/
errno_t _get_pgmptr(char **pValue)
{
    /* validation section */
    _VALIDATE_RETURN_ERRCODE(pValue != NULL, EINVAL);

_BEGIN_SECURE_CRT_DEPRECATION_DISABLE

    _VALIDATE_RETURN_ERRCODE(_pgmptr != NULL, EINVAL);

    *pValue = _pgmptr;
_END_SECURE_CRT_DEPRECATION_DISABLE
    return 0;
}


#ifndef _WIN32_WCE
// move into dos\dosmap.c to avoid fulllibc.lib link with NLS apis.
/***
*__copy_path_to_wide_string() - Convert an ANSI/MBCS path to a wide char string.
*
*Entry:
*       const char *path - SBCS/MBCS input path string.
*       wchar_t **outPath -  Pointer to output wide-char path string
*
*Exit:
*       outPath point to newly allocated wide char output string
*       returns TRUE if successful
*       returns FALSE and sets errno if unsuccessful
*
*Exceptions:
*
*******************************************************************************/
BOOL __cdecl __copy_path_to_wide_string (
        const char *path, 
        wchar_t **outPath
        )
{

    int len;
    UINT codePage = CP_ACP;

    /* validation section */
    _VALIDATE_RETURN_ERRCODE(path != NULL, EINVAL);
    _VALIDATE_RETURN_ERRCODE(outPath != NULL, EINVAL);

#ifndef _CORESYS
#ifndef _WIN32_WCE
    if (!__crtIsPackagedApp() && !AreFileApisANSI())
        codePage = CP_OEMCP;
#endif // _WIN32_WCE
#endif

    *outPath = NULL;

    /* get the buffer size needed for conversion */
    if  ( (len = MultiByteToWideChar(codePage, 0 /* Use default flags */, path, -1, 0, 0) ) == 0 )
    {
        _dosmaperr(GetLastError());
        return FALSE;
    }

    /* allocate enough space for path wide char */
    if ( (*outPath = (wchar_t*)_malloc_crt( len * sizeof(wchar_t) ) ) == NULL )
    {
        /* malloc should set the errno */
        return FALSE;
    }

    /* now do the conversion */
    if ( MultiByteToWideChar(codePage, 0 /* Use default flags */, path, -1, *outPath, len) == 0 )
    {
        _dosmaperr(GetLastError());
        _free_crt(*outPath);
        *outPath = NULL;
        return FALSE;
    }

    return TRUE;
}
#endif // _WIN32_WCE