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