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