import os
import sys
import errno
from rpython.annotator.model import s_Str0
from rpython.rtyper.lltypesystem.rffi import CConstant, CExternVariable, INT
from rpython.rtyper.lltypesystem import lltype, ll2ctypes, rffi
from rpython.rtyper.tool import rffi_platform
from rpython.rlib import debug, jit, rstring, rthread, types
from rpython.rlib._os_support import (
    _CYGWIN, _MACRO_ON_POSIX, UNDERSCORE_ON_WIN32, _WIN32,
    _prefer_unicode, _preferred_traits)
from rpython.rlib.objectmodel import (
    specialize, enforceargs, register_replacement_for, NOT_CONSTANT)
from rpython.rlib.rarithmetic import intmask, widen
from rpython.rlib.signature import signature
from rpython.tool.sourcetools import func_renamer
from rpython.translator.platform import platform
from rpython.translator.tool.cbuild import ExternalCompilationInfo


if _WIN32:
    from rpython.rlib import rwin32
    from rpython.rlib.rwin32file import make_win32_traits

class CConstantErrno(CConstant):
    # these accessors are used when calling get_errno() or set_errno()
    # on top of CPython
    def __getitem__(self, index):
        assert index == 0
        try:
            return ll2ctypes.TLS.errno
        except AttributeError:
            raise ValueError("no C function call occurred so far, "
                             "errno is undefined")
    def __setitem__(self, index, value):
        assert index == 0
        ll2ctypes.TLS.errno = value

if os.name == 'nt':
    if platform.name == 'msvc':
        includes=['errno.h','stdio.h']
    else:
        includes=['errno.h','stdio.h', 'stdint.h']
    separate_module_sources =['''
        /* Lifted completely from CPython 3.3 Modules/posix_module.c */
        #include <malloc.h> /* for _msize */
        typedef struct {
            intptr_t osfhnd;
            char osfile;
        } my_ioinfo;
        extern __declspec(dllimport) char * __pioinfo[];
        #define IOINFO_L2E 5
        #define IOINFO_ARRAY_ELTS   (1 << IOINFO_L2E)
        #define IOINFO_ARRAYS 64
        #define _NHANDLE_           (IOINFO_ARRAYS * IOINFO_ARRAY_ELTS)
        #define FOPEN 0x01
        #define _NO_CONSOLE_FILENO (intptr_t)-2

        /* This function emulates what the windows CRT
            does to validate file handles */
        RPY_EXTERN int
        _PyVerify_fd(int fd)
        {
            const int i1 = fd >> IOINFO_L2E;
            const int i2 = fd & ((1 << IOINFO_L2E) - 1);

            static size_t sizeof_ioinfo = 0;

            /* Determine the actual size of the ioinfo structure,
             * as used by the CRT loaded in memory
             */
            if (sizeof_ioinfo == 0 && __pioinfo[0] != NULL) {
                sizeof_ioinfo = _msize(__pioinfo[0]) / IOINFO_ARRAY_ELTS;
            }
            if (sizeof_ioinfo == 0) {
                /* This should not happen... */
                goto fail;
            }

            /* See that it isn't a special CLEAR fileno */
                if (fd != _NO_CONSOLE_FILENO) {
                /* Microsoft CRT would check that 0<=fd<_nhandle but we can't do that.  Instead
                 * we check pointer validity and other info
                 */
                if (0 <= i1 && i1 < IOINFO_ARRAYS && __pioinfo[i1] != NULL) {
                    /* finally, check that the file is open */
                    my_ioinfo* info = (my_ioinfo*)(__pioinfo[i1] + i2 * sizeof_ioinfo);
                    if (info->osfile & FOPEN) {
                        return 1;
                    }
                }
            }
          fail:
            errno = EBADF;
            return 0;
        }
    ''',]
else:
    separate_module_sources = []
    includes=['errno.h','stdio.h']
errno_eci = ExternalCompilationInfo(
    includes=includes,
    separate_module_sources=separate_module_sources,
)

# Direct getters/setters, don't use directly!
_get_errno, _set_errno = CExternVariable(INT, 'errno', errno_eci,
                                         CConstantErrno, sandboxsafe=True,
                                         _nowrapper=True, c_type='int')

def get_saved_errno():
    """Return the value of the "saved errno".
    This value is saved after a call to a C function, if it was declared
    with the flag llexternal(..., save_err=rffi.RFFI_SAVE_ERRNO).
    Functions without that flag don't change the saved errno.
    """
    return intmask(rthread.tlfield_rpy_errno.getraw())

def set_saved_errno(errno):
    """Set the value of the saved errno.  This value will be used to
    initialize the real errno just before calling the following C function,
    provided it was declared llexternal(..., save_err=RFFI_READSAVED_ERRNO).
    Note also that it is more common to want the real errno to be initially
    zero; for that case, use llexternal(..., save_err=RFFI_ZERO_ERRNO_BEFORE)
    and then you don't need set_saved_errno(0).
    """
    rthread.tlfield_rpy_errno.setraw(rffi.cast(INT, errno))

def get_saved_alterrno():
    """Return the value of the "saved alterrno".
    This value is saved after a call to a C function, if it was declared
    with the flag llexternal(..., save_err=rffi.RFFI_SAVE_ERRNO | rffl.RFFI_ALT_ERRNO).
    Functions without that flag don't change the saved errno.
    """
    return intmask(rthread.tlfield_alt_errno.getraw())

def set_saved_alterrno(errno):
    """Set the value of the saved alterrno.  This value will be used to
    initialize the real errno just before calling the following C function,
    provided it was declared llexternal(..., save_err=RFFI_READSAVED_ERRNO | rffl.RFFI_ALT_ERRNO).
    Note also that it is more common to want the real errno to be initially
    zero; for that case, use llexternal(..., save_err=RFFI_ZERO_ERRNO_BEFORE)
    and then you don't need set_saved_errno(0).
    """
    rthread.tlfield_alt_errno.setraw(rffi.cast(INT, errno))


# These are not posix specific, but where should they move to?
@specialize.call_location()
def _errno_before(save_err):
    if save_err & rffi.RFFI_READSAVED_ERRNO:
        if save_err & rffi.RFFI_ALT_ERRNO:
            _set_errno(rthread.tlfield_alt_errno.getraw())
        else:
            _set_errno(rthread.tlfield_rpy_errno.getraw())
    elif save_err & rffi.RFFI_ZERO_ERRNO_BEFORE:
        _set_errno(rffi.cast(rffi.INT, 0))
    if _WIN32 and (save_err & rffi.RFFI_READSAVED_LASTERROR):
        if save_err & rffi.RFFI_ALT_ERRNO:
            err = rthread.tlfield_alt_lasterror.getraw()
        else:
            err = rthread.tlfield_rpy_lasterror.getraw()
        # careful, getraw() overwrites GetLastError.
        # We must assign it with _SetLastError() as the last
        # operation, i.e. after the errno handling.
        rwin32._SetLastError(err)

@specialize.call_location()
def _errno_after(save_err):
    if _WIN32:
        if save_err & rffi.RFFI_SAVE_LASTERROR:
            err = rwin32._GetLastError()
            # careful, setraw() overwrites GetLastError.
            # We must read it first, before the errno handling.
            if save_err & rffi.RFFI_ALT_ERRNO:
                rthread.tlfield_alt_lasterror.setraw(err)
            else:
                rthread.tlfield_rpy_lasterror.setraw(err)
        elif save_err & rffi.RFFI_SAVE_WSALASTERROR:
            from rpython.rlib import _rsocket_rffi
            err = _rsocket_rffi._WSAGetLastError()
            if save_err & rffi.RFFI_ALT_ERRNO:
                rthread.tlfield_alt_lasterror.setraw(err)
            else:
                rthread.tlfield_rpy_lasterror.setraw(err)
    if save_err & rffi.RFFI_SAVE_ERRNO:
        if save_err & rffi.RFFI_ALT_ERRNO:
            rthread.tlfield_alt_errno.setraw(_get_errno())
        else:
            rthread.tlfield_rpy_errno.setraw(_get_errno())


if os.name == 'nt':
    is_valid_fd = jit.dont_look_inside(rffi.llexternal(
        "_PyVerify_fd", [rffi.INT], rffi.INT,
        compilation_info=errno_eci,
        ))
    @enforceargs(int)
    def validate_fd(fd):
        if not is_valid_fd(fd):
            from errno import EBADF
            raise OSError(EBADF, 'Bad file descriptor')
