1// Written in the D programming language.
2
3/**
4Standard I/O functions that extend $(B core.stdc.stdio).  $(B core.stdc.stdio)
5is $(D_PARAM public)ally imported when importing $(B std.stdio).
6
7Source: $(PHOBOSSRC std/_stdio.d)
8Copyright: Copyright Digital Mars 2007-.
9License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
10Authors:   $(HTTP digitalmars.com, Walter Bright),
11           $(HTTP erdani.org, Andrei Alexandrescu),
12           Alex R��nne Petersen
13 */
14module std.stdio;
15
16import core.stdc.stddef; // wchar_t
17public import core.stdc.stdio;
18import std.algorithm.mutation; // copy
19import std.meta; // allSatisfy
20import std.range.primitives; // ElementEncodingType, empty, front,
21    // isBidirectionalRange, isInputRange, put
22import std.traits; // isSomeChar, isSomeString, Unqual, isPointer
23import std.typecons; // Flag
24
25/++
26If flag $(D KeepTerminator) is set to $(D KeepTerminator.yes), then the delimiter
27is included in the strings returned.
28+/
29alias KeepTerminator = Flag!"keepTerminator";
30
31version (CRuntime_Microsoft)
32{
33    version = MICROSOFT_STDIO;
34}
35else version (CRuntime_DigitalMars)
36{
37    // Specific to the way Digital Mars C does stdio
38    version = DIGITAL_MARS_STDIO;
39}
40
41version (CRuntime_Glibc)
42{
43    // Specific to the way Gnu C does stdio
44    version = GCC_IO;
45    version = HAS_GETDELIM;
46}
47else version (CRuntime_Bionic)
48{
49    version = GENERIC_IO;
50    version = HAS_GETDELIM;
51}
52else version (CRuntime_Musl)
53{
54    version = GENERIC_IO;
55    version = HAS_GETDELIM;
56}
57
58version (OSX)
59{
60    version = GENERIC_IO;
61    version = HAS_GETDELIM;
62}
63else version (FreeBSD)
64{
65    version = GENERIC_IO;
66    version = HAS_GETDELIM;
67}
68else version (NetBSD)
69{
70    version = GENERIC_IO;
71    version = HAS_GETDELIM;
72}
73else version (DragonFlyBSD)
74{
75    version = GENERIC_IO;
76    version = HAS_GETDELIM;
77}
78else version (Solaris)
79{
80    version = GENERIC_IO;
81    version = NO_GETDELIM;
82}
83
84// Character type used for operating system filesystem APIs
85version (Windows)
86{
87    private alias FSChar = wchar;
88}
89else version (Posix)
90{
91    private alias FSChar = char;
92}
93else
94    static assert(0);
95
96version (Windows)
97{
98    // core.stdc.stdio.fopen expects file names to be
99    // encoded in CP_ACP on Windows instead of UTF-8.
100    /+ Waiting for druntime pull 299
101    +/
102    extern (C) nothrow @nogc FILE* _wfopen(in wchar* filename, in wchar* mode);
103    extern (C) nothrow @nogc FILE* _wfreopen(in wchar* filename, in wchar* mode, FILE* fp);
104
105    import core.sys.windows.windows : HANDLE;
106}
107
108version (DIGITAL_MARS_STDIO)
109{
110    extern (C)
111    {
112        /* **
113         * Digital Mars under-the-hood C I/O functions.
114         * Use _iobuf* for the unshared version of FILE*,
115         * usable when the FILE is locked.
116         */
117      nothrow:
118      @nogc:
119        int _fputc_nlock(int, _iobuf*);
120        int _fputwc_nlock(int, _iobuf*);
121        int _fgetc_nlock(_iobuf*);
122        int _fgetwc_nlock(_iobuf*);
123        int __fp_lock(FILE*);
124        void __fp_unlock(FILE*);
125
126        int setmode(int, int);
127    }
128    alias FPUTC = _fputc_nlock;
129    alias FPUTWC = _fputwc_nlock;
130    alias FGETC = _fgetc_nlock;
131    alias FGETWC = _fgetwc_nlock;
132
133    alias FLOCK = __fp_lock;
134    alias FUNLOCK = __fp_unlock;
135
136    alias _setmode = setmode;
137    enum _O_BINARY = 0x8000;
138    int _fileno(FILE* f) { return f._file; }
139    alias fileno = _fileno;
140}
141else version (MICROSOFT_STDIO)
142{
143    extern (C)
144    {
145        /* **
146         * Microsoft under-the-hood C I/O functions
147         */
148      nothrow:
149      @nogc:
150        int _fputc_nolock(int, _iobuf*);
151        int _fputwc_nolock(int, _iobuf*);
152        int _fgetc_nolock(_iobuf*);
153        int _fgetwc_nolock(_iobuf*);
154        void _lock_file(FILE*);
155        void _unlock_file(FILE*);
156        int _setmode(int, int);
157        int _fileno(FILE*);
158        FILE* _fdopen(int, const (char)*);
159        int _fseeki64(FILE*, long, int);
160        long _ftelli64(FILE*);
161    }
162    alias FPUTC = _fputc_nolock;
163    alias FPUTWC = _fputwc_nolock;
164    alias FGETC = _fgetc_nolock;
165    alias FGETWC = _fgetwc_nolock;
166
167    alias FLOCK = _lock_file;
168    alias FUNLOCK = _unlock_file;
169
170    alias setmode = _setmode;
171    alias fileno = _fileno;
172
173    enum
174    {
175        _O_RDONLY = 0x0000,
176        _O_APPEND = 0x0004,
177        _O_TEXT   = 0x4000,
178        _O_BINARY = 0x8000,
179    }
180}
181else version (GCC_IO)
182{
183    /* **
184     * Gnu under-the-hood C I/O functions; see
185     * http://gnu.org/software/libc/manual/html_node/I_002fO-on-Streams.html
186     */
187    extern (C)
188    {
189      nothrow:
190      @nogc:
191        int fputc_unlocked(int, _iobuf*);
192        int fputwc_unlocked(wchar_t, _iobuf*);
193        int fgetc_unlocked(_iobuf*);
194        int fgetwc_unlocked(_iobuf*);
195        void flockfile(FILE*);
196        void funlockfile(FILE*);
197
198        private size_t fwrite_unlocked(const(void)* ptr,
199                size_t size, size_t n, _iobuf *stream);
200    }
201
202    alias FPUTC = fputc_unlocked;
203    alias FPUTWC = fputwc_unlocked;
204    alias FGETC = fgetc_unlocked;
205    alias FGETWC = fgetwc_unlocked;
206
207    alias FLOCK = flockfile;
208    alias FUNLOCK = funlockfile;
209}
210else version (GENERIC_IO)
211{
212    nothrow:
213    @nogc:
214
215    extern (C)
216    {
217        void flockfile(FILE*);
218        void funlockfile(FILE*);
219    }
220
221    int fputc_unlocked(int c, _iobuf* fp) { return fputc(c, cast(shared) fp); }
222    int fputwc_unlocked(wchar_t c, _iobuf* fp)
223    {
224        import core.stdc.wchar_ : fputwc;
225        return fputwc(c, cast(shared) fp);
226    }
227    int fgetc_unlocked(_iobuf* fp) { return fgetc(cast(shared) fp); }
228    int fgetwc_unlocked(_iobuf* fp)
229    {
230        import core.stdc.wchar_ : fgetwc;
231        return fgetwc(cast(shared) fp);
232    }
233
234    alias FPUTC = fputc_unlocked;
235    alias FPUTWC = fputwc_unlocked;
236    alias FGETC = fgetc_unlocked;
237    alias FGETWC = fgetwc_unlocked;
238
239    alias FLOCK = flockfile;
240    alias FUNLOCK = funlockfile;
241}
242else
243{
244    static assert(0, "unsupported C I/O system");
245}
246
247version (HAS_GETDELIM) extern(C) nothrow @nogc
248{
249    ptrdiff_t getdelim(char**, size_t*, int, FILE*);
250    // getline() always comes together with getdelim()
251    ptrdiff_t getline(char**, size_t*, FILE*);
252}
253
254//------------------------------------------------------------------------------
255struct ByRecord(Fields...)
256{
257private:
258    import std.typecons : Tuple;
259
260    File file;
261    char[] line;
262    Tuple!(Fields) current;
263    string format;
264
265public:
266    this(File f, string format)
267    {
268        assert(f.isOpen);
269        file = f;
270        this.format = format;
271        popFront(); // prime the range
272    }
273
274    /// Range primitive implementations.
275    @property bool empty()
276    {
277        return !file.isOpen;
278    }
279
280    /// Ditto
281    @property ref Tuple!(Fields) front()
282    {
283        return current;
284    }
285
286    /// Ditto
287    void popFront()
288    {
289        import std.conv : text;
290        import std.exception : enforce;
291        import std.format : formattedRead;
292        import std.string : chomp;
293
294        enforce(file.isOpen, "ByRecord: File must be open");
295        file.readln(line);
296        if (!line.length)
297        {
298            file.detach();
299        }
300        else
301        {
302            line = chomp(line);
303            formattedRead(line, format, &current);
304            enforce(line.empty, text("Leftover characters in record: `",
305                            line, "'"));
306        }
307    }
308}
309
310template byRecord(Fields...)
311{
312    ByRecord!(Fields) byRecord(File f, string format)
313    {
314        return typeof(return)(f, format);
315    }
316}
317
318/**
319Encapsulates a $(D FILE*). Generally D does not attempt to provide
320thin wrappers over equivalent functions in the C standard library, but
321manipulating $(D FILE*) values directly is unsafe and error-prone in
322many ways. The $(D File) type ensures safe manipulation, automatic
323file closing, and a lot of convenience.
324
325The underlying $(D FILE*) handle is maintained in a reference-counted
326manner, such that as soon as the last $(D File) variable bound to a
327given $(D FILE*) goes out of scope, the underlying $(D FILE*) is
328automatically closed.
329
330Example:
331----
332// test.d
333void main(string[] args)
334{
335    auto f = File("test.txt", "w"); // open for writing
336    f.write("Hello");
337    if (args.length > 1)
338    {
339        auto g = f; // now g and f write to the same file
340                    // internal reference count is 2
341        g.write(", ", args[1]);
342        // g exits scope, reference count decreases to 1
343    }
344    f.writeln("!");
345    // f exits scope, reference count falls to zero,
346    // underlying `FILE*` is closed.
347}
348----
349$(CONSOLE
350% rdmd test.d Jimmy
351% cat test.txt
352Hello, Jimmy!
353% __
354)
355 */
356struct File
357{
358    import std.range.primitives : ElementEncodingType;
359    import std.traits : isScalarType, isArray;
360    enum Orientation { unknown, narrow, wide }
361
362    private struct Impl
363    {
364        FILE * handle = null; // Is null iff this Impl is closed by another File
365        uint refs = uint.max / 2;
366        bool isPopened; // true iff the stream has been created by popen()
367        Orientation orientation;
368    }
369    private Impl* _p;
370    private string _name;
371
372    package this(FILE* handle, string name, uint refs = 1, bool isPopened = false) @trusted
373    {
374        import core.stdc.stdlib : malloc;
375        import std.exception : enforce;
376
377        assert(!_p);
378        _p = cast(Impl*) enforce(malloc(Impl.sizeof), "Out of memory");
379        _p.handle = handle;
380        _p.refs = refs;
381        _p.isPopened = isPopened;
382        _p.orientation = Orientation.unknown;
383        _name = name;
384    }
385
386/**
387Constructor taking the name of the file to open and the open mode.
388
389Copying one $(D File) object to another results in the two $(D File)
390objects referring to the same underlying file.
391
392The destructor automatically closes the file as soon as no $(D File)
393object refers to it anymore.
394
395Params:
396    name = range or string representing the file _name
397    stdioOpenmode = range or string represting the open mode
398        (with the same semantics as in the C standard library
399        $(HTTP cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen)
400        function)
401
402Throws: $(D ErrnoException) if the file could not be opened.
403 */
404    this(string name, in char[] stdioOpenmode = "rb") @safe
405    {
406        import std.conv : text;
407        import std.exception : errnoEnforce;
408
409        this(errnoEnforce(.fopen(name, stdioOpenmode),
410                        text("Cannot open file `", name, "' in mode `",
411                                stdioOpenmode, "'")),
412                name);
413
414        // MSVCRT workaround (issue 14422)
415        version (MICROSOFT_STDIO)
416        {
417            bool append, update;
418            foreach (c; stdioOpenmode)
419                if (c == 'a')
420                    append = true;
421                else
422                if (c == '+')
423                    update = true;
424            if (append && !update)
425                seek(size);
426        }
427    }
428
429    /// ditto
430    this(R1, R2)(R1 name)
431        if (isInputRange!R1 && isSomeChar!(ElementEncodingType!R1))
432    {
433        import std.conv : to;
434        this(name.to!string, "rb");
435    }
436
437    /// ditto
438    this(R1, R2)(R1 name, R2 mode)
439        if (isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) &&
440            isInputRange!R2 && isSomeChar!(ElementEncodingType!R2))
441    {
442        import std.conv : to;
443        this(name.to!string, mode.to!string);
444    }
445
446    @safe unittest
447    {
448        static import std.file;
449        import std.utf : byChar;
450        auto deleteme = testFilename();
451        auto f = File(deleteme.byChar, "w".byChar);
452        f.close();
453        std.file.remove(deleteme);
454    }
455
456    ~this() @safe
457    {
458        detach();
459    }
460
461    this(this) @safe nothrow
462    {
463        if (!_p) return;
464        assert(_p.refs);
465        ++_p.refs;
466    }
467
468/**
469Assigns a file to another. The target of the assignment gets detached
470from whatever file it was attached to, and attaches itself to the new
471file.
472 */
473    void opAssign(File rhs) @safe
474    {
475        import std.algorithm.mutation : swap;
476
477        swap(this, rhs);
478    }
479
480/**
481First calls $(D detach) (throwing on failure), and then attempts to
482_open file $(D name) with mode $(D stdioOpenmode). The mode has the
483same semantics as in the C standard library $(HTTP
484cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function.
485
486Throws: $(D ErrnoException) in case of error.
487 */
488    void open(string name, in char[] stdioOpenmode = "rb") @safe
489    {
490        detach();
491        this = File(name, stdioOpenmode);
492    }
493
494/**
495Reuses the `File` object to either open a different file, or change
496the file mode. If `name` is `null`, the mode of the currently open
497file is changed; otherwise, a new file is opened, reusing the C
498`FILE*`. The function has the same semantics as in the C standard
499library $(HTTP cplusplus.com/reference/cstdio/freopen/, freopen)
500function.
501
502Note: Calling `reopen` with a `null` `name` is not implemented
503in all C runtimes.
504
505Throws: $(D ErrnoException) in case of error.
506 */
507    void reopen(string name, in char[] stdioOpenmode = "rb") @trusted
508    {
509        import std.conv : text;
510        import std.exception : enforce, errnoEnforce;
511        import std.internal.cstring : tempCString;
512
513        enforce(isOpen, "Attempting to reopen() an unopened file");
514
515        auto namez = (name == null ? _name : name).tempCString!FSChar();
516        auto modez = stdioOpenmode.tempCString!FSChar();
517
518        FILE* fd = _p.handle;
519        version (Windows)
520            fd =  _wfreopen(namez, modez, fd);
521        else
522            fd = freopen(namez, modez, fd);
523
524        errnoEnforce(fd, name
525            ? text("Cannot reopen file `", name, "' in mode `", stdioOpenmode, "'")
526            : text("Cannot reopen file in mode `", stdioOpenmode, "'"));
527
528        if (name !is null)
529            _name = name;
530    }
531
532    @system unittest // Test changing filename
533    {
534        import std.exception : assertThrown, assertNotThrown;
535        static import std.file;
536
537        auto deleteme = testFilename();
538        std.file.write(deleteme, "foo");
539        scope(exit) std.file.remove(deleteme);
540        auto f = File(deleteme);
541        assert(f.readln() == "foo");
542
543        auto deleteme2 = testFilename();
544        std.file.write(deleteme2, "bar");
545        scope(exit) std.file.remove(deleteme2);
546        f.reopen(deleteme2);
547        assert(f.name == deleteme2);
548        assert(f.readln() == "bar");
549        f.close();
550    }
551
552    version (CRuntime_DigitalMars) {} else // Not implemented
553    version (CRuntime_Microsoft) {} else // Not implemented
554    @system unittest // Test changing mode
555    {
556        import std.exception : assertThrown, assertNotThrown;
557        static import std.file;
558
559        auto deleteme = testFilename();
560        std.file.write(deleteme, "foo");
561        scope(exit) std.file.remove(deleteme);
562        auto f = File(deleteme, "r+");
563        assert(f.readln() == "foo");
564        f.reopen(null, "w");
565        f.write("bar");
566        f.seek(0);
567        f.reopen(null, "a");
568        f.write("baz");
569        assert(f.name == deleteme);
570        f.close();
571        assert(std.file.readText(deleteme) == "barbaz");
572    }
573
574/**
575First calls $(D detach) (throwing on failure), and then runs a command
576by calling the C standard library function $(HTTP
577opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen).
578
579Throws: $(D ErrnoException) in case of error.
580 */
581    version (Posix) void popen(string command, in char[] stdioOpenmode = "r") @safe
582    {
583        import std.exception : errnoEnforce;
584
585        detach();
586        this = File(errnoEnforce(.popen(command, stdioOpenmode),
587                        "Cannot run command `"~command~"'"),
588                command, 1, true);
589    }
590
591/**
592First calls $(D detach) (throwing on failure), and then attempts to
593associate the given file descriptor with the $(D File). The mode must
594be compatible with the mode of the file descriptor.
595
596Throws: $(D ErrnoException) in case of error.
597 */
598    void fdopen(int fd, in char[] stdioOpenmode = "rb") @safe
599    {
600        fdopen(fd, stdioOpenmode, null);
601    }
602
603    package void fdopen(int fd, in char[] stdioOpenmode, string name) @trusted
604    {
605        import std.exception : errnoEnforce;
606        import std.internal.cstring : tempCString;
607
608        auto modez = stdioOpenmode.tempCString();
609        detach();
610
611        version (DIGITAL_MARS_STDIO)
612        {
613            // This is a re-implementation of DMC's fdopen, but without the
614            // mucking with the file descriptor.  POSIX standard requires the
615            // new fdopen'd file to retain the given file descriptor's
616            // position.
617            import core.stdc.stdio : fopen;
618            auto fp = fopen("NUL", modez);
619            errnoEnforce(fp, "Cannot open placeholder NUL stream");
620            FLOCK(fp);
621            auto iob = cast(_iobuf*) fp;
622            .close(iob._file);
623            iob._file = fd;
624            iob._flag &= ~_IOTRAN;
625            FUNLOCK(fp);
626        }
627        else
628        {
629            version (Windows) // MSVCRT
630                auto fp = _fdopen(fd, modez);
631            else version (Posix)
632            {
633                import core.sys.posix.stdio : fdopen;
634                auto fp = fdopen(fd, modez);
635            }
636            errnoEnforce(fp);
637        }
638        this = File(fp, name);
639    }
640
641    // Declare a dummy HANDLE to allow generating documentation
642    // for Windows-only methods.
643    version (StdDdoc) { version (Windows) {} else alias HANDLE = int; }
644
645/**
646First calls $(D detach) (throwing on failure), and then attempts to
647associate the given Windows $(D HANDLE) with the $(D File). The mode must
648be compatible with the access attributes of the handle. Windows only.
649
650Throws: $(D ErrnoException) in case of error.
651*/
652    version (StdDdoc)
653    void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode);
654
655    version (Windows)
656    void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode)
657    {
658        import core.stdc.stdint : intptr_t;
659        import std.exception : errnoEnforce;
660        import std.format : format;
661
662        // Create file descriptors from the handles
663        version (DIGITAL_MARS_STDIO)
664            auto fd = _handleToFD(handle, FHND_DEVICE);
665        else // MSVCRT
666        {
667            int mode;
668            modeLoop:
669            foreach (c; stdioOpenmode)
670                switch (c)
671                {
672                    case 'r': mode |= _O_RDONLY; break;
673                    case '+': mode &=~_O_RDONLY; break;
674                    case 'a': mode |= _O_APPEND; break;
675                    case 'b': mode |= _O_BINARY; break;
676                    case 't': mode |= _O_TEXT;   break;
677                    case ',': break modeLoop;
678                    default: break;
679                }
680
681            auto fd = _open_osfhandle(cast(intptr_t) handle, mode);
682        }
683
684        errnoEnforce(fd >= 0, "Cannot open Windows HANDLE");
685        fdopen(fd, stdioOpenmode, "HANDLE(%s)".format(handle));
686    }
687
688
689/** Returns $(D true) if the file is opened. */
690    @property bool isOpen() const @safe pure nothrow
691    {
692        return _p !is null && _p.handle;
693    }
694
695/**
696Returns $(D true) if the file is at end (see $(HTTP
697cplusplus.com/reference/clibrary/cstdio/feof.html, feof)).
698
699Throws: $(D Exception) if the file is not opened.
700 */
701    @property bool eof() const @trusted pure
702    {
703        import std.exception : enforce;
704
705        enforce(_p && _p.handle, "Calling eof() against an unopened file.");
706        return .feof(cast(FILE*) _p.handle) != 0;
707    }
708
709/** Returns the name of the last opened file, if any.
710If a $(D File) was created with $(LREF tmpfile) and $(LREF wrapFile)
711it has no name.*/
712    @property string name() const @safe pure nothrow
713    {
714        return _name;
715    }
716
717/**
718If the file is not opened, returns $(D true). Otherwise, returns
719$(HTTP cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for
720the file handle.
721 */
722    @property bool error() const @trusted pure nothrow
723    {
724        return !isOpen || .ferror(cast(FILE*) _p.handle);
725    }
726
727    @safe unittest
728    {
729        // Issue 12349
730        static import std.file;
731        auto deleteme = testFilename();
732        auto f = File(deleteme, "w");
733        scope(exit) std.file.remove(deleteme);
734
735        f.close();
736        assert(f.error);
737    }
738
739/**
740Detaches from the underlying file. If the sole owner, calls $(D close).
741
742Throws: $(D ErrnoException) on failure if closing the file.
743  */
744    void detach() @safe
745    {
746        if (!_p) return;
747        if (_p.refs == 1)
748            close();
749        else
750        {
751            assert(_p.refs);
752            --_p.refs;
753            _p = null;
754        }
755    }
756
757    @safe unittest
758    {
759        static import std.file;
760
761        auto deleteme = testFilename();
762        scope(exit) std.file.remove(deleteme);
763        auto f = File(deleteme, "w");
764        {
765            auto f2 = f;
766            f2.detach();
767        }
768        assert(f._p.refs == 1);
769        f.close();
770    }
771
772/**
773If the file was unopened, succeeds vacuously. Otherwise closes the
774file (by calling $(HTTP
775cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)),
776throwing on error. Even if an exception is thrown, afterwards the $(D
777File) object is empty. This is different from $(D detach) in that it
778always closes the file; consequently, all other $(D File) objects
779referring to the same handle will see a closed file henceforth.
780
781Throws: $(D ErrnoException) on error.
782 */
783    void close() @trusted
784    {
785        import core.stdc.stdlib : free;
786        import std.exception : errnoEnforce;
787
788        if (!_p) return; // succeed vacuously
789        scope(exit)
790        {
791            assert(_p.refs);
792            if (!--_p.refs)
793                free(_p);
794            _p = null; // start a new life
795        }
796        if (!_p.handle) return; // Impl is closed by another File
797
798        scope(exit) _p.handle = null; // nullify the handle anyway
799        version (Posix)
800        {
801            import core.sys.posix.stdio : pclose;
802            import std.format : format;
803
804            if (_p.isPopened)
805            {
806                auto res = pclose(_p.handle);
807                errnoEnforce(res != -1,
808                        "Could not close pipe `"~_name~"'");
809                errnoEnforce(res == 0, format("Command returned %d", res));
810                return;
811            }
812        }
813        errnoEnforce(.fclose(_p.handle) == 0,
814                "Could not close file `"~_name~"'");
815    }
816
817/**
818If the file is not opened, succeeds vacuously. Otherwise, returns
819$(HTTP cplusplus.com/reference/clibrary/cstdio/_clearerr.html,
820_clearerr) for the file handle.
821 */
822    void clearerr() @safe pure nothrow
823    {
824        _p is null || _p.handle is null ||
825        .clearerr(_p.handle);
826    }
827
828/**
829Flushes the C $(D FILE) buffers.
830
831Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush)
832for the file handle.
833
834Throws: $(D Exception) if the file is not opened or if the call to $(D fflush) fails.
835 */
836    void flush() @trusted
837    {
838        import std.exception : enforce, errnoEnforce;
839
840        enforce(isOpen, "Attempting to flush() in an unopened file");
841        errnoEnforce(.fflush(_p.handle) == 0);
842    }
843
844    @safe unittest
845    {
846        // Issue 12349
847        import std.exception : assertThrown;
848        static import std.file;
849
850        auto deleteme = testFilename();
851        auto f = File(deleteme, "w");
852        scope(exit) std.file.remove(deleteme);
853
854        f.close();
855        assertThrown(f.flush());
856    }
857
858/**
859Forces any data buffered by the OS to be written to disk.
860Call $(LREF flush) before calling this function to flush the C $(D FILE) buffers first.
861
862This function calls
863$(HTTP msdn.microsoft.com/en-us/library/windows/desktop/aa364439%28v=vs.85%29.aspx,
864$(D FlushFileBuffers)) on Windows and
865$(HTTP pubs.opengroup.org/onlinepubs/7908799/xsh/fsync.html,
866$(D fsync)) on POSIX for the file handle.
867
868Throws: $(D Exception) if the file is not opened or if the OS call fails.
869 */
870    void sync() @trusted
871    {
872        import std.exception : enforce;
873
874        enforce(isOpen, "Attempting to sync() an unopened file");
875
876        version (Windows)
877        {
878            import core.sys.windows.windows : FlushFileBuffers;
879            wenforce(FlushFileBuffers(windowsHandle), "FlushFileBuffers failed");
880        }
881        else
882        {
883            import core.sys.posix.unistd : fsync;
884            import std.exception : errnoEnforce;
885            errnoEnforce(fsync(fileno) == 0, "fsync failed");
886        }
887    }
888
889/**
890Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the
891file handle. The number of items to read and the size of
892each item is inferred from the size and type of the input array, respectively.
893
894Returns: The slice of $(D buffer) containing the data that was actually read.
895This will be shorter than $(D buffer) if EOF was reached before the buffer
896could be filled.
897
898Throws: $(D Exception) if $(D buffer) is empty.
899        $(D ErrnoException) if the file is not opened or the call to $(D fread) fails.
900
901$(D rawRead) always reads in binary mode on Windows.
902 */
903    T[] rawRead(T)(T[] buffer)
904    {
905        import std.exception : errnoEnforce;
906
907        if (!buffer.length)
908            throw new Exception("rawRead must take a non-empty buffer");
909        version (Windows)
910        {
911            immutable fd = ._fileno(_p.handle);
912            immutable mode = ._setmode(fd, _O_BINARY);
913            scope(exit) ._setmode(fd, mode);
914            version (DIGITAL_MARS_STDIO)
915            {
916                import core.atomic : atomicOp;
917
918                // @@@BUG@@@ 4243
919                immutable info = __fhnd_info[fd];
920                atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
921                scope(exit) __fhnd_info[fd] = info;
922            }
923        }
924        immutable freadResult = trustedFread(_p.handle, buffer);
925        assert(freadResult <= buffer.length); // fread return guarantee
926        if (freadResult != buffer.length) // error or eof
927        {
928            errnoEnforce(!error);
929            return buffer[0 .. freadResult];
930        }
931        return buffer;
932    }
933
934    ///
935    @system unittest
936    {
937        static import std.file;
938
939        auto testFile = testFilename();
940        std.file.write(testFile, "\r\n\n\r\n");
941        scope(exit) std.file.remove(testFile);
942
943        auto f = File(testFile, "r");
944        auto buf = f.rawRead(new char[5]);
945        f.close();
946        assert(buf == "\r\n\n\r\n");
947    }
948
949/**
950Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file
951handle. The number of items to write and the size of each
952item is inferred from the size and type of the input array, respectively. An
953error is thrown if the buffer could not be written in its entirety.
954
955$(D rawWrite) always writes in binary mode on Windows.
956
957Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwrite) fails.
958 */
959    void rawWrite(T)(in T[] buffer)
960    {
961        import std.conv : text;
962        import std.exception : errnoEnforce;
963
964        version (Windows)
965        {
966            flush(); // before changing translation mode
967            immutable fd = ._fileno(_p.handle);
968            immutable mode = ._setmode(fd, _O_BINARY);
969            scope(exit) ._setmode(fd, mode);
970            version (DIGITAL_MARS_STDIO)
971            {
972                import core.atomic : atomicOp;
973
974                // @@@BUG@@@ 4243
975                immutable info = __fhnd_info[fd];
976                atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
977                scope(exit) __fhnd_info[fd] = info;
978            }
979            scope(exit) flush(); // before restoring translation mode
980        }
981        auto result = trustedFwrite(_p.handle, buffer);
982        if (result == result.max) result = 0;
983        errnoEnforce(result == buffer.length,
984                text("Wrote ", result, " instead of ", buffer.length,
985                        " objects of type ", T.stringof, " to file `",
986                        _name, "'"));
987    }
988
989    ///
990    @system unittest
991    {
992        static import std.file;
993
994        auto testFile = testFilename();
995        auto f = File(testFile, "w");
996        scope(exit) std.file.remove(testFile);
997
998        f.rawWrite("\r\n\n\r\n");
999        f.close();
1000        assert(std.file.read(testFile) == "\r\n\n\r\n");
1001    }
1002
1003/**
1004Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek)
1005for the file handle.
1006
1007Throws: $(D Exception) if the file is not opened.
1008        $(D ErrnoException) if the call to $(D fseek) fails.
1009 */
1010    void seek(long offset, int origin = SEEK_SET) @trusted
1011    {
1012        import std.conv : to, text;
1013        import std.exception : enforce, errnoEnforce;
1014
1015        enforce(isOpen, "Attempting to seek() in an unopened file");
1016        version (Windows)
1017        {
1018            version (CRuntime_Microsoft)
1019            {
1020                alias fseekFun = _fseeki64;
1021                alias off_t = long;
1022            }
1023            else
1024            {
1025                alias fseekFun = fseek;
1026                alias off_t = int;
1027            }
1028        }
1029        else version (Posix)
1030        {
1031            import core.sys.posix.stdio : fseeko, off_t;
1032            alias fseekFun = fseeko;
1033        }
1034        errnoEnforce(fseekFun(_p.handle, to!off_t(offset), origin) == 0,
1035                "Could not seek in file `"~_name~"'");
1036    }
1037
1038    @system unittest
1039    {
1040        import std.conv : text;
1041        static import std.file;
1042
1043        auto deleteme = testFilename();
1044        auto f = File(deleteme, "w+");
1045        scope(exit) { f.close(); std.file.remove(deleteme); }
1046        f.rawWrite("abcdefghijklmnopqrstuvwxyz");
1047        f.seek(7);
1048        assert(f.readln() == "hijklmnopqrstuvwxyz");
1049
1050        version (CRuntime_DigitalMars)
1051            auto bigOffset = int.max - 100;
1052        else
1053        version (CRuntime_Bionic)
1054            auto bigOffset = int.max - 100;
1055        else
1056            auto bigOffset = cast(ulong) int.max + 100;
1057        f.seek(bigOffset);
1058        assert(f.tell == bigOffset, text(f.tell));
1059        // Uncomment the tests below only if you want to wait for
1060        // a long time
1061        // f.rawWrite("abcdefghijklmnopqrstuvwxyz");
1062        // f.seek(-3, SEEK_END);
1063        // assert(f.readln() == "xyz");
1064    }
1065
1066/**
1067Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/ftell.html, ftell) for the
1068managed file handle.
1069
1070Throws: $(D Exception) if the file is not opened.
1071        $(D ErrnoException) if the call to $(D ftell) fails.
1072 */
1073    @property ulong tell() const @trusted
1074    {
1075        import std.exception : enforce, errnoEnforce;
1076
1077        enforce(isOpen, "Attempting to tell() in an unopened file");
1078        version (Windows)
1079        {
1080            version (CRuntime_Microsoft)
1081                immutable result = _ftelli64(cast(FILE*) _p.handle);
1082            else
1083                immutable result = ftell(cast(FILE*) _p.handle);
1084        }
1085        else version (Posix)
1086        {
1087            import core.sys.posix.stdio : ftello;
1088            immutable result = ftello(cast(FILE*) _p.handle);
1089        }
1090        errnoEnforce(result != -1,
1091                "Query ftell() failed for file `"~_name~"'");
1092        return result;
1093    }
1094
1095    ///
1096    @system unittest
1097    {
1098        import std.conv : text;
1099        static import std.file;
1100
1101        auto testFile = testFilename();
1102        std.file.write(testFile, "abcdefghijklmnopqrstuvwqxyz");
1103        scope(exit) { std.file.remove(testFile); }
1104
1105        auto f = File(testFile);
1106        auto a = new ubyte[4];
1107        f.rawRead(a);
1108        assert(f.tell == 4, text(f.tell));
1109    }
1110
1111/**
1112Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind)
1113for the file handle.
1114
1115Throws: $(D Exception) if the file is not opened.
1116 */
1117    void rewind() @safe
1118    {
1119        import std.exception : enforce;
1120
1121        enforce(isOpen, "Attempting to rewind() an unopened file");
1122        .rewind(_p.handle);
1123    }
1124
1125/**
1126Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for
1127the file handle.
1128
1129Throws: $(D Exception) if the file is not opened.
1130        $(D ErrnoException) if the call to $(D setvbuf) fails.
1131 */
1132    void setvbuf(size_t size, int mode = _IOFBF) @trusted
1133    {
1134        import std.exception : enforce, errnoEnforce;
1135
1136        enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
1137        errnoEnforce(.setvbuf(_p.handle, null, mode, size) == 0,
1138                "Could not set buffering for file `"~_name~"'");
1139    }
1140
1141/**
1142Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html,
1143_setvbuf) for the file handle.
1144
1145Throws: $(D Exception) if the file is not opened.
1146        $(D ErrnoException) if the call to $(D setvbuf) fails.
1147*/
1148    void setvbuf(void[] buf, int mode = _IOFBF) @trusted
1149    {
1150        import std.exception : enforce, errnoEnforce;
1151
1152        enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
1153        errnoEnforce(.setvbuf(_p.handle,
1154                        cast(char*) buf.ptr, mode, buf.length) == 0,
1155                "Could not set buffering for file `"~_name~"'");
1156    }
1157
1158
1159    version (Windows)
1160    {
1161        import core.sys.windows.windows : ULARGE_INTEGER, OVERLAPPED, BOOL;
1162
1163        private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length,
1164            Flags flags)
1165        {
1166            if (!start && !length)
1167                length = ulong.max;
1168            ULARGE_INTEGER liStart = void, liLength = void;
1169            liStart.QuadPart = start;
1170            liLength.QuadPart = length;
1171            OVERLAPPED overlapped;
1172            overlapped.Offset = liStart.LowPart;
1173            overlapped.OffsetHigh = liStart.HighPart;
1174            overlapped.hEvent = null;
1175            return F(windowsHandle, flags, 0, liLength.LowPart,
1176                liLength.HighPart, &overlapped);
1177        }
1178
1179        private static T wenforce(T)(T cond, string str)
1180        {
1181            import core.sys.windows.windows : GetLastError;
1182            import std.windows.syserror : sysErrorString;
1183
1184            if (cond) return cond;
1185            throw new Exception(str ~ ": " ~ sysErrorString(GetLastError()));
1186        }
1187    }
1188    version (Posix)
1189    {
1190        private int lockImpl(int operation, short l_type,
1191            ulong start, ulong length)
1192        {
1193            import core.sys.posix.fcntl : fcntl, flock, off_t;
1194            import core.sys.posix.unistd : getpid;
1195            import std.conv : to;
1196
1197            flock fl = void;
1198            fl.l_type   = l_type;
1199            fl.l_whence = SEEK_SET;
1200            fl.l_start  = to!off_t(start);
1201            fl.l_len    = to!off_t(length);
1202            fl.l_pid    = getpid();
1203            return fcntl(fileno, operation, &fl);
1204        }
1205    }
1206
1207/**
1208Locks the specified file segment. If the file segment is already locked
1209by another process, waits until the existing lock is released.
1210If both $(D start) and $(D length) are zero, the entire file is locked.
1211
1212Locks created using $(D lock) and $(D tryLock) have the following properties:
1213$(UL
1214 $(LI All locks are automatically released when the process terminates.)
1215 $(LI Locks are not inherited by child processes.)
1216 $(LI Closing a file will release all locks associated with the file. On POSIX,
1217      even locks acquired via a different $(D File) will be released as well.)
1218 $(LI Not all NFS implementations correctly implement file locking.)
1219)
1220 */
1221    void lock(LockType lockType = LockType.readWrite,
1222        ulong start = 0, ulong length = 0)
1223    {
1224        import std.exception : enforce;
1225
1226        enforce(isOpen, "Attempting to call lock() on an unopened file");
1227        version (Posix)
1228        {
1229            import core.sys.posix.fcntl : F_RDLCK, F_SETLKW, F_WRLCK;
1230            import std.exception : errnoEnforce;
1231            immutable short type = lockType == LockType.readWrite
1232                ? F_WRLCK : F_RDLCK;
1233            errnoEnforce(lockImpl(F_SETLKW, type, start, length) != -1,
1234                    "Could not set lock for file `"~_name~"'");
1235        }
1236        else
1237        version (Windows)
1238        {
1239            import core.sys.windows.windows : LockFileEx, LOCKFILE_EXCLUSIVE_LOCK;
1240            immutable type = lockType == LockType.readWrite ?
1241                LOCKFILE_EXCLUSIVE_LOCK : 0;
1242            wenforce(lockImpl!LockFileEx(start, length, type),
1243                    "Could not set lock for file `"~_name~"'");
1244        }
1245        else
1246            static assert(false);
1247    }
1248
1249/**
1250Attempts to lock the specified file segment.
1251If both $(D start) and $(D length) are zero, the entire file is locked.
1252Returns: $(D true) if the lock was successful, and $(D false) if the
1253specified file segment was already locked.
1254 */
1255    bool tryLock(LockType lockType = LockType.readWrite,
1256        ulong start = 0, ulong length = 0)
1257    {
1258        import std.exception : enforce;
1259
1260        enforce(isOpen, "Attempting to call tryLock() on an unopened file");
1261        version (Posix)
1262        {
1263            import core.stdc.errno : EACCES, EAGAIN, errno;
1264            import core.sys.posix.fcntl : F_RDLCK, F_SETLK, F_WRLCK;
1265            import std.exception : errnoEnforce;
1266            immutable short type = lockType == LockType.readWrite
1267                ? F_WRLCK : F_RDLCK;
1268            immutable res = lockImpl(F_SETLK, type, start, length);
1269            if (res == -1 && (errno == EACCES || errno == EAGAIN))
1270                return false;
1271            errnoEnforce(res != -1, "Could not set lock for file `"~_name~"'");
1272            return true;
1273        }
1274        else
1275        version (Windows)
1276        {
1277            import core.sys.windows.windows : GetLastError, LockFileEx, LOCKFILE_EXCLUSIVE_LOCK,
1278                ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, LOCKFILE_FAIL_IMMEDIATELY;
1279            immutable type = lockType == LockType.readWrite
1280                ? LOCKFILE_EXCLUSIVE_LOCK : 0;
1281            immutable res = lockImpl!LockFileEx(start, length,
1282                type | LOCKFILE_FAIL_IMMEDIATELY);
1283            if (!res && (GetLastError() == ERROR_IO_PENDING
1284                || GetLastError() == ERROR_LOCK_VIOLATION))
1285                return false;
1286            wenforce(res, "Could not set lock for file `"~_name~"'");
1287            return true;
1288        }
1289        else
1290            static assert(false);
1291    }
1292
1293/**
1294Removes the lock over the specified file segment.
1295 */
1296    void unlock(ulong start = 0, ulong length = 0)
1297    {
1298        import std.exception : enforce;
1299
1300        enforce(isOpen, "Attempting to call unlock() on an unopened file");
1301        version (Posix)
1302        {
1303            import core.sys.posix.fcntl : F_SETLK, F_UNLCK;
1304            import std.exception : errnoEnforce;
1305            errnoEnforce(lockImpl(F_SETLK, F_UNLCK, start, length) != -1,
1306                    "Could not remove lock for file `"~_name~"'");
1307        }
1308        else
1309        version (Windows)
1310        {
1311            import core.sys.windows.windows : UnlockFileEx;
1312            wenforce(lockImpl!UnlockFileEx(start, length),
1313                "Could not remove lock for file `"~_name~"'");
1314        }
1315        else
1316            static assert(false);
1317    }
1318
1319    version (Windows)
1320    @system unittest
1321    {
1322        static import std.file;
1323        auto deleteme = testFilename();
1324        scope(exit) std.file.remove(deleteme);
1325        auto f = File(deleteme, "wb");
1326        assert(f.tryLock());
1327        auto g = File(deleteme, "wb");
1328        assert(!g.tryLock());
1329        assert(!g.tryLock(LockType.read));
1330        f.unlock();
1331        f.lock(LockType.read);
1332        assert(!g.tryLock());
1333        assert(g.tryLock(LockType.read));
1334        f.unlock();
1335        g.unlock();
1336    }
1337
1338    version (Posix)
1339    @system unittest
1340    {
1341        static import std.file;
1342        auto deleteme = testFilename();
1343        scope(exit) std.file.remove(deleteme);
1344
1345        // Since locks are per-process, we cannot test lock failures within
1346        // the same process. fork() is used to create a second process.
1347        static void runForked(void delegate() code)
1348        {
1349            import core.stdc.stdlib : exit;
1350            import core.sys.posix.sys.wait : wait;
1351            import core.sys.posix.unistd : fork;
1352            int child, status;
1353            if ((child = fork()) == 0)
1354            {
1355                code();
1356                exit(0);
1357            }
1358            else
1359            {
1360                assert(wait(&status) != -1);
1361                assert(status == 0, "Fork crashed");
1362            }
1363        }
1364
1365        auto f = File(deleteme, "w+b");
1366
1367        runForked
1368        ({
1369            auto g = File(deleteme, "a+b");
1370            assert(g.tryLock());
1371            g.unlock();
1372            assert(g.tryLock(LockType.read));
1373        });
1374
1375        assert(f.tryLock());
1376        runForked
1377        ({
1378            auto g = File(deleteme, "a+b");
1379            assert(!g.tryLock());
1380            assert(!g.tryLock(LockType.read));
1381        });
1382        f.unlock();
1383
1384        f.lock(LockType.read);
1385        runForked
1386        ({
1387            auto g = File(deleteme, "a+b");
1388            assert(!g.tryLock());
1389            assert(g.tryLock(LockType.read));
1390            g.unlock();
1391        });
1392        f.unlock();
1393    }
1394
1395
1396/**
1397Writes its arguments in text format to the file.
1398
1399Throws: $(D Exception) if the file is not opened.
1400        $(D ErrnoException) on an error writing to the file.
1401*/
1402    void write(S...)(S args)
1403    {
1404        import std.traits : isBoolean, isIntegral, isAggregateType;
1405        auto w = lockingTextWriter();
1406        foreach (arg; args)
1407        {
1408            alias A = typeof(arg);
1409            static if (isAggregateType!A || is(A == enum))
1410            {
1411                import std.format : formattedWrite;
1412
1413                formattedWrite(w, "%s", arg);
1414            }
1415            else static if (isSomeString!A)
1416            {
1417                put(w, arg);
1418            }
1419            else static if (isIntegral!A)
1420            {
1421                import std.conv : toTextRange;
1422
1423                toTextRange(arg, w);
1424            }
1425            else static if (isBoolean!A)
1426            {
1427                put(w, arg ? "true" : "false");
1428            }
1429            else static if (isSomeChar!A)
1430            {
1431                put(w, arg);
1432            }
1433            else
1434            {
1435                import std.format : formattedWrite;
1436
1437                // Most general case
1438                formattedWrite(w, "%s", arg);
1439            }
1440        }
1441    }
1442
1443/**
1444Writes its arguments in text format to the file, followed by a newline.
1445
1446Throws: $(D Exception) if the file is not opened.
1447        $(D ErrnoException) on an error writing to the file.
1448*/
1449    void writeln(S...)(S args)
1450    {
1451        write(args, '\n');
1452    }
1453
1454/**
1455Writes its arguments in text format to the file, according to the
1456format string fmt.
1457
1458Params:
1459fmt = The $(LINK2 std_format.html#format-string, format string).
1460When passed as a compile-time argument, the string will be statically checked
1461against the argument types passed.
1462args = Items to write.
1463
1464Throws: $(D Exception) if the file is not opened.
1465        $(D ErrnoException) on an error writing to the file.
1466*/
1467    void writef(alias fmt, A...)(A args)
1468    if (isSomeString!(typeof(fmt)))
1469    {
1470        import std.format : checkFormatException;
1471
1472        alias e = checkFormatException!(fmt, A);
1473        static assert(!e, e.msg);
1474        return this.writef(fmt, args);
1475    }
1476
1477    /// ditto
1478    void writef(Char, A...)(in Char[] fmt, A args)
1479    {
1480        import std.format : formattedWrite;
1481
1482        formattedWrite(lockingTextWriter(), fmt, args);
1483    }
1484
1485    /// Equivalent to `file.writef(fmt, args, '\n')`.
1486    void writefln(alias fmt, A...)(A args)
1487    if (isSomeString!(typeof(fmt)))
1488    {
1489        import std.format : checkFormatException;
1490
1491        alias e = checkFormatException!(fmt, A);
1492        static assert(!e, e.msg);
1493        return this.writefln(fmt, args);
1494    }
1495
1496    /// ditto
1497    void writefln(Char, A...)(in Char[] fmt, A args)
1498    {
1499        import std.format : formattedWrite;
1500
1501        auto w = lockingTextWriter();
1502        formattedWrite(w, fmt, args);
1503        w.put('\n');
1504    }
1505
1506/**
1507Read line from the file handle and return it as a specified type.
1508
1509This version manages its own read buffer, which means one memory allocation per call. If you are not
1510retaining a reference to the read data, consider the $(D File.readln(buf)) version, which may offer
1511better performance as it can reuse its read buffer.
1512
1513Params:
1514    S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
1515    terminator = Line terminator (by default, $(D '\n')).
1516
1517Note:
1518    String terminators are not supported due to ambiguity with readln(buf) below.
1519
1520Returns:
1521    The line that was read, including the line terminator character.
1522
1523Throws:
1524    $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
1525
1526Example:
1527---
1528// Reads `stdin` and writes it to `stdout`.
1529import std.stdio;
1530
1531void main()
1532{
1533    string line;
1534    while ((line = stdin.readln()) !is null)
1535        write(line);
1536}
1537---
1538*/
1539    S readln(S = string)(dchar terminator = '\n')
1540    if (isSomeString!S)
1541    {
1542        Unqual!(ElementEncodingType!S)[] buf;
1543        readln(buf, terminator);
1544        return cast(S) buf;
1545    }
1546
1547    @system unittest
1548    {
1549        import std.algorithm.comparison : equal;
1550        static import std.file;
1551        import std.meta : AliasSeq;
1552
1553        auto deleteme = testFilename();
1554        std.file.write(deleteme, "hello\nworld\n");
1555        scope(exit) std.file.remove(deleteme);
1556        foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
1557        {
1558            auto witness = [ "hello\n", "world\n" ];
1559            auto f = File(deleteme);
1560            uint i = 0;
1561            String buf;
1562            while ((buf = f.readln!String()).length)
1563            {
1564                assert(i < witness.length);
1565                assert(equal(buf, witness[i++]));
1566            }
1567            assert(i == witness.length);
1568        }
1569    }
1570
1571    @system unittest
1572    {
1573        static import std.file;
1574        import std.typecons : Tuple;
1575
1576        auto deleteme = testFilename();
1577        std.file.write(deleteme, "cze���� \U0002000D");
1578        scope(exit) std.file.remove(deleteme);
1579        uint[] lengths = [12,8,7];
1580        foreach (uint i, C; Tuple!(char, wchar, dchar).Types)
1581        {
1582            immutable(C)[] witness = "cze���� \U0002000D";
1583            auto buf = File(deleteme).readln!(immutable(C)[])();
1584            assert(buf.length == lengths[i]);
1585            assert(buf == witness);
1586        }
1587    }
1588
1589/**
1590Read line from the file handle and write it to $(D buf[]), including
1591terminating character.
1592
1593This can be faster than $(D line = File.readln()) because you can reuse
1594the buffer for each call. Note that reusing the buffer means that you
1595must copy the previous contents if you wish to retain them.
1596
1597Params:
1598buf = Buffer used to store the resulting line data. buf is
1599resized as necessary.
1600terminator = Line terminator (by default, $(D '\n')). Use
1601$(REF newline, std,ascii) for portability (unless the file was opened in
1602text mode).
1603
1604Returns:
16050 for end of file, otherwise number of characters read
1606
1607Throws: $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode
1608conversion error.
1609
1610Example:
1611---
1612// Read lines from `stdin` into a string
1613// Ignore lines starting with '#'
1614// Write the string to `stdout`
1615
1616void main()
1617{
1618    string output;
1619    char[] buf;
1620
1621    while (stdin.readln(buf))
1622    {
1623        if (buf[0] == '#')
1624            continue;
1625
1626        output ~= buf;
1627    }
1628
1629    write(output);
1630}
1631---
1632
1633This method can be more efficient than the one in the previous example
1634because $(D stdin.readln(buf)) reuses (if possible) memory allocated
1635for $(D buf), whereas $(D line = stdin.readln()) makes a new memory allocation
1636for every line.
1637
1638For even better performance you can help $(D readln) by passing in a
1639large buffer to avoid memory reallocations. This can be done by reusing the
1640largest buffer returned by $(D readln):
1641
1642Example:
1643---
1644// Read lines from `stdin` and count words
1645
1646void main()
1647{
1648    char[] buf;
1649    size_t words = 0;
1650
1651    while (!stdin.eof)
1652    {
1653        char[] line = buf;
1654        stdin.readln(line);
1655        if (line.length > buf.length)
1656            buf = line;
1657
1658        words += line.split.length;
1659    }
1660
1661    writeln(words);
1662}
1663---
1664This is actually what $(LREF byLine) does internally, so its usage
1665is recommended if you want to process a complete file.
1666*/
1667    size_t readln(C)(ref C[] buf, dchar terminator = '\n')
1668    if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
1669    {
1670        import std.exception : enforce;
1671
1672        static if (is(C == char))
1673        {
1674            enforce(_p && _p.handle, "Attempt to read from an unopened file.");
1675            if (_p.orientation == Orientation.unknown)
1676            {
1677                import core.stdc.wchar_ : fwide;
1678                auto w = fwide(_p.handle, 0);
1679                if (w < 0) _p.orientation = Orientation.narrow;
1680                else if (w > 0) _p.orientation = Orientation.wide;
1681            }
1682            return readlnImpl(_p.handle, buf, terminator, _p.orientation);
1683        }
1684        else
1685        {
1686            // TODO: optimize this
1687            string s = readln(terminator);
1688            buf.length = 0;
1689            if (!s.length) return 0;
1690            foreach (C c; s)
1691            {
1692                buf ~= c;
1693            }
1694            return buf.length;
1695        }
1696    }
1697
1698    @system unittest
1699    {
1700        // @system due to readln
1701        static import std.file;
1702        auto deleteme = testFilename();
1703        std.file.write(deleteme, "123\n456789");
1704        scope(exit) std.file.remove(deleteme);
1705
1706        auto file = File(deleteme);
1707        char[] buffer = new char[10];
1708        char[] line = buffer;
1709        file.readln(line);
1710        auto beyond = line.length;
1711        buffer[beyond] = 'a';
1712        file.readln(line); // should not write buffer beyond line
1713        assert(buffer[beyond] == 'a');
1714    }
1715
1716    @system unittest // bugzilla 15293
1717    {
1718        // @system due to readln
1719        static import std.file;
1720        auto deleteme = testFilename();
1721        std.file.write(deleteme, "a\n\naa");
1722        scope(exit) std.file.remove(deleteme);
1723
1724        auto file = File(deleteme);
1725        char[] buffer;
1726        char[] line;
1727
1728        file.readln(buffer, '\n');
1729
1730        line = buffer;
1731        file.readln(line, '\n');
1732
1733        line = buffer;
1734        file.readln(line, '\n');
1735
1736        assert(line[0 .. 1].capacity == 0);
1737    }
1738
1739/** ditto */
1740    size_t readln(C, R)(ref C[] buf, R terminator)
1741    if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
1742        isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
1743    {
1744        import std.algorithm.mutation : swap;
1745        import std.algorithm.searching : endsWith;
1746        import std.range.primitives : back;
1747
1748        auto last = terminator.back;
1749        C[] buf2;
1750        swap(buf, buf2);
1751        for (;;)
1752        {
1753            if (!readln(buf2, last) || endsWith(buf2, terminator))
1754            {
1755                if (buf.empty)
1756                {
1757                    buf = buf2;
1758                }
1759                else
1760                {
1761                    buf ~= buf2;
1762                }
1763                break;
1764            }
1765            buf ~= buf2;
1766        }
1767        return buf.length;
1768    }
1769
1770    @system unittest
1771    {
1772        static import std.file;
1773        import std.typecons : Tuple;
1774
1775        auto deleteme = testFilename();
1776        std.file.write(deleteme, "hello\n\rworld\nhow\n\rare ya");
1777        scope(exit) std.file.remove(deleteme);
1778        foreach (C; Tuple!(char, wchar, dchar).Types)
1779        {
1780            immutable(C)[][] witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ];
1781            auto f = File(deleteme);
1782            uint i = 0;
1783            C[] buf;
1784            while (f.readln(buf, "\n\r"))
1785            {
1786                assert(i < witness.length);
1787                assert(buf == witness[i++]);
1788            }
1789            assert(buf.length == 0);
1790        }
1791    }
1792
1793    /**
1794     * Reads formatted _data from the file using $(REF formattedRead, std,_format).
1795     * Params:
1796     * format = The $(LINK2 std_format.html#_format-string, _format string).
1797     * When passed as a compile-time argument, the string will be statically checked
1798     * against the argument types passed.
1799     * data = Items to be read.
1800     * Example:
1801----
1802// test.d
1803void main()
1804{
1805    import std.stdio;
1806    auto f = File("input");
1807    foreach (_; 0 .. 3)
1808    {
1809        int a;
1810        f.readf!" %d"(a);
1811        writeln(++a);
1812    }
1813}
1814----
1815$(CONSOLE
1816% echo "1 2 3" > input
1817% rdmd test.d
18182
18193
18204
1821)
1822     */
1823    uint readf(alias format, Data...)(auto ref Data data)
1824    if (isSomeString!(typeof(format)))
1825    {
1826        import std.format : checkFormatException;
1827
1828        alias e = checkFormatException!(format, Data);
1829        static assert(!e, e.msg);
1830        return this.readf(format, data);
1831    }
1832
1833    /// ditto
1834    uint readf(Data...)(in char[] format, auto ref Data data)
1835    {
1836        import std.format : formattedRead;
1837
1838        assert(isOpen);
1839        auto input = LockingTextReader(this);
1840        return formattedRead(input, format, data);
1841    }
1842
1843    ///
1844    @system unittest
1845    {
1846        static import std.file;
1847
1848        auto deleteme = testFilename();
1849        std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
1850        scope(exit) std.file.remove(deleteme);
1851        string s;
1852        auto f = File(deleteme);
1853        f.readf!"%s\n"(s);
1854        assert(s == "hello", "["~s~"]");
1855        f.readf("%s\n", s);
1856        assert(s == "world", "["~s~"]");
1857
1858        bool b1, b2;
1859        f.readf("%s\n%s\n", b1, b2);
1860        assert(b1 == true && b2 == false);
1861    }
1862
1863    // backwards compatibility with pointers
1864    @system unittest
1865    {
1866        // @system due to readf
1867        static import std.file;
1868
1869        auto deleteme = testFilename();
1870        std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
1871        scope(exit) std.file.remove(deleteme);
1872        string s;
1873        auto f = File(deleteme);
1874        f.readf("%s\n", &s);
1875        assert(s == "hello", "["~s~"]");
1876        f.readf("%s\n", &s);
1877        assert(s == "world", "["~s~"]");
1878
1879        // Issue 11698
1880        bool b1, b2;
1881        f.readf("%s\n%s\n", &b1, &b2);
1882        assert(b1 == true && b2 == false);
1883    }
1884
1885    // backwards compatibility (mixed)
1886    @system unittest
1887    {
1888        // @system due to readf
1889        static import std.file;
1890
1891        auto deleteme = testFilename();
1892        std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
1893        scope(exit) std.file.remove(deleteme);
1894        string s1, s2;
1895        auto f = File(deleteme);
1896        f.readf("%s\n%s\n", s1, &s2);
1897        assert(s1 == "hello");
1898        assert(s2 == "world");
1899
1900        // Issue 11698
1901        bool b1, b2;
1902        f.readf("%s\n%s\n", &b1, b2);
1903        assert(b1 == true && b2 == false);
1904    }
1905
1906    // Issue 12260 - Nice error of std.stdio.readf with newlines
1907    @system unittest
1908    {
1909        static import std.file;
1910
1911        auto deleteme = testFilename();
1912        std.file.write(deleteme, "1\n2");
1913        scope(exit) std.file.remove(deleteme);
1914        int input;
1915        auto f = File(deleteme);
1916        f.readf("%s", &input);
1917
1918        import std.conv : ConvException;
1919        import std.exception : collectException;
1920        assert(collectException!ConvException(f.readf("%s", &input)).msg ==
1921            "Unexpected '\\n' when converting from type LockingTextReader to type int");
1922    }
1923
1924/**
1925 Returns a temporary file by calling
1926 $(HTTP cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile).
1927 Note that the created file has no $(LREF name).*/
1928    static File tmpfile() @safe
1929    {
1930        import std.exception : errnoEnforce;
1931
1932        return File(errnoEnforce(.tmpfile(),
1933                "Could not create temporary file with tmpfile()"),
1934            null);
1935    }
1936
1937/**
1938Unsafe function that wraps an existing $(D FILE*). The resulting $(D
1939File) never takes the initiative in closing the file.
1940Note that the created file has no $(LREF name)*/
1941    /*private*/ static File wrapFile(FILE* f) @safe
1942    {
1943        import std.exception : enforce;
1944
1945        return File(enforce(f, "Could not wrap null FILE*"),
1946            null, /*uint.max / 2*/ 9999);
1947    }
1948
1949/**
1950Returns the $(D FILE*) corresponding to this object.
1951 */
1952    FILE* getFP() @safe pure
1953    {
1954        import std.exception : enforce;
1955
1956        enforce(_p && _p.handle,
1957                "Attempting to call getFP() on an unopened file");
1958        return _p.handle;
1959    }
1960
1961    @system unittest
1962    {
1963        static import core.stdc.stdio;
1964        assert(stdout.getFP() == core.stdc.stdio.stdout);
1965    }
1966
1967/**
1968Returns the file number corresponding to this object.
1969 */
1970    @property int fileno() const @trusted
1971    {
1972        import std.exception : enforce;
1973
1974        enforce(isOpen, "Attempting to call fileno() on an unopened file");
1975        return .fileno(cast(FILE*) _p.handle);
1976    }
1977
1978/**
1979Returns the underlying operating system $(D HANDLE) (Windows only).
1980*/
1981    version (StdDdoc)
1982    @property HANDLE windowsHandle();
1983
1984    version (Windows)
1985    @property HANDLE windowsHandle()
1986    {
1987        version (DIGITAL_MARS_STDIO)
1988            return _fdToHandle(fileno);
1989        else
1990            return cast(HANDLE)_get_osfhandle(fileno);
1991    }
1992
1993
1994// Note: This was documented until 2013/08
1995/*
1996Range that reads one line at a time.  Returned by $(LREF byLine).
1997
1998Allows to directly use range operations on lines of a file.
1999*/
2000    struct ByLine(Char, Terminator)
2001    {
2002    private:
2003        import std.typecons : RefCounted, RefCountedAutoInitialize;
2004
2005        /* Ref-counting stops the source range's Impl
2006         * from getting out of sync after the range is copied, e.g.
2007         * when accessing range.front, then using std.range.take,
2008         * then accessing range.front again. */
2009        alias PImpl = RefCounted!(Impl, RefCountedAutoInitialize.no);
2010        PImpl impl;
2011
2012        static if (isScalarType!Terminator)
2013            enum defTerm = '\n';
2014        else
2015            enum defTerm = cast(Terminator)"\n";
2016
2017    public:
2018        this(File f, KeepTerminator kt = No.keepTerminator,
2019                Terminator terminator = defTerm)
2020        {
2021            impl = PImpl(f, kt, terminator);
2022        }
2023
2024        @property bool empty()
2025        {
2026            return impl.refCountedPayload.empty;
2027        }
2028
2029        @property Char[] front()
2030        {
2031            return impl.refCountedPayload.front;
2032        }
2033
2034        void popFront()
2035        {
2036            impl.refCountedPayload.popFront();
2037        }
2038
2039    private:
2040        struct Impl
2041        {
2042        private:
2043            File file;
2044            Char[] line;
2045            Char[] buffer;
2046            Terminator terminator;
2047            KeepTerminator keepTerminator;
2048
2049        public:
2050            this(File f, KeepTerminator kt, Terminator terminator)
2051            {
2052                file = f;
2053                this.terminator = terminator;
2054                keepTerminator = kt;
2055                popFront();
2056            }
2057
2058            // Range primitive implementations.
2059            @property bool empty()
2060            {
2061                return line is null;
2062            }
2063
2064            @property Char[] front()
2065            {
2066                return line;
2067            }
2068
2069            void popFront()
2070            {
2071                import std.algorithm.searching : endsWith;
2072                assert(file.isOpen);
2073                line = buffer;
2074                file.readln(line, terminator);
2075                if (line.length > buffer.length)
2076                {
2077                    buffer = line;
2078                }
2079                if (line.empty)
2080                {
2081                    file.detach();
2082                    line = null;
2083                }
2084                else if (keepTerminator == No.keepTerminator
2085                        && endsWith(line, terminator))
2086                {
2087                    static if (isScalarType!Terminator)
2088                        enum tlen = 1;
2089                    else static if (isArray!Terminator)
2090                    {
2091                        static assert(
2092                            is(Unqual!(ElementEncodingType!Terminator) == Char));
2093                        const tlen = terminator.length;
2094                    }
2095                    else
2096                        static assert(false);
2097                    line = line[0 .. line.length - tlen];
2098                }
2099            }
2100        }
2101    }
2102
2103/**
2104Returns an input range set up to read from the file handle one line
2105at a time.
2106
2107The element type for the range will be $(D Char[]). Range primitives
2108may throw $(D StdioException) on I/O error.
2109
2110Note:
2111Each $(D front) will not persist after $(D
2112popFront) is called, so the caller must copy its contents (e.g. by
2113calling $(D to!string)) when retention is needed. If the caller needs
2114to retain a copy of every line, use the $(LREF byLineCopy) function
2115instead.
2116
2117Params:
2118Char = Character type for each line, defaulting to $(D char).
2119keepTerminator = Use $(D Yes.keepTerminator) to include the
2120terminator at the end of each line.
2121terminator = Line separator ($(D '\n') by default). Use
2122$(REF newline, std,ascii) for portability (unless the file was opened in
2123text mode).
2124
2125Example:
2126----
2127import std.algorithm, std.stdio, std.string;
2128// Count words in a file using ranges.
2129void main()
2130{
2131    auto file = File("file.txt"); // Open for reading
2132    const wordCount = file.byLine()            // Read lines
2133                          .map!split           // Split into words
2134                          .map!(a => a.length) // Count words per line
2135                          .sum();              // Total word count
2136    writeln(wordCount);
2137}
2138----
2139
2140Example:
2141----
2142import std.range, std.stdio;
2143// Read lines using foreach.
2144void main()
2145{
2146    auto file = File("file.txt"); // Open for reading
2147    auto range = file.byLine();
2148    // Print first three lines
2149    foreach (line; range.take(3))
2150        writeln(line);
2151    // Print remaining lines beginning with '#'
2152    foreach (line; range)
2153    {
2154        if (!line.empty && line[0] == '#')
2155            writeln(line);
2156    }
2157}
2158----
2159Notice that neither example accesses the line data returned by
2160$(D front) after the corresponding $(D popFront) call is made (because
2161the contents may well have changed).
2162*/
2163    auto byLine(Terminator = char, Char = char)
2164            (KeepTerminator keepTerminator = No.keepTerminator,
2165            Terminator terminator = '\n')
2166    if (isScalarType!Terminator)
2167    {
2168        return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
2169    }
2170
2171/// ditto
2172    auto byLine(Terminator, Char = char)
2173            (KeepTerminator keepTerminator, Terminator terminator)
2174    if (is(Unqual!(ElementEncodingType!Terminator) == Char))
2175    {
2176        return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
2177    }
2178
2179    @system unittest
2180    {
2181        static import std.file;
2182        auto deleteme = testFilename();
2183        std.file.write(deleteme, "hi");
2184        scope(success) std.file.remove(deleteme);
2185
2186        import std.meta : AliasSeq;
2187        foreach (T; AliasSeq!(char, wchar, dchar))
2188        {
2189            auto blc = File(deleteme).byLine!(T, T);
2190            assert(blc.front == "hi");
2191            // check front is cached
2192            assert(blc.front is blc.front);
2193        }
2194    }
2195
2196    private struct ByLineCopy(Char, Terminator)
2197    {
2198    private:
2199        import std.typecons : RefCounted, RefCountedAutoInitialize;
2200
2201        /* Ref-counting stops the source range's ByLineCopyImpl
2202         * from getting out of sync after the range is copied, e.g.
2203         * when accessing range.front, then using std.range.take,
2204         * then accessing range.front again. */
2205        alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator),
2206            RefCountedAutoInitialize.no);
2207        Impl impl;
2208
2209    public:
2210        this(File f, KeepTerminator kt, Terminator terminator)
2211        {
2212            impl = Impl(f, kt, terminator);
2213        }
2214
2215        @property bool empty()
2216        {
2217            return impl.refCountedPayload.empty;
2218        }
2219
2220        @property Char[] front()
2221        {
2222            return impl.refCountedPayload.front;
2223        }
2224
2225        void popFront()
2226        {
2227            impl.refCountedPayload.popFront();
2228        }
2229    }
2230
2231    private struct ByLineCopyImpl(Char, Terminator)
2232    {
2233        ByLine!(Unqual!Char, Terminator).Impl impl;
2234        bool gotFront;
2235        Char[] line;
2236
2237    public:
2238        this(File f, KeepTerminator kt, Terminator terminator)
2239        {
2240            impl = ByLine!(Unqual!Char, Terminator).Impl(f, kt, terminator);
2241        }
2242
2243        @property bool empty()
2244        {
2245            return impl.empty;
2246        }
2247
2248        @property front()
2249        {
2250            if (!gotFront)
2251            {
2252                line = impl.front.dup;
2253                gotFront = true;
2254            }
2255            return line;
2256        }
2257
2258        void popFront()
2259        {
2260            impl.popFront();
2261            gotFront = false;
2262        }
2263    }
2264
2265/**
2266Returns an input range set up to read from the file handle one line
2267at a time. Each line will be newly allocated. $(D front) will cache
2268its value to allow repeated calls without unnecessary allocations.
2269
2270Note: Due to caching byLineCopy can be more memory-efficient than
2271$(D File.byLine.map!idup).
2272
2273The element type for the range will be $(D Char[]). Range
2274primitives may throw $(D StdioException) on I/O error.
2275
2276Params:
2277Char = Character type for each line, defaulting to $(D immutable char).
2278keepTerminator = Use $(D Yes.keepTerminator) to include the
2279terminator at the end of each line.
2280terminator = Line separator ($(D '\n') by default). Use
2281$(REF newline, std,ascii) for portability (unless the file was opened in
2282text mode).
2283
2284Example:
2285----
2286import std.algorithm, std.array, std.stdio;
2287// Print sorted lines of a file.
2288void main()
2289{
2290    auto sortedLines = File("file.txt")   // Open for reading
2291                       .byLineCopy()      // Read persistent lines
2292                       .array()           // into an array
2293                       .sort();           // then sort them
2294    foreach (line; sortedLines)
2295        writeln(line);
2296}
2297----
2298See_Also:
2299$(REF readText, std,file)
2300*/
2301    auto byLineCopy(Terminator = char, Char = immutable char)
2302            (KeepTerminator keepTerminator = No.keepTerminator,
2303            Terminator terminator = '\n')
2304    if (isScalarType!Terminator)
2305    {
2306        return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2307    }
2308
2309/// ditto
2310    auto byLineCopy(Terminator, Char = immutable char)
2311            (KeepTerminator keepTerminator, Terminator terminator)
2312    if (is(Unqual!(ElementEncodingType!Terminator) == Unqual!Char))
2313    {
2314        return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2315    }
2316
2317    @safe unittest
2318    {
2319        static assert(is(typeof(File("").byLine.front) == char[]));
2320        static assert(is(typeof(File("").byLineCopy.front) == string));
2321        static assert(
2322            is(typeof(File("").byLineCopy!(char, char).front) == char[]));
2323    }
2324
2325    @system unittest
2326    {
2327        import std.algorithm.comparison : equal;
2328        static import std.file;
2329
2330        scope(failure) printf("Failed test at line %d\n", __LINE__);
2331        auto deleteme = testFilename();
2332        std.file.write(deleteme, "");
2333        scope(success) std.file.remove(deleteme);
2334
2335        // Test empty file
2336        auto f = File(deleteme);
2337        foreach (line; f.byLine())
2338        {
2339            assert(false);
2340        }
2341        f.detach();
2342        assert(!f.isOpen);
2343
2344        void test(Terminator)(string txt, in string[] witness,
2345                KeepTerminator kt, Terminator term, bool popFirstLine = false)
2346        {
2347            import std.algorithm.sorting : sort;
2348            import std.array : array;
2349            import std.conv : text;
2350            import std.range.primitives : walkLength;
2351
2352            uint i;
2353            std.file.write(deleteme, txt);
2354            auto f = File(deleteme);
2355            scope(exit)
2356            {
2357                f.close();
2358                assert(!f.isOpen);
2359            }
2360            auto lines = f.byLine(kt, term);
2361            if (popFirstLine)
2362            {
2363                lines.popFront();
2364                i = 1;
2365            }
2366            assert(lines.empty || lines.front is lines.front);
2367            foreach (line; lines)
2368            {
2369                assert(line == witness[i++]);
2370            }
2371            assert(i == witness.length, text(i, " != ", witness.length));
2372
2373            // Issue 11830
2374            auto walkedLength = File(deleteme).byLine(kt, term).walkLength;
2375            assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length));
2376
2377            // test persistent lines
2378            assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort());
2379        }
2380
2381        KeepTerminator kt = No.keepTerminator;
2382        test("", null, kt, '\n');
2383        test("\n", [ "" ], kt, '\n');
2384        test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n');
2385        test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n', true);
2386        test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ], kt, '\n');
2387        test("foo", [ "foo" ], kt, '\n', true);
2388        test("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"],
2389            kt, "\r\n");
2390        test("sue\r", ["sue"], kt, '\r');
2391
2392        kt = Yes.keepTerminator;
2393        test("", null, kt, '\n');
2394        test("\n", [ "\n" ], kt, '\n');
2395        test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt, '\n');
2396        test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n');
2397        test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n', true);
2398        test("foo", [ "foo" ], kt, '\n');
2399        test("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"],
2400            kt, "\r\n");
2401        test("sue\r", ["sue\r"], kt, '\r');
2402    }
2403
2404    @system unittest
2405    {
2406        import std.algorithm.comparison : equal;
2407        import std.range : drop, take;
2408
2409        version (Win64)
2410        {
2411            static import std.file;
2412
2413            /* the C function tmpfile doesn't seem to work, even when called from C */
2414            auto deleteme = testFilename();
2415            auto file = File(deleteme, "w+");
2416            scope(success) std.file.remove(deleteme);
2417        }
2418        else version (CRuntime_Bionic)
2419        {
2420            static import std.file;
2421
2422            /* the C function tmpfile doesn't work when called from a shared
2423               library apk:
2424               https://code.google.com/p/android/issues/detail?id=66815 */
2425            auto deleteme = testFilename();
2426            auto file = File(deleteme, "w+");
2427            scope(success) std.file.remove(deleteme);
2428        }
2429        else
2430            auto file = File.tmpfile();
2431        file.write("1\n2\n3\n");
2432
2433        // bug 9599
2434        file.rewind();
2435        File.ByLine!(char, char) fbl = file.byLine();
2436        auto fbl2 = fbl;
2437        assert(fbl.front == "1");
2438        assert(fbl.front is fbl2.front);
2439        assert(fbl.take(1).equal(["1"]));
2440        assert(fbl.equal(["2", "3"]));
2441        assert(fbl.empty);
2442        assert(file.isOpen); // we still have a valid reference
2443
2444        file.rewind();
2445        fbl = file.byLine();
2446        assert(!fbl.drop(2).empty);
2447        assert(fbl.equal(["3"]));
2448        assert(fbl.empty);
2449        assert(file.isOpen);
2450
2451        file.detach();
2452        assert(!file.isOpen);
2453    }
2454
2455    @system unittest
2456    {
2457        static import std.file;
2458        auto deleteme = testFilename();
2459        std.file.write(deleteme, "hi");
2460        scope(success) std.file.remove(deleteme);
2461
2462        auto blc = File(deleteme).byLineCopy;
2463        assert(!blc.empty);
2464        // check front is cached
2465        assert(blc.front is blc.front);
2466    }
2467
2468    /**
2469    Creates an input range set up to parse one line at a time from the file
2470    into a tuple.
2471
2472    Range primitives may throw $(D StdioException) on I/O error.
2473
2474    Params:
2475        format = tuple record $(REF_ALTTEXT _format, formattedRead, std, _format)
2476
2477    Returns:
2478        The input range set up to parse one line at a time into a record tuple.
2479
2480    See_Also:
2481
2482        It is similar to $(LREF byLine) and uses
2483        $(REF_ALTTEXT _format, formattedRead, std, _format) under the hood.
2484    */
2485    template byRecord(Fields...)
2486    {
2487        ByRecord!(Fields) byRecord(string format)
2488        {
2489            return typeof(return)(this, format);
2490        }
2491    }
2492
2493    ///
2494    @system unittest
2495    {
2496         static import std.file;
2497         import std.typecons : tuple;
2498
2499         // prepare test file
2500         auto testFile = testFilename();
2501         scope(failure) printf("Failed test at line %d\n", __LINE__);
2502         std.file.write(testFile, "1 2\n4 1\n5 100");
2503         scope(exit) std.file.remove(testFile);
2504
2505         File f = File(testFile);
2506         scope(exit) f.close();
2507
2508         auto expected = [tuple(1, 2), tuple(4, 1), tuple(5, 100)];
2509         uint i;
2510         foreach (e; f.byRecord!(int, int)("%s %s"))
2511         {
2512             assert(e == expected[i++]);
2513         }
2514    }
2515
2516    // Note: This was documented until 2013/08
2517    /*
2518     * Range that reads a chunk at a time.
2519     */
2520    struct ByChunk
2521    {
2522    private:
2523        File    file_;
2524        ubyte[] chunk_;
2525
2526        void prime()
2527        {
2528            chunk_ = file_.rawRead(chunk_);
2529            if (chunk_.length == 0)
2530                file_.detach();
2531        }
2532
2533    public:
2534        this(File file, size_t size)
2535        {
2536            this(file, new ubyte[](size));
2537        }
2538
2539        this(File file, ubyte[] buffer)
2540        {
2541            import std.exception : enforce;
2542            enforce(buffer.length, "size must be larger than 0");
2543            file_ = file;
2544            chunk_ = buffer;
2545            prime();
2546        }
2547
2548        // $(D ByChunk)'s input range primitive operations.
2549        @property nothrow
2550        bool empty() const
2551        {
2552            return !file_.isOpen;
2553        }
2554
2555        /// Ditto
2556        @property nothrow
2557        ubyte[] front()
2558        {
2559            version (assert)
2560            {
2561                import core.exception : RangeError;
2562                if (empty)
2563                    throw new RangeError();
2564            }
2565            return chunk_;
2566        }
2567
2568        /// Ditto
2569        void popFront()
2570        {
2571            version (assert)
2572            {
2573                import core.exception : RangeError;
2574                if (empty)
2575                    throw new RangeError();
2576            }
2577            prime();
2578        }
2579    }
2580
2581/**
2582Returns an input range set up to read from the file handle a chunk at a
2583time.
2584
2585The element type for the range will be $(D ubyte[]). Range primitives
2586may throw $(D StdioException) on I/O error.
2587
2588Example:
2589---------
2590void main()
2591{
2592    // Read standard input 4KB at a time
2593    foreach (ubyte[] buffer; stdin.byChunk(4096))
2594    {
2595        ... use buffer ...
2596    }
2597}
2598---------
2599
2600The parameter may be a number (as shown in the example above) dictating the
2601size of each chunk. Alternatively, $(D byChunk) accepts a
2602user-provided buffer that it uses directly.
2603
2604Example:
2605---------
2606void main()
2607{
2608    // Read standard input 4KB at a time
2609    foreach (ubyte[] buffer; stdin.byChunk(new ubyte[4096]))
2610    {
2611        ... use buffer ...
2612    }
2613}
2614---------
2615
2616In either case, the content of the buffer is reused across calls. That means
2617$(D front) will not persist after $(D popFront) is called, so if retention is
2618needed, the caller must copy its contents (e.g. by calling $(D buffer.dup)).
2619
2620In the  example above, $(D buffer.length) is 4096 for all iterations, except
2621for the last one, in which case $(D buffer.length) may be less than 4096 (but
2622always greater than zero).
2623
2624With the mentioned limitations, $(D byChunk) works with any algorithm
2625compatible with input ranges.
2626
2627Example:
2628---
2629// Efficient file copy, 1MB at a time.
2630import std.algorithm, std.stdio;
2631void main()
2632{
2633    stdin.byChunk(1024 * 1024).copy(stdout.lockingTextWriter());
2634}
2635---
2636
2637$(REF joiner, std,algorithm,iteration) can be used to join chunks together into
2638a single range lazily.
2639Example:
2640---
2641import std.algorithm, std.stdio;
2642void main()
2643{
2644    //Range of ranges
2645    static assert(is(typeof(stdin.byChunk(4096).front) == ubyte[]));
2646    //Range of elements
2647    static assert(is(typeof(stdin.byChunk(4096).joiner.front) == ubyte));
2648}
2649---
2650
2651Returns: A call to $(D byChunk) returns a range initialized with the $(D File)
2652object and the appropriate buffer.
2653
2654Throws: If the user-provided size is zero or the user-provided buffer
2655is empty, throws an $(D Exception). In case of an I/O error throws
2656$(D StdioException).
2657 */
2658    auto byChunk(size_t chunkSize)
2659    {
2660        return ByChunk(this, chunkSize);
2661    }
2662/// Ditto
2663    ByChunk byChunk(ubyte[] buffer)
2664    {
2665        return ByChunk(this, buffer);
2666    }
2667
2668    @system unittest
2669    {
2670        static import std.file;
2671
2672        scope(failure) printf("Failed test at line %d\n", __LINE__);
2673
2674        auto deleteme = testFilename();
2675        std.file.write(deleteme, "asd\ndef\nasdf");
2676
2677        auto witness = ["asd\n", "def\n", "asdf" ];
2678        auto f = File(deleteme);
2679        scope(exit)
2680        {
2681            f.close();
2682            assert(!f.isOpen);
2683            std.file.remove(deleteme);
2684        }
2685
2686        uint i;
2687        foreach (chunk; f.byChunk(4))
2688            assert(chunk == cast(ubyte[]) witness[i++]);
2689
2690        assert(i == witness.length);
2691    }
2692
2693    @system unittest
2694    {
2695        static import std.file;
2696
2697        scope(failure) printf("Failed test at line %d\n", __LINE__);
2698
2699        auto deleteme = testFilename();
2700        std.file.write(deleteme, "asd\ndef\nasdf");
2701
2702        auto witness = ["asd\n", "def\n", "asdf" ];
2703        auto f = File(deleteme);
2704        scope(exit)
2705        {
2706            f.close();
2707            assert(!f.isOpen);
2708            std.file.remove(deleteme);
2709        }
2710
2711        uint i;
2712        foreach (chunk; f.byChunk(new ubyte[4]))
2713            assert(chunk == cast(ubyte[]) witness[i++]);
2714
2715        assert(i == witness.length);
2716    }
2717
2718    // Note: This was documented until 2013/08
2719/*
2720$(D Range) that locks the file and allows fast writing to it.
2721 */
2722    struct LockingTextWriter
2723    {
2724    private:
2725        import std.range.primitives : ElementType, isInfinite, isInputRange;
2726        // the shared file handle
2727        FILE* fps_;
2728
2729        // the unshared version of fps
2730        @property _iobuf* handle_() @trusted { return cast(_iobuf*) fps_; }
2731
2732        // the file's orientation (byte- or wide-oriented)
2733        int orientation_;
2734    public:
2735
2736        this(ref File f) @trusted
2737        {
2738            import core.stdc.wchar_ : fwide;
2739            import std.exception : enforce;
2740
2741            enforce(f._p && f._p.handle, "Attempting to write to closed File");
2742            fps_ = f._p.handle;
2743            orientation_ = fwide(fps_, 0);
2744            FLOCK(fps_);
2745        }
2746
2747        ~this() @trusted
2748        {
2749            if (fps_)
2750            {
2751                FUNLOCK(fps_);
2752                fps_ = null;
2753            }
2754        }
2755
2756        this(this) @trusted
2757        {
2758            if (fps_)
2759            {
2760                FLOCK(fps_);
2761            }
2762        }
2763
2764        /// Range primitive implementations.
2765        void put(A)(A writeme)
2766            if ((isSomeChar!(Unqual!(ElementType!A)) ||
2767                  is(ElementType!A : const(ubyte))) &&
2768                isInputRange!A &&
2769                !isInfinite!A)
2770        {
2771            import std.exception : errnoEnforce;
2772
2773            alias C = ElementEncodingType!A;
2774            static assert(!is(C == void));
2775            static if (isSomeString!A && C.sizeof == 1 || is(A : const(ubyte)[]))
2776            {
2777                if (orientation_ <= 0)
2778                {
2779                    //file.write(writeme); causes infinite recursion!!!
2780                    //file.rawWrite(writeme);
2781                    auto result = trustedFwrite(fps_, writeme);
2782                    if (result != writeme.length) errnoEnforce(0);
2783                    return;
2784                }
2785            }
2786
2787            // put each element in turn.
2788            alias Elem = Unqual!(ElementType!A);
2789            foreach (Elem c; writeme)
2790            {
2791                put(c);
2792            }
2793        }
2794
2795        /// ditto
2796        void put(C)(C c) @safe if (isSomeChar!C || is(C : const(ubyte)))
2797        {
2798            import std.traits : Parameters;
2799            static auto trustedFPUTC(int ch, _iobuf* h) @trusted
2800            {
2801                return FPUTC(ch, h);
2802            }
2803            static auto trustedFPUTWC(Parameters!FPUTWC[0] ch, _iobuf* h) @trusted
2804            {
2805                return FPUTWC(ch, h);
2806            }
2807
2808            static if (c.sizeof == 1)
2809            {
2810                // simple char
2811                if (orientation_ <= 0) trustedFPUTC(c, handle_);
2812                else trustedFPUTWC(c, handle_);
2813            }
2814            else static if (c.sizeof == 2)
2815            {
2816                import std.utf : encode, UseReplacementDchar;
2817
2818                if (orientation_ <= 0)
2819                {
2820                    if (c <= 0x7F)
2821                    {
2822                        trustedFPUTC(c, handle_);
2823                    }
2824                    else
2825                    {
2826                        char[4] buf;
2827                        immutable size = encode!(UseReplacementDchar.yes)(buf, c);
2828                        foreach (i ; 0 .. size)
2829                            trustedFPUTC(buf[i], handle_);
2830                    }
2831                }
2832                else
2833                {
2834                    trustedFPUTWC(c, handle_);
2835                }
2836            }
2837            else // 32-bit characters
2838            {
2839                import std.utf : encode;
2840
2841                if (orientation_ <= 0)
2842                {
2843                    if (c <= 0x7F)
2844                    {
2845                        trustedFPUTC(c, handle_);
2846                    }
2847                    else
2848                    {
2849                        char[4] buf = void;
2850                        immutable len = encode(buf, c);
2851                        foreach (i ; 0 .. len)
2852                            trustedFPUTC(buf[i], handle_);
2853                    }
2854                }
2855                else
2856                {
2857                    version (Windows)
2858                    {
2859                        import std.utf : isValidDchar;
2860
2861                        assert(isValidDchar(c));
2862                        if (c <= 0xFFFF)
2863                        {
2864                            trustedFPUTWC(c, handle_);
2865                        }
2866                        else
2867                        {
2868                            trustedFPUTWC(cast(wchar)
2869                                    ((((c - 0x10000) >> 10) & 0x3FF)
2870                                            + 0xD800), handle_);
2871                            trustedFPUTWC(cast(wchar)
2872                                    (((c - 0x10000) & 0x3FF) + 0xDC00),
2873                                    handle_);
2874                        }
2875                    }
2876                    else version (Posix)
2877                    {
2878                        trustedFPUTWC(c, handle_);
2879                    }
2880                    else
2881                    {
2882                        static assert(0);
2883                    }
2884                }
2885            }
2886        }
2887    }
2888
2889/** Returns an output range that locks the file and allows fast writing to it.
2890
2891See $(LREF byChunk) for an example.
2892*/
2893    auto lockingTextWriter() @safe
2894    {
2895        return LockingTextWriter(this);
2896    }
2897
2898    // An output range which optionally locks the file and puts it into
2899    // binary mode (similar to rawWrite). Because it needs to restore
2900    // the file mode on destruction, it is RefCounted on Windows.
2901    struct BinaryWriterImpl(bool locking)
2902    {
2903        import std.traits : hasIndirections;
2904    private:
2905        FILE* fps;
2906        string name;
2907
2908        version (Windows)
2909        {
2910            int fd, oldMode;
2911            version (DIGITAL_MARS_STDIO)
2912                ubyte oldInfo;
2913        }
2914
2915    package:
2916        this(ref File f)
2917        {
2918            import std.exception : enforce;
2919
2920            enforce(f._p && f._p.handle);
2921            name = f._name;
2922            fps = f._p.handle;
2923            static if (locking)
2924                FLOCK(fps);
2925
2926            version (Windows)
2927            {
2928                .fflush(fps); // before changing translation mode
2929                fd = ._fileno(fps);
2930                oldMode = ._setmode(fd, _O_BINARY);
2931                version (DIGITAL_MARS_STDIO)
2932                {
2933                    import core.atomic : atomicOp;
2934
2935                    // @@@BUG@@@ 4243
2936                    oldInfo = __fhnd_info[fd];
2937                    atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
2938                }
2939            }
2940        }
2941
2942    public:
2943        ~this()
2944        {
2945            if (!fps)
2946                return;
2947
2948            version (Windows)
2949            {
2950                .fflush(fps); // before restoring translation mode
2951                version (DIGITAL_MARS_STDIO)
2952                {
2953                    // @@@BUG@@@ 4243
2954                    __fhnd_info[fd] = oldInfo;
2955                }
2956                ._setmode(fd, oldMode);
2957            }
2958
2959            FUNLOCK(fps);
2960            fps = null;
2961        }
2962
2963        void rawWrite(T)(in T[] buffer)
2964        {
2965            import std.conv : text;
2966            import std.exception : errnoEnforce;
2967
2968            auto result = trustedFwrite(fps, buffer);
2969            if (result == result.max) result = 0;
2970            errnoEnforce(result == buffer.length,
2971                    text("Wrote ", result, " instead of ", buffer.length,
2972                            " objects of type ", T.stringof, " to file `",
2973                            name, "'"));
2974        }
2975
2976        version (Windows)
2977        {
2978            @disable this(this);
2979        }
2980        else
2981        {
2982            this(this)
2983            {
2984                if (fps)
2985                {
2986                    FLOCK(fps);
2987                }
2988            }
2989        }
2990
2991        void put(T)(auto ref in T value)
2992        if (!hasIndirections!T &&
2993            !isInputRange!T)
2994        {
2995            rawWrite((&value)[0 .. 1]);
2996        }
2997
2998        void put(T)(in T[] array)
2999        if (!hasIndirections!T &&
3000            !isInputRange!T)
3001        {
3002            rawWrite(array);
3003        }
3004    }
3005
3006/** Returns an output range that locks the file and allows fast writing to it.
3007
3008Example:
3009Produce a grayscale image of the $(LINK2 https://en.wikipedia.org/wiki/Mandelbrot_set, Mandelbrot set)
3010in binary $(LINK2 https://en.wikipedia.org/wiki/Netpbm_format, Netpbm format) to standard output.
3011---
3012import std.algorithm, std.range, std.stdio;
3013
3014void main()
3015{
3016    enum size = 500;
3017    writef("P5\n%d %d %d\n", size, size, ubyte.max);
3018
3019    iota(-1, 3, 2.0/size).map!(y =>
3020        iota(-1.5, 0.5, 2.0/size).map!(x =>
3021            cast(ubyte)(1+
3022                recurrence!((a, n) => x + y*1i + a[n-1]^^2)(0+0i)
3023                .take(ubyte.max)
3024                .countUntil!(z => z.re^^2 + z.im^^2 > 4))
3025        )
3026    )
3027    .copy(stdout.lockingBinaryWriter);
3028}
3029---
3030*/
3031    auto lockingBinaryWriter()
3032    {
3033        alias LockingBinaryWriterImpl = BinaryWriterImpl!true;
3034
3035        version (Windows)
3036        {
3037            import std.typecons : RefCounted;
3038            alias LockingBinaryWriter = RefCounted!LockingBinaryWriterImpl;
3039        }
3040        else
3041            alias LockingBinaryWriter = LockingBinaryWriterImpl;
3042
3043        return LockingBinaryWriter(this);
3044    }
3045
3046    @system unittest
3047    {
3048        import std.algorithm.mutation : reverse;
3049        import std.exception : collectException;
3050        static import std.file;
3051        import std.range : only, retro;
3052        import std.string : format;
3053
3054        auto deleteme = testFilename();
3055        scope(exit) collectException(std.file.remove(deleteme));
3056        auto output = File(deleteme, "wb");
3057        auto writer = output.lockingBinaryWriter();
3058        auto input = File(deleteme, "rb");
3059
3060        T[] readExact(T)(T[] buf)
3061        {
3062            auto result = input.rawRead(buf);
3063            assert(result.length == buf.length,
3064                "Read %d out of %d bytes"
3065                .format(result.length, buf.length));
3066            return result;
3067        }
3068
3069        // test raw values
3070        ubyte byteIn = 42;
3071        byteIn.only.copy(writer); output.flush();
3072        ubyte byteOut = readExact(new ubyte[1])[0];
3073        assert(byteIn == byteOut);
3074
3075        // test arrays
3076        ubyte[] bytesIn = [1, 2, 3, 4, 5];
3077        bytesIn.copy(writer); output.flush();
3078        ubyte[] bytesOut = readExact(new ubyte[bytesIn.length]);
3079        scope(failure) .writeln(bytesOut);
3080        assert(bytesIn == bytesOut);
3081
3082        // test ranges of values
3083        bytesIn.retro.copy(writer); output.flush();
3084        bytesOut = readExact(bytesOut);
3085        bytesOut.reverse();
3086        assert(bytesIn == bytesOut);
3087
3088        // test string
3089        "foobar".copy(writer); output.flush();
3090        char[] charsOut = readExact(new char[6]);
3091        assert(charsOut == "foobar");
3092
3093        // test ranges of arrays
3094        only("foo", "bar").copy(writer); output.flush();
3095        charsOut = readExact(charsOut);
3096        assert(charsOut == "foobar");
3097
3098        // test that we are writing arrays as is,
3099        // without UTF-8 transcoding
3100        "foo"d.copy(writer); output.flush();
3101        dchar[] dcharsOut = readExact(new dchar[3]);
3102        assert(dcharsOut == "foo");
3103    }
3104
3105/// Get the size of the file, ulong.max if file is not searchable, but still throws if an actual error occurs.
3106    @property ulong size() @safe
3107    {
3108        import std.exception : collectException;
3109
3110        ulong pos = void;
3111        if (collectException(pos = tell)) return ulong.max;
3112        scope(exit) seek(pos);
3113        seek(0, SEEK_END);
3114        return tell;
3115    }
3116}
3117
3118@system unittest
3119{
3120    @system struct SystemToString
3121    {
3122        string toString()
3123        {
3124            return "system";
3125        }
3126    }
3127
3128    @trusted struct TrustedToString
3129    {
3130        string toString()
3131        {
3132            return "trusted";
3133        }
3134    }
3135
3136    @safe struct SafeToString
3137    {
3138        string toString()
3139        {
3140            return "safe";
3141        }
3142    }
3143
3144    @system void systemTests()
3145    {
3146        //system code can write to files/stdout with anything!
3147        if (false)
3148        {
3149            auto f = File();
3150
3151            f.write("just a string");
3152            f.write("string with arg: ", 47);
3153            f.write(SystemToString());
3154            f.write(TrustedToString());
3155            f.write(SafeToString());
3156
3157            write("just a string");
3158            write("string with arg: ", 47);
3159            write(SystemToString());
3160            write(TrustedToString());
3161            write(SafeToString());
3162
3163            f.writeln("just a string");
3164            f.writeln("string with arg: ", 47);
3165            f.writeln(SystemToString());
3166            f.writeln(TrustedToString());
3167            f.writeln(SafeToString());
3168
3169            writeln("just a string");
3170            writeln("string with arg: ", 47);
3171            writeln(SystemToString());
3172            writeln(TrustedToString());
3173            writeln(SafeToString());
3174
3175            f.writef("string with arg: %s", 47);
3176            f.writef("%s", SystemToString());
3177            f.writef("%s", TrustedToString());
3178            f.writef("%s", SafeToString());
3179
3180            writef("string with arg: %s", 47);
3181            writef("%s", SystemToString());
3182            writef("%s", TrustedToString());
3183            writef("%s", SafeToString());
3184
3185            f.writefln("string with arg: %s", 47);
3186            f.writefln("%s", SystemToString());
3187            f.writefln("%s", TrustedToString());
3188            f.writefln("%s", SafeToString());
3189
3190            writefln("string with arg: %s", 47);
3191            writefln("%s", SystemToString());
3192            writefln("%s", TrustedToString());
3193            writefln("%s", SafeToString());
3194        }
3195    }
3196
3197    @safe void safeTests()
3198    {
3199        auto f = File();
3200
3201        //safe code can write to files only with @safe and @trusted code...
3202        if (false)
3203        {
3204            f.write("just a string");
3205            f.write("string with arg: ", 47);
3206            f.write(TrustedToString());
3207            f.write(SafeToString());
3208
3209            write("just a string");
3210            write("string with arg: ", 47);
3211            write(TrustedToString());
3212            write(SafeToString());
3213
3214            f.writeln("just a string");
3215            f.writeln("string with arg: ", 47);
3216            f.writeln(TrustedToString());
3217            f.writeln(SafeToString());
3218
3219            writeln("just a string");
3220            writeln("string with arg: ", 47);
3221            writeln(TrustedToString());
3222            writeln(SafeToString());
3223
3224            f.writef("string with arg: %s", 47);
3225            f.writef("%s", TrustedToString());
3226            f.writef("%s", SafeToString());
3227
3228            writef("string with arg: %s", 47);
3229            writef("%s", TrustedToString());
3230            writef("%s", SafeToString());
3231
3232            f.writefln("string with arg: %s", 47);
3233            f.writefln("%s", TrustedToString());
3234            f.writefln("%s", SafeToString());
3235
3236            writefln("string with arg: %s", 47);
3237            writefln("%s", TrustedToString());
3238            writefln("%s", SafeToString());
3239        }
3240
3241        static assert(!__traits(compiles, f.write(SystemToString().toString())));
3242        static assert(!__traits(compiles, f.writeln(SystemToString())));
3243        static assert(!__traits(compiles, f.writef("%s", SystemToString())));
3244        static assert(!__traits(compiles, f.writefln("%s", SystemToString())));
3245
3246        static assert(!__traits(compiles, write(SystemToString().toString())));
3247        static assert(!__traits(compiles, writeln(SystemToString())));
3248        static assert(!__traits(compiles, writef("%s", SystemToString())));
3249        static assert(!__traits(compiles, writefln("%s", SystemToString())));
3250    }
3251
3252    systemTests();
3253    safeTests();
3254}
3255
3256@safe unittest
3257{
3258    import std.exception : collectException;
3259    static import std.file;
3260
3261    auto deleteme = testFilename();
3262    scope(exit) collectException(std.file.remove(deleteme));
3263    std.file.write(deleteme, "1 2 3");
3264    auto f = File(deleteme);
3265    assert(f.size == 5);
3266    assert(f.tell == 0);
3267}
3268
3269@system unittest
3270{
3271    // @system due to readln
3272    static import std.file;
3273    import std.range : chain, only, repeat;
3274    import std.range.primitives : isOutputRange;
3275
3276    auto deleteme = testFilename();
3277    scope(exit) std.file.remove(deleteme);
3278
3279    {
3280        File f = File(deleteme, "w");
3281        auto writer = f.lockingTextWriter();
3282        static assert(isOutputRange!(typeof(writer), dchar));
3283        writer.put("���������");
3284        writer.put("���������"w);
3285        writer.put("���������"d);
3286        writer.put('���');
3287        writer.put(chain(only('���'), only('���')));
3288        writer.put(repeat('#', 12)); // BUG 11945
3289        writer.put(cast(immutable(ubyte)[])"���������"); // Bug 17229
3290    }
3291    assert(File(deleteme).readln() == "������������������������������������############���������");
3292}
3293
3294@safe unittest
3295{
3296    import std.exception : collectException;
3297    auto e = collectException({ File f; f.writeln("Hello!"); }());
3298    assert(e && e.msg == "Attempting to write to closed File");
3299}
3300
3301/// Used to specify the lock type for $(D File.lock) and $(D File.tryLock).
3302enum LockType
3303{
3304    /**
3305     * Specifies a _read (shared) lock. A _read lock denies all processes
3306     * write access to the specified region of the file, including the
3307     * process that first locks the region. All processes can _read the
3308     * locked region. Multiple simultaneous _read locks are allowed, as
3309     * long as there are no exclusive locks.
3310     */
3311    read,
3312
3313    /**
3314     * Specifies a read/write (exclusive) lock. A read/write lock denies all
3315     * other processes both read and write access to the locked file region.
3316     * If a segment has an exclusive lock, it may not have any shared locks
3317     * or other exclusive locks.
3318     */
3319    readWrite
3320}
3321
3322struct LockingTextReader
3323{
3324    private File _f;
3325    private char _front;
3326    private bool _hasChar;
3327
3328    this(File f)
3329    {
3330        import std.exception : enforce;
3331        enforce(f.isOpen, "LockingTextReader: File must be open");
3332        _f = f;
3333        FLOCK(_f._p.handle);
3334    }
3335
3336    this(this)
3337    {
3338        FLOCK(_f._p.handle);
3339    }
3340
3341    ~this()
3342    {
3343        if (_hasChar)
3344            ungetc(_front, cast(FILE*)_f._p.handle);
3345
3346        // File locking has its own reference count
3347        if (_f.isOpen) FUNLOCK(_f._p.handle);
3348    }
3349
3350    void opAssign(LockingTextReader r)
3351    {
3352        import std.algorithm.mutation : swap;
3353        swap(this, r);
3354    }
3355
3356    @property bool empty()
3357    {
3358        if (!_hasChar)
3359        {
3360            if (!_f.isOpen || _f.eof)
3361                return true;
3362            immutable int c = FGETC(cast(_iobuf*) _f._p.handle);
3363            if (c == EOF)
3364            {
3365                .destroy(_f);
3366                return true;
3367            }
3368            _front = cast(char) c;
3369            _hasChar = true;
3370        }
3371        return false;
3372    }
3373
3374    @property char front()
3375    {
3376        if (!_hasChar)
3377        {
3378            version (assert)
3379            {
3380                import core.exception : RangeError;
3381                if (empty)
3382                    throw new RangeError();
3383            }
3384            else
3385            {
3386                empty;
3387            }
3388        }
3389        return _front;
3390    }
3391
3392    void popFront()
3393    {
3394        if (!_hasChar)
3395            empty;
3396        _hasChar = false;
3397    }
3398}
3399
3400@system unittest
3401{
3402    // @system due to readf
3403    static import std.file;
3404    import std.range.primitives : isInputRange;
3405
3406    static assert(isInputRange!LockingTextReader);
3407    auto deleteme = testFilename();
3408    std.file.write(deleteme, "1 2 3");
3409    scope(exit) std.file.remove(deleteme);
3410    int x, y;
3411    auto f = File(deleteme);
3412    f.readf("%s ", &x);
3413    assert(x == 1);
3414    f.readf("%d ", &x);
3415    assert(x == 2);
3416    f.readf("%d ", &x);
3417    assert(x == 3);
3418}
3419
3420@system unittest // bugzilla 13686
3421{
3422    import std.algorithm.comparison : equal;
3423    static import std.file;
3424    import std.utf : byDchar;
3425
3426    auto deleteme = testFilename();
3427    std.file.write(deleteme, "��������");
3428    scope(exit) std.file.remove(deleteme);
3429
3430    string s;
3431    File(deleteme).readf("%s", &s);
3432    assert(s == "��������");
3433
3434    auto ltr = LockingTextReader(File(deleteme)).byDchar;
3435    assert(equal(ltr, "��������".byDchar));
3436}
3437
3438@system unittest // bugzilla 12320
3439{
3440    static import std.file;
3441    auto deleteme = testFilename();
3442    std.file.write(deleteme, "ab");
3443    scope(exit) std.file.remove(deleteme);
3444    auto ltr = LockingTextReader(File(deleteme));
3445    assert(ltr.front == 'a');
3446    ltr.popFront();
3447    assert(ltr.front == 'b');
3448    ltr.popFront();
3449    assert(ltr.empty);
3450}
3451
3452@system unittest // bugzilla 14861
3453{
3454    // @system due to readf
3455    static import std.file;
3456    auto deleteme = testFilename();
3457    File fw = File(deleteme, "w");
3458    for (int i; i != 5000; i++)
3459        fw.writeln(i, ";", "������������;��������;����������������");
3460    fw.close();
3461    scope(exit) std.file.remove(deleteme);
3462    // Test read
3463    File fr = File(deleteme, "r");
3464    scope (exit) fr.close();
3465    int nom; string fam, nam, ot;
3466    // Error format read
3467    while (!fr.eof)
3468        fr.readf("%s;%s;%s;%s\n", &nom, &fam, &nam, &ot);
3469}
3470
3471/**
3472 * Indicates whether $(D T) is a file handle, i.e. the type
3473 * is implicitly convertable to $(LREF File) or a pointer to a
3474 * $(REF FILE, core,stdc,stdio).
3475 *
3476 * Returns:
3477 *      `true` if `T` is a file handle, `false` otherwise.
3478 */
3479template isFileHandle(T)
3480{
3481    enum isFileHandle = is(T : FILE*) ||
3482        is(T : File);
3483}
3484
3485///
3486@safe unittest
3487{
3488    static assert(isFileHandle!(FILE*));
3489    static assert(isFileHandle!(File));
3490}
3491
3492/**
3493 * Property used by writeln/etc. so it can infer @safe since stdout is __gshared
3494 */
3495private @property File trustedStdout() @trusted
3496{
3497    return stdout;
3498}
3499
3500/***********************************
3501For each argument $(D arg) in $(D args), format the argument (using
3502$(REF to, std,conv)) and write the resulting
3503string to $(D args[0]). A call without any arguments will fail to
3504compile.
3505
3506Params:
3507    args = the items to write to `stdout`
3508
3509Throws: In case of an I/O error, throws an $(D StdioException).
3510
3511Example:
3512    Reads `stdin` and writes it to `stdout` with an argument
3513    counter.
3514---
3515import std.stdio;
3516
3517void main()
3518{
3519    string line;
3520
3521    for (size_t count = 0; (line = readln) !is null; count++)
3522    {
3523         write("Input ", count, ": ", line, "\n");
3524    }
3525}
3526---
3527 */
3528void write(T...)(T args)
3529if (!is(T[0] : File))
3530{
3531    trustedStdout.write(args);
3532}
3533
3534@system unittest
3535{
3536    static import std.file;
3537
3538    scope(failure) printf("Failed test at line %d\n", __LINE__);
3539    void[] buf;
3540    if (false) write(buf);
3541    // test write
3542    auto deleteme = testFilename();
3543    auto f = File(deleteme, "w");
3544    f.write("Hello, ",  "world number ", 42, "!");
3545    f.close();
3546    scope(exit) { std.file.remove(deleteme); }
3547    assert(cast(char[]) std.file.read(deleteme) == "Hello, world number 42!");
3548}
3549
3550/***********************************
3551 * Equivalent to `write(args, '\n')`.  Calling `writeln` without
3552 * arguments is valid and just prints a newline to the standard
3553 * output.
3554 *
3555 * Params:
3556 *      args = the items to write to `stdout`
3557 *
3558 * Throws:
3559 *      In case of an I/O error, throws an $(LREF StdioException).
3560 * Example:
3561 *        Reads $(D stdin) and writes it to $(D stdout) with a argument
3562 *        counter.
3563---
3564import std.stdio;
3565
3566void main()
3567{
3568    string line;
3569
3570    for (size_t count = 0; (line = readln) !is null; count++)
3571    {
3572         writeln("Input ", count, ": ", line);
3573    }
3574}
3575---
3576 */
3577void writeln(T...)(T args)
3578{
3579    import std.traits : isAggregateType;
3580    static if (T.length == 0)
3581    {
3582        import std.exception : enforce;
3583
3584        enforce(fputc('\n', .trustedStdout._p.handle) != EOF, "fputc failed");
3585    }
3586    else static if (T.length == 1 &&
3587                    is(typeof(args[0]) : const(char)[]) &&
3588                    !is(typeof(args[0]) == enum) &&
3589                    !is(Unqual!(typeof(args[0])) == typeof(null)) &&
3590                    !isAggregateType!(typeof(args[0])))
3591    {
3592        import std.traits : isStaticArray;
3593
3594        // Specialization for strings - a very frequent case
3595        auto w = .trustedStdout.lockingTextWriter();
3596
3597        static if (isStaticArray!(typeof(args[0])))
3598        {
3599            w.put(args[0][]);
3600        }
3601        else
3602        {
3603            w.put(args[0]);
3604        }
3605        w.put('\n');
3606    }
3607    else
3608    {
3609        // Most general instance
3610        trustedStdout.write(args, '\n');
3611    }
3612}
3613
3614@safe unittest
3615{
3616    // Just make sure the call compiles
3617    if (false) writeln();
3618
3619    if (false) writeln("wyda");
3620
3621    // bug 8040
3622    if (false) writeln(null);
3623    if (false) writeln(">", null, "<");
3624
3625    // Bugzilla 14041
3626    if (false)
3627    {
3628        char[8] a;
3629        writeln(a);
3630    }
3631}
3632
3633@system unittest
3634{
3635    static import std.file;
3636
3637    scope(failure) printf("Failed test at line %d\n", __LINE__);
3638
3639    // test writeln
3640    auto deleteme = testFilename();
3641    auto f = File(deleteme, "w");
3642    scope(exit) { std.file.remove(deleteme); }
3643    f.writeln("Hello, ",  "world number ", 42, "!");
3644    f.close();
3645    version (Windows)
3646        assert(cast(char[]) std.file.read(deleteme) ==
3647                "Hello, world number 42!\r\n");
3648    else
3649        assert(cast(char[]) std.file.read(deleteme) ==
3650                "Hello, world number 42!\n");
3651
3652    // test writeln on stdout
3653    auto saveStdout = stdout;
3654    scope(exit) stdout = saveStdout;
3655    stdout.open(deleteme, "w");
3656    writeln("Hello, ",  "world number ", 42, "!");
3657    stdout.close();
3658    version (Windows)
3659        assert(cast(char[]) std.file.read(deleteme) ==
3660                "Hello, world number 42!\r\n");
3661    else
3662        assert(cast(char[]) std.file.read(deleteme) ==
3663                "Hello, world number 42!\n");
3664
3665    stdout.open(deleteme, "w");
3666    writeln("Hello!"c);
3667    writeln("Hello!"w);    // bug 8386
3668    writeln("Hello!"d);    // bug 8386
3669    writeln("embedded\0null"c); // bug 8730
3670    stdout.close();
3671    version (Windows)
3672        assert(cast(char[]) std.file.read(deleteme) ==
3673            "Hello!\r\nHello!\r\nHello!\r\nembedded\0null\r\n");
3674    else
3675        assert(cast(char[]) std.file.read(deleteme) ==
3676            "Hello!\nHello!\nHello!\nembedded\0null\n");
3677}
3678
3679@system unittest
3680{
3681    static import std.file;
3682
3683    auto deleteme = testFilename();
3684    auto f = File(deleteme, "w");
3685    scope(exit) { std.file.remove(deleteme); }
3686
3687    enum EI : int    { A, B }
3688    enum ED : double { A, B }
3689    enum EC : char   { A, B }
3690    enum ES : string { A = "aaa", B = "bbb" }
3691
3692    f.writeln(EI.A);  // false, but A on 2.058
3693    f.writeln(EI.B);  // true, but B on 2.058
3694
3695    f.writeln(ED.A);  // A
3696    f.writeln(ED.B);  // B
3697
3698    f.writeln(EC.A);  // A
3699    f.writeln(EC.B);  // B
3700
3701    f.writeln(ES.A);  // A
3702    f.writeln(ES.B);  // B
3703
3704    f.close();
3705    version (Windows)
3706        assert(cast(char[]) std.file.read(deleteme) ==
3707                "A\r\nB\r\nA\r\nB\r\nA\r\nB\r\nA\r\nB\r\n");
3708    else
3709        assert(cast(char[]) std.file.read(deleteme) ==
3710                "A\nB\nA\nB\nA\nB\nA\nB\n");
3711}
3712
3713@system unittest
3714{
3715    static auto useInit(T)(T ltw)
3716    {
3717        T val;
3718        val = ltw;
3719        val = T.init;
3720        return val;
3721    }
3722    useInit(stdout.lockingTextWriter());
3723}
3724
3725
3726/***********************************
3727Writes formatted data to standard output (without a trailing newline).
3728
3729Params:
3730fmt = The $(LINK2 std_format.html#format-string, format string).
3731When passed as a compile-time argument, the string will be statically checked
3732against the argument types passed.
3733args = Items to write.
3734
3735Note: In older versions of Phobos, it used to be possible to write:
3736
3737------
3738writef(stderr, "%s", "message");
3739------
3740
3741to print a message to $(D stderr). This syntax is no longer supported, and has
3742been superceded by:
3743
3744------
3745stderr.writef("%s", "message");
3746------
3747
3748*/
3749void writef(alias fmt, A...)(A args)
3750if (isSomeString!(typeof(fmt)))
3751{
3752    import std.format : checkFormatException;
3753
3754    alias e = checkFormatException!(fmt, A);
3755    static assert(!e, e.msg);
3756    return .writef(fmt, args);
3757}
3758
3759/// ditto
3760void writef(Char, A...)(in Char[] fmt, A args)
3761{
3762    trustedStdout.writef(fmt, args);
3763}
3764
3765@system unittest
3766{
3767    static import std.file;
3768
3769    scope(failure) printf("Failed test at line %d\n", __LINE__);
3770
3771    // test writef
3772    auto deleteme = testFilename();
3773    auto f = File(deleteme, "w");
3774    scope(exit) { std.file.remove(deleteme); }
3775    f.writef!"Hello, %s world number %s!"("nice", 42);
3776    f.close();
3777    assert(cast(char[]) std.file.read(deleteme) ==  "Hello, nice world number 42!");
3778    // test write on stdout
3779    auto saveStdout = stdout;
3780    scope(exit) stdout = saveStdout;
3781    stdout.open(deleteme, "w");
3782    writef!"Hello, %s world number %s!"("nice", 42);
3783    stdout.close();
3784    assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
3785}
3786
3787/***********************************
3788 * Equivalent to $(D writef(fmt, args, '\n')).
3789 */
3790void writefln(alias fmt, A...)(A args)
3791if (isSomeString!(typeof(fmt)))
3792{
3793    import std.format : checkFormatException;
3794
3795    alias e = checkFormatException!(fmt, A);
3796    static assert(!e, e.msg);
3797    return .writefln(fmt, args);
3798}
3799
3800/// ditto
3801void writefln(Char, A...)(in Char[] fmt, A args)
3802{
3803    trustedStdout.writefln(fmt, args);
3804}
3805
3806@system unittest
3807{
3808    static import std.file;
3809
3810    scope(failure) printf("Failed test at line %d\n", __LINE__);
3811
3812    // test File.writefln
3813    auto deleteme = testFilename();
3814    auto f = File(deleteme, "w");
3815    scope(exit) { std.file.remove(deleteme); }
3816    f.writefln!"Hello, %s world number %s!"("nice", 42);
3817    f.close();
3818    version (Windows)
3819        assert(cast(char[]) std.file.read(deleteme) ==
3820                "Hello, nice world number 42!\r\n");
3821    else
3822        assert(cast(char[]) std.file.read(deleteme) ==
3823                "Hello, nice world number 42!\n",
3824                cast(char[]) std.file.read(deleteme));
3825
3826    // test writefln
3827    auto saveStdout = stdout;
3828    scope(exit) stdout = saveStdout;
3829    stdout.open(deleteme, "w");
3830    writefln!"Hello, %s world number %s!"("nice", 42);
3831    stdout.close();
3832    version (Windows)
3833        assert(cast(char[]) std.file.read(deleteme) ==
3834                "Hello, nice world number 42!\r\n");
3835    else
3836        assert(cast(char[]) std.file.read(deleteme) ==
3837                "Hello, nice world number 42!\n");
3838}
3839
3840/**
3841 * Reads formatted data from $(D stdin) using $(REF formattedRead, std,_format).
3842 * Params:
3843 * format = The $(LINK2 std_format.html#_format-string, _format string).
3844 * When passed as a compile-time argument, the string will be statically checked
3845 * against the argument types passed.
3846 * args = Items to be read.
3847 * Example:
3848----
3849// test.d
3850void main()
3851{
3852    import std.stdio;
3853    foreach (_; 0 .. 3)
3854    {
3855        int a;
3856        readf!" %d"(a);
3857        writeln(++a);
3858    }
3859}
3860----
3861$(CONSOLE
3862% echo "1 2 3" | rdmd test.d
38632
38643
38654
3866)
3867 */
3868uint readf(alias format, A...)(auto ref A args)
3869if (isSomeString!(typeof(format)))
3870{
3871    import std.format : checkFormatException;
3872
3873    alias e = checkFormatException!(format, A);
3874    static assert(!e, e.msg);
3875    return .readf(format, args);
3876}
3877
3878/// ditto
3879uint readf(A...)(in char[] format, auto ref A args)
3880{
3881    return stdin.readf(format, args);
3882}
3883
3884@system unittest
3885{
3886    float f;
3887    if (false) uint x = readf("%s", &f);
3888
3889    char a;
3890    wchar b;
3891    dchar c;
3892    if (false) readf("%s %s %s", a, b, c);
3893    // backwards compatibility with pointers
3894    if (false) readf("%s %s %s", a, &b, c);
3895    if (false) readf("%s %s %s", &a, &b, &c);
3896}
3897
3898/**********************************
3899 * Read line from $(D stdin).
3900 *
3901 * This version manages its own read buffer, which means one memory allocation per call. If you are not
3902 * retaining a reference to the read data, consider the $(D readln(buf)) version, which may offer
3903 * better performance as it can reuse its read buffer.
3904 *
3905 * Returns:
3906 *        The line that was read, including the line terminator character.
3907 * Params:
3908 *        S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
3909 *        terminator = Line terminator (by default, $(D '\n')).
3910 * Note:
3911 *        String terminators are not supported due to ambiguity with readln(buf) below.
3912 * Throws:
3913 *        $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
3914 * Example:
3915 *        Reads $(D stdin) and writes it to $(D stdout).
3916---
3917import std.stdio;
3918
3919void main()
3920{
3921    string line;
3922    while ((line = readln()) !is null)
3923        write(line);
3924}
3925---
3926*/
3927S readln(S = string)(dchar terminator = '\n')
3928if (isSomeString!S)
3929{
3930    return stdin.readln!S(terminator);
3931}
3932
3933/**********************************
3934 * Read line from $(D stdin) and write it to buf[], including terminating character.
3935 *
3936 * This can be faster than $(D line = readln()) because you can reuse
3937 * the buffer for each call. Note that reusing the buffer means that you
3938 * must copy the previous contents if you wish to retain them.
3939 *
3940 * Returns:
3941 *        $(D size_t) 0 for end of file, otherwise number of characters read
3942 * Params:
3943 *        buf = Buffer used to store the resulting line data. buf is resized as necessary.
3944 *        terminator = Line terminator (by default, $(D '\n')). Use $(REF newline, std,ascii)
3945 *        for portability (unless the file was opened in text mode).
3946 * Throws:
3947 *        $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
3948 * Example:
3949 *        Reads $(D stdin) and writes it to $(D stdout).
3950---
3951import std.stdio;
3952
3953void main()
3954{
3955    char[] buf;
3956    while (readln(buf))
3957        write(buf);
3958}
3959---
3960*/
3961size_t readln(C)(ref C[] buf, dchar terminator = '\n')
3962if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
3963{
3964    return stdin.readln(buf, terminator);
3965}
3966
3967/** ditto */
3968size_t readln(C, R)(ref C[] buf, R terminator)
3969if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
3970    isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
3971{
3972    return stdin.readln(buf, terminator);
3973}
3974
3975@safe unittest
3976{
3977    import std.meta : AliasSeq;
3978
3979    //we can't actually test readln, so at the very least,
3980    //we test compilability
3981    void foo()
3982    {
3983        readln();
3984        readln('\t');
3985        foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
3986        {
3987            readln!String();
3988            readln!String('\t');
3989        }
3990        foreach (String; AliasSeq!(char[], wchar[], dchar[]))
3991        {
3992            String buf;
3993            readln(buf);
3994            readln(buf, '\t');
3995            readln(buf, "<br />");
3996        }
3997    }
3998}
3999
4000/*
4001 * Convenience function that forwards to $(D core.sys.posix.stdio.fopen)
4002 * (to $(D _wfopen) on Windows)
4003 * with appropriately-constructed C-style strings.
4004 */
4005private FILE* fopen(R1, R2)(R1 name, R2 mode = "r")
4006if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) &&
4007    (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2))
4008{
4009    import std.internal.cstring : tempCString;
4010
4011    auto namez = name.tempCString!FSChar();
4012    auto modez = mode.tempCString!FSChar();
4013
4014    static fopenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
4015    {
4016        version (Windows)
4017        {
4018            return _wfopen(namez, modez);
4019        }
4020        else version (Posix)
4021        {
4022            /*
4023             * The new opengroup large file support API is transparently
4024             * included in the normal C bindings. http://opengroup.org/platform/lfs.html#1.0
4025             * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and
4026             * the normal functions work fine. If not, then large file support
4027             * probably isn't available. Do not use the old transitional API
4028             * (the native extern(C) fopen64, http://www.unix.org/version2/whatsnew/lfs20mar.html#3.0)
4029             */
4030            import core.sys.posix.stdio : fopen;
4031            return fopen(namez, modez);
4032        }
4033        else
4034        {
4035            return .fopen(namez, modez);
4036        }
4037    }
4038    return fopenImpl(namez, modez);
4039}
4040
4041version (Posix)
4042{
4043    /***********************************
4044     * Convenience function that forwards to $(D core.sys.posix.stdio.popen)
4045     * with appropriately-constructed C-style strings.
4046     */
4047    FILE* popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc
4048    if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) &&
4049        (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2))
4050    {
4051        import std.internal.cstring : tempCString;
4052
4053        auto namez = name.tempCString!FSChar();
4054        auto modez = mode.tempCString!FSChar();
4055
4056        static popenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
4057        {
4058            import core.sys.posix.stdio : popen;
4059            return popen(namez, modez);
4060        }
4061        return popenImpl(namez, modez);
4062    }
4063}
4064
4065/*
4066 * Convenience function that forwards to $(D core.stdc.stdio.fwrite)
4067 */
4068private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted
4069{
4070    return fwrite(obj.ptr, T.sizeof, obj.length, f);
4071}
4072
4073/*
4074 * Convenience function that forwards to $(D core.stdc.stdio.fread)
4075 */
4076private auto trustedFread(T)(FILE* f, T[] obj) @trusted
4077{
4078    return fread(obj.ptr, T.sizeof, obj.length, f);
4079}
4080
4081/**
4082 * Iterates through the lines of a file by using $(D foreach).
4083 *
4084 * Example:
4085 *
4086---------
4087void main()
4088{
4089  foreach (string line; lines(stdin))
4090  {
4091    ... use line ...
4092  }
4093}
4094---------
4095The line terminator ($(D '\n') by default) is part of the string read (it
4096could be missing in the last line of the file). Several types are
4097supported for $(D line), and the behavior of $(D lines)
4098changes accordingly:
4099
4100$(OL $(LI If $(D line) has type $(D string), $(D
4101wstring), or $(D dstring), a new string of the respective type
4102is allocated every read.) $(LI If $(D line) has type $(D
4103char[]), $(D wchar[]), $(D dchar[]), the line's content
4104will be reused (overwritten) across reads.) $(LI If $(D line)
4105has type $(D immutable(ubyte)[]), the behavior is similar to
4106case (1), except that no UTF checking is attempted upon input.) $(LI
4107If $(D line) has type $(D ubyte[]), the behavior is
4108similar to case (2), except that no UTF checking is attempted upon
4109input.))
4110
4111In all cases, a two-symbols versions is also accepted, in which case
4112the first symbol (of integral type, e.g. $(D ulong) or $(D
4113uint)) tracks the zero-based number of the current line.
4114
4115Example:
4116----
4117  foreach (ulong i, string line; lines(stdin))
4118  {
4119    ... use line ...
4120  }
4121----
4122
4123 In case of an I/O error, an $(D StdioException) is thrown.
4124
4125See_Also:
4126$(LREF byLine)
4127 */
4128
4129struct lines
4130{
4131    private File f;
4132    private dchar terminator = '\n';
4133
4134    /**
4135    Constructor.
4136    Params:
4137    f = File to read lines from.
4138    terminator = Line separator ($(D '\n') by default).
4139    */
4140    this(File f, dchar terminator = '\n')
4141    {
4142        this.f = f;
4143        this.terminator = terminator;
4144    }
4145
4146    int opApply(D)(scope D dg)
4147    {
4148        import std.traits : Parameters;
4149        alias Parms = Parameters!(dg);
4150        static if (isSomeString!(Parms[$ - 1]))
4151        {
4152            enum bool duplicate = is(Parms[$ - 1] == string)
4153                || is(Parms[$ - 1] == wstring) || is(Parms[$ - 1] == dstring);
4154            int result = 0;
4155            static if (is(Parms[$ - 1] : const(char)[]))
4156                alias C = char;
4157            else static if (is(Parms[$ - 1] : const(wchar)[]))
4158                alias C = wchar;
4159            else static if (is(Parms[$ - 1] : const(dchar)[]))
4160                alias C = dchar;
4161            C[] line;
4162            static if (Parms.length == 2)
4163                Parms[0] i = 0;
4164            for (;;)
4165            {
4166                import std.conv : to;
4167
4168                if (!f.readln(line, terminator)) break;
4169                auto copy = to!(Parms[$ - 1])(line);
4170                static if (Parms.length == 2)
4171                {
4172                    result = dg(i, copy);
4173                    ++i;
4174                }
4175                else
4176                {
4177                    result = dg(copy);
4178                }
4179                if (result != 0) break;
4180            }
4181            return result;
4182        }
4183        else
4184        {
4185            // raw read
4186            return opApplyRaw(dg);
4187        }
4188    }
4189    // no UTF checking
4190    int opApplyRaw(D)(scope D dg)
4191    {
4192        import std.conv : to;
4193        import std.exception : assumeUnique;
4194        import std.traits : Parameters;
4195
4196        alias Parms = Parameters!(dg);
4197        enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]);
4198        int result = 1;
4199        int c = void;
4200        FLOCK(f._p.handle);
4201        scope(exit) FUNLOCK(f._p.handle);
4202        ubyte[] buffer;
4203        static if (Parms.length == 2)
4204            Parms[0] line = 0;
4205        while ((c = FGETC(cast(_iobuf*) f._p.handle)) != -1)
4206        {
4207            buffer ~= to!(ubyte)(c);
4208            if (c == terminator)
4209            {
4210                static if (duplicate)
4211                    auto arg = assumeUnique(buffer);
4212                else
4213                    alias arg = buffer;
4214                // unlock the file while calling the delegate
4215                FUNLOCK(f._p.handle);
4216                scope(exit) FLOCK(f._p.handle);
4217                static if (Parms.length == 1)
4218                {
4219                    result = dg(arg);
4220                }
4221                else
4222                {
4223                    result = dg(line, arg);
4224                    ++line;
4225                }
4226                if (result) break;
4227                static if (!duplicate)
4228                    buffer.length = 0;
4229            }
4230        }
4231        // can only reach when FGETC returned -1
4232        if (!f.eof) throw new StdioException("Error in reading file"); // error occured
4233        return result;
4234    }
4235}
4236
4237@system unittest
4238{
4239    static import std.file;
4240    import std.meta : AliasSeq;
4241
4242    scope(failure) printf("Failed test at line %d\n", __LINE__);
4243
4244    auto deleteme = testFilename();
4245    scope(exit) { std.file.remove(deleteme); }
4246
4247    alias TestedWith =
4248          AliasSeq!(string, wstring, dstring,
4249                    char[], wchar[], dchar[]);
4250    foreach (T; TestedWith)
4251    {
4252        // test looping with an empty file
4253        std.file.write(deleteme, "");
4254        auto f = File(deleteme, "r");
4255        foreach (T line; lines(f))
4256        {
4257            assert(false);
4258        }
4259        f.close();
4260
4261        // test looping with a file with three lines
4262        std.file.write(deleteme, "Line one\nline two\nline three\n");
4263        f.open(deleteme, "r");
4264        uint i = 0;
4265        foreach (T line; lines(f))
4266        {
4267            if (i == 0) assert(line == "Line one\n");
4268            else if (i == 1) assert(line == "line two\n");
4269            else if (i == 2) assert(line == "line three\n");
4270            else assert(false);
4271            ++i;
4272        }
4273        f.close();
4274
4275        // test looping with a file with three lines, last without a newline
4276        std.file.write(deleteme, "Line one\nline two\nline three");
4277        f.open(deleteme, "r");
4278        i = 0;
4279        foreach (T line; lines(f))
4280        {
4281            if (i == 0) assert(line == "Line one\n");
4282            else if (i == 1) assert(line == "line two\n");
4283            else if (i == 2) assert(line == "line three");
4284            else assert(false);
4285            ++i;
4286        }
4287        f.close();
4288    }
4289
4290    // test with ubyte[] inputs
4291    alias TestedWith2 = AliasSeq!(immutable(ubyte)[], ubyte[]);
4292    foreach (T; TestedWith2)
4293    {
4294        // test looping with an empty file
4295        std.file.write(deleteme, "");
4296        auto f = File(deleteme, "r");
4297        foreach (T line; lines(f))
4298        {
4299            assert(false);
4300        }
4301        f.close();
4302
4303        // test looping with a file with three lines
4304        std.file.write(deleteme, "Line one\nline two\nline three\n");
4305        f.open(deleteme, "r");
4306        uint i = 0;
4307        foreach (T line; lines(f))
4308        {
4309            if (i == 0) assert(cast(char[]) line == "Line one\n");
4310            else if (i == 1) assert(cast(char[]) line == "line two\n",
4311                T.stringof ~ " " ~ cast(char[]) line);
4312            else if (i == 2) assert(cast(char[]) line == "line three\n");
4313            else assert(false);
4314            ++i;
4315        }
4316        f.close();
4317
4318        // test looping with a file with three lines, last without a newline
4319        std.file.write(deleteme, "Line one\nline two\nline three");
4320        f.open(deleteme, "r");
4321        i = 0;
4322        foreach (T line; lines(f))
4323        {
4324            if (i == 0) assert(cast(char[]) line == "Line one\n");
4325            else if (i == 1) assert(cast(char[]) line == "line two\n");
4326            else if (i == 2) assert(cast(char[]) line == "line three");
4327            else assert(false);
4328            ++i;
4329        }
4330        f.close();
4331
4332    }
4333
4334    foreach (T; AliasSeq!(ubyte[]))
4335    {
4336        // test looping with a file with three lines, last without a newline
4337        // using a counter too this time
4338        std.file.write(deleteme, "Line one\nline two\nline three");
4339        auto f = File(deleteme, "r");
4340        uint i = 0;
4341        foreach (ulong j, T line; lines(f))
4342        {
4343            if (i == 0) assert(cast(char[]) line == "Line one\n");
4344            else if (i == 1) assert(cast(char[]) line == "line two\n");
4345            else if (i == 2) assert(cast(char[]) line == "line three");
4346            else assert(false);
4347            ++i;
4348        }
4349        f.close();
4350    }
4351}
4352
4353/**
4354Iterates through a file a chunk at a time by using $(D foreach).
4355
4356Example:
4357
4358---------
4359void main()
4360{
4361    foreach (ubyte[] buffer; chunks(stdin, 4096))
4362    {
4363        ... use buffer ...
4364    }
4365}
4366---------
4367
4368The content of $(D buffer) is reused across calls. In the
4369 example above, $(D buffer.length) is 4096 for all iterations,
4370 except for the last one, in which case $(D buffer.length) may
4371 be less than 4096 (but always greater than zero).
4372
4373 In case of an I/O error, an $(D StdioException) is thrown.
4374*/
4375auto chunks(File f, size_t size)
4376{
4377    return ChunksImpl(f, size);
4378}
4379private struct ChunksImpl
4380{
4381    private File f;
4382    private size_t size;
4383    // private string fileName; // Currently, no use
4384
4385    this(File f, size_t size)
4386    in
4387    {
4388        assert(size, "size must be larger than 0");
4389    }
4390    body
4391    {
4392        this.f = f;
4393        this.size = size;
4394    }
4395
4396    int opApply(D)(scope D dg)
4397    {
4398        import core.stdc.stdlib : alloca;
4399        enum maxStackSize = 1024 * 16;
4400        ubyte[] buffer = void;
4401        if (size < maxStackSize)
4402            buffer = (cast(ubyte*) alloca(size))[0 .. size];
4403        else
4404            buffer = new ubyte[size];
4405        size_t r = void;
4406        int result = 1;
4407        uint tally = 0;
4408        while ((r = trustedFread(f._p.handle, buffer)) > 0)
4409        {
4410            assert(r <= size);
4411            if (r != size)
4412            {
4413                // error occured
4414                if (!f.eof) throw new StdioException(null);
4415                buffer.length = r;
4416            }
4417            static if (is(typeof(dg(tally, buffer))))
4418            {
4419                if ((result = dg(tally, buffer)) != 0) break;
4420            }
4421            else
4422            {
4423                if ((result = dg(buffer)) != 0) break;
4424            }
4425            ++tally;
4426        }
4427        return result;
4428    }
4429}
4430
4431@system unittest
4432{
4433    static import std.file;
4434
4435    scope(failure) printf("Failed test at line %d\n", __LINE__);
4436
4437    auto deleteme = testFilename();
4438    scope(exit) { std.file.remove(deleteme); }
4439
4440    // test looping with an empty file
4441    std.file.write(deleteme, "");
4442    auto f = File(deleteme, "r");
4443    foreach (ubyte[] line; chunks(f, 4))
4444    {
4445        assert(false);
4446    }
4447    f.close();
4448
4449    // test looping with a file with three lines
4450    std.file.write(deleteme, "Line one\nline two\nline three\n");
4451    f = File(deleteme, "r");
4452    uint i = 0;
4453    foreach (ubyte[] line; chunks(f, 3))
4454    {
4455        if (i == 0) assert(cast(char[]) line == "Lin");
4456        else if (i == 1) assert(cast(char[]) line == "e o");
4457        else if (i == 2) assert(cast(char[]) line == "ne\n");
4458        else break;
4459        ++i;
4460    }
4461    f.close();
4462}
4463
4464
4465/**
4466Writes an array or range to a file.
4467Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)).
4468Similar to $(REF write, std,file), strings are written as-is,
4469rather than encoded according to the $(D File)'s $(HTTP
4470en.cppreference.com/w/c/io#Narrow_and_wide_orientation,
4471orientation).
4472*/
4473void toFile(T)(T data, string fileName)
4474if (is(typeof(copy(data, stdout.lockingBinaryWriter))))
4475{
4476    copy(data, File(fileName, "wb").lockingBinaryWriter);
4477}
4478
4479@system unittest
4480{
4481    static import std.file;
4482
4483    auto deleteme = testFilename();
4484    scope(exit) { std.file.remove(deleteme); }
4485
4486    "Test".toFile(deleteme);
4487    assert(std.file.readText(deleteme) == "Test");
4488}
4489
4490/*********************
4491 * Thrown if I/O errors happen.
4492 */
4493class StdioException : Exception
4494{
4495    static import core.stdc.errno;
4496    /// Operating system error code.
4497    uint errno;
4498
4499/**
4500Initialize with a message and an error code.
4501*/
4502    this(string message, uint e = core.stdc.errno.errno) @trusted
4503    {
4504        import std.exception : errnoString;
4505        errno = e;
4506        auto sysmsg = errnoString(errno);
4507        // If e is 0, we don't use the system error message.  (The message
4508        // is "Success", which is rather pointless for an exception.)
4509        super(e == 0 ? message
4510                     : (message ? message ~ " (" ~ sysmsg ~ ")" : sysmsg));
4511    }
4512
4513/** Convenience functions that throw an $(D StdioException). */
4514    static void opCall(string msg)
4515    {
4516        throw new StdioException(msg);
4517    }
4518
4519/// ditto
4520    static void opCall()
4521    {
4522        throw new StdioException(null, core.stdc.errno.errno);
4523    }
4524}
4525
4526enum StdFileHandle: string
4527{
4528    stdin  = "core.stdc.stdio.stdin",
4529    stdout = "core.stdc.stdio.stdout",
4530    stderr = "core.stdc.stdio.stderr",
4531}
4532
4533// Undocumented but public because the std* handles are aliasing it.
4534@property ref File makeGlobal(StdFileHandle _iob)()
4535{
4536    __gshared File.Impl impl;
4537    __gshared File result;
4538
4539    // Use an inline spinlock to make sure the initializer is only run once.
4540    // We assume there will be at most uint.max / 2 threads trying to initialize
4541    // `handle` at once and steal the high bit to indicate that the globals have
4542    // been initialized.
4543    static shared uint spinlock;
4544    import core.atomic : atomicLoad, atomicOp, MemoryOrder;
4545    if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2)
4546    {
4547        for (;;)
4548        {
4549            if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max / 2)
4550                break;
4551            if (atomicOp!"+="(spinlock, 1) == 1)
4552            {
4553                with (StdFileHandle)
4554                    assert(_iob == stdin || _iob == stdout || _iob == stderr);
4555                impl.handle = mixin(_iob);
4556                result._p = &impl;
4557                atomicOp!"+="(spinlock, uint.max / 2);
4558                break;
4559            }
4560            atomicOp!"-="(spinlock, 1);
4561        }
4562    }
4563    return result;
4564}
4565
4566/** The standard input stream.
4567    Bugs:
4568        Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
4569        it is thread un-safe to reassign `stdin` to a different `File` instance
4570        than the default.
4571*/
4572alias stdin = makeGlobal!(StdFileHandle.stdin);
4573
4574///
4575@safe unittest
4576{
4577    // Read stdin, sort lines, write to stdout
4578    import std.algorithm.mutation : copy;
4579    import std.algorithm.sorting : sort;
4580    import std.array : array;
4581    import std.typecons : Yes;
4582
4583    void main() {
4584        stdin                       // read from stdin
4585        .byLineCopy(Yes.keepTerminator) // copying each line
4586        .array()                    // convert to array of lines
4587        .sort()                     // sort the lines
4588        .copy(                      // copy output of .sort to an OutputRange
4589            stdout.lockingTextWriter()); // the OutputRange
4590    }
4591}
4592
4593/**
4594    The standard output stream.
4595    Bugs:
4596        Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
4597        it is thread un-safe to reassign `stdout` to a different `File` instance
4598        than the default.
4599*/
4600alias stdout = makeGlobal!(StdFileHandle.stdout);
4601
4602/**
4603    The standard error stream.
4604    Bugs:
4605        Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
4606        it is thread un-safe to reassign `stderr` to a different `File` instance
4607        than the default.
4608*/
4609alias stderr = makeGlobal!(StdFileHandle.stderr);
4610
4611@system unittest
4612{
4613    static import std.file;
4614    import std.typecons : tuple;
4615
4616    scope(failure) printf("Failed test at line %d\n", __LINE__);
4617    auto deleteme = testFilename();
4618
4619    std.file.write(deleteme, "1 2\n4 1\n5 100");
4620    scope(exit) std.file.remove(deleteme);
4621    {
4622        File f = File(deleteme);
4623        scope(exit) f.close();
4624        auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ];
4625        uint i;
4626        foreach (e; f.byRecord!(int, int)("%s %s"))
4627        {
4628            //writeln(e);
4629            assert(e == t[i++]);
4630        }
4631        assert(i == 3);
4632    }
4633}
4634
4635@safe unittest
4636{
4637    // Retain backwards compatibility
4638    // https://issues.dlang.org/show_bug.cgi?id=17472
4639    static assert(is(typeof(stdin) == File));
4640    static assert(is(typeof(stdout) == File));
4641    static assert(is(typeof(stderr) == File));
4642}
4643
4644// roll our own appender, but with "safe" arrays
4645private struct ReadlnAppender
4646{
4647    char[] buf;
4648    size_t pos;
4649    bool safeAppend = false;
4650
4651    void initialize(char[] b)
4652    {
4653        buf = b;
4654        pos = 0;
4655    }
4656    @property char[] data() @trusted
4657    {
4658        if (safeAppend)
4659            assumeSafeAppend(buf.ptr[0 .. pos]);
4660        return buf.ptr[0 .. pos];
4661    }
4662
4663    bool reserveWithoutAllocating(size_t n)
4664    {
4665        if (buf.length >= pos + n) // buf is already large enough
4666            return true;
4667
4668        immutable curCap = buf.capacity;
4669        if (curCap >= pos + n)
4670        {
4671            buf.length = curCap;
4672            /* Any extra capacity we end up not using can safely be claimed
4673            by someone else. */
4674            safeAppend = true;
4675            return true;
4676        }
4677
4678        return false;
4679    }
4680    void reserve(size_t n) @trusted
4681    {
4682        import core.stdc.string : memcpy;
4683        if (!reserveWithoutAllocating(n))
4684        {
4685            size_t ncap = buf.length * 2 + 128 + n;
4686            char[] nbuf = new char[ncap];
4687            memcpy(nbuf.ptr, buf.ptr, pos);
4688            buf = nbuf;
4689            // Allocated a new buffer. No one else knows about it.
4690            safeAppend = true;
4691        }
4692    }
4693    void putchar(char c) @trusted
4694    {
4695        reserve(1);
4696        buf.ptr[pos++] = c;
4697    }
4698    void putdchar(dchar dc) @trusted
4699    {
4700        import std.utf : encode, UseReplacementDchar;
4701
4702        char[4] ubuf;
4703        immutable size = encode!(UseReplacementDchar.yes)(ubuf, dc);
4704        reserve(size);
4705        foreach (c; ubuf)
4706            buf.ptr[pos++] = c;
4707    }
4708    void putonly(char[] b) @trusted
4709    {
4710        import core.stdc.string : memcpy;
4711        assert(pos == 0);   // assume this is the only put call
4712        if (reserveWithoutAllocating(b.length))
4713            memcpy(buf.ptr + pos, b.ptr, b.length);
4714        else
4715            buf = b.dup;
4716        pos = b.length;
4717    }
4718}
4719
4720// Private implementation of readln
4721version (DIGITAL_MARS_STDIO)
4722private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/)
4723{
4724    FLOCK(fps);
4725    scope(exit) FUNLOCK(fps);
4726
4727    /* Since fps is now locked, we can create an "unshared" version
4728     * of fp.
4729     */
4730    auto fp = cast(_iobuf*) fps;
4731
4732    ReadlnAppender app;
4733    app.initialize(buf);
4734
4735    if (__fhnd_info[fp._file] & FHND_WCHAR)
4736    {   /* Stream is in wide characters.
4737         * Read them and convert to chars.
4738         */
4739        static assert(wchar_t.sizeof == 2);
4740        for (int c = void; (c = FGETWC(fp)) != -1; )
4741        {
4742            if ((c & ~0x7F) == 0)
4743            {
4744                app.putchar(cast(char) c);
4745                if (c == terminator)
4746                    break;
4747            }
4748            else
4749            {
4750                if (c >= 0xD800 && c <= 0xDBFF)
4751                {
4752                    int c2 = void;
4753                    if ((c2 = FGETWC(fp)) != -1 ||
4754                            c2 < 0xDC00 && c2 > 0xDFFF)
4755                    {
4756                        StdioException("unpaired UTF-16 surrogate");
4757                    }
4758                    c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
4759                }
4760                app.putdchar(cast(dchar) c);
4761            }
4762        }
4763        if (ferror(fps))
4764            StdioException();
4765    }
4766
4767    else if (fp._flag & _IONBF)
4768    {
4769        /* Use this for unbuffered I/O, when running
4770         * across buffer boundaries, or for any but the common
4771         * cases.
4772         */
4773      L1:
4774        int c;
4775        while ((c = FGETC(fp)) != -1)
4776        {
4777            app.putchar(cast(char) c);
4778            if (c == terminator)
4779            {
4780                buf = app.data;
4781                return buf.length;
4782            }
4783
4784        }
4785
4786        if (ferror(fps))
4787            StdioException();
4788    }
4789    else
4790    {
4791        int u = fp._cnt;
4792        char* p = fp._ptr;
4793        int i;
4794        if (fp._flag & _IOTRAN)
4795        {   /* Translated mode ignores \r and treats ^Z as end-of-file
4796             */
4797            char c;
4798            while (1)
4799            {
4800                if (i == u)         // if end of buffer
4801                    goto L1;        // give up
4802                c = p[i];
4803                i++;
4804                if (c != '\r')
4805                {
4806                    if (c == terminator)
4807                        break;
4808                    if (c != 0x1A)
4809                        continue;
4810                    goto L1;
4811                }
4812                else
4813                {   if (i != u && p[i] == terminator)
4814                        break;
4815                    goto L1;
4816                }
4817            }
4818            app.putonly(p[0 .. i]);
4819            app.buf[i - 1] = cast(char) terminator;
4820            if (terminator == '\n' && c == '\r')
4821                i++;
4822        }
4823        else
4824        {
4825            while (1)
4826            {
4827                if (i == u)         // if end of buffer
4828                    goto L1;        // give up
4829                auto c = p[i];
4830                i++;
4831                if (c == terminator)
4832                    break;
4833            }
4834            app.putonly(p[0 .. i]);
4835        }
4836        fp._cnt -= i;
4837        fp._ptr += i;
4838    }
4839
4840    buf = app.data;
4841    return buf.length;
4842}
4843
4844version (MICROSOFT_STDIO)
4845private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/)
4846{
4847    FLOCK(fps);
4848    scope(exit) FUNLOCK(fps);
4849
4850    /* Since fps is now locked, we can create an "unshared" version
4851     * of fp.
4852     */
4853    auto fp = cast(_iobuf*) fps;
4854
4855    ReadlnAppender app;
4856    app.initialize(buf);
4857
4858    int c;
4859    while ((c = FGETC(fp)) != -1)
4860    {
4861        app.putchar(cast(char) c);
4862        if (c == terminator)
4863        {
4864            buf = app.data;
4865            return buf.length;
4866        }
4867
4868    }
4869
4870    if (ferror(fps))
4871        StdioException();
4872    buf = app.data;
4873    return buf.length;
4874}
4875
4876version (HAS_GETDELIM)
4877private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation)
4878{
4879    import core.stdc.stdlib : free;
4880    import core.stdc.wchar_ : fwide;
4881
4882    if (orientation == File.Orientation.wide)
4883    {
4884        /* Stream is in wide characters.
4885         * Read them and convert to chars.
4886         */
4887        FLOCK(fps);
4888        scope(exit) FUNLOCK(fps);
4889        auto fp = cast(_iobuf*) fps;
4890        version (Windows)
4891        {
4892            buf.length = 0;
4893            for (int c = void; (c = FGETWC(fp)) != -1; )
4894            {
4895                if ((c & ~0x7F) == 0)
4896                {   buf ~= c;
4897                    if (c == terminator)
4898                        break;
4899                }
4900                else
4901                {
4902                    if (c >= 0xD800 && c <= 0xDBFF)
4903                    {
4904                        int c2 = void;
4905                        if ((c2 = FGETWC(fp)) != -1 ||
4906                                c2 < 0xDC00 && c2 > 0xDFFF)
4907                        {
4908                            StdioException("unpaired UTF-16 surrogate");
4909                        }
4910                        c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
4911                    }
4912                    import std.utf : encode;
4913                    encode(buf, c);
4914                }
4915            }
4916            if (ferror(fp))
4917                StdioException();
4918            return buf.length;
4919        }
4920        else version (Posix)
4921        {
4922            buf.length = 0;
4923            for (int c; (c = FGETWC(fp)) != -1; )
4924            {
4925                import std.utf : encode;
4926
4927                if ((c & ~0x7F) == 0)
4928                    buf ~= cast(char) c;
4929                else
4930                    encode(buf, cast(dchar) c);
4931                if (c == terminator)
4932                    break;
4933            }
4934            if (ferror(fps))
4935                StdioException();
4936            return buf.length;
4937        }
4938        else
4939        {
4940            static assert(0);
4941        }
4942    }
4943
4944    static char *lineptr = null;
4945    static size_t n = 0;
4946    scope(exit)
4947    {
4948        if (n > 128 * 1024)
4949        {
4950            // Bound memory used by readln
4951            free(lineptr);
4952            lineptr = null;
4953            n = 0;
4954        }
4955    }
4956
4957    auto s = getdelim(&lineptr, &n, terminator, fps);
4958    if (s < 0)
4959    {
4960        if (ferror(fps))
4961            StdioException();
4962        buf.length = 0;                // end of file
4963        return 0;
4964    }
4965
4966    if (s <= buf.length)
4967    {
4968        buf = buf[0 .. s];
4969        buf[] = lineptr[0 .. s];
4970    }
4971    else
4972    {
4973        buf = lineptr[0 .. s].dup;
4974    }
4975    return s;
4976}
4977
4978version (NO_GETDELIM)
4979private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation)
4980{
4981    import core.stdc.wchar_ : fwide;
4982
4983    FLOCK(fps);
4984    scope(exit) FUNLOCK(fps);
4985    auto fp = cast(_iobuf*) fps;
4986    if (orientation == File.Orientation.wide)
4987    {
4988        /* Stream is in wide characters.
4989         * Read them and convert to chars.
4990         */
4991        version (Windows)
4992        {
4993            buf.length = 0;
4994            for (int c; (c = FGETWC(fp)) != -1; )
4995            {
4996                if ((c & ~0x7F) == 0)
4997                {   buf ~= c;
4998                    if (c == terminator)
4999                        break;
5000                }
5001                else
5002                {
5003                    if (c >= 0xD800 && c <= 0xDBFF)
5004                    {
5005                        int c2 = void;
5006                        if ((c2 = FGETWC(fp)) != -1 ||
5007                                c2 < 0xDC00 && c2 > 0xDFFF)
5008                        {
5009                            StdioException("unpaired UTF-16 surrogate");
5010                        }
5011                        c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
5012                    }
5013                    import std.utf : encode;
5014                    encode(buf, c);
5015                }
5016            }
5017            if (ferror(fp))
5018                StdioException();
5019            return buf.length;
5020        }
5021        else version (Posix)
5022        {
5023            import std.utf : encode;
5024            buf.length = 0;
5025            for (int c; (c = FGETWC(fp)) != -1; )
5026            {
5027                if ((c & ~0x7F) == 0)
5028                    buf ~= cast(char) c;
5029                else
5030                    encode(buf, cast(dchar) c);
5031                if (c == terminator)
5032                    break;
5033            }
5034            if (ferror(fps))
5035                StdioException();
5036            return buf.length;
5037        }
5038        else
5039        {
5040            static assert(0);
5041        }
5042    }
5043
5044    // Narrow stream
5045    // First, fill the existing buffer
5046    for (size_t bufPos = 0; bufPos < buf.length; )
5047    {
5048        immutable c = FGETC(fp);
5049        if (c == -1)
5050        {
5051            buf.length = bufPos;
5052            goto endGame;
5053        }
5054        buf[bufPos++] = cast(char) c;
5055        if (c == terminator)
5056        {
5057            // No need to test for errors in file
5058            buf.length = bufPos;
5059            return bufPos;
5060        }
5061    }
5062    // Then, append to it
5063    for (int c; (c = FGETC(fp)) != -1; )
5064    {
5065        buf ~= cast(char) c;
5066        if (c == terminator)
5067        {
5068            // No need to test for errors in file
5069            return buf.length;
5070        }
5071    }
5072
5073  endGame:
5074    if (ferror(fps))
5075        StdioException();
5076    return buf.length;
5077}
5078
5079@system unittest
5080{
5081    static import std.file;
5082    auto deleteme = testFilename();
5083    scope(exit) std.file.remove(deleteme);
5084
5085    std.file.write(deleteme, "abcd\n0123456789abcde\n1234\n");
5086    File f = File(deleteme, "rb");
5087
5088    char[] ln = new char[2];
5089    char* lnptr = ln.ptr;
5090    f.readln(ln);
5091
5092    assert(ln == "abcd\n");
5093    char[] t = ln[0 .. 2];
5094    t ~= 't';
5095    assert(t == "abt");
5096    assert(ln == "abcd\n");  // bug 13856: ln stomped to "abtd"
5097
5098    // it can also stomp the array length
5099    ln = new char[4];
5100    lnptr = ln.ptr;
5101    f.readln(ln);
5102    assert(ln == "0123456789abcde\n");
5103
5104    char[100] buf;
5105    ln = buf[];
5106    f.readln(ln);
5107    assert(ln == "1234\n");
5108    assert(ln.ptr == buf.ptr); // avoid allocation, buffer is good enough
5109}
5110
5111/** Experimental network access via the File interface
5112
5113        Opens a TCP connection to the given host and port, then returns
5114        a File struct with read and write access through the same interface
5115        as any other file (meaning writef and the byLine ranges work!).
5116
5117        Authors:
5118                Adam D. Ruppe
5119
5120        Bugs:
5121                Only works on Linux
5122*/
5123version (linux)
5124{
5125    File openNetwork(string host, ushort port)
5126    {
5127        import core.stdc.string : memcpy;
5128        import core.sys.posix.arpa.inet : htons;
5129        import core.sys.posix.netdb : gethostbyname;
5130        import core.sys.posix.netinet.in_ : sockaddr_in;
5131        static import core.sys.posix.unistd;
5132        static import sock = core.sys.posix.sys.socket;
5133        import std.conv : to;
5134        import std.exception : enforce;
5135        import std.internal.cstring : tempCString;
5136
5137        auto h = enforce( gethostbyname(host.tempCString()),
5138            new StdioException("gethostbyname"));
5139
5140        int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0);
5141        enforce(s != -1, new StdioException("socket"));
5142
5143        scope(failure)
5144        {
5145            // want to make sure it doesn't dangle if something throws. Upon
5146            // normal exit, the File struct's reference counting takes care of
5147            // closing, so we don't need to worry about success
5148            core.sys.posix.unistd.close(s);
5149        }
5150
5151        sockaddr_in addr;
5152
5153        addr.sin_family = sock.AF_INET;
5154        addr.sin_port = htons(port);
5155        memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length);
5156
5157        enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
5158            new StdioException("Connect failed"));
5159
5160        File f;
5161        f.fdopen(s, "w+", host ~ ":" ~ to!string(port));
5162        return f;
5163    }
5164}
5165
5166version (unittest) string testFilename(string file = __FILE__, size_t line = __LINE__) @safe
5167{
5168    import std.conv : text;
5169    import std.file : deleteme;
5170    import std.path : baseName;
5171
5172    // filename intentionally contains non-ASCII (Russian) characters for test Issue 7648
5173    return text(deleteme, "-����������.", baseName(file), ".", line);
5174}
5175