Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

root/trunk/phobos/std/stdio.d

Revision 2359, 68.0 kB (checked in by andrei, 14 years ago)

Fix for bug 4922

  • Property svn:eol-style set to native
Line 
1 // Written in the D programming language.
2
3 /**
4 Standard I/O functions that extend $(B std.c.stdio).  $(B std.c.stdio)
5 is $(D_PARAM public)ally imported when importing $(B std.stdio).
6
7 Source: $(PHOBOSSRC std/_stdio.d)
8 Macros:
9 WIKI=Phobos/StdStdio
10
11 Copyright: Copyright Digital Mars 2007-.
12 License:   $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
13 Authors:   $(WEB digitalmars.com, Walter Bright),
14            $(WEB erdani.org, Andrei Alexandrescu)
15  */
16 module std.stdio;
17
18 public import core.stdc.stdio;
19 import std.stdiobase;
20 import core.memory, core.stdc.errno, core.stdc.stddef, core.stdc.stdlib,
21     core.stdc.string, core.stdc.wchar_;
22 import std.algorithm, std.array, std.conv, std.exception, std.file, std.format,
23     std.range, std.string, std.traits, std.typecons,
24     std.typetuple, std.utf;
25
26 version (DigitalMars) version (Windows)
27 {
28     // Specific to the way Digital Mars C does stdio
29     version = DIGITAL_MARS_STDIO;
30     import std.c.stdio : __fhnd_info, FHND_WCHAR, FHND_TEXT;
31 }
32
33 version (Posix)
34 {
35     import core.sys.posix.stdio;
36     alias core.sys.posix.stdio.fileno fileno;
37 }
38
39 version (linux)
40 {
41     // Specific to the way Gnu C does stdio
42     version = GCC_IO;
43     extern(C) FILE* fopen64(const char*, const char*);
44 }
45
46 version (OSX)
47 {
48     version = GENERIC_IO;
49     alias core.stdc.stdio.fopen fopen64;
50 }
51
52 version (FreeBSD)
53 {
54     version = GENERIC_IO;
55     alias core.stdc.stdio.fopen fopen64;
56 }
57
58 version(Windows)
59 {
60     alias core.stdc.stdio.fopen fopen64;
61 }
62
63 version (DIGITAL_MARS_STDIO)
64 {
65     extern (C)
66     {
67         /* **
68          * Digital Mars under-the-hood C I/O functions.
69          * Use _iobuf* for the unshared version of FILE*,
70          * usable when the FILE is locked.
71          */
72         int _fputc_nlock(int, _iobuf*);
73         int _fputwc_nlock(int, _iobuf*);
74         int _fgetc_nlock(_iobuf*);
75         int _fgetwc_nlock(_iobuf*);
76         int __fp_lock(FILE*);
77         void __fp_unlock(FILE*);
78
79         int setmode(int, int);
80     }
81     alias _fputc_nlock FPUTC;
82     alias _fputwc_nlock FPUTWC;
83     alias _fgetc_nlock FGETC;
84     alias _fgetwc_nlock FGETWC;
85
86     alias __fp_lock FLOCK;
87     alias __fp_unlock FUNLOCK;
88
89     alias setmode _setmode;
90     enum _O_BINARY = 0x8000;
91     int _fileno(FILE* f) { return f._file; }
92     alias _fileno fileno;
93 }
94 else version (GCC_IO)
95 {
96     /* **
97      * Gnu under-the-hood C I/O functions; see
98      * http://gnu.org/software/libc/manual/html_node/I_002fO-on-Streams.html
99      */
100     extern (C)
101     {
102         int fputc_unlocked(int, _iobuf*);
103         int fputwc_unlocked(wchar_t, _iobuf*);
104         int fgetc_unlocked(_iobuf*);
105         int fgetwc_unlocked(_iobuf*);
106         void flockfile(FILE*);
107         void funlockfile(FILE*);
108         ssize_t getline(char**, size_t*, FILE*);
109         ssize_t getdelim (char**, size_t*, int, FILE*);
110
111         private size_t fwrite_unlocked(const(void)* ptr,
112                 size_t size, size_t n, _iobuf *stream);
113     }
114
115     version (linux)
116     {
117         // declare fopen64 if not already
118         static if (!is(typeof(fopen64)))
119             extern (C) FILE* fopen64(in char*, in char*);
120     }
121
122     alias fputc_unlocked FPUTC;
123     alias fputwc_unlocked FPUTWC;
124     alias fgetc_unlocked FGETC;
125     alias fgetwc_unlocked FGETWC;
126
127     alias flockfile FLOCK;
128     alias funlockfile FUNLOCK;
129 }
130 else version (GENERIC_IO)
131 {
132     extern (C)
133     {
134         void flockfile(FILE*);
135         void funlockfile(FILE*);
136     }
137
138     int fputc_unlocked(int c, _iobuf* fp) { return fputc(c, cast(shared) fp); }
139     int fputwc_unlocked(wchar_t c, _iobuf* fp)
140     {
141         return fputwc(c, cast(shared) fp);
142     }
143     int fgetc_unlocked(_iobuf* fp) { return fgetc(cast(shared) fp); }
144     int fgetwc_unlocked(_iobuf* fp) { return fgetwc(cast(shared) fp); }
145
146     alias fputc_unlocked FPUTC;
147     alias fputwc_unlocked FPUTWC;
148     alias fgetc_unlocked FGETC;
149     alias fgetwc_unlocked FGETWC;
150
151     alias flockfile FLOCK;
152     alias funlockfile FUNLOCK;
153 }
154 else
155 {
156     static assert(0, "unsupported C I/O system");
157 }
158
159 //------------------------------------------------------------------------------
160 struct ByRecord(Fields...)
161 {
162 private:
163     File file;
164     char[] line;
165     Tuple!(Fields) current;
166     string format;
167
168 public:
169     this(File f, string format)
170     {
171         assert(f.isOpen);
172         file = f;
173         this.format = format;
174         popFront; // prime the range
175     }
176
177     /// Range primitive implementations.
178     @property bool empty()
179     {
180         return !file.isOpen;
181     }
182
183     /// Ditto
184     @property ref Tuple!(Fields) front()
185     {
186         return current;
187     }
188
189     /// Ditto
190     void popFront()
191     {
192         enforce(file.isOpen);
193         file.readln(line);
194         if (!line.length)
195         {
196             file.detach;
197         }
198         else
199         {
200             line = chomp(line);
201             formattedRead(line, format, &current);
202             enforce(line.empty, text("Leftover characters in record: `",
203                             line, "'"));
204         }
205     }
206 }
207
208 template byRecord(Fields...)
209 {
210     ByRecord!(Fields) byRecord(File f, string format)
211     {
212         return typeof(return)(f, format);
213     }
214 }
215
216 /**
217 Encapsulates a $(D FILE*). Generally D does not attempt to provide
218 thin wrappers over equivalent functions in the C standard library, but
219 manipulating $(D FILE*) values directly is unsafe and error-prone in
220 many ways. The $(D File) type ensures safe manipulation, automatic
221 file closing, and a lot of convenience.
222
223 The underlying $(D FILE*) handle is maintained in a reference-counted
224 manner, such that as soon as the last $(D File) variable bound to a
225 given $(D FILE*) goes out of scope, the underlying $(D FILE*) is
226 automatically closed.
227
228 Example:
229 ----
230 // test.d
231 void main(string args[])
232 {
233     auto f = File("test.txt", "w"); // open for writing
234     f.write("Hello");
235     if (args.length > 1)
236     {
237         auto g = f; // now g and f write to the same file
238                     // internal reference count is 2
239         g.write(", ", args[1]);
240         // g exits scope, reference count decreases to 1
241     }
242     f.writeln("!");
243     // f exits scope, reference count falls to zero,
244     // underlying $(D FILE*) is closed.
245 }
246 ----
247 <pre class=console>
248 % rdmd test.d Jimmy
249 % cat test.txt
250 Hello, Jimmy!
251 % __
252 </pre>
253  */
254 struct File
255 {
256     private struct Impl
257     {
258         FILE * handle = null;
259         uint refs = uint.max / 2;
260         string name = null;
261         bool isPipe;
262         this(FILE* h, uint r, string n, bool pipe = false)
263         {
264             handle = h;
265             refs = r;
266             name = n;
267             isPipe = pipe;
268         }
269     }
270     private Impl * p;
271
272 /**
273 Constructor taking the name of the file to open and the open mode
274 (with the same semantics as in the C standard library $(WEB
275 cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen)
276 function). Throws an exception if the file could not be opened.
277
278 Copying one $(D File) object to another results in the two $(D File)
279 objects referring to the same underlying file.
280
281 The destructor automatically closes the file as soon as no $(D File)
282 object refers to it anymore.
283  */
284     this(string name, in char[] stdioOpenmode = "rb")
285     {
286         p = new Impl(errnoEnforce(.fopen(name, stdioOpenmode),
287                         text("Cannot open file `", name, "' in mode `",
288                                 stdioOpenmode, "'")),
289                 1, name);
290     }
291
292     ~this()
293     {
294         if (!p) return;
295         if (p.refs == 1) close;
296         else --p.refs;
297     }
298
299     this(this)
300     {
301         if (!p) return;
302         assert(p.refs);
303         ++p.refs;
304     }
305
306 /**
307 Assigns a file to another. The target of the assignment gets detached
308 from whatever file it was attached to, and attaches itself to the new
309 file.
310  */
311     void opAssign(File rhs)
312     {
313         swap(p, rhs.p);
314     }
315
316 /**
317 First calls $(D detach) (throwing on failure), and then attempts to
318 _open file $(D name) with mode $(D stdioOpenmode). The mode has the
319 same semantics as in the C standard library $(WEB
320 cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function.
321 Throws exception in case of error.
322  */
323     void open(string name, in char[] stdioOpenmode = "rb")
324     {
325         detach;
326         auto another = File(name, stdioOpenmode);
327         swap(this, another);
328     }
329
330 /**
331 First calls $(D detach) (throwing on failure), and then runs a command
332 by calling the C standard library function $(WEB
333 opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen).
334  */
335     version(Posix) void popen(string command, in char[] stdioOpenmode = "r")
336     {
337         detach;
338         p = new Impl(errnoEnforce(.popen(command, stdioOpenmode),
339                         "Cannot run command `"~command~"'"),
340                 1, command, true);
341     }
342
343 /** Returns $(D true) if the file is opened. */
344     @property bool isOpen() const
345     {
346         return p !is null && p.handle;
347     }
348
349 /**
350 Returns $(D true) if the file is at end (see $(WEB
351 cplusplus.com/reference/clibrary/cstdio/feof.html, feof)). The file
352 must be opened, otherwise an exception is thrown.
353  */
354     @property bool eof() const
355     {
356         enforce(p && p.handle, "Calling eof() against an unopened file.");
357         return .feof(cast(FILE*) p.handle) != 0;
358     }
359
360 /** Returns the name of the file, if any. */
361     @property string name() const
362     {
363         return p.name;
364     }
365
366 /**
367 If the file is not opened, returns $(D false). Otherwise, returns
368 $(WEB cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for
369 the file handle.
370  */
371     @property bool error() const
372     {
373         return !p.handle || .ferror(cast(FILE*) p.handle);
374     }
375
376 /**
377 Detaches from the underlying file. If the sole owner, calls $(D close)
378 and throws if that fails.
379   */
380     void detach()
381     {
382         if (!p) return;
383         // @@@BUG
384         //if (p.refs == 1) close;
385         p = null;
386     }
387
388 /**
389 If the file was unopened, succeeds vacuously. Otherwise closes the
390 file (by calling $(WEB
391 cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)),
392 throwing on error. Even if an exception is thrown, afterwards the $(D
393 File) object is empty. This is different from $(D detach) in that it
394 always closes the file; consequently, all other $(D File) objects
395 referring to the same handle will see a closed file henceforth.
396  */
397     void close()
398     {
399         if (!p) return; // succeed vacuously
400         if (!p.handle)
401         {
402             p = null; // start a new life
403             return;
404         }
405         scope(exit)
406         {
407             p.handle = null; // nullify the handle anyway
408             p.name = null;
409             --p.refs;
410             p = null;
411         }
412         version (Posix)
413         {
414             if (p.isPipe)
415             {
416                 // Ignore the result of the command
417                 errnoEnforce(.pclose(p.handle) == 0,
418                         "Could not close pipe `"~p.name~"'");
419                 return;
420             }
421         }
422         //fprintf(std.c.stdio.stderr, ("Closing file `"~name~"`.\n\0").ptr);
423         errnoEnforce(.fclose(p.handle) == 0,
424                 "Could not close file `"~p.name~"'");
425     }
426
427 /**
428 If the file is not opened, succeeds vacuously. Otherwise, returns
429 $(WEB cplusplus.com/reference/clibrary/cstdio/_clearerr.html,
430 _clearerr) for the file handle.
431  */
432     void clearerr()
433     {
434         p is null || p.handle is null ||
435         .clearerr(p.handle);
436     }
437
438 /**
439 If the file is not opened, throws an exception. Otherwise, calls $(WEB
440 cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush) for the
441 file handle and throws on error.
442  */
443     void flush()
444     {
445         errnoEnforce
446         (.fflush(enforce(p.handle, "Calling fflush() on an unopened file"))
447                 == 0);
448     }
449
450 /**
451 If the file is not opened, throws an exception. Otherwise, calls $(WEB
452 cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the
453 file handle and throws on error.
454
455 $(D rawRead) always read in binary mode on Windows.
456  */
457     T[] rawRead(T)(T[] buffer)
458     {
459         enforce(buffer.length, "rawRead must take a non-empty buffer");
460         version(Windows)
461         {
462             immutable fd = ._fileno(p.handle);
463             immutable mode = ._setmode(fd, _O_BINARY);
464             scope(exit) ._setmode(fd, mode);
465             version(DIGITAL_MARS_STDIO)
466             {
467                 // @@@BUG@@@ 4243
468                 immutable info = __fhnd_info[fd];
469                 __fhnd_info[fd] &= ~FHND_TEXT;
470                 scope(exit) __fhnd_info[fd] = info;
471             }
472         }
473         immutable result =
474             .fread(buffer.ptr, T.sizeof, buffer.length, p.handle);
475         errnoEnforce(!error);
476         return result ? buffer[0 .. result] : null;
477     }
478
479     unittest
480     {
481         std.file.write("deleteme", "\r\n\n\r\n");
482         scope(exit) std.file.remove("deleteme");
483         auto f = File("deleteme", "r");
484         auto buf = f.rawRead(new char[5]);
485         f.close();
486         assert(buf == "\r\n\n\r\n");
487         /+
488         buf = stdin.rawRead(new char[5]);
489         assert(buf == "\r\n\n\r\n");
490         +/
491     }
492
493 /**
494 If the file is not opened, throws an exception. Otherwise, calls $(WEB
495 cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the
496 file handle and throws on error.
497
498 $(D rawWrite) always write in binary mode on Windows.
499  */
500     void rawWrite(T)(in T[] buffer)
501     {
502         version(Windows)
503         {
504             flush(); // before changing translation mode
505             immutable fd = ._fileno(p.handle);
506             immutable mode = ._setmode(fd, _O_BINARY);
507             scope(exit) ._setmode(fd, mode);
508             version(DIGITAL_MARS_STDIO)
509             {
510                 // @@@BUG@@@ 4243
511                 immutable info = __fhnd_info[fd];
512                 __fhnd_info[fd] &= ~FHND_TEXT;
513                 scope(exit) __fhnd_info[fd] = info;
514             }
515             scope(exit) flush(); // before restoring translation mode
516         }
517         auto result =
518             .fwrite(buffer.ptr, T.sizeof, buffer.length, p.handle);
519         if (result == result.max) result = 0;
520         errnoEnforce(result == buffer.length,
521                 text("Wrote ", result, " instead of ", buffer.length,
522                         " objects of type ", T.stringof, " to file `",
523                         p.name, "'"));
524     }
525
526     unittest
527     {
528         auto f = File("deleteme", "w");
529         scope(exit) std.file.remove("deleteme");
530         f.rawWrite("\r\n\n\r\n");
531         f.close();
532         assert(std.file.read("deleteme") == "\r\n\n\r\n");
533     }
534
535 /**
536 If the file is not opened, throws an exception. Otherwise, calls $(WEB
537 cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek) for the
538 file handle. Throws on error.
539  */
540     void seek(long offset, int origin = SEEK_SET)
541     {
542         enforce(isOpen, "Attempting to seek() in an unopened file");
543         version (Windows)
544         {
545             errnoEnforce(fseek(p.handle, to!int(offset), origin) == 0,
546                     "Could not seek in file `"~p.name~"'");
547         }
548         else
549         {
550             //static assert(off_t.sizeof == 8);
551             errnoEnforce(fseeko(p.handle, offset, origin) == 0,
552                     "Could not seek in file `"~p.name~"'");
553         }
554     }
555
556     unittest
557     {
558         auto f = File("deleteme", "w+");
559         scope(exit) { f.close(); std.file.remove("deleteme"); }
560         f.rawWrite("abcdefghijklmnopqrstuvwxyz");
561         f.seek(7);
562         assert(f.readln() == "hijklmnopqrstuvwxyz");
563         version (Windows)
564         {
565             // No test for large files yet
566         }
567         else
568         {
569             auto bigOffset = cast(ulong) int.max + 100;
570             f.seek(bigOffset);
571             assert(f.tell == bigOffset, text(f.tell));
572             // Uncomment the tests below only if you want to wait for
573             // a long time
574             // f.rawWrite("abcdefghijklmnopqrstuvwxyz");
575             // f.seek(-3, SEEK_END);
576             // assert(f.readln() == "xyz");
577         }
578     }
579
580 /**
581 If the file is not opened, throws an exception. Otherwise, calls $(WEB
582 cplusplus.com/reference/clibrary/cstdio/ftell.html, ftell) for the
583 managed file handle. Throws on error.
584  */
585     @property ulong tell() const
586     {
587         enforce(isOpen, "Attempting to tell() in an unopened file");
588         version (Windows)
589         {
590             immutable result = ftell(cast(FILE*) p.handle);
591         }
592         else
593         {
594             immutable result = ftello(cast(FILE*) p.handle);
595         }
596         errnoEnforce(result != -1,
597                 "Query ftell() failed for file `"~p.name~"'");
598         return result;
599     }
600
601     unittest
602     {
603         std.file.write("deleteme", "abcdefghijklmnopqrstuvwqxyz");
604         scope(exit) { std.file.remove("deleteme"); }
605         auto f = File("deleteme");
606         auto a = new ubyte[4];
607         f.rawRead(a);
608         assert(f.tell == 4, text(f.tell));
609     }
610
611 /**
612 If the file is not opened, throws an exception. Otherwise, calls $(WEB
613 cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind) for the
614 file handle. Throws on error.
615  */
616     void rewind()
617     {
618         enforce(isOpen, "Attempting to rewind() an unopened file");
619         .rewind(p.handle);
620     }
621
622 /**
623 If the file is not opened, throws an exception. Otherwise, calls $(WEB
624 cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for
625 the file handle.
626  */
627     void setvbuf(size_t size, int mode = _IOFBF)
628     {
629         enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
630         errnoEnforce(.setvbuf(p.handle, null, mode, size) == 0,
631                 "Could not set buffering for file `"~p.name~"'");
632     }
633
634 /**
635 If the file is not opened, throws an exception. Otherwise, calls
636 $(WEB cplusplus.com/reference/clibrary/cstdio/_setvbuf.html,
637 _setvbuf) for the file handle. */
638     void setvbuf(void[] buf, int mode = _IOFBF)
639     {
640         enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
641         errnoEnforce(.setvbuf(p.handle,
642                         cast(char*) buf.ptr, mode, buf.length) == 0,
643                 "Could not set buffering for file `"~p.name~"'");
644     }
645
646 /**
647 If the file is not opened, throws an exception. Otherwise, writes its
648 arguments in text format to the file. */
649     void write(S...)(S args)
650     {
651         auto w = lockingTextWriter();
652         foreach (arg; args)
653         {
654             static if (isSomeString!(typeof(arg)))
655             {
656                 w.put(arg);
657             }
658             else
659             {
660                 std.format.formattedWrite(w, "%s", arg);
661             }
662         }
663     }
664
665 /**
666 If the file is not opened, throws an exception. Otherwise, writes its
667 arguments in text format to the file, followed by a newline. */
668     void writeln(S...)(S args)
669     {
670         write(args, '\n');
671         errnoEnforce(.fflush(p.handle) == 0,
672                     "Could not flush file `"~p.name~"'");
673     }
674
675     private enum errorMessage =
676         "You must pass a formatting string as the first"
677         " argument to writef or writefln. If no formatting is needed,"
678         " you may want to use write or writeln.";
679
680 /**
681 If the file is not opened, throws an exception. Otherwise, writes its
682 arguments in text format to the file, according to the format in the
683 first argument. */
684     void writef(S...)(S args)// if (isSomeString!(S[0]))
685     {
686         assert(p);
687         assert(p.handle);
688         static assert(S.length>0, errorMessage);
689         static assert(isSomeString!(S[0]), errorMessage);
690         auto w = lockingTextWriter();
691         std.format.formattedWrite(w, args);
692     }
693
694 /**
695 Same as writef, plus adds a newline. */
696     void writefln(S...)(S args)
697     {
698         static assert(S.length>0, errorMessage);
699         static assert(isSomeString!(S[0]), errorMessage);
700         auto w = lockingTextWriter;
701         std.format.formattedWrite(w, args);
702         w.put('\n');
703         .fflush(p.handle);
704     }
705
706 /**********************************
707 Read line from stream $(D fp) and write it to $(D buf[]), including
708 terminating character.
709
710 This is often faster than $(D File.readln(dchar)) because the buffer
711 is reused each call. Note that reusing the buffer means that the
712 previous contents of it has to be copied if needed.
713
714 Params:
715 fp = input stream
716 buf = buffer used to store the resulting line data. buf is
717 resized as necessary.
718
719 Returns:
720 0 for end of file, otherwise number of characters read
721
722 Throws: $(D StdioException) on error
723
724 Example:
725 ---
726 // Reads $(D stdin) and writes it to $(D stdout).
727 import std.stdio;
728
729 int main()
730 {
731     char[] buf;
732     while (stdin.readln(buf))
733         write(buf);
734     return 0;
735 }
736 ---
737
738 This method is more efficient than the one in the previous example
739 because $(D stdin.readln(buf)) reuses (if possible) memory allocated
740 by $(D buf), whereas $(D buf = stdin.readln()) makes a new memory allocation
741 with every line.  */
742     S readln(S = string)(dchar terminator = '\n')
743     {
744         Unqual!(typeof(S.init[0]))[] buf;
745         readln(buf, terminator);
746         return assumeUnique(buf);
747     }
748
749     unittest
750     {
751         std.file.write("deleteme", "hello\nworld\n");
752         scope(exit) std.file.remove("deleteme");
753         foreach (C; Tuple!(char, wchar, dchar).Types)
754         {
755             auto witness = [ "hello\n", "world\n" ];
756             auto f = File("deleteme");
757             uint i = 0;
758             immutable(C)[] buf;
759             while ((buf = f.readln!(typeof(buf))()).length)
760             {
761                 assert(i < witness.length);
762                 assert(equal(buf, witness[i++]));
763             }
764             assert(i == witness.length);
765         }
766     }
767
768 /** ditto */
769     size_t readln(C)(ref C[] buf, dchar terminator = '\n') if (isSomeChar!C)
770     {
771         static if (is(C == char))
772         {
773             enforce(p && p.handle, "Attempt to read from an unopened file.");
774             return readlnImpl(p.handle, buf, terminator);
775         }
776         else
777         {
778             // TODO: optimize this
779             string s = readln(terminator);
780             if (!s.length) return 0;
781             buf.length = 0;
782             foreach (wchar c; s)
783             {
784                 buf ~= c;
785             }
786             return buf.length;
787         }
788     }
789
790 /** ditto */
791     size_t readln(C, R)(ref C[] buf, R terminator)
792         if (isBidirectionalRange!R && is(typeof(terminator.front == buf[0])))
793     {
794         auto last = terminator.back();
795         C[] buf2;
796         swap(buf, buf2);
797         for (;;) {
798             if (!readln(buf2, last) || endsWith(buf2, terminator)) {
799                 if (buf.empty) {
800                     buf = buf2;
801                 } else {
802                     buf ~= buf2;
803                 }
804                 break;
805             }
806             buf ~= buf2;
807         }
808         return buf.length;
809     }
810
811     unittest
812     {
813         std.file.write("deleteme", "hello\n\rworld\nhow\n\rare ya");
814         auto witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ];
815         scope(exit) std.file.remove("deleteme");
816         auto f = File("deleteme");
817         uint i = 0;
818         char[] buf;
819         while (f.readln(buf, "\n\r"))
820         {
821             assert(i < witness.length);
822             assert(buf == witness[i++]);
823         }
824     }
825
826     uint readf(Data...)(in char[] format, Data data)
827     {
828         assert(isOpen);
829         auto input = LockingTextReader(this);
830         return formattedRead(input, format, data);
831     }
832
833     unittest
834     {
835         std.file.write("deleteme", "hello\nworld\n");
836         scope(exit) std.file.remove("deleteme");
837         string s;
838         auto f = File("deleteme");
839         f.readf("%s\n", &s);
840         assert(s == "hello", "["~s~"]");
841     }
842
843 /**
844  Returns a temporary file by calling $(WEB
845  cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile). */
846     static File tmpfile()
847     {
848         auto h = errnoEnforce(core.stdc.stdio.tmpfile,
849                 "Could not create temporary file with tmpfile()");
850         File result = void;
851         result.p = new Impl(h, 1, null);
852         return result;
853     }
854
855 /**
856 Unsafe function that wraps an existing $(D FILE*). The resulting $(D
857 File) never takes the initiative in closing the file. */
858     /*private*/ static File wrapFile(FILE* f)
859     {
860         File result = void;
861         //result.p = new Impl(f, uint.max / 2, null);
862         result.p = new Impl(f, 9999, null);
863         return result;
864     }
865
866 /**
867 Returns the $(D FILE*) corresponding to this object.
868  */
869     FILE* getFP()
870     {
871         enforce(p && p.handle,
872                 "Attempting to call getFP() on an unopened file");
873         return p.handle;
874     }
875
876     unittest
877     {
878         assert(stdout.getFP == std.c.stdio.stdout);
879     }
880
881 /**
882 Returns the file number corresponding to this object.
883  */
884     /*version(Posix) */int fileno() const
885     {
886         enforce(isOpen, "Attempting to call fileno() on an unopened file");
887         return .fileno(cast(FILE*) p.handle);
888     }
889
890 /**
891 Range that reads one line at a time. */
892     enum KeepTerminator : bool { no, yes }
893     /// ditto
894     struct ByLine(Char, Terminator)
895     {
896         File file;
897         Char[] line;
898         Terminator terminator;
899         KeepTerminator keepTerminator;
900
901         this(File f, KeepTerminator kt = KeepTerminator.no,
902                 Terminator terminator = '\n')
903         {
904             file = f;
905             this.terminator = terminator;
906             keepTerminator = kt;
907             popFront; // prime the range
908             // @@@BUG@@@ line below should not exist
909             //if (file.p) ++file.p.refs;
910         }
911
912         /// Range primitive implementations.
913         bool empty() const
914         {
915             return !file.isOpen;
916         }
917
918         /// Ditto
919         Char[] front()
920         {
921             return line;
922         }
923
924         /// Ditto
925         void popFront()
926         {
927             enforce(file.isOpen);
928             file.readln(line, terminator);
929             if (!line.length)
930                 file.detach;
931             else if (keepTerminator == KeepTerminator.no
932                     && std.algorithm.endsWith(line, terminator))
933                 line.length = line.length - 1;
934         }
935     }
936
937 /**
938 Convenience function that returns the $(D LinesReader) corresponding
939 to this file. */
940     ByLine!(Char, Terminator) byLine(Terminator = char, Char = char)
941     (KeepTerminator keepTerminator = KeepTerminator.no,
942             Terminator terminator = '\n')
943     {
944         return typeof(return)(this, keepTerminator, terminator);
945     }
946
947     unittest
948     {
949         //printf("Entering test at line %d\n", __LINE__);
950         scope(failure) printf("Failed test at line %d\n", __LINE__);
951         std.file.write("testingByLine", "asd\ndef\nasdf");
952         auto witness = [ "asd", "def", "asdf" ];
953         uint i;
954         auto f = File("testingByLine");
955         scope(exit)
956         {
957             f.close;
958             assert(!f.isOpen);
959             //std.file.remove("testingByLine");
960         }
961         foreach (line; f.byLine())
962         {
963             assert(line == witness[i++]);
964         }
965         assert(i == witness.length);
966         i = 0;
967         f.rewind;
968         foreach (line; f.byLine(KeepTerminator.yes))
969         {
970             assert(line == witness[i++] ~ '\n' || i == witness.length);
971         }
972         assert(i == witness.length);
973     }
974
975     template byRecord(Fields...)
976     {
977         ByRecord!(Fields) byRecord(string format)
978         {
979             return typeof(return)(this, format);
980         }
981     }
982
983     unittest
984     {
985         // scope(failure) printf("Failed test at line %d\n", __LINE__);
986         // std.file.write("deleteme", "1 2\n4 1\n5 100");
987         // scope(exit) std.file.remove("deleteme");
988         // File f = File("deleteme");
989         // scope(exit) f.close;
990         // auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ];
991         // uint i;
992         // foreach (e; f.byRecord!(int, int)("%s %s"))
993         // {
994         //     //.writeln(e);
995         //     assert(e == t[i++]);
996         // }
997     }
998
999
1000     /**
1001      * Range that reads a chunk at a time.
1002      */
1003     struct ByChunk
1004     {
1005       private:
1006         File    file_;
1007         ubyte[] chunk_;
1008  
1009  
1010       public:
1011         this(File file, size_t size)
1012         in
1013         {
1014             assert(size, "size must be larger than 0");
1015         }
1016         body
1017         {
1018             file_  = file;
1019             chunk_ = new ubyte[](size);
1020  
1021             popFront();
1022         }
1023  
1024
1025         /// Range primitive operations.
1026         @property
1027         bool empty() const
1028         {
1029             return !file_.isOpen;
1030         }
1031  
1032
1033         /// Ditto
1034         @property
1035         nothrow ubyte[] front()
1036         {
1037             return chunk_;
1038         }
1039  
1040
1041         /// Ditto
1042         void popFront()
1043         {
1044             enforce(!empty, "Cannot call popFront on empty range");
1045  
1046             chunk_ = file_.rawRead(chunk_);
1047             if (chunk_.length == 0)
1048                 file_.detach();
1049         }
1050     }
1051
1052 /**
1053 Iterates through a file a chunk at a time by using $(D foreach).
1054
1055 Example:
1056
1057 ---------
1058 void main()
1059 {
1060   foreach (ubyte[] buffer; stdin.byChunk(4096))
1061   {
1062     ... use buffer ...
1063   }
1064 }
1065 ---------
1066
1067 The content of $(D buffer) is reused across calls. In the example
1068 above, $(D buffer.length) is 4096 for all iterations, except for the
1069 last one, in which case $(D buffer.length) may be less than 4096 (but
1070 always greater than zero).
1071
1072 In case of an I/O error, an $(D StdioException) is thrown.
1073  */
1074     ByChunk byChunk(size_t chunkSize)
1075     {
1076         return ByChunk(this, chunkSize);
1077     }
1078
1079     unittest
1080     {
1081         scope(failure) printf("Failed test at line %d\n", __LINE__);
1082
1083         std.file.write("testingByChunk", "asd\ndef\nasdf");
1084
1085         auto witness = ["asd\n", "def\n", "asdf" ];
1086         auto f = File("testingByChunk");
1087         scope(exit)
1088         {
1089             f.close;
1090             assert(!f.isOpen);
1091             std.file.remove("testingByChunk");
1092         }
1093
1094         uint i;
1095         foreach (chunk; f.byChunk(4))
1096             assert(chunk == cast(ubyte[])witness[i++]);
1097
1098         assert(i == witness.length);
1099     }
1100
1101 /**
1102 $(D Range) that locks the file and allows fast writing to it.
1103  */
1104     struct LockingTextWriter
1105     {
1106         FILE* fps;          // the shared file handle
1107         _iobuf* handle;     // the unshared version of fps
1108         int orientation;
1109
1110         this(ref File f)
1111         {
1112             enforce(f.p && f.p.handle);
1113             fps = f.p.handle;
1114             orientation = fwide(fps, 0);
1115             FLOCK(fps);
1116             handle = cast(_iobuf*)fps;
1117         }
1118
1119         ~this()
1120         {
1121             FUNLOCK(fps);
1122             fps = null;
1123             handle = null;
1124         }
1125
1126         this(this)
1127         {
1128             enforce(fps);
1129             FLOCK(fps);
1130         }
1131
1132         /// Range primitive implementations.
1133         void put(A)(A writeme) if (is(ElementType!A : const(dchar)))
1134         {
1135             static if (isSomeString!A)
1136                 alias typeof(writeme[0]) C;
1137             else
1138                 alias ElementType!A C;
1139             static assert(!is(C == void));
1140             if (writeme[0].sizeof == 1 && orientation <= 0)
1141             {
1142                 //file.write(writeme); causes infinite recursion!!!
1143                 //file.rawWrite(writeme);
1144                 auto result =
1145                     .fwrite(writeme.ptr, C.sizeof, writeme.length, fps);
1146                 if (result != writeme.length) errnoEnforce(0);
1147             }
1148             else
1149             {
1150                 // put each character in turn
1151                 foreach (dchar c; writeme)
1152                 {
1153                     put(c);
1154                 }
1155             }
1156         }
1157
1158         // @@@BUG@@@ 2340
1159         //void front(C)(C c) if (is(C : dchar)) {
1160         /// ditto
1161         void put(C)(C c) if (is(C : const(dchar)))
1162         {
1163             static if (c.sizeof == 1)
1164             {
1165                 // simple char
1166                 if (orientation <= 0) FPUTC(c, handle);
1167                 else FPUTWC(c, handle);
1168             }
1169             else static if (c.sizeof == 2)
1170             {
1171                 if (orientation <= 0)
1172                 {
1173                     if (c <= 0x7F)
1174                     {
1175                         FPUTC(c, handle);
1176                     }
1177                     else
1178                     {
1179                         char[4] buf;
1180                         auto b = std.utf.toUTF8(buf, c);
1181                         foreach (i ; 0 .. b.length)
1182                             FPUTC(b[i], handle);
1183                     }
1184                 }
1185                 else
1186                 {
1187                     FPUTWC(c, handle);
1188                 }
1189             }
1190             else // 32-bit characters
1191             {
1192                 if (orientation <= 0)
1193                 {
1194                     if (c <= 0x7F)
1195                     {
1196                         FPUTC(c, handle);
1197                     }
1198                     else
1199                     {
1200                         char[4] buf = void;
1201                         auto b = std.utf.toUTF8(buf, c);
1202                         foreach (i ; 0 .. b.length)
1203                             FPUTC(b[i], handle);
1204                     }
1205                 }
1206                 else
1207                 {
1208                     version (Windows)
1209                     {
1210                         assert(isValidDchar(c));
1211                         if (c <= 0xFFFF)
1212                         {
1213                             FPUTWC(c, handle);
1214                         }
1215                         else
1216                         {
1217                             FPUTWC(cast(wchar)
1218                                     ((((c - 0x10000) >> 10) & 0x3FF)
1219                                             + 0xD800), handle);
1220                             FPUTWC(cast(wchar)
1221                                     (((c - 0x10000) & 0x3FF) + 0xDC00),
1222                                     handle);
1223                         }
1224                     }
1225                     else version (Posix)
1226                     {
1227                         FPUTWC(c, handle);
1228                     }
1229                     else
1230                     {
1231                         static assert(0);
1232                     }
1233                 }
1234             }
1235         }
1236     }
1237
1238 /// Convenience function.
1239     LockingTextWriter lockingTextWriter()
1240     {
1241         return LockingTextWriter(this);
1242     }
1243
1244 /// Get the size of the file, ulong.max if file is not searchable, but still throws if an actual error occurs.
1245     @property ulong size()
1246     {
1247         ulong pos = void;
1248         if (collectException(pos = tell)) return ulong.max;
1249         scope(exit) seek(pos);
1250         seek(0, SEEK_END);
1251         return tell;
1252     }
1253 }
1254
1255 unittest
1256 {
1257     scope(exit) collectException(std.file.remove("deleteme"));
1258     std.file.write("deleteme", "1 2 3");
1259     auto f = File("deleteme");
1260     assert(f.size == 5);
1261     assert(f.tell == 0);
1262 }
1263
1264 struct LockingTextReader
1265 {
1266     private File _f;
1267     private dchar _crt;
1268
1269     this(File f)
1270     {
1271         enforce(f.isOpen);
1272         _f = f;
1273         FLOCK(_f.p.handle);
1274     }
1275
1276     this(this)
1277     {
1278         FLOCK(_f.p.handle);
1279     }
1280
1281     ~this()
1282     {
1283         // File locking has its own reference count
1284         if (_f.isOpen) FUNLOCK(_f.p.handle);
1285     }
1286
1287     void opAssign(LockingTextReader r)
1288     {
1289         swap(this, r);
1290     }
1291
1292     @property bool empty()
1293     {
1294         if (!_f.isOpen || _f.eof) return true;
1295         if (_crt == _crt.init)
1296         {
1297             _crt = FGETC(cast(_iobuf*) _f.p.handle);
1298             if (_crt == -1)
1299             {
1300                 clear(_f);
1301                 return true;
1302             }
1303             else
1304             {
1305                 enforce(ungetc(_crt, cast(FILE*) _f.p.handle) == _crt);
1306             }
1307         }
1308         return false;
1309     }
1310
1311     dchar front()
1312     {
1313         enforce(!empty);
1314         return _crt;
1315     }
1316
1317     void popFront()
1318     {
1319         enforce(!empty);
1320         if (FGETC(cast(_iobuf*) _f.p.handle) == -1)
1321         {
1322             enforce(_f.eof);
1323         }
1324         _crt = _crt.init;
1325     }
1326
1327     // void unget(dchar c)
1328     // {
1329     //     ungetc(c, cast(FILE*) _f.p.handle);
1330     // }
1331 }
1332
1333 unittest
1334 {
1335     std.file.write("deleteme", "1 2 3");
1336     int x, y;
1337     auto f = File("deleteme");
1338     f.readf("%s ", &x);
1339     assert(x == 1);
1340     f.readf("%d ", &x);
1341     assert(x == 2);
1342     f.readf("%d ", &x);
1343     assert(x == 3);
1344     //pragma(msg, "--- todo: readf ---");
1345 }
1346
1347 private
1348 void writefx(FILE* fps, TypeInfo[] arguments, void* argptr, int newline=false)
1349 {
1350     int orientation = fwide(fps, 0);    // move this inside the lock?
1351
1352     /* Do the file stream locking at the outermost level
1353      * rather than character by character.
1354      */
1355     FLOCK(fps);
1356     scope(exit) FUNLOCK(fps);
1357
1358     auto fp = cast(_iobuf*)fps;     // fp is locked version
1359
1360     if (orientation <= 0)                // byte orientation or no orientation
1361     {
1362         void putc(dchar c)
1363         {
1364             if (c <= 0x7F)
1365             {
1366                 FPUTC(c, fp);
1367             }
1368             else
1369             {
1370                 char[4] buf = void;
1371                 foreach (i; 0 .. std.utf.toUTF8(buf, c).length)
1372                     FPUTC(buf[i], fp);
1373             }
1374         }
1375
1376         std.format.doFormat(&putc, arguments, argptr);
1377         if (newline)
1378             FPUTC('\n', fp);
1379     }
1380     else if (orientation > 0)                // wide orientation
1381     {
1382         version (Windows)
1383         {
1384             void putcw(dchar c)
1385             {
1386                 assert(isValidDchar(c));
1387                 if (c <= 0xFFFF)
1388                 {
1389                     FPUTWC(c, fp);
1390                 }
1391                 else
1392                 {
1393                     FPUTWC(cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) +
1394                                     0xD800), fp);
1395                     FPUTWC(cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00), fp);
1396                 }
1397             }
1398         }
1399         else version (Posix)
1400         {
1401             void putcw(dchar c)
1402             {
1403                 FPUTWC(c, fp);
1404             }
1405         }
1406         else
1407         {
1408             static assert(0);
1409         }
1410
1411         std.format.doFormat(&putcw, arguments, argptr);
1412         if (newline)
1413             FPUTWC('\n', fp);
1414     }
1415 }
1416
1417 template isStreamingDevice(T)
1418 {
1419     enum isStreamingDevice = is(T : FILE*) ||
1420         is(T : File);
1421 }
1422
1423 /***********************************
1424 For each argument $(D arg) in $(D args), format the argument (as per
1425 $(LINK2 std_conv.html, to!(string)(arg))) and write the resulting
1426 string to $(D args[0]). A call without any arguments will fail to
1427 compile.
1428
1429 Throws: In case of an I/O error, throws an $(D StdioException).
1430  */
1431 void write(T...)(T args) if (!is(T[0] : File))
1432 {
1433     stdout.write(args);
1434 }
1435
1436 unittest
1437 {
1438     //printf("Entering test at line %d\n", __LINE__);
1439     scope(failure) printf("Failed test at line %d\n", __LINE__);
1440     void[] buf;
1441     write(buf);
1442     // test write
1443     string file = "dmd-build-test.deleteme.txt";
1444     auto f = File(file, "w");
1445 //    scope(exit) { std.file.remove(file); }
1446      f.write("Hello, ",  "world number ", 42, "!");
1447      f.close;
1448      assert(cast(char[]) std.file.read(file) == "Hello, world number 42!");
1449     // // test write on stdout
1450     //auto saveStdout = stdout;
1451     //scope(exit) stdout = saveStdout;
1452     //stdout.open(file, "w");
1453     Object obj;
1454     //write("Hello, ",  "world number ", 42, "! ", obj);
1455     //stdout.close;
1456     // auto result = cast(char[]) std.file.read(file);
1457     // assert(result == "Hello, world number 42! null", result);
1458 }
1459
1460 /***********************************
1461  * Equivalent to $(D write(args, '\n')).  Calling $(D writeln) without
1462  * arguments is valid and just prints a newline to the standard
1463  * output.
1464  */
1465 void writeln(T...)(T args) if (T.length == 0)
1466 {
1467     enforce(fputc('\n', .stdout.p.handle) == '\n');
1468 }
1469
1470 unittest
1471 {
1472     // Just make sure the call compiles
1473     if (false) writeln();
1474 }
1475
1476 /// ditto
1477 void writeln(T...)(T args)
1478 if (T.length == 1 && is(typeof(args[0]) : const(char)[]))
1479 {
1480     enforce(fprintf(.stdout.p.handle, "%.*s\n",
1481                     cast(int) args[0].length, args[0].ptr) >= 0);
1482 }
1483
1484 unittest
1485 {
1486     if (false) writeln("wyda");
1487 }
1488
1489 /// Ditto
1490 void writeln(T...)(T args)
1491 if (T.length > 1 || T.length == 1 && !is(typeof(args[0]) : const(char)[]))
1492 {
1493     stdout.write(args, '\n');
1494 }
1495
1496 unittest
1497 {
1498         //printf("Entering test at line %d\n", __LINE__);
1499     scope(failure) printf("Failed test at line %d\n", __LINE__);
1500     // test writeln
1501     string file = "dmd-build-test.deleteme.txt";
1502     auto f = File(file, "w");
1503     scope(exit) { std.file.remove(file); }
1504     f.writeln("Hello, ",  "world number ", 42, "!");
1505     f.close;
1506     version (Windows)
1507         assert(cast(char[]) std.file.read(file) ==
1508                 "Hello, world number 42!\r\n");
1509     else
1510         assert(cast(char[]) std.file.read(file) ==
1511                 "Hello, world number 42!\n");
1512     // test writeln on stdout
1513     auto saveStdout = stdout;
1514     scope(exit) stdout = saveStdout;
1515     stdout.open(file, "w");
1516     writeln("Hello, ",  "world number ", 42, "!");
1517     stdout.close;
1518     version (Windows)
1519         assert(cast(char[]) std.file.read(file) ==
1520                 "Hello, world number 42!\r\n");
1521     else
1522         assert(cast(char[]) std.file.read(file) ==
1523                 "Hello, world number 42!\n");
1524 }
1525
1526 /***********************************
1527  * If the first argument $(D args[0]) is a $(D FILE*), use
1528  * $(LINK2 std_format.html#format-string, the format specifier) in
1529  * $(D args[1]) to control the formatting of $(D
1530  * args[2..$]), and write the resulting string to $(D args[0]).
1531  * If $(D arg[0]) is not a $(D FILE*), the call is
1532  * equivalent to $(D writef(stdout, args)).
1533  *
1534
1535 IMPORTANT:
1536
1537 New behavior starting with D 2.006: unlike previous versions,
1538 $(D writef) (and also $(D writefln)) only scans its first
1539 string argument for format specifiers, but not subsequent string
1540 arguments. This decision was made because the old behavior made it
1541 unduly hard to simply print string variables that occasionally
1542 embedded percent signs.
1543
1544 Also new starting with 2.006 is support for positional
1545 parameters with
1546 $(LINK2 http://opengroup.org/onlinepubs/009695399/functions/printf.html,
1547 POSIX) syntax.
1548
1549 Example:
1550
1551 -------------------------
1552 writef("Date: %2$s %1$s", "October", 5); // "Date: 5 October"
1553 ------------------------
1554
1555 The positional and non-positional styles can be mixed in the same
1556 format string. (POSIX leaves this behavior undefined.) The internal
1557 counter for non-positional parameters tracks the popFront parameter after
1558 the largest positional parameter already used.
1559
1560 New starting with 2.008: raw format specifiers. Using the "%r"
1561 specifier makes $(D writef) simply write the binary
1562 representation of the argument. Use "%-r" to write numbers in little
1563 endian format, "%+r" to write numbers in big endian format, and "%r"
1564 to write numbers in platform-native format.
1565
1566 */
1567
1568 void writef(T...)(T args)
1569 {
1570     stdout.writef(args);
1571 }
1572
1573 unittest
1574 {
1575     //printf("Entering test at line %d\n", __LINE__);
1576     scope(failure) printf("Failed test at line %d\n", __LINE__);
1577     // test writef
1578     string file = "dmd-build-test.deleteme.txt";
1579     auto f = File(file, "w");
1580     scope(exit) { std.file.remove(file); }
1581     f.writef("Hello, %s world number %s!", "nice", 42);
1582     f.close;
1583     assert(cast(char[]) std.file.read(file) ==  "Hello, nice world number 42!");
1584     // test write on stdout
1585     auto saveStdout = stdout;
1586     scope(exit) stdout = saveStdout;
1587     stdout.open(file, "w");
1588     writef("Hello, %s world number %s!", "nice", 42);
1589     stdout.close;
1590     assert(cast(char[]) std.file.read(file) == "Hello, nice world number 42!");
1591 }
1592
1593 /***********************************
1594  * Equivalent to $(D writef(args, '\n')).
1595  */
1596 void writefln(T...)(T args)
1597 {
1598     stdout.writefln(args);
1599 }
1600
1601 unittest
1602 {
1603         //printf("Entering test at line %d\n", __LINE__);
1604     scope(failure) printf("Failed test at line %d\n", __LINE__);
1605     // test writefln
1606     string file = "dmd-build-test.deleteme.txt";
1607     auto f = File(file, "w");
1608     scope(exit) { std.file.remove(file); }
1609     f.writefln("Hello, %s world number %s!", "nice", 42);
1610     f.close;
1611     version (Windows)
1612         assert(cast(char[]) std.file.read(file) ==
1613                 "Hello, nice world number 42!\r\n");
1614     else
1615         assert(cast(char[]) std.file.read(file) ==
1616                 "Hello, nice world number 42!\n",
1617                 cast(char[]) std.file.read(file));
1618     // test write on stdout
1619     // auto saveStdout = stdout;
1620     // scope(exit) stdout = saveStdout;
1621     // stdout.open(file, "w");
1622     // assert(stdout.isOpen);
1623     // writefln("Hello, %s world number %s!", "nice", 42);
1624     // foreach (F ; TypeTuple!(ifloat, idouble, ireal))
1625     // {
1626     //     F a = 5i;
1627     //     F b = a % 2;
1628     //     writeln(b);
1629     // }
1630     // stdout.close;
1631     // auto read = cast(char[]) std.file.read(file);
1632     // version (Windows)
1633     //     assert(read == "Hello, nice world number 42!\r\n1\r\n1\r\n1\r\n", read);
1634     // else
1635     //     assert(read == "Hello, nice world number 42!\n1\n1\n1\n", "["~read~"]");
1636 }
1637
1638 /**
1639  * Formatted read one line from stdin.
1640  */
1641 uint readf(A...)(in char[] format, A args)
1642 {
1643     return stdin.readf(format, args);
1644 }
1645
1646 unittest
1647 {
1648     float f;
1649     if (false) uint x = readf("%s", &f);
1650
1651     char a;
1652     wchar b;
1653     dchar c;
1654     if (false) readf("%s %s %s", &a,&b,&c);
1655 }
1656
1657 /**********************************
1658  * Read line from stream $(D fp).
1659  * Returns:
1660  *        $(D null) for end of file,
1661  *        $(D char[]) for line read from $(D fp), including terminating character
1662  * Params:
1663  *        $(D fp) = input stream
1664  *        $(D terminator) = line terminator, '\n' by default
1665  * Throws:
1666  *        $(D StdioException) on error
1667  * Example:
1668  *        Reads $(D stdin) and writes it to $(D stdout).
1669 ---
1670 import std.stdio;
1671
1672 int main()
1673 {
1674     char[] buf;
1675     while ((buf = readln()) != null)
1676         write(buf);
1677     return 0;
1678 }
1679 ---
1680 */
1681 string readln(dchar terminator = '\n')
1682 {
1683     return stdin.readln(terminator);
1684 }
1685
1686 /** ditto */
1687 size_t readln(ref char[] buf, dchar terminator = '\n')
1688 {
1689     return stdin.readln(buf, terminator);
1690 }
1691
1692 /*
1693  * Convenience function that forwards to $(D std.c.stdio.fopen)
1694  * with appropriately-constructed C-style strings.
1695  */
1696 private FILE* fopen(in char[] name, in char[] mode = "r")
1697 {
1698     const namez = toStringz(name), modez = toStringz(mode);
1699     return fopen64(namez, modez);
1700 }
1701
1702 version (Posix)
1703 {
1704     extern(C) FILE* popen(const char*, const char*);
1705
1706 /***********************************
1707  * Convenience function that forwards to $(D std.c.stdio.popen)
1708  * with appropriately-constructed C-style strings.
1709  */
1710     FILE* popen(in char[] name, in char[] mode = "r")
1711     {
1712         return popen(toStringz(name), toStringz(mode));
1713     }
1714 }
1715
1716 /*
1717  * Convenience function that forwards to $(D std.c.stdio.fwrite)
1718  * and throws an exception upon error
1719  */
1720 private void binaryWrite(T)(FILE* f, T obj)
1721 {
1722     immutable result = fwrite(obj.ptr, obj[0].sizeof, obj.length, f);
1723     if (result != obj.length) StdioException();
1724 }
1725
1726 /**
1727  * Iterates through the lines of a file by using $(D foreach).
1728  *
1729  * Example:
1730  *
1731 ---------
1732 void main()
1733 {
1734   foreach (string line; lines(stdin))
1735   {
1736     ... use line ...
1737   }
1738 }
1739 ---------
1740  The line terminator ('\n' by default) is part of the string read (it
1741 could be missing in the last line of the file). Several types are
1742 supported for $(D line), and the behavior of $(D lines)
1743 changes accordingly:
1744
1745 $(OL $(LI If $(D line) has type $(D string), $(D
1746 wstring), or $(D dstring), a new string of the respective type
1747 is allocated every read.) $(LI If $(D line) has type $(D
1748 char[]), $(D wchar[]), $(D dchar[]), the line's content
1749 will be reused (overwritten) across reads.) $(LI If $(D line)
1750 has type $(D immutable(ubyte)[]), the behavior is similar to
1751 case (1), except that no UTF checking is attempted upon input.) $(LI
1752 If $(D line) has type $(D ubyte[]), the behavior is
1753 similar to case (2), except that no UTF checking is attempted upon
1754 input.))
1755
1756 In all cases, a two-symbols versions is also accepted, in which case
1757 the first symbol (of integral type, e.g. $(D ulong) or $(D
1758 uint)) tracks the zero-based number of the current line.
1759
1760 Example:
1761 ----
1762   foreach (ulong i, string line; lines(stdin))
1763   {
1764     ... use line ...
1765   }
1766 ----
1767
1768  In case of an I/O error, an $(D StdioException) is thrown.
1769  */
1770
1771 struct lines
1772 {
1773     private File f;
1774     private dchar terminator = '\n';
1775     // private string fileName;  // Curretly, no use
1776
1777     this(File f, dchar terminator = '\n')
1778     {
1779         this.f = f;
1780         this.terminator = terminator;
1781     }
1782
1783     // Keep these commented lines for later, when Walter fixes the
1784     // exception model.
1785
1786 //     static lines opCall(string fName, dchar terminator = '\n')
1787 //     {
1788 //         auto f = enforce(fopen(fName),
1789 //             new StdioException("Cannot open file `"~fName~"' for reading"));
1790 //         auto result = lines(f, terminator);
1791 //         result.fileName = fName;
1792 //         return result;
1793 //     }
1794
1795     int opApply(D)(scope D dg)
1796     {
1797 //         scope(exit) {
1798 //             if (fileName.length && fclose(f))
1799 //                 StdioException("Could not close file `"~fileName~"'");
1800 //         }
1801         alias ParameterTypeTuple!(dg) Parms;
1802         static if (isSomeString!(Parms[$ - 1]))
1803         {
1804             enum bool duplicate = is(Parms[$ - 1] == string)
1805                 || is(Parms[$ - 1] == wstring) || is(Parms[$ - 1] == dstring);
1806             int result = 0;
1807             static if (is(Parms[$ - 1] : const(char)[]))
1808                 alias char C;
1809             else static if (is(Parms[$ - 1] : const(wchar)[]))
1810                 alias wchar C;
1811             else static if (is(Parms[$ - 1] : const(dchar)[]))
1812                 alias dchar C;
1813             C[] line;
1814             static if (Parms.length == 2)
1815                 Parms[0] i = 0;
1816             for (;;)
1817             {
1818                 if (!f.readln(line, terminator)) break;
1819                 auto copy = to!(Parms[$ - 1])(line);
1820                 static if (Parms.length == 2)
1821                 {
1822                     result = dg(i, copy);
1823                     ++i;
1824                 }
1825                 else
1826                 {
1827                     result = dg(copy);
1828                 }
1829                 if (result != 0) break;
1830             }
1831             return result;
1832         }
1833         else
1834         {
1835             // raw read
1836             return opApplyRaw(dg);
1837         }
1838     }
1839     // no UTF checking
1840     int opApplyRaw(D)(scope D dg)
1841     {
1842         alias ParameterTypeTuple!(dg) Parms;
1843         enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]);
1844         int result = 1;
1845         int c = void;
1846         FLOCK(f.p.handle);
1847         scope(exit) FUNLOCK(f.p.handle);
1848         ubyte[] buffer;
1849         static if (Parms.length == 2)
1850             Parms[0] line = 0;
1851         while ((c = FGETC(cast(_iobuf*)f.p.handle)) != -1)
1852         {
1853             buffer ~= to!(ubyte)(c);
1854             if (c == terminator)
1855             {
1856                 static if (duplicate)
1857                     auto arg = assumeUnique(buffer);
1858                 else
1859                     alias buffer arg;
1860                 // unlock the file while calling the delegate
1861                 FUNLOCK(f.p.handle);
1862                 scope(exit) FLOCK(f.p.handle);
1863                 static if (Parms.length == 1)
1864                 {
1865                     result = dg(arg);
1866                 }
1867                 else
1868                 {
1869                     result = dg(line, arg);
1870                     ++line;
1871                 }
1872                 if (result) break;
1873                 static if (!duplicate)
1874                     buffer.length = 0;
1875             }
1876         }
1877         // can only reach when FGETC returned -1
1878         if (!f.eof) throw new StdioException("Error in reading file"); // error occured
1879         return result;
1880     }
1881 }
1882
1883 unittest
1884 {
1885         //printf("Entering test at line %d\n", __LINE__);
1886     scope(failure) printf("Failed test at line %d\n", __LINE__);
1887     string file = "dmd-build-test.deleteme.txt";
1888     scope(exit) { std.file.remove(file); }
1889     alias TypeTuple!(string, wstring, dstring,
1890                      char[], wchar[], dchar[])
1891         TestedWith;
1892     foreach (T; TestedWith) {
1893         // test looping with an empty file
1894         std.file.write(file, "");
1895         auto f = File(file, "r");
1896         foreach (T line; lines(f))
1897         {
1898             assert(false);
1899         }
1900         f.close;
1901
1902         // test looping with a file with three lines
1903         std.file.write(file, "Line one\nline two\nline three\n");
1904         f.open(file, "r");
1905         uint i = 0;
1906         foreach (T line; lines(f))
1907         {
1908             if (i == 0) assert(line == "Line one\n");
1909             else if (i == 1) assert(line == "line two\n");
1910             else if (i == 2) assert(line == "line three\n");
1911             else assert(false);
1912             ++i;
1913         }
1914         f.close;
1915
1916         // test looping with a file with three lines, last without a newline
1917         std.file.write(file, "Line one\nline two\nline three");
1918         f.open(file, "r");
1919         i = 0;
1920         foreach (T line; lines(f))
1921         {
1922             if (i == 0) assert(line == "Line one\n");
1923             else if (i == 1) assert(line == "line two\n");
1924             else if (i == 2) assert(line == "line three");
1925             else assert(false);
1926             ++i;
1927         }
1928         f.close;
1929     }
1930
1931     // test with ubyte[] inputs
1932     //@@@BUG 2612@@@
1933     //alias TypeTuple!(immutable(ubyte)[], ubyte[]) TestedWith2;
1934     alias TypeTuple!(immutable(ubyte)[], ubyte[]) TestedWith2;
1935     foreach (T; TestedWith2) {
1936         // test looping with an empty file
1937         std.file.write(file, "");
1938         auto f = File(file, "r");
1939         foreach (T line; lines(f))
1940         {
1941             assert(false);
1942         }
1943         f.close;
1944
1945         // test looping with a file with three lines
1946         std.file.write(file, "Line one\nline two\nline three\n");
1947         f.open(file, "r");
1948         uint i = 0;
1949         foreach (T line; lines(f))
1950         {
1951             if (i == 0) assert(cast(char[]) line == "Line one\n");
1952             else if (i == 1) assert(cast(char[]) line == "line two\n",
1953                 T.stringof ~ " " ~ cast(char[]) line);
1954             else if (i == 2) assert(cast(char[]) line == "line three\n");
1955             else assert(false);
1956             ++i;
1957         }
1958         f.close;
1959
1960         // test looping with a file with three lines, last without a newline
1961         std.file.write(file, "Line one\nline two\nline three");
1962         f.open(file, "r");
1963         i = 0;
1964         foreach (T line; lines(f))
1965         {
1966             if (i == 0) assert(cast(char[]) line == "Line one\n");
1967             else if (i == 1) assert(cast(char[]) line == "line two\n");
1968             else if (i == 2) assert(cast(char[]) line == "line three");
1969             else assert(false);
1970             ++i;
1971         }
1972         f.close;
1973
1974     }
1975
1976     foreach (T; TypeTuple!(ubyte[]))
1977     {
1978         // test looping with a file with three lines, last without a newline
1979         // using a counter too this time
1980         std.file.write(file, "Line one\nline two\nline three");
1981         auto f = File(file, "r");
1982         uint i = 0;
1983         foreach (ulong j, T line; lines(f))
1984         {
1985             if (i == 0) assert(cast(char[]) line == "Line one\n");
1986             else if (i == 1) assert(cast(char[]) line == "line two\n");
1987             else if (i == 2) assert(cast(char[]) line == "line three");
1988             else assert(false);
1989             ++i;
1990         }
1991         f.close;
1992     }
1993 }
1994
1995 /**
1996 Iterates through a file a chunk at a time by using $(D
1997 foreach).
1998
1999 Example:
2000
2001 ---------
2002 void main()
2003 {
2004   foreach (ubyte[] buffer; chunks(stdin, 4096))
2005   {
2006     ... use buffer ...
2007   }
2008 }
2009 ---------
2010
2011 The content of $(D buffer) is reused across calls. In the
2012  example above, $(D buffer.length) is 4096 for all iterations,
2013  except for the last one, in which case $(D buffer.length) may
2014  be less than 4096 (but always greater than zero).
2015
2016  In case of an I/O error, an $(D StdioException) is thrown.
2017 */
2018
2019 struct chunks
2020 {
2021     private File f;
2022     private size_t size;
2023     // private string fileName; // Currently, no use
2024
2025     this(File f, size_t size)
2026     in
2027     {
2028         assert(size, "size must be larger than 0");
2029     }
2030     body
2031     {
2032         this.f = f;
2033         this.size = size;
2034     }
2035
2036 //     static chunks opCall(string fName, size_t size)
2037 //     {
2038 //         auto f = enforce(fopen(fName),
2039 //             new StdioException("Cannot open file `"~fName~"' for reading"));
2040 //         auto result = chunks(f, size);
2041 //         result.fileName  = fName;
2042 //         return result;
2043 //     }
2044
2045     int opApply(D)(scope D dg)
2046     {
2047         const maxStackSize = 1024 * 16;
2048         ubyte[] buffer = void;
2049         if (size < maxStackSize)
2050             buffer = (cast(ubyte*) alloca(size))[0 .. size];
2051         else
2052             buffer = new ubyte[size];
2053         size_t r = void;
2054         int result = 1;
2055         uint tally = 0;
2056         while ((r = core.stdc.stdio.fread(buffer.ptr,
2057                                 buffer[0].sizeof, size, f.p.handle)) > 0)
2058         {
2059             assert(r <= size);
2060             if (r != size)
2061             {
2062                 // error occured
2063                 if (!f.eof) throw new StdioException(null);
2064                 buffer.length = r;
2065             }
2066             static if (is(typeof(dg(tally, buffer)))) {
2067                 if ((result = dg(tally, buffer)) != 0) break;
2068             } else {
2069                 if ((result = dg(buffer)) != 0) break;
2070             }
2071             ++tally;
2072         }
2073         return result;
2074     }
2075 }
2076
2077 unittest
2078 {
2079         //printf("Entering test at line %d\n", __LINE__);
2080     scope(failure) printf("Failed test at line %d\n", __LINE__);
2081     string file = "dmd-build-test.deleteme.txt";
2082     scope(exit) { std.file.remove(file); }
2083     // test looping with an empty file
2084     std.file.write(file, "");
2085     auto f = File(file, "r");
2086     foreach (ubyte[] line; chunks(f, 4))
2087     {
2088         assert(false);
2089     }
2090     f.close;
2091
2092     // test looping with a file with three lines
2093     std.file.write(file, "Line one\nline two\nline three\n");
2094     f = File(file, "r");
2095     uint i = 0;
2096     foreach (ubyte[] line; chunks(f, 3))
2097     {
2098         if (i == 0) assert(cast(char[]) line == "Lin");
2099         else if (i == 1) assert(cast(char[]) line == "e o");
2100         else if (i == 2) assert(cast(char[]) line == "ne\n");
2101         else break;
2102         ++i;
2103     }
2104     f.close;
2105 }
2106
2107 /*********************
2108  * Thrown if I/O errors happen.
2109  */
2110 class StdioException : Exception
2111 {
2112     /// Operating system error code.
2113     uint errno;
2114
2115 /**
2116 Initialize with a message and an error code. */
2117     this(string message, uint e = .getErrno)
2118     {
2119         errno = e;
2120         version (Posix)
2121         {
2122             char[256] buf = void;
2123             version (linux)
2124             {
2125                 auto s = std.c.string.strerror_r(errno, buf.ptr, buf.length);
2126             }
2127             else
2128             {
2129                 std.c.string.strerror_r(errno, buf.ptr, buf.length);
2130                 auto s = buf.ptr;
2131             }
2132         }
2133         else
2134         {
2135             auto s = std.c.string.strerror(errno);
2136         }
2137         auto sysmsg = to!string(s);
2138         super(message ? message ~ "(" ~ sysmsg ~ ")" : sysmsg);
2139     }
2140
2141 /** Convenience functions that throw an $(D StdioException). */
2142     static void opCall(string msg)
2143     {
2144         throw new StdioException(msg);
2145     }
2146
2147 /// ditto
2148     static void opCall()
2149     {
2150         throw new StdioException(null, .getErrno);
2151     }
2152 }
2153
2154 extern(C) void std_stdio_static_this()
2155 {
2156     //Bind stdin, stdout, stderr
2157     __gshared File.Impl stdinImpl;
2158     stdinImpl.handle = core.stdc.stdio.stdin;
2159     .stdin.p = &stdinImpl;
2160     // stdout
2161     __gshared File.Impl stdoutImpl;
2162     stdoutImpl.handle = core.stdc.stdio.stdout;
2163     .stdout.p = &stdoutImpl;
2164     // stderr
2165     __gshared File.Impl stderrImpl;
2166     stderrImpl.handle = core.stdc.stdio.stderr;
2167     .stderr.p = &stderrImpl;
2168 }
2169
2170 //---------
2171 __gshared
2172 {
2173     File stdin;
2174     File stdout;
2175     File stderr;
2176 }
2177
2178 unittest
2179 {
2180     scope(failure) printf("Failed test at line %d\n", __LINE__);
2181     std.file.write("deleteme", "1 2\n4 1\n5 100");
2182     scope(exit) std.file.remove("deleteme");
2183     {
2184         File f = File("deleteme");
2185         scope(exit) f.close;
2186         auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ];
2187         uint i;
2188         foreach (e; f.byRecord!(int, int)("%s %s"))
2189         {
2190             //writeln(e);
2191             assert(e == t[i++]);
2192         }
2193         assert(i == 3);
2194     }
2195 }
2196
2197 // Private implementation of readln
2198 version (DIGITAL_MARS_STDIO)
2199 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n')
2200 {
2201     FLOCK(fps);
2202     scope(exit) FUNLOCK(fps);
2203
2204     /* Since fps is now locked, we can create an "unshared" version
2205      * of fp.
2206      */
2207     auto fp = cast(_iobuf*)fps;
2208
2209     if (__fhnd_info[fp._file] & FHND_WCHAR)
2210     {   /* Stream is in wide characters.
2211          * Read them and convert to chars.
2212          */
2213         static assert(wchar_t.sizeof == 2);
2214         auto app = appender(buf);
2215         app.clear();
2216         for (int c = void; (c = FGETWC(fp)) != -1; )
2217         {
2218             if ((c & ~0x7F) == 0)
2219             {   app.put(cast(char) c);
2220                 if (c == terminator)
2221                     break;
2222             }
2223             else
2224             {
2225                 if (c >= 0xD800 && c <= 0xDBFF)
2226                 {
2227                     int c2 = void;
2228                     if ((c2 = FGETWC(fp)) != -1 ||
2229                             c2 < 0xDC00 && c2 > 0xDFFF)
2230                     {
2231                         StdioException("unpaired UTF-16 surrogate");
2232                     }
2233                     c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
2234                 }
2235                 //std.utf.encode(buf, c);
2236                 app.put(cast(dchar)c);
2237             }
2238         }
2239         if (ferror(fps))
2240             StdioException();
2241         buf = app.data;
2242         return buf.length;
2243     }
2244
2245     auto sz = GC.sizeOf(buf.ptr);
2246     //auto sz = buf.length;
2247     buf = buf.ptr[0 .. sz];
2248     if (fp._flag & _IONBF)
2249     {
2250         /* Use this for unbuffered I/O, when running
2251          * across buffer boundaries, or for any but the common
2252          * cases.
2253          */
2254       L1:
2255         auto app = appender(buf);
2256         app.clear();
2257         if(app.capacity == 0)
2258             app.reserve(128); // get at least 128 bytes available
2259
2260         int c;
2261         while((c = FGETC(fp)) != -1) {
2262             app.put(cast(char) c);
2263             if(c == terminator) {
2264                 buf = app.data;
2265                 return buf.length;
2266             }
2267
2268         }
2269
2270         if (ferror(fps))
2271             StdioException();
2272         buf = app.data;
2273         return buf.length;
2274     }
2275     else
2276     {
2277         int u = fp._cnt;
2278         char* p = fp._ptr;
2279         int i;
2280         if (fp._flag & _IOTRAN)
2281         {   /* Translated mode ignores \r and treats ^Z as end-of-file
2282              */
2283             char c;
2284             while (1)
2285             {
2286                 if (i == u)                // if end of buffer
2287                     goto L1;        // give up
2288                 c = p[i];
2289                 i++;
2290                 if (c != '\r')
2291                 {
2292                     if (c == terminator)
2293                         break;
2294                     if (c != 0x1A)
2295                         continue;
2296                     goto L1;
2297                 }
2298                 else
2299                 {   if (i != u && p[i] == terminator)
2300                         break;
2301                     goto L1;
2302                 }
2303             }
2304             if (i > sz)
2305             {
2306                 buf = cast(char[])GC.malloc(i, GC.BlkAttr.NO_SCAN)[0 .. i];
2307             }
2308             if (i - 1)
2309                 memcpy(buf.ptr, p, i - 1);
2310             buf[i - 1] = cast(char)terminator;
2311             buf = buf[0 .. i];
2312             if (terminator == '\n' && c == '\r')
2313                 i++;
2314         }
2315         else
2316         {
2317             while (1)
2318             {
2319                 if (i == u)                // if end of buffer
2320                     goto L1;        // give up
2321                 auto c = p[i];
2322                 i++;
2323                 if (c == terminator)
2324                     break;
2325             }
2326             if (i > sz)
2327             {
2328                 buf = cast(char[])GC.malloc(i, GC.BlkAttr.NO_SCAN)[0 .. i];
2329             }
2330             memcpy(buf.ptr, p, i);
2331             buf = buf[0 .. i];
2332         }
2333         fp._cnt -= i;
2334         fp._ptr += i;
2335         return i;
2336     }
2337 }
2338
2339 version (GCC_IO)
2340 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n')
2341 {
2342     if (fwide(fps, 0) > 0)
2343     {   /* Stream is in wide characters.
2344          * Read them and convert to chars.
2345          */
2346         FLOCK(fps);
2347         scope(exit) FUNLOCK(fps);
2348         auto fp = cast(_iobuf*)fps;
2349         version (Windows)
2350         {
2351             buf.length = 0;
2352             for (int c = void; (c = FGETWC(fp)) != -1; )
2353             {
2354                 if ((c & ~0x7F) == 0)
2355                 {   buf ~= c;
2356                     if (c == terminator)
2357                         break;
2358                 }
2359                 else
2360                 {
2361                     if (c >= 0xD800 && c <= 0xDBFF)
2362                     {
2363                         int c2 = void;
2364                         if ((c2 = FGETWC(fp)) != -1 ||
2365                                 c2 < 0xDC00 && c2 > 0xDFFF)
2366                         {
2367                             StdioException("unpaired UTF-16 surrogate");
2368                         }
2369                         c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
2370                     }
2371                     std.utf.encode(buf, c);
2372                 }
2373             }
2374             if (ferror(fp))
2375                 StdioException();
2376             return buf.length;
2377         }
2378         else version (Posix)
2379         {
2380             buf.length = 0;
2381             for (int c; (c = FGETWC(fp)) != -1; )
2382             {
2383                 if ((c & ~0x7F) == 0)
2384                     buf ~= cast(char)c;
2385                 else
2386                     std.utf.encode(buf, cast(dchar)c);
2387                 if (c == terminator)
2388                     break;
2389             }
2390             if (ferror(fps))
2391                 StdioException();
2392             return buf.length;
2393         }
2394         else
2395         {
2396             static assert(0);
2397         }
2398     }
2399
2400     char *lineptr = null;
2401     size_t n = 0;
2402     auto s = getdelim(&lineptr, &n, terminator, fps);
2403     scope(exit) free(lineptr);
2404     if (s < 0)
2405     {
2406         if (ferror(fps))
2407             StdioException();
2408         buf.length = 0;                // end of file
2409         return 0;
2410     }
2411     buf = buf.ptr[0 .. GC.sizeOf(buf.ptr)];
2412     if (s <= buf.length)
2413     {
2414         buf.length = s;
2415         buf[] = lineptr[0 .. s];
2416     }
2417     else
2418     {
2419         buf = lineptr[0 .. s].dup;
2420     }
2421     return s;
2422 }
2423
2424 version (GENERIC_IO)
2425 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n')
2426 {
2427     FLOCK(fps);
2428     scope(exit) FUNLOCK(fps);
2429     auto fp = cast(_iobuf*)fps;
2430     if (fwide(fps, 0) > 0)
2431     {   /* Stream is in wide characters.
2432          * Read them and convert to chars.
2433          */
2434         version (Windows)
2435         {
2436             buf.length = 0;
2437             for (int c; (c = FGETWC(fp)) != -1; )
2438             {
2439                 if ((c & ~0x7F) == 0)
2440                 {   buf ~= c;
2441                     if (c == terminator)
2442                         break;
2443                 }
2444                 else
2445                 {
2446                     if (c >= 0xD800 && c <= 0xDBFF)
2447                     {
2448                         int c2 = void;
2449                         if ((c2 = FGETWC(fp)) != -1 ||
2450                                 c2 < 0xDC00 && c2 > 0xDFFF)
2451                         {
2452                             StdioException("unpaired UTF-16 surrogate");
2453                         }
2454                         c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
2455                     }
2456                     std.utf.encode(buf, c);
2457                 }
2458             }
2459             if (ferror(fp))
2460                 StdioException();
2461             return buf.length;
2462         }
2463         else version (Posix)
2464         {
2465             buf.length = 0;
2466             for (int c; (c = FGETWC(fp)) != -1; )
2467             {
2468                 if ((c & ~0x7F) == 0)
2469                     buf ~= cast(char)c;
2470                 else
2471                     std.utf.encode(buf, cast(dchar)c);
2472                 if (c == terminator)
2473                     break;
2474             }
2475             if (ferror(fps))
2476                 StdioException();
2477             return buf.length;
2478         }
2479         else
2480         {
2481             static assert(0);
2482         }
2483     }
2484
2485     // Narrow stream
2486     buf.length = 0;
2487     for (int c; (c = FGETC(fp)) != -1; )
2488     {
2489         buf ~= cast(char)c;
2490         if (c == terminator)
2491             break;
2492     }
2493     if (ferror(fps))
2494         StdioException();
2495     return buf.length;
2496 }
2497
2498
2499 /** Experimental network access via the File interface
2500
2501         Opens a TCP connection to the given host and port, then returns
2502         a File struct with read and write access through the same interface
2503         as any other file (meaning writef and the byLine ranges work!).
2504
2505         Authors:
2506                 Adam D. Ruppe
2507
2508         Bugs:
2509                 Only works on Linux
2510 */
2511 version(linux) {
2512     static import linux = std.c.linux.linux;
2513     static import sock = std.c.linux.socket;
2514
2515     File openNetwork(string host, ushort port) {
2516         auto h = enforce( sock.gethostbyname(std.string.toStringz(host)),
2517             new StdioException("gethostbyname"));
2518
2519         int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0);
2520         enforce(s != -1, new StdioException("socket"));
2521
2522         scope(failure) {
2523             linux.close(s); // want to make sure it doesn't dangle if
2524                             // something throws. Upon normal exit, the
2525                             // File struct's reference counting takes
2526                             // care of closing, so we don't need to
2527                             // worry about success
2528         }
2529
2530         sock.sockaddr_in addr;
2531
2532         addr.sin_family = sock.AF_INET;
2533         addr.sin_port = sock.htons(port);
2534         std.c.string.memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length);
2535
2536         enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
2537             new StdioException("Connect failed"));
2538
2539         FILE* fp = enforce(fdopen(s, "w+".ptr));
2540
2541         File f;
2542         f.p = new File.Impl(fp, 1, host ~ ":" ~ to!string(port));
2543
2544         return f;
2545     }
2546 }
Note: See TracBrowser for help on using the browser.