/**
 * D header file for C99 <stdio.h>
 *
 * $(C_HEADER_DESCRIPTION pubs.opengroup.org/onlinepubs/009695399/basedefs/_stdio.h.html, _stdio.h)
 *
 * Copyright: Copyright Sean Kelly 2005 - 2009.
 * License: Distributed under the
 *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
 *    (See accompanying file LICENSE)
 * Authors:   Sean Kelly,
 *            Alex Rønne Petersen
 * Source:    https://github.com/dlang/druntime/blob/master/src/core/stdc/stdio.d
 * Standards: ISO/IEC 9899:1999 (E)
 */

module core.stdc.stdio;

version (OSX)
    version = Darwin;
else version (iOS)
    version = Darwin;
else version (TVOS)
    version = Darwin;
else version (WatchOS)
    version = Darwin;

private
{
    import core.stdc.config;
    import core.stdc.stdarg; // for va_list
    import core.stdc.stdint : intptr_t;

  version (FreeBSD)
  {
    import core.sys.posix.sys.types;
  }
  else version (OpenBSD)
  {
    import core.sys.posix.sys.types;
  }
  version (NetBSD)
  {
    import core.sys.posix.sys.types;
  }
  version (DragonFlyBSD)
  {
    import core.sys.posix.sys.types;
  }
}

extern (C):
@system:
nothrow:
@nogc:

version (CRuntime_DigitalMars)
{
    enum
    {
        ///
        BUFSIZ       = 0x4000,
        ///
        EOF          = -1,
        ///
        FOPEN_MAX    = 20,
        ///
        FILENAME_MAX = 256, // 255 plus NULL
        ///
        TMP_MAX      = 32767,
        ///
        SYS_OPEN     = 20,      // non-standard
    }

    ///
    enum int     _NFILE     = 60;       // non-standard
    ///
    enum string  _P_tmpdir  = "\\"; // non-standard
    ///
    enum wstring _wP_tmpdir = "\\"; // non-standard
    ///
    enum int     L_tmpnam   = _P_tmpdir.length + 12;
}
else version (CRuntime_Microsoft)
{
    enum
    {
        ///
        BUFSIZ       = 512,
        ///
        EOF          = -1,
        ///
        FOPEN_MAX    = 20,
        ///
        FILENAME_MAX = 260,
        /// Actually int.max since Visual Studio 2015.
        TMP_MAX      = 32767,
        ///
        _SYS_OPEN    = 20,      // non-standard
    }

    ///
    enum int     _NFILE     = 512;       // non-standard
    /// Removed since Visual Studio 2015.
    enum string  _P_tmpdir  = "\\"; // non-standard
    /// Removed since Visual Studio 2015.
    enum wstring _wP_tmpdir = "\\"; // non-standard
    /// Actually 260 since Visual Studio 2015.
    enum int     L_tmpnam   = _P_tmpdir.length + 12;
}
else version (CRuntime_Glibc)
{
    enum
    {
        ///
        BUFSIZ       = 8192,
        ///
        EOF          = -1,
        ///
        FOPEN_MAX    = 16,
        ///
        FILENAME_MAX = 4095,
        ///
        TMP_MAX      = 238328,
        ///
        L_tmpnam     = 20
    }
}
else version (CRuntime_Musl)
{
    enum
    {
        ///
        BUFSIZ       = 1024,
        ///
        EOF          = -1,
        ///
        FOPEN_MAX    = 1000,
        ///
        FILENAME_MAX = 4096,
        ///
        TMP_MAX      = 10000,
        ///
        L_tmpnam     = 20
    }
}
else version (Darwin)
{
    enum
    {
        ///
        BUFSIZ       = 1024,
        ///
        EOF          = -1,
        ///
        FOPEN_MAX    = 20,
        ///
        FILENAME_MAX = 1024,
        ///
        TMP_MAX      = 308915776,
        ///
        L_tmpnam     = 1024,
    }

    private
    {
        struct __sbuf
        {
            ubyte*  _base;
            int     _size;
        }

        struct __sFILEX
        {

        }
    }
}
else version (FreeBSD)
{
    enum
    {
        ///
        BUFSIZ       = 1024,
        ///
        EOF          = -1,
        ///
        FOPEN_MAX    = 20,
        ///
        FILENAME_MAX = 1024,
        ///
        TMP_MAX      = 308915776,
        ///
        L_tmpnam     = 1024
    }

    struct __sbuf
    {
        ubyte *_base;
        int _size;
    }
}
else version (NetBSD)
{
    enum
    {
        ///
        BUFSIZ       = 1024,
        ///
        EOF          = -1,
        ///
        FOPEN_MAX    = 20,
        ///
        FILENAME_MAX = 1024,
        ///
        TMP_MAX      = 308915776,
        ///
        L_tmpnam     = 1024
    }

    struct __sbuf
    {
        ubyte *_base;
        int _size;
    }
}
else version (OpenBSD)
{
    enum
    {
        ///
        BUFSIZ       = 1024,
        ///
        EOF          = -1,
        ///
        FOPEN_MAX    = 20,
        ///
        FILENAME_MAX = 1024,
        ///
        TMP_MAX      = 0x7fffffff,
        ///
        L_tmpnam     = 1024
    }

    struct __sbuf
    {
        ubyte *_base;
        int _size;
    }
}
else version (DragonFlyBSD)
{
    enum
    {
        BUFSIZ       = 1024,
        EOF          = -1,
        FOPEN_MAX    = 20,
        FILENAME_MAX = 1024,
        TMP_MAX      = 308915776,
        L_tmpnam     = 1024
    }

    struct __sbuf {                     // <sys/sbuf.h>
        byte*            s_buf;         // storage buffer
        int function(void *, const char *, int) sbuf_drain_func;
        void*            s_drain_arg;   // user-supplied drain argument
        int              s_error;       // current error code
        ssize_t          s_size;        // size of storage buffer
        ssize_t          s_len;         // current length of string
        int              s_flags;       // flags
        ssize_t          s_sect_len;    // current length of section
    }

    enum {
        SBUF_FIXEDLEN   = 0x00000000,   // fixed length buffer (default)
        SBUF_AUTOEXTEND = 0x00000001,   // automatically extend buffer
        SBUF_USRFLAGMSK = 0x0000ffff,   // mask of flags the user may specify
        SBUF_DYNAMIC    = 0x00010000,   // s_buf must be freed
        SBUF_FINISHED   = 0x00020000,   // set by sbuf_finish()
        SBUF_DYNSTRUCT  = 0x00080000,   // sbuf must be freed
        SBUF_INSECTION  = 0x00100000,   // set by sbuf_start_section()
    }
}
else version (Solaris)
{
    enum
    {
        ///
        BUFSIZ = 1024,
        ///
        EOF = -1,
        ///
        FOPEN_MAX = _NFILE,
        ///
        FILENAME_MAX = 1024,
        ///
        TMP_MAX = 17576,
        ///
        L_tmpnam = 25,
    }

    version (X86)
        ///
        enum int _NFILE = 60;
    else
        ///
        enum int _NFILE = 20;
}
else version (CRuntime_Bionic)
{
    enum
    {
        ///
        BUFSIZ       = 1024,
        ///
        EOF          = -1,
        ///
        FOPEN_MAX    = 20,
        ///
        FILENAME_MAX = 1024,
        ///
        TMP_MAX      = 308915776,
        ///
        L_tmpnam     = 1024
    }

    struct __sbuf
    {
        ubyte* _base;
        int _size;
    }
}
else version (CRuntime_UClibc)
{
    enum
    {
        ///
        BUFSIZ       = 4096,
        ///
        EOF          = -1,
        ///
        FOPEN_MAX    = 16,
        ///
        FILENAME_MAX = 4095,
        ///
        TMP_MAX      = 238328,
        ///
        L_tmpnam     = 20
    }
}
else
{
    static assert( false, "Unsupported platform" );
}

