The OpenD Programming Language

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