""" External functions accessing the win32 api.
Common types, functions from core win32 libraries, such as kernel32
"""

import os
import errno

from rpython.rlib.rposix_environ import make_env_impls
from rpython.rtyper.tool import rffi_platform
from rpython.tool.udir import udir
from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.translator.platform import CompilationError
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rlib.rarithmetic import intmask, r_longlong
from rpython.rlib import jit

# This module can be imported on any platform,
# but most symbols are not usable...
WIN32 = os.name == "nt"

if WIN32:
    eci = ExternalCompilationInfo(
        includes = ['windows.h', 'stdio.h', 'stdlib.h', 'io.h'],
        libraries = ['kernel32'],
        )
else:
    eci = ExternalCompilationInfo()

class CConfig:
    _compilation_info_ = eci

    if WIN32:
        DWORD_PTR = rffi_platform.SimpleType("DWORD_PTR", rffi.LONG)
        WORD = rffi_platform.SimpleType("WORD", rffi.USHORT)
        DWORD = rffi_platform.SimpleType("DWORD", rffi.UINT)
        BOOL = rffi_platform.SimpleType("BOOL", rffi.LONG)
        BYTE = rffi_platform.SimpleType("BYTE", rffi.UCHAR)
        WCHAR = rffi_platform.SimpleType("WCHAR", rffi.UCHAR)
        INT = rffi_platform.SimpleType("INT", rffi.INT)
        LONG = rffi_platform.SimpleType("LONG", rffi.LONG)
        PLONG = rffi_platform.SimpleType("PLONG", rffi.LONGP)
        LPVOID = rffi_platform.SimpleType("LPVOID", rffi.INTP)
        LPCVOID = rffi_platform.SimpleType("LPCVOID", rffi.VOIDP)
        LPSTR = rffi_platform.SimpleType("LPSTR", rffi.CCHARP)
        LPCSTR = rffi_platform.SimpleType("LPCSTR", rffi.CCHARP)
        LPWSTR = rffi_platform.SimpleType("LPWSTR", rffi.CWCHARP)
        LPCWSTR = rffi_platform.SimpleType("LPCWSTR", rffi.CWCHARP)
        LPDWORD = rffi_platform.SimpleType("LPDWORD", rffi.UINTP)
        LPWORD = rffi_platform.SimpleType("LPWORD", rffi.USHORTP)
        LPBOOL = rffi_platform.SimpleType("LPBOOL", rffi.LONGP)
        LPBYTE = rffi_platform.SimpleType("LPBYTE", rffi.UCHARP)
        SIZE_T = rffi_platform.SimpleType("SIZE_T", rffi.SIZE_T)
        ULONG_PTR = rffi_platform.SimpleType("ULONG_PTR", rffi.ULONG)

        HRESULT = rffi_platform.SimpleType("HRESULT", rffi.LONG)
        HLOCAL = rffi_platform.SimpleType("HLOCAL", rffi.VOIDP)

        FILETIME = rffi_platform.Struct('FILETIME',
                                        [('dwLowDateTime', rffi.UINT),
                                         ('dwHighDateTime', rffi.UINT)])
        SYSTEMTIME = rffi_platform.Struct('SYSTEMTIME',
                                          [])

        Struct = rffi_platform.Struct
        COORD = Struct("COORD",
                       [("X", rffi.SHORT),
                        ("Y", rffi.SHORT)])

        SMALL_RECT = Struct("SMALL_RECT",
                            [("Left", rffi.SHORT),
                             ("Top", rffi.SHORT),
                             ("Right", rffi.SHORT),
                             ("Bottom", rffi.SHORT)])

        CONSOLE_SCREEN_BUFFER_INFO = Struct("CONSOLE_SCREEN_BUFFER_INFO",
                                            [("dwSize", COORD),
                                             ("dwCursorPosition", COORD),
                                             ("wAttributes", WORD.ctype_hint),
                                             ("srWindow", SMALL_RECT),
                                             ("dwMaximumWindowSize", COORD)])

        OSVERSIONINFOEX = rffi_platform.Struct(
            'OSVERSIONINFOEX',
            [('dwOSVersionInfoSize', rffi.UINT),
             ('dwMajorVersion', rffi.UINT),
             ('dwMinorVersion', rffi.UINT),
             ('dwBuildNumber',  rffi.UINT),
             ('dwPlatformId',  rffi.UINT),
             ('szCSDVersion', rffi.CFixedArray(lltype.Char, 1)),
             ('wServicePackMajor', rffi.USHORT),
             ('wServicePackMinor', rffi.USHORT),
             ('wSuiteMask', rffi.USHORT),
             ('wProductType', rffi.UCHAR),
         ])

        LPSECURITY_ATTRIBUTES = rffi_platform.SimpleType(
            "LPSECURITY_ATTRIBUTES", rffi.CCHARP)

        DEFAULT_LANGUAGE = rffi_platform.ConstantInteger(
            "MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)")

        defines = """FORMAT_MESSAGE_ALLOCATE_BUFFER FORMAT_MESSAGE_FROM_SYSTEM
                       MAX_PATH _MAX_ENV FORMAT_MESSAGE_IGNORE_INSERTS
                       WAIT_OBJECT_0 WAIT_TIMEOUT INFINITE
                       ERROR_INVALID_HANDLE
                       DELETE READ_CONTROL SYNCHRONIZE WRITE_DAC
                       WRITE_OWNER PROCESS_ALL_ACCESS
                       PROCESS_CREATE_PROCESS PROCESS_CREATE_THREAD
                       PROCESS_DUP_HANDLE PROCESS_QUERY_INFORMATION
                       PROCESS_SET_QUOTA
                       PROCESS_SUSPEND_RESUME PROCESS_TERMINATE
                       PROCESS_VM_OPERATION PROCESS_VM_READ
                       PROCESS_VM_WRITE
                       CTRL_C_EVENT CTRL_BREAK_EVENT
                       MB_ERR_INVALID_CHARS ERROR_NO_UNICODE_TRANSLATION
                       WC_NO_BEST_FIT_CHARS STD_INPUT_HANDLE STD_OUTPUT_HANDLE
                       STD_ERROR_HANDLE HANDLE_FLAG_INHERIT FILE_TYPE_CHAR
                       LOAD_WITH_ALTERED_SEARCH_PATH CT_CTYPE3 C3_HIGHSURROGATE
                       CP_ACP CP_UTF8 CP_UTF7 CP_OEMCP MB_ERR_INVALID_CHARS
                    """
        from rpython.translator.platform import host_factory
        static_platform = host_factory()
        if static_platform.name == 'msvc':
            defines += ' PROCESS_QUERY_LIMITED_INFORMATION'
        for name in defines.split():
            locals()[name] = rffi_platform.ConstantInteger(name)

