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