enum
{
    /// Offset is relative to the beginning
    SEEK_SET,
    /// Offset is relative to the current position
    SEEK_CUR,
    /// Offset is relative to the end
    SEEK_END
}

version (CRuntime_DigitalMars)
{
    ///
    alias c_long fpos_t;

    ///
    struct _iobuf
    {
        char* _ptr;
        int   _cnt;
        char* _base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char* __tmpnum;
    }

    ///
    alias shared(_iobuf) FILE;
}
else version (CRuntime_Microsoft)
{
    ///
    alias long fpos_t;

    ///
    struct _iobuf
    {
        void* undefined;
    }

    ///
    alias shared(_iobuf) FILE;
}
else version (CRuntime_Glibc)
{
    import core.stdc.wchar_ : mbstate_t;
    ///
    struct fpos_t
    {
        long __pos; // couldn't use off_t because of static if issue
        mbstate_t __state;
    }

    ///
    struct _IO_FILE
    {
        int     _flags;
        char*   _read_ptr;
        char*   _read_end;
        char*   _read_base;
        char*   _write_base;
        char*   _write_ptr;
        char*   _write_end;
        char*   _buf_base;
        char*   _buf_end;
        char*   _save_base;
        char*   _backup_base;
        char*   _save_end;
        void*   _markers;
        _IO_FILE* _chain;
        int     _fileno;
        int     _flags2;
        ptrdiff_t _old_offset;
        ushort  _cur_column;
        byte    _vtable_offset;
        char[1] _shortbuf = 0;
        void*   _lock;

        ptrdiff_t _offset;

        /*_IO_codecvt*/ void* _codecvt;
        /*_IO_wide_data*/ void* _wide_data;
        _IO_FILE *_freeres_list;
        void *_freeres_buf;
        size_t __pad5;
        int _mode;

        char[15 * int.sizeof - 4 * (void*).sizeof - size_t.sizeof] _unused2;
    }

    ///
    alias _IO_FILE _iobuf;
    ///
    alias shared(_IO_FILE) FILE;
}
else version (CRuntime_Musl)
{
    union fpos_t
    {
        char[16] __opaque = 0;
        double __align;
    }
    struct _IO_FILE;

    ///
    alias _IO_FILE _iobuf; // needed for phobos
    ///
    alias shared(_IO_FILE) FILE;
}
else version (Darwin)
{
    ///
    alias long fpos_t;

    ///
    struct __sFILE
    {
        ubyte*    _p;
        int       _r;
        int       _w;
        short     _flags;
        short     _file;
        __sbuf    _bf;
        int       _lbfsize;

        void*     _cookie;
        int     function(void*)                    _close;
        int     function(void*, char*, int)        _read;
        fpos_t  function(void*, fpos_t, int)       _seek;
        int     function(void*, char *, int)       _write;

        __sbuf    _ub;
        __sFILEX* _extra;
        int       _ur;

        ubyte[3]  _ubuf;
        ubyte[1]  _nbuf;

        __sbuf    _lb;

        int       _blksize;
        fpos_t    _offset;
    }

    ///
    alias __sFILE _iobuf;
    ///
    alias shared(__sFILE) FILE;
}
else version (FreeBSD)
{
    // Need to import wchar_ now since __mbstate_t now resides there
    import core.stdc.wchar_ : mbstate_t;

    ///
    alias off_t fpos_t;

    ///
    struct __sFILE
    {
        ubyte*          _p;
        int             _r;
        int             _w;
        short           _flags;
        short           _file;
        __sbuf          _bf;
        int             _lbfsize;

        void*           _cookie;
        int     function(void*)                 _close;
        int     function(void*, char*, int)     _read;
        fpos_t  function(void*, fpos_t, int)    _seek;
        int     function(void*, const scope char*, int)  _write;

        __sbuf          _ub;
        ubyte*          _up;
        int             _ur;

        ubyte[3]        _ubuf;
        ubyte[1]        _nbuf;

        __sbuf          _lb;

        int             _blksize;
        fpos_t          _offset;

        pthread_mutex_t _fl_mutex;
        pthread_t       _fl_owner;
        int             _fl_count;
        int             _orientation;
        mbstate_t       _mbstate;
    }

    ///
    alias __sFILE _iobuf;
    ///
    alias shared(__sFILE) FILE;
}
else version (NetBSD)
{
    ///
    alias off_t fpos_t;

    ///
    struct __sFILE
    {
        ubyte*          _p;
        int             _r;
        int             _w;
        ushort           _flags;
        short           _file;
        __sbuf          _bf;
        int             _lbfsize;

        void*           _cookie;
        int     function(void*)                 _close;
        ssize_t     function(void*, char*, size_t)     _read;
        fpos_t  function(void*, fpos_t, int)    _seek;
        ssize_t     function(void*, const scope char*, size_t)  _write;

        __sbuf          _ub;
        ubyte*          _up;
        int             _ur;

        ubyte[3]        _ubuf;
        ubyte[1]        _nbuf;

        int     function(void *)    _flush;
        /* Formerly used by fgetln/fgetwln; kept for binary compatibility */
        char[__sbuf.sizeof - _flush.sizeof]    _lb_unused = void;


        int             _blksize;
        off_t          _offset;
        static assert(off_t.sizeof==8);
    }

    ///
    alias __sFILE _iobuf;
    ///
    alias shared(__sFILE) FILE;
}
else version (OpenBSD)
{
    ///
    alias fpos_t = off_t;

    ///
    struct __sFILE
    {
        ubyte*          _p;
        int             _r;
        int             _w;
        short           _flags;
        short           _file;
        __sbuf          _bf;
        int             _lbfsize;

        void*           _cookie;
        int     function(void*)                         _close;
        int     function(void*, scope char*, int)       _read;
        fpos_t  function(void*, fpos_t, int)            _seek;
        int     function(void*, scope const char*, int) _write;

        __sbuf          _ext;
        ubyte*          _up;
        int             _ur;

        ubyte[3]        _ubuf;
        ubyte[1]        _nbuf;

        __sbuf          _lb;

        int             _blksize;
        fpos_t          _offset;
    }

    ///
    alias __sFILE _iobuf;
    ///
    alias shared(__sFILE) FILE;
}
else version (DragonFlyBSD)
{
    alias off_t fpos_t;

    /// See /usr/include/stdio.h
    struct __FILE_public
    {
        ubyte*          *_p;            /* current position in (some) buffer */
        int             _flags;         /* flags, below; this FILE is free if 0 */
        int             _fileno;        /* fileno, if Unix descriptor, else -1 */
        ssize_t         _r;             /* read space left for getc() */
        ssize_t         _w;             /* write space left for putc() */
        ssize_t         _lbfsize;       /* 0 or -_bf._size, for inline putc */
    }

    alias __FILE_public _iobuf;
    alias shared(__FILE_public) FILE;
}
else version (Solaris)
{
    import core.stdc.wchar_ : mbstate_t;

    ///
    alias c_long fpos_t;

    version (D_LP64)
    {
        ///
        struct _iobuf
        {
            char*      _ptr;   /* next character from/to here in buffer */
            char*      _base;  /* the buffer */
            char*      _end;   /* the end of the buffer */
            size_t     _cnt;   /* number of available characters in buffer */
            int        _file;  /* UNIX System file descriptor */
            int        _flag;  /* the state of the stream */
            ubyte[24]  _lock;  //rmutex_t   _lock; /* lock for this structure */
            mbstate_t  _state; /* mbstate_t */
            ubyte[32]  __fill; /* filler to bring size to 128 bytes */
        }
    }
    else
    {
        ///
        struct _iobuf
        {
            char* _ptr;
            int _cnt;
            char* _base;
            char _flag = 0;
            char _magic = 0;
            ushort __flags; // __orientation:2
                            // __ionolock:1
                            // __seekable:1
                            // __extendedfd:1
                            // __xf_nocheck:1
                            // __filler:10
        }
    }
    ///
    alias shared(_iobuf) FILE;
}
else version (CRuntime_Bionic)
{
    ///
    alias c_long fpos_t; // couldn't use off_t because of static if issue

    ///
    struct __sFILE
    {
        ubyte*    _p;
        int       _r;
        int       _w;
        short     _flags;
        short     _file;
        __sbuf    _bf;
        int       _lbfsize;

        void*     _cookie;
        int      function(void*)                          _close;
        int      function(void*, scope char*, int)        _read;
        fpos_t   function(void*, fpos_t, int)             _seek;
        int      function(void*, scope const char*, int)  _write;

        __sbuf    _ext;
        ubyte*    _up;
        int       _ur;

        ubyte[3]  _ubuf;
        ubyte[1]  _nbuf;

        __sbuf    _lb;

        int       _blksize;
        fpos_t    _offset;
    }

    ///
    alias __sFILE _iobuf;
    ///
    alias shared(__sFILE) FILE;
}
else version (CRuntime_UClibc)
{
    import core.stdc.wchar_ : mbstate_t;
    import core.stdc.stddef : wchar_t;
    import core.sys.posix.sys.types : ssize_t, pthread_mutex_t;

    ///
    struct fpos_t
    {
        long __pos; // couldn't use off_t because of static if issue
        mbstate_t __state;
        int __mblen_pending;
    }

    struct _IO_cookie_io_functions_t
    {
       ssize_t function(void* __cookie, char* __buf, size_t __bufsize)          read;
       ssize_t function(void* __cookie, const char* __buf, size_t __bufsize)    write;
       int function(void* __cookie, long* __pos, int __whence)                  seek;
       int function(void* __cookie)                                             close;
    }

    alias _IO_cookie_io_functions_t cookie_io_functions_t;

    ///
    struct __STDIO_FILE_STRUCT
    {
        ushort __modeflags;
        char[2] __ungot_width = 0;
        int __filedes;
        char* __bufstart;
        char* __bufend;
        char* __bufpos;
        char* __bufread;
        char* __bufgetc_u;
        char*__bufputc_u;
        __STDIO_FILE_STRUCT* __nextopen;
        void *__cookie;
        _IO_cookie_io_functions_t __gcs;
        wchar_t[2] __ungot = 0;
        mbstate_t __state;
        void *__unused;
        int __user_locking;
        pthread_mutex_t __lock;
    }

    ///
    alias __STDIO_FILE_STRUCT _iobuf;
    ///
    alias shared(__STDIO_FILE_STRUCT) FILE;
}
else
{
    static assert( false, "Unsupported platform" );
}

