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