The OpenD Programming Language

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