diff options
Diffstat (limited to 'Python/pylifecycle.c')
| -rw-r--r-- | Python/pylifecycle.c | 1581 | 
1 files changed, 1581 insertions, 0 deletions
| diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c new file mode 100644 index 0000000..38543e0 --- /dev/null +++ b/Python/pylifecycle.c @@ -0,0 +1,1581 @@ + +/* Python interpreter top-level routines, including init/exit */ + +#include "Python.h" + +#include "Python-ast.h" +#undef Yield /* undefine macro conflicting with winbase.h */ +#include "grammar.h" +#include "node.h" +#include "token.h" +#include "parsetok.h" +#include "errcode.h" +#include "code.h" +#include "symtable.h" +#include "ast.h" +#include "marshal.h" +#include "osdefs.h" +#include <locale.h> + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +#ifdef MS_WINDOWS +#include "malloc.h" /* for alloca */ +#endif + +#ifdef HAVE_LANGINFO_H +#include <langinfo.h> +#endif + +#ifdef MS_WINDOWS +#undef BYTE +#include "windows.h" +#endif + +_Py_IDENTIFIER(flush); +_Py_IDENTIFIER(name); +_Py_IDENTIFIER(stdin); +_Py_IDENTIFIER(stdout); +_Py_IDENTIFIER(stderr); + +#ifdef __cplusplus +extern "C" { +#endif + +extern wchar_t *Py_GetPath(void); + +extern grammar _PyParser_Grammar; /* From graminit.c */ + +/* Forward */ +static void initmain(PyInterpreterState *interp); +static int initfsencoding(PyInterpreterState *interp); +static void initsite(void); +static int initstdio(void); +static void initsigs(void); +static void call_py_exitfuncs(void); +static void wait_for_thread_shutdown(void); +static void call_ll_exitfuncs(void); +extern int _PyUnicode_Init(void); +extern int _PyStructSequence_Init(void); +extern void _PyUnicode_Fini(void); +extern int _PyLong_Init(void); +extern void PyLong_Fini(void); +extern int _PyFaulthandler_Init(void); +extern void _PyFaulthandler_Fini(void); +extern void _PyHash_Fini(void); +extern int _PyTraceMalloc_Init(void); +extern int _PyTraceMalloc_Fini(void); + +#ifdef WITH_THREAD +extern void _PyGILState_Init(PyInterpreterState *, PyThreadState *); +extern void _PyGILState_Fini(void); +#endif /* WITH_THREAD */ + +/* Global configuration variable declarations are in pydebug.h */ +/* XXX (ncoghlan): move those declarations to pylifecycle.h? */ +int Py_DebugFlag; /* Needed by parser.c */ +int Py_VerboseFlag; /* Needed by import.c */ +int Py_QuietFlag; /* Needed by sysmodule.c */ +int Py_InteractiveFlag; /* Needed by Py_FdIsInteractive() below */ +int Py_InspectFlag; /* Needed to determine whether to exit at SystemExit */ +int Py_OptimizeFlag = 0; /* Needed by compile.c */ +int Py_NoSiteFlag; /* Suppress 'import site' */ +int Py_BytesWarningFlag; /* Warn on str(bytes) and str(buffer) */ +int Py_UseClassExceptionsFlag = 1; /* Needed by bltinmodule.c: deprecated */ +int Py_FrozenFlag; /* Needed by getpath.c */ +int Py_IgnoreEnvironmentFlag; /* e.g. PYTHONPATH, PYTHONHOME */ +int Py_DontWriteBytecodeFlag; /* Suppress writing bytecode files (*.py[co]) */ +int Py_NoUserSiteDirectory = 0; /* for -s and site.py */ +int Py_UnbufferedStdioFlag = 0; /* Unbuffered binary std{in,out,err} */ +int Py_HashRandomizationFlag = 0; /* for -R and PYTHONHASHSEED */ +int Py_IsolatedFlag = 0; /* for -I, isolate from user's env */ + +PyThreadState *_Py_Finalizing = NULL; + +/* Hack to force loading of object files */ +int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t) = \ +    PyOS_mystrnicmp; /* Python/pystrcmp.o */ + +/* PyModule_GetWarningsModule is no longer necessary as of 2.6 +since _warnings is builtin.  This API should not be used. */ +PyObject * +PyModule_GetWarningsModule(void) +{ +    return PyImport_ImportModule("warnings"); +} + +static int initialized = 0; + +/* API to access the initialized flag -- useful for esoteric use */ + +int +Py_IsInitialized(void) +{ +    return initialized; +} + +/* Helper to allow an embedding application to override the normal + * mechanism that attempts to figure out an appropriate IO encoding + */ + +static char *_Py_StandardStreamEncoding = NULL; +static char *_Py_StandardStreamErrors = NULL; + +int +Py_SetStandardStreamEncoding(const char *encoding, const char *errors) +{ +    if (Py_IsInitialized()) { +        /* This is too late to have any effect */ +        return -1; +    } +    /* Can't call PyErr_NoMemory() on errors, as Python hasn't been +     * initialised yet. +     * +     * However, the raw memory allocators are initialised appropriately +     * as C static variables, so _PyMem_RawStrdup is OK even though +     * Py_Initialize hasn't been called yet. +     */ +    if (encoding) { +        _Py_StandardStreamEncoding = _PyMem_RawStrdup(encoding); +        if (!_Py_StandardStreamEncoding) { +            return -2; +        } +    } +    if (errors) { +        _Py_StandardStreamErrors = _PyMem_RawStrdup(errors); +        if (!_Py_StandardStreamErrors) { +            if (_Py_StandardStreamEncoding) { +                PyMem_RawFree(_Py_StandardStreamEncoding); +            } +            return -3; +        } +    } +    return 0; +} + +/* Global initializations.  Can be undone by Py_Finalize().  Don't +   call this twice without an intervening Py_Finalize() call.  When +   initializations fail, a fatal error is issued and the function does +   not return.  On return, the first thread and interpreter state have +   been created. + +   Locking: you must hold the interpreter lock while calling this. +   (If the lock has not yet been initialized, that's equivalent to +   having the lock, but you cannot use multiple threads.) + +*/ + +static int +add_flag(int flag, const char *envs) +{ +    int env = atoi(envs); +    if (flag < env) +        flag = env; +    if (flag < 1) +        flag = 1; +    return flag; +} + +static char* +get_codec_name(const char *encoding) +{ +    char *name_utf8, *name_str; +    PyObject *codec, *name = NULL; + +    codec = _PyCodec_Lookup(encoding); +    if (!codec) +        goto error; + +    name = _PyObject_GetAttrId(codec, &PyId_name); +    Py_CLEAR(codec); +    if (!name) +        goto error; + +    name_utf8 = _PyUnicode_AsString(name); +    if (name_utf8 == NULL) +        goto error; +    name_str = _PyMem_RawStrdup(name_utf8); +    Py_DECREF(name); +    if (name_str == NULL) { +        PyErr_NoMemory(); +        return NULL; +    } +    return name_str; + +error: +    Py_XDECREF(codec); +    Py_XDECREF(name); +    return NULL; +} + +static char* +get_locale_encoding(void) +{ +#ifdef MS_WINDOWS +    char codepage[100]; +    PyOS_snprintf(codepage, sizeof(codepage), "cp%d", GetACP()); +    return get_codec_name(codepage); +#elif defined(HAVE_LANGINFO_H) && defined(CODESET) +    char* codeset = nl_langinfo(CODESET); +    if (!codeset || codeset[0] == '\0') { +        PyErr_SetString(PyExc_ValueError, "CODESET is not set or empty"); +        return NULL; +    } +    return get_codec_name(codeset); +#else +    PyErr_SetNone(PyExc_NotImplementedError); +    return NULL; +#endif +} + +static void +import_init(PyInterpreterState *interp, PyObject *sysmod) +{ +    PyObject *importlib; +    PyObject *impmod; +    PyObject *sys_modules; +    PyObject *value; + +    /* Import _importlib through its frozen version, _frozen_importlib. */ +    if (PyImport_ImportFrozenModule("_frozen_importlib") <= 0) { +        Py_FatalError("Py_Initialize: can't import _frozen_importlib"); +    } +    else if (Py_VerboseFlag) { +        PySys_FormatStderr("import _frozen_importlib # frozen\n"); +    } +    importlib = PyImport_AddModule("_frozen_importlib"); +    if (importlib == NULL) { +        Py_FatalError("Py_Initialize: couldn't get _frozen_importlib from " +                      "sys.modules"); +    } +    interp->importlib = importlib; +    Py_INCREF(interp->importlib); + +    /* Install _importlib as __import__ */ +    impmod = PyInit_imp(); +    if (impmod == NULL) { +        Py_FatalError("Py_Initialize: can't import imp"); +    } +    else if (Py_VerboseFlag) { +        PySys_FormatStderr("import imp # builtin\n"); +    } +    sys_modules = PyImport_GetModuleDict(); +    if (Py_VerboseFlag) { +        PySys_FormatStderr("import sys # builtin\n"); +    } +    if (PyDict_SetItemString(sys_modules, "_imp", impmod) < 0) { +        Py_FatalError("Py_Initialize: can't save _imp to sys.modules"); +    } + +    value = PyObject_CallMethod(importlib, "_install", "OO", sysmod, impmod); +    if (value == NULL) { +        PyErr_Print(); +        Py_FatalError("Py_Initialize: importlib install failed"); +    } +    Py_DECREF(value); +    Py_DECREF(impmod); + +    _PyImportZip_Init(); +} + + +void +_Py_InitializeEx_Private(int install_sigs, int install_importlib) +{ +    PyInterpreterState *interp; +    PyThreadState *tstate; +    PyObject *bimod, *sysmod, *pstderr; +    char *p; +    extern void _Py_ReadyTypes(void); + +    if (initialized) +        return; +    initialized = 1; +    _Py_Finalizing = NULL; + +#if defined(HAVE_LANGINFO_H) && defined(HAVE_SETLOCALE) +    /* Set up the LC_CTYPE locale, so we can obtain +       the locale's charset without having to switch +       locales. */ +    setlocale(LC_CTYPE, ""); +#endif + +    if ((p = Py_GETENV("PYTHONDEBUG")) && *p != '\0') +        Py_DebugFlag = add_flag(Py_DebugFlag, p); +    if ((p = Py_GETENV("PYTHONVERBOSE")) && *p != '\0') +        Py_VerboseFlag = add_flag(Py_VerboseFlag, p); +    if ((p = Py_GETENV("PYTHONOPTIMIZE")) && *p != '\0') +        Py_OptimizeFlag = add_flag(Py_OptimizeFlag, p); +    if ((p = Py_GETENV("PYTHONDONTWRITEBYTECODE")) && *p != '\0') +        Py_DontWriteBytecodeFlag = add_flag(Py_DontWriteBytecodeFlag, p); +    /* The variable is only tested for existence here; _PyRandom_Init will +       check its value further. */ +    if ((p = Py_GETENV("PYTHONHASHSEED")) && *p != '\0') +        Py_HashRandomizationFlag = add_flag(Py_HashRandomizationFlag, p); + +    _PyRandom_Init(); + +    interp = PyInterpreterState_New(); +    if (interp == NULL) +        Py_FatalError("Py_Initialize: can't make first interpreter"); + +    tstate = PyThreadState_New(interp); +    if (tstate == NULL) +        Py_FatalError("Py_Initialize: can't make first thread"); +    (void) PyThreadState_Swap(tstate); + +#ifdef WITH_THREAD +    /* We can't call _PyEval_FiniThreads() in Py_Finalize because +       destroying the GIL might fail when it is being referenced from +       another running thread (see issue #9901). +       Instead we destroy the previously created GIL here, which ensures +       that we can call Py_Initialize / Py_Finalize multiple times. */ +    _PyEval_FiniThreads(); + +    /* Auto-thread-state API */ +    _PyGILState_Init(interp, tstate); +#endif /* WITH_THREAD */ + +    _Py_ReadyTypes(); + +    if (!_PyFrame_Init()) +        Py_FatalError("Py_Initialize: can't init frames"); + +    if (!_PyLong_Init()) +        Py_FatalError("Py_Initialize: can't init longs"); + +    if (!PyByteArray_Init()) +        Py_FatalError("Py_Initialize: can't init bytearray"); + +    if (!_PyFloat_Init()) +        Py_FatalError("Py_Initialize: can't init float"); + +    interp->modules = PyDict_New(); +    if (interp->modules == NULL) +        Py_FatalError("Py_Initialize: can't make modules dictionary"); + +    /* Init Unicode implementation; relies on the codec registry */ +    if (_PyUnicode_Init() < 0) +        Py_FatalError("Py_Initialize: can't initialize unicode"); +    if (_PyStructSequence_Init() < 0) +        Py_FatalError("Py_Initialize: can't initialize structseq"); + +    bimod = _PyBuiltin_Init(); +    if (bimod == NULL) +        Py_FatalError("Py_Initialize: can't initialize builtins modules"); +    _PyImport_FixupBuiltin(bimod, "builtins"); +    interp->builtins = PyModule_GetDict(bimod); +    if (interp->builtins == NULL) +        Py_FatalError("Py_Initialize: can't initialize builtins dict"); +    Py_INCREF(interp->builtins); + +    /* initialize builtin exceptions */ +    _PyExc_Init(bimod); + +    sysmod = _PySys_Init(); +    if (sysmod == NULL) +        Py_FatalError("Py_Initialize: can't initialize sys"); +    interp->sysdict = PyModule_GetDict(sysmod); +    if (interp->sysdict == NULL) +        Py_FatalError("Py_Initialize: can't initialize sys dict"); +    Py_INCREF(interp->sysdict); +    _PyImport_FixupBuiltin(sysmod, "sys"); +    PySys_SetPath(Py_GetPath()); +    PyDict_SetItemString(interp->sysdict, "modules", +                         interp->modules); + +    /* Set up a preliminary stderr printer until we have enough +       infrastructure for the io module in place. */ +    pstderr = PyFile_NewStdPrinter(fileno(stderr)); +    if (pstderr == NULL) +        Py_FatalError("Py_Initialize: can't set preliminary stderr"); +    _PySys_SetObjectId(&PyId_stderr, pstderr); +    PySys_SetObject("__stderr__", pstderr); +    Py_DECREF(pstderr); + +    _PyImport_Init(); + +    _PyImportHooks_Init(); + +    /* Initialize _warnings. */ +    _PyWarnings_Init(); + +    if (!install_importlib) +        return; + +    import_init(interp, sysmod); + +    /* initialize the faulthandler module */ +    if (_PyFaulthandler_Init()) +        Py_FatalError("Py_Initialize: can't initialize faulthandler"); + +    if (_PyTime_Init() < 0) +        Py_FatalError("Py_Initialize: can't initialize time"); + +    if (initfsencoding(interp) < 0) +        Py_FatalError("Py_Initialize: unable to load the file system codec"); + +    if (install_sigs) +        initsigs(); /* Signal handling stuff, including initintr() */ + +    if (_PyTraceMalloc_Init() < 0) +        Py_FatalError("Py_Initialize: can't initialize tracemalloc"); + +    initmain(interp); /* Module __main__ */ +    if (initstdio() < 0) +        Py_FatalError( +            "Py_Initialize: can't initialize sys standard streams"); + +    /* Initialize warnings. */ +    if (PySys_HasWarnOptions()) { +        PyObject *warnings_module = PyImport_ImportModule("warnings"); +        if (warnings_module == NULL) { +            fprintf(stderr, "'import warnings' failed; traceback:\n"); +            PyErr_Print(); +        } +        Py_XDECREF(warnings_module); +    } + +    if (!Py_NoSiteFlag) +        initsite(); /* Module site */ +} + +void +Py_InitializeEx(int install_sigs) +{ +    _Py_InitializeEx_Private(install_sigs, 1); +} + +void +Py_Initialize(void) +{ +    Py_InitializeEx(1); +} + + +#ifdef COUNT_ALLOCS +extern void dump_counts(FILE*); +#endif + +/* Flush stdout and stderr */ + +static int +file_is_closed(PyObject *fobj) +{ +    int r; +    PyObject *tmp = PyObject_GetAttrString(fobj, "closed"); +    if (tmp == NULL) { +        PyErr_Clear(); +        return 0; +    } +    r = PyObject_IsTrue(tmp); +    Py_DECREF(tmp); +    if (r < 0) +        PyErr_Clear(); +    return r > 0; +} + +static void +flush_std_files(void) +{ +    PyObject *fout = _PySys_GetObjectId(&PyId_stdout); +    PyObject *ferr = _PySys_GetObjectId(&PyId_stderr); +    PyObject *tmp; + +    if (fout != NULL && fout != Py_None && !file_is_closed(fout)) { +        tmp = _PyObject_CallMethodId(fout, &PyId_flush, ""); +        if (tmp == NULL) +            PyErr_WriteUnraisable(fout); +        else +            Py_DECREF(tmp); +    } + +    if (ferr != NULL && ferr != Py_None && !file_is_closed(ferr)) { +        tmp = _PyObject_CallMethodId(ferr, &PyId_flush, ""); +        if (tmp == NULL) +            PyErr_Clear(); +        else +            Py_DECREF(tmp); +    } +} + +/* Undo the effect of Py_Initialize(). + +   Beware: if multiple interpreter and/or thread states exist, these +   are not wiped out; only the current thread and interpreter state +   are deleted.  But since everything else is deleted, those other +   interpreter and thread states should no longer be used. + +   (XXX We should do better, e.g. wipe out all interpreters and +   threads.) + +   Locking: as above. + +*/ + +void +Py_Finalize(void) +{ +    PyInterpreterState *interp; +    PyThreadState *tstate; + +    if (!initialized) +        return; + +    wait_for_thread_shutdown(); + +    /* The interpreter is still entirely intact at this point, and the +     * exit funcs may be relying on that.  In particular, if some thread +     * or exit func is still waiting to do an import, the import machinery +     * expects Py_IsInitialized() to return true.  So don't say the +     * interpreter is uninitialized until after the exit funcs have run. +     * Note that Threading.py uses an exit func to do a join on all the +     * threads created thru it, so this also protects pending imports in +     * the threads created via Threading. +     */ +    call_py_exitfuncs(); + +    /* Get current thread state and interpreter pointer */ +    tstate = PyThreadState_GET(); +    interp = tstate->interp; + +    /* Remaining threads (e.g. daemon threads) will automatically exit +       after taking the GIL (in PyEval_RestoreThread()). */ +    _Py_Finalizing = tstate; +    initialized = 0; + +    /* Flush sys.stdout and sys.stderr */ +    flush_std_files(); + +    /* Disable signal handling */ +    PyOS_FiniInterrupts(); + +    /* Collect garbage.  This may call finalizers; it's nice to call these +     * before all modules are destroyed. +     * XXX If a __del__ or weakref callback is triggered here, and tries to +     * XXX import a module, bad things can happen, because Python no +     * XXX longer believes it's initialized. +     * XXX     Fatal Python error: Interpreter not initialized (version mismatch?) +     * XXX is easy to provoke that way.  I've also seen, e.g., +     * XXX     Exception exceptions.ImportError: 'No module named sha' +     * XXX         in <function callback at 0x008F5718> ignored +     * XXX but I'm unclear on exactly how that one happens.  In any case, +     * XXX I haven't seen a real-life report of either of these. +     */ +    PyGC_Collect(); +#ifdef COUNT_ALLOCS +    /* With COUNT_ALLOCS, it helps to run GC multiple times: +       each collection might release some types from the type +       list, so they become garbage. */ +    while (PyGC_Collect() > 0) +        /* nothing */; +#endif +    /* Destroy all modules */ +    PyImport_Cleanup(); + +    /* Flush sys.stdout and sys.stderr (again, in case more was printed) */ +    flush_std_files(); + +    /* Collect final garbage.  This disposes of cycles created by +     * class definitions, for example. +     * XXX This is disabled because it caused too many problems.  If +     * XXX a __del__ or weakref callback triggers here, Python code has +     * XXX a hard time running, because even the sys module has been +     * XXX cleared out (sys.stdout is gone, sys.excepthook is gone, etc). +     * XXX One symptom is a sequence of information-free messages +     * XXX coming from threads (if a __del__ or callback is invoked, +     * XXX other threads can execute too, and any exception they encounter +     * XXX triggers a comedy of errors as subsystem after subsystem +     * XXX fails to find what it *expects* to find in sys to help report +     * XXX the exception and consequent unexpected failures).  I've also +     * XXX seen segfaults then, after adding print statements to the +     * XXX Python code getting called. +     */ +#if 0 +    PyGC_Collect(); +#endif + +    /* Disable tracemalloc after all Python objects have been destroyed, +       so it is possible to use tracemalloc in objects destructor. */ +    _PyTraceMalloc_Fini(); + +    /* Destroy the database used by _PyImport_{Fixup,Find}Extension */ +    _PyImport_Fini(); + +    /* Cleanup typeobject.c's internal caches. */ +    _PyType_Fini(); + +    /* unload faulthandler module */ +    _PyFaulthandler_Fini(); + +    /* Debugging stuff */ +#ifdef COUNT_ALLOCS +    dump_counts(stdout); +#endif +    /* dump hash stats */ +    _PyHash_Fini(); + +    _PY_DEBUG_PRINT_TOTAL_REFS(); + +#ifdef Py_TRACE_REFS +    /* Display all objects still alive -- this can invoke arbitrary +     * __repr__ overrides, so requires a mostly-intact interpreter. +     * Alas, a lot of stuff may still be alive now that will be cleaned +     * up later. +     */ +    if (Py_GETENV("PYTHONDUMPREFS")) +        _Py_PrintReferences(stderr); +#endif /* Py_TRACE_REFS */ + +    /* Clear interpreter state and all thread states. */ +    PyInterpreterState_Clear(interp); + +    /* Now we decref the exception classes.  After this point nothing +       can raise an exception.  That's okay, because each Fini() method +       below has been checked to make sure no exceptions are ever +       raised. +    */ + +    _PyExc_Fini(); + +    /* Sundry finalizers */ +    PyMethod_Fini(); +    PyFrame_Fini(); +    PyCFunction_Fini(); +    PyTuple_Fini(); +    PyList_Fini(); +    PySet_Fini(); +    PyBytes_Fini(); +    PyByteArray_Fini(); +    PyLong_Fini(); +    PyFloat_Fini(); +    PyDict_Fini(); +    PySlice_Fini(); +    _PyGC_Fini(); +    _PyRandom_Fini(); + +    /* Cleanup Unicode implementation */ +    _PyUnicode_Fini(); + +    /* reset file system default encoding */ +    if (!Py_HasFileSystemDefaultEncoding && Py_FileSystemDefaultEncoding) { +        PyMem_RawFree((char*)Py_FileSystemDefaultEncoding); +        Py_FileSystemDefaultEncoding = NULL; +    } + +    /* XXX Still allocated: +       - various static ad-hoc pointers to interned strings +       - int and float free list blocks +       - whatever various modules and libraries allocate +    */ + +    PyGrammar_RemoveAccelerators(&_PyParser_Grammar); + +    /* Cleanup auto-thread-state */ +#ifdef WITH_THREAD +    _PyGILState_Fini(); +#endif /* WITH_THREAD */ + +    /* Delete current thread. After this, many C API calls become crashy. */ +    PyThreadState_Swap(NULL); +    PyInterpreterState_Delete(interp); + +#ifdef Py_TRACE_REFS +    /* Display addresses (& refcnts) of all objects still alive. +     * An address can be used to find the repr of the object, printed +     * above by _Py_PrintReferences. +     */ +    if (Py_GETENV("PYTHONDUMPREFS")) +        _Py_PrintReferenceAddresses(stderr); +#endif /* Py_TRACE_REFS */ +#ifdef PYMALLOC_DEBUG +    if (Py_GETENV("PYTHONMALLOCSTATS")) +        _PyObject_DebugMallocStats(stderr); +#endif + +    call_ll_exitfuncs(); +} + +/* Create and initialize a new interpreter and thread, and return the +   new thread.  This requires that Py_Initialize() has been called +   first. + +   Unsuccessful initialization yields a NULL pointer.  Note that *no* +   exception information is available even in this case -- the +   exception information is held in the thread, and there is no +   thread. + +   Locking: as above. + +*/ + +PyThreadState * +Py_NewInterpreter(void) +{ +    PyInterpreterState *interp; +    PyThreadState *tstate, *save_tstate; +    PyObject *bimod, *sysmod; + +    if (!initialized) +        Py_FatalError("Py_NewInterpreter: call Py_Initialize first"); + +    interp = PyInterpreterState_New(); +    if (interp == NULL) +        return NULL; + +    tstate = PyThreadState_New(interp); +    if (tstate == NULL) { +        PyInterpreterState_Delete(interp); +        return NULL; +    } + +    save_tstate = PyThreadState_Swap(tstate); + +    /* XXX The following is lax in error checking */ + +    interp->modules = PyDict_New(); + +    bimod = _PyImport_FindBuiltin("builtins"); +    if (bimod != NULL) { +        interp->builtins = PyModule_GetDict(bimod); +        if (interp->builtins == NULL) +            goto handle_error; +        Py_INCREF(interp->builtins); +    } + +    /* initialize builtin exceptions */ +    _PyExc_Init(bimod); + +    sysmod = _PyImport_FindBuiltin("sys"); +    if (bimod != NULL && sysmod != NULL) { +        PyObject *pstderr; + +        interp->sysdict = PyModule_GetDict(sysmod); +        if (interp->sysdict == NULL) +            goto handle_error; +        Py_INCREF(interp->sysdict); +        PySys_SetPath(Py_GetPath()); +        PyDict_SetItemString(interp->sysdict, "modules", +                             interp->modules); +        /* Set up a preliminary stderr printer until we have enough +           infrastructure for the io module in place. */ +        pstderr = PyFile_NewStdPrinter(fileno(stderr)); +        if (pstderr == NULL) +            Py_FatalError("Py_Initialize: can't set preliminary stderr"); +        _PySys_SetObjectId(&PyId_stderr, pstderr); +        PySys_SetObject("__stderr__", pstderr); +        Py_DECREF(pstderr); + +        _PyImportHooks_Init(); + +        import_init(interp, sysmod); + +        if (initfsencoding(interp) < 0) +            goto handle_error; + +        if (initstdio() < 0) +            Py_FatalError( +            "Py_Initialize: can't initialize sys standard streams"); +        initmain(interp); +        if (!Py_NoSiteFlag) +            initsite(); +    } + +    if (!PyErr_Occurred()) +        return tstate; + +handle_error: +    /* Oops, it didn't work.  Undo it all. */ + +    PyErr_PrintEx(0); +    PyThreadState_Clear(tstate); +    PyThreadState_Swap(save_tstate); +    PyThreadState_Delete(tstate); +    PyInterpreterState_Delete(interp); + +    return NULL; +} + +/* Delete an interpreter and its last thread.  This requires that the +   given thread state is current, that the thread has no remaining +   frames, and that it is its interpreter's only remaining thread. +   It is a fatal error to violate these constraints. + +   (Py_Finalize() doesn't have these constraints -- it zaps +   everything, regardless.) + +   Locking: as above. + +*/ + +void +Py_EndInterpreter(PyThreadState *tstate) +{ +    PyInterpreterState *interp = tstate->interp; + +    if (tstate != PyThreadState_GET()) +        Py_FatalError("Py_EndInterpreter: thread is not current"); +    if (tstate->frame != NULL) +        Py_FatalError("Py_EndInterpreter: thread still has a frame"); + +    wait_for_thread_shutdown(); + +    if (tstate != interp->tstate_head || tstate->next != NULL) +        Py_FatalError("Py_EndInterpreter: not the last thread"); + +    PyImport_Cleanup(); +    PyInterpreterState_Clear(interp); +    PyThreadState_Swap(NULL); +    PyInterpreterState_Delete(interp); +} + +#ifdef MS_WINDOWS +static wchar_t *progname = L"python"; +#else +static wchar_t *progname = L"python3"; +#endif + +void +Py_SetProgramName(wchar_t *pn) +{ +    if (pn && *pn) +        progname = pn; +} + +wchar_t * +Py_GetProgramName(void) +{ +    return progname; +} + +static wchar_t *default_home = NULL; +static wchar_t env_home[MAXPATHLEN+1]; + +void +Py_SetPythonHome(wchar_t *home) +{ +    default_home = home; +} + +wchar_t * +Py_GetPythonHome(void) +{ +    wchar_t *home = default_home; +    if (home == NULL && !Py_IgnoreEnvironmentFlag) { +        char* chome = Py_GETENV("PYTHONHOME"); +        if (chome) { +            size_t size = Py_ARRAY_LENGTH(env_home); +            size_t r = mbstowcs(env_home, chome, size); +            if (r != (size_t)-1 && r < size) +                home = env_home; +        } + +    } +    return home; +} + +/* Create __main__ module */ + +static void +initmain(PyInterpreterState *interp) +{ +    PyObject *m, *d, *loader; +    m = PyImport_AddModule("__main__"); +    if (m == NULL) +        Py_FatalError("can't create __main__ module"); +    d = PyModule_GetDict(m); +    if (PyDict_GetItemString(d, "__builtins__") == NULL) { +        PyObject *bimod = PyImport_ImportModule("builtins"); +        if (bimod == NULL) { +            Py_FatalError("Failed to retrieve builtins module"); +        } +        if (PyDict_SetItemString(d, "__builtins__", bimod) < 0) { +            Py_FatalError("Failed to initialize __main__.__builtins__"); +        } +        Py_DECREF(bimod); +    } +    /* Main is a little special - imp.is_builtin("__main__") will return +     * False, but BuiltinImporter is still the most appropriate initial +     * setting for its __loader__ attribute. A more suitable value will +     * be set if __main__ gets further initialized later in the startup +     * process. +     */ +    loader = PyDict_GetItemString(d, "__loader__"); +    if (loader == NULL || loader == Py_None) { +        PyObject *loader = PyObject_GetAttrString(interp->importlib, +                                                  "BuiltinImporter"); +        if (loader == NULL) { +            Py_FatalError("Failed to retrieve BuiltinImporter"); +        } +        if (PyDict_SetItemString(d, "__loader__", loader) < 0) { +            Py_FatalError("Failed to initialize __main__.__loader__"); +        } +        Py_DECREF(loader); +    } +} + +static int +initfsencoding(PyInterpreterState *interp) +{ +    PyObject *codec; + +    if (Py_FileSystemDefaultEncoding == NULL) +    { +        Py_FileSystemDefaultEncoding = get_locale_encoding(); +        if (Py_FileSystemDefaultEncoding == NULL) +            Py_FatalError("Py_Initialize: Unable to get the locale encoding"); + +        Py_HasFileSystemDefaultEncoding = 0; +        interp->fscodec_initialized = 1; +        return 0; +    } + +    /* the encoding is mbcs, utf-8 or ascii */ +    codec = _PyCodec_Lookup(Py_FileSystemDefaultEncoding); +    if (!codec) { +        /* Such error can only occurs in critical situations: no more +         * memory, import a module of the standard library failed, +         * etc. */ +        return -1; +    } +    Py_DECREF(codec); +    interp->fscodec_initialized = 1; +    return 0; +} + +/* Import the site module (not into __main__ though) */ + +static void +initsite(void) +{ +    PyObject *m; +    m = PyImport_ImportModule("site"); +    if (m == NULL) { +        fprintf(stderr, "Failed to import the site module\n"); +        PyErr_Print(); +        Py_Finalize(); +        exit(1); +    } +    else { +        Py_DECREF(m); +    } +} + +static PyObject* +create_stdio(PyObject* io, +    int fd, int write_mode, char* name, +    char* encoding, char* errors) +{ +    PyObject *buf = NULL, *stream = NULL, *text = NULL, *raw = NULL, *res; +    const char* mode; +    const char* newline; +    PyObject *line_buffering; +    int buffering, isatty; +    _Py_IDENTIFIER(open); +    _Py_IDENTIFIER(isatty); +    _Py_IDENTIFIER(TextIOWrapper); +    _Py_IDENTIFIER(mode); + +    /* stdin is always opened in buffered mode, first because it shouldn't +       make a difference in common use cases, second because TextIOWrapper +       depends on the presence of a read1() method which only exists on +       buffered streams. +    */ +    if (Py_UnbufferedStdioFlag && write_mode) +        buffering = 0; +    else +        buffering = -1; +    if (write_mode) +        mode = "wb"; +    else +        mode = "rb"; +    buf = _PyObject_CallMethodId(io, &PyId_open, "isiOOOi", +                                 fd, mode, buffering, +                                 Py_None, Py_None, Py_None, 0); +    if (buf == NULL) +        goto error; + +    if (buffering) { +        _Py_IDENTIFIER(raw); +        raw = _PyObject_GetAttrId(buf, &PyId_raw); +        if (raw == NULL) +            goto error; +    } +    else { +        raw = buf; +        Py_INCREF(raw); +    } + +    text = PyUnicode_FromString(name); +    if (text == NULL || _PyObject_SetAttrId(raw, &PyId_name, text) < 0) +        goto error; +    res = _PyObject_CallMethodId(raw, &PyId_isatty, ""); +    if (res == NULL) +        goto error; +    isatty = PyObject_IsTrue(res); +    Py_DECREF(res); +    if (isatty == -1) +        goto error; +    if (isatty || Py_UnbufferedStdioFlag) +        line_buffering = Py_True; +    else +        line_buffering = Py_False; + +    Py_CLEAR(raw); +    Py_CLEAR(text); + +#ifdef MS_WINDOWS +    /* sys.stdin: enable universal newline mode, translate "\r\n" and "\r" +       newlines to "\n". +       sys.stdout and sys.stderr: translate "\n" to "\r\n". */ +    newline = NULL; +#else +    /* sys.stdin: split lines at "\n". +       sys.stdout and sys.stderr: don't translate newlines (use "\n"). */ +    newline = "\n"; +#endif + +    stream = _PyObject_CallMethodId(io, &PyId_TextIOWrapper, "OsssO", +                                    buf, encoding, errors, +                                    newline, line_buffering); +    Py_CLEAR(buf); +    if (stream == NULL) +        goto error; + +    if (write_mode) +        mode = "w"; +    else +        mode = "r"; +    text = PyUnicode_FromString(mode); +    if (!text || _PyObject_SetAttrId(stream, &PyId_mode, text) < 0) +        goto error; +    Py_CLEAR(text); +    return stream; + +error: +    Py_XDECREF(buf); +    Py_XDECREF(stream); +    Py_XDECREF(text); +    Py_XDECREF(raw); +    return NULL; +} + +static int +is_valid_fd(int fd) +{ +    int dummy_fd; +    if (fd < 0 || !_PyVerify_fd(fd)) +        return 0; +    dummy_fd = dup(fd); +    if (dummy_fd < 0) +        return 0; +    close(dummy_fd); +    return 1; +} + +/* Initialize sys.stdin, stdout, stderr and builtins.open */ +static int +initstdio(void) +{ +    PyObject *iomod = NULL, *wrapper; +    PyObject *bimod = NULL; +    PyObject *m; +    PyObject *std = NULL; +    int status = 0, fd; +    PyObject * encoding_attr; +    char *pythonioencoding = NULL, *encoding, *errors; + +    /* Hack to avoid a nasty recursion issue when Python is invoked +       in verbose mode: pre-import the Latin-1 and UTF-8 codecs */ +    if ((m = PyImport_ImportModule("encodings.utf_8")) == NULL) { +        goto error; +    } +    Py_DECREF(m); + +    if (!(m = PyImport_ImportModule("encodings.latin_1"))) { +        goto error; +    } +    Py_DECREF(m); + +    if (!(bimod = PyImport_ImportModule("builtins"))) { +        goto error; +    } + +    if (!(iomod = PyImport_ImportModule("io"))) { +        goto error; +    } +    if (!(wrapper = PyObject_GetAttrString(iomod, "OpenWrapper"))) { +        goto error; +    } + +    /* Set builtins.open */ +    if (PyObject_SetAttrString(bimod, "open", wrapper) == -1) { +        Py_DECREF(wrapper); +        goto error; +    } +    Py_DECREF(wrapper); + +    encoding = _Py_StandardStreamEncoding; +    errors = _Py_StandardStreamErrors; +    if (!encoding || !errors) { +        if (!errors) { +            /* When the LC_CTYPE locale is the POSIX locale ("C locale"), +               stdin and stdout use the surrogateescape error handler by +               default, instead of the strict error handler. */ +            char *loc = setlocale(LC_CTYPE, NULL); +            if (loc != NULL && strcmp(loc, "C") == 0) +                errors = "surrogateescape"; +        } + +        pythonioencoding = Py_GETENV("PYTHONIOENCODING"); +        if (pythonioencoding) { +            char *err; +            pythonioencoding = _PyMem_Strdup(pythonioencoding); +            if (pythonioencoding == NULL) { +                PyErr_NoMemory(); +                goto error; +            } +            err = strchr(pythonioencoding, ':'); +            if (err) { +                *err = '\0'; +                err++; +                if (*err && !_Py_StandardStreamErrors) { +                    errors = err; +                } +            } +            if (*pythonioencoding && !encoding) { +                encoding = pythonioencoding; +            } +        } +    } + +    /* Set sys.stdin */ +    fd = fileno(stdin); +    /* Under some conditions stdin, stdout and stderr may not be connected +     * and fileno() may point to an invalid file descriptor. For example +     * GUI apps don't have valid standard streams by default. +     */ +    if (!is_valid_fd(fd)) { +        std = Py_None; +        Py_INCREF(std); +    } +    else { +        std = create_stdio(iomod, fd, 0, "<stdin>", encoding, errors); +        if (std == NULL) +            goto error; +    } /* if (fd < 0) */ +    PySys_SetObject("__stdin__", std); +    _PySys_SetObjectId(&PyId_stdin, std); +    Py_DECREF(std); + +    /* Set sys.stdout */ +    fd = fileno(stdout); +    if (!is_valid_fd(fd)) { +        std = Py_None; +        Py_INCREF(std); +    } +    else { +        std = create_stdio(iomod, fd, 1, "<stdout>", encoding, errors); +        if (std == NULL) +            goto error; +    } /* if (fd < 0) */ +    PySys_SetObject("__stdout__", std); +    _PySys_SetObjectId(&PyId_stdout, std); +    Py_DECREF(std); + +#if 1 /* Disable this if you have trouble debugging bootstrap stuff */ +    /* Set sys.stderr, replaces the preliminary stderr */ +    fd = fileno(stderr); +    if (!is_valid_fd(fd)) { +        std = Py_None; +        Py_INCREF(std); +    } +    else { +        std = create_stdio(iomod, fd, 1, "<stderr>", encoding, "backslashreplace"); +        if (std == NULL) +            goto error; +    } /* if (fd < 0) */ + +    /* Same as hack above, pre-import stderr's codec to avoid recursion +       when import.c tries to write to stderr in verbose mode. */ +    encoding_attr = PyObject_GetAttrString(std, "encoding"); +    if (encoding_attr != NULL) { +        const char * std_encoding; +        std_encoding = _PyUnicode_AsString(encoding_attr); +        if (std_encoding != NULL) { +            PyObject *codec_info = _PyCodec_Lookup(std_encoding); +            Py_XDECREF(codec_info); +        } +        Py_DECREF(encoding_attr); +    } +    PyErr_Clear();  /* Not a fatal error if codec isn't available */ + +    if (PySys_SetObject("__stderr__", std) < 0) { +        Py_DECREF(std); +        goto error; +    } +    if (_PySys_SetObjectId(&PyId_stderr, std) < 0) { +        Py_DECREF(std); +        goto error; +    } +    Py_DECREF(std); +#endif + +    if (0) { +  error: +        status = -1; +    } + +    /* We won't need them anymore. */ +    if (_Py_StandardStreamEncoding) { +        PyMem_RawFree(_Py_StandardStreamEncoding); +        _Py_StandardStreamEncoding = NULL; +    } +    if (_Py_StandardStreamErrors) { +        PyMem_RawFree(_Py_StandardStreamErrors); +        _Py_StandardStreamErrors = NULL; +    } +    PyMem_Free(pythonioencoding); +    Py_XDECREF(bimod); +    Py_XDECREF(iomod); +    return status; +} + + +/* Print the current exception (if an exception is set) with its traceback, + * or display the current Python stack. + * + * Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is + * called on catastrophic cases. */ + +static void +_Py_PrintFatalError(int fd) +{ +    PyObject *ferr, *res; +    PyObject *exception, *v, *tb; +    int has_tb; +    PyThreadState *tstate; + +    PyErr_Fetch(&exception, &v, &tb); +    if (exception == NULL) { +        /* No current exception */ +        goto display_stack; +    } + +    ferr = _PySys_GetObjectId(&PyId_stderr); +    if (ferr == NULL || ferr == Py_None) { +        /* sys.stderr is not set yet or set to None, +           no need to try to display the exception */ +        goto display_stack; +    } + +    PyErr_NormalizeException(&exception, &v, &tb); +    if (tb == NULL) { +        tb = Py_None; +        Py_INCREF(tb); +    } +    PyException_SetTraceback(v, tb); +    if (exception == NULL) { +        /* PyErr_NormalizeException() failed */ +        goto display_stack; +    } + +    has_tb = (tb != NULL && tb != Py_None); +    PyErr_Display(exception, v, tb); +    Py_XDECREF(exception); +    Py_XDECREF(v); +    Py_XDECREF(tb); + +    /* sys.stderr may be buffered: call sys.stderr.flush() */ +    res = _PyObject_CallMethodId(ferr, &PyId_flush, ""); +    if (res == NULL) +        PyErr_Clear(); +    else +        Py_DECREF(res); + +    if (has_tb) +        return; + +display_stack: +    /* PyGILState_GetThisThreadState() works even if the GIL was released */ +    tstate = PyGILState_GetThisThreadState(); +    if (tstate == NULL) { +        /* _Py_DumpTracebackThreads() requires the thread state to display +         * frames */ +        return; +    } + +    fputc('\n', stderr); +    fflush(stderr); + +    /* display the current Python stack */ +    _Py_DumpTracebackThreads(fd, tstate->interp, tstate); +} +/* Print fatal error message and abort */ + +void +Py_FatalError(const char *msg) +{ +    const int fd = fileno(stderr); +    static int reentrant = 0; +#ifdef MS_WINDOWS +    size_t len; +    WCHAR* buffer; +    size_t i; +#endif + +    if (reentrant) { +        /* Py_FatalError() caused a second fatal error. +           Example: flush_std_files() raises a recursion error. */ +        goto exit; +    } +    reentrant = 1; + +    fprintf(stderr, "Fatal Python error: %s\n", msg); +    fflush(stderr); /* it helps in Windows debug build */ + +    /* Print the exception (if an exception is set) with its traceback, +     * or display the current Python stack. */ +    _Py_PrintFatalError(fd); + +    /* Flush sys.stdout and sys.stderr */ +    flush_std_files(); + +    /* The main purpose of faulthandler is to display the traceback. We already +     * did our best to display it. So faulthandler can now be disabled. +     * (Don't trigger it on abort().) */ +    _PyFaulthandler_Fini(); + +#ifdef MS_WINDOWS +    len = strlen(msg); + +    /* Convert the message to wchar_t. This uses a simple one-to-one +    conversion, assuming that the this error message actually uses ASCII +    only. If this ceases to be true, we will have to convert. */ +    buffer = alloca( (len+1) * (sizeof *buffer)); +    for( i=0; i<=len; ++i) +        buffer[i] = msg[i]; +    OutputDebugStringW(L"Fatal Python error: "); +    OutputDebugStringW(buffer); +    OutputDebugStringW(L"\n"); +#endif /* MS_WINDOWS */ + +exit: +#if defined(MS_WINDOWS) && defined(_DEBUG) +    DebugBreak(); +#endif +    abort(); +} + +/* Clean up and exit */ + +#ifdef WITH_THREAD +#include "pythread.h" +#endif + +static void (*pyexitfunc)(void) = NULL; +/* For the atexit module. */ +void _Py_PyAtExit(void (*func)(void)) +{ +    pyexitfunc = func; +} + +static void +call_py_exitfuncs(void) +{ +    if (pyexitfunc == NULL) +        return; + +    (*pyexitfunc)(); +    PyErr_Clear(); +} + +/* Wait until threading._shutdown completes, provided +   the threading module was imported in the first place. +   The shutdown routine will wait until all non-daemon +   "threading" threads have completed. */ +static void +wait_for_thread_shutdown(void) +{ +#ifdef WITH_THREAD +    _Py_IDENTIFIER(_shutdown); +    PyObject *result; +    PyThreadState *tstate = PyThreadState_GET(); +    PyObject *threading = PyMapping_GetItemString(tstate->interp->modules, +                                                  "threading"); +    if (threading == NULL) { +        /* threading not imported */ +        PyErr_Clear(); +        return; +    } +    result = _PyObject_CallMethodId(threading, &PyId__shutdown, ""); +    if (result == NULL) { +        PyErr_WriteUnraisable(threading); +    } +    else { +        Py_DECREF(result); +    } +    Py_DECREF(threading); +#endif +} + +#define NEXITFUNCS 32 +static void (*exitfuncs[NEXITFUNCS])(void); +static int nexitfuncs = 0; + +int Py_AtExit(void (*func)(void)) +{ +    if (nexitfuncs >= NEXITFUNCS) +        return -1; +    exitfuncs[nexitfuncs++] = func; +    return 0; +} + +static void +call_ll_exitfuncs(void) +{ +    while (nexitfuncs > 0) +        (*exitfuncs[--nexitfuncs])(); + +    fflush(stdout); +    fflush(stderr); +} + +void +Py_Exit(int sts) +{ +    Py_Finalize(); + +    exit(sts); +} + +static void +initsigs(void) +{ +#ifdef SIGPIPE +    PyOS_setsig(SIGPIPE, SIG_IGN); +#endif +#ifdef SIGXFZ +    PyOS_setsig(SIGXFZ, SIG_IGN); +#endif +#ifdef SIGXFSZ +    PyOS_setsig(SIGXFSZ, SIG_IGN); +#endif +    PyOS_InitInterrupts(); /* May imply initsignal() */ +    if (PyErr_Occurred()) { +        Py_FatalError("Py_Initialize: can't import signal"); +    } +} + + +/* Restore signals that the interpreter has called SIG_IGN on to SIG_DFL. + * + * All of the code in this function must only use async-signal-safe functions, + * listed at `man 7 signal` or + * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html. + */ +void +_Py_RestoreSignals(void) +{ +#ifdef SIGPIPE +    PyOS_setsig(SIGPIPE, SIG_DFL); +#endif +#ifdef SIGXFZ +    PyOS_setsig(SIGXFZ, SIG_DFL); +#endif +#ifdef SIGXFSZ +    PyOS_setsig(SIGXFSZ, SIG_DFL); +#endif +} + + +/* + * The file descriptor fd is considered ``interactive'' if either + *   a) isatty(fd) is TRUE, or + *   b) the -i flag was given, and the filename associated with + *      the descriptor is NULL or "<stdin>" or "???". + */ +int +Py_FdIsInteractive(FILE *fp, const char *filename) +{ +    if (isatty((int)fileno(fp))) +        return 1; +    if (!Py_InteractiveFlag) +        return 0; +    return (filename == NULL) || +           (strcmp(filename, "<stdin>") == 0) || +           (strcmp(filename, "???") == 0); +} + + +/* Wrappers around sigaction() or signal(). */ + +PyOS_sighandler_t +PyOS_getsig(int sig) +{ +#ifdef HAVE_SIGACTION +    struct sigaction context; +    if (sigaction(sig, NULL, &context) == -1) +        return SIG_ERR; +    return context.sa_handler; +#else +    PyOS_sighandler_t handler; +/* Special signal handling for the secure CRT in Visual Studio 2005 */ +#if defined(_MSC_VER) && _MSC_VER >= 1400 +    switch (sig) { +    /* Only these signals are valid */ +    case SIGINT: +    case SIGILL: +    case SIGFPE: +    case SIGSEGV: +    case SIGTERM: +    case SIGBREAK: +    case SIGABRT: +        break; +    /* Don't call signal() with other values or it will assert */ +    default: +        return SIG_ERR; +    } +#endif /* _MSC_VER && _MSC_VER >= 1400 */ +    handler = signal(sig, SIG_IGN); +    if (handler != SIG_ERR) +        signal(sig, handler); +    return handler; +#endif +} + +/* + * All of the code in this function must only use async-signal-safe functions, + * listed at `man 7 signal` or + * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html. + */ +PyOS_sighandler_t +PyOS_setsig(int sig, PyOS_sighandler_t handler) +{ +#ifdef HAVE_SIGACTION +    /* Some code in Modules/signalmodule.c depends on sigaction() being +     * used here if HAVE_SIGACTION is defined.  Fix that if this code +     * changes to invalidate that assumption. +     */ +    struct sigaction context, ocontext; +    context.sa_handler = handler; +    sigemptyset(&context.sa_mask); +    context.sa_flags = 0; +    if (sigaction(sig, &context, &ocontext) == -1) +        return SIG_ERR; +    return ocontext.sa_handler; +#else +    PyOS_sighandler_t oldhandler; +    oldhandler = signal(sig, handler); +#ifdef HAVE_SIGINTERRUPT +    siginterrupt(sig, 1); +#endif +    return oldhandler; +#endif +} + +#ifdef __cplusplus +} +#endif | 