enum
{
    ///
    _F_RDWR = 0x0003, // non-standard
    ///
    _F_READ = 0x0001, // non-standard
    ///
    _F_WRIT = 0x0002, // non-standard
    ///
    _F_BUF  = 0x0004, // non-standard
    ///
    _F_LBUF = 0x0008, // non-standard
    ///
    _F_ERR  = 0x0010, // non-standard
    ///
    _F_EOF  = 0x0020, // non-standard
    ///
    _F_BIN  = 0x0040, // non-standard
    ///
    _F_IN   = 0x0080, // non-standard
    ///
    _F_OUT  = 0x0100, // non-standard
    ///
    _F_TERM = 0x0200, // non-standard
}

version (CRuntime_DigitalMars)
{
    enum
    {
        ///
        _IOFBF   = 0,
        ///
        _IOLBF   = 0x40,
        ///
        _IONBF   = 4,
        ///
        _IOREAD  = 1,     // non-standard
        ///
        _IOWRT   = 2,     // non-standard
        ///
        _IOMYBUF = 8,     // non-standard
        ///
        _IOEOF   = 0x10,  // non-standard
        ///
        _IOERR   = 0x20,  // non-standard
        ///
        _IOSTRG  = 0x40,  // non-standard
        ///
        _IORW    = 0x80,  // non-standard
        ///
        _IOTRAN  = 0x100, // non-standard
        ///
        _IOAPP   = 0x200, // non-standard
    }

    extern shared void function() _fcloseallp;

    private extern shared FILE[_NFILE] _iob;

    ///
    enum stdin  = &_iob[0];
    ///
    enum stdout = &_iob[1];
    ///
    enum stderr = &_iob[2];
    ///
    enum stdaux = &_iob[3];
    ///
    enum stdprn = &_iob[4];
}
else version (CRuntime_Microsoft)
{
    enum
    {
        ///
        _IOFBF   = 0,
        ///
        _IOLBF   = 0x40,
        ///
        _IONBF   = 4,
        /// Removed since Visual Studio 2015.
        _IOREAD  = 1,     // non-standard
        /// Removed since Visual Studio 2015.
        _IOWRT   = 2,     // non-standard
        /// Removed since Visual Studio 2015.
        _IOMYBUF = 8,     // non-standard
        /// Removed since Visual Studio 2015.
        _IOEOF   = 0x10,  // non-standard
        /// Removed since Visual Studio 2015.
        _IOERR   = 0x20,  // non-standard
        /// Removed since Visual Studio 2015.
        _IOSTRG  = 0x40,  // non-standard
        /// Removed since Visual Studio 2015.
        _IORW    = 0x80,  // non-standard
        /// Removed since Visual Studio 2015.
        _IOAPP   = 0x200, // non-standard
        /// Removed since Visual Studio 2015.
        _IOAPPEND = 0x200, // non-standard
    }

    extern shared void function() _fcloseallp;

    FILE* __acrt_iob_func(int hnd);     // VS2015+, reimplemented in msvc.d for VS2013-

    ///
    FILE* stdin()() { return __acrt_iob_func(0); }
    ///
    FILE* stdout()() { return __acrt_iob_func(1); }
    ///
    FILE* stderr()() { return __acrt_iob_func(2); }
}
else version (CRuntime_Glibc)
{
    enum
    {
        ///
        _IOFBF = 0,
        ///
        _IOLBF = 1,
        ///
        _IONBF = 2,
    }

    ///
    extern shared FILE* stdin;
    ///
    extern shared FILE* stdout;
    ///
    extern shared FILE* stderr;
}
else version (Darwin)
{
    enum
    {
        ///
        _IOFBF = 0,
        ///
        _IOLBF = 1,
        ///
        _IONBF = 2,
    }

    private extern shared FILE* __stdinp;
    private extern shared FILE* __stdoutp;
    private extern shared FILE* __stderrp;

    ///
    alias __stdinp  stdin;
    ///
    alias __stdoutp stdout;
    ///
    alias __stderrp stderr;
}
else version (FreeBSD)
{
    enum
    {
        ///
        _IOFBF = 0,
        ///
        _IOLBF = 1,
        ///
        _IONBF = 2,
    }

    private extern shared FILE* __stdinp;
    private extern shared FILE* __stdoutp;
    private extern shared FILE* __stderrp;

    ///
    alias __stdinp  stdin;
    ///
    alias __stdoutp stdout;
    ///
    alias __stderrp stderr;
}
else version (NetBSD)
{
    enum
    {
        ///
        _IOFBF = 0,
        ///
        _IOLBF = 1,
        ///
        _IONBF = 2,
    }

    private extern shared FILE[3] __sF;
    @property auto __stdin()() { return &__sF[0]; }
    @property auto __stdout()() { return &__sF[1]; }
    @property auto __stderr()() { return &__sF[2]; }
    ///
    alias __stdin stdin;
    ///
    alias __stdout stdout;
    ///
    alias __stderr stderr;
}
else version (OpenBSD)
{
    enum
    {
        ///
        _IOFBF = 0,
        ///
        _IOLBF = 1,
        ///
        _IONBF = 2,
    }

    private extern shared FILE[3] __sF;
    @property auto __stdin()() { return &__sF[0]; }
    @property auto __stdout()() { return &__sF[1]; }
    @property auto __stderr()() { return &__sF[2]; }
    ///
    alias __stdin stdin;
    ///
    alias __stdout stdout;
    ///
    alias __stderr stderr;
}
else version (DragonFlyBSD)
{
    enum
    {
        _IOFBF = 0,
        _IOLBF = 1,
        _IONBF = 2,
    }

    private extern shared FILE* __stdinp;
    private extern shared FILE* __stdoutp;
    private extern shared FILE* __stderrp;

    alias __stdinp  stdin;
    alias __stdoutp stdout;
    alias __stderrp stderr;
}
else version (Solaris)
{
    enum
    {
        ///
        _IOFBF = 0x00,
        ///
        _IOLBF = 0x40,
        ///
        _IONBF = 0x04,
        ///
        _IOEOF = 0x20,
        ///
        _IOERR = 0x40,
        ///
        _IOREAD = 0x01,
        ///
        _IOWRT = 0x02,
        ///
        _IORW = 0x80,
        ///
        _IOMYBUF = 0x08,
    }

    private extern shared FILE[_NFILE] __iob;

    ///
    @property auto stdin()() { return &__iob[0]; }
    ///
    @property auto stdout()() { return &__iob[1]; }
    ///
    @property auto stderr()() { return &__iob[2]; }
}
else version (CRuntime_Bionic)
{
    enum
    {
        ///
        _IOFBF = 0,
        ///
        _IOLBF = 1,
        ///
        _IONBF = 2,
    }

    private extern shared FILE[3] __sF;

    ///
    @property auto stdin()() { return &__sF[0]; }
    ///
    @property auto stdout()() { return &__sF[1]; }
    ///
    @property auto stderr()() { return &__sF[2]; }
}
else version (CRuntime_Musl)
{
    // needs tail const
    extern shared FILE* stdin;
    ///
    extern shared FILE* stdout;
    ///
    extern shared FILE* stderr;
    enum
    {
        ///
        _IOFBF = 0,
        ///
        _IOLBF = 1,
        ///
        _IONBF = 2,
    }
}
else version (CRuntime_UClibc)
{
    enum
    {
        ///
        _IOFBF = 0,
        ///
        _IOLBF = 1,
        ///
        _IONBF = 2,
    }

    ///
    extern shared FILE* stdin;
    ///
    extern shared FILE* stdout;
    ///
    extern shared FILE* stderr;
}
else
{
    static assert( false, "Unsupported platform" );
}

