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 : borrow, RefCountedAutoInitialize, SafeRefCounted;
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 = SafeRefCounted!(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         /* Verifiably `@safe` when built with -preview=DIP1000. */
2194         @property bool empty() @trusted
2195         {
2196             // Using `ref` is actually necessary here.
2197             return impl.borrow!((ref i) => i.empty);
2198         }
2199 
2200         /* Verifiably `@safe` when built with -preview=DIP1000. */
2201         @property Char[] front() @trusted
2202         {
2203             // Using `ref` is likely optional here.
2204             return impl.borrow!((ref i) => i.front);
2205         }
2206 
2207         /* Verifiably `@safe` when built with -preview=DIP1000. */
2208         void popFront() @trusted
2209         {
2210             return impl.borrow!((ref i) => i.popFront());
2211         }
2212 
2213     private:
2214         struct Impl
2215         {
2216         private:
2217             File file;
2218             Char[] line;
2219             Char[] buffer;
2220             Terminator terminator;
2221             KeepTerminator keepTerminator;
2222             bool haveLine;
2223 
2224         @safe:
2225         public:
2226             this(File f, KeepTerminator kt, Terminator terminator)
2227             {
2228                 file = f;
2229                 this.terminator = terminator;
2230                 keepTerminator = kt;
2231             }
2232 
2233             // Range primitive implementations.
2234             @property bool empty()
2235             {
2236                 needLine();
2237                 return line is null;
2238             }
2239 
2240             @property Char[] front()
2241             {
2242                 needLine();
2243                 return line;
2244             }
2245 
2246             void popFront()
2247             {
2248                 needLine();
2249                 haveLine = false;
2250             }
2251 
2252         private:
2253             void needLine()
2254             {
2255                 if (haveLine)
2256                     return;
2257                 import std.algorithm.searching : endsWith;
2258                 assert(file.isOpen);
2259                 line = buffer;
2260                 file.readln(line, terminator);
2261                 if (line.length > buffer.length)
2262                 {
2263                     buffer = line;
2264                 }
2265                 if (line.empty)
2266                 {
2267                     file.detach();
2268                     line = null;
2269                 }
2270                 else if (keepTerminator == No.keepTerminator
2271                         && endsWith(line, terminator))
2272                 {
2273                     static if (isScalarType!Terminator)
2274                         enum tlen = 1;
2275                     else static if (isArray!Terminator)
2276                     {
2277                         static assert(
2278                             is(immutable ElementEncodingType!Terminator == immutable Char));
2279                         const tlen = terminator.length;
2280                     }
2281                     else
2282                         static assert(false);
2283                     line = line[0 .. line.length - tlen];
2284                 }
2285                 haveLine = true;
2286             }
2287         }
2288     }
2289 
2290 /**
2291 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2292 set up to read from the file handle one line at a time.
2293 
2294 The element type for the range will be `Char[]`. Range primitives
2295 may throw `StdioException` on I/O error.
2296 
2297 Note:
2298 Each `front` will not persist after $(D
2299 popFront) is called, so the caller must copy its contents (e.g. by
2300 calling `to!string`) when retention is needed. If the caller needs
2301 to retain a copy of every line, use the $(LREF byLineCopy) function
2302 instead.
2303 
2304 Params:
2305 Char = Character type for each line, defaulting to `char`.
2306 keepTerminator = Use `Yes.keepTerminator` to include the
2307 terminator at the end of each line.
2308 terminator = Line separator (`'\n'` by default). Use
2309 $(REF newline, std,ascii) for portability (unless the file was opened in
2310 text mode).
2311 
2312 Example:
2313 ----
2314 import std.algorithm, std.stdio, std.string;
2315 // Count words in a file using ranges.
2316 void main()
2317 {
2318     auto file = File("file.txt"); // Open for reading
2319     const wordCount = file.byLine()            // Read lines
2320                           .map!split           // Split into words
2321                           .map!(a => a.length) // Count words per line
2322                           .sum();              // Total word count
2323     writeln(wordCount);
2324 }
2325 ----
2326 
2327 Example:
2328 ----
2329 import std.range, std.stdio;
2330 // Read lines using foreach.
2331 void main()
2332 {
2333     auto file = File("file.txt"); // Open for reading
2334     auto range = file.byLine();
2335     // Print first three lines
2336     foreach (line; range.take(3))
2337         writeln(line);
2338     // Print remaining lines beginning with '#'
2339     foreach (line; range)
2340     {
2341         if (!line.empty && line[0] == '#')
2342             writeln(line);
2343     }
2344 }
2345 ----
2346 Notice that neither example accesses the line data returned by
2347 `front` after the corresponding `popFront` call is made (because
2348 the contents may well have changed).
2349 ----
2350 
2351 Windows specific Example:
2352 ----
2353 import std.stdio;
2354 
2355 version (Windows)
2356 void main()
2357 {
2358 
2359 	foreach (line; File("file.txt").byLine(No.keepTerminator, "\r\n"))
2360 	{
2361 		writeln("|"~line~"|");
2362 		if (line == "HelloWorld")
2363 		    writeln("^This Line is here.");
2364 	}
2365 
2366 }
2367 */
2368     auto byLine(Terminator = char, Char = char)
2369             (KeepTerminator keepTerminator = No.keepTerminator,
2370             Terminator terminator = '\n')
2371     if (isScalarType!Terminator)
2372     {
2373         return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator);
2374     }
2375 
2376 /// ditto
2377     auto byLine(Terminator, Char = char)
2378             (KeepTerminator keepTerminator, Terminator terminator)
2379     if (is(immutable ElementEncodingType!Terminator == immutable Char))
2380     {
2381         return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator);
2382     }
2383 
2384     @safe unittest
2385     {
2386         static import std.file;
2387         auto deleteme = testFilename();
2388         std.file.write(deleteme, "hi");
2389         scope(success) std.file.remove(deleteme);
2390 
2391         import std.meta : AliasSeq;
2392         static foreach (T; AliasSeq!(char, wchar, dchar))
2393         {{
2394             auto blc = File(deleteme).byLine!(T, T);
2395             assert(blc.front == "hi");
2396             // check front is cached
2397             assert(blc.front is blc.front);
2398         }}
2399     }
2400 
2401     // https://issues.dlang.org/show_bug.cgi?id=19980
2402     @safe unittest
2403     {
2404         static import std.file;
2405         auto deleteme = testFilename();
2406         std.file.write(deleteme, "Line 1\nLine 2\nLine 3\n");
2407         scope(success) std.file.remove(deleteme);
2408 
2409         auto f = File(deleteme);
2410         f.byLine();
2411         f.byLine();
2412         assert(f.byLine().front == "Line 1");
2413     }
2414 
2415     private struct ByLineCopy(Char, Terminator)
2416     {
2417     private:
2418         import std.typecons : borrow, RefCountedAutoInitialize, SafeRefCounted;
2419 
2420         /* Ref-counting stops the source range's ByLineCopyImpl
2421          * from getting out of sync after the range is copied, e.g.
2422          * when accessing range.front, then using std.range.take,
2423          * then accessing range.front again. */
2424         alias Impl = SafeRefCounted!(ByLineCopyImpl!(Char, Terminator),
2425             RefCountedAutoInitialize.no);
2426         Impl impl;
2427 
2428     public:
2429         this(File f, KeepTerminator kt, Terminator terminator)
2430         {
2431             impl = Impl(f, kt, terminator);
2432         }
2433 
2434         /* Verifiably `@safe` when built with -preview=DIP1000. */
2435         @property bool empty() @trusted
2436         {
2437             // Using `ref` is actually necessary here.
2438             return impl.borrow!((ref i) => i.empty);
2439         }
2440 
2441         /* Verifiably `@safe` when built with -preview=DIP1000. */
2442         @property Char[] front() @trusted
2443         {
2444             // Using `ref` is likely optional here.
2445             return impl.borrow!((ref i) => i.front);
2446         }
2447 
2448         /* Verifiably `@safe` when built with -preview=DIP1000. */
2449         void popFront() @trusted
2450         {
2451             impl.borrow!((ref i) => i.popFront());
2452         }
2453     }
2454 
2455     private struct ByLineCopyImpl(Char, Terminator)
2456     {
2457         ByLineImpl!(Unqual!Char, Terminator).Impl impl;
2458         bool gotFront;
2459         Char[] line;
2460 
2461     public:
2462         this(File f, KeepTerminator kt, Terminator terminator)
2463         {
2464             impl = ByLineImpl!(Unqual!Char, Terminator).Impl(f, kt, terminator);
2465         }
2466 
2467         @property bool empty()
2468         {
2469             return impl.empty;
2470         }
2471 
2472         @property front()
2473         {
2474             if (!gotFront)
2475             {
2476                 line = impl.front.dup;
2477                 gotFront = true;
2478             }
2479             return line;
2480         }
2481 
2482         void popFront()
2483         {
2484             impl.popFront();
2485             gotFront = false;
2486         }
2487     }
2488 
2489 /**
2490 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2491 set up to read from the file handle one line
2492 at a time. Each line will be newly allocated. `front` will cache
2493 its value to allow repeated calls without unnecessary allocations.
2494 
2495 Note: Due to caching byLineCopy can be more memory-efficient than
2496 `File.byLine.map!idup`.
2497 
2498 The element type for the range will be `Char[]`. Range
2499 primitives may throw `StdioException` on I/O error.
2500 
2501 Params:
2502 Char = Character type for each line, defaulting to $(D immutable char).
2503 keepTerminator = Use `Yes.keepTerminator` to include the
2504 terminator at the end of each line.
2505 terminator = Line separator (`'\n'` by default). Use
2506 $(REF newline, std,ascii) for portability (unless the file was opened in
2507 text mode).
2508 
2509 Example:
2510 ----
2511 import std.algorithm, std.array, std.stdio;
2512 // Print sorted lines of a file.
2513 void main()
2514 {
2515     auto sortedLines = File("file.txt")   // Open for reading
2516                        .byLineCopy()      // Read persistent lines
2517                        .array()           // into an array
2518                        .sort();           // then sort them
2519     foreach (line; sortedLines)
2520         writeln(line);
2521 }
2522 ----
2523 See_Also:
2524 $(REF readText, std,file)
2525 */
2526     auto byLineCopy(Terminator = char, Char = immutable char)
2527             (KeepTerminator keepTerminator = No.keepTerminator,
2528             Terminator terminator = '\n')
2529     if (isScalarType!Terminator)
2530     {
2531         return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2532     }
2533 
2534 /// ditto
2535     auto byLineCopy(Terminator, Char = immutable char)
2536             (KeepTerminator keepTerminator, Terminator terminator)
2537     if (is(immutable ElementEncodingType!Terminator == immutable Char))
2538     {
2539         return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2540     }
2541 
2542     @safe unittest
2543     {
2544         static assert(is(typeof(File("").byLine.front) == char[]));
2545         static assert(is(typeof(File("").byLineCopy.front) == string));
2546         static assert(
2547             is(typeof(File("").byLineCopy!(char, char).front) == char[]));
2548     }
2549 
2550     @safe unittest
2551     {
2552         import std.algorithm.comparison : equal;
2553         static import std.file;
2554 
2555         auto deleteme = testFilename();
2556         std.file.write(deleteme, "");
2557         scope(success) std.file.remove(deleteme);
2558 
2559         // Test empty file
2560         auto f = File(deleteme);
2561         foreach (line; f.byLine())
2562         {
2563             assert(false);
2564         }
2565         f.detach();
2566         assert(!f.isOpen);
2567 
2568         void test(Terminator)(string txt, in string[] witness,
2569                 KeepTerminator kt, Terminator term, bool popFirstLine = false)
2570         {
2571             import std.algorithm.sorting : sort;
2572             import std.array : array;
2573             import std.conv : text;
2574             import std.range.primitives : walkLength;
2575 
2576             uint i;
2577             std.file.write(deleteme, txt);
2578             auto f = File(deleteme);
2579             scope(exit)
2580             {
2581                 f.close();
2582                 assert(!f.isOpen);
2583             }
2584             auto lines = f.byLine(kt, term);
2585             if (popFirstLine)
2586             {
2587                 lines.popFront();
2588                 i = 1;
2589             }
2590             assert(lines.empty || lines.front is lines.front);
2591             foreach (line; lines)
2592             {
2593                 assert(line == witness[i++]);
2594             }
2595             assert(i == witness.length, text(i, " != ", witness.length));
2596 
2597             // https://issues.dlang.org/show_bug.cgi?id=11830
2598             auto walkedLength = File(deleteme).byLine(kt, term).walkLength;
2599             assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length));
2600 
2601             // test persistent lines
2602             assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort());
2603         }
2604 
2605         KeepTerminator kt = No.keepTerminator;
2606         test("", null, kt, '\n');
2607         test("\n", [ "" ], kt, '\n');
2608         test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n');
2609         test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n', true);
2610         test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ], kt, '\n');
2611         test("foo", [ "foo" ], kt, '\n', true);
2612         test("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"],
2613             kt, "\r\n");
2614         test("sue\r", ["sue"], kt, '\r');
2615 
2616         kt = Yes.keepTerminator;
2617         test("", null, kt, '\n');
2618         test("\n", [ "\n" ], kt, '\n');
2619         test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt, '\n');
2620         test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n');
2621         test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n', true);
2622         test("foo", [ "foo" ], kt, '\n');
2623         test("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"],
2624             kt, "\r\n");
2625         test("sue\r", ["sue\r"], kt, '\r');
2626     }
2627 
2628     @safe unittest
2629     {
2630         import std.algorithm.comparison : equal;
2631         import std.range : drop, take;
2632 
2633         version (Win64)
2634         {
2635             static import std.file;
2636 
2637             /* the C function tmpfile doesn't seem to work, even when called from C */
2638             auto deleteme = testFilename();
2639             auto file = File(deleteme, "w+");
2640             scope(success) std.file.remove(deleteme);
2641         }
2642         else version (CRuntime_Bionic)
2643         {
2644             static import std.file;
2645 
2646             /* the C function tmpfile doesn't work when called from a shared
2647                library apk:
2648                https://code.google.com/p/android/issues/detail?id=66815 */
2649             auto deleteme = testFilename();
2650             auto file = File(deleteme, "w+");
2651             scope(success) std.file.remove(deleteme);
2652         }
2653         else
2654             auto file = File.tmpfile();
2655         file.write("1\n2\n3\n");
2656 
2657         // https://issues.dlang.org/show_bug.cgi?id=9599
2658         file.rewind();
2659         File.ByLineImpl!(char, char) fbl = file.byLine();
2660         auto fbl2 = fbl;
2661         assert(fbl.front == "1");
2662         assert(fbl.front is fbl2.front);
2663         assert(fbl.take(1).equal(["1"]));
2664         assert(fbl.equal(["2", "3"]));
2665         assert(fbl.empty);
2666         assert(file.isOpen); // we still have a valid reference
2667 
2668         file.rewind();
2669         fbl = file.byLine();
2670         assert(!fbl.drop(2).empty);
2671         assert(fbl.equal(["3"]));
2672         assert(fbl.empty);
2673         assert(file.isOpen);
2674 
2675         file.detach();
2676         assert(!file.isOpen);
2677     }
2678 
2679     @safe unittest
2680     {
2681         static import std.file;
2682         auto deleteme = testFilename();
2683         std.file.write(deleteme, "hi");
2684         scope(success) std.file.remove(deleteme);
2685 
2686         auto blc = File(deleteme).byLineCopy;
2687         assert(!blc.empty);
2688         // check front is cached
2689         assert(blc.front is blc.front);
2690     }
2691 
2692     /**
2693     Creates an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2694     set up to parse one line at a time from the file into a tuple.
2695 
2696     Range primitives may throw `StdioException` on I/O error.
2697 
2698     Params:
2699         format = tuple record $(REF_ALTTEXT _format, formattedRead, std, _format)
2700 
2701     Returns:
2702         The input range set up to parse one line at a time into a record tuple.
2703 
2704     See_Also:
2705 
2706         It is similar to $(LREF byLine) and uses
2707         $(REF_ALTTEXT _format, formattedRead, std, _format) under the hood.
2708     */
2709     template byRecord(Fields...)
2710     {
2711         auto byRecord(string format)
2712         {
2713             return ByRecordImpl!(Fields)(this, format);
2714         }
2715     }
2716 
2717     ///
2718     @system unittest
2719     {
2720          static import std.file;
2721          import std.typecons : tuple;
2722 
2723          // prepare test file
2724          auto testFile = std.file.deleteme();
2725          scope(failure) printf("Failed test at line %d\n", __LINE__);
2726          std.file.write(testFile, "1 2\n4 1\n5 100");
2727          scope(exit) std.file.remove(testFile);
2728 
2729          File f = File(testFile);
2730          scope(exit) f.close();
2731 
2732          auto expected = [tuple(1, 2), tuple(4, 1), tuple(5, 100)];
2733          uint i;
2734          foreach (e; f.byRecord!(int, int)("%s %s"))
2735          {
2736              assert(e == expected[i++]);
2737          }
2738     }
2739 
2740     // Note: This was documented until 2013/08
2741     /*
2742      * Range that reads a chunk at a time.
2743      */
2744     private struct ByChunkImpl
2745     {
2746     private:
2747         File    file_;
2748         ubyte[] chunk_;
2749 
2750         void prime()
2751         {
2752             chunk_ = file_.rawRead(chunk_);
2753             if (chunk_.length == 0)
2754                 file_.detach();
2755         }
2756 
2757     public:
2758         this(File file, size_t size)
2759         {
2760             this(file, new ubyte[](size));
2761         }
2762 
2763         this(File file, ubyte[] buffer)
2764         {
2765             import std.exception : enforce;
2766             enforce(buffer.length, "size must be larger than 0");
2767             file_ = file;
2768             chunk_ = buffer;
2769             prime();
2770         }
2771 
2772         // `ByChunk`'s input range primitive operations.
2773         @property nothrow
2774         bool empty() const
2775         {
2776             return !file_.isOpen;
2777         }
2778 
2779         /// Ditto
2780         @property nothrow
2781         ubyte[] front()
2782         {
2783             version (assert)
2784             {
2785                 import core.exception : RangeError;
2786                 if (empty)
2787                     throw new RangeError();
2788             }
2789             return chunk_;
2790         }
2791 
2792         /// Ditto
2793         void popFront()
2794         {
2795             version (assert)
2796             {
2797                 import core.exception : RangeError;
2798                 if (empty)
2799                     throw new RangeError();
2800             }
2801             prime();
2802         }
2803     }
2804 
2805 /**
2806 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2807 set up to read from the file handle a chunk at a time.
2808 
2809 The element type for the range will be `ubyte[]`. Range primitives
2810 may throw `StdioException` on I/O error.
2811 
2812 Example:
2813 ---------
2814 void main()
2815 {
2816     // Read standard input 4KB at a time
2817     foreach (ubyte[] buffer; stdin.byChunk(4096))
2818     {
2819         ... use buffer ...
2820     }
2821 }
2822 ---------
2823 
2824 The parameter may be a number (as shown in the example above) dictating the
2825 size of each chunk. Alternatively, `byChunk` accepts a
2826 user-provided buffer that it uses directly.
2827 
2828 Example:
2829 ---------
2830 void main()
2831 {
2832     // Read standard input 4KB at a time
2833     foreach (ubyte[] buffer; stdin.byChunk(new ubyte[4096]))
2834     {
2835         ... use buffer ...
2836     }
2837 }
2838 ---------
2839 
2840 In either case, the content of the buffer is reused across calls. That means
2841 `front` will not persist after `popFront` is called, so if retention is
2842 needed, the caller must copy its contents (e.g. by calling `buffer.dup`).
2843 
2844 In the  example above, `buffer.length` is 4096 for all iterations, except
2845 for the last one, in which case `buffer.length` may be less than 4096 (but
2846 always greater than zero).
2847 
2848 With the mentioned limitations, `byChunk` works with any algorithm
2849 compatible with input ranges.
2850 
2851 Example:
2852 ---
2853 // Efficient file copy, 1MB at a time.
2854 import std.algorithm, std.stdio;
2855 void main()
2856 {
2857     stdin.byChunk(1024 * 1024).copy(stdout.lockingTextWriter());
2858 }
2859 ---
2860 
2861 $(REF joiner, std,algorithm,iteration) can be used to join chunks together into
2862 a single range lazily.
2863 Example:
2864 ---
2865 import std.algorithm, std.stdio;
2866 void main()
2867 {
2868     //Range of ranges
2869     static assert(is(typeof(stdin.byChunk(4096).front) == ubyte[]));
2870     //Range of elements
2871     static assert(is(typeof(stdin.byChunk(4096).joiner.front) == ubyte));
2872 }
2873 ---
2874 
2875 Returns: A call to `byChunk` returns a range initialized with the `File`
2876 object and the appropriate buffer.
2877 
2878 Throws: If the user-provided size is zero or the user-provided buffer
2879 is empty, throws an `Exception`. In case of an I/O error throws
2880 `StdioException`.
2881  */
2882     auto byChunk(size_t chunkSize)
2883     {
2884         return ByChunkImpl(this, chunkSize);
2885     }
2886 /// Ditto
2887     auto byChunk(ubyte[] buffer)
2888     {
2889         return ByChunkImpl(this, buffer);
2890     }
2891 
2892     @system unittest
2893     {
2894         static import std.file;
2895 
2896         scope(failure) printf("Failed test at line %d\n", __LINE__);
2897 
2898         auto deleteme = testFilename();
2899         std.file.write(deleteme, "asd\ndef\nasdf");
2900 
2901         auto witness = ["asd\n", "def\n", "asdf" ];
2902         auto f = File(deleteme);
2903         scope(exit)
2904         {
2905             f.close();
2906             assert(!f.isOpen);
2907             std.file.remove(deleteme);
2908         }
2909 
2910         uint i;
2911         foreach (chunk; f.byChunk(4))
2912             assert(chunk == cast(ubyte[]) witness[i++]);
2913 
2914         assert(i == witness.length);
2915     }
2916 
2917     @system unittest
2918     {
2919         static import std.file;
2920 
2921         scope(failure) printf("Failed test at line %d\n", __LINE__);
2922 
2923         auto deleteme = testFilename();
2924         std.file.write(deleteme, "asd\ndef\nasdf");
2925 
2926         auto witness = ["asd\n", "def\n", "asdf" ];
2927         auto f = File(deleteme);
2928         scope(exit)
2929         {
2930             f.close();
2931             assert(!f.isOpen);
2932             std.file.remove(deleteme);
2933         }
2934 
2935         uint i;
2936         foreach (chunk; f.byChunk(new ubyte[4]))
2937             assert(chunk == cast(ubyte[]) witness[i++]);
2938 
2939         assert(i == witness.length);
2940     }
2941 
2942     // Note: This was documented until 2013/08
2943 /*
2944 `Range` that locks the file and allows fast writing to it.
2945  */
2946     struct LockingTextWriter
2947     {
2948     private:
2949         import std.range.primitives : ElementType, isInfinite, isInputRange;
2950         // Access the FILE* handle through the 'file_' member
2951         // to keep the object alive through refcounting
2952         File file_;
2953 
2954         // the unshared version of FILE* handle, extracted from the File object
2955         @property _iobuf* handle_() @trusted { return cast(_iobuf*) file_._p.handle; }
2956 
2957         // the file's orientation (byte- or wide-oriented)
2958         int orientation_;
2959 
2960         // Buffers for when we need to transcode.
2961         wchar highSurrogate = '\0'; // '\0' indicates empty
2962         void highSurrogateShouldBeEmpty() @safe
2963         {
2964             import std.utf : UTFException;
2965             if (highSurrogate != '\0')
2966                 throw new UTFException("unpaired surrogate UTF-16 value");
2967         }
2968         char[4] rbuf8;
2969         size_t rbuf8Filled = 0;
2970     public:
2971 
2972         this(ref File f) @trusted
2973         {
2974             import std.exception : enforce;
2975 
2976             enforce(f._p && f._p.handle, "Attempting to write to closed File");
2977             file_ = f;
2978             FILE* fps = f._p.handle;
2979 
2980             version (CRuntime_Microsoft)
2981             {
2982                 // Microsoft doesn't implement fwide. Instead, there's the
2983                 // concept of ANSI/UNICODE mode. fputc doesn't work in UNICODE
2984                 // mode; fputwc has to be used. So that essentially means
2985                 // "wide-oriented" for us.
2986                 immutable int mode = _setmode(f.fileno, _O_TEXT);
2987                     // Set some arbitrary mode to obtain the previous one.
2988                 if (mode != -1) // _setmode() succeeded
2989                 {
2990                     _setmode(f.fileno, mode); // Restore previous mode.
2991                     if (mode & (_O_WTEXT | _O_U16TEXT | _O_U8TEXT))
2992                     {
2993                         orientation_ = 1; // wide
2994                     }
2995                 }
2996             }
2997             else
2998             {
2999                 import core.stdc.wchar_ : fwide;
3000                 orientation_ = fwide(fps, 0);
3001             }
3002 
3003             _FLOCK(fps);
3004         }
3005 
3006         ~this() @trusted
3007         {
3008             if (auto p = file_._p)
3009             {
3010                 if (p.handle) _FUNLOCK(p.handle);
3011             }
3012             file_ = File.init;
3013                 /* Destroy file_ before possibly throwing. Else it wouldn't be
3014                 destroyed, and its reference count would be wrong. */
3015             highSurrogateShouldBeEmpty();
3016         }
3017 
3018         this(this) @trusted
3019         {
3020             if (auto p = file_._p)
3021             {
3022                 if (p.handle) _FLOCK(p.handle);
3023             }
3024         }
3025 
3026         /// Range primitive implementations.
3027         void put(A)(scope A writeme)
3028         if ((isSomeChar!(ElementType!A) ||
3029             is(ElementType!A : const(ubyte))) &&
3030             isInputRange!A &&
3031             !isInfinite!A)
3032         {
3033             import std.exception : errnoEnforce;
3034 
3035             alias C = ElementEncodingType!A;
3036             static assert(!is(C == void));
3037             static if (isSomeString!A && C.sizeof == 1 || is(A : const(ubyte)[]))
3038             {
3039                 if (orientation_ <= 0)
3040                 {
3041                     //file.write(writeme); causes infinite recursion!!!
3042                     //file.rawWrite(writeme);
3043                     auto result = trustedFwrite(file_._p.handle, writeme);
3044                     if (result != writeme.length) errnoEnforce(0);
3045                     return;
3046                 }
3047             }
3048 
3049             // put each element in turn.
3050             foreach (c; writeme)
3051             {
3052                 put(c);
3053             }
3054         }
3055 
3056         /// ditto
3057         void put(C)(scope C c) @safe
3058         if (isSomeChar!C || is(C : const(ubyte)))
3059         {
3060             import std.utf : decodeFront, encode, stride;
3061 
3062             static if (c.sizeof == 1)
3063             {
3064                 highSurrogateShouldBeEmpty();
3065                 if (orientation_ <= 0) trustedFPUTC(c, handle_);
3066                 else if (c <= 0x7F) trustedFPUTWC(c, handle_);
3067                 else if (c >= 0b1100_0000) // start byte of multibyte sequence
3068                 {
3069                     rbuf8[0] = c;
3070                     rbuf8Filled = 1;
3071                 }
3072                 else // continuation byte of multibyte sequence
3073                 {
3074                     rbuf8[rbuf8Filled] = c;
3075                     ++rbuf8Filled;
3076                     if (stride(rbuf8[]) == rbuf8Filled) // sequence is complete
3077                     {
3078                         char[] str = rbuf8[0 .. rbuf8Filled];
3079                         immutable dchar d = decodeFront(str);
3080                         wchar_t[4 / wchar_t.sizeof] wbuf;
3081                         immutable size = encode(wbuf, d);
3082                         foreach (i; 0 .. size)
3083                             trustedFPUTWC(wbuf[i], handle_);
3084                         rbuf8Filled = 0;
3085                     }
3086                 }
3087             }
3088             else static if (c.sizeof == 2)
3089             {
3090                 import std.utf : decode;
3091 
3092                 if (c <= 0x7F)
3093                 {
3094                     highSurrogateShouldBeEmpty();
3095                     if (orientation_ <= 0) trustedFPUTC(c, handle_);
3096                     else trustedFPUTWC(c, handle_);
3097                 }
3098                 else if (0xD800 <= c && c <= 0xDBFF) // high surrogate
3099                 {
3100                     highSurrogateShouldBeEmpty();
3101                     highSurrogate = c;
3102                 }
3103                 else // standalone or low surrogate
3104                 {
3105                     dchar d = c;
3106                     if (highSurrogate != '\0')
3107                     {
3108                         immutable wchar[2] rbuf = [highSurrogate, c];
3109                         size_t index = 0;
3110                         d = decode(rbuf[], index);
3111                         highSurrogate = 0;
3112                     }
3113                     if (orientation_ <= 0)
3114                     {
3115                         char[4] wbuf;
3116                         immutable size = encode(wbuf, d);
3117                         foreach (i; 0 .. size)
3118                             trustedFPUTC(wbuf[i], handle_);
3119                     }
3120                     else
3121                     {
3122                         wchar_t[4 / wchar_t.sizeof] wbuf;
3123                         immutable size = encode(wbuf, d);
3124                         foreach (i; 0 .. size)
3125                             trustedFPUTWC(wbuf[i], handle_);
3126                     }
3127                     rbuf8Filled = 0;
3128                 }
3129             }
3130             else // 32-bit characters
3131             {
3132                 import std.utf : encode;
3133 
3134                 highSurrogateShouldBeEmpty();
3135                 if (orientation_ <= 0)
3136                 {
3137                     if (c <= 0x7F)
3138                     {
3139                         trustedFPUTC(c, handle_);
3140                     }
3141                     else
3142                     {
3143                         char[4] buf = void;
3144                         immutable len = encode(buf, c);
3145                         foreach (i ; 0 .. len)
3146                             trustedFPUTC(buf[i], handle_);
3147                     }
3148                 }
3149                 else
3150                 {
3151                     version (Windows)
3152                     {
3153                         import std.utf : isValidDchar;
3154 
3155                         assert(isValidDchar(c));
3156                         if (c <= 0xFFFF)
3157                         {
3158                             trustedFPUTWC(cast(wchar_t) c, handle_);
3159                         }
3160                         else
3161                         {
3162                             trustedFPUTWC(cast(wchar_t)
3163                                     ((((c - 0x10000) >> 10) & 0x3FF)
3164                                             + 0xD800), handle_);
3165                             trustedFPUTWC(cast(wchar_t)
3166                                     (((c - 0x10000) & 0x3FF) + 0xDC00),
3167                                     handle_);
3168                         }
3169                     }
3170                     else version (Posix)
3171                     {
3172                         trustedFPUTWC(cast(wchar_t) c, handle_);
3173                     }
3174                     else
3175                     {
3176                         static assert(0);
3177                     }
3178                 }
3179             }
3180         }
3181     }
3182 
3183     /**
3184      * Output range which locks the file when created, and unlocks the file when it goes
3185      * out of scope.
3186      *
3187      * Returns: An $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
3188      * which accepts string types, `ubyte[]`, individual character types, and
3189      * individual `ubyte`s.
3190      *
3191      * Note: Writing either arrays of `char`s or `ubyte`s is faster than
3192      * writing each character individually from a range. For large amounts of data,
3193      * writing the contents in chunks using an intermediary array can result
3194      * in a speed increase.
3195      *
3196      * Throws: $(REF UTFException, std, utf) if the data given is a `char` range
3197      * and it contains malformed UTF data.
3198      *
3199      * See_Also: $(LREF byChunk) for an example.
3200      */
3201     auto lockingTextWriter() @safe
3202     {
3203         return LockingTextWriter(this);
3204     }
3205 
3206     // An output range which optionally locks the file and puts it into
3207     // binary mode (similar to rawWrite). Because it needs to restore
3208     // the file mode on destruction, it is RefCounted on Windows.
3209     struct BinaryWriterImpl(bool locking)
3210     {
3211         import std.traits : hasIndirections;
3212     private:
3213         // Access the FILE* handle through the 'file_' member
3214         // to keep the object alive through refcounting
3215         File file_;
3216         string name;
3217 
3218         version (Windows)
3219         {
3220             fileno_t fd;
3221             int oldMode;
3222         }
3223 
3224     public:
3225         // Don't use this, but `File.lockingBinaryWriter()` instead.
3226         // Must be public for RefCounted and emplace() in druntime.
3227         this(scope ref File f)
3228         {
3229             import std.exception : enforce;
3230             file_ = f;
3231             enforce(f._p && f._p.handle);
3232             name = f._name;
3233             FILE* fps = f._p.handle;
3234             static if (locking)
3235                 _FLOCK(fps);
3236 
3237             version (Windows)
3238             {
3239                 .fflush(fps); // before changing translation mode
3240                 fd = .fileno(fps);
3241                 oldMode = ._setmode(fd, _O_BINARY);
3242             }
3243         }
3244 
3245         ~this()
3246         {
3247             if (!file_._p || !file_._p.handle)
3248                 return;
3249 
3250             FILE* fps = file_._p.handle;
3251 
3252             version (Windows)
3253             {
3254                 .fflush(fps); // before restoring translation mode
3255                 ._setmode(fd, oldMode);
3256             }
3257 
3258             _FUNLOCK(fps);
3259         }
3260 
3261         void rawWrite(T)(in T[] buffer)
3262         {
3263             import std.conv : text;
3264             import std.exception : errnoEnforce;
3265 
3266             auto result = trustedFwrite(file_._p.handle, buffer);
3267             if (result == result.max) result = 0;
3268             errnoEnforce(result == buffer.length,
3269                     text("Wrote ", result, " instead of ", buffer.length,
3270                             " objects of type ", T.stringof, " to file `",
3271                             name, "'"));
3272         }
3273 
3274         version (Windows)
3275         {
3276             @disable this(this);
3277         }
3278         else
3279         {
3280             this(this)
3281             {
3282                 if (auto p = file_._p)
3283                 {
3284                     if (p.handle) _FLOCK(p.handle);
3285                 }
3286             }
3287         }
3288 
3289         void put(T)(auto ref scope const T value)
3290         if (!hasIndirections!T &&
3291             !isInputRange!T)
3292         {
3293             rawWrite((&value)[0 .. 1]);
3294         }
3295 
3296         void put(T)(scope const(T)[] array)
3297         if (!hasIndirections!T &&
3298             !isInputRange!T)
3299         {
3300             rawWrite(array);
3301         }
3302     }
3303 
3304 /** Returns an output range that locks the file and allows fast writing to it.
3305 
3306 Example:
3307 Produce a grayscale image of the $(LINK2 https://en.wikipedia.org/wiki/Mandelbrot_set, Mandelbrot set)
3308 in binary $(LINK2 https://en.wikipedia.org/wiki/Netpbm_format, Netpbm format) to standard output.
3309 ---
3310 import std.algorithm, std.complex, std.range, std.stdio;
3311 
3312 void main()
3313 {
3314     enum size = 500;
3315     writef("P5\n%d %d %d\n", size, size, ubyte.max);
3316 
3317     iota(-1, 3, 2.0/size).map!(y =>
3318         iota(-1.5, 0.5, 2.0/size).map!(x =>
3319             cast(ubyte)(1+
3320                 recurrence!((a, n) => x + y * complex(0, 1) + a[n-1]^^2)(complex(0))
3321                 .take(ubyte.max)
3322                 .countUntil!(z => z.re^^2 + z.im^^2 > 4))
3323         )
3324     )
3325     .copy(stdout.lockingBinaryWriter);
3326 }
3327 ---
3328 */
3329     auto lockingBinaryWriter()
3330     {
3331         alias LockingBinaryWriterImpl = BinaryWriterImpl!true;
3332 
3333         version (Windows)
3334         {
3335             import std.typecons : RefCounted;
3336             alias LockingBinaryWriter = RefCounted!LockingBinaryWriterImpl;
3337         }
3338         else
3339             alias LockingBinaryWriter = LockingBinaryWriterImpl;
3340 
3341         return LockingBinaryWriter(this);
3342     }
3343 
3344     @system unittest
3345     {
3346         import std.algorithm.mutation : reverse;
3347         import std.exception : collectException;
3348         static import std.file;
3349         import std.range : only, retro;
3350         import std.string : format;
3351 
3352         auto deleteme = testFilename();
3353         scope(exit) collectException(std.file.remove(deleteme));
3354 
3355         {
3356             auto writer = File(deleteme, "wb").lockingBinaryWriter();
3357             auto input = File(deleteme, "rb");
3358 
3359             ubyte[1] byteIn = [42];
3360             writer.rawWrite(byteIn);
3361             destroy(writer);
3362 
3363             ubyte[1] byteOut = input.rawRead(new ubyte[1]);
3364             assert(byteIn[0] == byteOut[0]);
3365         }
3366 
3367         auto output = File(deleteme, "wb");
3368         auto writer = output.lockingBinaryWriter();
3369         auto input = File(deleteme, "rb");
3370 
3371         T[] readExact(T)(T[] buf)
3372         {
3373             auto result = input.rawRead(buf);
3374             assert(result.length == buf.length,
3375                 "Read %d out of %d bytes"
3376                 .format(result.length, buf.length));
3377             return result;
3378         }
3379 
3380         // test raw values
3381         ubyte byteIn = 42;
3382         byteIn.only.copy(writer); output.flush();
3383         ubyte byteOut = readExact(new ubyte[1])[0];
3384         assert(byteIn == byteOut);
3385 
3386         // test arrays
3387         ubyte[] bytesIn = [1, 2, 3, 4, 5];
3388         bytesIn.copy(writer); output.flush();
3389         ubyte[] bytesOut = readExact(new ubyte[bytesIn.length]);
3390         scope(failure) .writeln(bytesOut);
3391         assert(bytesIn == bytesOut);
3392 
3393         // test ranges of values
3394         bytesIn.retro.copy(writer); output.flush();
3395         bytesOut = readExact(bytesOut);
3396         bytesOut.reverse();
3397         assert(bytesIn == bytesOut);
3398 
3399         // test string
3400         "foobar".copy(writer); output.flush();
3401         char[] charsOut = readExact(new char[6]);
3402         assert(charsOut == "foobar");
3403 
3404         // test ranges of arrays
3405         only("foo", "bar").copy(writer); output.flush();
3406         charsOut = readExact(charsOut);
3407         assert(charsOut == "foobar");
3408 
3409         // test that we are writing arrays as is,
3410         // without UTF-8 transcoding
3411         "foo"d.copy(writer); output.flush();
3412         dchar[] dcharsOut = readExact(new dchar[3]);
3413         assert(dcharsOut == "foo");
3414     }
3415 
3416 /** Returns the size of the file in bytes, ulong.max if file is not searchable or throws if the operation fails.
3417 Example:
3418 ---
3419 import std.stdio, std.file;
3420 
3421 void main()
3422 {
3423     string deleteme = "delete.me";
3424     auto file_handle = File(deleteme, "w");
3425     file_handle.write("abc"); //create temporary file
3426     scope(exit) deleteme.remove; //remove temporary file at scope exit
3427 
3428     assert(file_handle.size() == 3); //check if file size is 3 bytes
3429 }
3430 ---
3431 */
3432     @property ulong size() @safe
3433     {
3434         import std.exception : collectException;
3435 
3436         ulong pos = void;
3437         if (collectException(pos = tell)) return ulong.max;
3438         scope(exit) seek(pos);
3439         seek(0, SEEK_END);
3440         return tell;
3441     }
3442 }
3443 
3444 @system unittest
3445 {
3446     @system struct SystemToString
3447     {
3448         string toString()
3449         {
3450             return "system";
3451         }
3452     }
3453 
3454     @trusted struct TrustedToString
3455     {
3456         string toString()
3457         {
3458             return "trusted";
3459         }
3460     }
3461 
3462     @safe struct SafeToString
3463     {
3464         string toString()
3465         {
3466             return "safe";
3467         }
3468     }
3469 
3470     @system void systemTests()
3471     {
3472         //system code can write to files/stdout with anything!
3473         if (false)
3474         {
3475             auto f = File();
3476 
3477             f.write("just a string");
3478             f.write("string with arg: ", 47);
3479             f.write(SystemToString());
3480             f.write(TrustedToString());
3481             f.write(SafeToString());
3482 
3483             write("just a string");
3484             write("string with arg: ", 47);
3485             write(SystemToString());
3486             write(TrustedToString());
3487             write(SafeToString());
3488 
3489             f.writeln("just a string");
3490             f.writeln("string with arg: ", 47);
3491             f.writeln(SystemToString());
3492             f.writeln(TrustedToString());
3493             f.writeln(SafeToString());
3494 
3495             writeln("just a string");
3496             writeln("string with arg: ", 47);
3497             writeln(SystemToString());
3498             writeln(TrustedToString());
3499             writeln(SafeToString());
3500 
3501             f.writef("string with arg: %s", 47);
3502             f.writef("%s", SystemToString());
3503             f.writef("%s", TrustedToString());
3504             f.writef("%s", SafeToString());
3505 
3506             writef("string with arg: %s", 47);
3507             writef("%s", SystemToString());
3508             writef("%s", TrustedToString());
3509             writef("%s", SafeToString());
3510 
3511             f.writefln("string with arg: %s", 47);
3512             f.writefln("%s", SystemToString());
3513             f.writefln("%s", TrustedToString());
3514             f.writefln("%s", SafeToString());
3515 
3516             writefln("string with arg: %s", 47);
3517             writefln("%s", SystemToString());
3518             writefln("%s", TrustedToString());
3519             writefln("%s", SafeToString());
3520         }
3521     }
3522 
3523     @safe void safeTests()
3524     {
3525         auto f = File();
3526 
3527         //safe code can write to files only with @safe and @trusted code...
3528         if (false)
3529         {
3530             f.write("just a string");
3531             f.write("string with arg: ", 47);
3532             f.write(TrustedToString());
3533             f.write(SafeToString());
3534 
3535             write("just a string");
3536             write("string with arg: ", 47);
3537             write(TrustedToString());
3538             write(SafeToString());
3539 
3540             f.writeln("just a string");
3541             f.writeln("string with arg: ", 47);
3542             f.writeln(TrustedToString());
3543             f.writeln(SafeToString());
3544 
3545             writeln("just a string");
3546             writeln("string with arg: ", 47);
3547             writeln(TrustedToString());
3548             writeln(SafeToString());
3549 
3550             f.writef("string with arg: %s", 47);
3551             f.writef("%s", TrustedToString());
3552             f.writef("%s", SafeToString());
3553 
3554             writef("string with arg: %s", 47);
3555             writef("%s", TrustedToString());
3556             writef("%s", SafeToString());
3557 
3558             f.writefln("string with arg: %s", 47);
3559             f.writefln("%s", TrustedToString());
3560             f.writefln("%s", SafeToString());
3561 
3562             writefln("string with arg: %s", 47);
3563             writefln("%s", TrustedToString());
3564             writefln("%s", SafeToString());
3565         }
3566 
3567         static assert(!__traits(compiles, f.write(SystemToString().toString())));
3568         static assert(!__traits(compiles, f.writeln(SystemToString())));
3569         static assert(!__traits(compiles, f.writef("%s", SystemToString())));
3570         static assert(!__traits(compiles, f.writefln("%s", SystemToString())));
3571 
3572         static assert(!__traits(compiles, write(SystemToString().toString())));
3573         static assert(!__traits(compiles, writeln(SystemToString())));
3574         static assert(!__traits(compiles, writef("%s", SystemToString())));
3575         static assert(!__traits(compiles, writefln("%s", SystemToString())));
3576     }
3577 
3578     systemTests();
3579     safeTests();
3580 }
3581 
3582 @safe unittest
3583 {
3584     import std.exception : collectException;
3585     static import std.file;
3586 
3587     auto deleteme = testFilename();
3588     scope(exit) collectException(std.file.remove(deleteme));
3589     std.file.write(deleteme, "1 2 3");
3590     auto f = File(deleteme);
3591     assert(f.size == 5);
3592     assert(f.tell == 0);
3593 }
3594 
3595 @safe unittest
3596 {
3597     static import std.file;
3598     import std.range : chain, only, repeat;
3599     import std.range.primitives : isOutputRange;
3600 
3601     auto deleteme = testFilename();
3602     scope(exit) std.file.remove(deleteme);
3603 
3604     {
3605         auto writer = File(deleteme, "w").lockingTextWriter();
3606         static assert(isOutputRange!(typeof(writer), dchar));
3607         writer.put("日本語");
3608         writer.put("日本語"w);
3609         writer.put("日本語"d);
3610         writer.put('日');
3611         writer.put(chain(only('本'), only('語')));
3612         // https://issues.dlang.org/show_bug.cgi?id=11945
3613         writer.put(repeat('#', 12));
3614         // https://issues.dlang.org/show_bug.cgi?id=17229
3615         writer.put(cast(immutable(ubyte)[])"日本語");
3616     }
3617     assert(File(deleteme).readln() == "日本語日本語日本語日本語############日本語");
3618 }
3619 
3620 @safe unittest // wchar -> char
3621 {
3622     static import std.file;
3623     import std.exception : assertThrown;
3624     import std.utf : UTFException;
3625 
3626     auto deleteme = testFilename();
3627     scope(exit) std.file.remove(deleteme);
3628 
3629     {
3630         auto writer = File(deleteme, "w").lockingTextWriter();
3631         writer.put("\U0001F608"w);
3632     }
3633     assert(std.file.readText!string(deleteme) == "\U0001F608");
3634 
3635     // Test invalid input: unpaired high surrogate
3636     {
3637         immutable wchar surr = "\U0001F608"w[0];
3638         auto f = File(deleteme, "w");
3639         assertThrown!UTFException(() {
3640             auto writer = f.lockingTextWriter();
3641             writer.put('x');
3642             writer.put(surr);
3643             assertThrown!UTFException(writer.put(char('y')));
3644             assertThrown!UTFException(writer.put(wchar('y')));
3645             assertThrown!UTFException(writer.put(dchar('y')));
3646             assertThrown!UTFException(writer.put(surr));
3647             // First `surr` is still unpaired at this point. `writer` gets
3648             // destroyed now, and the destructor throws a UTFException for
3649             // the unpaired surrogate.
3650         } ());
3651     }
3652     assert(std.file.readText!string(deleteme) == "x");
3653 
3654     // Test invalid input: unpaired low surrogate
3655     {
3656         immutable wchar surr = "\U0001F608"w[1];
3657         auto writer = File(deleteme, "w").lockingTextWriter();
3658         assertThrown!UTFException(writer.put(surr));
3659         writer.put('y');
3660         assertThrown!UTFException(writer.put(surr));
3661     }
3662     assert(std.file.readText!string(deleteme) == "y");
3663 }
3664 
3665 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=18801
3666 {
3667     static import std.file;
3668     import std.string : stripLeft;
3669 
3670     auto deleteme = testFilename();
3671     scope(exit) std.file.remove(deleteme);
3672 
3673     {
3674         auto writer = File(deleteme, "w,ccs=UTF-8").lockingTextWriter();
3675         writer.put("foo");
3676     }
3677     assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foo");
3678 
3679     {
3680         auto writer = File(deleteme, "a,ccs=UTF-8").lockingTextWriter();
3681         writer.put("bar");
3682     }
3683     assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foobar");
3684 }
3685 @safe unittest // char/wchar -> wchar_t
3686 {
3687     import core.stdc.locale : LC_CTYPE, setlocale;
3688     import core.stdc.wchar_ : fwide;
3689     import core.stdc.string : strlen;
3690     import std.algorithm.searching : any, endsWith;
3691     import std.conv : text;
3692     import std.meta : AliasSeq;
3693     import std.string : fromStringz, stripLeft;
3694     static import std.file;
3695     auto deleteme = testFilename();
3696     scope(exit) std.file.remove(deleteme);
3697     const char* oldCt = () @trusted {
3698         const(char)* p = setlocale(LC_CTYPE, null);
3699         // Subsequent calls to `setlocale` might invalidate this return value,
3700         // so duplicate it.
3701         // See: https://github.com/dlang/phobos/pull/7660
3702         return p ? p[0 .. strlen(p) + 1].idup.ptr : null;
3703     }();
3704     const utf8 = ["en_US.UTF-8", "C.UTF-8", ".65001"].any!((loc) @trusted {
3705         return setlocale(LC_CTYPE, loc.ptr).fromStringz.endsWith(loc);
3706     });
3707     scope(exit) () @trusted { setlocale(LC_CTYPE, oldCt); } ();
3708     alias strs = AliasSeq!("xä\U0001F607", "yö\U0001F608"w);
3709     {
3710         auto f = File(deleteme, "w");
3711         version (CRuntime_Microsoft)
3712         {
3713             () @trusted { _setmode(fileno(f.getFP()), _O_U8TEXT); } ();
3714         }
3715         else
3716         {
3717             assert(fwide(f.getFP(), 1) == 1);
3718         }
3719         auto writer = f.lockingTextWriter();
3720         assert(writer.orientation_ == 1);
3721         static foreach (s; strs) writer.put(s);
3722     }
3723     assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") ==
3724         text(strs));
3725 }
3726 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=18789
3727 {
3728     static import std.file;
3729     auto deleteme = testFilename();
3730     scope(exit) std.file.remove(deleteme);
3731     // converting to char
3732     {
3733         auto f = File(deleteme, "w");
3734         f.writeln("\U0001F608"w); // UTFException
3735     }
3736     // converting to wchar_t
3737     {
3738         auto f = File(deleteme, "w,ccs=UTF-16LE");
3739         // from char
3740         f.writeln("ö"); // writes garbage
3741         f.writeln("\U0001F608"); // ditto
3742         // from wchar
3743         f.writeln("\U0001F608"w); // leads to ErrnoException
3744     }
3745 }
3746 
3747 @safe unittest
3748 {
3749     import std.exception : collectException;
3750     auto e = collectException({ File f; f.writeln("Hello!"); }());
3751     assert(e && e.msg == "Attempting to write to closed File");
3752 }
3753 
3754 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=21592
3755 {
3756     import std.exception : collectException;
3757     import std.utf : UTFException;
3758     static import std.file;
3759     auto deleteme = testFilename();
3760     scope(exit) std.file.remove(deleteme);
3761     auto f = File(deleteme, "w");
3762     auto e = collectException!UTFException(f.writeln(wchar(0xD801)));
3763     assert(e.next is null);
3764 }
3765 
3766 version (StdStressTest)
3767 {
3768     // https://issues.dlang.org/show_bug.cgi?id=15768
3769     @system unittest
3770     {
3771         import std.parallelism : parallel;
3772         import std.range : iota;
3773 
3774         auto deleteme = testFilename();
3775         stderr = File(deleteme, "w");
3776 
3777         foreach (t; 1_000_000.iota.parallel)
3778         {
3779             stderr.write("aaa");
3780         }
3781     }
3782 }
3783 
3784 /// Used to specify the lock type for `File.lock` and `File.tryLock`.
3785 enum LockType
3786 {
3787     /**
3788      * Specifies a _read (shared) lock. A _read lock denies all processes
3789      * write access to the specified region of the file, including the
3790      * process that first locks the region. All processes can _read the
3791      * locked region. Multiple simultaneous _read locks are allowed, as
3792      * long as there are no exclusive locks.
3793      */
3794     read,
3795 
3796     /**
3797      * Specifies a read/write (exclusive) lock. A read/write lock denies all
3798      * other processes both read and write access to the locked file region.
3799      * If a segment has an exclusive lock, it may not have any shared locks
3800      * or other exclusive locks.
3801      */
3802     readWrite
3803 }
3804 
3805 struct LockingTextReader
3806 {
3807     private File _f;
3808     private char _front;
3809     private bool _hasChar;
3810 
3811     this(File f)
3812     {
3813         import std.exception : enforce;
3814         enforce(f.isOpen, "LockingTextReader: File must be open");
3815         _f = f;
3816         _FLOCK(_f._p.handle);
3817     }
3818 
3819     this(this)
3820     {
3821         _FLOCK(_f._p.handle);
3822     }
3823 
3824     ~this()
3825     {
3826         if (_hasChar)
3827             ungetc(_front, cast(FILE*)_f._p.handle);
3828 
3829         // File locking has its own reference count
3830         if (_f.isOpen) _FUNLOCK(_f._p.handle);
3831     }
3832 
3833     void opAssign(LockingTextReader r)
3834     {
3835         import std.algorithm.mutation : swap;
3836         swap(this, r);
3837     }
3838 
3839     @property bool empty()
3840     {
3841         if (!_hasChar)
3842         {
3843             if (!_f.isOpen || _f.eof)
3844                 return true;
3845             immutable int c = _FGETC(cast(_iobuf*) _f._p.handle);
3846             if (c == EOF)
3847             {
3848                 .destroy(_f);
3849                 return true;
3850             }
3851             _front = cast(char) c;
3852             _hasChar = true;
3853         }
3854         return false;
3855     }
3856 
3857     @property char front()
3858     {
3859         if (!_hasChar)
3860         {
3861             version (assert)
3862             {
3863                 import core.exception : RangeError;
3864                 if (empty)
3865                     throw new RangeError();
3866             }
3867             else
3868             {
3869                 empty;
3870             }
3871         }
3872         return _front;
3873     }
3874 
3875     void popFront()
3876     {
3877         if (!_hasChar)
3878             empty;
3879         _hasChar = false;
3880     }
3881 }
3882 
3883 @system unittest
3884 {
3885     // @system due to readf
3886     static import std.file;
3887     import std.range.primitives : isInputRange;
3888 
3889     static assert(isInputRange!LockingTextReader);
3890     auto deleteme = testFilename();
3891     std.file.write(deleteme, "1 2 3");
3892     scope(exit) std.file.remove(deleteme);
3893     int x;
3894     auto f = File(deleteme);
3895     f.readf("%s ", &x);
3896     assert(x == 1);
3897     f.readf("%d ", &x);
3898     assert(x == 2);
3899     f.readf("%d ", &x);
3900     assert(x == 3);
3901 }
3902 
3903 // https://issues.dlang.org/show_bug.cgi?id=13686
3904 @system unittest
3905 {
3906     import std.algorithm.comparison : equal;
3907     static import std.file;
3908     import std.utf : byDchar;
3909 
3910     auto deleteme = testFilename();
3911     std.file.write(deleteme, "Тест");
3912     scope(exit) std.file.remove(deleteme);
3913 
3914     string s;
3915     File(deleteme).readf("%s", &s);
3916     assert(s == "Тест");
3917 
3918     auto ltr = LockingTextReader(File(deleteme)).byDchar;
3919     assert(equal(ltr, "Тест".byDchar));
3920 }
3921 
3922 // https://issues.dlang.org/show_bug.cgi?id=12320
3923 @system unittest
3924 {
3925     static import std.file;
3926     auto deleteme = testFilename();
3927     std.file.write(deleteme, "ab");
3928     scope(exit) std.file.remove(deleteme);
3929     auto ltr = LockingTextReader(File(deleteme));
3930     assert(ltr.front == 'a');
3931     ltr.popFront();
3932     assert(ltr.front == 'b');
3933     ltr.popFront();
3934     assert(ltr.empty);
3935 }
3936 
3937 // https://issues.dlang.org/show_bug.cgi?id=14861
3938 @system unittest
3939 {
3940     // @system due to readf
3941     static import std.file;
3942     auto deleteme = testFilename();
3943     File fw = File(deleteme, "w");
3944     for (int i; i != 5000; i++)
3945         fw.writeln(i, ";", "Иванов;Пётр;Петрович");
3946     fw.close();
3947     scope(exit) std.file.remove(deleteme);
3948     // Test read
3949     File fr = File(deleteme, "r");
3950     scope (exit) fr.close();
3951     int nom; string fam, nam, ot;
3952     // Error format read
3953     while (!fr.eof)
3954         fr.readf("%s;%s;%s;%s\n", &nom, &fam, &nam, &ot);
3955 }
3956 
3957 /**
3958  * Indicates whether `T` is a file handle, i.e. the type
3959  * is implicitly convertable to $(LREF File) or a pointer to a
3960  * $(REF FILE, core,stdc,stdio).
3961  *
3962  * Returns:
3963  *      `true` if `T` is a file handle, `false` otherwise.
3964  */
3965 template isFileHandle(T)
3966 {
3967     enum isFileHandle = is(T : FILE*) ||
3968         is(T : File);
3969 }
3970 
3971 ///
3972 @safe unittest
3973 {
3974     static assert(isFileHandle!(FILE*));
3975     static assert(isFileHandle!(File));
3976 }
3977 
3978 /**
3979  * Property used by writeln/etc. so it can infer @safe since stdout is __gshared
3980  */
3981 private @property File trustedStdout() @trusted
3982 {
3983     return stdout;
3984 }
3985 
3986 /***********************************
3987 Writes its arguments in text format to standard output (without a trailing newline).
3988 
3989 Params:
3990     args = the items to write to `stdout`
3991 
3992 Throws: In case of an I/O error, throws an `StdioException`.
3993 
3994 Example:
3995     Reads `stdin` and writes it to `stdout` with an argument
3996     counter.
3997 ---
3998 import std.stdio;
3999 
4000 void main()
4001 {
4002     string line;
4003 
4004     for (size_t count = 0; (line = readln) !is null; count++)
4005     {
4006          write("Input ", count, ": ", line, "\n");
4007     }
4008 }
4009 ---
4010  */
4011 void write(T...)(T args)
4012 if (!is(T[0] : File))
4013 {
4014     trustedStdout.write(args);
4015 }
4016 
4017 @system unittest
4018 {
4019     static import std.file;
4020 
4021     scope(failure) printf("Failed test at line %d\n", __LINE__);
4022     void[] buf;
4023     if (false) write(buf);
4024     // test write
4025     auto deleteme = testFilename();
4026     auto f = File(deleteme, "w");
4027     f.write("Hello, ",  "world number ", 42, "!");
4028     f.close();
4029     scope(exit) { std.file.remove(deleteme); }
4030     assert(cast(char[]) std.file.read(deleteme) == "Hello, world number 42!");
4031 }
4032 
4033 /***********************************
4034  * Equivalent to `write(args, '\n')`.  Calling `writeln` without
4035  * arguments is valid and just prints a newline to the standard
4036  * output.
4037  *
4038  * Params:
4039  *      args = the items to write to `stdout`
4040  *
4041  * Throws:
4042  *      In case of an I/O error, throws an $(LREF StdioException).
4043  * Example:
4044  *        Reads `stdin` and writes it to `stdout` with an argument
4045  *        counter.
4046 ---
4047 import std.stdio;
4048 
4049 void main()
4050 {
4051     string line;
4052 
4053     for (size_t count = 0; (line = readln) !is null; count++)
4054     {
4055          writeln("Input ", count, ": ", line);
4056     }
4057 }
4058 ---
4059  */
4060 void writeln(T...)(T args)
4061 {
4062     static if (T.length == 0)
4063     {
4064         import std.exception : enforce;
4065 
4066         enforce(fputc('\n', .trustedStdout._p.handle) != EOF, "fputc failed");
4067     }
4068     else static if (T.length == 1 &&
4069                     is(T[0] : const(char)[]) &&
4070                     (is(T[0] == U[], U) || __traits(isStaticArray, T[0])))
4071     {
4072         // Specialization for strings - a very frequent case
4073         auto w = .trustedStdout.lockingTextWriter();
4074 
4075         static if (__traits(isStaticArray, T[0]))
4076         {
4077             w.put(args[0][]);
4078         }
4079         else
4080         {
4081             w.put(args[0]);
4082         }
4083         w.put('\n');
4084     }
4085     else
4086     {
4087         // Most general instance
4088         trustedStdout.write(args, '\n');
4089     }
4090 }
4091 
4092 @safe unittest
4093 {
4094     // Just make sure the call compiles
4095     if (false) writeln();
4096 
4097     if (false) writeln("wyda");
4098 
4099     // https://issues.dlang.org/show_bug.cgi?id=8040
4100     if (false) writeln(null);
4101     if (false) writeln(">", null, "<");
4102 
4103     // https://issues.dlang.org/show_bug.cgi?id=14041
4104     if (false)
4105     {
4106         char[8] a;
4107         writeln(a);
4108         immutable b = a;
4109         b.writeln;
4110         const c = a[];
4111         c.writeln;
4112     }
4113 }
4114 
4115 @system unittest
4116 {
4117     static import std.file;
4118 
4119     scope(failure) printf("Failed test at line %d\n", __LINE__);
4120 
4121     // test writeln
4122     auto deleteme = testFilename();
4123     auto f = File(deleteme, "w");
4124     scope(exit) { std.file.remove(deleteme); }
4125     f.writeln("Hello, ",  "world number ", 42, "!");
4126     f.close();
4127     version (Windows)
4128         assert(cast(char[]) std.file.read(deleteme) ==
4129                 "Hello, world number 42!\r\n");
4130     else
4131         assert(cast(char[]) std.file.read(deleteme) ==
4132                 "Hello, world number 42!\n");
4133 
4134     // test writeln on stdout
4135     auto saveStdout = stdout;
4136     scope(exit) stdout = saveStdout;
4137     stdout.open(deleteme, "w");
4138     writeln("Hello, ",  "world number ", 42, "!");
4139     stdout.close();
4140     version (Windows)
4141         assert(cast(char[]) std.file.read(deleteme) ==
4142                 "Hello, world number 42!\r\n");
4143     else
4144         assert(cast(char[]) std.file.read(deleteme) ==
4145                 "Hello, world number 42!\n");
4146 
4147     stdout.open(deleteme, "w");
4148     writeln("Hello!"c);
4149     writeln("Hello!"w);    // https://issues.dlang.org/show_bug.cgi?id=8386
4150     writeln("Hello!"d);    // https://issues.dlang.org/show_bug.cgi?id=8386
4151     writeln("embedded\0null"c); // https://issues.dlang.org/show_bug.cgi?id=8730
4152     stdout.close();
4153     version (Windows)
4154         assert(cast(char[]) std.file.read(deleteme) ==
4155             "Hello!\r\nHello!\r\nHello!\r\nembedded\0null\r\n");
4156     else
4157         assert(cast(char[]) std.file.read(deleteme) ==
4158             "Hello!\nHello!\nHello!\nembedded\0null\n");
4159 }
4160 
4161 @system unittest
4162 {
4163     static import std.file;
4164 
4165     auto deleteme = testFilename();
4166     auto f = File(deleteme, "w");
4167     scope(exit) { std.file.remove(deleteme); }
4168 
4169     enum EI : int    { A, B }
4170     enum ED : double { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
4171     enum EC : char   { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
4172     enum ES : string { A = "aaa", B = "bbb" }
4173 
4174     f.writeln(EI.A);  // false, but A on 2.058
4175     f.writeln(EI.B);  // true, but B on 2.058
4176 
4177     f.writeln(ED.A);  // A
4178     f.writeln(ED.B);  // B
4179 
4180     f.writeln(EC.A);  // A
4181     f.writeln(EC.B);  // B
4182 
4183     f.writeln(ES.A);  // A
4184     f.writeln(ES.B);  // B
4185 
4186     f.close();
4187     version (Windows)
4188         assert(cast(char[]) std.file.read(deleteme) ==
4189                 "A\r\nB\r\nA\r\nB\r\nA\r\nB\r\nA\r\nB\r\n");
4190     else
4191         assert(cast(char[]) std.file.read(deleteme) ==
4192                 "A\nB\nA\nB\nA\nB\nA\nB\n");
4193 }
4194 
4195 @system unittest
4196 {
4197     static auto useInit(T)(T ltw)
4198     {
4199         T val;
4200         val = ltw;
4201         val = T.init;
4202         return val;
4203     }
4204     useInit(stdout.lockingTextWriter());
4205 }
4206 
4207 @system unittest
4208 {
4209     // https://issues.dlang.org/show_bug.cgi?id=21920
4210     void function(string) printer = &writeln!string;
4211     if (false) printer("Hello");
4212 }
4213 
4214 
4215 /***********************************
4216 Writes formatted data to standard output (without a trailing newline).
4217 
4218 Params:
4219 fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
4220 When passed as a compile-time argument, the string will be statically checked
4221 against the argument types passed.
4222 args = Items to write.
4223 
4224 Note: In older versions of Phobos, it used to be possible to write:
4225 
4226 ------
4227 writef(stderr, "%s", "message");
4228 ------
4229 
4230 to print a message to `stderr`. This syntax is no longer supported, and has
4231 been superceded by:
4232 
4233 ------
4234 stderr.writef("%s", "message");
4235 ------
4236 
4237 */
4238 void writef(alias fmt, A...)(A args)
4239 if (isSomeString!(typeof(fmt)))
4240 {
4241     import std.format : checkFormatException;
4242 
4243     alias e = checkFormatException!(fmt, A);
4244     static assert(!e, e);
4245     return .writef(fmt, args);
4246 }
4247 
4248 /// ditto
4249 void writef(Char, A...)(in Char[] fmt, A args)
4250 {
4251     trustedStdout.writef(fmt, args);
4252 }
4253 
4254 @system unittest
4255 {
4256     static import std.file;
4257 
4258     scope(failure) printf("Failed test at line %d\n", __LINE__);
4259 
4260     // test writef
4261     auto deleteme = testFilename();
4262     auto f = File(deleteme, "w");
4263     scope(exit) { std.file.remove(deleteme); }
4264     f.writef!"Hello, %s world number %s!"("nice", 42);
4265     f.close();
4266     assert(cast(char[]) std.file.read(deleteme) ==  "Hello, nice world number 42!");
4267     // test write on stdout
4268     auto saveStdout = stdout;
4269     scope(exit) stdout = saveStdout;
4270     stdout.open(deleteme, "w");
4271     writef!"Hello, %s world number %s!"("nice", 42);
4272     stdout.close();
4273     assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
4274 }
4275 
4276 /***********************************
4277  * Equivalent to $(D writef(fmt, args, '\n')).
4278  */
4279 void writefln(alias fmt, A...)(A args)
4280 if (isSomeString!(typeof(fmt)))
4281 {
4282     import std.format : checkFormatException;
4283 
4284     alias e = checkFormatException!(fmt, A);
4285     static assert(!e, e);
4286     return .writefln(fmt, args);
4287 }
4288 
4289 /// ditto
4290 void writefln(Char, A...)(in Char[] fmt, A args)
4291 {
4292     trustedStdout.writefln(fmt, args);
4293 }
4294 
4295 @system unittest
4296 {
4297     static import std.file;
4298 
4299     scope(failure) printf("Failed test at line %d\n", __LINE__);
4300 
4301     // test File.writefln
4302     auto deleteme = testFilename();
4303     auto f = File(deleteme, "w");
4304     scope(exit) { std.file.remove(deleteme); }
4305     f.writefln!"Hello, %s world number %s!"("nice", 42);
4306     f.close();
4307     version (Windows)
4308         assert(cast(char[]) std.file.read(deleteme) ==
4309                 "Hello, nice world number 42!\r\n");
4310     else
4311         assert(cast(char[]) std.file.read(deleteme) ==
4312                 "Hello, nice world number 42!\n",
4313                 cast(char[]) std.file.read(deleteme));
4314 
4315     // test writefln
4316     auto saveStdout = stdout;
4317     scope(exit) stdout = saveStdout;
4318     stdout.open(deleteme, "w");
4319     writefln!"Hello, %s world number %s!"("nice", 42);
4320     stdout.close();
4321     version (Windows)
4322         assert(cast(char[]) std.file.read(deleteme) ==
4323                 "Hello, nice world number 42!\r\n");
4324     else
4325         assert(cast(char[]) std.file.read(deleteme) ==
4326                 "Hello, nice world number 42!\n");
4327 }
4328 
4329 /**
4330  * Reads formatted data from `stdin` using $(REF formattedRead, std,_format).
4331  * Params:
4332  * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
4333  * When passed as a compile-time argument, the string will be statically checked
4334  * against the argument types passed.
4335  * args = Items to be read.
4336  * Returns:
4337  *      Same as `formattedRead`: The number of variables filled. If the input range `r` ends early,
4338  *      this number will be less than the number of variables provided.
4339  * Example:
4340 ----
4341 // test.d
4342 void main()
4343 {
4344     import std.stdio;
4345     foreach (_; 0 .. 3)
4346     {
4347         int a;
4348         readf!" %d"(a);
4349         writeln(++a);
4350     }
4351 }
4352 ----
4353 $(CONSOLE
4354 % echo "1 2 3" | rdmd test.d
4355 2
4356 3
4357 4
4358 )
4359  */
4360 uint readf(alias format, A...)(auto ref A args)
4361 if (isSomeString!(typeof(format)))
4362 {
4363     import std.format : checkFormatException;
4364 
4365     alias e = checkFormatException!(format, A);
4366     static assert(!e, e);
4367     return .readf(format, args);
4368 }
4369 
4370 /// ditto
4371 uint readf(A...)(scope const(char)[] format, auto ref A args)
4372 {
4373     return stdin.readf(format, args);
4374 }
4375 
4376 @system unittest
4377 {
4378     float f;
4379     if (false) readf("%s", &f);
4380 
4381     char a;
4382     wchar b;
4383     dchar c;
4384     if (false) readf("%s %s %s", a, b, c);
4385     // backwards compatibility with pointers
4386     if (false) readf("%s %s %s", a, &b, c);
4387     if (false) readf("%s %s %s", &a, &b, &c);
4388 }
4389 
4390 /**********************************
4391  * Read line from `stdin`.
4392  *
4393  * This version manages its own read buffer, which means one memory allocation per call. If you are not
4394  * retaining a reference to the read data, consider the `readln(buf)` version, which may offer
4395  * better performance as it can reuse its read buffer.
4396  *
4397  * Returns:
4398  *        The line that was read, including the line terminator character.
4399  * Params:
4400  *        S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`.
4401  *        terminator = Line terminator (by default, `'\n'`).
4402  * Note:
4403  *        String terminators are not supported due to ambiguity with readln(buf) below.
4404  * Throws:
4405  *        `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
4406  * Example:
4407  *        Reads `stdin` and writes it to `stdout`.
4408 ---
4409 import std.stdio;
4410 
4411 void main()
4412 {
4413     string line;
4414     while ((line = readln()) !is null)
4415         write(line);
4416 }
4417 ---
4418 */
4419 S readln(S = string)(dchar terminator = '\n')
4420 if (isSomeString!S)
4421 {
4422     return stdin.readln!S(terminator);
4423 }
4424 
4425 /**********************************
4426  * Read line from `stdin` and write it to buf[], including terminating character.
4427  *
4428  * This can be faster than $(D line = readln()) because you can reuse
4429  * the buffer for each call. Note that reusing the buffer means that you
4430  * must copy the previous contents if you wish to retain them.
4431  *
4432  * Returns:
4433  *        `size_t` 0 for end of file, otherwise number of characters read
4434  * Params:
4435  *        buf = Buffer used to store the resulting line data. buf is resized as necessary.
4436  *        terminator = Line terminator (by default, `'\n'`). Use $(REF newline, std,ascii)
4437  *        for portability (unless the file was opened in text mode).
4438  * Throws:
4439  *        `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
4440  * Example:
4441  *        Reads `stdin` and writes it to `stdout`.
4442 ---
4443 import std.stdio;
4444 
4445 void main()
4446 {
4447     char[] buf;
4448     while (readln(buf))
4449         write(buf);
4450 }
4451 ---
4452 */
4453 size_t readln(C)(ref C[] buf, dchar terminator = '\n')
4454 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
4455 {
4456     return stdin.readln(buf, terminator);
4457 }
4458 
4459 /** ditto */
4460 size_t readln(C, R)(ref C[] buf, R terminator)
4461 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
4462     isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
4463 {
4464     return stdin.readln(buf, terminator);
4465 }
4466 
4467 @safe unittest
4468 {
4469     import std.meta : AliasSeq;
4470 
4471     //we can't actually test readln, so at the very least,
4472     //we test compilability
4473     void foo()
4474     {
4475         readln();
4476         readln('\t');
4477         static foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
4478         {
4479             readln!String();
4480             readln!String('\t');
4481         }
4482         static foreach (String; AliasSeq!(char[], wchar[], dchar[]))
4483         {{
4484             String buf;
4485             readln(buf);
4486             readln(buf, '\t');
4487             readln(buf, "<br />");
4488         }}
4489     }
4490 }
4491 
4492 /*
4493  * Convenience function that forwards to `core.sys.posix.stdio.fopen`
4494  * (to `_wfopen` on Windows)
4495  * with appropriately-constructed C-style strings.
4496  */
4497 private FILE* _fopen(R1, R2)(R1 name, R2 mode = "r")
4498 if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) &&
4499     (isSomeFiniteCharInputRange!R2 || isSomeString!R2))
4500 {
4501     import std.internal.cstring : tempCString;
4502 
4503     auto namez = name.tempCString!FSChar();
4504     auto modez = mode.tempCString!FSChar();
4505 
4506     static _fopenImpl(scope const(FSChar)* namez, scope const(FSChar)* modez) @trusted nothrow @nogc
4507     {
4508         version (Windows)
4509         {
4510             return _wfopen(namez, modez);
4511         }
4512         else version (Posix)
4513         {
4514             /*
4515              * The new opengroup large file support API is transparently
4516              * included in the normal C bindings. https://www.opengroup.org/platform/lfs.html#1.0
4517              * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and
4518              * the normal functions work fine. If not, then large file support
4519              * probably isn't available. Do not use the old transitional API
4520              * (the native extern(C) fopen64, https://unix.org/version2/whatsnew/lfs20mar.html#3.0)
4521              */
4522             import core.sys.posix.stdio : fopen;
4523             return fopen(namez, modez);
4524         }
4525         else
4526         {
4527             return fopen(namez, modez);
4528         }
4529     }
4530     return _fopenImpl(namez, modez);
4531 }
4532 
4533 version (Posix)
4534 {
4535     /***********************************
4536      * Convenience function that forwards to `core.sys.posix.stdio.popen`
4537      * with appropriately-constructed C-style strings.
4538      */
4539     FILE* _popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc
4540     if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) &&
4541         (isSomeFiniteCharInputRange!R2 || isSomeString!R2))
4542     {
4543         import std.internal.cstring : tempCString;
4544 
4545         auto namez = name.tempCString!FSChar();
4546         auto modez = mode.tempCString!FSChar();
4547 
4548         static popenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
4549         {
4550             import core.sys.posix.stdio : popen;
4551             return popen(namez, modez);
4552         }
4553         return popenImpl(namez, modez);
4554     }
4555 }
4556 
4557 /*
4558  * Convenience function that forwards to `core.stdc.stdio.fwrite`
4559  */
4560 private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted
4561 {
4562     return fwrite(obj.ptr, T.sizeof, obj.length, f);
4563 }
4564 
4565 /*
4566  * Convenience function that forwards to `core.stdc.stdio.fread`
4567  */
4568 private auto trustedFread(T)(FILE* f, T[] obj) @trusted
4569 if (!imported!"std.traits".hasIndirections!T)
4570 {
4571     return fread(obj.ptr, T.sizeof, obj.length, f);
4572 }
4573 
4574 private auto trustedFread(T)(FILE* f, T[] obj) @system
4575 if (imported!"std.traits".hasIndirections!T)
4576 {
4577     return fread(obj.ptr, T.sizeof, obj.length, f);
4578 }
4579 
4580 /**
4581  * Iterates through the lines of a file by using `foreach`.
4582  *
4583  * Example:
4584  *
4585 ---------
4586 void main()
4587 {
4588   foreach (string line; lines(stdin))
4589   {
4590     ... use line ...
4591   }
4592 }
4593 ---------
4594 The line terminator (`'\n'` by default) is part of the string read (it
4595 could be missing in the last line of the file). Several types are
4596 supported for `line`, and the behavior of `lines`
4597 changes accordingly:
4598 
4599 $(OL $(LI If `line` has type `string`, $(D
4600 wstring), or `dstring`, a new string of the respective type
4601 is allocated every read.) $(LI If `line` has type $(D
4602 char[]), `wchar[]`, `dchar[]`, the line's content
4603 will be reused (overwritten) across reads.) $(LI If `line`
4604 has type `immutable(ubyte)[]`, the behavior is similar to
4605 case (1), except that no UTF checking is attempted upon input.) $(LI
4606 If `line` has type `ubyte[]`, the behavior is
4607 similar to case (2), except that no UTF checking is attempted upon
4608 input.))
4609 
4610 In all cases, a two-symbols versions is also accepted, in which case
4611 the first symbol (of integral type, e.g. `ulong` or $(D
4612 uint)) tracks the zero-based number of the current line.
4613 
4614 Example:
4615 ----
4616   foreach (ulong i, string line; lines(stdin))
4617   {
4618     ... use line ...
4619   }
4620 ----
4621 
4622  In case of an I/O error, an `StdioException` is thrown.
4623 
4624 See_Also:
4625 $(LREF byLine)
4626  */
4627 
4628 struct lines
4629 {
4630     private File f;
4631     private dchar terminator = '\n';
4632 
4633     /**
4634     Constructor.
4635     Params:
4636     f = File to read lines from.
4637     terminator = Line separator (`'\n'` by default).
4638     */
4639     this(File f, dchar terminator = '\n') @safe
4640     {
4641         this.f = f;
4642         this.terminator = terminator;
4643     }
4644 
4645     int opApply(D)(scope D dg)
4646     {
4647         import std.traits : Parameters;
4648         alias Parms = Parameters!(dg);
4649         static if (isSomeString!(Parms[$ - 1]))
4650         {
4651             int result = 0;
4652             static if (is(Parms[$ - 1] : const(char)[]))
4653                 alias C = char;
4654             else static if (is(Parms[$ - 1] : const(wchar)[]))
4655                 alias C = wchar;
4656             else static if (is(Parms[$ - 1] : const(dchar)[]))
4657                 alias C = dchar;
4658             C[] line;
4659             static if (Parms.length == 2)
4660                 Parms[0] i = 0;
4661             for (;;)
4662             {
4663                 import std.conv : to;
4664 
4665                 if (!f.readln(line, terminator)) break;
4666                 auto copy = to!(Parms[$ - 1])(line);
4667                 static if (Parms.length == 2)
4668                 {
4669                     result = dg(i, copy);
4670                     ++i;
4671                 }
4672                 else
4673                 {
4674                     result = dg(copy);
4675                 }
4676                 if (result != 0) break;
4677             }
4678             return result;
4679         }
4680         else
4681         {
4682             // raw read
4683             return opApplyRaw(dg);
4684         }
4685     }
4686     // no UTF checking
4687     int opApplyRaw(D)(scope D dg)
4688     {
4689         import std.conv : to;
4690         import std.exception : assumeUnique;
4691         import std.traits : Parameters;
4692 
4693         alias Parms = Parameters!(dg);
4694         enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]);
4695         int result = 1;
4696         int c = void;
4697         _FLOCK(f._p.handle);
4698         scope(exit) _FUNLOCK(f._p.handle);
4699         ubyte[] buffer;
4700         static if (Parms.length == 2)
4701             Parms[0] line = 0;
4702         while ((c = _FGETC(cast(_iobuf*) f._p.handle)) != -1)
4703         {
4704             buffer ~= to!(ubyte)(c);
4705             if (c == terminator)
4706             {
4707                 static if (duplicate)
4708                     auto arg = assumeUnique(buffer);
4709                 else
4710                     alias arg = buffer;
4711                 // unlock the file while calling the delegate
4712                 _FUNLOCK(f._p.handle);
4713                 scope(exit) _FLOCK(f._p.handle);
4714                 static if (Parms.length == 1)
4715                 {
4716                     result = dg(arg);
4717                 }
4718                 else
4719                 {
4720                     result = dg(line, arg);
4721                     ++line;
4722                 }
4723                 if (result) break;
4724                 static if (!duplicate)
4725                     buffer.length = 0;
4726             }
4727         }
4728         // can only reach when _FGETC returned -1
4729         if (!f.eof) throw new StdioException("Error in reading file"); // error occured
4730         return result;
4731     }
4732 }
4733 
4734 @safe unittest
4735 {
4736     /*
4737         As pointed out in <https://github.com/dlang/phobos/issues/10605>,
4738         it's a pity that `byLine()` & co. aren't @safe to use yet.
4739 
4740         This is a first attempt at working towards that goal.
4741         For now, this test doesn't do much; as there isn't much to do safely yet.
4742      */
4743     auto deleteMe = testFilename();
4744     scope(exit) { imported!"std.file".remove(deleteMe); }
4745 
4746     // Setup
4747     {
4748         auto f = File(deleteMe, "w");
4749         scope(exit) { f.close(); }
4750         foreach (i; 1 .. 11)
4751             f.writeln(i);
4752     }
4753 
4754     // Actual tests
4755     {
4756         auto f = File(deleteMe, "r");
4757         scope(exit) { f.close(); }
4758 
4759         auto myLines = lines(f);
4760         foreach (string line; myLines)
4761             continue;
4762     }
4763 
4764 
4765     {
4766         auto f = File(deleteMe, "r");
4767         scope(exit) { f.close(); }
4768 
4769         auto myByLineCopy = f.byLineCopy;
4770         foreach (line; myByLineCopy)
4771             continue;
4772     }
4773 
4774     {
4775         auto f = File(deleteMe, "r");
4776         scope(exit) { f.close(); }
4777 
4778         auto myByLine = f.byLine;
4779         foreach (line; myByLine)
4780             continue;
4781     }
4782 }
4783 
4784 @system unittest
4785 {
4786     static import std.file;
4787     import std.meta : AliasSeq;
4788 
4789     scope(failure) printf("Failed test at line %d\n", __LINE__);
4790 
4791     auto deleteme = testFilename();
4792     scope(exit) { std.file.remove(deleteme); }
4793 
4794     alias TestedWith =
4795           AliasSeq!(string, wstring, dstring,
4796                     char[], wchar[], dchar[]);
4797     foreach (T; TestedWith)
4798     {
4799         // test looping with an empty file
4800         std.file.write(deleteme, "");
4801         auto f = File(deleteme, "r");
4802         foreach (T line; lines(f))
4803         {
4804             assert(false);
4805         }
4806         f.close();
4807 
4808         // test looping with a file with three lines
4809         std.file.write(deleteme, "Line one\nline two\nline three\n");
4810         f.open(deleteme, "r");
4811         uint i = 0;
4812         foreach (T line; lines(f))
4813         {
4814             if (i == 0) assert(line == "Line one\n");
4815             else if (i == 1) assert(line == "line two\n");
4816             else if (i == 2) assert(line == "line three\n");
4817             else assert(false);
4818             ++i;
4819         }
4820         f.close();
4821 
4822         // test looping with a file with three lines, last without a newline
4823         std.file.write(deleteme, "Line one\nline two\nline three");
4824         f.open(deleteme, "r");
4825         i = 0;
4826         foreach (T line; lines(f))
4827         {
4828             if (i == 0) assert(line == "Line one\n");
4829             else if (i == 1) assert(line == "line two\n");
4830             else if (i == 2) assert(line == "line three");
4831             else assert(false);
4832             ++i;
4833         }
4834         f.close();
4835     }
4836 
4837     // test with ubyte[] inputs
4838     alias TestedWith2 = AliasSeq!(immutable(ubyte)[], ubyte[]);
4839     foreach (T; TestedWith2)
4840     {
4841         // test looping with an empty file
4842         std.file.write(deleteme, "");
4843         auto f = File(deleteme, "r");
4844         foreach (T line; lines(f))
4845         {
4846             assert(false);
4847         }
4848         f.close();
4849 
4850         // test looping with a file with three lines
4851         std.file.write(deleteme, "Line one\nline two\nline three\n");
4852         f.open(deleteme, "r");
4853         uint i = 0;
4854         foreach (T line; lines(f))
4855         {
4856             if (i == 0) assert(cast(char[]) line == "Line one\n");
4857             else if (i == 1) assert(cast(char[]) line == "line two\n",
4858                 T.stringof ~ " " ~ cast(char[]) line);
4859             else if (i == 2) assert(cast(char[]) line == "line three\n");
4860             else assert(false);
4861             ++i;
4862         }
4863         f.close();
4864 
4865         // test looping with a file with three lines, last without a newline
4866         std.file.write(deleteme, "Line one\nline two\nline three");
4867         f.open(deleteme, "r");
4868         i = 0;
4869         foreach (T line; lines(f))
4870         {
4871             if (i == 0) assert(cast(char[]) line == "Line one\n");
4872             else if (i == 1) assert(cast(char[]) line == "line two\n");
4873             else if (i == 2) assert(cast(char[]) line == "line three");
4874             else assert(false);
4875             ++i;
4876         }
4877         f.close();
4878 
4879     }
4880 
4881     static foreach (T; AliasSeq!(ubyte[]))
4882     {
4883         // test looping with a file with three lines, last without a newline
4884         // using a counter too this time
4885         std.file.write(deleteme, "Line one\nline two\nline three");
4886         auto f = File(deleteme, "r");
4887         uint i = 0;
4888         foreach (ulong j, T line; lines(f))
4889         {
4890             if (i == 0) assert(cast(char[]) line == "Line one\n");
4891             else if (i == 1) assert(cast(char[]) line == "line two\n");
4892             else if (i == 2) assert(cast(char[]) line == "line three");
4893             else assert(false);
4894             ++i;
4895         }
4896         f.close();
4897     }
4898 }
4899 
4900 /**
4901 Iterates through a file a chunk at a time by using `foreach`.
4902 
4903 Example:
4904 
4905 ---------
4906 void main()
4907 {
4908     foreach (ubyte[] buffer; chunks(stdin, 4096))
4909     {
4910         ... use buffer ...
4911     }
4912 }
4913 ---------
4914 
4915 The content of `buffer` is reused across calls. In the
4916  example above, `buffer.length` is 4096 for all iterations,
4917  except for the last one, in which case `buffer.length` may
4918  be less than 4096 (but always greater than zero).
4919 
4920  In case of an I/O error, an `StdioException` is thrown.
4921 */
4922 auto chunks(File f, size_t size)
4923 {
4924     return ChunksImpl(f, size);
4925 }
4926 private struct ChunksImpl
4927 {
4928     private File f;
4929     private size_t size;
4930     // private string fileName; // Currently, no use
4931 
4932     this(File f, size_t size)
4933     in
4934     {
4935         assert(size, "size must be larger than 0");
4936     }
4937     do
4938     {
4939         this.f = f;
4940         this.size = size;
4941     }
4942 
4943     int opApply(D)(scope D dg)
4944     {
4945         import core.stdc.stdlib : alloca;
4946         import std.exception : enforce;
4947 
4948         enforce(f.isOpen, "Attempting to read from an unopened file");
4949         enum maxStackSize = 1024 * 16;
4950         ubyte[] buffer = void;
4951         if (size < maxStackSize)
4952             buffer = (cast(ubyte*) alloca(size))[0 .. size];
4953         else
4954             buffer = new ubyte[size];
4955         size_t r = void;
4956         int result = 1;
4957         uint tally = 0;
4958         while ((r = trustedFread(f._p.handle, buffer)) > 0)
4959         {
4960             assert(r <= size);
4961             if (r != size)
4962             {
4963                 // error occured
4964                 if (!f.eof) throw new StdioException(null);
4965                 buffer.length = r;
4966             }
4967             static if (is(typeof(dg(tally, buffer))))
4968             {
4969                 if ((result = dg(tally, buffer)) != 0) break;
4970             }
4971             else
4972             {
4973                 if ((result = dg(buffer)) != 0) break;
4974             }
4975             ++tally;
4976         }
4977         return result;
4978     }
4979 }
4980 
4981 @system unittest
4982 {
4983     static import std.file;
4984 
4985     scope(failure) printf("Failed test at line %d\n", __LINE__);
4986 
4987     auto deleteme = testFilename();
4988     scope(exit) { std.file.remove(deleteme); }
4989 
4990     // test looping with an empty file
4991     std.file.write(deleteme, "");
4992     auto f = File(deleteme, "r");
4993     foreach (ubyte[] line; chunks(f, 4))
4994     {
4995         assert(false);
4996     }
4997     f.close();
4998 
4999     // test looping with a file with three lines
5000     std.file.write(deleteme, "Line one\nline two\nline three\n");
5001     f = File(deleteme, "r");
5002     uint i = 0;
5003     foreach (ubyte[] line; chunks(f, 3))
5004     {
5005         if (i == 0) assert(cast(char[]) line == "Lin");
5006         else if (i == 1) assert(cast(char[]) line == "e o");
5007         else if (i == 2) assert(cast(char[]) line == "ne\n");
5008         else break;
5009         ++i;
5010     }
5011     f.close();
5012 }
5013 
5014 // Issue 21730 - null ptr dereferenced in ChunksImpl.opApply (SIGSEGV)
5015 @system unittest
5016 {
5017     import std.exception : assertThrown;
5018     static import std.file;
5019 
5020     auto deleteme = testFilename();
5021     scope(exit) { if (std.file.exists(deleteme)) std.file.remove(deleteme); }
5022 
5023     auto err1 = File(deleteme, "w+x");
5024     err1.close;
5025     std.file.remove(deleteme);
5026     assertThrown(() {foreach (ubyte[] buf; chunks(err1, 4096)) {}}());
5027 }
5028 
5029 /**
5030 Writes an array or range to a file.
5031 Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)).
5032 Similar to $(REF write, std,file), strings are written as-is,
5033 rather than encoded according to the `File`'s $(HTTP
5034 en.cppreference.com/w/c/io#Narrow_and_wide_orientation,
5035 orientation).
5036 */
5037 void toFile(T)(T data, string fileName)
5038 if (is(typeof(copy(data, stdout.lockingBinaryWriter))))
5039 {
5040     copy(data, File(fileName, "wb").lockingBinaryWriter);
5041 }
5042 
5043 @system unittest
5044 {
5045     static import std.file;
5046 
5047     auto deleteme = testFilename();
5048     scope(exit) { std.file.remove(deleteme); }
5049 
5050     "Test".toFile(deleteme);
5051     assert(std.file.readText(deleteme) == "Test");
5052 }
5053 
5054 /*********************
5055  * Thrown if I/O errors happen.
5056  */
5057 class StdioException : Exception
5058 {
5059     static import core.stdc.errno;
5060     /// Operating system error code.
5061     uint errno;
5062 
5063 /**
5064 Initialize with a message and an error code.
5065 */
5066     this(string message, uint e = core.stdc.errno.errno) @trusted
5067     {
5068         import std.exception : errnoString;
5069         errno = e;
5070         auto sysmsg = errnoString(errno);
5071         // If e is 0, we don't use the system error message.  (The message
5072         // is "Success", which is rather pointless for an exception.)
5073         super(e == 0 ? message
5074                      : (message ? message ~ " (" ~ sysmsg ~ ")" : sysmsg));
5075     }
5076 
5077 /** Convenience functions that throw an `StdioException`. */
5078     static void opCall(string msg) @safe
5079     {
5080         throw new StdioException(msg);
5081     }
5082 
5083 /// ditto
5084     static void opCall() @safe
5085     {
5086         throw new StdioException(null, core.stdc.errno.errno);
5087     }
5088 }
5089 
5090 enum StdFileHandle: string
5091 {
5092     stdin  = "core.stdc.stdio.stdin",
5093     stdout = "core.stdc.stdio.stdout",
5094     stderr = "core.stdc.stdio.stderr",
5095 }
5096 
5097 // Undocumented but public because the std* handles are aliasing it.
5098 @property ref File makeGlobal(StdFileHandle _iob)()
5099 {
5100     __gshared File.Impl impl;
5101     __gshared File result;
5102 
5103     // Use an inline spinlock to make sure the initializer is only run once.
5104     // We assume there will be at most uint.max / 2 threads trying to initialize
5105     // `handle` at once and steal the high bit to indicate that the globals have
5106     // been initialized.
5107     static shared uint spinlock;
5108     import core.atomic : atomicLoad, atomicOp, MemoryOrder;
5109     if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2)
5110     {
5111         for (;;)
5112         {
5113             if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max / 2)
5114                 break;
5115             if (atomicOp!"+="(spinlock, 1) == 1)
5116             {
5117                 with (StdFileHandle)
5118                     assert(_iob == stdin || _iob == stdout || _iob == stderr);
5119                 impl.handle = cast() mixin(_iob);
5120                 result._p = &impl;
5121                 atomicOp!"+="(spinlock, uint.max / 2);
5122                 break;
5123             }
5124             atomicOp!"-="(spinlock, 1);
5125         }
5126     }
5127     return result;
5128 }
5129 
5130 /** The standard input stream.
5131 
5132     Returns:
5133         stdin as a $(LREF File).
5134 
5135     Note:
5136         The returned $(LREF File) wraps $(REF stdin,core,stdc,stdio), and
5137         is therefore thread global. Reassigning `stdin` to a different
5138         `File` must be done in a single-threaded or locked context in
5139         order to avoid race conditions.
5140 
5141         All reading from `stdin` automatically locks the file globally,
5142         and will cause all other threads calling `read` to wait until
5143         the lock is released.
5144 */
5145 alias stdin = makeGlobal!(StdFileHandle.stdin);
5146 
5147 ///
5148 @safe unittest
5149 {
5150     // Read stdin, sort lines, write to stdout
5151     import std.algorithm.mutation : copy;
5152     import std.algorithm.sorting : sort;
5153     import std.array : array;
5154     import std.typecons : Yes;
5155 
5156     void main()
5157     {
5158         stdin                       // read from stdin
5159         .byLineCopy(Yes.keepTerminator) // copying each line
5160         .array()                    // convert to array of lines
5161         .sort()                     // sort the lines
5162         .copy(                      // copy output of .sort to an OutputRange
5163             stdout.lockingTextWriter()); // the OutputRange
5164     }
5165 }
5166 
5167 /**
5168     The standard output stream.
5169 
5170     Returns:
5171         stdout as a $(LREF File).
5172 
5173     Note:
5174         The returned $(LREF File) wraps $(REF stdout,core,stdc,stdio), and
5175         is therefore thread global. Reassigning `stdout` to a different
5176         `File` must be done in a single-threaded or locked context in
5177         order to avoid race conditions.
5178 
5179         All writing to `stdout` automatically locks the file globally,
5180         and will cause all other threads calling `write` to wait until
5181         the lock is released.
5182 */
5183 alias stdout = makeGlobal!(StdFileHandle.stdout);
5184 
5185 ///
5186 @safe unittest
5187 {
5188     void main()
5189     {
5190         stdout.writeln("Write a message to stdout.");
5191     }
5192 }
5193 
5194 ///
5195 @safe unittest
5196 {
5197     void main()
5198     {
5199         import std.algorithm.iteration : filter, map, sum;
5200         import std.format : format;
5201         import std.range : iota, tee;
5202 
5203         int len;
5204         const r = 6.iota
5205                   .filter!(a => a % 2) // 1 3 5
5206                   .map!(a => a * 2) // 2 6 10
5207                   .tee!(_ => stdout.writefln("len: %d", len++))
5208                   .sum;
5209 
5210         assert(r == 18);
5211     }
5212 }
5213 
5214 ///
5215 @safe unittest
5216 {
5217     void main()
5218     {
5219         import std.algorithm.mutation : copy;
5220         import std.algorithm.iteration : map;
5221         import std.format : format;
5222         import std.range : iota;
5223 
5224         10.iota
5225         .map!(e => "N: %d".format(e))
5226         .copy(stdout.lockingTextWriter()); // the OutputRange
5227     }
5228 }
5229 
5230 /**
5231     The standard error stream.
5232 
5233     Returns:
5234         stderr as a $(LREF File).
5235 
5236     Note:
5237         The returned $(LREF File) wraps $(REF stderr,core,stdc,stdio), and
5238         is therefore thread global. Reassigning `stderr` to a different
5239         `File` must be done in a single-threaded or locked context in
5240         order to avoid race conditions.
5241 
5242         All writing to `stderr` automatically locks the file globally,
5243         and will cause all other threads calling `write` to wait until
5244         the lock is released.
5245 */
5246 alias stderr = makeGlobal!(StdFileHandle.stderr);
5247 
5248 ///
5249 @safe unittest
5250 {
5251     void main()
5252     {
5253         stderr.writeln("Write a message to stderr.");
5254     }
5255 }
5256 
5257 @system unittest
5258 {
5259     static import std.file;
5260     import std.typecons : tuple;
5261 
5262     scope(failure) printf("Failed test at line %d\n", __LINE__);
5263     auto deleteme = testFilename();
5264 
5265     std.file.write(deleteme, "1 2\n4 1\n5 100");
5266     scope(exit) std.file.remove(deleteme);
5267     {
5268         File f = File(deleteme);
5269         scope(exit) f.close();
5270         auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ];
5271         uint i;
5272         foreach (e; f.byRecord!(int, int)("%s %s"))
5273         {
5274             //writeln(e);
5275             assert(e == t[i++]);
5276         }
5277         assert(i == 3);
5278     }
5279 }
5280 
5281 @safe unittest
5282 {
5283     // Retain backwards compatibility
5284     // https://issues.dlang.org/show_bug.cgi?id=17472
5285     static assert(is(typeof(stdin) == File));
5286     static assert(is(typeof(stdout) == File));
5287     static assert(is(typeof(stderr) == File));
5288 }
5289 
5290 // roll our own appender, but with "safe" arrays
5291 private struct ReadlnAppender
5292 {
5293     char[] buf;
5294     size_t pos;
5295     bool safeAppend = false;
5296 
5297     void initialize(char[] b) @safe
5298     {
5299         buf = b;
5300         pos = 0;
5301     }
5302     @property char[] data() @trusted
5303     {
5304         if (safeAppend)
5305             assumeSafeAppend(buf.ptr[0 .. pos]);
5306         return buf.ptr[0 .. pos];
5307     }
5308 
5309     bool reserveWithoutAllocating(size_t n)
5310     {
5311         if (buf.length >= pos + n) // buf is already large enough
5312             return true;
5313 
5314         immutable curCap = buf.capacity;
5315         if (curCap >= pos + n)
5316         {
5317             buf.length = curCap;
5318             /* Any extra capacity we end up not using can safely be claimed
5319             by someone else. */
5320             safeAppend = true;
5321             return true;
5322         }
5323 
5324         return false;
5325     }
5326     void reserve(size_t n) @trusted
5327     {
5328         import core.stdc.string : memcpy;
5329         if (!reserveWithoutAllocating(n))
5330         {
5331             size_t ncap = buf.length * 2 + 128 + n;
5332             char[] nbuf = new char[ncap];
5333             memcpy(nbuf.ptr, buf.ptr, pos);
5334             buf = nbuf;
5335             // Allocated a new buffer. No one else knows about it.
5336             safeAppend = true;
5337         }
5338     }
5339     void putchar(char c) @trusted
5340     {
5341         reserve(1);
5342         buf.ptr[pos++] = c;
5343     }
5344     void putdchar(dchar dc) @trusted
5345     {
5346         import std.utf : encode, UseReplacementDchar;
5347 
5348         char[4] ubuf;
5349         immutable size = encode!(UseReplacementDchar.yes)(ubuf, dc);
5350         reserve(size);
5351         foreach (c; ubuf)
5352             buf.ptr[pos++] = c;
5353     }
5354     void putonly(const char[] b) @trusted
5355     {
5356         import core.stdc.string : memcpy;
5357         assert(pos == 0);   // assume this is the only put call
5358         if (reserveWithoutAllocating(b.length))
5359             memcpy(buf.ptr + pos, b.ptr, b.length);
5360         else
5361             buf = b.dup;
5362         pos = b.length;
5363     }
5364 }
5365 
5366 private struct LockedFile
5367 {
5368     private @system _iobuf* fp;
5369 
5370     this(FILE* fps) @trusted
5371     {
5372         _FLOCK(fps);
5373         // Since fps is now locked, we can cast away shared
5374         fp = cast(_iobuf*) fps;
5375     }
5376 
5377     @disable this();
5378     @disable this(this);
5379     @disable void opAssign(LockedFile);
5380 
5381     // these use unlocked fgetc calls
5382     @trusted fgetc() { return _FGETC(fp); }
5383     @trusted fgetwc() { return _FGETWC(fp); }
5384 
5385     ~this() @trusted
5386     {
5387         _FUNLOCK(cast(FILE*) fp);
5388     }
5389 }
5390 
5391 @safe unittest
5392 {
5393     void f() @safe
5394     {
5395         FILE* fps;
5396         auto lf = LockedFile(fps);
5397         static assert(!__traits(compiles, lf = LockedFile(fps)));
5398         version (ShouldFail)
5399         {
5400             lf.fps = null; // error with -preview=systemVariables
5401         }
5402     }
5403 }
5404 
5405 // Private implementation of readln
5406 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) @safe
5407 {
5408     version (CRuntime_Microsoft)
5409     {
5410         auto lf = LockedFile(fps);
5411 
5412         ReadlnAppender app;
5413         app.initialize(buf);
5414 
5415         int c;
5416         while ((c = lf.fgetc()) != -1)
5417         {
5418             app.putchar(cast(char) c);
5419             if (c == terminator)
5420             {
5421                 buf = app.data;
5422                 return buf.length;
5423             }
5424 
5425         }
5426 
5427         if (ferror(fps))
5428             StdioException();
5429         buf = app.data;
5430         return buf.length;
5431     }
5432     else static if (__traits(compiles, core.sys.posix.stdio.getdelim))
5433     {
5434         if (orientation == File.Orientation.wide)
5435         {
5436             import core.stdc.wchar_ : fwide;
5437 
5438             auto lf = LockedFile(fps);
5439             /* Stream is in wide characters.
5440              * Read them and convert to chars.
5441              */
5442             version (Windows)
5443             {
5444                 buf.length = 0;
5445                 for (int c = void; (c = lf.fgetwc()) != -1; )
5446                 {
5447                     if ((c & ~0x7F) == 0)
5448                     {   buf ~= c;
5449                         if (c == terminator)
5450                             break;
5451                     }
5452                     else
5453                     {
5454                         if (c >= 0xD800 && c <= 0xDBFF)
5455                         {
5456                             int c2 = void;
5457                             if ((c2 = lf.fgetwc()) != -1 ||
5458                                     c2 < 0xDC00 && c2 > 0xDFFF)
5459                             {
5460                                 StdioException("unpaired UTF-16 surrogate");
5461                             }
5462                             c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
5463                         }
5464                         import std.utf : encode;
5465                         encode(buf, c);
5466                     }
5467                 }
5468                 if (ferror(fps))
5469                     StdioException();
5470                 return buf.length;
5471             }
5472             else version (Posix)
5473             {
5474                 buf.length = 0;
5475                 for (int c; (c = lf.fgetwc()) != -1; )
5476                 {
5477                     import std.utf : encode;
5478 
5479                     if ((c & ~0x7F) == 0)
5480                         buf ~= cast(char) c;
5481                     else
5482                         encode(buf, cast(dchar) c);
5483                     if (c == terminator)
5484                         break;
5485                 }
5486                 if (ferror(fps))
5487                     StdioException();
5488                 return buf.length;
5489             }
5490             else
5491             {
5492                 static assert(0);
5493             }
5494         }
5495         return () @trusted {
5496             import core.stdc.stdlib : free;
5497 
5498             static char *lineptr = null;
5499             static size_t n = 0;
5500             scope(exit)
5501             {
5502                 if (n > 128 * 1024)
5503                 {
5504                     // Bound memory used by readln
5505                     free(lineptr);
5506                     lineptr = null;
5507                     n = 0;
5508                 }
5509             }
5510 
5511             const s = core.sys.posix.stdio.getdelim(&lineptr, &n, terminator, fps);
5512             if (s < 0)
5513             {
5514                 if (ferror(fps))
5515                     StdioException();
5516                 buf.length = 0;                // end of file
5517                 return 0;
5518             }
5519 
5520             const line = lineptr[0 .. s];
5521             if (s <= buf.length)
5522             {
5523                 buf = buf[0 .. s];
5524                 buf[] = line;
5525             }
5526             else
5527             {
5528                 buf = line.dup;
5529             }
5530             return s;
5531         }();
5532     }
5533     else // version (NO_GETDELIM)
5534     {
5535         import core.stdc.wchar_ : fwide;
5536 
5537         auto lf = LockedFile(fps);
5538         if (orientation == File.Orientation.wide)
5539         {
5540             /* Stream is in wide characters.
5541              * Read them and convert to chars.
5542              */
5543             version (Windows)
5544             {
5545                 buf.length = 0;
5546                 for (int c; (c = lf.fgetwc()) != -1; )
5547                 {
5548                     if ((c & ~0x7F) == 0)
5549                     {   buf ~= c;
5550                         if (c == terminator)
5551                             break;
5552                     }
5553                     else
5554                     {
5555                         if (c >= 0xD800 && c <= 0xDBFF)
5556                         {
5557                             int c2 = void;
5558                             if ((c2 = lf.fgetwc()) != -1 ||
5559                                     c2 < 0xDC00 && c2 > 0xDFFF)
5560                             {
5561                                 StdioException("unpaired UTF-16 surrogate");
5562                             }
5563                             c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
5564                         }
5565                         import std.utf : encode;
5566                         encode(buf, c);
5567                     }
5568                 }
5569                 if (ferror(fps))
5570                     StdioException();
5571                 return buf.length;
5572             }
5573             else version (Posix)
5574             {
5575                 import std.utf : encode;
5576                 buf.length = 0;
5577                 for (int c; (c = lf.fgetwc()) != -1; )
5578                 {
5579                     if ((c & ~0x7F) == 0)
5580                         buf ~= cast(char) c;
5581                     else
5582                         encode(buf, cast(dchar) c);
5583                     if (c == terminator)
5584                         break;
5585                 }
5586                 if (ferror(fps))
5587                     StdioException();
5588                 return buf.length;
5589             }
5590             else
5591             {
5592                 static assert(0);
5593             }
5594         }
5595 
5596         // Narrow stream
5597         // First, fill the existing buffer
5598         for (size_t bufPos = 0; bufPos < buf.length; )
5599         {
5600             immutable c = lf.fgetc();
5601             if (c == -1)
5602             {
5603                 buf.length = bufPos;
5604                 goto endGame;
5605             }
5606             buf[bufPos++] = cast(char) c;
5607             if (c == terminator)
5608             {
5609                 // No need to test for errors in file
5610                 buf.length = bufPos;
5611                 return bufPos;
5612             }
5613         }
5614         // Then, append to it
5615         for (int c; (c = lf.fgetc()) != -1; )
5616         {
5617             buf ~= cast(char) c;
5618             if (c == terminator)
5619             {
5620                 // No need to test for errors in file
5621                 return buf.length;
5622             }
5623         }
5624 
5625     endGame:
5626         if (ferror(fps))
5627             StdioException();
5628         return buf.length;
5629     }
5630 }
5631 
5632 @system unittest
5633 {
5634     static import std.file;
5635     auto deleteme = testFilename();
5636     scope(exit) std.file.remove(deleteme);
5637 
5638     std.file.write(deleteme, "abcd\n0123456789abcde\n1234\n");
5639     File f = File(deleteme, "rb");
5640 
5641     char[] ln = new char[2];
5642     f.readln(ln);
5643 
5644     assert(ln == "abcd\n");
5645     char[] t = ln[0 .. 2];
5646     t ~= 't';
5647     assert(t == "abt");
5648     // https://issues.dlang.org/show_bug.cgi?id=13856: ln stomped to "abtd"
5649     assert(ln == "abcd\n");
5650 
5651     // it can also stomp the array length
5652     ln = new char[4];
5653     f.readln(ln);
5654     assert(ln == "0123456789abcde\n");
5655 
5656     char[100] buf;
5657     ln = buf[];
5658     f.readln(ln);
5659     assert(ln == "1234\n");
5660     assert(ln.ptr == buf.ptr); // avoid allocation, buffer is good enough
5661 }
5662 
5663 /** Experimental network access via the File interface
5664 
5665         Opens a TCP connection to the given host and port, then returns
5666         a File struct with read and write access through the same interface
5667         as any other file (meaning writef and the byLine ranges work!).
5668 
5669         Authors:
5670                 Adam D. Ruppe
5671 
5672         Bugs:
5673                 Only works on Linux
5674 */
5675 version (linux)
5676 {
5677     File openNetwork(string host, ushort port)
5678     {
5679         import core.stdc.string : memcpy;
5680         import core.sys.posix.arpa.inet : htons;
5681         import core.sys.posix.netdb : gethostbyname;
5682         import core.sys.posix.netinet.in_ : sockaddr_in;
5683         static import core.sys.posix.unistd;
5684         static import sock = core.sys.posix.sys.socket;
5685         import std.conv : to;
5686         import std.exception : enforce;
5687         import std.internal.cstring : tempCString;
5688 
5689         auto h = enforce( gethostbyname(host.tempCString()),
5690             new StdioException("gethostbyname"));
5691 
5692         int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0);
5693         enforce(s != -1, new StdioException("socket"));
5694 
5695         scope(failure)
5696         {
5697             // want to make sure it doesn't dangle if something throws. Upon
5698             // normal exit, the File struct's reference counting takes care of
5699             // closing, so we don't need to worry about success
5700             core.sys.posix.unistd.close(s);
5701         }
5702 
5703         sockaddr_in addr;
5704 
5705         addr.sin_family = sock.AF_INET;
5706         addr.sin_port = htons(port);
5707         memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length);
5708 
5709         enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
5710             new StdioException("Connect failed"));
5711 
5712         File f;
5713         f.fdopen(s, "w+", host ~ ":" ~ to!string(port));
5714         return f;
5715     }
5716 }
5717 
5718 version (StdUnittest) private string testFilename(string file = __FILE__, size_t line = __LINE__) @safe
5719 {
5720     import std.conv : text;
5721     import std.file : deleteme;
5722     import std.path : baseName;
5723 
5724     // filename intentionally contains non-ASCII (Russian) characters for
5725     // https://issues.dlang.org/show_bug.cgi?id=7648
5726     return text(deleteme, "-детка.", baseName(file), ".", line);
5727 }