for k, v in rffi_platform.configure(CConfig).items():
    globals()[k] = v

def winexternal(name, args, result, **kwds):
    return rffi.llexternal(name, args, result, compilation_info=eci,
                           calling_conv='win', **kwds)

if WIN32:
    HANDLE = rffi.COpaquePtr(typedef='HANDLE')
    assert rffi.cast(HANDLE, -1) == rffi.cast(HANDLE, -1)

    LPHANDLE = rffi.CArrayPtr(HANDLE)
    HMODULE = HANDLE
    NULL_HANDLE = rffi.cast(HANDLE, 0)
    INVALID_HANDLE_VALUE = rffi.cast(HANDLE, -1)
    GENERIC_READ     = rffi.cast(DWORD, r_longlong(0x80000000))
    GENERIC_WRITE    = rffi.cast(DWORD, r_longlong(0x40000000))
    GENERIC_EXECUTE  = rffi.cast(DWORD, r_longlong(0x20000000))
    GENERIC_ALL      = rffi.cast(DWORD, r_longlong(0x10000000))
    FILE_SHARE_READ  = rffi.cast(DWORD, r_longlong(0x00000001))
    FILE_SHARE_WRITE = rffi.cast(DWORD, r_longlong(0x00000002))

    PFILETIME = rffi.CArrayPtr(FILETIME)

    _GetLastError = winexternal('GetLastError', [], DWORD,
                                _nowrapper=True, sandboxsafe=True)
    _SetLastError = winexternal('SetLastError', [DWORD], lltype.Void,
                                _nowrapper=True, sandboxsafe=True)

    def GetLastError_saved():
        """Return the value of the "saved LastError".
        The C-level GetLastError() is saved there after a call to a C
        function, if that C function was declared with the flag
        llexternal(..., save_err=rffi.RFFI_SAVE_LASTERROR).
        Functions without that flag don't change the saved LastError.
        Alternatively, if the function was declared RFFI_SAVE_WSALASTERROR,
        then the value of the C-level WSAGetLastError() is saved instead
        (into the same "saved LastError" variable).
        """
        from rpython.rlib import rthread
        return rffi.cast(lltype.Signed, rthread.tlfield_rpy_lasterror.getraw())

    def SetLastError_saved(err):
        """Set the value of the saved LastError.  This value will be used in
        a call to the C-level SetLastError() just before calling the
        following C function, provided it was declared
        llexternal(..., save_err=RFFI_READSAVED_LASTERROR).
        """
        from rpython.rlib import rthread
        rthread.tlfield_rpy_lasterror.setraw(rffi.cast(DWORD, err))

    def GetLastError_alt_saved():
        """Return the value of the "saved alt LastError".
        The C-level GetLastError() is saved there after a call to a C
        function, if that C function was declared with the flag
        llexternal(..., save_err=RFFI_SAVE_LASTERROR | RFFI_ALT_ERRNO).
        Functions without that flag don't change the saved LastError.
        Alternatively, if the function was declared
        RFFI_SAVE_WSALASTERROR | RFFI_ALT_ERRNO,
        then the value of the C-level WSAGetLastError() is saved instead
        (into the same "saved alt LastError" variable).
        """
        from rpython.rlib import rthread
        return rffi.cast(lltype.Signed, rthread.tlfield_alt_lasterror.getraw())

    def SetLastError_alt_saved(err):
        """Set the value of the saved alt LastError.  This value will be used in
        a call to the C-level SetLastError() just before calling the
        following C function, provided it was declared
        llexternal(..., save_err=RFFI_READSAVED_LASTERROR | RFFI_ALT_ERRNO).
        """
        from rpython.rlib import rthread
        rthread.tlfield_alt_lasterror.setraw(rffi.cast(DWORD, err))

    # In tests, the first call to _GetLastError() is always wrong,
    # because error is hidden by operations in ll2ctypes.  Call it now.
    _GetLastError()

    GetModuleHandle = winexternal('GetModuleHandleA', [rffi.CCHARP], HMODULE)
    LoadLibrary = winexternal('LoadLibraryA', [rffi.CCHARP], HMODULE,
                              save_err=rffi.RFFI_SAVE_LASTERROR)
    def wrap_loadlibraryex(func):
        def loadlibrary(name, flags=LOAD_WITH_ALTERED_SEARCH_PATH):
            # Requires a full path name with '/' -> '\\'
            return func(name, NULL_HANDLE, flags)
        return loadlibrary

    _LoadLibraryExA = winexternal('LoadLibraryExA',
                                [rffi.CCHARP, HANDLE, DWORD], HMODULE,
                                save_err=rffi.RFFI_SAVE_LASTERROR)
    LoadLibraryExA = wrap_loadlibraryex(_LoadLibraryExA)
    LoadLibraryW = winexternal('LoadLibraryW', [rffi.CWCHARP], HMODULE,
                              save_err=rffi.RFFI_SAVE_LASTERROR)
    _LoadLibraryExW = winexternal('LoadLibraryExW',
                                [rffi.CWCHARP, HANDLE, DWORD], HMODULE,
                                save_err=rffi.RFFI_SAVE_LASTERROR)
    LoadLibraryExW = wrap_loadlibraryex(_LoadLibraryExW)
    GetProcAddress = winexternal('GetProcAddress',
                                 [HMODULE, rffi.CCHARP],
                                 rffi.VOIDP)
    FreeLibrary = winexternal('FreeLibrary', [HMODULE], BOOL, releasegil=False)

    LocalFree = winexternal('LocalFree', [HLOCAL], HLOCAL)
    CloseHandle = winexternal('CloseHandle', [HANDLE], BOOL, releasegil=False,
                              save_err=rffi.RFFI_SAVE_LASTERROR)
    CloseHandle_no_err = winexternal('CloseHandle', [HANDLE], BOOL,
                                     releasegil=False)

    FormatMessage = winexternal(
        'FormatMessageA',
        [DWORD, rffi.VOIDP, DWORD, DWORD, rffi.CCHARP, DWORD, rffi.VOIDP],
        DWORD)
    FormatMessageW = winexternal(
        'FormatMessageW',
        [DWORD, rffi.VOIDP, DWORD, DWORD, rffi.CWCHARP, DWORD, rffi.VOIDP],
        DWORD)

    _get_osfhandle = rffi.llexternal('_get_osfhandle', [rffi.INT], rffi.INTP)

    def get_osfhandle(fd):
        from rpython.rlib.rposix import FdValidator
        with FdValidator(fd):
            handle = rffi.cast(HANDLE, _get_osfhandle(fd))
        if handle == INVALID_HANDLE_VALUE:
            raise WindowsError(ERROR_INVALID_HANDLE, "Invalid file handle")
        return handle

    _open_osfhandle = rffi.llexternal('_open_osfhandle', [rffi.INTP, rffi.INT], rffi.INT)

    def open_osfhandle(handle, flags):
        from rpython.rlib.rposix import FdValidator
        fd = _open_osfhandle(handle, flags)
        with FdValidator(fd):
            return fd
    
    wcsncpy_s = rffi.llexternal('wcsncpy_s', 
                    [rffi.CWCHARP, rffi.SIZE_T, rffi.CWCHARP, rffi.SIZE_T], rffi.INT)

    def build_winerror_to_errno():
        """Build a dictionary mapping windows error numbers to POSIX errno.
        The function returns the dict, and the default value for codes not
        in the dict."""
        # Prior to Visual Studio 8, the MSVCRT dll doesn't export the
        # _dosmaperr() function, which is available only when compiled
        # against the static CRT library. After Visual Studio 9, this
        # private function seems to be gone, so use a static map, from
        # CPython PC/errmap.h
        errors = {
                2: 2, 3: 2, 4: 24, 5: 13, 6: 9, 7: 12, 8: 12, 9: 12, 10: 7,
                11: 8, 15: 2, 16: 13, 17: 18, 18: 2, 19: 13, 20: 13, 21: 13,
                22: 13, 23: 13, 24: 13, 25: 13, 26: 13, 27: 13, 28: 13,
                29: 13, 30: 13, 31: 13, 32: 13, 33: 13, 34: 13, 35: 13,
                36: 13, 53: 2, 65: 13, 67: 2, 80: 17, 82: 13, 83: 13, 89: 11,
                108: 13, 109: 32, 112: 28, 114: 9, 128: 10, 129: 10, 130: 9,
                132: 13, 145: 41, 158: 13, 161: 2, 164: 11, 167: 13, 183: 17,
                188: 8, 189: 8, 190: 8, 191: 8, 192: 8, 193: 8, 194: 8,
                195: 8, 196: 8, 197: 8, 198: 8, 199: 8, 200: 8, 201: 8,
                202: 8, 206: 2, 215: 11, 232: 32, 267: 20, 1816: 12,
                }
        return errors, errno.EINVAL

    # A bit like strerror...
    def FormatError(code):
        return llimpl_FormatError(code)
    def FormatErrorW(code):
        """
        returns utf8, n_codepoints
        """
        return llimpl_FormatErrorW(code)

    def llimpl_FormatError(code):
        "Return a message corresponding to the given Windows error code."
        buf = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
        buf[0] = lltype.nullptr(rffi.CCHARP.TO)
        try:
            msglen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                                   FORMAT_MESSAGE_FROM_SYSTEM |
                                   FORMAT_MESSAGE_IGNORE_INSERTS,
                                   None,
                                   rffi.cast(DWORD, code),
                                   DEFAULT_LANGUAGE,
                                   rffi.cast(rffi.CCHARP, buf),
                                   0, None)
            buflen = intmask(msglen)

            # remove trailing cr/lf and dots
            s_buf = buf[0]
            while buflen > 0 and (s_buf[buflen - 1] <= ' ' or
                                  s_buf[buflen - 1] == '.'):
                buflen -= 1

            if buflen <= 0:
                result = 'Windows Error %d' % (code,)
            else:
                result = rffi.charpsize2str(s_buf, buflen)
        finally:
            LocalFree(rffi.cast(rffi.VOIDP, buf[0]))
            lltype.free(buf, flavor='raw')

        return result

    def llimpl_FormatErrorW(code):
        "Return a utf8-encoded msg and its length"
        buf = lltype.malloc(rffi.CWCHARPP.TO, 1, flavor='raw')
        buf[0] = lltype.nullptr(rffi.CWCHARP.TO)
        try:
            msglen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                                    FORMAT_MESSAGE_FROM_SYSTEM |
                                    FORMAT_MESSAGE_IGNORE_INSERTS,
                                    None,
                                    rffi.cast(DWORD, code),
                                    DEFAULT_LANGUAGE,
                                    rffi.cast(rffi.CWCHARP, buf),
                                    0, None)
            buflen = intmask(msglen)

            # remove trailing cr/lf and dots
            s_buf = buf[0]
            while buflen > 0 and (ord(s_buf[buflen - 1]) <= ord(' ') or
                                  s_buf[buflen - 1] == u'.'):
                buflen -= 1

            if buflen <= 0:
                msg = 'Windows Error %d' % (code,)
                result = msg, len(msg)
            else:
                result = rffi.wcharpsize2utf8(s_buf, buflen), buflen
        finally:
            LocalFree(rffi.cast(rffi.VOIDP, buf[0]))
            lltype.free(buf, flavor='raw')

        return result

    def lastSavedWindowsError(context="Windows Error"):
        code = GetLastError_saved()
        return WindowsError(code, context)

    def FAILED(hr):
        return rffi.cast(HRESULT, hr) < 0

    _GetModuleFileName = winexternal('GetModuleFileNameA',
                                     [HMODULE, rffi.CCHARP, DWORD],
                                     DWORD)

    def GetModuleFileName(module):
        size = MAX_PATH
        buf = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw')
        try:
            res = _GetModuleFileName(module, buf, size)
            if not res:
                return ''
            else:
                return ''.join([buf[i] for i in range(res)])
        finally:
            lltype.free(buf, flavor='raw')

    _GetVersionEx = winexternal('GetVersionExA',
                                [lltype.Ptr(OSVERSIONINFOEX)],
                                DWORD,
                                save_err=rffi.RFFI_SAVE_LASTERROR)

    @jit.dont_look_inside
    def GetVersionEx():
        info = lltype.malloc(OSVERSIONINFOEX, flavor='raw')
        rffi.setintfield(info, 'c_dwOSVersionInfoSize',
                         rffi.sizeof(OSVERSIONINFOEX))
        try:
            if not _GetVersionEx(info):
                raise lastSavedWindowsError()
            return (rffi.cast(lltype.Signed, info.c_dwMajorVersion),
                    rffi.cast(lltype.Signed, info.c_dwMinorVersion),
                    rffi.cast(lltype.Signed, info.c_dwBuildNumber),
                    rffi.cast(lltype.Signed, info.c_dwPlatformId),
                    rffi.charp2str(rffi.cast(rffi.CCHARP,
                                             info.c_szCSDVersion)),
                    rffi.cast(lltype.Signed, info.c_wServicePackMajor),
                    rffi.cast(lltype.Signed, info.c_wServicePackMinor),
                    rffi.cast(lltype.Signed, info.c_wSuiteMask),
                    rffi.cast(lltype.Signed, info.c_wProductType))
        finally:
            lltype.free(info, flavor='raw')

    _WaitForSingleObject = winexternal(
        'WaitForSingleObject', [HANDLE, DWORD], DWORD,
        save_err=rffi.RFFI_SAVE_LASTERROR)

    def WaitForSingleObject(handle, timeout):
        """Return values:
        - WAIT_OBJECT_0 when the object is signaled
        - WAIT_TIMEOUT when the timeout elapsed"""
        res = _WaitForSingleObject(handle, timeout)
        if res == rffi.cast(DWORD, -1):
            raise lastSavedWindowsError("WaitForSingleObject")
        return res

    _WaitForMultipleObjects = winexternal(
        'WaitForMultipleObjects', [
            DWORD, rffi.CArrayPtr(HANDLE), BOOL, DWORD], DWORD,
            save_err=rffi.RFFI_SAVE_LASTERROR)

    def WaitForMultipleObjects(handles, waitall=False, timeout=INFINITE):
        """Return values:
        - WAIT_OBJECT_0 + index when an object is signaled
        - WAIT_TIMEOUT when the timeout elapsed"""
        nb = len(handles)
        handle_array = lltype.malloc(rffi.CArrayPtr(HANDLE).TO, nb,
                                     flavor='raw')
        try:
            for i in range(nb):
                handle_array[i] = handles[i]
            res = _WaitForMultipleObjects(nb, handle_array, waitall, timeout)
            if res == rffi.cast(DWORD, -1):
                raise lastSavedWindowsError("WaitForMultipleObjects")
            return res
        finally:
            lltype.free(handle_array, flavor='raw')

    _CreateEvent = winexternal(
        'CreateEventA', [rffi.VOIDP, BOOL, BOOL, LPCSTR], HANDLE,
        save_err=rffi.RFFI_SAVE_LASTERROR)
    def CreateEvent(*args):
        handle = _CreateEvent(*args)
        if handle == NULL_HANDLE:
            raise lastSavedWindowsError("CreateEvent")
        return handle
    SetEvent = winexternal(
        'SetEvent', [HANDLE], BOOL)
    ResetEvent = winexternal(
        'ResetEvent', [HANDLE], BOOL)
    _OpenProcess = winexternal(
        'OpenProcess', [DWORD, BOOL, DWORD], HANDLE,
        save_err=rffi.RFFI_SAVE_LASTERROR)
    def OpenProcess(*args):
        ''' OpenProcess( dwDesiredAccess, bInheritHandle, dwProcessId)
        where dwDesiredAccess is a combination of the flags:
        DELETE (0x00010000L)
        READ_CONTROL (0x00020000L)
        SYNCHRONIZE (0x00100000L)
        WRITE_DAC (0x00040000L)
        WRITE_OWNER (0x00080000L)

        PROCESS_ALL_ACCESS
        PROCESS_CREATE_PROCESS (0x0080)
        PROCESS_CREATE_THREAD (0x0002)
        PROCESS_DUP_HANDLE (0x0040)
        PROCESS_QUERY_INFORMATION (0x0400)
        PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
        PROCESS_SET_QUOTA (0x0100)
        PROCESS_SUSPEND_RESUME (0x0800)
        PROCESS_TERMINATE (0x0001)
        PROCESS_VM_OPERATION (0x0008)
        PROCESS_VM_READ (0x0010)
        PROCESS_VM_WRITE (0x0020)
        SYNCHRONIZE (0x00100000L)
        '''
        handle = _OpenProcess(*args)
        if handle == NULL_HANDLE:
            raise lastSavedWindowsError("OpenProcess")
        return handle
    TerminateProcess = winexternal(
        'TerminateProcess', [HANDLE, rffi.UINT], BOOL,
        save_err=rffi.RFFI_SAVE_LASTERROR)
    GenerateConsoleCtrlEvent = winexternal(
        'GenerateConsoleCtrlEvent', [DWORD, DWORD], BOOL,
        save_err=rffi.RFFI_SAVE_LASTERROR)
    _GetCurrentProcessId = winexternal(
        'GetCurrentProcessId', [], DWORD)
    def GetCurrentProcessId():
        return rffi.cast(lltype.Signed, _GetCurrentProcessId())

    _GetConsoleCP = winexternal('GetConsoleCP', [], DWORD)
    _GetConsoleOutputCP = winexternal('GetConsoleOutputCP', [], DWORD)
    def GetConsoleCP():
        return rffi.cast(lltype.Signed, _GetConsoleCP())
    def GetConsoleOutputCP():
        return rffi.cast(lltype.Signed, _GetConsoleOutputCP())

    _wenviron_items, _wgetenv, _wputenv = make_env_impls(win32=True)


    _GetStdHandle = winexternal(
        'GetStdHandle', [DWORD], HANDLE)

    def GetStdHandle(handle_id):
        return _GetStdHandle(handle_id)
    CONSOLE_SCREEN_BUFFER_INFO_P = lltype.Ptr(CONSOLE_SCREEN_BUFFER_INFO)
    GetConsoleScreenBufferInfo = winexternal(
        "GetConsoleScreenBufferInfo", [HANDLE, CONSOLE_SCREEN_BUFFER_INFO_P], BOOL)

    _GetHandleInformation = winexternal(
        'GetHandleInformation', [HANDLE, LPDWORD], BOOL)
    _SetHandleInformation = winexternal(
        'SetHandleInformation', [HANDLE, DWORD, DWORD], BOOL)

    def set_inheritable(fd, inheritable):
        handle = get_osfhandle(fd)
        set_handle_inheritable(handle, inheritable)

    def set_handle_inheritable(handle, inheritable):
        assert lltype.typeOf(handle) is HANDLE
        if inheritable:
            flags = HANDLE_FLAG_INHERIT
        else:
            flags = 0
        if not _SetHandleInformation(handle, HANDLE_FLAG_INHERIT, flags):
            raise lastSavedWindowsError("SetHandleInformation")

    def get_inheritable(fd):
        handle = get_osfhandle(fd)
        return get_handle_inheritable(handle)

    def get_handle_inheritable(handle):
        assert lltype.typeOf(handle) is HANDLE
        pflags = lltype.malloc(LPDWORD.TO, 1, flavor='raw')
        try:
            if not _GetHandleInformation(handle, pflags):
                raise lastSavedWindowsError("GetHandleInformation")
            flags = pflags[0]
        finally:
            lltype.free(pflags, flavor='raw')
        return (flags & HANDLE_FLAG_INHERIT) != 0

    _GetFileType = winexternal('GetFileType', [HANDLE], DWORD)

    def c_dup_noninheritable(fd1):
        from rpython.rlib.rposix import c_dup

        ftype = _GetFileType(get_osfhandle(fd1))
        fd2 = c_dup(fd1)     # the inheritable version
        if fd2 >= 0 and ftype != FILE_TYPE_CHAR:
            try:
                set_inheritable(fd2, False)
            except:
                os.close(fd2)
                raise
        return fd2

    def c_dup2_noninheritable(fd1, fd2):
        from rpython.rlib.rposix import c_dup2

        ftype = _GetFileType(get_osfhandle(fd1))
        res = c_dup2(fd1, fd2)     # the inheritable version
        if res >= 0 and ftype != FILE_TYPE_CHAR:
            try:
                set_inheritable(fd2, False)
            except:
                os.close(fd2)
                raise
        return res
    
    GetConsoleMode = winexternal(
        'GetConsoleMode', [HANDLE, LPDWORD], BOOL)
        
    GetNumberOfConsoleInputEvents = winexternal(
        'GetNumberOfConsoleInputEvents', [HANDLE, LPDWORD], BOOL)

    ERROR_INSUFFICIENT_BUFFER = 122
    ERROR_OPERATION_ABORTED   = 995
    CP_UTF8 = 65001 
    
    ReadConsoleW = winexternal(
        'ReadConsoleW', [HANDLE, LPWSTR, DWORD, LPDWORD, LPVOID], BOOL,
        save_err=rffi.RFFI_SAVE_LASTERROR)
        
    WriteConsoleW = winexternal(
        'WriteConsoleW', [HANDLE, LPVOID, DWORD, LPDWORD, LPVOID], BOOL,
        save_err=rffi.RFFI_SAVE_LASTERROR)

    GetStringTypeW = winexternal(
        'GetStringTypeW', [DWORD, rffi.CWCHARP, rffi.INT, LPWORD], BOOL, 
        save_err=rffi.RFFI_SAVE_LASTERROR)

    _SetEnvironmentVariableW = winexternal(
        'SetEnvironmentVariableW', [LPWSTR, LPWSTR], BOOL,
        save_err=rffi.RFFI_SAVE_LASTERROR)

    def SetEnvironmentVariableW(name, value):
        with rffi.scoped_unicode2wcharp(name) as nameWbuf:
            with rffi.scoped_unicode2wcharp(value) as valueWbuf:
                return _SetEnvironmentVariableW(nameWbuf, valueWbuf) 