else:
    def is_valid_fd(fd):
        return 1

    @enforceargs(int)
    def validate_fd(fd):
        pass

def closerange(fd_low, fd_high):
    # this behaves like os.closerange() from Python 2.6.
    for fd in xrange(fd_low, fd_high):
        try:
            if is_valid_fd(fd):
                os.close(fd)
        except OSError:
            pass

if _WIN32:
    includes = ['io.h', 'sys/utime.h', 'sys/types.h']
    libraries = []
else:
    if sys.platform.startswith(('darwin', 'netbsd', 'openbsd')):
        _ptyh = 'util.h'
    elif sys.platform.startswith('freebsd'):
        _ptyh = 'libutil.h'
    else:
        _ptyh = 'pty.h'
    includes = ['unistd.h',  'sys/types.h', 'sys/wait.h',
                'utime.h', 'sys/time.h', 'sys/times.h',
                'grp.h', 'dirent.h', 'sys/stat.h', 'fcntl.h',
                'signal.h', 'sys/utsname.h', _ptyh]
    libraries = ['util']
eci = ExternalCompilationInfo(
    includes=includes,
    libraries=libraries,
)

class CConfig:
    _compilation_info_ = eci
    SEEK_SET = rffi_platform.DefinedConstantInteger('SEEK_SET')
    SEEK_CUR = rffi_platform.DefinedConstantInteger('SEEK_CUR')
    SEEK_END = rffi_platform.DefinedConstantInteger('SEEK_END')
    OFF_T_SIZE = rffi_platform.SizeOf('off_t')

    HAVE_UTIMES = rffi_platform.Has('utimes')
    UTIMBUF = rffi_platform.Struct('struct %sutimbuf' % UNDERSCORE_ON_WIN32,
                                   [('actime', rffi.INT),
                                    ('modtime', rffi.INT)])
    if not _WIN32:
        CLOCK_T = rffi_platform.SimpleType('clock_t', rffi.INT)

        TMS = rffi_platform.Struct(
            'struct tms', [('tms_utime', rffi.INT),
                           ('tms_stime', rffi.INT),
                           ('tms_cutime', rffi.INT),
                           ('tms_cstime', rffi.INT)])

    GETPGRP_HAVE_ARG = rffi_platform.Has("getpgrp(0)")
    SETPGRP_HAVE_ARG = rffi_platform.Has("setpgrp(0, 0)")

config = rffi_platform.configure(CConfig)
globals().update(config)

def external(name, args, result, compilation_info=eci, **kwds):
    return rffi.llexternal(name, args, result,
                           compilation_info=compilation_info, **kwds)

# For now we require off_t to be the same size as LONGLONG, which is the
# interface required by callers of functions that thake an argument of type
# off_t.
if not _WIN32:
    assert OFF_T_SIZE == rffi.sizeof(rffi.LONGLONG)

c_dup = external(UNDERSCORE_ON_WIN32 + 'dup', [rffi.INT], rffi.INT,
                 save_err=rffi.RFFI_SAVE_ERRNO)
c_dup2 = external(UNDERSCORE_ON_WIN32 + 'dup2', [rffi.INT, rffi.INT], rffi.INT,
                  save_err=rffi.RFFI_SAVE_ERRNO)
c_open = external(UNDERSCORE_ON_WIN32 + 'open',
                  [rffi.CCHARP, rffi.INT, rffi.MODE_T], rffi.INT,
                  save_err=rffi.RFFI_SAVE_ERRNO)

# Win32 Unicode functions
c_wopen = external(UNDERSCORE_ON_WIN32 + 'wopen',
                   [rffi.CWCHARP, rffi.INT, rffi.MODE_T], rffi.INT,
                   save_err=rffi.RFFI_SAVE_ERRNO)

#___________________________________________________________________
# Wrappers around posix functions, that accept either strings, or
# instances with a "as_bytes()" method.
# - pypy.modules.posix.interp_posix passes an object containing a unicode path
#   which can encode itself with sys.filesystemencoding.
# - but rpython.rtyper.module.ll_os.py on Windows will replace these functions
#   with other wrappers that directly handle unicode strings.
@specialize.argtype(0)
@signature(types.any(), returns=s_Str0)
def _as_bytes(path):
    assert path is not None
    if isinstance(path, str):
        return path
    elif isinstance(path, unicode):
        # This never happens in PyPy's Python interpreter!
        # Only in raw RPython code that uses unicode strings.
        # We implement python2 behavior: silently convert to ascii.
        return path.encode('ascii')
    else:
        return path.as_bytes()

@specialize.argtype(0)
def _as_bytes0(path):
    """Crashes translation if the path contains NUL characters."""
    res = _as_bytes(path)
    rstring.check_str0(res)
    return res

@specialize.argtype(0)
def _as_unicode(path):
    assert path is not None
    if isinstance(path, unicode):
        return path
    else:
        return path.as_unicode()

@specialize.argtype(0)
def _as_unicode0(path):
    """Crashes translation if the path contains NUL characters."""
    res = _as_unicode(path)
    rstring.check_str0(res)
    return res

@specialize.argtype(0, 1)
def putenv(name, value):
    os.environ[_as_bytes(name)] = _as_bytes(value)

@specialize.argtype(0)
def unsetenv(name):
    del os.environ[_as_bytes(name)]

#___________________________________________________________________
# Implementation of many posix functions.
# They usually check the return value and raise an (RPython) OSError
# with errno.

def replace_os_function(name):
    func = getattr(os, name, None)
    if func is None:
        return lambda f: f
    return register_replacement_for(
        func,
        sandboxed_name='ll_os.ll_os_%s' % name)

@specialize.arg(0)
def handle_posix_error(name, result):
    result = widen(result)
    if result < 0:
        raise OSError(get_saved_errno(), '%s failed' % name)
    return result

@replace_os_function('dup')
def dup(fd):
    validate_fd(fd)
    return handle_posix_error('dup', c_dup(fd))

@replace_os_function('dup2')
def dup2(fd, newfd):
    validate_fd(fd)
    handle_posix_error('dup2', c_dup2(fd, newfd))

#___________________________________________________________________

@replace_os_function('open')
@specialize.argtype(0)
@enforceargs(NOT_CONSTANT, int, int, typecheck=False)
def open(path, flags, mode):
    if _prefer_unicode(path):
        fd = c_wopen(_as_unicode0(path), flags, mode)
    else:
        fd = c_open(_as_bytes0(path), flags, mode)
    return handle_posix_error('open', fd)

c_read = external(UNDERSCORE_ON_WIN32 + 'read',
                  [rffi.INT, rffi.VOIDP, rffi.SIZE_T], rffi.SSIZE_T,
                  save_err=rffi.RFFI_SAVE_ERRNO)
c_write = external(UNDERSCORE_ON_WIN32 + 'write',
                   [rffi.INT, rffi.VOIDP, rffi.SIZE_T], rffi.SSIZE_T,
                   save_err=rffi.RFFI_SAVE_ERRNO)