///
int remove(scope const char* filename);
///
int rename(scope const char* from, scope const char* to);

///
@trusted FILE* tmpfile(); // No unsafe pointer manipulation.
///
char* tmpnam(char* s);

///
int   fclose(FILE* stream);

// No unsafe pointer manipulation.
@trusted
{
    ///
    int   fflush(FILE* stream);
}

///
FILE* fopen(scope const char* filename, scope const char* mode);
///
FILE* freopen(scope const char* filename, scope const char* mode, FILE* stream);

///
void setbuf(FILE* stream, char* buf);
///
int  setvbuf(FILE* stream, char* buf, int mode, size_t size);

version (MinGW)
{
    // Prefer the MinGW versions over the MSVC ones, as the latter don't handle
    // reals at all.
    ///
    pragma(printf)
    int __mingw_fprintf(FILE* stream, scope const char* format, scope const ...);
    ///
    alias __mingw_fprintf fprintf;

    ///
    pragma(scanf)
    int __mingw_fscanf(FILE* stream, scope const char* format, scope ...);
    ///
    alias __mingw_fscanf fscanf;

    ///
    pragma(printf)
    int __mingw_sprintf(scope char* s, scope const char* format, scope const ...);
    ///
    alias __mingw_sprintf sprintf;

    ///
    pragma(scanf)
    int __mingw_sscanf(scope const char* s, scope const char* format, scope ...);
    ///
    alias __mingw_sscanf sscanf;

    ///
    pragma(printf)
    int __mingw_vfprintf(FILE* stream, scope const char* format, va_list arg);
    ///
    alias __mingw_vfprintf vfprintf;

    ///
    pragma(scanf)
    int __mingw_vfscanf(FILE* stream, scope const char* format, va_list arg);
    ///
    alias __mingw_vfscanf vfscanf;

    ///
    pragma(printf)
    int __mingw_vsprintf(scope char* s, scope const char* format, va_list arg);
    ///
    alias __mingw_vsprintf vsprintf;

    ///
    pragma(scanf)
    int __mingw_vsscanf(scope const char* s, scope const char* format, va_list arg);
    ///
    alias __mingw_vsscanf vsscanf;

    ///
    pragma(printf)
    int __mingw_vprintf(scope const char* format, va_list arg);
    ///
    alias __mingw_vprintf vprintf;

    ///
    pragma(scanf)
    int __mingw_vscanf(scope const char* format, va_list arg);
    ///
    alias __mingw_vscanf vscanf;

    ///
    pragma(printf)
    int __mingw_printf(scope const char* format, scope const ...);
    ///
    alias __mingw_printf printf;

    ///
    pragma(scanf)
    int __mingw_scanf(scope const char* format, scope ...);
    ///
    alias __mingw_scanf scanf;
}
else
{
    ///
    pragma(printf)
    int fprintf(FILE* stream, scope const char* format, scope const ...);
    ///
    pragma(scanf)
    int fscanf(FILE* stream, scope const char* format, scope ...);
    ///
    pragma(printf)
    int sprintf(scope char* s, scope const char* format, scope const ...);
    ///
    pragma(scanf)
    int sscanf(scope const char* s, scope const char* format, scope ...);
    ///
    pragma(printf)
    int vfprintf(FILE* stream, scope const char* format, va_list arg);
    ///
    pragma(scanf)
    int vfscanf(FILE* stream, scope const char* format, va_list arg);
    ///
    pragma(printf)
    int vsprintf(scope char* s, scope const char* format, va_list arg);
    ///
    pragma(scanf)
    int vsscanf(scope const char* s, scope const char* format, va_list arg);
    ///
    pragma(printf)
    int vprintf(scope const char* format, va_list arg);
    ///
    pragma(scanf)
    int vscanf(scope const char* format, va_list arg);
    ///
    pragma(printf)
    int printf(scope const char* format, scope const ...);
    ///
    pragma(scanf)
    int scanf(scope const char* format, scope ...);
}

