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