diff libphobos/src/std/stdio.d @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libphobos/src/std/stdio.d	Thu Feb 13 11:34:05 2020 +0900
@@ -0,0 +1,5174 @@
+// Written in the D programming language.
+
+/**
+Standard I/O functions that extend $(B core.stdc.stdio).  $(B core.stdc.stdio)
+is $(D_PARAM public)ally imported when importing $(B std.stdio).
+
+Source: $(PHOBOSSRC std/_stdio.d)
+Copyright: Copyright Digital Mars 2007-.
+License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors:   $(HTTP digitalmars.com, Walter Bright),
+           $(HTTP erdani.org, Andrei Alexandrescu),
+           Alex Rønne Petersen
+ */
+module std.stdio;
+
+import core.stdc.stddef; // wchar_t
+public import core.stdc.stdio;
+import std.algorithm.mutation; // copy
+import std.meta; // allSatisfy
+import std.range.primitives; // ElementEncodingType, empty, front,
+    // isBidirectionalRange, isInputRange, put
+import std.traits; // isSomeChar, isSomeString, Unqual, isPointer
+import std.typecons; // Flag
+
+/++
+If flag $(D KeepTerminator) is set to $(D KeepTerminator.yes), then the delimiter
+is included in the strings returned.
++/
+alias KeepTerminator = Flag!"keepTerminator";
+
+version (CRuntime_Microsoft)
+{
+    version = MICROSOFT_STDIO;
+}
+else version (CRuntime_DigitalMars)
+{
+    // Specific to the way Digital Mars C does stdio
+    version = DIGITAL_MARS_STDIO;
+}
+
+version (CRuntime_Glibc)
+{
+    // Specific to the way Gnu C does stdio
+    version = GCC_IO;
+    version = HAS_GETDELIM;
+}
+else version (CRuntime_Bionic)
+{
+    version = GENERIC_IO;
+    version = HAS_GETDELIM;
+}
+else version (CRuntime_Musl)
+{
+    version = GENERIC_IO;
+    version = HAS_GETDELIM;
+}
+
+version (OSX)
+{
+    version = GENERIC_IO;
+    version = HAS_GETDELIM;
+}
+else version (FreeBSD)
+{
+    version = GENERIC_IO;
+    version = HAS_GETDELIM;
+}
+else version (NetBSD)
+{
+    version = GENERIC_IO;
+    version = HAS_GETDELIM;
+}
+else version (DragonFlyBSD)
+{
+    version = GENERIC_IO;
+    version = HAS_GETDELIM;
+}
+else version (Solaris)
+{
+    version = GENERIC_IO;
+    version = NO_GETDELIM;
+}
+
+// Character type used for operating system filesystem APIs
+version (Windows)
+{
+    private alias FSChar = wchar;
+}
+else version (Posix)
+{
+    private alias FSChar = char;
+}
+else
+    static assert(0);
+
+version (Windows)
+{
+    // core.stdc.stdio.fopen expects file names to be
+    // encoded in CP_ACP on Windows instead of UTF-8.
+    /+ Waiting for druntime pull 299
+    +/
+    extern (C) nothrow @nogc FILE* _wfopen(in wchar* filename, in wchar* mode);
+    extern (C) nothrow @nogc FILE* _wfreopen(in wchar* filename, in wchar* mode, FILE* fp);
+
+    import core.sys.windows.windows : HANDLE;
+}
+
+version (DIGITAL_MARS_STDIO)
+{
+    extern (C)
+    {
+        /* **
+         * Digital Mars under-the-hood C I/O functions.
+         * Use _iobuf* for the unshared version of FILE*,
+         * usable when the FILE is locked.
+         */
+      nothrow:
+      @nogc:
+        int _fputc_nlock(int, _iobuf*);
+        int _fputwc_nlock(int, _iobuf*);
+        int _fgetc_nlock(_iobuf*);
+        int _fgetwc_nlock(_iobuf*);
+        int __fp_lock(FILE*);
+        void __fp_unlock(FILE*);
+
+        int setmode(int, int);
+    }
+    alias FPUTC = _fputc_nlock;
+    alias FPUTWC = _fputwc_nlock;
+    alias FGETC = _fgetc_nlock;
+    alias FGETWC = _fgetwc_nlock;
+
+    alias FLOCK = __fp_lock;
+    alias FUNLOCK = __fp_unlock;
+
+    alias _setmode = setmode;
+    enum _O_BINARY = 0x8000;
+    int _fileno(FILE* f) { return f._file; }
+    alias fileno = _fileno;
+}
+else version (MICROSOFT_STDIO)
+{
+    extern (C)
+    {
+        /* **
+         * Microsoft under-the-hood C I/O functions
+         */
+      nothrow:
+      @nogc:
+        int _fputc_nolock(int, _iobuf*);
+        int _fputwc_nolock(int, _iobuf*);
+        int _fgetc_nolock(_iobuf*);
+        int _fgetwc_nolock(_iobuf*);
+        void _lock_file(FILE*);
+        void _unlock_file(FILE*);
+        int _setmode(int, int);
+        int _fileno(FILE*);
+        FILE* _fdopen(int, const (char)*);
+        int _fseeki64(FILE*, long, int);
+        long _ftelli64(FILE*);
+    }
+    alias FPUTC = _fputc_nolock;
+    alias FPUTWC = _fputwc_nolock;
+    alias FGETC = _fgetc_nolock;
+    alias FGETWC = _fgetwc_nolock;
+
+    alias FLOCK = _lock_file;
+    alias FUNLOCK = _unlock_file;
+
+    alias setmode = _setmode;
+    alias fileno = _fileno;
+
+    enum
+    {
+        _O_RDONLY = 0x0000,
+        _O_APPEND = 0x0004,
+        _O_TEXT   = 0x4000,
+        _O_BINARY = 0x8000,
+    }
+}
+else version (GCC_IO)
+{
+    /* **
+     * Gnu under-the-hood C I/O functions; see
+     * http://gnu.org/software/libc/manual/html_node/I_002fO-on-Streams.html
+     */
+    extern (C)
+    {
+      nothrow:
+      @nogc:
+        int fputc_unlocked(int, _iobuf*);
+        int fputwc_unlocked(wchar_t, _iobuf*);
+        int fgetc_unlocked(_iobuf*);
+        int fgetwc_unlocked(_iobuf*);
+        void flockfile(FILE*);
+        void funlockfile(FILE*);
+
+        private size_t fwrite_unlocked(const(void)* ptr,
+                size_t size, size_t n, _iobuf *stream);
+    }
+
+    alias FPUTC = fputc_unlocked;
+    alias FPUTWC = fputwc_unlocked;
+    alias FGETC = fgetc_unlocked;
+    alias FGETWC = fgetwc_unlocked;
+
+    alias FLOCK = flockfile;
+    alias FUNLOCK = funlockfile;
+}
+else version (GENERIC_IO)
+{
+    nothrow:
+    @nogc:
+
+    extern (C)
+    {
+        void flockfile(FILE*);
+        void funlockfile(FILE*);
+    }
+
+    int fputc_unlocked(int c, _iobuf* fp) { return fputc(c, cast(shared) fp); }
+    int fputwc_unlocked(wchar_t c, _iobuf* fp)
+    {
+        import core.stdc.wchar_ : fputwc;
+        return fputwc(c, cast(shared) fp);
+    }
+    int fgetc_unlocked(_iobuf* fp) { return fgetc(cast(shared) fp); }
+    int fgetwc_unlocked(_iobuf* fp)
+    {
+        import core.stdc.wchar_ : fgetwc;
+        return fgetwc(cast(shared) fp);
+    }
+
+    alias FPUTC = fputc_unlocked;
+    alias FPUTWC = fputwc_unlocked;
+    alias FGETC = fgetc_unlocked;
+    alias FGETWC = fgetwc_unlocked;
+
+    alias FLOCK = flockfile;
+    alias FUNLOCK = funlockfile;
+}
+else
+{
+    static assert(0, "unsupported C I/O system");
+}
+
+version (HAS_GETDELIM) extern(C) nothrow @nogc
+{
+    ptrdiff_t getdelim(char**, size_t*, int, FILE*);
+    // getline() always comes together with getdelim()
+    ptrdiff_t getline(char**, size_t*, FILE*);
+}
+
+//------------------------------------------------------------------------------
+struct ByRecord(Fields...)
+{
+private:
+    import std.typecons : Tuple;
+
+    File file;
+    char[] line;
+    Tuple!(Fields) current;
+    string format;
+
+public:
+    this(File f, string format)
+    {
+        assert(f.isOpen);
+        file = f;
+        this.format = format;
+        popFront(); // prime the range
+    }
+
+    /// Range primitive implementations.
+    @property bool empty()
+    {
+        return !file.isOpen;
+    }
+
+    /// Ditto
+    @property ref Tuple!(Fields) front()
+    {
+        return current;
+    }
+
+    /// Ditto
+    void popFront()
+    {
+        import std.conv : text;
+        import std.exception : enforce;
+        import std.format : formattedRead;
+        import std.string : chomp;
+
+        enforce(file.isOpen, "ByRecord: File must be open");
+        file.readln(line);
+        if (!line.length)
+        {
+            file.detach();
+        }
+        else
+        {
+            line = chomp(line);
+            formattedRead(line, format, &current);
+            enforce(line.empty, text("Leftover characters in record: `",
+                            line, "'"));
+        }
+    }
+}
+
+template byRecord(Fields...)
+{
+    ByRecord!(Fields) byRecord(File f, string format)
+    {
+        return typeof(return)(f, format);
+    }
+}
+
+/**
+Encapsulates a $(D FILE*). Generally D does not attempt to provide
+thin wrappers over equivalent functions in the C standard library, but
+manipulating $(D FILE*) values directly is unsafe and error-prone in
+many ways. The $(D File) type ensures safe manipulation, automatic
+file closing, and a lot of convenience.
+
+The underlying $(D FILE*) handle is maintained in a reference-counted
+manner, such that as soon as the last $(D File) variable bound to a
+given $(D FILE*) goes out of scope, the underlying $(D FILE*) is
+automatically closed.
+
+Example:
+----
+// test.d
+void main(string[] args)
+{
+    auto f = File("test.txt", "w"); // open for writing
+    f.write("Hello");
+    if (args.length > 1)
+    {
+        auto g = f; // now g and f write to the same file
+                    // internal reference count is 2
+        g.write(", ", args[1]);
+        // g exits scope, reference count decreases to 1
+    }
+    f.writeln("!");
+    // f exits scope, reference count falls to zero,
+    // underlying `FILE*` is closed.
+}
+----
+$(CONSOLE
+% rdmd test.d Jimmy
+% cat test.txt
+Hello, Jimmy!
+% __
+)
+ */
+struct File
+{
+    import std.range.primitives : ElementEncodingType;
+    import std.traits : isScalarType, isArray;
+    enum Orientation { unknown, narrow, wide }
+
+    private struct Impl
+    {
+        FILE * handle = null; // Is null iff this Impl is closed by another File
+        uint refs = uint.max / 2;
+        bool isPopened; // true iff the stream has been created by popen()
+        Orientation orientation;
+    }
+    private Impl* _p;
+    private string _name;
+
+    package this(FILE* handle, string name, uint refs = 1, bool isPopened = false) @trusted
+    {
+        import core.stdc.stdlib : malloc;
+        import std.exception : enforce;
+
+        assert(!_p);
+        _p = cast(Impl*) enforce(malloc(Impl.sizeof), "Out of memory");
+        _p.handle = handle;
+        _p.refs = refs;
+        _p.isPopened = isPopened;
+        _p.orientation = Orientation.unknown;
+        _name = name;
+    }
+
+/**
+Constructor taking the name of the file to open and the open mode.
+
+Copying one $(D File) object to another results in the two $(D File)
+objects referring to the same underlying file.
+
+The destructor automatically closes the file as soon as no $(D File)
+object refers to it anymore.
+
+Params:
+    name = range or string representing the file _name
+    stdioOpenmode = range or string represting the open mode
+        (with the same semantics as in the C standard library
+        $(HTTP cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen)
+        function)
+
+Throws: $(D ErrnoException) if the file could not be opened.
+ */
+    this(string name, in char[] stdioOpenmode = "rb") @safe
+    {
+        import std.conv : text;
+        import std.exception : errnoEnforce;
+
+        this(errnoEnforce(.fopen(name, stdioOpenmode),
+                        text("Cannot open file `", name, "' in mode `",
+                                stdioOpenmode, "'")),
+                name);
+
+        // MSVCRT workaround (issue 14422)
+        version (MICROSOFT_STDIO)
+        {
+            bool append, update;
+            foreach (c; stdioOpenmode)
+                if (c == 'a')
+                    append = true;
+                else
+                if (c == '+')
+                    update = true;
+            if (append && !update)
+                seek(size);
+        }
+    }
+
+    /// ditto
+    this(R1, R2)(R1 name)
+        if (isInputRange!R1 && isSomeChar!(ElementEncodingType!R1))
+    {
+        import std.conv : to;
+        this(name.to!string, "rb");
+    }
+
+    /// ditto
+    this(R1, R2)(R1 name, R2 mode)
+        if (isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) &&
+            isInputRange!R2 && isSomeChar!(ElementEncodingType!R2))
+    {
+        import std.conv : to;
+        this(name.to!string, mode.to!string);
+    }
+
+    @safe unittest
+    {
+        static import std.file;
+        import std.utf : byChar;
+        auto deleteme = testFilename();
+        auto f = File(deleteme.byChar, "w".byChar);
+        f.close();
+        std.file.remove(deleteme);
+    }
+
+    ~this() @safe
+    {
+        detach();
+    }
+
+    this(this) @safe nothrow
+    {
+        if (!_p) return;
+        assert(_p.refs);
+        ++_p.refs;
+    }
+
+/**
+Assigns a file to another. The target of the assignment gets detached
+from whatever file it was attached to, and attaches itself to the new
+file.
+ */
+    void opAssign(File rhs) @safe
+    {
+        import std.algorithm.mutation : swap;
+
+        swap(this, rhs);
+    }
+
+/**
+First calls $(D detach) (throwing on failure), and then attempts to
+_open file $(D name) with mode $(D stdioOpenmode). The mode has the
+same semantics as in the C standard library $(HTTP
+cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function.
+
+Throws: $(D ErrnoException) in case of error.
+ */
+    void open(string name, in char[] stdioOpenmode = "rb") @safe
+    {
+        detach();
+        this = File(name, stdioOpenmode);
+    }
+
+/**
+Reuses the `File` object to either open a different file, or change
+the file mode. If `name` is `null`, the mode of the currently open
+file is changed; otherwise, a new file is opened, reusing the C
+`FILE*`. The function has the same semantics as in the C standard
+library $(HTTP cplusplus.com/reference/cstdio/freopen/, freopen)
+function.
+
+Note: Calling `reopen` with a `null` `name` is not implemented
+in all C runtimes.
+
+Throws: $(D ErrnoException) in case of error.
+ */
+    void reopen(string name, in char[] stdioOpenmode = "rb") @trusted
+    {
+        import std.conv : text;
+        import std.exception : enforce, errnoEnforce;
+        import std.internal.cstring : tempCString;
+
+        enforce(isOpen, "Attempting to reopen() an unopened file");
+
+        auto namez = (name == null ? _name : name).tempCString!FSChar();
+        auto modez = stdioOpenmode.tempCString!FSChar();
+
+        FILE* fd = _p.handle;
+        version (Windows)
+            fd =  _wfreopen(namez, modez, fd);
+        else
+            fd = freopen(namez, modez, fd);
+
+        errnoEnforce(fd, name
+            ? text("Cannot reopen file `", name, "' in mode `", stdioOpenmode, "'")
+            : text("Cannot reopen file in mode `", stdioOpenmode, "'"));
+
+        if (name !is null)
+            _name = name;
+    }
+
+    @system unittest // Test changing filename
+    {
+        import std.exception : assertThrown, assertNotThrown;
+        static import std.file;
+
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "foo");
+        scope(exit) std.file.remove(deleteme);
+        auto f = File(deleteme);
+        assert(f.readln() == "foo");
+
+        auto deleteme2 = testFilename();
+        std.file.write(deleteme2, "bar");
+        scope(exit) std.file.remove(deleteme2);
+        f.reopen(deleteme2);
+        assert(f.name == deleteme2);
+        assert(f.readln() == "bar");
+        f.close();
+    }
+
+    version (CRuntime_DigitalMars) {} else // Not implemented
+    version (CRuntime_Microsoft) {} else // Not implemented
+    @system unittest // Test changing mode
+    {
+        import std.exception : assertThrown, assertNotThrown;
+        static import std.file;
+
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "foo");
+        scope(exit) std.file.remove(deleteme);
+        auto f = File(deleteme, "r+");
+        assert(f.readln() == "foo");
+        f.reopen(null, "w");
+        f.write("bar");
+        f.seek(0);
+        f.reopen(null, "a");
+        f.write("baz");
+        assert(f.name == deleteme);
+        f.close();
+        assert(std.file.readText(deleteme) == "barbaz");
+    }
+
+/**
+First calls $(D detach) (throwing on failure), and then runs a command
+by calling the C standard library function $(HTTP
+opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen).
+
+Throws: $(D ErrnoException) in case of error.
+ */
+    version (Posix) void popen(string command, in char[] stdioOpenmode = "r") @safe
+    {
+        import std.exception : errnoEnforce;
+
+        detach();
+        this = File(errnoEnforce(.popen(command, stdioOpenmode),
+                        "Cannot run command `"~command~"'"),
+                command, 1, true);
+    }
+
+/**
+First calls $(D detach) (throwing on failure), and then attempts to
+associate the given file descriptor with the $(D File). The mode must
+be compatible with the mode of the file descriptor.
+
+Throws: $(D ErrnoException) in case of error.
+ */
+    void fdopen(int fd, in char[] stdioOpenmode = "rb") @safe
+    {
+        fdopen(fd, stdioOpenmode, null);
+    }
+
+    package void fdopen(int fd, in char[] stdioOpenmode, string name) @trusted
+    {
+        import std.exception : errnoEnforce;
+        import std.internal.cstring : tempCString;
+
+        auto modez = stdioOpenmode.tempCString();
+        detach();
+
+        version (DIGITAL_MARS_STDIO)
+        {
+            // This is a re-implementation of DMC's fdopen, but without the
+            // mucking with the file descriptor.  POSIX standard requires the
+            // new fdopen'd file to retain the given file descriptor's
+            // position.
+            import core.stdc.stdio : fopen;
+            auto fp = fopen("NUL", modez);
+            errnoEnforce(fp, "Cannot open placeholder NUL stream");
+            FLOCK(fp);
+            auto iob = cast(_iobuf*) fp;
+            .close(iob._file);
+            iob._file = fd;
+            iob._flag &= ~_IOTRAN;
+            FUNLOCK(fp);
+        }
+        else
+        {
+            version (Windows) // MSVCRT
+                auto fp = _fdopen(fd, modez);
+            else version (Posix)
+            {
+                import core.sys.posix.stdio : fdopen;
+                auto fp = fdopen(fd, modez);
+            }
+            errnoEnforce(fp);
+        }
+        this = File(fp, name);
+    }
+
+    // Declare a dummy HANDLE to allow generating documentation
+    // for Windows-only methods.
+    version (StdDdoc) { version (Windows) {} else alias HANDLE = int; }
+
+/**
+First calls $(D detach) (throwing on failure), and then attempts to
+associate the given Windows $(D HANDLE) with the $(D File). The mode must
+be compatible with the access attributes of the handle. Windows only.
+
+Throws: $(D ErrnoException) in case of error.
+*/
+    version (StdDdoc)
+    void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode);
+
+    version (Windows)
+    void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode)
+    {
+        import core.stdc.stdint : intptr_t;
+        import std.exception : errnoEnforce;
+        import std.format : format;
+
+        // Create file descriptors from the handles
+        version (DIGITAL_MARS_STDIO)
+            auto fd = _handleToFD(handle, FHND_DEVICE);
+        else // MSVCRT
+        {
+            int mode;
+            modeLoop:
+            foreach (c; stdioOpenmode)
+                switch (c)
+                {
+                    case 'r': mode |= _O_RDONLY; break;
+                    case '+': mode &=~_O_RDONLY; break;
+                    case 'a': mode |= _O_APPEND; break;
+                    case 'b': mode |= _O_BINARY; break;
+                    case 't': mode |= _O_TEXT;   break;
+                    case ',': break modeLoop;
+                    default: break;
+                }
+
+            auto fd = _open_osfhandle(cast(intptr_t) handle, mode);
+        }
+
+        errnoEnforce(fd >= 0, "Cannot open Windows HANDLE");
+        fdopen(fd, stdioOpenmode, "HANDLE(%s)".format(handle));
+    }
+
+
+/** Returns $(D true) if the file is opened. */
+    @property bool isOpen() const @safe pure nothrow
+    {
+        return _p !is null && _p.handle;
+    }
+
+/**
+Returns $(D true) if the file is at end (see $(HTTP
+cplusplus.com/reference/clibrary/cstdio/feof.html, feof)).
+
+Throws: $(D Exception) if the file is not opened.
+ */
+    @property bool eof() const @trusted pure
+    {
+        import std.exception : enforce;
+
+        enforce(_p && _p.handle, "Calling eof() against an unopened file.");
+        return .feof(cast(FILE*) _p.handle) != 0;
+    }
+
+/** Returns the name of the last opened file, if any.
+If a $(D File) was created with $(LREF tmpfile) and $(LREF wrapFile)
+it has no name.*/
+    @property string name() const @safe pure nothrow
+    {
+        return _name;
+    }
+
+/**
+If the file is not opened, returns $(D true). Otherwise, returns
+$(HTTP cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for
+the file handle.
+ */
+    @property bool error() const @trusted pure nothrow
+    {
+        return !isOpen || .ferror(cast(FILE*) _p.handle);
+    }
+
+    @safe unittest
+    {
+        // Issue 12349
+        static import std.file;
+        auto deleteme = testFilename();
+        auto f = File(deleteme, "w");
+        scope(exit) std.file.remove(deleteme);
+
+        f.close();
+        assert(f.error);
+    }
+
+/**
+Detaches from the underlying file. If the sole owner, calls $(D close).
+
+Throws: $(D ErrnoException) on failure if closing the file.
+  */
+    void detach() @safe
+    {
+        if (!_p) return;
+        if (_p.refs == 1)
+            close();
+        else
+        {
+            assert(_p.refs);
+            --_p.refs;
+            _p = null;
+        }
+    }
+
+    @safe unittest
+    {
+        static import std.file;
+
+        auto deleteme = testFilename();
+        scope(exit) std.file.remove(deleteme);
+        auto f = File(deleteme, "w");
+        {
+            auto f2 = f;
+            f2.detach();
+        }
+        assert(f._p.refs == 1);
+        f.close();
+    }
+
+/**
+If the file was unopened, succeeds vacuously. Otherwise closes the
+file (by calling $(HTTP
+cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)),
+throwing on error. Even if an exception is thrown, afterwards the $(D
+File) object is empty. This is different from $(D detach) in that it
+always closes the file; consequently, all other $(D File) objects
+referring to the same handle will see a closed file henceforth.
+
+Throws: $(D ErrnoException) on error.
+ */
+    void close() @trusted
+    {
+        import core.stdc.stdlib : free;
+        import std.exception : errnoEnforce;
+
+        if (!_p) return; // succeed vacuously
+        scope(exit)
+        {
+            assert(_p.refs);
+            if (!--_p.refs)
+                free(_p);
+            _p = null; // start a new life
+        }
+        if (!_p.handle) return; // Impl is closed by another File
+
+        scope(exit) _p.handle = null; // nullify the handle anyway
+        version (Posix)
+        {
+            import core.sys.posix.stdio : pclose;
+            import std.format : format;
+
+            if (_p.isPopened)
+            {
+                auto res = pclose(_p.handle);
+                errnoEnforce(res != -1,
+                        "Could not close pipe `"~_name~"'");
+                errnoEnforce(res == 0, format("Command returned %d", res));
+                return;
+            }
+        }
+        errnoEnforce(.fclose(_p.handle) == 0,
+                "Could not close file `"~_name~"'");
+    }
+
+/**
+If the file is not opened, succeeds vacuously. Otherwise, returns
+$(HTTP cplusplus.com/reference/clibrary/cstdio/_clearerr.html,
+_clearerr) for the file handle.
+ */
+    void clearerr() @safe pure nothrow
+    {
+        _p is null || _p.handle is null ||
+        .clearerr(_p.handle);
+    }
+
+/**
+Flushes the C $(D FILE) buffers.
+
+Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush)
+for the file handle.
+
+Throws: $(D Exception) if the file is not opened or if the call to $(D fflush) fails.
+ */
+    void flush() @trusted
+    {
+        import std.exception : enforce, errnoEnforce;
+
+        enforce(isOpen, "Attempting to flush() in an unopened file");
+        errnoEnforce(.fflush(_p.handle) == 0);
+    }
+
+    @safe unittest
+    {
+        // Issue 12349
+        import std.exception : assertThrown;
+        static import std.file;
+
+        auto deleteme = testFilename();
+        auto f = File(deleteme, "w");
+        scope(exit) std.file.remove(deleteme);
+
+        f.close();
+        assertThrown(f.flush());
+    }
+
+/**
+Forces any data buffered by the OS to be written to disk.
+Call $(LREF flush) before calling this function to flush the C $(D FILE) buffers first.
+
+This function calls
+$(HTTP msdn.microsoft.com/en-us/library/windows/desktop/aa364439%28v=vs.85%29.aspx,
+$(D FlushFileBuffers)) on Windows and
+$(HTTP pubs.opengroup.org/onlinepubs/7908799/xsh/fsync.html,
+$(D fsync)) on POSIX for the file handle.
+
+Throws: $(D Exception) if the file is not opened or if the OS call fails.
+ */
+    void sync() @trusted
+    {
+        import std.exception : enforce;
+
+        enforce(isOpen, "Attempting to sync() an unopened file");
+
+        version (Windows)
+        {
+            import core.sys.windows.windows : FlushFileBuffers;
+            wenforce(FlushFileBuffers(windowsHandle), "FlushFileBuffers failed");
+        }
+        else
+        {
+            import core.sys.posix.unistd : fsync;
+            import std.exception : errnoEnforce;
+            errnoEnforce(fsync(fileno) == 0, "fsync failed");
+        }
+    }
+
+/**
+Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the
+file handle. The number of items to read and the size of
+each item is inferred from the size and type of the input array, respectively.
+
+Returns: The slice of $(D buffer) containing the data that was actually read.
+This will be shorter than $(D buffer) if EOF was reached before the buffer
+could be filled.
+
+Throws: $(D Exception) if $(D buffer) is empty.
+        $(D ErrnoException) if the file is not opened or the call to $(D fread) fails.
+
+$(D rawRead) always reads in binary mode on Windows.
+ */
+    T[] rawRead(T)(T[] buffer)
+    {
+        import std.exception : errnoEnforce;
+
+        if (!buffer.length)
+            throw new Exception("rawRead must take a non-empty buffer");
+        version (Windows)
+        {
+            immutable fd = ._fileno(_p.handle);
+            immutable mode = ._setmode(fd, _O_BINARY);
+            scope(exit) ._setmode(fd, mode);
+            version (DIGITAL_MARS_STDIO)
+            {
+                import core.atomic : atomicOp;
+
+                // @@@BUG@@@ 4243
+                immutable info = __fhnd_info[fd];
+                atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
+                scope(exit) __fhnd_info[fd] = info;
+            }
+        }
+        immutable freadResult = trustedFread(_p.handle, buffer);
+        assert(freadResult <= buffer.length); // fread return guarantee
+        if (freadResult != buffer.length) // error or eof
+        {
+            errnoEnforce(!error);
+            return buffer[0 .. freadResult];
+        }
+        return buffer;
+    }
+
+    ///
+    @system unittest
+    {
+        static import std.file;
+
+        auto testFile = testFilename();
+        std.file.write(testFile, "\r\n\n\r\n");
+        scope(exit) std.file.remove(testFile);
+
+        auto f = File(testFile, "r");
+        auto buf = f.rawRead(new char[5]);
+        f.close();
+        assert(buf == "\r\n\n\r\n");
+    }
+
+/**
+Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file
+handle. The number of items to write and the size of each
+item is inferred from the size and type of the input array, respectively. An
+error is thrown if the buffer could not be written in its entirety.
+
+$(D rawWrite) always writes in binary mode on Windows.
+
+Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwrite) fails.
+ */
+    void rawWrite(T)(in T[] buffer)
+    {
+        import std.conv : text;
+        import std.exception : errnoEnforce;
+
+        version (Windows)
+        {
+            flush(); // before changing translation mode
+            immutable fd = ._fileno(_p.handle);
+            immutable mode = ._setmode(fd, _O_BINARY);
+            scope(exit) ._setmode(fd, mode);
+            version (DIGITAL_MARS_STDIO)
+            {
+                import core.atomic : atomicOp;
+
+                // @@@BUG@@@ 4243
+                immutable info = __fhnd_info[fd];
+                atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
+                scope(exit) __fhnd_info[fd] = info;
+            }
+            scope(exit) flush(); // before restoring translation mode
+        }
+        auto result = trustedFwrite(_p.handle, buffer);
+        if (result == result.max) result = 0;
+        errnoEnforce(result == buffer.length,
+                text("Wrote ", result, " instead of ", buffer.length,
+                        " objects of type ", T.stringof, " to file `",
+                        _name, "'"));
+    }
+
+    ///
+    @system unittest
+    {
+        static import std.file;
+
+        auto testFile = testFilename();
+        auto f = File(testFile, "w");
+        scope(exit) std.file.remove(testFile);
+
+        f.rawWrite("\r\n\n\r\n");
+        f.close();
+        assert(std.file.read(testFile) == "\r\n\n\r\n");
+    }
+
+/**
+Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek)
+for the file handle.
+
+Throws: $(D Exception) if the file is not opened.
+        $(D ErrnoException) if the call to $(D fseek) fails.
+ */
+    void seek(long offset, int origin = SEEK_SET) @trusted
+    {
+        import std.conv : to, text;
+        import std.exception : enforce, errnoEnforce;
+
+        enforce(isOpen, "Attempting to seek() in an unopened file");
+        version (Windows)
+        {
+            version (CRuntime_Microsoft)
+            {
+                alias fseekFun = _fseeki64;
+                alias off_t = long;
+            }
+            else
+            {
+                alias fseekFun = fseek;
+                alias off_t = int;
+            }
+        }
+        else version (Posix)
+        {
+            import core.sys.posix.stdio : fseeko, off_t;
+            alias fseekFun = fseeko;
+        }
+        errnoEnforce(fseekFun(_p.handle, to!off_t(offset), origin) == 0,
+                "Could not seek in file `"~_name~"'");
+    }
+
+    @system unittest
+    {
+        import std.conv : text;
+        static import std.file;
+
+        auto deleteme = testFilename();
+        auto f = File(deleteme, "w+");
+        scope(exit) { f.close(); std.file.remove(deleteme); }
+        f.rawWrite("abcdefghijklmnopqrstuvwxyz");
+        f.seek(7);
+        assert(f.readln() == "hijklmnopqrstuvwxyz");
+
+        version (CRuntime_DigitalMars)
+            auto bigOffset = int.max - 100;
+        else
+        version (CRuntime_Bionic)
+            auto bigOffset = int.max - 100;
+        else
+            auto bigOffset = cast(ulong) int.max + 100;
+        f.seek(bigOffset);
+        assert(f.tell == bigOffset, text(f.tell));
+        // Uncomment the tests below only if you want to wait for
+        // a long time
+        // f.rawWrite("abcdefghijklmnopqrstuvwxyz");
+        // f.seek(-3, SEEK_END);
+        // assert(f.readln() == "xyz");
+    }
+
+/**
+Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/ftell.html, ftell) for the
+managed file handle.
+
+Throws: $(D Exception) if the file is not opened.
+        $(D ErrnoException) if the call to $(D ftell) fails.
+ */
+    @property ulong tell() const @trusted
+    {
+        import std.exception : enforce, errnoEnforce;
+
+        enforce(isOpen, "Attempting to tell() in an unopened file");
+        version (Windows)
+        {
+            version (CRuntime_Microsoft)
+                immutable result = _ftelli64(cast(FILE*) _p.handle);
+            else
+                immutable result = ftell(cast(FILE*) _p.handle);
+        }
+        else version (Posix)
+        {
+            import core.sys.posix.stdio : ftello;
+            immutable result = ftello(cast(FILE*) _p.handle);
+        }
+        errnoEnforce(result != -1,
+                "Query ftell() failed for file `"~_name~"'");
+        return result;
+    }
+
+    ///
+    @system unittest
+    {
+        import std.conv : text;
+        static import std.file;
+
+        auto testFile = testFilename();
+        std.file.write(testFile, "abcdefghijklmnopqrstuvwqxyz");
+        scope(exit) { std.file.remove(testFile); }
+
+        auto f = File(testFile);
+        auto a = new ubyte[4];
+        f.rawRead(a);
+        assert(f.tell == 4, text(f.tell));
+    }
+
+/**
+Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind)
+for the file handle.
+
+Throws: $(D Exception) if the file is not opened.
+ */
+    void rewind() @safe
+    {
+        import std.exception : enforce;
+
+        enforce(isOpen, "Attempting to rewind() an unopened file");
+        .rewind(_p.handle);
+    }
+
+/**
+Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for
+the file handle.
+
+Throws: $(D Exception) if the file is not opened.
+        $(D ErrnoException) if the call to $(D setvbuf) fails.
+ */
+    void setvbuf(size_t size, int mode = _IOFBF) @trusted
+    {
+        import std.exception : enforce, errnoEnforce;
+
+        enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
+        errnoEnforce(.setvbuf(_p.handle, null, mode, size) == 0,
+                "Could not set buffering for file `"~_name~"'");
+    }
+
+/**
+Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html,
+_setvbuf) for the file handle.
+
+Throws: $(D Exception) if the file is not opened.
+        $(D ErrnoException) if the call to $(D setvbuf) fails.
+*/
+    void setvbuf(void[] buf, int mode = _IOFBF) @trusted
+    {
+        import std.exception : enforce, errnoEnforce;
+
+        enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
+        errnoEnforce(.setvbuf(_p.handle,
+                        cast(char*) buf.ptr, mode, buf.length) == 0,
+                "Could not set buffering for file `"~_name~"'");
+    }
+
+
+    version (Windows)
+    {
+        import core.sys.windows.windows : ULARGE_INTEGER, OVERLAPPED, BOOL;
+
+        private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length,
+            Flags flags)
+        {
+            if (!start && !length)
+                length = ulong.max;
+            ULARGE_INTEGER liStart = void, liLength = void;
+            liStart.QuadPart = start;
+            liLength.QuadPart = length;
+            OVERLAPPED overlapped;
+            overlapped.Offset = liStart.LowPart;
+            overlapped.OffsetHigh = liStart.HighPart;
+            overlapped.hEvent = null;
+            return F(windowsHandle, flags, 0, liLength.LowPart,
+                liLength.HighPart, &overlapped);
+        }
+
+        private static T wenforce(T)(T cond, string str)
+        {
+            import core.sys.windows.windows : GetLastError;
+            import std.windows.syserror : sysErrorString;
+
+            if (cond) return cond;
+            throw new Exception(str ~ ": " ~ sysErrorString(GetLastError()));
+        }
+    }
+    version (Posix)
+    {
+        private int lockImpl(int operation, short l_type,
+            ulong start, ulong length)
+        {
+            import core.sys.posix.fcntl : fcntl, flock, off_t;
+            import core.sys.posix.unistd : getpid;
+            import std.conv : to;
+
+            flock fl = void;
+            fl.l_type   = l_type;
+            fl.l_whence = SEEK_SET;
+            fl.l_start  = to!off_t(start);
+            fl.l_len    = to!off_t(length);
+            fl.l_pid    = getpid();
+            return fcntl(fileno, operation, &fl);
+        }
+    }
+
+/**
+Locks the specified file segment. If the file segment is already locked
+by another process, waits until the existing lock is released.
+If both $(D start) and $(D length) are zero, the entire file is locked.
+
+Locks created using $(D lock) and $(D tryLock) have the following properties:
+$(UL
+ $(LI All locks are automatically released when the process terminates.)
+ $(LI Locks are not inherited by child processes.)
+ $(LI Closing a file will release all locks associated with the file. On POSIX,
+      even locks acquired via a different $(D File) will be released as well.)
+ $(LI Not all NFS implementations correctly implement file locking.)
+)
+ */
+    void lock(LockType lockType = LockType.readWrite,
+        ulong start = 0, ulong length = 0)
+    {
+        import std.exception : enforce;
+
+        enforce(isOpen, "Attempting to call lock() on an unopened file");
+        version (Posix)
+        {
+            import core.sys.posix.fcntl : F_RDLCK, F_SETLKW, F_WRLCK;
+            import std.exception : errnoEnforce;
+            immutable short type = lockType == LockType.readWrite
+                ? F_WRLCK : F_RDLCK;
+            errnoEnforce(lockImpl(F_SETLKW, type, start, length) != -1,
+                    "Could not set lock for file `"~_name~"'");
+        }
+        else
+        version (Windows)
+        {
+            import core.sys.windows.windows : LockFileEx, LOCKFILE_EXCLUSIVE_LOCK;
+            immutable type = lockType == LockType.readWrite ?
+                LOCKFILE_EXCLUSIVE_LOCK : 0;
+            wenforce(lockImpl!LockFileEx(start, length, type),
+                    "Could not set lock for file `"~_name~"'");
+        }
+        else
+            static assert(false);
+    }
+
+/**
+Attempts to lock the specified file segment.
+If both $(D start) and $(D length) are zero, the entire file is locked.
+Returns: $(D true) if the lock was successful, and $(D false) if the
+specified file segment was already locked.
+ */
+    bool tryLock(LockType lockType = LockType.readWrite,
+        ulong start = 0, ulong length = 0)
+    {
+        import std.exception : enforce;
+
+        enforce(isOpen, "Attempting to call tryLock() on an unopened file");
+        version (Posix)
+        {
+            import core.stdc.errno : EACCES, EAGAIN, errno;
+            import core.sys.posix.fcntl : F_RDLCK, F_SETLK, F_WRLCK;
+            import std.exception : errnoEnforce;
+            immutable short type = lockType == LockType.readWrite
+                ? F_WRLCK : F_RDLCK;
+            immutable res = lockImpl(F_SETLK, type, start, length);
+            if (res == -1 && (errno == EACCES || errno == EAGAIN))
+                return false;
+            errnoEnforce(res != -1, "Could not set lock for file `"~_name~"'");
+            return true;
+        }
+        else
+        version (Windows)
+        {
+            import core.sys.windows.windows : GetLastError, LockFileEx, LOCKFILE_EXCLUSIVE_LOCK,
+                ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, LOCKFILE_FAIL_IMMEDIATELY;
+            immutable type = lockType == LockType.readWrite
+                ? LOCKFILE_EXCLUSIVE_LOCK : 0;
+            immutable res = lockImpl!LockFileEx(start, length,
+                type | LOCKFILE_FAIL_IMMEDIATELY);
+            if (!res && (GetLastError() == ERROR_IO_PENDING
+                || GetLastError() == ERROR_LOCK_VIOLATION))
+                return false;
+            wenforce(res, "Could not set lock for file `"~_name~"'");
+            return true;
+        }
+        else
+            static assert(false);
+    }
+
+/**
+Removes the lock over the specified file segment.
+ */
+    void unlock(ulong start = 0, ulong length = 0)
+    {
+        import std.exception : enforce;
+
+        enforce(isOpen, "Attempting to call unlock() on an unopened file");
+        version (Posix)
+        {
+            import core.sys.posix.fcntl : F_SETLK, F_UNLCK;
+            import std.exception : errnoEnforce;
+            errnoEnforce(lockImpl(F_SETLK, F_UNLCK, start, length) != -1,
+                    "Could not remove lock for file `"~_name~"'");
+        }
+        else
+        version (Windows)
+        {
+            import core.sys.windows.windows : UnlockFileEx;
+            wenforce(lockImpl!UnlockFileEx(start, length),
+                "Could not remove lock for file `"~_name~"'");
+        }
+        else
+            static assert(false);
+    }
+
+    version (Windows)
+    @system unittest
+    {
+        static import std.file;
+        auto deleteme = testFilename();
+        scope(exit) std.file.remove(deleteme);
+        auto f = File(deleteme, "wb");
+        assert(f.tryLock());
+        auto g = File(deleteme, "wb");
+        assert(!g.tryLock());
+        assert(!g.tryLock(LockType.read));
+        f.unlock();
+        f.lock(LockType.read);
+        assert(!g.tryLock());
+        assert(g.tryLock(LockType.read));
+        f.unlock();
+        g.unlock();
+    }
+
+    version (Posix)
+    @system unittest
+    {
+        static import std.file;
+        auto deleteme = testFilename();
+        scope(exit) std.file.remove(deleteme);
+
+        // Since locks are per-process, we cannot test lock failures within
+        // the same process. fork() is used to create a second process.
+        static void runForked(void delegate() code)
+        {
+            import core.stdc.stdlib : exit;
+            import core.sys.posix.sys.wait : wait;
+            import core.sys.posix.unistd : fork;
+            int child, status;
+            if ((child = fork()) == 0)
+            {
+                code();
+                exit(0);
+            }
+            else
+            {
+                assert(wait(&status) != -1);
+                assert(status == 0, "Fork crashed");
+            }
+        }
+
+        auto f = File(deleteme, "w+b");
+
+        runForked
+        ({
+            auto g = File(deleteme, "a+b");
+            assert(g.tryLock());
+            g.unlock();
+            assert(g.tryLock(LockType.read));
+        });
+
+        assert(f.tryLock());
+        runForked
+        ({
+            auto g = File(deleteme, "a+b");
+            assert(!g.tryLock());
+            assert(!g.tryLock(LockType.read));
+        });
+        f.unlock();
+
+        f.lock(LockType.read);
+        runForked
+        ({
+            auto g = File(deleteme, "a+b");
+            assert(!g.tryLock());
+            assert(g.tryLock(LockType.read));
+            g.unlock();
+        });
+        f.unlock();
+    }
+
+
+/**
+Writes its arguments in text format to the file.
+
+Throws: $(D Exception) if the file is not opened.
+        $(D ErrnoException) on an error writing to the file.
+*/
+    void write(S...)(S args)
+    {
+        import std.traits : isBoolean, isIntegral, isAggregateType;
+        auto w = lockingTextWriter();
+        foreach (arg; args)
+        {
+            alias A = typeof(arg);
+            static if (isAggregateType!A || is(A == enum))
+            {
+                import std.format : formattedWrite;
+
+                formattedWrite(w, "%s", arg);
+            }
+            else static if (isSomeString!A)
+            {
+                put(w, arg);
+            }
+            else static if (isIntegral!A)
+            {
+                import std.conv : toTextRange;
+
+                toTextRange(arg, w);
+            }
+            else static if (isBoolean!A)
+            {
+                put(w, arg ? "true" : "false");
+            }
+            else static if (isSomeChar!A)
+            {
+                put(w, arg);
+            }
+            else
+            {
+                import std.format : formattedWrite;
+
+                // Most general case
+                formattedWrite(w, "%s", arg);
+            }
+        }
+    }
+
+/**
+Writes its arguments in text format to the file, followed by a newline.
+
+Throws: $(D Exception) if the file is not opened.
+        $(D ErrnoException) on an error writing to the file.
+*/
+    void writeln(S...)(S args)
+    {
+        write(args, '\n');
+    }
+
+/**
+Writes its arguments in text format to the file, according to the
+format string fmt.
+
+Params:
+fmt = The $(LINK2 std_format.html#format-string, format string).
+When passed as a compile-time argument, the string will be statically checked
+against the argument types passed.
+args = Items to write.
+
+Throws: $(D Exception) if the file is not opened.
+        $(D ErrnoException) on an error writing to the file.
+*/
+    void writef(alias fmt, A...)(A args)
+    if (isSomeString!(typeof(fmt)))
+    {
+        import std.format : checkFormatException;
+
+        alias e = checkFormatException!(fmt, A);
+        static assert(!e, e.msg);
+        return this.writef(fmt, args);
+    }
+
+    /// ditto
+    void writef(Char, A...)(in Char[] fmt, A args)
+    {
+        import std.format : formattedWrite;
+
+        formattedWrite(lockingTextWriter(), fmt, args);
+    }
+
+    /// Equivalent to `file.writef(fmt, args, '\n')`.
+    void writefln(alias fmt, A...)(A args)
+    if (isSomeString!(typeof(fmt)))
+    {
+        import std.format : checkFormatException;
+
+        alias e = checkFormatException!(fmt, A);
+        static assert(!e, e.msg);
+        return this.writefln(fmt, args);
+    }
+
+    /// ditto
+    void writefln(Char, A...)(in Char[] fmt, A args)
+    {
+        import std.format : formattedWrite;
+
+        auto w = lockingTextWriter();
+        formattedWrite(w, fmt, args);
+        w.put('\n');
+    }
+
+/**
+Read line from the file handle and return it as a specified type.
+
+This version manages its own read buffer, which means one memory allocation per call. If you are not
+retaining a reference to the read data, consider the $(D File.readln(buf)) version, which may offer
+better performance as it can reuse its read buffer.
+
+Params:
+    S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
+    terminator = Line terminator (by default, $(D '\n')).
+
+Note:
+    String terminators are not supported due to ambiguity with readln(buf) below.
+
+Returns:
+    The line that was read, including the line terminator character.
+
+Throws:
+    $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
+
+Example:
+---
+// Reads `stdin` and writes it to `stdout`.
+import std.stdio;
+
+void main()
+{
+    string line;
+    while ((line = stdin.readln()) !is null)
+        write(line);
+}
+---
+*/
+    S readln(S = string)(dchar terminator = '\n')
+    if (isSomeString!S)
+    {
+        Unqual!(ElementEncodingType!S)[] buf;
+        readln(buf, terminator);
+        return cast(S) buf;
+    }
+
+    @system unittest
+    {
+        import std.algorithm.comparison : equal;
+        static import std.file;
+        import std.meta : AliasSeq;
+
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "hello\nworld\n");
+        scope(exit) std.file.remove(deleteme);
+        foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
+        {
+            auto witness = [ "hello\n", "world\n" ];
+            auto f = File(deleteme);
+            uint i = 0;
+            String buf;
+            while ((buf = f.readln!String()).length)
+            {
+                assert(i < witness.length);
+                assert(equal(buf, witness[i++]));
+            }
+            assert(i == witness.length);
+        }
+    }
+
+    @system unittest
+    {
+        static import std.file;
+        import std.typecons : Tuple;
+
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "cześć \U0002000D");
+        scope(exit) std.file.remove(deleteme);
+        uint[] lengths = [12,8,7];
+        foreach (uint i, C; Tuple!(char, wchar, dchar).Types)
+        {
+            immutable(C)[] witness = "cześć \U0002000D";
+            auto buf = File(deleteme).readln!(immutable(C)[])();
+            assert(buf.length == lengths[i]);
+            assert(buf == witness);
+        }
+    }
+
+/**
+Read line from the file handle and write it to $(D buf[]), including
+terminating character.
+
+This can be faster than $(D line = File.readln()) because you can reuse
+the buffer for each call. Note that reusing the buffer means that you
+must copy the previous contents if you wish to retain them.
+
+Params:
+buf = Buffer used to store the resulting line data. buf is
+resized as necessary.
+terminator = Line terminator (by default, $(D '\n')). Use
+$(REF newline, std,ascii) for portability (unless the file was opened in
+text mode).
+
+Returns:
+0 for end of file, otherwise number of characters read
+
+Throws: $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode
+conversion error.
+
+Example:
+---
+// Read lines from `stdin` into a string
+// Ignore lines starting with '#'
+// Write the string to `stdout`
+
+void main()
+{
+    string output;
+    char[] buf;
+
+    while (stdin.readln(buf))
+    {
+        if (buf[0] == '#')
+            continue;
+
+        output ~= buf;
+    }
+
+    write(output);
+}
+---
+
+This method can be more efficient than the one in the previous example
+because $(D stdin.readln(buf)) reuses (if possible) memory allocated
+for $(D buf), whereas $(D line = stdin.readln()) makes a new memory allocation
+for every line.
+
+For even better performance you can help $(D readln) by passing in a
+large buffer to avoid memory reallocations. This can be done by reusing the
+largest buffer returned by $(D readln):
+
+Example:
+---
+// Read lines from `stdin` and count words
+
+void main()
+{
+    char[] buf;
+    size_t words = 0;
+
+    while (!stdin.eof)
+    {
+        char[] line = buf;
+        stdin.readln(line);
+        if (line.length > buf.length)
+            buf = line;
+
+        words += line.split.length;
+    }
+
+    writeln(words);
+}
+---
+This is actually what $(LREF byLine) does internally, so its usage
+is recommended if you want to process a complete file.
+*/
+    size_t readln(C)(ref C[] buf, dchar terminator = '\n')
+    if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
+    {
+        import std.exception : enforce;
+
+        static if (is(C == char))
+        {
+            enforce(_p && _p.handle, "Attempt to read from an unopened file.");
+            if (_p.orientation == Orientation.unknown)
+            {
+                import core.stdc.wchar_ : fwide;
+                auto w = fwide(_p.handle, 0);
+                if (w < 0) _p.orientation = Orientation.narrow;
+                else if (w > 0) _p.orientation = Orientation.wide;
+            }
+            return readlnImpl(_p.handle, buf, terminator, _p.orientation);
+        }
+        else
+        {
+            // TODO: optimize this
+            string s = readln(terminator);
+            buf.length = 0;
+            if (!s.length) return 0;
+            foreach (C c; s)
+            {
+                buf ~= c;
+            }
+            return buf.length;
+        }
+    }
+
+    @system unittest
+    {
+        // @system due to readln
+        static import std.file;
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "123\n456789");
+        scope(exit) std.file.remove(deleteme);
+
+        auto file = File(deleteme);
+        char[] buffer = new char[10];
+        char[] line = buffer;
+        file.readln(line);
+        auto beyond = line.length;
+        buffer[beyond] = 'a';
+        file.readln(line); // should not write buffer beyond line
+        assert(buffer[beyond] == 'a');
+    }
+
+    @system unittest // bugzilla 15293
+    {
+        // @system due to readln
+        static import std.file;
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "a\n\naa");
+        scope(exit) std.file.remove(deleteme);
+
+        auto file = File(deleteme);
+        char[] buffer;
+        char[] line;
+
+        file.readln(buffer, '\n');
+
+        line = buffer;
+        file.readln(line, '\n');
+
+        line = buffer;
+        file.readln(line, '\n');
+
+        assert(line[0 .. 1].capacity == 0);
+    }
+
+/** ditto */
+    size_t readln(C, R)(ref C[] buf, R terminator)
+    if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
+        isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
+    {
+        import std.algorithm.mutation : swap;
+        import std.algorithm.searching : endsWith;
+        import std.range.primitives : back;
+
+        auto last = terminator.back;
+        C[] buf2;
+        swap(buf, buf2);
+        for (;;)
+        {
+            if (!readln(buf2, last) || endsWith(buf2, terminator))
+            {
+                if (buf.empty)
+                {
+                    buf = buf2;
+                }
+                else
+                {
+                    buf ~= buf2;
+                }
+                break;
+            }
+            buf ~= buf2;
+        }
+        return buf.length;
+    }
+
+    @system unittest
+    {
+        static import std.file;
+        import std.typecons : Tuple;
+
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "hello\n\rworld\nhow\n\rare ya");
+        scope(exit) std.file.remove(deleteme);
+        foreach (C; Tuple!(char, wchar, dchar).Types)
+        {
+            immutable(C)[][] witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ];
+            auto f = File(deleteme);
+            uint i = 0;
+            C[] buf;
+            while (f.readln(buf, "\n\r"))
+            {
+                assert(i < witness.length);
+                assert(buf == witness[i++]);
+            }
+            assert(buf.length == 0);
+        }
+    }
+
+    /**
+     * Reads formatted _data from the file using $(REF formattedRead, std,_format).
+     * Params:
+     * format = The $(LINK2 std_format.html#_format-string, _format string).
+     * When passed as a compile-time argument, the string will be statically checked
+     * against the argument types passed.
+     * data = Items to be read.
+     * Example:
+----
+// test.d
+void main()
+{
+    import std.stdio;
+    auto f = File("input");
+    foreach (_; 0 .. 3)
+    {
+        int a;
+        f.readf!" %d"(a);
+        writeln(++a);
+    }
+}
+----
+$(CONSOLE
+% echo "1 2 3" > input
+% rdmd test.d
+2
+3
+4
+)
+     */
+    uint readf(alias format, Data...)(auto ref Data data)
+    if (isSomeString!(typeof(format)))
+    {
+        import std.format : checkFormatException;
+
+        alias e = checkFormatException!(format, Data);
+        static assert(!e, e.msg);
+        return this.readf(format, data);
+    }
+
+    /// ditto
+    uint readf(Data...)(in char[] format, auto ref Data data)
+    {
+        import std.format : formattedRead;
+
+        assert(isOpen);
+        auto input = LockingTextReader(this);
+        return formattedRead(input, format, data);
+    }
+
+    ///
+    @system unittest
+    {
+        static import std.file;
+
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
+        scope(exit) std.file.remove(deleteme);
+        string s;
+        auto f = File(deleteme);
+        f.readf!"%s\n"(s);
+        assert(s == "hello", "["~s~"]");
+        f.readf("%s\n", s);
+        assert(s == "world", "["~s~"]");
+
+        bool b1, b2;
+        f.readf("%s\n%s\n", b1, b2);
+        assert(b1 == true && b2 == false);
+    }
+
+    // backwards compatibility with pointers
+    @system unittest
+    {
+        // @system due to readf
+        static import std.file;
+
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
+        scope(exit) std.file.remove(deleteme);
+        string s;
+        auto f = File(deleteme);
+        f.readf("%s\n", &s);
+        assert(s == "hello", "["~s~"]");
+        f.readf("%s\n", &s);
+        assert(s == "world", "["~s~"]");
+
+        // Issue 11698
+        bool b1, b2;
+        f.readf("%s\n%s\n", &b1, &b2);
+        assert(b1 == true && b2 == false);
+    }
+
+    // backwards compatibility (mixed)
+    @system unittest
+    {
+        // @system due to readf
+        static import std.file;
+
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
+        scope(exit) std.file.remove(deleteme);
+        string s1, s2;
+        auto f = File(deleteme);
+        f.readf("%s\n%s\n", s1, &s2);
+        assert(s1 == "hello");
+        assert(s2 == "world");
+
+        // Issue 11698
+        bool b1, b2;
+        f.readf("%s\n%s\n", &b1, b2);
+        assert(b1 == true && b2 == false);
+    }
+
+    // Issue 12260 - Nice error of std.stdio.readf with newlines
+    @system unittest
+    {
+        static import std.file;
+
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "1\n2");
+        scope(exit) std.file.remove(deleteme);
+        int input;
+        auto f = File(deleteme);
+        f.readf("%s", &input);
+
+        import std.conv : ConvException;
+        import std.exception : collectException;
+        assert(collectException!ConvException(f.readf("%s", &input)).msg ==
+            "Unexpected '\\n' when converting from type LockingTextReader to type int");
+    }
+
+/**
+ Returns a temporary file by calling
+ $(HTTP cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile).
+ Note that the created file has no $(LREF name).*/
+    static File tmpfile() @safe
+    {
+        import std.exception : errnoEnforce;
+
+        return File(errnoEnforce(.tmpfile(),
+                "Could not create temporary file with tmpfile()"),
+            null);
+    }
+
+/**
+Unsafe function that wraps an existing $(D FILE*). The resulting $(D
+File) never takes the initiative in closing the file.
+Note that the created file has no $(LREF name)*/
+    /*private*/ static File wrapFile(FILE* f) @safe
+    {
+        import std.exception : enforce;
+
+        return File(enforce(f, "Could not wrap null FILE*"),
+            null, /*uint.max / 2*/ 9999);
+    }
+
+/**
+Returns the $(D FILE*) corresponding to this object.
+ */
+    FILE* getFP() @safe pure
+    {
+        import std.exception : enforce;
+
+        enforce(_p && _p.handle,
+                "Attempting to call getFP() on an unopened file");
+        return _p.handle;
+    }
+
+    @system unittest
+    {
+        static import core.stdc.stdio;
+        assert(stdout.getFP() == core.stdc.stdio.stdout);
+    }
+
+/**
+Returns the file number corresponding to this object.
+ */
+    @property int fileno() const @trusted
+    {
+        import std.exception : enforce;
+
+        enforce(isOpen, "Attempting to call fileno() on an unopened file");
+        return .fileno(cast(FILE*) _p.handle);
+    }
+
+/**
+Returns the underlying operating system $(D HANDLE) (Windows only).
+*/
+    version (StdDdoc)
+    @property HANDLE windowsHandle();
+
+    version (Windows)
+    @property HANDLE windowsHandle()
+    {
+        version (DIGITAL_MARS_STDIO)
+            return _fdToHandle(fileno);
+        else
+            return cast(HANDLE)_get_osfhandle(fileno);
+    }
+
+
+// Note: This was documented until 2013/08
+/*
+Range that reads one line at a time.  Returned by $(LREF byLine).
+
+Allows to directly use range operations on lines of a file.
+*/
+    struct ByLine(Char, Terminator)
+    {
+    private:
+        import std.typecons : RefCounted, RefCountedAutoInitialize;
+
+        /* Ref-counting stops the source range's Impl
+         * from getting out of sync after the range is copied, e.g.
+         * when accessing range.front, then using std.range.take,
+         * then accessing range.front again. */
+        alias PImpl = RefCounted!(Impl, RefCountedAutoInitialize.no);
+        PImpl impl;
+
+        static if (isScalarType!Terminator)
+            enum defTerm = '\n';
+        else
+            enum defTerm = cast(Terminator)"\n";
+
+    public:
+        this(File f, KeepTerminator kt = No.keepTerminator,
+                Terminator terminator = defTerm)
+        {
+            impl = PImpl(f, kt, terminator);
+        }
+
+        @property bool empty()
+        {
+            return impl.refCountedPayload.empty;
+        }
+
+        @property Char[] front()
+        {
+            return impl.refCountedPayload.front;
+        }
+
+        void popFront()
+        {
+            impl.refCountedPayload.popFront();
+        }
+
+    private:
+        struct Impl
+        {
+        private:
+            File file;
+            Char[] line;
+            Char[] buffer;
+            Terminator terminator;
+            KeepTerminator keepTerminator;
+
+        public:
+            this(File f, KeepTerminator kt, Terminator terminator)
+            {
+                file = f;
+                this.terminator = terminator;
+                keepTerminator = kt;
+                popFront();
+            }
+
+            // Range primitive implementations.
+            @property bool empty()
+            {
+                return line is null;
+            }
+
+            @property Char[] front()
+            {
+                return line;
+            }
+
+            void popFront()
+            {
+                import std.algorithm.searching : endsWith;
+                assert(file.isOpen);
+                line = buffer;
+                file.readln(line, terminator);
+                if (line.length > buffer.length)
+                {
+                    buffer = line;
+                }
+                if (line.empty)
+                {
+                    file.detach();
+                    line = null;
+                }
+                else if (keepTerminator == No.keepTerminator
+                        && endsWith(line, terminator))
+                {
+                    static if (isScalarType!Terminator)
+                        enum tlen = 1;
+                    else static if (isArray!Terminator)
+                    {
+                        static assert(
+                            is(Unqual!(ElementEncodingType!Terminator) == Char));
+                        const tlen = terminator.length;
+                    }
+                    else
+                        static assert(false);
+                    line = line[0 .. line.length - tlen];
+                }
+            }
+        }
+    }
+
+/**
+Returns an input range set up to read from the file handle one line
+at a time.
+
+The element type for the range will be $(D Char[]). Range primitives
+may throw $(D StdioException) on I/O error.
+
+Note:
+Each $(D front) will not persist after $(D
+popFront) is called, so the caller must copy its contents (e.g. by
+calling $(D to!string)) when retention is needed. If the caller needs
+to retain a copy of every line, use the $(LREF byLineCopy) function
+instead.
+
+Params:
+Char = Character type for each line, defaulting to $(D char).
+keepTerminator = Use $(D Yes.keepTerminator) to include the
+terminator at the end of each line.
+terminator = Line separator ($(D '\n') by default). Use
+$(REF newline, std,ascii) for portability (unless the file was opened in
+text mode).
+
+Example:
+----
+import std.algorithm, std.stdio, std.string;
+// Count words in a file using ranges.
+void main()
+{
+    auto file = File("file.txt"); // Open for reading
+    const wordCount = file.byLine()            // Read lines
+                          .map!split           // Split into words
+                          .map!(a => a.length) // Count words per line
+                          .sum();              // Total word count
+    writeln(wordCount);
+}
+----
+
+Example:
+----
+import std.range, std.stdio;
+// Read lines using foreach.
+void main()
+{
+    auto file = File("file.txt"); // Open for reading
+    auto range = file.byLine();
+    // Print first three lines
+    foreach (line; range.take(3))
+        writeln(line);
+    // Print remaining lines beginning with '#'
+    foreach (line; range)
+    {
+        if (!line.empty && line[0] == '#')
+            writeln(line);
+    }
+}
+----
+Notice that neither example accesses the line data returned by
+$(D front) after the corresponding $(D popFront) call is made (because
+the contents may well have changed).
+*/
+    auto byLine(Terminator = char, Char = char)
+            (KeepTerminator keepTerminator = No.keepTerminator,
+            Terminator terminator = '\n')
+    if (isScalarType!Terminator)
+    {
+        return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
+    }
+
+/// ditto
+    auto byLine(Terminator, Char = char)
+            (KeepTerminator keepTerminator, Terminator terminator)
+    if (is(Unqual!(ElementEncodingType!Terminator) == Char))
+    {
+        return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
+    }
+
+    @system unittest
+    {
+        static import std.file;
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "hi");
+        scope(success) std.file.remove(deleteme);
+
+        import std.meta : AliasSeq;
+        foreach (T; AliasSeq!(char, wchar, dchar))
+        {
+            auto blc = File(deleteme).byLine!(T, T);
+            assert(blc.front == "hi");
+            // check front is cached
+            assert(blc.front is blc.front);
+        }
+    }
+
+    private struct ByLineCopy(Char, Terminator)
+    {
+    private:
+        import std.typecons : RefCounted, RefCountedAutoInitialize;
+
+        /* Ref-counting stops the source range's ByLineCopyImpl
+         * from getting out of sync after the range is copied, e.g.
+         * when accessing range.front, then using std.range.take,
+         * then accessing range.front again. */
+        alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator),
+            RefCountedAutoInitialize.no);
+        Impl impl;
+
+    public:
+        this(File f, KeepTerminator kt, Terminator terminator)
+        {
+            impl = Impl(f, kt, terminator);
+        }
+
+        @property bool empty()
+        {
+            return impl.refCountedPayload.empty;
+        }
+
+        @property Char[] front()
+        {
+            return impl.refCountedPayload.front;
+        }
+
+        void popFront()
+        {
+            impl.refCountedPayload.popFront();
+        }
+    }
+
+    private struct ByLineCopyImpl(Char, Terminator)
+    {
+        ByLine!(Unqual!Char, Terminator).Impl impl;
+        bool gotFront;
+        Char[] line;
+
+    public:
+        this(File f, KeepTerminator kt, Terminator terminator)
+        {
+            impl = ByLine!(Unqual!Char, Terminator).Impl(f, kt, terminator);
+        }
+
+        @property bool empty()
+        {
+            return impl.empty;
+        }
+
+        @property front()
+        {
+            if (!gotFront)
+            {
+                line = impl.front.dup;
+                gotFront = true;
+            }
+            return line;
+        }
+
+        void popFront()
+        {
+            impl.popFront();
+            gotFront = false;
+        }
+    }
+
+/**
+Returns an input range set up to read from the file handle one line
+at a time. Each line will be newly allocated. $(D front) will cache
+its value to allow repeated calls without unnecessary allocations.
+
+Note: Due to caching byLineCopy can be more memory-efficient than
+$(D File.byLine.map!idup).
+
+The element type for the range will be $(D Char[]). Range
+primitives may throw $(D StdioException) on I/O error.
+
+Params:
+Char = Character type for each line, defaulting to $(D immutable char).
+keepTerminator = Use $(D Yes.keepTerminator) to include the
+terminator at the end of each line.
+terminator = Line separator ($(D '\n') by default). Use
+$(REF newline, std,ascii) for portability (unless the file was opened in
+text mode).
+
+Example:
+----
+import std.algorithm, std.array, std.stdio;
+// Print sorted lines of a file.
+void main()
+{
+    auto sortedLines = File("file.txt")   // Open for reading
+                       .byLineCopy()      // Read persistent lines
+                       .array()           // into an array
+                       .sort();           // then sort them
+    foreach (line; sortedLines)
+        writeln(line);
+}
+----
+See_Also:
+$(REF readText, std,file)
+*/
+    auto byLineCopy(Terminator = char, Char = immutable char)
+            (KeepTerminator keepTerminator = No.keepTerminator,
+            Terminator terminator = '\n')
+    if (isScalarType!Terminator)
+    {
+        return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
+    }
+
+/// ditto
+    auto byLineCopy(Terminator, Char = immutable char)
+            (KeepTerminator keepTerminator, Terminator terminator)
+    if (is(Unqual!(ElementEncodingType!Terminator) == Unqual!Char))
+    {
+        return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
+    }
+
+    @safe unittest
+    {
+        static assert(is(typeof(File("").byLine.front) == char[]));
+        static assert(is(typeof(File("").byLineCopy.front) == string));
+        static assert(
+            is(typeof(File("").byLineCopy!(char, char).front) == char[]));
+    }
+
+    @system unittest
+    {
+        import std.algorithm.comparison : equal;
+        static import std.file;
+
+        scope(failure) printf("Failed test at line %d\n", __LINE__);
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "");
+        scope(success) std.file.remove(deleteme);
+
+        // Test empty file
+        auto f = File(deleteme);
+        foreach (line; f.byLine())
+        {
+            assert(false);
+        }
+        f.detach();
+        assert(!f.isOpen);
+
+        void test(Terminator)(string txt, in string[] witness,
+                KeepTerminator kt, Terminator term, bool popFirstLine = false)
+        {
+            import std.algorithm.sorting : sort;
+            import std.array : array;
+            import std.conv : text;
+            import std.range.primitives : walkLength;
+
+            uint i;
+            std.file.write(deleteme, txt);
+            auto f = File(deleteme);
+            scope(exit)
+            {
+                f.close();
+                assert(!f.isOpen);
+            }
+            auto lines = f.byLine(kt, term);
+            if (popFirstLine)
+            {
+                lines.popFront();
+                i = 1;
+            }
+            assert(lines.empty || lines.front is lines.front);
+            foreach (line; lines)
+            {
+                assert(line == witness[i++]);
+            }
+            assert(i == witness.length, text(i, " != ", witness.length));
+
+            // Issue 11830
+            auto walkedLength = File(deleteme).byLine(kt, term).walkLength;
+            assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length));
+
+            // test persistent lines
+            assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort());
+        }
+
+        KeepTerminator kt = No.keepTerminator;
+        test("", null, kt, '\n');
+        test("\n", [ "" ], kt, '\n');
+        test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n');
+        test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n', true);
+        test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ], kt, '\n');
+        test("foo", [ "foo" ], kt, '\n', true);
+        test("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"],
+            kt, "\r\n");
+        test("sue\r", ["sue"], kt, '\r');
+
+        kt = Yes.keepTerminator;
+        test("", null, kt, '\n');
+        test("\n", [ "\n" ], kt, '\n');
+        test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt, '\n');
+        test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n');
+        test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n', true);
+        test("foo", [ "foo" ], kt, '\n');
+        test("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"],
+            kt, "\r\n");
+        test("sue\r", ["sue\r"], kt, '\r');
+    }
+
+    @system unittest
+    {
+        import std.algorithm.comparison : equal;
+        import std.range : drop, take;
+
+        version (Win64)
+        {
+            static import std.file;
+
+            /* the C function tmpfile doesn't seem to work, even when called from C */
+            auto deleteme = testFilename();
+            auto file = File(deleteme, "w+");
+            scope(success) std.file.remove(deleteme);
+        }
+        else version (CRuntime_Bionic)
+        {
+            static import std.file;
+
+            /* the C function tmpfile doesn't work when called from a shared
+               library apk:
+               https://code.google.com/p/android/issues/detail?id=66815 */
+            auto deleteme = testFilename();
+            auto file = File(deleteme, "w+");
+            scope(success) std.file.remove(deleteme);
+        }
+        else
+            auto file = File.tmpfile();
+        file.write("1\n2\n3\n");
+
+        // bug 9599
+        file.rewind();
+        File.ByLine!(char, char) fbl = file.byLine();
+        auto fbl2 = fbl;
+        assert(fbl.front == "1");
+        assert(fbl.front is fbl2.front);
+        assert(fbl.take(1).equal(["1"]));
+        assert(fbl.equal(["2", "3"]));
+        assert(fbl.empty);
+        assert(file.isOpen); // we still have a valid reference
+
+        file.rewind();
+        fbl = file.byLine();
+        assert(!fbl.drop(2).empty);
+        assert(fbl.equal(["3"]));
+        assert(fbl.empty);
+        assert(file.isOpen);
+
+        file.detach();
+        assert(!file.isOpen);
+    }
+
+    @system unittest
+    {
+        static import std.file;
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "hi");
+        scope(success) std.file.remove(deleteme);
+
+        auto blc = File(deleteme).byLineCopy;
+        assert(!blc.empty);
+        // check front is cached
+        assert(blc.front is blc.front);
+    }
+
+    /**
+    Creates an input range set up to parse one line at a time from the file
+    into a tuple.
+
+    Range primitives may throw $(D StdioException) on I/O error.
+
+    Params:
+        format = tuple record $(REF_ALTTEXT _format, formattedRead, std, _format)
+
+    Returns:
+        The input range set up to parse one line at a time into a record tuple.
+
+    See_Also:
+
+        It is similar to $(LREF byLine) and uses
+        $(REF_ALTTEXT _format, formattedRead, std, _format) under the hood.
+    */
+    template byRecord(Fields...)
+    {
+        ByRecord!(Fields) byRecord(string format)
+        {
+            return typeof(return)(this, format);
+        }
+    }
+
+    ///
+    @system unittest
+    {
+         static import std.file;
+         import std.typecons : tuple;
+
+         // prepare test file
+         auto testFile = testFilename();
+         scope(failure) printf("Failed test at line %d\n", __LINE__);
+         std.file.write(testFile, "1 2\n4 1\n5 100");
+         scope(exit) std.file.remove(testFile);
+
+         File f = File(testFile);
+         scope(exit) f.close();
+
+         auto expected = [tuple(1, 2), tuple(4, 1), tuple(5, 100)];
+         uint i;
+         foreach (e; f.byRecord!(int, int)("%s %s"))
+         {
+             assert(e == expected[i++]);
+         }
+    }
+
+    // Note: This was documented until 2013/08
+    /*
+     * Range that reads a chunk at a time.
+     */
+    struct ByChunk
+    {
+    private:
+        File    file_;
+        ubyte[] chunk_;
+
+        void prime()
+        {
+            chunk_ = file_.rawRead(chunk_);
+            if (chunk_.length == 0)
+                file_.detach();
+        }
+
+    public:
+        this(File file, size_t size)
+        {
+            this(file, new ubyte[](size));
+        }
+
+        this(File file, ubyte[] buffer)
+        {
+            import std.exception : enforce;
+            enforce(buffer.length, "size must be larger than 0");
+            file_ = file;
+            chunk_ = buffer;
+            prime();
+        }
+
+        // $(D ByChunk)'s input range primitive operations.
+        @property nothrow
+        bool empty() const
+        {
+            return !file_.isOpen;
+        }
+
+        /// Ditto
+        @property nothrow
+        ubyte[] front()
+        {
+            version (assert)
+            {
+                import core.exception : RangeError;
+                if (empty)
+                    throw new RangeError();
+            }
+            return chunk_;
+        }
+
+        /// Ditto
+        void popFront()
+        {
+            version (assert)
+            {
+                import core.exception : RangeError;
+                if (empty)
+                    throw new RangeError();
+            }
+            prime();
+        }
+    }
+
+/**
+Returns an input range set up to read from the file handle a chunk at a
+time.
+
+The element type for the range will be $(D ubyte[]). Range primitives
+may throw $(D StdioException) on I/O error.
+
+Example:
+---------
+void main()
+{
+    // Read standard input 4KB at a time
+    foreach (ubyte[] buffer; stdin.byChunk(4096))
+    {
+        ... use buffer ...
+    }
+}
+---------
+
+The parameter may be a number (as shown in the example above) dictating the
+size of each chunk. Alternatively, $(D byChunk) accepts a
+user-provided buffer that it uses directly.
+
+Example:
+---------
+void main()
+{
+    // Read standard input 4KB at a time
+    foreach (ubyte[] buffer; stdin.byChunk(new ubyte[4096]))
+    {
+        ... use buffer ...
+    }
+}
+---------
+
+In either case, the content of the buffer is reused across calls. That means
+$(D front) will not persist after $(D popFront) is called, so if retention is
+needed, the caller must copy its contents (e.g. by calling $(D buffer.dup)).
+
+In the  example above, $(D buffer.length) is 4096 for all iterations, except
+for the last one, in which case $(D buffer.length) may be less than 4096 (but
+always greater than zero).
+
+With the mentioned limitations, $(D byChunk) works with any algorithm
+compatible with input ranges.
+
+Example:
+---
+// Efficient file copy, 1MB at a time.
+import std.algorithm, std.stdio;
+void main()
+{
+    stdin.byChunk(1024 * 1024).copy(stdout.lockingTextWriter());
+}
+---
+
+$(REF joiner, std,algorithm,iteration) can be used to join chunks together into
+a single range lazily.
+Example:
+---
+import std.algorithm, std.stdio;
+void main()
+{
+    //Range of ranges
+    static assert(is(typeof(stdin.byChunk(4096).front) == ubyte[]));
+    //Range of elements
+    static assert(is(typeof(stdin.byChunk(4096).joiner.front) == ubyte));
+}
+---
+
+Returns: A call to $(D byChunk) returns a range initialized with the $(D File)
+object and the appropriate buffer.
+
+Throws: If the user-provided size is zero or the user-provided buffer
+is empty, throws an $(D Exception). In case of an I/O error throws
+$(D StdioException).
+ */
+    auto byChunk(size_t chunkSize)
+    {
+        return ByChunk(this, chunkSize);
+    }
+/// Ditto
+    ByChunk byChunk(ubyte[] buffer)
+    {
+        return ByChunk(this, buffer);
+    }
+
+    @system unittest
+    {
+        static import std.file;
+
+        scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "asd\ndef\nasdf");
+
+        auto witness = ["asd\n", "def\n", "asdf" ];
+        auto f = File(deleteme);
+        scope(exit)
+        {
+            f.close();
+            assert(!f.isOpen);
+            std.file.remove(deleteme);
+        }
+
+        uint i;
+        foreach (chunk; f.byChunk(4))
+            assert(chunk == cast(ubyte[]) witness[i++]);
+
+        assert(i == witness.length);
+    }
+
+    @system unittest
+    {
+        static import std.file;
+
+        scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+        auto deleteme = testFilename();
+        std.file.write(deleteme, "asd\ndef\nasdf");
+
+        auto witness = ["asd\n", "def\n", "asdf" ];
+        auto f = File(deleteme);
+        scope(exit)
+        {
+            f.close();
+            assert(!f.isOpen);
+            std.file.remove(deleteme);
+        }
+
+        uint i;
+        foreach (chunk; f.byChunk(new ubyte[4]))
+            assert(chunk == cast(ubyte[]) witness[i++]);
+
+        assert(i == witness.length);
+    }
+
+    // Note: This was documented until 2013/08
+/*
+$(D Range) that locks the file and allows fast writing to it.
+ */
+    struct LockingTextWriter
+    {
+    private:
+        import std.range.primitives : ElementType, isInfinite, isInputRange;
+        // the shared file handle
+        FILE* fps_;
+
+        // the unshared version of fps
+        @property _iobuf* handle_() @trusted { return cast(_iobuf*) fps_; }
+
+        // the file's orientation (byte- or wide-oriented)
+        int orientation_;
+    public:
+
+        this(ref File f) @trusted
+        {
+            import core.stdc.wchar_ : fwide;
+            import std.exception : enforce;
+
+            enforce(f._p && f._p.handle, "Attempting to write to closed File");
+            fps_ = f._p.handle;
+            orientation_ = fwide(fps_, 0);
+            FLOCK(fps_);
+        }
+
+        ~this() @trusted
+        {
+            if (fps_)
+            {
+                FUNLOCK(fps_);
+                fps_ = null;
+            }
+        }
+
+        this(this) @trusted
+        {
+            if (fps_)
+            {
+                FLOCK(fps_);
+            }
+        }
+
+        /// Range primitive implementations.
+        void put(A)(A writeme)
+            if ((isSomeChar!(Unqual!(ElementType!A)) ||
+                  is(ElementType!A : const(ubyte))) &&
+                isInputRange!A &&
+                !isInfinite!A)
+        {
+            import std.exception : errnoEnforce;
+
+            alias C = ElementEncodingType!A;
+            static assert(!is(C == void));
+            static if (isSomeString!A && C.sizeof == 1 || is(A : const(ubyte)[]))
+            {
+                if (orientation_ <= 0)
+                {
+                    //file.write(writeme); causes infinite recursion!!!
+                    //file.rawWrite(writeme);
+                    auto result = trustedFwrite(fps_, writeme);
+                    if (result != writeme.length) errnoEnforce(0);
+                    return;
+                }
+            }
+
+            // put each element in turn.
+            alias Elem = Unqual!(ElementType!A);
+            foreach (Elem c; writeme)
+            {
+                put(c);
+            }
+        }
+
+        /// ditto
+        void put(C)(C c) @safe if (isSomeChar!C || is(C : const(ubyte)))
+        {
+            import std.traits : Parameters;
+            static auto trustedFPUTC(int ch, _iobuf* h) @trusted
+            {
+                return FPUTC(ch, h);
+            }
+            static auto trustedFPUTWC(Parameters!FPUTWC[0] ch, _iobuf* h) @trusted
+            {
+                return FPUTWC(ch, h);
+            }
+
+            static if (c.sizeof == 1)
+            {
+                // simple char
+                if (orientation_ <= 0) trustedFPUTC(c, handle_);
+                else trustedFPUTWC(c, handle_);
+            }
+            else static if (c.sizeof == 2)
+            {
+                import std.utf : encode, UseReplacementDchar;
+
+                if (orientation_ <= 0)
+                {
+                    if (c <= 0x7F)
+                    {
+                        trustedFPUTC(c, handle_);
+                    }
+                    else
+                    {
+                        char[4] buf;
+                        immutable size = encode!(UseReplacementDchar.yes)(buf, c);
+                        foreach (i ; 0 .. size)
+                            trustedFPUTC(buf[i], handle_);
+                    }
+                }
+                else
+                {
+                    trustedFPUTWC(c, handle_);
+                }
+            }
+            else // 32-bit characters
+            {
+                import std.utf : encode;
+
+                if (orientation_ <= 0)
+                {
+                    if (c <= 0x7F)
+                    {
+                        trustedFPUTC(c, handle_);
+                    }
+                    else
+                    {
+                        char[4] buf = void;
+                        immutable len = encode(buf, c);
+                        foreach (i ; 0 .. len)
+                            trustedFPUTC(buf[i], handle_);
+                    }
+                }
+                else
+                {
+                    version (Windows)
+                    {
+                        import std.utf : isValidDchar;
+
+                        assert(isValidDchar(c));
+                        if (c <= 0xFFFF)
+                        {
+                            trustedFPUTWC(c, handle_);
+                        }
+                        else
+                        {
+                            trustedFPUTWC(cast(wchar)
+                                    ((((c - 0x10000) >> 10) & 0x3FF)
+                                            + 0xD800), handle_);
+                            trustedFPUTWC(cast(wchar)
+                                    (((c - 0x10000) & 0x3FF) + 0xDC00),
+                                    handle_);
+                        }
+                    }
+                    else version (Posix)
+                    {
+                        trustedFPUTWC(c, handle_);
+                    }
+                    else
+                    {
+                        static assert(0);
+                    }
+                }
+            }
+        }
+    }
+
+/** Returns an output range that locks the file and allows fast writing to it.
+
+See $(LREF byChunk) for an example.
+*/
+    auto lockingTextWriter() @safe
+    {
+        return LockingTextWriter(this);
+    }
+
+    // An output range which optionally locks the file and puts it into
+    // binary mode (similar to rawWrite). Because it needs to restore
+    // the file mode on destruction, it is RefCounted on Windows.
+    struct BinaryWriterImpl(bool locking)
+    {
+        import std.traits : hasIndirections;
+    private:
+        FILE* fps;
+        string name;
+
+        version (Windows)
+        {
+            int fd, oldMode;
+            version (DIGITAL_MARS_STDIO)
+                ubyte oldInfo;
+        }
+
+    package:
+        this(ref File f)
+        {
+            import std.exception : enforce;
+
+            enforce(f._p && f._p.handle);
+            name = f._name;
+            fps = f._p.handle;
+            static if (locking)
+                FLOCK(fps);
+
+            version (Windows)
+            {
+                .fflush(fps); // before changing translation mode
+                fd = ._fileno(fps);
+                oldMode = ._setmode(fd, _O_BINARY);
+                version (DIGITAL_MARS_STDIO)
+                {
+                    import core.atomic : atomicOp;
+
+                    // @@@BUG@@@ 4243
+                    oldInfo = __fhnd_info[fd];
+                    atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
+                }
+            }
+        }
+
+    public:
+        ~this()
+        {
+            if (!fps)
+                return;
+
+            version (Windows)
+            {
+                .fflush(fps); // before restoring translation mode
+                version (DIGITAL_MARS_STDIO)
+                {
+                    // @@@BUG@@@ 4243
+                    __fhnd_info[fd] = oldInfo;
+                }
+                ._setmode(fd, oldMode);
+            }
+
+            FUNLOCK(fps);
+            fps = null;
+        }
+
+        void rawWrite(T)(in T[] buffer)
+        {
+            import std.conv : text;
+            import std.exception : errnoEnforce;
+
+            auto result = trustedFwrite(fps, buffer);
+            if (result == result.max) result = 0;
+            errnoEnforce(result == buffer.length,
+                    text("Wrote ", result, " instead of ", buffer.length,
+                            " objects of type ", T.stringof, " to file `",
+                            name, "'"));
+        }
+
+        version (Windows)
+        {
+            @disable this(this);
+        }
+        else
+        {
+            this(this)
+            {
+                if (fps)
+                {
+                    FLOCK(fps);
+                }
+            }
+        }
+
+        void put(T)(auto ref in T value)
+        if (!hasIndirections!T &&
+            !isInputRange!T)
+        {
+            rawWrite((&value)[0 .. 1]);
+        }
+
+        void put(T)(in T[] array)
+        if (!hasIndirections!T &&
+            !isInputRange!T)
+        {
+            rawWrite(array);
+        }
+    }
+
+/** Returns an output range that locks the file and allows fast writing to it.
+
+Example:
+Produce a grayscale image of the $(LINK2 https://en.wikipedia.org/wiki/Mandelbrot_set, Mandelbrot set)
+in binary $(LINK2 https://en.wikipedia.org/wiki/Netpbm_format, Netpbm format) to standard output.
+---
+import std.algorithm, std.range, std.stdio;
+
+void main()
+{
+    enum size = 500;
+    writef("P5\n%d %d %d\n", size, size, ubyte.max);
+
+    iota(-1, 3, 2.0/size).map!(y =>
+        iota(-1.5, 0.5, 2.0/size).map!(x =>
+            cast(ubyte)(1+
+                recurrence!((a, n) => x + y*1i + a[n-1]^^2)(0+0i)
+                .take(ubyte.max)
+                .countUntil!(z => z.re^^2 + z.im^^2 > 4))
+        )
+    )
+    .copy(stdout.lockingBinaryWriter);
+}
+---
+*/
+    auto lockingBinaryWriter()
+    {
+        alias LockingBinaryWriterImpl = BinaryWriterImpl!true;
+
+        version (Windows)
+        {
+            import std.typecons : RefCounted;
+            alias LockingBinaryWriter = RefCounted!LockingBinaryWriterImpl;
+        }
+        else
+            alias LockingBinaryWriter = LockingBinaryWriterImpl;
+
+        return LockingBinaryWriter(this);
+    }
+
+    @system unittest
+    {
+        import std.algorithm.mutation : reverse;
+        import std.exception : collectException;
+        static import std.file;
+        import std.range : only, retro;
+        import std.string : format;
+
+        auto deleteme = testFilename();
+        scope(exit) collectException(std.file.remove(deleteme));
+        auto output = File(deleteme, "wb");
+        auto writer = output.lockingBinaryWriter();
+        auto input = File(deleteme, "rb");
+
+        T[] readExact(T)(T[] buf)
+        {
+            auto result = input.rawRead(buf);
+            assert(result.length == buf.length,
+                "Read %d out of %d bytes"
+                .format(result.length, buf.length));
+            return result;
+        }
+
+        // test raw values
+        ubyte byteIn = 42;
+        byteIn.only.copy(writer); output.flush();
+        ubyte byteOut = readExact(new ubyte[1])[0];
+        assert(byteIn == byteOut);
+
+        // test arrays
+        ubyte[] bytesIn = [1, 2, 3, 4, 5];
+        bytesIn.copy(writer); output.flush();
+        ubyte[] bytesOut = readExact(new ubyte[bytesIn.length]);
+        scope(failure) .writeln(bytesOut);
+        assert(bytesIn == bytesOut);
+
+        // test ranges of values
+        bytesIn.retro.copy(writer); output.flush();
+        bytesOut = readExact(bytesOut);
+        bytesOut.reverse();
+        assert(bytesIn == bytesOut);
+
+        // test string
+        "foobar".copy(writer); output.flush();
+        char[] charsOut = readExact(new char[6]);
+        assert(charsOut == "foobar");
+
+        // test ranges of arrays
+        only("foo", "bar").copy(writer); output.flush();
+        charsOut = readExact(charsOut);
+        assert(charsOut == "foobar");
+
+        // test that we are writing arrays as is,
+        // without UTF-8 transcoding
+        "foo"d.copy(writer); output.flush();
+        dchar[] dcharsOut = readExact(new dchar[3]);
+        assert(dcharsOut == "foo");
+    }
+
+/// Get the size of the file, ulong.max if file is not searchable, but still throws if an actual error occurs.
+    @property ulong size() @safe
+    {
+        import std.exception : collectException;
+
+        ulong pos = void;
+        if (collectException(pos = tell)) return ulong.max;
+        scope(exit) seek(pos);
+        seek(0, SEEK_END);
+        return tell;
+    }
+}
+
+@system unittest
+{
+    @system struct SystemToString
+    {
+        string toString()
+        {
+            return "system";
+        }
+    }
+
+    @trusted struct TrustedToString
+    {
+        string toString()
+        {
+            return "trusted";
+        }
+    }
+
+    @safe struct SafeToString
+    {
+        string toString()
+        {
+            return "safe";
+        }
+    }
+
+    @system void systemTests()
+    {
+        //system code can write to files/stdout with anything!
+        if (false)
+        {
+            auto f = File();
+
+            f.write("just a string");
+            f.write("string with arg: ", 47);
+            f.write(SystemToString());
+            f.write(TrustedToString());
+            f.write(SafeToString());
+
+            write("just a string");
+            write("string with arg: ", 47);
+            write(SystemToString());
+            write(TrustedToString());
+            write(SafeToString());
+
+            f.writeln("just a string");
+            f.writeln("string with arg: ", 47);
+            f.writeln(SystemToString());
+            f.writeln(TrustedToString());
+            f.writeln(SafeToString());
+
+            writeln("just a string");
+            writeln("string with arg: ", 47);
+            writeln(SystemToString());
+            writeln(TrustedToString());
+            writeln(SafeToString());
+
+            f.writef("string with arg: %s", 47);
+            f.writef("%s", SystemToString());
+            f.writef("%s", TrustedToString());
+            f.writef("%s", SafeToString());
+
+            writef("string with arg: %s", 47);
+            writef("%s", SystemToString());
+            writef("%s", TrustedToString());
+            writef("%s", SafeToString());
+
+            f.writefln("string with arg: %s", 47);
+            f.writefln("%s", SystemToString());
+            f.writefln("%s", TrustedToString());
+            f.writefln("%s", SafeToString());
+
+            writefln("string with arg: %s", 47);
+            writefln("%s", SystemToString());
+            writefln("%s", TrustedToString());
+            writefln("%s", SafeToString());
+        }
+    }
+
+    @safe void safeTests()
+    {
+        auto f = File();
+
+        //safe code can write to files only with @safe and @trusted code...
+        if (false)
+        {
+            f.write("just a string");
+            f.write("string with arg: ", 47);
+            f.write(TrustedToString());
+            f.write(SafeToString());
+
+            write("just a string");
+            write("string with arg: ", 47);
+            write(TrustedToString());
+            write(SafeToString());
+
+            f.writeln("just a string");
+            f.writeln("string with arg: ", 47);
+            f.writeln(TrustedToString());
+            f.writeln(SafeToString());
+
+            writeln("just a string");
+            writeln("string with arg: ", 47);
+            writeln(TrustedToString());
+            writeln(SafeToString());
+
+            f.writef("string with arg: %s", 47);
+            f.writef("%s", TrustedToString());
+            f.writef("%s", SafeToString());
+
+            writef("string with arg: %s", 47);
+            writef("%s", TrustedToString());
+            writef("%s", SafeToString());
+
+            f.writefln("string with arg: %s", 47);
+            f.writefln("%s", TrustedToString());
+            f.writefln("%s", SafeToString());
+
+            writefln("string with arg: %s", 47);
+            writefln("%s", TrustedToString());
+            writefln("%s", SafeToString());
+        }
+
+        static assert(!__traits(compiles, f.write(SystemToString().toString())));
+        static assert(!__traits(compiles, f.writeln(SystemToString())));
+        static assert(!__traits(compiles, f.writef("%s", SystemToString())));
+        static assert(!__traits(compiles, f.writefln("%s", SystemToString())));
+
+        static assert(!__traits(compiles, write(SystemToString().toString())));
+        static assert(!__traits(compiles, writeln(SystemToString())));
+        static assert(!__traits(compiles, writef("%s", SystemToString())));
+        static assert(!__traits(compiles, writefln("%s", SystemToString())));
+    }
+
+    systemTests();
+    safeTests();
+}
+
+@safe unittest
+{
+    import std.exception : collectException;
+    static import std.file;
+
+    auto deleteme = testFilename();
+    scope(exit) collectException(std.file.remove(deleteme));
+    std.file.write(deleteme, "1 2 3");
+    auto f = File(deleteme);
+    assert(f.size == 5);
+    assert(f.tell == 0);
+}
+
+@system unittest
+{
+    // @system due to readln
+    static import std.file;
+    import std.range : chain, only, repeat;
+    import std.range.primitives : isOutputRange;
+
+    auto deleteme = testFilename();
+    scope(exit) std.file.remove(deleteme);
+
+    {
+        File f = File(deleteme, "w");
+        auto writer = f.lockingTextWriter();
+        static assert(isOutputRange!(typeof(writer), dchar));
+        writer.put("日本語");
+        writer.put("日本語"w);
+        writer.put("日本語"d);
+        writer.put('日');
+        writer.put(chain(only('本'), only('語')));
+        writer.put(repeat('#', 12)); // BUG 11945
+        writer.put(cast(immutable(ubyte)[])"日本語"); // Bug 17229
+    }
+    assert(File(deleteme).readln() == "日本語日本語日本語日本語############日本語");
+}
+
+@safe unittest
+{
+    import std.exception : collectException;
+    auto e = collectException({ File f; f.writeln("Hello!"); }());
+    assert(e && e.msg == "Attempting to write to closed File");
+}
+
+/// Used to specify the lock type for $(D File.lock) and $(D File.tryLock).
+enum LockType
+{
+    /**
+     * Specifies a _read (shared) lock. A _read lock denies all processes
+     * write access to the specified region of the file, including the
+     * process that first locks the region. All processes can _read the
+     * locked region. Multiple simultaneous _read locks are allowed, as
+     * long as there are no exclusive locks.
+     */
+    read,
+
+    /**
+     * Specifies a read/write (exclusive) lock. A read/write lock denies all
+     * other processes both read and write access to the locked file region.
+     * If a segment has an exclusive lock, it may not have any shared locks
+     * or other exclusive locks.
+     */
+    readWrite
+}
+
+struct LockingTextReader
+{
+    private File _f;
+    private char _front;
+    private bool _hasChar;
+
+    this(File f)
+    {
+        import std.exception : enforce;
+        enforce(f.isOpen, "LockingTextReader: File must be open");
+        _f = f;
+        FLOCK(_f._p.handle);
+    }
+
+    this(this)
+    {
+        FLOCK(_f._p.handle);
+    }
+
+    ~this()
+    {
+        if (_hasChar)
+            ungetc(_front, cast(FILE*)_f._p.handle);
+
+        // File locking has its own reference count
+        if (_f.isOpen) FUNLOCK(_f._p.handle);
+    }
+
+    void opAssign(LockingTextReader r)
+    {
+        import std.algorithm.mutation : swap;
+        swap(this, r);
+    }
+
+    @property bool empty()
+    {
+        if (!_hasChar)
+        {
+            if (!_f.isOpen || _f.eof)
+                return true;
+            immutable int c = FGETC(cast(_iobuf*) _f._p.handle);
+            if (c == EOF)
+            {
+                .destroy(_f);
+                return true;
+            }
+            _front = cast(char) c;
+            _hasChar = true;
+        }
+        return false;
+    }
+
+    @property char front()
+    {
+        if (!_hasChar)
+        {
+            version (assert)
+            {
+                import core.exception : RangeError;
+                if (empty)
+                    throw new RangeError();
+            }
+            else
+            {
+                empty;
+            }
+        }
+        return _front;
+    }
+
+    void popFront()
+    {
+        if (!_hasChar)
+            empty;
+        _hasChar = false;
+    }
+}
+
+@system unittest
+{
+    // @system due to readf
+    static import std.file;
+    import std.range.primitives : isInputRange;
+
+    static assert(isInputRange!LockingTextReader);
+    auto deleteme = testFilename();
+    std.file.write(deleteme, "1 2 3");
+    scope(exit) std.file.remove(deleteme);
+    int x, y;
+    auto f = File(deleteme);
+    f.readf("%s ", &x);
+    assert(x == 1);
+    f.readf("%d ", &x);
+    assert(x == 2);
+    f.readf("%d ", &x);
+    assert(x == 3);
+}
+
+@system unittest // bugzilla 13686
+{
+    import std.algorithm.comparison : equal;
+    static import std.file;
+    import std.utf : byDchar;
+
+    auto deleteme = testFilename();
+    std.file.write(deleteme, "Тест");
+    scope(exit) std.file.remove(deleteme);
+
+    string s;
+    File(deleteme).readf("%s", &s);
+    assert(s == "Тест");
+
+    auto ltr = LockingTextReader(File(deleteme)).byDchar;
+    assert(equal(ltr, "Тест".byDchar));
+}
+
+@system unittest // bugzilla 12320
+{
+    static import std.file;
+    auto deleteme = testFilename();
+    std.file.write(deleteme, "ab");
+    scope(exit) std.file.remove(deleteme);
+    auto ltr = LockingTextReader(File(deleteme));
+    assert(ltr.front == 'a');
+    ltr.popFront();
+    assert(ltr.front == 'b');
+    ltr.popFront();
+    assert(ltr.empty);
+}
+
+@system unittest // bugzilla 14861
+{
+    // @system due to readf
+    static import std.file;
+    auto deleteme = testFilename();
+    File fw = File(deleteme, "w");
+    for (int i; i != 5000; i++)
+        fw.writeln(i, ";", "Иванов;Пётр;Петрович");
+    fw.close();
+    scope(exit) std.file.remove(deleteme);
+    // Test read
+    File fr = File(deleteme, "r");
+    scope (exit) fr.close();
+    int nom; string fam, nam, ot;
+    // Error format read
+    while (!fr.eof)
+        fr.readf("%s;%s;%s;%s\n", &nom, &fam, &nam, &ot);
+}
+
+/**
+ * Indicates whether $(D T) is a file handle, i.e. the type
+ * is implicitly convertable to $(LREF File) or a pointer to a
+ * $(REF FILE, core,stdc,stdio).
+ *
+ * Returns:
+ *      `true` if `T` is a file handle, `false` otherwise.
+ */
+template isFileHandle(T)
+{
+    enum isFileHandle = is(T : FILE*) ||
+        is(T : File);
+}
+
+///
+@safe unittest
+{
+    static assert(isFileHandle!(FILE*));
+    static assert(isFileHandle!(File));
+}
+
+/**
+ * Property used by writeln/etc. so it can infer @safe since stdout is __gshared
+ */
+private @property File trustedStdout() @trusted
+{
+    return stdout;
+}
+
+/***********************************
+For each argument $(D arg) in $(D args), format the argument (using
+$(REF to, std,conv)) and write the resulting
+string to $(D args[0]). A call without any arguments will fail to
+compile.
+
+Params:
+    args = the items to write to `stdout`
+
+Throws: In case of an I/O error, throws an $(D StdioException).
+
+Example:
+    Reads `stdin` and writes it to `stdout` with an argument
+    counter.
+---
+import std.stdio;
+
+void main()
+{
+    string line;
+
+    for (size_t count = 0; (line = readln) !is null; count++)
+    {
+         write("Input ", count, ": ", line, "\n");
+    }
+}
+---
+ */
+void write(T...)(T args)
+if (!is(T[0] : File))
+{
+    trustedStdout.write(args);
+}
+
+@system unittest
+{
+    static import std.file;
+
+    scope(failure) printf("Failed test at line %d\n", __LINE__);
+    void[] buf;
+    if (false) write(buf);
+    // test write
+    auto deleteme = testFilename();
+    auto f = File(deleteme, "w");
+    f.write("Hello, ",  "world number ", 42, "!");
+    f.close();
+    scope(exit) { std.file.remove(deleteme); }
+    assert(cast(char[]) std.file.read(deleteme) == "Hello, world number 42!");
+}
+
+/***********************************
+ * Equivalent to `write(args, '\n')`.  Calling `writeln` without
+ * arguments is valid and just prints a newline to the standard
+ * output.
+ *
+ * Params:
+ *      args = the items to write to `stdout`
+ *
+ * Throws:
+ *      In case of an I/O error, throws an $(LREF StdioException).
+ * Example:
+ *        Reads $(D stdin) and writes it to $(D stdout) with a argument
+ *        counter.
+---
+import std.stdio;
+
+void main()
+{
+    string line;
+
+    for (size_t count = 0; (line = readln) !is null; count++)
+    {
+         writeln("Input ", count, ": ", line);
+    }
+}
+---
+ */
+void writeln(T...)(T args)
+{
+    import std.traits : isAggregateType;
+    static if (T.length == 0)
+    {
+        import std.exception : enforce;
+
+        enforce(fputc('\n', .trustedStdout._p.handle) != EOF, "fputc failed");
+    }
+    else static if (T.length == 1 &&
+                    is(typeof(args[0]) : const(char)[]) &&
+                    !is(typeof(args[0]) == enum) &&
+                    !is(Unqual!(typeof(args[0])) == typeof(null)) &&
+                    !isAggregateType!(typeof(args[0])))
+    {
+        import std.traits : isStaticArray;
+
+        // Specialization for strings - a very frequent case
+        auto w = .trustedStdout.lockingTextWriter();
+
+        static if (isStaticArray!(typeof(args[0])))
+        {
+            w.put(args[0][]);
+        }
+        else
+        {
+            w.put(args[0]);
+        }
+        w.put('\n');
+    }
+    else
+    {
+        // Most general instance
+        trustedStdout.write(args, '\n');
+    }
+}
+
+@safe unittest
+{
+    // Just make sure the call compiles
+    if (false) writeln();
+
+    if (false) writeln("wyda");
+
+    // bug 8040
+    if (false) writeln(null);
+    if (false) writeln(">", null, "<");
+
+    // Bugzilla 14041
+    if (false)
+    {
+        char[8] a;
+        writeln(a);
+    }
+}
+
+@system unittest
+{
+    static import std.file;
+
+    scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+    // test writeln
+    auto deleteme = testFilename();
+    auto f = File(deleteme, "w");
+    scope(exit) { std.file.remove(deleteme); }
+    f.writeln("Hello, ",  "world number ", 42, "!");
+    f.close();
+    version (Windows)
+        assert(cast(char[]) std.file.read(deleteme) ==
+                "Hello, world number 42!\r\n");
+    else
+        assert(cast(char[]) std.file.read(deleteme) ==
+                "Hello, world number 42!\n");
+
+    // test writeln on stdout
+    auto saveStdout = stdout;
+    scope(exit) stdout = saveStdout;
+    stdout.open(deleteme, "w");
+    writeln("Hello, ",  "world number ", 42, "!");
+    stdout.close();
+    version (Windows)
+        assert(cast(char[]) std.file.read(deleteme) ==
+                "Hello, world number 42!\r\n");
+    else
+        assert(cast(char[]) std.file.read(deleteme) ==
+                "Hello, world number 42!\n");
+
+    stdout.open(deleteme, "w");
+    writeln("Hello!"c);
+    writeln("Hello!"w);    // bug 8386
+    writeln("Hello!"d);    // bug 8386
+    writeln("embedded\0null"c); // bug 8730
+    stdout.close();
+    version (Windows)
+        assert(cast(char[]) std.file.read(deleteme) ==
+            "Hello!\r\nHello!\r\nHello!\r\nembedded\0null\r\n");
+    else
+        assert(cast(char[]) std.file.read(deleteme) ==
+            "Hello!\nHello!\nHello!\nembedded\0null\n");
+}
+
+@system unittest
+{
+    static import std.file;
+
+    auto deleteme = testFilename();
+    auto f = File(deleteme, "w");
+    scope(exit) { std.file.remove(deleteme); }
+
+    enum EI : int    { A, B }
+    enum ED : double { A, B }
+    enum EC : char   { A, B }
+    enum ES : string { A = "aaa", B = "bbb" }
+
+    f.writeln(EI.A);  // false, but A on 2.058
+    f.writeln(EI.B);  // true, but B on 2.058
+
+    f.writeln(ED.A);  // A
+    f.writeln(ED.B);  // B
+
+    f.writeln(EC.A);  // A
+    f.writeln(EC.B);  // B
+
+    f.writeln(ES.A);  // A
+    f.writeln(ES.B);  // B
+
+    f.close();
+    version (Windows)
+        assert(cast(char[]) std.file.read(deleteme) ==
+                "A\r\nB\r\nA\r\nB\r\nA\r\nB\r\nA\r\nB\r\n");
+    else
+        assert(cast(char[]) std.file.read(deleteme) ==
+                "A\nB\nA\nB\nA\nB\nA\nB\n");
+}
+
+@system unittest
+{
+    static auto useInit(T)(T ltw)
+    {
+        T val;
+        val = ltw;
+        val = T.init;
+        return val;
+    }
+    useInit(stdout.lockingTextWriter());
+}
+
+
+/***********************************
+Writes formatted data to standard output (without a trailing newline).
+
+Params:
+fmt = The $(LINK2 std_format.html#format-string, format string).
+When passed as a compile-time argument, the string will be statically checked
+against the argument types passed.
+args = Items to write.
+
+Note: In older versions of Phobos, it used to be possible to write:
+
+------
+writef(stderr, "%s", "message");
+------
+
+to print a message to $(D stderr). This syntax is no longer supported, and has
+been superceded by:
+
+------
+stderr.writef("%s", "message");
+------
+
+*/
+void writef(alias fmt, A...)(A args)
+if (isSomeString!(typeof(fmt)))
+{
+    import std.format : checkFormatException;
+
+    alias e = checkFormatException!(fmt, A);
+    static assert(!e, e.msg);
+    return .writef(fmt, args);
+}
+
+/// ditto
+void writef(Char, A...)(in Char[] fmt, A args)
+{
+    trustedStdout.writef(fmt, args);
+}
+
+@system unittest
+{
+    static import std.file;
+
+    scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+    // test writef
+    auto deleteme = testFilename();
+    auto f = File(deleteme, "w");
+    scope(exit) { std.file.remove(deleteme); }
+    f.writef!"Hello, %s world number %s!"("nice", 42);
+    f.close();
+    assert(cast(char[]) std.file.read(deleteme) ==  "Hello, nice world number 42!");
+    // test write on stdout
+    auto saveStdout = stdout;
+    scope(exit) stdout = saveStdout;
+    stdout.open(deleteme, "w");
+    writef!"Hello, %s world number %s!"("nice", 42);
+    stdout.close();
+    assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
+}
+
+/***********************************
+ * Equivalent to $(D writef(fmt, args, '\n')).
+ */
+void writefln(alias fmt, A...)(A args)
+if (isSomeString!(typeof(fmt)))
+{
+    import std.format : checkFormatException;
+
+    alias e = checkFormatException!(fmt, A);
+    static assert(!e, e.msg);
+    return .writefln(fmt, args);
+}
+
+/// ditto
+void writefln(Char, A...)(in Char[] fmt, A args)
+{
+    trustedStdout.writefln(fmt, args);
+}
+
+@system unittest
+{
+    static import std.file;
+
+    scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+    // test File.writefln
+    auto deleteme = testFilename();
+    auto f = File(deleteme, "w");
+    scope(exit) { std.file.remove(deleteme); }
+    f.writefln!"Hello, %s world number %s!"("nice", 42);
+    f.close();
+    version (Windows)
+        assert(cast(char[]) std.file.read(deleteme) ==
+                "Hello, nice world number 42!\r\n");
+    else
+        assert(cast(char[]) std.file.read(deleteme) ==
+                "Hello, nice world number 42!\n",
+                cast(char[]) std.file.read(deleteme));
+
+    // test writefln
+    auto saveStdout = stdout;
+    scope(exit) stdout = saveStdout;
+    stdout.open(deleteme, "w");
+    writefln!"Hello, %s world number %s!"("nice", 42);
+    stdout.close();
+    version (Windows)
+        assert(cast(char[]) std.file.read(deleteme) ==
+                "Hello, nice world number 42!\r\n");
+    else
+        assert(cast(char[]) std.file.read(deleteme) ==
+                "Hello, nice world number 42!\n");
+}
+
+/**
+ * Reads formatted data from $(D stdin) using $(REF formattedRead, std,_format).
+ * Params:
+ * format = The $(LINK2 std_format.html#_format-string, _format string).
+ * When passed as a compile-time argument, the string will be statically checked
+ * against the argument types passed.
+ * args = Items to be read.
+ * Example:
+----
+// test.d
+void main()
+{
+    import std.stdio;
+    foreach (_; 0 .. 3)
+    {
+        int a;
+        readf!" %d"(a);
+        writeln(++a);
+    }
+}
+----
+$(CONSOLE
+% echo "1 2 3" | rdmd test.d
+2
+3
+4
+)
+ */
+uint readf(alias format, A...)(auto ref A args)
+if (isSomeString!(typeof(format)))
+{
+    import std.format : checkFormatException;
+
+    alias e = checkFormatException!(format, A);
+    static assert(!e, e.msg);
+    return .readf(format, args);
+}
+
+/// ditto
+uint readf(A...)(in char[] format, auto ref A args)
+{
+    return stdin.readf(format, args);
+}
+
+@system unittest
+{
+    float f;
+    if (false) uint x = readf("%s", &f);
+
+    char a;
+    wchar b;
+    dchar c;
+    if (false) readf("%s %s %s", a, b, c);
+    // backwards compatibility with pointers
+    if (false) readf("%s %s %s", a, &b, c);
+    if (false) readf("%s %s %s", &a, &b, &c);
+}
+
+/**********************************
+ * Read line from $(D stdin).
+ *
+ * This version manages its own read buffer, which means one memory allocation per call. If you are not
+ * retaining a reference to the read data, consider the $(D readln(buf)) version, which may offer
+ * better performance as it can reuse its read buffer.
+ *
+ * Returns:
+ *        The line that was read, including the line terminator character.
+ * Params:
+ *        S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
+ *        terminator = Line terminator (by default, $(D '\n')).
+ * Note:
+ *        String terminators are not supported due to ambiguity with readln(buf) below.
+ * Throws:
+ *        $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
+ * Example:
+ *        Reads $(D stdin) and writes it to $(D stdout).
+---
+import std.stdio;
+
+void main()
+{
+    string line;
+    while ((line = readln()) !is null)
+        write(line);
+}
+---
+*/
+S readln(S = string)(dchar terminator = '\n')
+if (isSomeString!S)
+{
+    return stdin.readln!S(terminator);
+}
+
+/**********************************
+ * Read line from $(D stdin) and write it to buf[], including terminating character.
+ *
+ * This can be faster than $(D line = readln()) because you can reuse
+ * the buffer for each call. Note that reusing the buffer means that you
+ * must copy the previous contents if you wish to retain them.
+ *
+ * Returns:
+ *        $(D size_t) 0 for end of file, otherwise number of characters read
+ * Params:
+ *        buf = Buffer used to store the resulting line data. buf is resized as necessary.
+ *        terminator = Line terminator (by default, $(D '\n')). Use $(REF newline, std,ascii)
+ *        for portability (unless the file was opened in text mode).
+ * Throws:
+ *        $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
+ * Example:
+ *        Reads $(D stdin) and writes it to $(D stdout).
+---
+import std.stdio;
+
+void main()
+{
+    char[] buf;
+    while (readln(buf))
+        write(buf);
+}
+---
+*/
+size_t readln(C)(ref C[] buf, dchar terminator = '\n')
+if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
+{
+    return stdin.readln(buf, terminator);
+}
+
+/** ditto */
+size_t readln(C, R)(ref C[] buf, R terminator)
+if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
+    isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
+{
+    return stdin.readln(buf, terminator);
+}
+
+@safe unittest
+{
+    import std.meta : AliasSeq;
+
+    //we can't actually test readln, so at the very least,
+    //we test compilability
+    void foo()
+    {
+        readln();
+        readln('\t');
+        foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
+        {
+            readln!String();
+            readln!String('\t');
+        }
+        foreach (String; AliasSeq!(char[], wchar[], dchar[]))
+        {
+            String buf;
+            readln(buf);
+            readln(buf, '\t');
+            readln(buf, "<br />");
+        }
+    }
+}
+
+/*
+ * Convenience function that forwards to $(D core.sys.posix.stdio.fopen)
+ * (to $(D _wfopen) on Windows)
+ * with appropriately-constructed C-style strings.
+ */
+private FILE* fopen(R1, R2)(R1 name, R2 mode = "r")
+if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) &&
+    (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2))
+{
+    import std.internal.cstring : tempCString;
+
+    auto namez = name.tempCString!FSChar();
+    auto modez = mode.tempCString!FSChar();
+
+    static fopenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
+    {
+        version (Windows)
+        {
+            return _wfopen(namez, modez);
+        }
+        else version (Posix)
+        {
+            /*
+             * The new opengroup large file support API is transparently
+             * included in the normal C bindings. http://opengroup.org/platform/lfs.html#1.0
+             * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and
+             * the normal functions work fine. If not, then large file support
+             * probably isn't available. Do not use the old transitional API
+             * (the native extern(C) fopen64, http://www.unix.org/version2/whatsnew/lfs20mar.html#3.0)
+             */
+            import core.sys.posix.stdio : fopen;
+            return fopen(namez, modez);
+        }
+        else
+        {
+            return .fopen(namez, modez);
+        }
+    }
+    return fopenImpl(namez, modez);
+}
+
+version (Posix)
+{
+    /***********************************
+     * Convenience function that forwards to $(D core.sys.posix.stdio.popen)
+     * with appropriately-constructed C-style strings.
+     */
+    FILE* popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc
+    if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) &&
+        (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2))
+    {
+        import std.internal.cstring : tempCString;
+
+        auto namez = name.tempCString!FSChar();
+        auto modez = mode.tempCString!FSChar();
+
+        static popenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
+        {
+            import core.sys.posix.stdio : popen;
+            return popen(namez, modez);
+        }
+        return popenImpl(namez, modez);
+    }
+}
+
+/*
+ * Convenience function that forwards to $(D core.stdc.stdio.fwrite)
+ */
+private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted
+{
+    return fwrite(obj.ptr, T.sizeof, obj.length, f);
+}
+
+/*
+ * Convenience function that forwards to $(D core.stdc.stdio.fread)
+ */
+private auto trustedFread(T)(FILE* f, T[] obj) @trusted
+{
+    return fread(obj.ptr, T.sizeof, obj.length, f);
+}
+
+/**
+ * Iterates through the lines of a file by using $(D foreach).
+ *
+ * Example:
+ *
+---------
+void main()
+{
+  foreach (string line; lines(stdin))
+  {
+    ... use line ...
+  }
+}
+---------
+The line terminator ($(D '\n') by default) is part of the string read (it
+could be missing in the last line of the file). Several types are
+supported for $(D line), and the behavior of $(D lines)
+changes accordingly:
+
+$(OL $(LI If $(D line) has type $(D string), $(D
+wstring), or $(D dstring), a new string of the respective type
+is allocated every read.) $(LI If $(D line) has type $(D
+char[]), $(D wchar[]), $(D dchar[]), the line's content
+will be reused (overwritten) across reads.) $(LI If $(D line)
+has type $(D immutable(ubyte)[]), the behavior is similar to
+case (1), except that no UTF checking is attempted upon input.) $(LI
+If $(D line) has type $(D ubyte[]), the behavior is
+similar to case (2), except that no UTF checking is attempted upon
+input.))
+
+In all cases, a two-symbols versions is also accepted, in which case
+the first symbol (of integral type, e.g. $(D ulong) or $(D
+uint)) tracks the zero-based number of the current line.
+
+Example:
+----
+  foreach (ulong i, string line; lines(stdin))
+  {
+    ... use line ...
+  }
+----
+
+ In case of an I/O error, an $(D StdioException) is thrown.
+
+See_Also:
+$(LREF byLine)
+ */
+
+struct lines
+{
+    private File f;
+    private dchar terminator = '\n';
+
+    /**
+    Constructor.
+    Params:
+    f = File to read lines from.
+    terminator = Line separator ($(D '\n') by default).
+    */
+    this(File f, dchar terminator = '\n')
+    {
+        this.f = f;
+        this.terminator = terminator;
+    }
+
+    int opApply(D)(scope D dg)
+    {
+        import std.traits : Parameters;
+        alias Parms = Parameters!(dg);
+        static if (isSomeString!(Parms[$ - 1]))
+        {
+            enum bool duplicate = is(Parms[$ - 1] == string)
+                || is(Parms[$ - 1] == wstring) || is(Parms[$ - 1] == dstring);
+            int result = 0;
+            static if (is(Parms[$ - 1] : const(char)[]))
+                alias C = char;
+            else static if (is(Parms[$ - 1] : const(wchar)[]))
+                alias C = wchar;
+            else static if (is(Parms[$ - 1] : const(dchar)[]))
+                alias C = dchar;
+            C[] line;
+            static if (Parms.length == 2)
+                Parms[0] i = 0;
+            for (;;)
+            {
+                import std.conv : to;
+
+                if (!f.readln(line, terminator)) break;
+                auto copy = to!(Parms[$ - 1])(line);
+                static if (Parms.length == 2)
+                {
+                    result = dg(i, copy);
+                    ++i;
+                }
+                else
+                {
+                    result = dg(copy);
+                }
+                if (result != 0) break;
+            }
+            return result;
+        }
+        else
+        {
+            // raw read
+            return opApplyRaw(dg);
+        }
+    }
+    // no UTF checking
+    int opApplyRaw(D)(scope D dg)
+    {
+        import std.conv : to;
+        import std.exception : assumeUnique;
+        import std.traits : Parameters;
+
+        alias Parms = Parameters!(dg);
+        enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]);
+        int result = 1;
+        int c = void;
+        FLOCK(f._p.handle);
+        scope(exit) FUNLOCK(f._p.handle);
+        ubyte[] buffer;
+        static if (Parms.length == 2)
+            Parms[0] line = 0;
+        while ((c = FGETC(cast(_iobuf*) f._p.handle)) != -1)
+        {
+            buffer ~= to!(ubyte)(c);
+            if (c == terminator)
+            {
+                static if (duplicate)
+                    auto arg = assumeUnique(buffer);
+                else
+                    alias arg = buffer;
+                // unlock the file while calling the delegate
+                FUNLOCK(f._p.handle);
+                scope(exit) FLOCK(f._p.handle);
+                static if (Parms.length == 1)
+                {
+                    result = dg(arg);
+                }
+                else
+                {
+                    result = dg(line, arg);
+                    ++line;
+                }
+                if (result) break;
+                static if (!duplicate)
+                    buffer.length = 0;
+            }
+        }
+        // can only reach when FGETC returned -1
+        if (!f.eof) throw new StdioException("Error in reading file"); // error occured
+        return result;
+    }
+}
+
+@system unittest
+{
+    static import std.file;
+    import std.meta : AliasSeq;
+
+    scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+    auto deleteme = testFilename();
+    scope(exit) { std.file.remove(deleteme); }
+
+    alias TestedWith =
+          AliasSeq!(string, wstring, dstring,
+                    char[], wchar[], dchar[]);
+    foreach (T; TestedWith)
+    {
+        // test looping with an empty file
+        std.file.write(deleteme, "");
+        auto f = File(deleteme, "r");
+        foreach (T line; lines(f))
+        {
+            assert(false);
+        }
+        f.close();
+
+        // test looping with a file with three lines
+        std.file.write(deleteme, "Line one\nline two\nline three\n");
+        f.open(deleteme, "r");
+        uint i = 0;
+        foreach (T line; lines(f))
+        {
+            if (i == 0) assert(line == "Line one\n");
+            else if (i == 1) assert(line == "line two\n");
+            else if (i == 2) assert(line == "line three\n");
+            else assert(false);
+            ++i;
+        }
+        f.close();
+
+        // test looping with a file with three lines, last without a newline
+        std.file.write(deleteme, "Line one\nline two\nline three");
+        f.open(deleteme, "r");
+        i = 0;
+        foreach (T line; lines(f))
+        {
+            if (i == 0) assert(line == "Line one\n");
+            else if (i == 1) assert(line == "line two\n");
+            else if (i == 2) assert(line == "line three");
+            else assert(false);
+            ++i;
+        }
+        f.close();
+    }
+
+    // test with ubyte[] inputs
+    alias TestedWith2 = AliasSeq!(immutable(ubyte)[], ubyte[]);
+    foreach (T; TestedWith2)
+    {
+        // test looping with an empty file
+        std.file.write(deleteme, "");
+        auto f = File(deleteme, "r");
+        foreach (T line; lines(f))
+        {
+            assert(false);
+        }
+        f.close();
+
+        // test looping with a file with three lines
+        std.file.write(deleteme, "Line one\nline two\nline three\n");
+        f.open(deleteme, "r");
+        uint i = 0;
+        foreach (T line; lines(f))
+        {
+            if (i == 0) assert(cast(char[]) line == "Line one\n");
+            else if (i == 1) assert(cast(char[]) line == "line two\n",
+                T.stringof ~ " " ~ cast(char[]) line);
+            else if (i == 2) assert(cast(char[]) line == "line three\n");
+            else assert(false);
+            ++i;
+        }
+        f.close();
+
+        // test looping with a file with three lines, last without a newline
+        std.file.write(deleteme, "Line one\nline two\nline three");
+        f.open(deleteme, "r");
+        i = 0;
+        foreach (T line; lines(f))
+        {
+            if (i == 0) assert(cast(char[]) line == "Line one\n");
+            else if (i == 1) assert(cast(char[]) line == "line two\n");
+            else if (i == 2) assert(cast(char[]) line == "line three");
+            else assert(false);
+            ++i;
+        }
+        f.close();
+
+    }
+
+    foreach (T; AliasSeq!(ubyte[]))
+    {
+        // test looping with a file with three lines, last without a newline
+        // using a counter too this time
+        std.file.write(deleteme, "Line one\nline two\nline three");
+        auto f = File(deleteme, "r");
+        uint i = 0;
+        foreach (ulong j, T line; lines(f))
+        {
+            if (i == 0) assert(cast(char[]) line == "Line one\n");
+            else if (i == 1) assert(cast(char[]) line == "line two\n");
+            else if (i == 2) assert(cast(char[]) line == "line three");
+            else assert(false);
+            ++i;
+        }
+        f.close();
+    }
+}
+
+/**
+Iterates through a file a chunk at a time by using $(D foreach).
+
+Example:
+
+---------
+void main()
+{
+    foreach (ubyte[] buffer; chunks(stdin, 4096))
+    {
+        ... use buffer ...
+    }
+}
+---------
+
+The content of $(D buffer) is reused across calls. In the
+ example above, $(D buffer.length) is 4096 for all iterations,
+ except for the last one, in which case $(D buffer.length) may
+ be less than 4096 (but always greater than zero).
+
+ In case of an I/O error, an $(D StdioException) is thrown.
+*/
+auto chunks(File f, size_t size)
+{
+    return ChunksImpl(f, size);
+}
+private struct ChunksImpl
+{
+    private File f;
+    private size_t size;
+    // private string fileName; // Currently, no use
+
+    this(File f, size_t size)
+    in
+    {
+        assert(size, "size must be larger than 0");
+    }
+    body
+    {
+        this.f = f;
+        this.size = size;
+    }
+
+    int opApply(D)(scope D dg)
+    {
+        import core.stdc.stdlib : alloca;
+        enum maxStackSize = 1024 * 16;
+        ubyte[] buffer = void;
+        if (size < maxStackSize)
+            buffer = (cast(ubyte*) alloca(size))[0 .. size];
+        else
+            buffer = new ubyte[size];
+        size_t r = void;
+        int result = 1;
+        uint tally = 0;
+        while ((r = trustedFread(f._p.handle, buffer)) > 0)
+        {
+            assert(r <= size);
+            if (r != size)
+            {
+                // error occured
+                if (!f.eof) throw new StdioException(null);
+                buffer.length = r;
+            }
+            static if (is(typeof(dg(tally, buffer))))
+            {
+                if ((result = dg(tally, buffer)) != 0) break;
+            }
+            else
+            {
+                if ((result = dg(buffer)) != 0) break;
+            }
+            ++tally;
+        }
+        return result;
+    }
+}
+
+@system unittest
+{
+    static import std.file;
+
+    scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+    auto deleteme = testFilename();
+    scope(exit) { std.file.remove(deleteme); }
+
+    // test looping with an empty file
+    std.file.write(deleteme, "");
+    auto f = File(deleteme, "r");
+    foreach (ubyte[] line; chunks(f, 4))
+    {
+        assert(false);
+    }
+    f.close();
+
+    // test looping with a file with three lines
+    std.file.write(deleteme, "Line one\nline two\nline three\n");
+    f = File(deleteme, "r");
+    uint i = 0;
+    foreach (ubyte[] line; chunks(f, 3))
+    {
+        if (i == 0) assert(cast(char[]) line == "Lin");
+        else if (i == 1) assert(cast(char[]) line == "e o");
+        else if (i == 2) assert(cast(char[]) line == "ne\n");
+        else break;
+        ++i;
+    }
+    f.close();
+}
+
+
+/**
+Writes an array or range to a file.
+Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)).
+Similar to $(REF write, std,file), strings are written as-is,
+rather than encoded according to the $(D File)'s $(HTTP
+en.cppreference.com/w/c/io#Narrow_and_wide_orientation,
+orientation).
+*/
+void toFile(T)(T data, string fileName)
+if (is(typeof(copy(data, stdout.lockingBinaryWriter))))
+{
+    copy(data, File(fileName, "wb").lockingBinaryWriter);
+}
+
+@system unittest
+{
+    static import std.file;
+
+    auto deleteme = testFilename();
+    scope(exit) { std.file.remove(deleteme); }
+
+    "Test".toFile(deleteme);
+    assert(std.file.readText(deleteme) == "Test");
+}
+
+/*********************
+ * Thrown if I/O errors happen.
+ */
+class StdioException : Exception
+{
+    static import core.stdc.errno;
+    /// Operating system error code.
+    uint errno;
+
+/**
+Initialize with a message and an error code.
+*/
+    this(string message, uint e = core.stdc.errno.errno) @trusted
+    {
+        import std.exception : errnoString;
+        errno = e;
+        auto sysmsg = errnoString(errno);
+        // If e is 0, we don't use the system error message.  (The message
+        // is "Success", which is rather pointless for an exception.)
+        super(e == 0 ? message
+                     : (message ? message ~ " (" ~ sysmsg ~ ")" : sysmsg));
+    }
+
+/** Convenience functions that throw an $(D StdioException). */
+    static void opCall(string msg)
+    {
+        throw new StdioException(msg);
+    }
+
+/// ditto
+    static void opCall()
+    {
+        throw new StdioException(null, core.stdc.errno.errno);
+    }
+}
+
+enum StdFileHandle: string
+{
+    stdin  = "core.stdc.stdio.stdin",
+    stdout = "core.stdc.stdio.stdout",
+    stderr = "core.stdc.stdio.stderr",
+}
+
+// Undocumented but public because the std* handles are aliasing it.
+@property ref File makeGlobal(StdFileHandle _iob)()
+{
+    __gshared File.Impl impl;
+    __gshared File result;
+
+    // Use an inline spinlock to make sure the initializer is only run once.
+    // We assume there will be at most uint.max / 2 threads trying to initialize
+    // `handle` at once and steal the high bit to indicate that the globals have
+    // been initialized.
+    static shared uint spinlock;
+    import core.atomic : atomicLoad, atomicOp, MemoryOrder;
+    if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2)
+    {
+        for (;;)
+        {
+            if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max / 2)
+                break;
+            if (atomicOp!"+="(spinlock, 1) == 1)
+            {
+                with (StdFileHandle)
+                    assert(_iob == stdin || _iob == stdout || _iob == stderr);
+                impl.handle = mixin(_iob);
+                result._p = &impl;
+                atomicOp!"+="(spinlock, uint.max / 2);
+                break;
+            }
+            atomicOp!"-="(spinlock, 1);
+        }
+    }
+    return result;
+}
+
+/** The standard input stream.
+    Bugs:
+        Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
+        it is thread un-safe to reassign `stdin` to a different `File` instance
+        than the default.
+*/
+alias stdin = makeGlobal!(StdFileHandle.stdin);
+
+///
+@safe unittest
+{
+    // Read stdin, sort lines, write to stdout
+    import std.algorithm.mutation : copy;
+    import std.algorithm.sorting : sort;
+    import std.array : array;
+    import std.typecons : Yes;
+
+    void main() {
+        stdin                       // read from stdin
+        .byLineCopy(Yes.keepTerminator) // copying each line
+        .array()                    // convert to array of lines
+        .sort()                     // sort the lines
+        .copy(                      // copy output of .sort to an OutputRange
+            stdout.lockingTextWriter()); // the OutputRange
+    }
+}
+
+/**
+    The standard output stream.
+    Bugs:
+        Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
+        it is thread un-safe to reassign `stdout` to a different `File` instance
+        than the default.
+*/
+alias stdout = makeGlobal!(StdFileHandle.stdout);
+
+/**
+    The standard error stream.
+    Bugs:
+        Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
+        it is thread un-safe to reassign `stderr` to a different `File` instance
+        than the default.
+*/
+alias stderr = makeGlobal!(StdFileHandle.stderr);
+
+@system unittest
+{
+    static import std.file;
+    import std.typecons : tuple;
+
+    scope(failure) printf("Failed test at line %d\n", __LINE__);
+    auto deleteme = testFilename();
+
+    std.file.write(deleteme, "1 2\n4 1\n5 100");
+    scope(exit) std.file.remove(deleteme);
+    {
+        File f = File(deleteme);
+        scope(exit) f.close();
+        auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ];
+        uint i;
+        foreach (e; f.byRecord!(int, int)("%s %s"))
+        {
+            //writeln(e);
+            assert(e == t[i++]);
+        }
+        assert(i == 3);
+    }
+}
+
+@safe unittest
+{
+    // Retain backwards compatibility
+    // https://issues.dlang.org/show_bug.cgi?id=17472
+    static assert(is(typeof(stdin) == File));
+    static assert(is(typeof(stdout) == File));
+    static assert(is(typeof(stderr) == File));
+}
+
+// roll our own appender, but with "safe" arrays
+private struct ReadlnAppender
+{
+    char[] buf;
+    size_t pos;
+    bool safeAppend = false;
+
+    void initialize(char[] b)
+    {
+        buf = b;
+        pos = 0;
+    }
+    @property char[] data() @trusted
+    {
+        if (safeAppend)
+            assumeSafeAppend(buf.ptr[0 .. pos]);
+        return buf.ptr[0 .. pos];
+    }
+
+    bool reserveWithoutAllocating(size_t n)
+    {
+        if (buf.length >= pos + n) // buf is already large enough
+            return true;
+
+        immutable curCap = buf.capacity;
+        if (curCap >= pos + n)
+        {
+            buf.length = curCap;
+            /* Any extra capacity we end up not using can safely be claimed
+            by someone else. */
+            safeAppend = true;
+            return true;
+        }
+
+        return false;
+    }
+    void reserve(size_t n) @trusted
+    {
+        import core.stdc.string : memcpy;
+        if (!reserveWithoutAllocating(n))
+        {
+            size_t ncap = buf.length * 2 + 128 + n;
+            char[] nbuf = new char[ncap];
+            memcpy(nbuf.ptr, buf.ptr, pos);
+            buf = nbuf;
+            // Allocated a new buffer. No one else knows about it.
+            safeAppend = true;
+        }
+    }
+    void putchar(char c) @trusted
+    {
+        reserve(1);
+        buf.ptr[pos++] = c;
+    }
+    void putdchar(dchar dc) @trusted
+    {
+        import std.utf : encode, UseReplacementDchar;
+
+        char[4] ubuf;
+        immutable size = encode!(UseReplacementDchar.yes)(ubuf, dc);
+        reserve(size);
+        foreach (c; ubuf)
+            buf.ptr[pos++] = c;
+    }
+    void putonly(char[] b) @trusted
+    {
+        import core.stdc.string : memcpy;
+        assert(pos == 0);   // assume this is the only put call
+        if (reserveWithoutAllocating(b.length))
+            memcpy(buf.ptr + pos, b.ptr, b.length);
+        else
+            buf = b.dup;
+        pos = b.length;
+    }
+}
+
+// Private implementation of readln
+version (DIGITAL_MARS_STDIO)
+private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/)
+{
+    FLOCK(fps);
+    scope(exit) FUNLOCK(fps);
+
+    /* Since fps is now locked, we can create an "unshared" version
+     * of fp.
+     */
+    auto fp = cast(_iobuf*) fps;
+
+    ReadlnAppender app;
+    app.initialize(buf);
+
+    if (__fhnd_info[fp._file] & FHND_WCHAR)
+    {   /* Stream is in wide characters.
+         * Read them and convert to chars.
+         */
+        static assert(wchar_t.sizeof == 2);
+        for (int c = void; (c = FGETWC(fp)) != -1; )
+        {
+            if ((c & ~0x7F) == 0)
+            {
+                app.putchar(cast(char) c);
+                if (c == terminator)
+                    break;
+            }
+            else
+            {
+                if (c >= 0xD800 && c <= 0xDBFF)
+                {
+                    int c2 = void;
+                    if ((c2 = FGETWC(fp)) != -1 ||
+                            c2 < 0xDC00 && c2 > 0xDFFF)
+                    {
+                        StdioException("unpaired UTF-16 surrogate");
+                    }
+                    c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
+                }
+                app.putdchar(cast(dchar) c);
+            }
+        }
+        if (ferror(fps))
+            StdioException();
+    }
+
+    else if (fp._flag & _IONBF)
+    {
+        /* Use this for unbuffered I/O, when running
+         * across buffer boundaries, or for any but the common
+         * cases.
+         */
+      L1:
+        int c;
+        while ((c = FGETC(fp)) != -1)
+        {
+            app.putchar(cast(char) c);
+            if (c == terminator)
+            {
+                buf = app.data;
+                return buf.length;
+            }
+
+        }
+
+        if (ferror(fps))
+            StdioException();
+    }
+    else
+    {
+        int u = fp._cnt;
+        char* p = fp._ptr;
+        int i;
+        if (fp._flag & _IOTRAN)
+        {   /* Translated mode ignores \r and treats ^Z as end-of-file
+             */
+            char c;
+            while (1)
+            {
+                if (i == u)         // if end of buffer
+                    goto L1;        // give up
+                c = p[i];
+                i++;
+                if (c != '\r')
+                {
+                    if (c == terminator)
+                        break;
+                    if (c != 0x1A)
+                        continue;
+                    goto L1;
+                }
+                else
+                {   if (i != u && p[i] == terminator)
+                        break;
+                    goto L1;
+                }
+            }
+            app.putonly(p[0 .. i]);
+            app.buf[i - 1] = cast(char) terminator;
+            if (terminator == '\n' && c == '\r')
+                i++;
+        }
+        else
+        {
+            while (1)
+            {
+                if (i == u)         // if end of buffer
+                    goto L1;        // give up
+                auto c = p[i];
+                i++;
+                if (c == terminator)
+                    break;
+            }
+            app.putonly(p[0 .. i]);
+        }
+        fp._cnt -= i;
+        fp._ptr += i;
+    }
+
+    buf = app.data;
+    return buf.length;
+}
+
+version (MICROSOFT_STDIO)
+private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/)
+{
+    FLOCK(fps);
+    scope(exit) FUNLOCK(fps);
+
+    /* Since fps is now locked, we can create an "unshared" version
+     * of fp.
+     */
+    auto fp = cast(_iobuf*) fps;
+
+    ReadlnAppender app;
+    app.initialize(buf);
+
+    int c;
+    while ((c = FGETC(fp)) != -1)
+    {
+        app.putchar(cast(char) c);
+        if (c == terminator)
+        {
+            buf = app.data;
+            return buf.length;
+        }
+
+    }
+
+    if (ferror(fps))
+        StdioException();
+    buf = app.data;
+    return buf.length;
+}
+
+version (HAS_GETDELIM)
+private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation)
+{
+    import core.stdc.stdlib : free;
+    import core.stdc.wchar_ : fwide;
+
+    if (orientation == File.Orientation.wide)
+    {
+        /* Stream is in wide characters.
+         * Read them and convert to chars.
+         */
+        FLOCK(fps);
+        scope(exit) FUNLOCK(fps);
+        auto fp = cast(_iobuf*) fps;
+        version (Windows)
+        {
+            buf.length = 0;
+            for (int c = void; (c = FGETWC(fp)) != -1; )
+            {
+                if ((c & ~0x7F) == 0)
+                {   buf ~= c;
+                    if (c == terminator)
+                        break;
+                }
+                else
+                {
+                    if (c >= 0xD800 && c <= 0xDBFF)
+                    {
+                        int c2 = void;
+                        if ((c2 = FGETWC(fp)) != -1 ||
+                                c2 < 0xDC00 && c2 > 0xDFFF)
+                        {
+                            StdioException("unpaired UTF-16 surrogate");
+                        }
+                        c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
+                    }
+                    import std.utf : encode;
+                    encode(buf, c);
+                }
+            }
+            if (ferror(fp))
+                StdioException();
+            return buf.length;
+        }
+        else version (Posix)
+        {
+            buf.length = 0;
+            for (int c; (c = FGETWC(fp)) != -1; )
+            {
+                import std.utf : encode;
+
+                if ((c & ~0x7F) == 0)
+                    buf ~= cast(char) c;
+                else
+                    encode(buf, cast(dchar) c);
+                if (c == terminator)
+                    break;
+            }
+            if (ferror(fps))
+                StdioException();
+            return buf.length;
+        }
+        else
+        {
+            static assert(0);
+        }
+    }
+
+    static char *lineptr = null;
+    static size_t n = 0;
+    scope(exit)
+    {
+        if (n > 128 * 1024)
+        {
+            // Bound memory used by readln
+            free(lineptr);
+            lineptr = null;
+            n = 0;
+        }
+    }
+
+    auto s = getdelim(&lineptr, &n, terminator, fps);
+    if (s < 0)
+    {
+        if (ferror(fps))
+            StdioException();
+        buf.length = 0;                // end of file
+        return 0;
+    }
+
+    if (s <= buf.length)
+    {
+        buf = buf[0 .. s];
+        buf[] = lineptr[0 .. s];
+    }
+    else
+    {
+        buf = lineptr[0 .. s].dup;
+    }
+    return s;
+}
+
+version (NO_GETDELIM)
+private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation)
+{
+    import core.stdc.wchar_ : fwide;
+
+    FLOCK(fps);
+    scope(exit) FUNLOCK(fps);
+    auto fp = cast(_iobuf*) fps;
+    if (orientation == File.Orientation.wide)
+    {
+        /* Stream is in wide characters.
+         * Read them and convert to chars.
+         */
+        version (Windows)
+        {
+            buf.length = 0;
+            for (int c; (c = FGETWC(fp)) != -1; )
+            {
+                if ((c & ~0x7F) == 0)
+                {   buf ~= c;
+                    if (c == terminator)
+                        break;
+                }
+                else
+                {
+                    if (c >= 0xD800 && c <= 0xDBFF)
+                    {
+                        int c2 = void;
+                        if ((c2 = FGETWC(fp)) != -1 ||
+                                c2 < 0xDC00 && c2 > 0xDFFF)
+                        {
+                            StdioException("unpaired UTF-16 surrogate");
+                        }
+                        c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
+                    }
+                    import std.utf : encode;
+                    encode(buf, c);
+                }
+            }
+            if (ferror(fp))
+                StdioException();
+            return buf.length;
+        }
+        else version (Posix)
+        {
+            import std.utf : encode;
+            buf.length = 0;
+            for (int c; (c = FGETWC(fp)) != -1; )
+            {
+                if ((c & ~0x7F) == 0)
+                    buf ~= cast(char) c;
+                else
+                    encode(buf, cast(dchar) c);
+                if (c == terminator)
+                    break;
+            }
+            if (ferror(fps))
+                StdioException();
+            return buf.length;
+        }
+        else
+        {
+            static assert(0);
+        }
+    }
+
+    // Narrow stream
+    // First, fill the existing buffer
+    for (size_t bufPos = 0; bufPos < buf.length; )
+    {
+        immutable c = FGETC(fp);
+        if (c == -1)
+        {
+            buf.length = bufPos;
+            goto endGame;
+        }
+        buf[bufPos++] = cast(char) c;
+        if (c == terminator)
+        {
+            // No need to test for errors in file
+            buf.length = bufPos;
+            return bufPos;
+        }
+    }
+    // Then, append to it
+    for (int c; (c = FGETC(fp)) != -1; )
+    {
+        buf ~= cast(char) c;
+        if (c == terminator)
+        {
+            // No need to test for errors in file
+            return buf.length;
+        }
+    }
+
+  endGame:
+    if (ferror(fps))
+        StdioException();
+    return buf.length;
+}
+
+@system unittest
+{
+    static import std.file;
+    auto deleteme = testFilename();
+    scope(exit) std.file.remove(deleteme);
+
+    std.file.write(deleteme, "abcd\n0123456789abcde\n1234\n");
+    File f = File(deleteme, "rb");
+
+    char[] ln = new char[2];
+    char* lnptr = ln.ptr;
+    f.readln(ln);
+
+    assert(ln == "abcd\n");
+    char[] t = ln[0 .. 2];
+    t ~= 't';
+    assert(t == "abt");
+    assert(ln == "abcd\n");  // bug 13856: ln stomped to "abtd"
+
+    // it can also stomp the array length
+    ln = new char[4];
+    lnptr = ln.ptr;
+    f.readln(ln);
+    assert(ln == "0123456789abcde\n");
+
+    char[100] buf;
+    ln = buf[];
+    f.readln(ln);
+    assert(ln == "1234\n");
+    assert(ln.ptr == buf.ptr); // avoid allocation, buffer is good enough
+}
+
+/** Experimental network access via the File interface
+
+        Opens a TCP connection to the given host and port, then returns
+        a File struct with read and write access through the same interface
+        as any other file (meaning writef and the byLine ranges work!).
+
+        Authors:
+                Adam D. Ruppe
+
+        Bugs:
+                Only works on Linux
+*/
+version (linux)
+{
+    File openNetwork(string host, ushort port)
+    {
+        import core.stdc.string : memcpy;
+        import core.sys.posix.arpa.inet : htons;
+        import core.sys.posix.netdb : gethostbyname;
+        import core.sys.posix.netinet.in_ : sockaddr_in;
+        static import core.sys.posix.unistd;
+        static import sock = core.sys.posix.sys.socket;
+        import std.conv : to;
+        import std.exception : enforce;
+        import std.internal.cstring : tempCString;
+
+        auto h = enforce( gethostbyname(host.tempCString()),
+            new StdioException("gethostbyname"));
+
+        int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0);
+        enforce(s != -1, new StdioException("socket"));
+
+        scope(failure)
+        {
+            // want to make sure it doesn't dangle if something throws. Upon
+            // normal exit, the File struct's reference counting takes care of
+            // closing, so we don't need to worry about success
+            core.sys.posix.unistd.close(s);
+        }
+
+        sockaddr_in addr;
+
+        addr.sin_family = sock.AF_INET;
+        addr.sin_port = htons(port);
+        memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length);
+
+        enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
+            new StdioException("Connect failed"));
+
+        File f;
+        f.fdopen(s, "w+", host ~ ":" ~ to!string(port));
+        return f;
+    }
+}
+
+version (unittest) string testFilename(string file = __FILE__, size_t line = __LINE__) @safe
+{
+    import std.conv : text;
+    import std.file : deleteme;
+    import std.path : baseName;
+
+    // filename intentionally contains non-ASCII (Russian) characters for test Issue 7648
+    return text(deleteme, "-детка.", baseName(file), ".", line);
+}