// No unsafe pointer manipulation.
@trusted
{
    ///
    int fgetc(FILE* stream);
    ///
    int fputc(int c, FILE* stream);
}

///
char* fgets(char* s, int n, FILE* stream);
///
int   fputs(scope const char* s, FILE* stream);
///
char* gets(char* s);
///
int   puts(scope const char* s);

// No unsafe pointer manipulation.
extern (D) @trusted
{
    ///
    int getchar()()                 { return getc(stdin);     }
    ///
    int putchar()(int c)            { return putc(c,stdout);  }
}

///
alias getc = fgetc;
///
alias putc = fputc;

///
@trusted int ungetc(int c, FILE* stream); // No unsafe pointer manipulation.

///
size_t fread(scope void* ptr, size_t size, size_t nmemb, FILE* stream);
///
size_t fwrite(scope const void* ptr, size_t size, size_t nmemb, FILE* stream);

// No unsafe pointer manipulation.
@trusted
{
    ///
    int fgetpos(FILE* stream, scope fpos_t * pos);
    ///
    int fsetpos(FILE* stream, scope const fpos_t* pos);

    ///
    int    fseek(FILE* stream, c_long offset, int whence);
    ///
    c_long ftell(FILE* stream);
}

version (CRuntime_DigitalMars)
{
  // No unsafe pointer manipulation.
  extern (D) @trusted
  {
    ///
    void rewind()(FILE* stream)   { fseek(stream,0L,SEEK_SET); stream._flag= stream._flag & ~_IOERR; }
    ///
    pure void clearerr()(FILE* stream) { stream._flag = stream._flag & ~(_IOERR|_IOEOF); }
    ///
    pure int  feof()(FILE* stream)     { return stream._flag&_IOEOF; }
    ///
    pure int  ferror()(FILE* stream)   { return stream._flag&_IOERR; }
    ///
    pure int  fileno()(FILE* stream)   { return stream._file; }
  }
    ///
    pragma(printf)
    int   _snprintf(scope char* s, size_t n, scope const char* fmt, scope const ...);
    ///
    alias _snprintf snprintf;

    ///
    pragma(printf)
    int   _vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
    ///
    alias _vsnprintf vsnprintf;

    //
    // Digital Mars under-the-hood C I/O functions. Uses _iobuf* for the
    // unshared version of FILE*, usable when the FILE is locked.
    //

    ///
    int _fputc_nlock(int c, _iobuf* fp);
    ///
    int _fputwc_nlock(int c, _iobuf* fp);
    ///
    int _fgetc_nlock(_iobuf* fp);
    ///
    int _fgetwc_nlock(_iobuf* fp);
    ///
    int __fp_lock(FILE* fp);
    ///
    void __fp_unlock(FILE* fp);
    ///
    int setmode(int fd, int mode);
}
else version (CRuntime_Microsoft)
{
  // No unsafe pointer manipulation.
  @trusted
  {
    ///
    void rewind(FILE* stream);
    ///
    pure void clearerr(FILE* stream);
    ///
    pure int  feof(FILE* stream);
    ///
    pure int  ferror(FILE* stream);
    ///
    pure int  fileno(FILE* stream);
  }

  version (MinGW)
  {
    pragma(printf)
    int   __mingw_snprintf(scope char* s, size_t n, scope const char* fmt, scope const ...);
    ///
    alias __mingw_snprintf _snprintf;
    ///
    alias __mingw_snprintf snprintf;

    ///
    pragma(printf)
    int   __mingw_vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
    ///
    alias __mingw_vsnprintf _vsnprintf;
    ///
    alias __mingw_vsnprintf vsnprintf;
  }
  else
  {
    ///
    pragma(printf)
    int _snprintf(scope char* s, size_t n, scope const char* format, scope const ...);
    ///
    pragma(printf)
    int  snprintf(scope char* s, size_t n, scope const char* format, scope const ...);

    ///
    pragma(printf)
    int _vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
    ///
    pragma(printf)
    int  vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
  }

    //
    // Microsoft under-the-hood C I/O functions. Uses _iobuf* for the unshared
    // version of FILE*, usable when the FILE is locked.
    //
    import core.stdc.stddef : wchar_t;
    import core.stdc.wchar_ : wint_t;

    ///
    int _fputc_nolock(int c, _iobuf* fp);
    ///
    int _fgetc_nolock(_iobuf* fp);
    ///
    wint_t _fputwc_nolock(wchar_t c, _iobuf* fp);
    ///
    wint_t _fgetwc_nolock(_iobuf* fp);
    ///
    void _lock_file(FILE* fp);
    ///
    void _unlock_file(FILE* fp);
    ///
    int _setmode(int fd, int mode);
    ///
    int _fseeki64(FILE* stream, long offset, int origin);
    ///
    long _ftelli64(FILE* stream);
    ///
    intptr_t _get_osfhandle(int fd);
    ///
    int _open_osfhandle(intptr_t osfhandle, int flags);
}
else version (CRuntime_Glibc)
{
  // No unsafe pointer manipulation.
  @trusted
  {
    ///
    void rewind(FILE* stream);
    ///
    pure void clearerr(FILE* stream);
    ///
    pure int  feof(FILE* stream);
    ///
    pure int  ferror(FILE* stream);
    ///
    int  fileno(FILE *);
  }

    ///
    pragma(printf)
    int  snprintf(scope char* s, size_t n, scope const char* format, scope const ...);
    ///
    pragma(printf)
    int  vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);

    //
    // Gnu under-the-hood C I/O functions. Uses _iobuf* for the unshared
    // version of FILE*, usable when the FILE is locked.
    // See http://gnu.org/software/libc/manual/html_node/I_002fO-on-Streams.html
    //
    import core.stdc.wchar_ : wint_t;
    import core.stdc.stddef : wchar_t;

    ///
    int fputc_unlocked(int c, _iobuf* stream);
    ///
    int fgetc_unlocked(_iobuf* stream);
    ///
    wint_t fputwc_unlocked(wchar_t wc, _iobuf* stream);
    ///
    wint_t fgetwc_unlocked(_iobuf* stream);
}
else version (Darwin)
{
  // No unsafe pointer manipulation.
  @trusted
  {
    ///
    void rewind(FILE*);
    ///
    pure void clearerr(FILE*);
    ///
    pure int  feof(FILE*);
    ///
    pure int  ferror(FILE*);
    ///
    int  fileno(FILE*);
  }

    ///
    pragma(printf)
    int  snprintf(scope char* s, size_t n, scope const char* format, scope const ...);
    ///
    pragma(printf)
    int  vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
}
else version (FreeBSD)
{
  // No unsafe pointer manipulation.
  @trusted
  {
    ///
    void rewind(FILE*);
    ///
    pure void clearerr(FILE*);
    ///
    pure int  feof(FILE*);
    ///
    pure int  ferror(FILE*);
    ///
    int  fileno(FILE*);
  }

    ///
    pragma(printf)
    int  snprintf(scope char* s, size_t n, scope const char* format, scope const ...);
    ///
    pragma(printf)
    int  vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
}
else version (NetBSD)
{
  // No unsafe pointer manipulation.
  @trusted
  {
    ///
    void rewind(FILE*);
    ///
    pure void clearerr(FILE*);
    ///
    pure int  feof(FILE*);
    ///
    pure int  ferror(FILE*);
    ///
    int  fileno(FILE*);
  }

    ///
    pragma(printf)
    int  snprintf(char* s, size_t n, const scope char* format, scope const ...);
    ///
    pragma(printf)
    int  vsnprintf(char* s, size_t n, const scope char* format, va_list arg);
}
else version (OpenBSD)
{
    // No unsafe pointer manipulation.
    @trusted
    {
        ///
        void rewind(FILE*);
    }
    @trusted private
    {
        ///
        pragma(mangle, "clearerr")
        pure void __clearerr(FILE*);
        ///
        pragma(mangle, "feof")
        pure int __feof(FILE*);
        ///
        pragma(mangle, "ferror")
        pure int __ferror(FILE*);
        ///
        pragma(mangle, "fileno")
        int __fileno(FILE*);
    }

    enum __SLBF = 0x0001;
    enum __SNBF = 0x0002;
    enum __SRD  = 0x0004;
    enum __SWR  = 0x0008;
    enum __SRW  = 0x0010;
    enum __SEOF = 0x0020;
    enum __SERR = 0x0040;
    enum __SMBF = 0x0080;
    enum __SAPP = 0x0100;
    enum __SSTR = 0x0200;
    enum __SOPT = 0x0400;
    enum __SNPT = 0x0800;
    enum __SOFF = 0x1000;
    enum __SMOD = 0x2000;
    enum __SALC = 0x4000;
    enum __SIGN = 0x8000;

    extern immutable __gshared int __isthreaded;

    extern (D) @trusted
    {
        void __sclearerr()(FILE* p)
        {
            p._flags = p._flags & ~(__SERR|__SEOF);
        }

        int __sfeof()(FILE* p)
        {
            return (p._flags & __SEOF) != 0;
        }

        int __sferror()(FILE* p)
        {
            return (p._flags & __SERR) != 0;
        }

        int __sfileno()(FILE* p)
        {
            return p._file;
        }

        pure void clearerr()(FILE* file)
        {
            !__isthreaded ? __sclearerr(file) : __clearerr(file);
        }

        pure int feof()(FILE* file)
        {
            return !__isthreaded ? __sfeof(file) : __feof(file);
        }

        pure int ferror()(FILE* file)
        {
            return !__isthreaded ? __sferror(file) : __ferror(file);
        }

        int fileno()(FILE* file)
        {
            return !__isthreaded ? __sfileno(file) : __fileno(file);
        }
    }

    ///
    pragma(printf)
    int  snprintf(scope char* s, size_t n, scope const char* format, scope const ...);
    ///
    pragma(printf)
    int  vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
}
else version (DragonFlyBSD)
{
  // No unsafe pointer manipulation.
  @trusted
  {
    void rewind(FILE*);
    pure void clearerr(FILE*);
    pure int  feof(FILE*);
    pure int  ferror(FILE*);
    int  fileno(FILE*);
  }
  enum __SLBF = 0x0001;
  enum __SNBF = 0x0002;
  enum __SRD  = 0x0004;
  enum __SWR  = 0x0008;
  enum __SRW  = 0x0010;
  enum __SEOF = 0x0020;
  enum __SERR = 0x0040;
  enum __SMBF = 0x0080;
  enum __SAPP = 0x0100;
  enum __SSTR = 0x0200;
  enum __SOPT = 0x0400;
  enum __SNPT = 0x0800;
  enum __SOFF = 0x1000;
  enum __SMOD = 0x2000;
  enum __SALC = 0x4000;
  enum __SIGN = 0x8000;

    pragma(printf)
    int  snprintf(scope char* s, size_t n, scope const char* format, scope const ...);
    pragma(printf)
    int  vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
}
else version (Solaris)
{
  // No unsafe pointer manipulation.
  @trusted
  {
    ///
    void rewind(FILE*);
    ///
    pure void clearerr(FILE*);
    ///
    pure int  feof(FILE*);
    ///
    pure int  ferror(FILE*);
    ///
    int  fileno(FILE*);
  }

    ///
    pragma(printf)
    int  snprintf(scope char* s, size_t n, scope const char* format, scope const ...);
    ///
    pragma(printf)
    int  vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
}
else version (CRuntime_Bionic)
{
  // No unsafe pointer manipulation.
  @trusted
  {
    ///
    void rewind(FILE*);
    ///
    pure void clearerr(FILE*);
    ///
    pure int  feof(FILE*);
    ///
    pure int  ferror(FILE*);
    ///
    int  fileno(FILE*);
  }

    ///
    pragma(printf)
    int  snprintf(scope char* s, size_t n, scope const char* format, scope const ...);
    ///
    pragma(printf)
    int  vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
}
else version (CRuntime_Musl)
{
    @trusted
    {
        ///
        void rewind(FILE* stream);
        ///
        pure void clearerr(FILE* stream);
        ///
        pure int  feof(FILE* stream);
        ///
        pure int  ferror(FILE* stream);
        ///
        int  fileno(FILE *);
    }

    ///
    pragma(printf)
    int snprintf(scope char* s, size_t n, scope const char* format, scope const ...);
    ///
    pragma(printf)
    int vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
}
else version (CRuntime_UClibc)
{
  // No unsafe pointer manipulation.
  @trusted
  {
    ///
    void rewind(FILE* stream);
    ///
    pure void clearerr(FILE* stream);
    ///
    pure int  feof(FILE* stream);
    ///
    pure int  ferror(FILE* stream);
    ///
    int  fileno(FILE *);
  }

    ///
    pragma(printf)
    int  snprintf(scope char* s, size_t n, scope const char* format, scope const ...);
    ///
    pragma(printf)
    int  vsnprintf(scope char* s, size_t n, scope const char* format, va_list arg);
}
else
{
    static assert( false, "Unsupported platform" );
}