c_close = external(UNDERSCORE_ON_WIN32 + 'close', [rffi.INT], rffi.INT,
                   releasegil=False, save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('read')
@enforceargs(int, int)
def read(fd, count):
    if count < 0:
        raise OSError(errno.EINVAL, None)
    validate_fd(fd)
    with rffi.scoped_alloc_buffer(count) as buf:
        void_buf = rffi.cast(rffi.VOIDP, buf.raw)
        got = handle_posix_error('read', c_read(fd, void_buf, count))
        return buf.str(got)

@replace_os_function('write')
@enforceargs(int, None)
def write(fd, data):
    count = len(data)
    validate_fd(fd)
    with rffi.scoped_nonmovingbuffer(data) as buf:
        return handle_posix_error('write', c_write(fd, buf, count))

@replace_os_function('close')
def close(fd):
    validate_fd(fd)
    handle_posix_error('close', c_close(fd))

c_lseek = external('_lseeki64' if _WIN32 else 'lseek',
                   [rffi.INT, rffi.LONGLONG, rffi.INT], rffi.LONGLONG,
                   macro=_MACRO_ON_POSIX, save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('lseek')
def lseek(fd, pos, how):
    validate_fd(fd)
    if SEEK_SET is not None:
        if how == 0:
            how = SEEK_SET
        elif how == 1:
            how = SEEK_CUR
        elif how == 2:
            how = SEEK_END
    return handle_posix_error('lseek', c_lseek(fd, pos, how))

c_ftruncate = external('ftruncate', [rffi.INT, rffi.LONGLONG], rffi.INT,
                       macro=_MACRO_ON_POSIX, save_err=rffi.RFFI_SAVE_ERRNO)
c_fsync = external('fsync' if not _WIN32 else '_commit', [rffi.INT], rffi.INT,
                   save_err=rffi.RFFI_SAVE_ERRNO)
c_fdatasync = external('fdatasync', [rffi.INT], rffi.INT,
                       save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('ftruncate')
def ftruncate(fd, length):
    validate_fd(fd)
    handle_posix_error('ftruncate', c_ftruncate(fd, length))

@replace_os_function('fsync')
def fsync(fd):
    validate_fd(fd)
    handle_posix_error('fsync', c_fsync(fd))

@replace_os_function('fdatasync')
def fdatasync(fd):
    validate_fd(fd)
    handle_posix_error('fdatasync', c_fdatasync(fd))

#___________________________________________________________________

c_chdir = external('chdir', [rffi.CCHARP], rffi.INT,
                   save_err=rffi.RFFI_SAVE_ERRNO)
c_fchdir = external('fchdir', [rffi.INT], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)
c_access = external(UNDERSCORE_ON_WIN32 + 'access',
                    [rffi.CCHARP, rffi.INT], rffi.INT)
c_waccess = external(UNDERSCORE_ON_WIN32 + 'waccess',
                     [rffi.CWCHARP, rffi.INT], rffi.INT)

@replace_os_function('chdir')
@specialize.argtype(0)
def chdir(path):
    if not _WIN32:
        handle_posix_error('chdir', c_chdir(_as_bytes0(path)))
    else:
        traits = _preferred_traits(path)
        win32traits = make_win32_traits(traits)
        path = traits.as_str0(path)

        # This is a reimplementation of the C library's chdir
        # function, but one that produces Win32 errors instead of DOS
        # error codes.
        # chdir is essentially a wrapper around SetCurrentDirectory;
        # however, it also needs to set "magic" environment variables
        # indicating the per-drive current directory, which are of the
        # form =<drive>:
        if not win32traits.SetCurrentDirectory(path):
            raise rwin32.lastSavedWindowsError()
        MAX_PATH = rwin32.MAX_PATH
        assert MAX_PATH > 0

        with traits.scoped_alloc_buffer(MAX_PATH) as path:
            res = win32traits.GetCurrentDirectory(MAX_PATH + 1, path.raw)
            if not res:
                raise rwin32.lastSavedWindowsError()
            res = rffi.cast(lltype.Signed, res)
            assert res > 0
            if res <= MAX_PATH + 1:
                new_path = path.str(res)
            else:
                with traits.scoped_alloc_buffer(res) as path:
                    res = win32traits.GetCurrentDirectory(res, path.raw)
                    if not res:
                        raise rwin32.lastSavedWindowsError()
                    res = rffi.cast(lltype.Signed, res)
                    assert res > 0
                    new_path = path.str(res)
        if traits.str is unicode:
            if new_path[0] == u'\\' or new_path[0] == u'/':  # UNC path
                return
            magic_envvar = u'=' + new_path[0] + u':'
        else:
            if new_path[0] == '\\' or new_path[0] == '/':  # UNC path
                return
            magic_envvar = '=' + new_path[0] + ':'
        if not win32traits.SetEnvironmentVariable(magic_envvar, new_path):
            raise rwin32.lastSavedWindowsError()

@replace_os_function('fchdir')
def fchdir(fd):
    validate_fd(fd)
    handle_posix_error('fchdir', c_fchdir(fd))

@replace_os_function('access')
@specialize.argtype(0)
def access(path, mode):
    if _WIN32:
        # All files are executable on Windows
        mode = mode & ~os.X_OK
    if _prefer_unicode(path):
        error = c_waccess(_as_unicode0(path), mode)
    else:
        error = c_access(_as_bytes0(path), mode)
    return error == 0

# This Win32 function is not exposed via os, but needed to get a
# correct implementation of os.path.abspath.
@specialize.argtype(0)
def getfullpathname(path):
    length = rwin32.MAX_PATH + 1
    traits = _preferred_traits(path)
    win32traits = make_win32_traits(traits)
    with traits.scoped_alloc_buffer(length) as buf:
        res = win32traits.GetFullPathName(
            traits.as_str0(path), rffi.cast(rwin32.DWORD, length),
            buf.raw, lltype.nullptr(win32traits.LPSTRP.TO))
        if res == 0:
            raise rwin32.lastSavedWindowsError("_getfullpathname failed")
        result = buf.str(intmask(res))
        assert result is not None
        result = rstring.assert_str0(result)
        return result

c_getcwd = external(UNDERSCORE_ON_WIN32 + 'getcwd',
                    [rffi.CCHARP, rffi.SIZE_T], rffi.CCHARP,
                    save_err=rffi.RFFI_SAVE_ERRNO)
c_wgetcwd = external(UNDERSCORE_ON_WIN32 + 'wgetcwd',
                     [rffi.CWCHARP, rffi.SIZE_T], rffi.CWCHARP,
                     save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('getcwd')
def getcwd():
    bufsize = 256
    while True:
        buf = lltype.malloc(rffi.CCHARP.TO, bufsize, flavor='raw')
        res = c_getcwd(buf, bufsize)
        if res:
            break   # ok
        error = get_saved_errno()
        lltype.free(buf, flavor='raw')
        if error != errno.ERANGE:
            raise OSError(error, "getcwd failed")
        # else try again with a larger buffer, up to some sane limit
        bufsize *= 4
        if bufsize > 1024*1024:  # xxx hard-coded upper limit
            raise OSError(error, "getcwd result too large")
    result = rffi.charp2str(res)
    lltype.free(buf, flavor='raw')
    return result

@replace_os_function('getcwdu')
def getcwdu():
    bufsize = 256
    while True:
        buf = lltype.malloc(rffi.CWCHARP.TO, bufsize, flavor='raw')
        res = c_wgetcwd(buf, bufsize)
        if res:
            break   # ok
        error = get_saved_errno()
        lltype.free(buf, flavor='raw')
        if error != errno.ERANGE:
            raise OSError(error, "getcwd failed")
        # else try again with a larger buffer, up to some sane limit
        bufsize *= 4
        if bufsize > 1024*1024:  # xxx hard-coded upper limit
            raise OSError(error, "getcwd result too large")
    result = rffi.wcharp2unicode(res)
    lltype.free(buf, flavor='raw')
    return result

if not _WIN32:
    class CConfig:
        _compilation_info_ = eci
        DIRENT = rffi_platform.Struct('struct dirent',
            [('d_name', lltype.FixedSizeArray(rffi.CHAR, 1))])

    DIRP = rffi.COpaquePtr('DIR')
    config = rffi_platform.configure(CConfig)
    DIRENT = config['DIRENT']
    DIRENTP = lltype.Ptr(DIRENT)
    c_opendir = external('opendir', [rffi.CCHARP], DIRP,
                         save_err=rffi.RFFI_SAVE_ERRNO)
    # XXX macro=True is hack to make sure we get the correct kind of
    # dirent struct (which depends on defines)
    c_readdir = external('readdir', [DIRP], DIRENTP,
                         macro=True, save_err=rffi.RFFI_FULL_ERRNO_ZERO)
    c_closedir = external('closedir', [DIRP], rffi.INT)

@replace_os_function('listdir')
@specialize.argtype(0)
def listdir(path):
    if not _WIN32:
        path = _as_bytes0(path)
        dirp = c_opendir(path)
        if not dirp:
            raise OSError(get_saved_errno(), "opendir failed")
        result = []
        while True:
            direntp = c_readdir(dirp)
            if not direntp:
                error = get_saved_errno()
                break
            namep = rffi.cast(rffi.CCHARP, direntp.c_d_name)
            name = rffi.charp2str(namep)
            if name != '.' and name != '..':
                result.append(name)
        c_closedir(dirp)
        if error:
            raise OSError(error, "readdir failed")
        return result
    else:  # _WIN32 case
        traits = _preferred_traits(path)
        win32traits = make_win32_traits(traits)
        path = traits.as_str0(path)

        if traits.str is unicode:
            if path and path[-1] not in (u'/', u'\\', u':'):
                path += u'/'
            mask = path + u'*.*'
        else:
            if path and path[-1] not in ('/', '\\', ':'):
                path += '/'
            mask = path + '*.*'

        filedata = lltype.malloc(win32traits.WIN32_FIND_DATA, flavor='raw')
        try:
            result = []
            hFindFile = win32traits.FindFirstFile(mask, filedata)
            if hFindFile == rwin32.INVALID_HANDLE_VALUE:
                error = rwin32.GetLastError_saved()
                if error == win32traits.ERROR_FILE_NOT_FOUND:
                    return result
                else:
                    raise WindowsError(error,  "FindFirstFile failed")
            while True:
                name = traits.charp2str(rffi.cast(traits.CCHARP,
                                                  filedata.c_cFileName))
                if traits.str is unicode:
                    if not (name == u"." or name == u".."):
                        result.append(name)
                else:
                    if not (name == "." or name == ".."):
                        result.append(name)
                if not win32traits.FindNextFile(hFindFile, filedata):
                    break
            # FindNextFile sets error to ERROR_NO_MORE_FILES if
            # it got to the end of the directory
            error = rwin32.GetLastError_saved()
            win32traits.FindClose(hFindFile)
            if error == win32traits.ERROR_NO_MORE_FILES:
                return result
            else:
                raise WindowsError(error,  "FindNextFile failed")
        finally:
            lltype.free(filedata, flavor='raw')

#___________________________________________________________________

c_execv = external('execv', [rffi.CCHARP, rffi.CCHARPP], rffi.INT,
                   save_err=rffi.RFFI_SAVE_ERRNO)
c_execve = external('execve',
                    [rffi.CCHARP, rffi.CCHARPP, rffi.CCHARPP], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)
c_spawnv = external('spawnv',
                    [rffi.INT, rffi.CCHARP, rffi.CCHARPP], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)
c_spawnve = external('spawnve',
                    [rffi.INT, rffi.CCHARP, rffi.CCHARPP, rffi.CCHARPP],
                     rffi.INT,
                     save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('execv')
def execv(path, args):
    rstring.check_str0(path)
    # This list conversion already takes care of NUL bytes.
    l_args = rffi.ll_liststr2charpp(args)
    c_execv(path, l_args)
    rffi.free_charpp(l_args)
    raise OSError(get_saved_errno(), "execv failed")

@replace_os_function('execve')
def execve(path, args, env):
    envstrs = []
    for item in env.iteritems():
        envstr = "%s=%s" % item
        envstrs.append(envstr)

    rstring.check_str0(path)
    # This list conversion already takes care of NUL bytes.
    l_args = rffi.ll_liststr2charpp(args)
    l_env = rffi.ll_liststr2charpp(envstrs)
    c_execve(path, l_args, l_env)

    rffi.free_charpp(l_env)
    rffi.free_charpp(l_args)
    raise OSError(get_saved_errno(), "execve failed")

@replace_os_function('spawnv')
def spawnv(mode, path, args):
    rstring.check_str0(path)
    l_args = rffi.ll_liststr2charpp(args)
    childpid = c_spawnv(mode, path, l_args)
    rffi.free_charpp(l_args)
    return handle_posix_error('spawnv', childpid)

@replace_os_function('spawnve')
def spawnve(mode, path, args, env):
    envstrs = []
    for item in env.iteritems():
        envstrs.append("%s=%s" % item)
    rstring.check_str0(path)
    l_args = rffi.ll_liststr2charpp(args)
    l_env = rffi.ll_liststr2charpp(envstrs)
    childpid = c_spawnve(mode, path, l_args, l_env)
    rffi.free_charpp(l_env)
    rffi.free_charpp(l_args)
    return handle_posix_error('spawnve', childpid)

c_fork = external('fork', [], rffi.PID_T, _nowrapper = True)
c_openpty = external('openpty',
                     [rffi.INTP, rffi.INTP, rffi.VOIDP, rffi.VOIDP, rffi.VOIDP],
                     rffi.INT,
                     save_err=rffi.RFFI_SAVE_ERRNO)
c_forkpty = external('forkpty',
                     [rffi.INTP, rffi.VOIDP, rffi.VOIDP, rffi.VOIDP],
                     rffi.PID_T,
                     save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('fork')
@jit.dont_look_inside
def fork():
    # NB. keep forkpty() up-to-date, too
    ofs = debug.debug_offset()
    opaqueaddr = rthread.gc_thread_before_fork()
    childpid = c_fork()
    rthread.gc_thread_after_fork(childpid, opaqueaddr)
    childpid = handle_posix_error('fork', childpid)
    if childpid == 0:
        debug.debug_forked(ofs)
    return childpid

@replace_os_function('openpty')
@jit.dont_look_inside
def openpty():
    master_p = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
    slave_p = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
    try:
        handle_posix_error(
            'openpty', c_openpty(master_p, slave_p, None, None, None))
        return (widen(master_p[0]), widen(slave_p[0]))
    finally:
        lltype.free(master_p, flavor='raw')
        lltype.free(slave_p, flavor='raw')

@replace_os_function('forkpty')
@jit.dont_look_inside
def forkpty():
    master_p = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
    master_p[0] = rffi.cast(rffi.INT, -1)
    try:
        ofs = debug.debug_offset()
        opaqueaddr = rthread.gc_thread_before_fork()
        childpid = c_forkpty(master_p, None, None, None)
        rthread.gc_thread_after_fork(childpid, opaqueaddr)
        childpid = handle_posix_error('forkpty', childpid)
        if childpid == 0:
            debug.debug_forked(ofs)
        return (childpid, master_p[0])
    finally:
        lltype.free(master_p, flavor='raw')

if _WIN32:
    # emulate waitpid() with the _cwait() of Microsoft's compiler
    c__cwait = external('_cwait',
                        [rffi.INTP, rffi.PID_T, rffi.INT], rffi.PID_T,
                        save_err=rffi.RFFI_SAVE_ERRNO)
    @jit.dont_look_inside
    def c_waitpid(pid, status_p, options):
        result = c__cwait(status_p, pid, options)
        # shift the status left a byte so this is more
        # like the POSIX waitpid
        status_p[0] = rffi.cast(rffi.INT, widen(status_p[0]) << 8)
        return result
elif _CYGWIN:
    c_waitpid = external('cygwin_waitpid',
                         [rffi.PID_T, rffi.INTP, rffi.INT], rffi.PID_T,
                         save_err=rffi.RFFI_SAVE_ERRNO)
else:
    c_waitpid = external('waitpid',
                         [rffi.PID_T, rffi.INTP, rffi.INT], rffi.PID_T,
                         save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('waitpid')
def waitpid(pid, options):
    status_p = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
    status_p[0] = rffi.cast(rffi.INT, 0)
    try:
        result = handle_posix_error('waitpid',
                                    c_waitpid(pid, status_p, options))
        status = widen(status_p[0])
        return (result, status)
    finally:
        lltype.free(status_p, flavor='raw')

def _make_waitmacro(name):
    c_func = external(name, [lltype.Signed], lltype.Signed,
                      macro=_MACRO_ON_POSIX)
    returning_int = name in ('WEXITSTATUS', 'WSTOPSIG', 'WTERMSIG')

    @replace_os_function(name)
    @func_renamer(name)
    def _waitmacro(status):
        if returning_int:
            return c_func(status)
        else:
            return bool(c_func(status))

WAIT_MACROS = ['WCOREDUMP', 'WIFCONTINUED', 'WIFSTOPPED',
               'WIFSIGNALED', 'WIFEXITED',
               'WEXITSTATUS', 'WSTOPSIG', 'WTERMSIG']
for name in WAIT_MACROS:
    _make_waitmacro(name)

#___________________________________________________________________

c_getlogin = external('getlogin', [], rffi.CCHARP,
                      releasegil=False, save_err=rffi.RFFI_SAVE_ERRNO)
c_getloadavg = external('getloadavg',
                        [rffi.CArrayPtr(lltype.Float), rffi.INT], rffi.INT)

@replace_os_function('getlogin')
def getlogin():
    result = c_getlogin()
    if not result:
        raise OSError(get_saved_errno(), "getlogin failed")
    return rffi.charp2str(result)

@replace_os_function('getloadavg')
def getloadavg():
    load = lltype.malloc(rffi.CArrayPtr(lltype.Float).TO, 3, flavor='raw')
    try:
        r = c_getloadavg(load, 3)
        if r != 3:
            raise OSError
        return (load[0], load[1], load[2])
    finally:
        lltype.free(load, flavor='raw')

#___________________________________________________________________

c_readlink = external('readlink',
                      [rffi.CCHARP, rffi.CCHARP, rffi.SIZE_T], rffi.SSIZE_T,
                      save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('readlink')
def readlink(path):
    path = _as_bytes0(path)
    bufsize = 1023
    while True:
        buf = lltype.malloc(rffi.CCHARP.TO, bufsize, flavor='raw')
        res = widen(c_readlink(path, buf, bufsize))
        if res < 0:
            lltype.free(buf, flavor='raw')
            error = get_saved_errno()    # failed
            raise OSError(error, "readlink failed")
        elif res < bufsize:
            break                       # ok
        else:
            # buf too small, try again with a larger buffer
            lltype.free(buf, flavor='raw')
            bufsize *= 4
    # convert the result to a string
    result = rffi.charp2strn(buf, res)
    lltype.free(buf, flavor='raw')
    return result

c_isatty = external(UNDERSCORE_ON_WIN32 + 'isatty', [rffi.INT], rffi.INT)

@replace_os_function('isatty')
def isatty(fd):
    if not is_valid_fd(fd):
        return False
    return c_isatty(fd) != 0

c_ttyname = external('ttyname', [lltype.Signed], rffi.CCHARP,
                     releasegil=False,
                     save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('ttyname')
def ttyname(fd):
    l_name = c_ttyname(fd)
    if not l_name:
        raise OSError(get_saved_errno(), "ttyname raised")
    return rffi.charp2str(l_name)

c_strerror = external('strerror', [rffi.INT], rffi.CCHARP,
                      releasegil=False)

@replace_os_function('strerror')
def strerror(errnum):
    res = c_strerror(errnum)
    if not res:
        raise ValueError("os_strerror failed")
    return rffi.charp2str(res)

c_system = external('system', [rffi.CCHARP], rffi.INT)

@replace_os_function('system')
def system(command):
    return widen(c_system(command))

c_unlink = external('unlink', [rffi.CCHARP], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)
c_mkdir = external('mkdir', [rffi.CCHARP, rffi.MODE_T], rffi.INT,
                   save_err=rffi.RFFI_SAVE_ERRNO)
c_rmdir = external(UNDERSCORE_ON_WIN32 + 'rmdir', [rffi.CCHARP], rffi.INT,
                   save_err=rffi.RFFI_SAVE_ERRNO)
c_wrmdir = external(UNDERSCORE_ON_WIN32 + 'wrmdir', [rffi.CWCHARP], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('unlink')
@specialize.argtype(0)
def unlink(path):
    if not _WIN32:
        handle_posix_error('unlink', c_unlink(_as_bytes0(path)))
    else:
        traits = _preferred_traits(path)
        win32traits = make_win32_traits(traits)
        if not win32traits.DeleteFile(traits.as_str0(path)):
            raise rwin32.lastSavedWindowsError()

@replace_os_function('mkdir')
@specialize.argtype(0)
def mkdir(path, mode=0o777):
    if not _WIN32:
        handle_posix_error('mkdir', c_mkdir(_as_bytes0(path), mode))
    else:
        traits = _preferred_traits(path)
        win32traits = make_win32_traits(traits)
        if not win32traits.CreateDirectory(traits.as_str0(path), None):
            raise rwin32.lastSavedWindowsError()

@replace_os_function('rmdir')
@specialize.argtype(0)
@jit.dont_look_inside
def rmdir(path):
    if _prefer_unicode(path):
        handle_posix_error('wrmdir', c_wrmdir(_as_unicode0(path)))
    else:
        handle_posix_error('rmdir', c_rmdir(_as_bytes0(path)))

c_chmod = external('chmod', [rffi.CCHARP, rffi.MODE_T], rffi.INT,
                   save_err=rffi.RFFI_SAVE_ERRNO)
c_fchmod = external('fchmod', [rffi.INT, rffi.MODE_T], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO,)
c_rename = external('rename', [rffi.CCHARP, rffi.CCHARP], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('chmod')
@specialize.argtype(0)
def chmod(path, mode):
    if not _WIN32:
        handle_posix_error('chmod', c_chmod(_as_bytes0(path), mode))
    else:
        traits = _preferred_traits(path)
        win32traits = make_win32_traits(traits)
        path = traits.as_str0(path)
        attr = win32traits.GetFileAttributes(path)
        if attr == win32traits.INVALID_FILE_ATTRIBUTES:
            raise rwin32.lastSavedWindowsError()
        if mode & 0200: # _S_IWRITE
            attr &= ~win32traits.FILE_ATTRIBUTE_READONLY
        else:
            attr |= win32traits.FILE_ATTRIBUTE_READONLY
        if not win32traits.SetFileAttributes(path, attr):
            raise rwin32.lastSavedWindowsError()

@replace_os_function('fchmod')
def fchmod(fd, mode):
    handle_posix_error('fchmod', c_fchmod(fd, mode))

@replace_os_function('rename')
@specialize.argtype(0, 1)
def rename(path1, path2):
    if not _WIN32:
        handle_posix_error('rename',
                           c_rename(_as_bytes0(path1), _as_bytes0(path2)))
    else:
        traits = _preferred_traits(path1)
        win32traits = make_win32_traits(traits)
        path1 = traits.as_str0(path1)
        path2 = traits.as_str0(path2)
        if not win32traits.MoveFile(path1, path2):
            raise rwin32.lastSavedWindowsError()

#___________________________________________________________________

c_mkfifo = external('mkfifo', [rffi.CCHARP, rffi.MODE_T], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)
c_mknod = external('mknod', [rffi.CCHARP, rffi.MODE_T, rffi.INT], rffi.INT,
#                                           # xxx: actually ^^^ dev_t
                   macro=_MACRO_ON_POSIX, save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('mkfifo')
@specialize.argtype(0)
def mkfifo(path, mode):
    handle_posix_error('mkfifo', c_mkfifo(_as_bytes0(path), mode))

@replace_os_function('mknod')
@specialize.argtype(0)
def mknod(path, mode, dev):
    handle_posix_error('mknod', c_mknod(_as_bytes0(path), mode, dev))

if _WIN32:
    CreatePipe = external('CreatePipe', [rwin32.LPHANDLE,
                                         rwin32.LPHANDLE,
                                         rffi.VOIDP,
                                         rwin32.DWORD],
                          rwin32.BOOL)
    c_open_osfhandle = external('_open_osfhandle', [rffi.INTPTR_T,
                                                    rffi.INT],
                                rffi.INT)
else:
    INT_ARRAY_P = rffi.CArrayPtr(rffi.INT)
    c_pipe = external('pipe', [INT_ARRAY_P], rffi.INT,
                      save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('pipe')
def pipe():
    if _WIN32:
        pread  = lltype.malloc(rwin32.LPHANDLE.TO, 1, flavor='raw')
        pwrite = lltype.malloc(rwin32.LPHANDLE.TO, 1, flavor='raw')
        try:
            if not CreatePipe(
                    pread, pwrite, lltype.nullptr(rffi.VOIDP.TO), 0):
                raise WindowsError(rwin32.GetLastError_saved(),
                                   "CreatePipe failed")
            hread = rffi.cast(rffi.INTPTR_T, pread[0])
            hwrite = rffi.cast(rffi.INTPTR_T, pwrite[0])
        finally:
            lltype.free(pwrite, flavor='raw')
            lltype.free(pread, flavor='raw')
        fdread = c_open_osfhandle(hread, 0)
        fdwrite = c_open_osfhandle(hwrite, 1)
        return (fdread, fdwrite)
    else:
        filedes = lltype.malloc(INT_ARRAY_P.TO, 2, flavor='raw')
        try:
            handle_posix_error('pipe', c_pipe(filedes))
            return (widen(filedes[0]), widen(filedes[1]))
        finally:
            lltype.free(filedes, flavor='raw')

c_link = external('link', [rffi.CCHARP, rffi.CCHARP], rffi.INT,
                  save_err=rffi.RFFI_SAVE_ERRNO,)
c_symlink = external('symlink', [rffi.CCHARP, rffi.CCHARP], rffi.INT,
                     save_err=rffi.RFFI_SAVE_ERRNO)

#___________________________________________________________________

@replace_os_function('link')
@specialize.argtype(0, 1)
def link(oldpath, newpath):
    oldpath = _as_bytes0(oldpath)
    newpath = _as_bytes0(newpath)
    handle_posix_error('link', c_link(oldpath, newpath))

@replace_os_function('symlink')
@specialize.argtype(0, 1)
def symlink(oldpath, newpath):
    oldpath = _as_bytes0(oldpath)
    newpath = _as_bytes0(newpath)
    handle_posix_error('symlink', c_symlink(oldpath, newpath))

c_umask = external(UNDERSCORE_ON_WIN32 + 'umask', [rffi.MODE_T], rffi.MODE_T)

@replace_os_function('umask')
def umask(newmask):
    return widen(c_umask(newmask))

c_chown = external('chown', [rffi.CCHARP, rffi.INT, rffi.INT], rffi.INT,
                   save_err=rffi.RFFI_SAVE_ERRNO)
c_lchown = external('lchown', [rffi.CCHARP, rffi.INT, rffi.INT], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)
c_fchown = external('fchown', [rffi.INT, rffi.INT, rffi.INT], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('chown')
def chown(path, uid, gid):
    handle_posix_error('chown', c_chown(path, uid, gid))

@replace_os_function('lchown')
def lchown(path, uid, gid):
    handle_posix_error('lchown', c_lchown(path, uid, gid))

@replace_os_function('fchown')
def fchown(fd, uid, gid):
    handle_posix_error('fchown', c_fchown(fd, uid, gid))

#___________________________________________________________________

UTIMBUFP = lltype.Ptr(UTIMBUF)
c_utime = external('utime', [rffi.CCHARP, UTIMBUFP], rffi.INT,
                   save_err=rffi.RFFI_SAVE_ERRNO)
if HAVE_UTIMES:
    class CConfig:
        _compilation_info_ = eci
        TIMEVAL = rffi_platform.Struct('struct timeval', [
            ('tv_sec', rffi.LONG),
            ('tv_usec', rffi.LONG)])
    config = rffi_platform.configure(CConfig)
    TIMEVAL = config['TIMEVAL']
    TIMEVAL2P = rffi.CArrayPtr(TIMEVAL)
    c_utimes = external('utimes', [rffi.CCHARP, TIMEVAL2P], rffi.INT,
                        save_err=rffi.RFFI_SAVE_ERRNO)

if _WIN32:
    from rpython.rlib import rwin32
    GetSystemTime = external(
        'GetSystemTime',
        [lltype.Ptr(rwin32.SYSTEMTIME)],
        lltype.Void,
        calling_conv='win',
        save_err=rffi.RFFI_SAVE_LASTERROR)

    SystemTimeToFileTime = external(
        'SystemTimeToFileTime',
        [lltype.Ptr(rwin32.SYSTEMTIME),
         lltype.Ptr(rwin32.FILETIME)],
        rwin32.BOOL,
        calling_conv='win',
        save_err=rffi.RFFI_SAVE_LASTERROR)

    SetFileTime = external(
        'SetFileTime',
        [rwin32.HANDLE,
         lltype.Ptr(rwin32.FILETIME),
         lltype.Ptr(rwin32.FILETIME),
         lltype.Ptr(rwin32.FILETIME)],
        rwin32.BOOL,
        calling_conv='win')


@replace_os_function('utime')
@specialize.argtype(0, 1)
def utime(path, times):
    if not _WIN32:
        path = _as_bytes0(path)
        if times is None:
            error = c_utime(path, lltype.nullptr(UTIMBUFP.TO))
        else:
            actime, modtime = times
            if HAVE_UTIMES:
                import math
                l_times = lltype.malloc(TIMEVAL2P.TO, 2, flavor='raw')
                fracpart, intpart = math.modf(actime)
                rffi.setintfield(l_times[0], 'c_tv_sec', int(intpart))
                rffi.setintfield(l_times[0], 'c_tv_usec', int(fracpart * 1e6))
                fracpart, intpart = math.modf(modtime)
                rffi.setintfield(l_times[1], 'c_tv_sec', int(intpart))
                rffi.setintfield(l_times[1], 'c_tv_usec', int(fracpart * 1e6))
                error = c_utimes(path, l_times)
                lltype.free(l_times, flavor='raw')
            else:
                # we only have utime(), which does not allow
                # sub-second resolution
                l_utimbuf = lltype.malloc(UTIMBUFP.TO, flavor='raw')
                l_utimbuf.c_actime  = rffi.r_time_t(actime)
                l_utimbuf.c_modtime = rffi.r_time_t(modtime)
                error = c_utime(path, l_utimbuf)
                lltype.free(l_utimbuf, flavor='raw')
        handle_posix_error('utime', error)
    else:  # _WIN32 case
        from rpython.rlib.rwin32file import time_t_to_FILE_TIME
        traits = _preferred_traits(path)
        win32traits = make_win32_traits(traits)
        path = traits.as_str0(path)
        hFile = win32traits.CreateFile(path,
                           win32traits.FILE_WRITE_ATTRIBUTES, 0,
                           None, win32traits.OPEN_EXISTING,
                           win32traits.FILE_FLAG_BACKUP_SEMANTICS,
                           rwin32.NULL_HANDLE)
        if hFile == rwin32.INVALID_HANDLE_VALUE:
            raise rwin32.lastSavedWindowsError()
        ctime = lltype.nullptr(rwin32.FILETIME)
        atime = lltype.malloc(rwin32.FILETIME, flavor='raw')
        mtime = lltype.malloc(rwin32.FILETIME, flavor='raw')
        try:
            if times is None:
                now = lltype.malloc(rwin32.SYSTEMTIME, flavor='raw')
                try:
                    GetSystemTime(now)
                    if (not SystemTimeToFileTime(now, atime) or
                        not SystemTimeToFileTime(now, mtime)):
                        raise rwin32.lastSavedWindowsError()
                finally:
                    lltype.free(now, flavor='raw')
            else:
                actime, modtime = times
                time_t_to_FILE_TIME(actime, atime)
                time_t_to_FILE_TIME(modtime, mtime)
            if not SetFileTime(hFile, ctime, atime, mtime):
                raise rwin32.lastSavedWindowsError()
        finally:
            rwin32.CloseHandle(hFile)
            lltype.free(atime, flavor='raw')
            lltype.free(mtime, flavor='raw')

if not _WIN32:
    TMSP = lltype.Ptr(TMS)
    c_times = external('times', [TMSP], CLOCK_T,
                        save_err=rffi.RFFI_SAVE_ERRNO |
                                 rffi.RFFI_ZERO_ERRNO_BEFORE)

    # Here is a random extra platform parameter which is important.
    # Strictly speaking, this should probably be retrieved at runtime, not
    # at translation time.
    CLOCK_TICKS_PER_SECOND = float(os.sysconf('SC_CLK_TCK'))
else:
    GetCurrentProcess = external(
        'GetCurrentProcess', [],
        rwin32.HANDLE, calling_conv='win')
    GetProcessTimes = external(
        'GetProcessTimes', [
            rwin32.HANDLE,
            lltype.Ptr(rwin32.FILETIME), lltype.Ptr(rwin32.FILETIME),
            lltype.Ptr(rwin32.FILETIME), lltype.Ptr(rwin32.FILETIME)],
        rwin32.BOOL, calling_conv='win')

@replace_os_function('times')
def times():
    if not _WIN32:
        l_tmsbuf = lltype.malloc(TMSP.TO, flavor='raw')
        try:
            # note: times() can return a negative value (or even -1)
            # even if there is no error
            result = rffi.cast(lltype.Signed, c_times(l_tmsbuf))
            if result == -1:
                errno = get_saved_errno()
                if errno != 0:
                    raise OSError(errno, 'times() failed')
            return (
                rffi.cast(lltype.Signed, l_tmsbuf.c_tms_utime)
                                               / CLOCK_TICKS_PER_SECOND,
                rffi.cast(lltype.Signed, l_tmsbuf.c_tms_stime)
                                               / CLOCK_TICKS_PER_SECOND,
                rffi.cast(lltype.Signed, l_tmsbuf.c_tms_cutime)
                                               / CLOCK_TICKS_PER_SECOND,
                rffi.cast(lltype.Signed, l_tmsbuf.c_tms_cstime)
                                               / CLOCK_TICKS_PER_SECOND,
                result / CLOCK_TICKS_PER_SECOND)
        finally:
            lltype.free(l_tmsbuf, flavor='raw')
    else:
        pcreate = lltype.malloc(rwin32.FILETIME, flavor='raw')
        pexit   = lltype.malloc(rwin32.FILETIME, flavor='raw')
        pkernel = lltype.malloc(rwin32.FILETIME, flavor='raw')
        puser   = lltype.malloc(rwin32.FILETIME, flavor='raw')
        try:
            hProc = GetCurrentProcess()
            GetProcessTimes(hProc, pcreate, pexit, pkernel, puser)
            # The fields of a FILETIME structure are the hi and lo parts
            # of a 64-bit value expressed in 100 nanosecond units
            # (of course).
            return (
                rffi.cast(lltype.Signed, pkernel.c_dwHighDateTime) * 429.4967296 +
                rffi.cast(lltype.Signed, pkernel.c_dwLowDateTime) * 1E-7,
                rffi.cast(lltype.Signed, puser.c_dwHighDateTime) * 429.4967296 +
                rffi.cast(lltype.Signed, puser.c_dwLowDateTime) * 1E-7,
                0., 0., 0.)
        finally:
            lltype.free(puser,   flavor='raw')
            lltype.free(pkernel, flavor='raw')
            lltype.free(pexit,   flavor='raw')
            lltype.free(pcreate, flavor='raw')

c_kill = external('kill', [rffi.PID_T, rffi.INT], rffi.INT,
                  save_err=rffi.RFFI_SAVE_ERRNO)
c_killpg = external('killpg', [rffi.INT, rffi.INT], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)
c_exit = external('_exit', [rffi.INT], lltype.Void)
c_nice = external('nice', [rffi.INT], rffi.INT,
                  save_err=rffi.RFFI_FULL_ERRNO_ZERO)

@replace_os_function('kill')
def kill(pid, sig):
    if not _WIN32:
        return handle_posix_error('kill', c_kill(pid, sig))
    else:
        if sig == rwin32.CTRL_C_EVENT or sig == rwin32.CTRL_BREAK_EVENT:
            if rwin32.GenerateConsoleCtrlEvent(sig, pid) == 0:
                raise rwin32.lastSavedWindowsError(
                    'kill() failed generating event')
            return
        handle = rwin32.OpenProcess(rwin32.PROCESS_ALL_ACCESS, False, pid)
        if not handle:
            raise rwin32.lastSavedWindowsError('kill() failed opening process')
        try:
            if rwin32.TerminateProcess(handle, sig) == 0:
                raise rwin32.lastSavedWindowsError(
                    'kill() failed to terminate process')
        finally:
            rwin32.CloseHandle(handle)

@replace_os_function('killpg')
def killpg(pgrp, sig):
    return handle_posix_error('killpg', c_killpg(pgrp, sig))

@replace_os_function('_exit')
@jit.dont_look_inside
def exit(status):
    debug.debug_flush()
    c_exit(status)

@replace_os_function('nice')
def nice(inc):
    # Assume that the system provides a standard-compliant version
    # of nice() that returns the new priority.  Nowadays, FreeBSD
    # might be the last major non-compliant system (xxx check me).
    res = widen(c_nice(inc))
    if res == -1:
        err = get_saved_errno()
        if err != 0:
            raise OSError(err, "os_nice failed")
    return res

c_ctermid = external('ctermid', [rffi.CCHARP], rffi.CCHARP)

@replace_os_function('ctermid')
def ctermid():
    return rffi.charp2str(c_ctermid(lltype.nullptr(rffi.CCHARP.TO)))

c_tmpnam = external('tmpnam', [rffi.CCHARP], rffi.CCHARP)

@replace_os_function('tmpnam')
def tmpnam():
    return rffi.charp2str(c_tmpnam(lltype.nullptr(rffi.CCHARP.TO)))

#___________________________________________________________________

c_getpid = external('getpid', [], rffi.PID_T,
                    releasegil=False, save_err=rffi.RFFI_SAVE_ERRNO)
c_getppid = external('getppid', [], rffi.PID_T,
                     releasegil=False, save_err=rffi.RFFI_SAVE_ERRNO)
c_setsid = external('setsid', [], rffi.PID_T,
                    save_err=rffi.RFFI_SAVE_ERRNO)
c_getsid = external('getsid', [rffi.PID_T], rffi.PID_T,
                    save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('getpid')
def getpid():
    return handle_posix_error('getpid', c_getpid())

@replace_os_function('getppid')
def getppid():
    return handle_posix_error('getppid', c_getppid())

@replace_os_function('setsid')
def setsid():
    return handle_posix_error('setsid', c_setsid())

@replace_os_function('getsid')
def getsid(pid):
    return handle_posix_error('getsid', c_getsid(pid))

c_getpgid = external('getpgid', [rffi.PID_T], rffi.PID_T,
                     save_err=rffi.RFFI_SAVE_ERRNO)
c_setpgid = external('setpgid', [rffi.PID_T, rffi.PID_T], rffi.INT,
                     save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('getpgid')
def getpgid(pid):
    return handle_posix_error('getpgid', c_getpgid(pid))

@replace_os_function('setpgid')
def setpgid(pid, gid):
    handle_posix_error('setpgid', c_setpgid(pid, gid))

PID_GROUPS_T = rffi.CArrayPtr(rffi.PID_T)
c_getgroups = external('getgroups', [rffi.INT, PID_GROUPS_T], rffi.INT,
                       save_err=rffi.RFFI_SAVE_ERRNO)
c_setgroups = external('setgroups', [rffi.SIZE_T, PID_GROUPS_T], rffi.INT,
                       save_err=rffi.RFFI_SAVE_ERRNO)
c_initgroups = external('initgroups', [rffi.CCHARP, rffi.PID_T], rffi.INT,
                        save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('getgroups')
def getgroups():
    n = handle_posix_error('getgroups',
                           c_getgroups(0, lltype.nullptr(PID_GROUPS_T.TO)))
    groups = lltype.malloc(PID_GROUPS_T.TO, n, flavor='raw')
    try:
        n = handle_posix_error('getgroups', c_getgroups(n, groups))
        return [widen(groups[i]) for i in range(n)]
    finally:
        lltype.free(groups, flavor='raw')

@replace_os_function('setgroups')
def setgroups(gids):
    n = len(gids)
    groups = lltype.malloc(PID_GROUPS_T.TO, n, flavor='raw')
    try:
        for i in range(n):
            groups[i] = rffi.cast(rffi.PID_T, gids[i])
        handle_posix_error('setgroups', c_setgroups(n, groups))
    finally:
        lltype.free(groups, flavor='raw')

@replace_os_function('initgroups')
def initgroups(user, group):
    handle_posix_error('initgroups', c_initgroups(user, group))

if GETPGRP_HAVE_ARG:
    c_getpgrp = external('getpgrp', [rffi.INT], rffi.INT,
                         save_err=rffi.RFFI_SAVE_ERRNO)
else:
    c_getpgrp = external('getpgrp', [], rffi.INT,
                         save_err=rffi.RFFI_SAVE_ERRNO)
if SETPGRP_HAVE_ARG:
    c_setpgrp = external('setpgrp', [rffi.INT, rffi.INT], rffi.INT,
                         save_err=rffi.RFFI_SAVE_ERRNO)
else:
    c_setpgrp = external('setpgrp', [], rffi.INT,
                         save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('getpgrp')
def getpgrp():
    if GETPGRP_HAVE_ARG:
        return handle_posix_error('getpgrp', c_getpgrp(0))
    else:
        return handle_posix_error('getpgrp', c_getpgrp())

@replace_os_function('setpgrp')
def setpgrp():
    if SETPGRP_HAVE_ARG:
        handle_posix_error('setpgrp', c_setpgrp(0, 0))
    else:
        handle_posix_error('setpgrp', c_setpgrp())

c_tcgetpgrp = external('tcgetpgrp', [rffi.INT], rffi.PID_T,
                       save_err=rffi.RFFI_SAVE_ERRNO)
c_tcsetpgrp = external('tcsetpgrp', [rffi.INT, rffi.PID_T], rffi.INT,
                       save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('tcgetpgrp')
def tcgetpgrp(fd):
    return handle_posix_error('tcgetpgrp', c_tcgetpgrp(fd))

@replace_os_function('tcsetpgrp')
def tcsetpgrp(fd, pgrp):
    return handle_posix_error('tcsetpgrp', c_tcsetpgrp(fd, pgrp))

#___________________________________________________________________

c_getuid = external('getuid', [], rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO)
c_geteuid = external('geteuid', [], rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO)
c_setuid = external('setuid', [rffi.INT], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)
c_seteuid = external('seteuid', [rffi.INT], rffi.INT,
                     save_err=rffi.RFFI_SAVE_ERRNO)
c_getgid = external('getgid', [], rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO)
c_getegid = external('getegid', [], rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO)
c_setgid = external('setgid', [rffi.INT], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO)
c_setegid = external('setegid', [rffi.INT], rffi.INT,
                     save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('getuid')
def getuid():
    return handle_posix_error('getuid', c_getuid())

@replace_os_function('geteuid')
def geteuid():
    return handle_posix_error('geteuid', c_geteuid())

@replace_os_function('setuid')
def setuid(uid):
    handle_posix_error('setuid', c_setuid(uid))

@replace_os_function('seteuid')
def seteuid(uid):
    handle_posix_error('seteuid', c_seteuid(uid))

@replace_os_function('getgid')
def getgid():
    return handle_posix_error('getgid', c_getgid())

@replace_os_function('getegid')
def getegid():
    return handle_posix_error('getegid', c_getegid())

@replace_os_function('setgid')
def setgid(gid):
    handle_posix_error('setgid', c_setgid(gid))

@replace_os_function('setegid')
def setegid(gid):
    handle_posix_error('setegid', c_setegid(gid))

c_setreuid = external('setreuid', [rffi.INT, rffi.INT], rffi.INT,
                      save_err=rffi.RFFI_SAVE_ERRNO)
c_setregid = external('setregid', [rffi.INT, rffi.INT], rffi.INT,
                      save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('setreuid')
def setreuid(ruid, euid):
    handle_posix_error('setreuid', c_setreuid(ruid, euid))

@replace_os_function('setregid')
def setregid(rgid, egid):
    handle_posix_error('setregid', c_setregid(rgid, egid))

c_getresuid = external('getresuid', [rffi.INTP] * 3, rffi.INT,
                       save_err=rffi.RFFI_SAVE_ERRNO)
c_getresgid = external('getresgid', [rffi.INTP] * 3, rffi.INT,
                       save_err=rffi.RFFI_SAVE_ERRNO)
c_setresuid = external('setresuid', [rffi.INT] * 3, rffi.INT,
                       save_err=rffi.RFFI_SAVE_ERRNO)
c_setresgid = external('setresgid', [rffi.INT] * 3, rffi.INT,
                       save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('getresuid')
def getresuid():
    out = lltype.malloc(rffi.INTP.TO, 3, flavor='raw')
    try:
        handle_posix_error('getresuid',
                           c_getresuid(rffi.ptradd(out, 0),
                                       rffi.ptradd(out, 1),
                                       rffi.ptradd(out, 2)))
        return (widen(out[0]), widen(out[1]), widen(out[2]))
    finally:
        lltype.free(out, flavor='raw')

@replace_os_function('getresgid')
def getresgid():
    out = lltype.malloc(rffi.INTP.TO, 3, flavor='raw')
    try:
        handle_posix_error('getresgid',
                           c_getresgid(rffi.ptradd(out, 0),
                                       rffi.ptradd(out, 1),
                                       rffi.ptradd(out, 2)))
        return (widen(out[0]), widen(out[1]), widen(out[2]))
    finally:
        lltype.free(out, flavor='raw')

@replace_os_function('setresuid')
def setresuid(ruid, euid, suid):
    handle_posix_error('setresuid', c_setresuid(ruid, euid, suid))

@replace_os_function('setresgid')
def setresgid(rgid, egid, sgid):
    handle_posix_error('setresgid', c_setresgid(rgid, egid, sgid))

#___________________________________________________________________

c_chroot = external('chroot', [rffi.CCHARP], rffi.INT,
                    save_err=rffi.RFFI_SAVE_ERRNO,
                    macro=_MACRO_ON_POSIX)

@replace_os_function('chroot')
def chroot(path):
    handle_posix_error('chroot', c_chroot(_as_bytes0(path)))

if not _WIN32:
    CHARARRAY1 = lltype.FixedSizeArray(lltype.Char, 1)
    class CConfig:
        _compilation_info_ = ExternalCompilationInfo(
            includes = ['sys/utsname.h']
        )
        UTSNAME = rffi_platform.Struct('struct utsname', [
            ('sysname',  CHARARRAY1),
            ('nodename', CHARARRAY1),
            ('release',  CHARARRAY1),
            ('version',  CHARARRAY1),
            ('machine',  CHARARRAY1)])
    config = rffi_platform.configure(CConfig)
    UTSNAMEP = lltype.Ptr(config['UTSNAME'])

    c_uname = external('uname', [UTSNAMEP], rffi.INT,
                       compilation_info=CConfig._compilation_info_,
                       save_err=rffi.RFFI_SAVE_ERRNO)

@replace_os_function('uname')
def uname():
    l_utsbuf = lltype.malloc(UTSNAMEP.TO, flavor='raw')
    try:
        handle_posix_error('uname', c_uname(l_utsbuf))
        return (
            rffi.charp2str(rffi.cast(rffi.CCHARP, l_utsbuf.c_sysname)),
            rffi.charp2str(rffi.cast(rffi.CCHARP, l_utsbuf.c_nodename)),
            rffi.charp2str(rffi.cast(rffi.CCHARP, l_utsbuf.c_release)),
            rffi.charp2str(rffi.cast(rffi.CCHARP, l_utsbuf.c_version)),
            rffi.charp2str(rffi.cast(rffi.CCHARP, l_utsbuf.c_machine)),
        )
    finally:
        lltype.free(l_utsbuf, flavor='raw')

# These are actually macros on some/most systems
c_makedev = external('makedev', [rffi.INT, rffi.INT], rffi.INT)
c_major = external('major', [rffi.INT], rffi.INT)
c_minor = external('minor', [rffi.INT], rffi.INT)

@replace_os_function('makedev')
@jit.dont_look_inside
def makedev(maj, min):
    return c_makedev(maj, min)

@replace_os_function('major')
@jit.dont_look_inside
def major(dev):
    return c_major(dev)

@replace_os_function('minor')
@jit.dont_look_inside
def minor(dev):
    return c_minor(dev)

#___________________________________________________________________

c_sysconf = external('sysconf', [rffi.INT], rffi.LONG,
                     save_err=rffi.RFFI_FULL_ERRNO_ZERO)
c_fpathconf = external('fpathconf', [rffi.INT, rffi.INT], rffi.LONG,
                       save_err=rffi.RFFI_FULL_ERRNO_ZERO)
c_pathconf = external('pathconf', [rffi.CCHARP, rffi.INT], rffi.LONG,
                      save_err=rffi.RFFI_FULL_ERRNO_ZERO)
c_confstr = external('confstr',
                     [rffi.INT, rffi.CCHARP, rffi.SIZE_T], rffi.SIZE_T,
                      save_err=rffi.RFFI_FULL_ERRNO_ZERO)

@replace_os_function('sysconf')
def sysconf(value):
    res = c_sysconf(value)
    if res == -1:
        errno = get_saved_errno()
        if errno != 0:
            raise OSError(errno, "sysconf failed")
    return res

@replace_os_function('fpathconf')
def fpathconf(fd, value):
    res = c_fpathconf(fd, value)
    if res == -1:
        errno = get_saved_errno()
        if errno != 0:
            raise OSError(errno, "fpathconf failed")
    return res

@replace_os_function('pathconf')
def pathconf(path, value):
    res = c_pathconf(_as_bytes0(path), value)
    if res == -1:
        errno = get_saved_errno()
        if errno != 0:
            raise OSError(errno, "pathconf failed")
    return res

@replace_os_function('confstr')
def confstr(value):
    n = intmask(c_confstr(value, lltype.nullptr(rffi.CCHARP.TO), 0))
    if n > 0:
        buf = lltype.malloc(rffi.CCHARP.TO, n, flavor='raw')
        try:
            c_confstr(value, buf, n)
            return rffi.charp2strn(buf, n)
        finally:
            lltype.free(buf, flavor='raw')
    else:
        errno = get_saved_errno()
        if errno != 0:
            raise OSError(errno, "confstr failed")
        return None

# ____________________________________________________________
# Support for os.environ

# XXX only for systems where os.environ is an instance of _Environ,
# which should cover Unix and Windows at least
assert type(os.environ) is not dict

from rpython.rtyper.controllerentry import ControllerEntryForPrebuilt

class EnvironExtRegistry(ControllerEntryForPrebuilt):
    _about_ = os.environ

    def getcontroller(self):
        from rpython.rlib.rposix_environ import OsEnvironController
        return OsEnvironController()