///
void perror(scope const char* s);

version (CRuntime_DigitalMars)
{
    version (none)
        import core.sys.windows.windows : HANDLE, _WaitSemaphore, _ReleaseSemaphore;
    else
    {
        // too slow to import windows
        private alias void* HANDLE;
        private void _WaitSemaphore(int iSemaphore);
        private void _ReleaseSemaphore(int iSemaphore);
    }

    enum
    {
        ///
        FHND_APPEND     = 0x04,
        ///
        FHND_DEVICE     = 0x08,
        ///
        FHND_TEXT       = 0x10,
        ///
        FHND_BYTE       = 0x20,
        ///
        FHND_WCHAR      = 0x40,
    }

    private enum _MAX_SEMAPHORES = 10 + _NFILE;
    private enum _semIO = 3;

    private extern __gshared short[_MAX_SEMAPHORES] _iSemLockCtrs;
    private extern __gshared int[_MAX_SEMAPHORES] _iSemThreadIds;
    private extern __gshared int[_MAX_SEMAPHORES] _iSemNestCount;
    private extern __gshared HANDLE[_NFILE] _osfhnd;
    extern shared ubyte[_NFILE] __fhnd_info;

    // this is copied from semlock.h in DMC's runtime.
    private void LockSemaphore()(uint num)
    {
        asm nothrow @nogc
        {
            mov EDX, num;
            lock;
            inc _iSemLockCtrs[EDX * 2];
            jz lsDone;
            push EDX;
            call _WaitSemaphore;
            add ESP, 4;
        }

    lsDone: {}
    }

    // this is copied from semlock.h in DMC's runtime.
    private void UnlockSemaphore()(uint num)
    {
        asm nothrow @nogc
        {
            mov EDX, num;
            lock;
            dec _iSemLockCtrs[EDX * 2];
            js usDone;
            push EDX;
            call _ReleaseSemaphore;
            add ESP, 4;
        }

    usDone: {}
    }

    // This converts a HANDLE to a file descriptor in DMC's runtime
    ///
    int _handleToFD()(HANDLE h, int flags)
    {
        LockSemaphore(_semIO);
        scope(exit) UnlockSemaphore(_semIO);

        foreach (fd; 0 .. _NFILE)
        {
            if (!_osfhnd[fd])
            {
                _osfhnd[fd] = h;
                __fhnd_info[fd] = cast(ubyte)flags;
                return fd;
            }
        }

        return -1;
    }

    ///
    HANDLE _fdToHandle()(int fd)
    {
        // no semaphore is required, once inserted, a file descriptor
        // doesn't change.
        if (fd < 0 || fd >= _NFILE)
            return null;

        return _osfhnd[fd];
    }

    enum
    {
        ///
        STDIN_FILENO  = 0,
        ///
        STDOUT_FILENO = 1,
        ///
        STDERR_FILENO = 2,
    }

    int open(scope const(char)* filename, int flags, ...); ///
    alias _open = open; ///
    int _wopen(scope const wchar* filename, int oflag, ...); ///
    int sopen(scope const char* filename, int oflag, int shflag, ...); ///
    alias _sopen = sopen; ///
    int _wsopen(scope const wchar* filename, int oflag, int shflag, ...); ///
    int close(int fd); ///
    alias _close = close; ///
    FILE *fdopen(int fd, scope const(char)* flags); ///
    alias _fdopen = fdopen; ///
    FILE *_wfdopen(int fd, scope const(wchar)* flags); ///

}
else version (CRuntime_Microsoft)
{
    int _open(scope const char* filename, int oflag, ...); ///
    int _wopen(scope const wchar* filename, int oflag, ...); ///
    int _sopen(scope const char* filename, int oflag, int shflag, ...); ///
    int _wsopen(scope const wchar* filename, int oflag, int shflag, ...); ///
    int _close(int fd); ///
    FILE *_fdopen(int fd, scope const(char)* flags); ///
    FILE *_wfdopen(int fd, scope const(wchar)* flags); ///
}

version (Windows)
{
    // file open flags
    enum
    {
        _O_RDONLY = 0x0000, ///
        O_RDONLY = _O_RDONLY, ///
        _O_WRONLY = 0x0001, ///
        O_WRONLY = _O_WRONLY, ///
        _O_RDWR   = 0x0002, ///
        O_RDWR = _O_RDWR, ///
        _O_APPEND = 0x0008, ///
        O_APPEND = _O_APPEND, ///
        _O_CREAT  = 0x0100, ///
        O_CREAT = _O_CREAT, ///
        _O_TRUNC  = 0x0200, ///
        O_TRUNC = _O_TRUNC, ///
        _O_EXCL   = 0x0400, ///
        O_EXCL = _O_EXCL, ///
        _O_TEXT   = 0x4000, ///
        O_TEXT = _O_TEXT, ///
        _O_BINARY = 0x8000, ///
        O_BINARY = _O_BINARY, ///
        _O_WTEXT = 0x10000, ///
        _O_U16TEXT = 0x20000, ///
        _O_U8TEXT = 0x40000, ///
        _O_ACCMODE = (_O_RDONLY|_O_WRONLY|_O_RDWR), ///
        O_ACCMODE = _O_ACCMODE, ///
        _O_RAW = _O_BINARY, ///
        O_RAW = _O_BINARY, ///
        _O_NOINHERIT = 0x0080, ///
        O_NOINHERIT = _O_NOINHERIT, ///
        _O_TEMPORARY = 0x0040, ///
        O_TEMPORARY = _O_TEMPORARY, ///
        _O_SHORT_LIVED = 0x1000, ///
        _O_SEQUENTIAL = 0x0020, ///
        O_SEQUENTIAL = _O_SEQUENTIAL, ///
        _O_RANDOM = 0x0010, ///
        O_RANDOM = _O_RANDOM, ///
    }

    enum
    {
        _S_IREAD  = 0x0100, /// read permission, owner
        S_IREAD = _S_IREAD, /// read permission, owner
        _S_IWRITE = 0x0080, /// write permission, owner
        S_IWRITE = _S_IWRITE, /// write permission, owner
    }

    enum
    {
        _SH_DENYRW = 0x10, /// deny read/write mode
        SH_DENYRW = _SH_DENYRW, /// deny read/write mode
        _SH_DENYWR = 0x20, /// deny write mode
        SH_DENYWR = _SH_DENYWR, /// deny write mode
        _SH_DENYRD = 0x30, /// deny read mode
        SH_DENYRD = _SH_DENYRD, /// deny read mode
        _SH_DENYNO = 0x40, /// deny none mode
        SH_DENYNO = _SH_DENYNO, /// deny none mode
    }
}
