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 2284, 67.9 kB (checked in by andrei, 14 years ago)

Added size property to File

  • 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     std.file.write("deleteme", "1 2 3");
1258     auto f = File("deleteme");
1259     assert(f.size == 5);
1260     assert(f.tell == 0);
1261 }
1262
1263 struct LockingTextReader
1264 {
1265     private File _f;
1266     private dchar _crt;
1267
1268     this(File f)
1269     {
1270         enforce(f.isOpen);
1271         _f = f;
1272         FLOCK(_f.p.handle);
1273     }
1274
1275     this(this)
1276     {
1277         FLOCK(_f.p.handle);
1278     }
1279
1280     ~this()
1281     {
1282         // File locking has its own reference count
1283         if (_f.isOpen) FUNLOCK(_f.p.handle);
1284     }
1285
1286     void opAssign(LockingTextReader r)
1287     {
1288         swap(this, r);
1289     }
1290
1291     @property bool empty()
1292     {
1293         if (!_f.isOpen || _f.eof) return true;
1294         if (_crt == _crt.init)
1295         {
1296             _crt = FGETC(cast(_iobuf*) _f.p.handle);
1297             if (_crt == -1)
1298             {
1299                 clear(_f);
1300                 return true;
1301             }
1302             else
1303             {
1304                 enforce(ungetc(_crt, cast(FILE*) _f.p.handle) == _crt);
1305             }
1306         }
1307         return false;
1308     }
1309
1310     dchar front()
1311     {
1312         enforce(!empty);
1313         return _crt;
1314     }
1315
1316     void popFront()
1317     {
1318         enforce(!empty);
1319         if (FGETC(cast(_iobuf*) _f.p.handle) == -1)
1320         {
1321             enforce(_f.eof);
1322         }
1323         _crt = _crt.init;
1324     }
1325
1326     // void unget(dchar c)
1327     // {
1328     //     ungetc(c, cast(FILE*) _f.p.handle);
1329     // }
1330 }
1331
1332 unittest
1333 {
1334     std.file.write("deleteme", "1 2 3");
1335     int x, y;
1336     auto f = File("deleteme");
1337     f.readf("%s ", &x);
1338     assert(x == 1);
1339     f.readf("%d ", &x);
1340     assert(x == 2);
1341     f.readf("%d ", &x);
1342     assert(x == 3);
1343     //pragma(msg, "--- todo: readf ---");
1344 }
1345
1346 private
1347 void writefx(FILE* fps, TypeInfo[] arguments, void* argptr, int newline=false)
1348 {
1349     int orientation = fwide(fps, 0);    // move this inside the lock?
1350
1351     /* Do the file stream locking at the outermost level
1352      * rather than character by character.
1353      */
1354     FLOCK(fps);
1355     scope(exit) FUNLOCK(fps);
1356
1357     auto fp = cast(_iobuf*)fps;     // fp is locked version
1358
1359     if (orientation <= 0)                // byte orientation or no orientation
1360     {
1361         void putc(dchar c)
1362         {
1363             if (c <= 0x7F)
1364             {
1365                 FPUTC(c, fp);
1366             }
1367             else
1368             {
1369                 char[4] buf = void;
1370                 foreach (i; 0 .. std.utf.toUTF8(buf, c).length)
1371                     FPUTC(buf[i], fp);
1372             }
1373         }
1374
1375         std.format.doFormat(&putc, arguments, argptr);
1376         if (newline)
1377             FPUTC('\n', fp);
1378     }
1379     else if (orientation > 0)                // wide orientation
1380     {
1381         version (Windows)
1382         {
1383             void putcw(dchar c)
1384             {
1385                 assert(isValidDchar(c));
1386                 if (c <= 0xFFFF)
1387                 {
1388                     FPUTWC(c, fp);
1389                 }
1390                 else
1391                 {
1392                     FPUTWC(cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) +
1393                                     0xD800), fp);
1394                     FPUTWC(cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00), fp);
1395                 }
1396             }
1397         }
1398         else version (Posix)
1399         {
1400             void putcw(dchar c)
1401             {
1402                 FPUTWC(c, fp);
1403             }
1404         }
1405         else
1406         {
1407             static assert(0);
1408         }
1409
1410         std.format.doFormat(&putcw, arguments, argptr);
1411         if (newline)
1412             FPUTWC('\n', fp);
1413     }
1414 }
1415
1416 template isStreamingDevice(T)
1417 {
1418     enum isStreamingDevice = is(T : FILE*) ||
1419         is(T : File);
1420 }
1421
1422 /***********************************
1423 For each argument $(D arg) in $(D args), format the argument (as per
1424 $(LINK2 std_conv.html, to!(string)(arg))) and write the resulting
1425 string to $(D args[0]). A call without any arguments will fail to
1426 compile.
1427
1428 Throws: In case of an I/O error, throws an $(D StdioException).
1429  */
1430 void write(T...)(T args) if (!is(T[0] : File))
1431 {
1432     stdout.write(args);
1433 }
1434
1435 unittest
1436 {
1437     //printf("Entering test at line %d\n", __LINE__);
1438     scope(failure) printf("Failed test at line %d\n", __LINE__);
1439     void[] buf;
1440     write(buf);
1441     // test write
1442     string file = "dmd-build-test.deleteme.txt";
1443     auto f = File(file, "w");
1444 //    scope(exit) { std.file.remove(file); }
1445      f.write("Hello, ",  "world number ", 42, "!");
1446      f.close;
1447      assert(cast(char[]) std.file.read(file) == "Hello, world number 42!");
1448     // // test write on stdout
1449     //auto saveStdout = stdout;
1450     //scope(exit) stdout = saveStdout;
1451     //stdout.open(file, "w");
1452     Object obj;
1453     //write("Hello, ",  "world number ", 42, "! ", obj);
1454     //stdout.close;
1455     // auto result = cast(char[]) std.file.read(file);
1456     // assert(result == "Hello, world number 42! null", result);
1457 }
1458
1459 /***********************************
1460  * Equivalent to $(D write(args, '\n')).  Calling $(D writeln) without
1461  * arguments is valid and just prints a newline to the standard
1462  * output.
1463  */
1464 void writeln(T...)(T args) if (T.length == 0)
1465 {
1466     enforce(fputc('\n', .stdout.p.handle) == '\n');
1467 }
1468
1469 unittest
1470 {
1471     // Just make sure the call compiles
1472     if (false) writeln();
1473 }
1474
1475 /// ditto
1476 void writeln(T...)(T args)
1477 if (T.length == 1 && is(typeof(args[0]) : const(char)[]))
1478 {
1479     enforce(fprintf(.stdout.p.handle, "%.*s\n",
1480                     cast(int) args[0].length, args[0].ptr) >= 0);
1481 }
1482
1483 unittest
1484 {
1485     if (false) writeln("wyda");
1486 }
1487
1488 /// Ditto
1489 void writeln(T...)(T args)
1490 if (T.length > 1 || T.length == 1 && !is(typeof(args[0]) : const(char)[]))
1491 {
1492     stdout.write(args, '\n');
1493 }
1494
1495 unittest
1496 {
1497         //printf("Entering test at line %d\n", __LINE__);
1498     scope(failure) printf("Failed test at line %d\n", __LINE__);
1499     // test writeln
1500     string file = "dmd-build-test.deleteme.txt";
1501     auto f = File(file, "w");
1502     scope(exit) { std.file.remove(file); }
1503     f.writeln("Hello, ",  "world number ", 42, "!");
1504     f.close;
1505     version (Windows)
1506         assert(cast(char[]) std.file.read(file) ==
1507                 "Hello, world number 42!\r\n");
1508     else
1509         assert(cast(char[]) std.file.read(file) ==
1510                 "Hello, world number 42!\n");
1511     // test writeln on stdout
1512     auto saveStdout = stdout;
1513     scope(exit) stdout = saveStdout;
1514     stdout.open(file, "w");
1515     writeln("Hello, ",  "world number ", 42, "!");
1516     stdout.close;
1517     version (Windows)
1518         assert(cast(char[]) std.file.read(file) ==
1519                 "Hello, world number 42!\r\n");
1520     else
1521         assert(cast(char[]) std.file.read(file) ==
1522                 "Hello, world number 42!\n");
1523 }
1524
1525 /***********************************
1526  * If the first argument $(D args[0]) is a $(D FILE*), use
1527  * $(LINK2 std_format.html#format-string, the format specifier) in
1528  * $(D args[1]) to control the formatting of $(D
1529  * args[2..$]), and write the resulting string to $(D args[0]).
1530  * If $(D arg[0]) is not a $(D FILE*), the call is
1531  * equivalent to $(D writef(stdout, args)).
1532  *
1533
1534 IMPORTANT:
1535
1536 New behavior starting with D 2.006: unlike previous versions,
1537 $(D writef) (and also $(D writefln)) only scans its first
1538 string argument for format specifiers, but not subsequent string
1539 arguments. This decision was made because the old behavior made it
1540 unduly hard to simply print string variables that occasionally
1541 embedded percent signs.
1542
1543 Also new starting with 2.006 is support for positional
1544 parameters with
1545 $(LINK2 http://opengroup.org/onlinepubs/009695399/functions/printf.html,
1546 POSIX) syntax.
1547
1548 Example:
1549
1550 -------------------------
1551 writef("Date: %2$s %1$s", "October", 5); // "Date: 5 October"
1552 ------------------------
1553
1554 The positional and non-positional styles can be mixed in the same
1555 format string. (POSIX leaves this behavior undefined.) The internal
1556 counter for non-positional parameters tracks the popFront parameter after
1557 the largest positional parameter already used.
1558
1559 New starting with 2.008: raw format specifiers. Using the "%r"
1560 specifier makes $(D writef) simply write the binary
1561 representation of the argument. Use "%-r" to write numbers in little
1562 endian format, "%+r" to write numbers in big endian format, and "%r"
1563 to write numbers in platform-native format.
1564
1565 */
1566
1567 void writef(T...)(T args)
1568 {
1569     stdout.writef(args);
1570 }
1571
1572 unittest
1573 {
1574     //printf("Entering test at line %d\n", __LINE__);
1575     scope(failure) printf("Failed test at line %d\n", __LINE__);
1576     // test writef
1577     string file = "dmd-build-test.deleteme.txt";
1578     auto f = File(file, "w");
1579     scope(exit) { std.file.remove(file); }
1580     f.writef("Hello, %s world number %s!", "nice", 42);
1581     f.close;
1582     assert(cast(char[]) std.file.read(file) ==  "Hello, nice world number 42!");
1583     // test write on stdout
1584     auto saveStdout = stdout;
1585     scope(exit) stdout = saveStdout;
1586     stdout.open(file, "w");
1587     writef("Hello, %s world number %s!", "nice", 42);
1588     stdout.close;
1589     assert(cast(char[]) std.file.read(file) == "Hello, nice world number 42!");
1590 }
1591
1592 /***********************************
1593  * Equivalent to $(D writef(args, '\n')).
1594  */
1595 void writefln(T...)(T args)
1596 {
1597     stdout.writefln(args);
1598 }
1599
1600 unittest
1601 {
1602         //printf("Entering test at line %d\n", __LINE__);
1603     scope(failure) printf("Failed test at line %d\n", __LINE__);
1604     // test writefln
1605     string file = "dmd-build-test.deleteme.txt";
1606     auto f = File(file, "w");
1607     scope(exit) { std.file.remove(file); }
1608     f.writefln("Hello, %s world number %s!", "nice", 42);
1609     f.close;
1610     version (Windows)
1611         assert(cast(char[]) std.file.read(file) ==
1612                 "Hello, nice world number 42!\r\n");
1613     else
1614         assert(cast(char[]) std.file.read(file) ==
1615                 "Hello, nice world number 42!\n",
1616                 cast(char[]) std.file.read(file));
1617     // test write on stdout
1618     // auto saveStdout = stdout;
1619     // scope(exit) stdout = saveStdout;
1620     // stdout.open(file, "w");
1621     // assert(stdout.isOpen);
1622     // writefln("Hello, %s world number %s!", "nice", 42);
1623     // foreach (F ; TypeTuple!(ifloat, idouble, ireal))
1624     // {
1625     //     F a = 5i;
1626     //     F b = a % 2;
1627     //     writeln(b);
1628     // }
1629     // stdout.close;
1630     // auto read = cast(char[]) std.file.read(file);
1631     // version (Windows)
1632     //     assert(read == "Hello, nice world number 42!\r\n1\r\n1\r\n1\r\n", read);
1633     // else
1634     //     assert(read == "Hello, nice world number 42!\n1\n1\n1\n", "["~read~"]");
1635 }
1636
1637 /**
1638  * Formatted read one line from stdin.
1639  */
1640 uint readf(A...)(in char[] format, A args)
1641 {
1642     return stdin.readf(format, args);
1643 }
1644
1645 unittest
1646 {
1647     float f;
1648     if (false) uint x = readf("%s", &f);
1649 }
1650
1651 /**********************************
1652  * Read line from stream $(D fp).
1653  * Returns:
1654  *        $(D null) for end of file,
1655  *        $(D char[]) for line read from $(D fp), including terminating character
1656  * Params:
1657  *        $(D fp) = input stream
1658  *        $(D terminator) = line terminator, '\n' by default
1659  * Throws:
1660  *        $(D StdioException) on error
1661  * Example:
1662  *        Reads $(D stdin) and writes it to $(D stdout).
1663 ---
1664 import std.stdio;
1665
1666 int main()
1667 {
1668     char[] buf;
1669     while ((buf = readln()) != null)
1670         write(buf);
1671     return 0;
1672 }
1673 ---
1674 */
1675 string readln(dchar terminator = '\n')
1676 {
1677     return stdin.readln(terminator);
1678 }
1679
1680 /** ditto */
1681 size_t readln(ref char[] buf, dchar terminator = '\n')
1682 {
1683     return stdin.readln(buf, terminator);
1684 }
1685
1686 /*
1687  * Convenience function that forwards to $(D std.c.stdio.fopen)
1688  * with appropriately-constructed C-style strings.
1689  */
1690 private FILE* fopen(in char[] name, in char[] mode = "r")
1691 {
1692     const namez = toStringz(name), modez = toStringz(mode);
1693     return fopen64(namez, modez);
1694 }
1695
1696 version (Posix)
1697 {
1698     extern(C) FILE* popen(const char*, const char*);
1699
1700 /***********************************
1701  * Convenience function that forwards to $(D std.c.stdio.popen)
1702  * with appropriately-constructed C-style strings.
1703  */
1704     FILE* popen(in char[] name, in char[] mode = "r")
1705     {
1706         return popen(toStringz(name), toStringz(mode));
1707     }
1708 }
1709
1710 /*
1711  * Convenience function that forwards to $(D std.c.stdio.fwrite)
1712  * and throws an exception upon error
1713  */
1714 private void binaryWrite(T)(FILE* f, T obj)
1715 {
1716     immutable result = fwrite(obj.ptr, obj[0].sizeof, obj.length, f);
1717     if (result != obj.length) StdioException();
1718 }
1719
1720 /**
1721  * Iterates through the lines of a file by using $(D foreach).
1722  *
1723  * Example:
1724  *
1725 ---------
1726 void main()
1727 {
1728   foreach (string line; lines(stdin))
1729   {
1730     ... use line ...
1731   }
1732 }
1733 ---------
1734  The line terminator ('\n' by default) is part of the string read (it
1735 could be missing in the last line of the file). Several types are
1736 supported for $(D line), and the behavior of $(D lines)
1737 changes accordingly:
1738
1739 $(OL $(LI If $(D line) has type $(D string), $(D
1740 wstring), or $(D dstring), a new string of the respective type
1741 is allocated every read.) $(LI If $(D line) has type $(D
1742 char[]), $(D wchar[]), $(D dchar[]), the line's content
1743 will be reused (overwritten) across reads.) $(LI If $(D line)
1744 has type $(D immutable(ubyte)[]), the behavior is similar to
1745 case (1), except that no UTF checking is attempted upon input.) $(LI
1746 If $(D line) has type $(D ubyte[]), the behavior is
1747 similar to case (2), except that no UTF checking is attempted upon
1748 input.))
1749
1750 In all cases, a two-symbols versions is also accepted, in which case
1751 the first symbol (of integral type, e.g. $(D ulong) or $(D
1752 uint)) tracks the zero-based number of the current line.
1753
1754 Example:
1755 ----
1756   foreach (ulong i, string line; lines(stdin))
1757   {
1758     ... use line ...
1759   }
1760 ----
1761
1762  In case of an I/O error, an $(D StdioException) is thrown.
1763  */
1764
1765 struct lines
1766 {
1767     private File f;
1768     private dchar terminator = '\n';
1769     // private string fileName;  // Curretly, no use
1770
1771     this(File f, dchar terminator = '\n')
1772     {
1773         this.f = f;
1774         this.terminator = terminator;
1775     }
1776
1777     // Keep these commented lines for later, when Walter fixes the
1778     // exception model.
1779
1780 //     static lines opCall(string fName, dchar terminator = '\n')
1781 //     {
1782 //         auto f = enforce(fopen(fName),
1783 //             new StdioException("Cannot open file `"~fName~"' for reading"));
1784 //         auto result = lines(f, terminator);
1785 //         result.fileName = fName;
1786 //         return result;
1787 //     }
1788
1789     int opApply(D)(scope D dg)
1790     {
1791 //         scope(exit) {
1792 //             if (fileName.length && fclose(f))
1793 //                 StdioException("Could not close file `"~fileName~"'");
1794 //         }
1795         alias ParameterTypeTuple!(dg) Parms;
1796         static if (isSomeString!(Parms[$ - 1]))
1797         {
1798             enum bool duplicate = is(Parms[$ - 1] == string)
1799                 || is(Parms[$ - 1] == wstring) || is(Parms[$ - 1] == dstring);
1800             int result = 0;
1801             static if (is(Parms[$ - 1] : const(char)[]))
1802                 alias char C;
1803             else static if (is(Parms[$ - 1] : const(wchar)[]))
1804                 alias wchar C;
1805             else static if (is(Parms[$ - 1] : const(dchar)[]))
1806                 alias dchar C;
1807             C[] line;
1808             static if (Parms.length == 2)
1809                 Parms[0] i = 0;
1810             for (;;)
1811             {
1812                 if (!f.readln(line, terminator)) break;
1813                 auto copy = to!(Parms[$ - 1])(line);
1814                 static if (Parms.length == 2)
1815                 {
1816                     result = dg(i, copy);
1817                     ++i;
1818                 }
1819                 else
1820                 {
1821                     result = dg(copy);
1822                 }
1823                 if (result != 0) break;
1824             }
1825             return result;
1826         }
1827         else
1828         {
1829             // raw read
1830             return opApplyRaw(dg);
1831         }
1832     }
1833     // no UTF checking
1834     int opApplyRaw(D)(scope D dg)
1835     {
1836         alias ParameterTypeTuple!(dg) Parms;
1837         enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]);
1838         int result = 1;
1839         int c = void;
1840         FLOCK(f.p.handle);
1841         scope(exit) FUNLOCK(f.p.handle);
1842         ubyte[] buffer;
1843         static if (Parms.length == 2)
1844             Parms[0] line = 0;
1845         while ((c = FGETC(cast(_iobuf*)f.p.handle)) != -1)
1846         {
1847             buffer ~= to!(ubyte)(c);
1848             if (c == terminator)
1849             {
1850                 static if (duplicate)
1851                     auto arg = assumeUnique(buffer);
1852                 else
1853                     alias buffer arg;
1854                 // unlock the file while calling the delegate
1855                 FUNLOCK(f.p.handle);
1856                 scope(exit) FLOCK(f.p.handle);
1857                 static if (Parms.length == 1)
1858                 {
1859                     result = dg(arg);
1860                 }
1861                 else
1862                 {
1863                     result = dg(line, arg);
1864                     ++line;
1865                 }
1866                 if (result) break;
1867                 static if (!duplicate)
1868                     buffer.length = 0;
1869             }
1870         }
1871         // can only reach when FGETC returned -1
1872         if (!f.eof) throw new StdioException("Error in reading file"); // error occured
1873         return result;
1874     }
1875 }
1876
1877 unittest
1878 {
1879         //printf("Entering test at line %d\n", __LINE__);
1880     scope(failure) printf("Failed test at line %d\n", __LINE__);
1881     string file = "dmd-build-test.deleteme.txt";
1882     scope(exit) { std.file.remove(file); }
1883     alias TypeTuple!(string, wstring, dstring,
1884                      char[], wchar[], dchar[])
1885         TestedWith;
1886     foreach (T; TestedWith) {
1887         // test looping with an empty file
1888         std.file.write(file, "");
1889         auto f = File(file, "r");
1890         foreach (T line; lines(f))
1891         {
1892             assert(false);
1893         }
1894         f.close;
1895
1896         // test looping with a file with three lines
1897         std.file.write(file, "Line one\nline two\nline three\n");
1898         f.open(file, "r");
1899         uint i = 0;
1900         foreach (T line; lines(f))
1901         {
1902             if (i == 0) assert(line == "Line one\n");
1903             else if (i == 1) assert(line == "line two\n");
1904             else if (i == 2) assert(line == "line three\n");
1905             else assert(false);
1906             ++i;
1907         }
1908         f.close;
1909
1910         // test looping with a file with three lines, last without a newline
1911         std.file.write(file, "Line one\nline two\nline three");
1912         f.open(file, "r");
1913         i = 0;
1914         foreach (T line; lines(f))
1915         {
1916             if (i == 0) assert(line == "Line one\n");
1917             else if (i == 1) assert(line == "line two\n");
1918             else if (i == 2) assert(line == "line three");
1919             else assert(false);
1920             ++i;
1921         }
1922         f.close;
1923     }
1924
1925     // test with ubyte[] inputs
1926     //@@@BUG 2612@@@
1927     //alias TypeTuple!(immutable(ubyte)[], ubyte[]) TestedWith2;
1928     alias TypeTuple!(immutable(ubyte)[], ubyte[]) TestedWith2;
1929     foreach (T; TestedWith2) {
1930         // test looping with an empty file
1931         std.file.write(file, "");
1932         auto f = File(file, "r");
1933         foreach (T line; lines(f))
1934         {
1935             assert(false);
1936         }
1937         f.close;
1938
1939         // test looping with a file with three lines
1940         std.file.write(file, "Line one\nline two\nline three\n");
1941         f.open(file, "r");
1942         uint i = 0;
1943         foreach (T line; lines(f))
1944         {
1945             if (i == 0) assert(cast(char[]) line == "Line one\n");
1946             else if (i == 1) assert(cast(char[]) line == "line two\n",
1947                 T.stringof ~ " " ~ cast(char[]) line);
1948             else if (i == 2) assert(cast(char[]) line == "line three\n");
1949             else assert(false);
1950             ++i;
1951         }
1952         f.close;
1953
1954         // test looping with a file with three lines, last without a newline
1955         std.file.write(file, "Line one\nline two\nline three");
1956         f.open(file, "r");
1957         i = 0;
1958         foreach (T line; lines(f))
1959         {
1960             if (i == 0) assert(cast(char[]) line == "Line one\n");
1961             else if (i == 1) assert(cast(char[]) line == "line two\n");
1962             else if (i == 2) assert(cast(char[]) line == "line three");
1963             else assert(false);
1964             ++i;
1965         }
1966         f.close;
1967
1968     }
1969
1970     foreach (T; TypeTuple!(ubyte[]))
1971     {
1972         // test looping with a file with three lines, last without a newline
1973         // using a counter too this time
1974         std.file.write(file, "Line one\nline two\nline three");
1975         auto f = File(file, "r");
1976         uint i = 0;
1977         foreach (ulong j, T line; lines(f))
1978         {
1979             if (i == 0) assert(cast(char[]) line == "Line one\n");
1980             else if (i == 1) assert(cast(char[]) line == "line two\n");
1981             else if (i == 2) assert(cast(char[]) line == "line three");
1982             else assert(false);
1983             ++i;
1984         }
1985         f.close;
1986     }
1987 }
1988
1989 /**
1990 Iterates through a file a chunk at a time by using $(D
1991 foreach).
1992
1993 Example:
1994
1995 ---------
1996 void main()
1997 {
1998   foreach (ubyte[] buffer; chunks(stdin, 4096))
1999   {
2000     ... use buffer ...
2001   }
2002 }
2003 ---------
2004
2005 The content of $(D buffer) is reused across calls. In the
2006  example above, $(D buffer.length) is 4096 for all iterations,
2007  except for the last one, in which case $(D buffer.length) may
2008  be less than 4096 (but always greater than zero).
2009
2010  In case of an I/O error, an $(D StdioException) is thrown.
2011 */
2012
2013 struct chunks
2014 {
2015     private File f;
2016     private size_t size;
2017     // private string fileName; // Currently, no use
2018
2019     this(File f, size_t size)
2020     in
2021     {
2022         assert(size, "size must be larger than 0");
2023     }
2024     body
2025     {
2026         this.f = f;
2027         this.size = size;
2028     }
2029
2030 //     static chunks opCall(string fName, size_t size)
2031 //     {
2032 //         auto f = enforce(fopen(fName),
2033 //             new StdioException("Cannot open file `"~fName~"' for reading"));
2034 //         auto result = chunks(f, size);
2035 //         result.fileName  = fName;
2036 //         return result;
2037 //     }
2038
2039     int opApply(D)(scope D dg)
2040     {
2041         const maxStackSize = 1024 * 16;
2042         ubyte[] buffer = void;
2043         if (size < maxStackSize)
2044             buffer = (cast(ubyte*) alloca(size))[0 .. size];
2045         else
2046             buffer = new ubyte[size];
2047         size_t r = void;
2048         int result = 1;
2049         uint tally = 0;
2050         while ((r = core.stdc.stdio.fread(buffer.ptr,
2051                                 buffer[0].sizeof, size, f.p.handle)) > 0)
2052         {
2053             assert(r <= size);
2054             if (r != size)
2055             {
2056                 // error occured
2057                 if (!f.eof) throw new StdioException(null);
2058                 buffer.length = r;
2059             }
2060             static if (is(typeof(dg(tally, buffer)))) {
2061                 if ((result = dg(tally, buffer)) != 0) break;
2062             } else {
2063                 if ((result = dg(buffer)) != 0) break;
2064             }
2065             ++tally;
2066         }
2067         return result;
2068     }
2069 }
2070
2071 unittest
2072 {
2073         //printf("Entering test at line %d\n", __LINE__);
2074     scope(failure) printf("Failed test at line %d\n", __LINE__);
2075     string file = "dmd-build-test.deleteme.txt";
2076     scope(exit) { std.file.remove(file); }
2077     // test looping with an empty file
2078     std.file.write(file, "");
2079     auto f = File(file, "r");
2080     foreach (ubyte[] line; chunks(f, 4))
2081     {
2082         assert(false);
2083     }
2084     f.close;
2085
2086     // test looping with a file with three lines
2087     std.file.write(file, "Line one\nline two\nline three\n");
2088     f = File(file, "r");
2089     uint i = 0;
2090     foreach (ubyte[] line; chunks(f, 3))
2091     {
2092         if (i == 0) assert(cast(char[]) line == "Lin");
2093         else if (i == 1) assert(cast(char[]) line == "e o");
2094         else if (i == 2) assert(cast(char[]) line == "ne\n");
2095         else break;
2096         ++i;
2097     }
2098     f.close;
2099 }
2100
2101 /*********************
2102  * Thrown if I/O errors happen.
2103  */
2104 class StdioException : Exception
2105 {
2106     /// Operating system error code.
2107     uint errno;
2108
2109 /**
2110 Initialize with a message and an error code. */
2111     this(string message, uint e = .getErrno)
2112     {
2113         errno = e;
2114         version (Posix)
2115         {
2116             char[256] buf = void;
2117             version (linux)
2118             {
2119                 auto s = std.c.string.strerror_r(errno, buf.ptr, buf.length);
2120             }
2121             else
2122             {
2123                 std.c.string.strerror_r(errno, buf.ptr, buf.length);
2124                 auto s = buf.ptr;
2125             }
2126         }
2127         else
2128         {
2129             auto s = std.c.string.strerror(errno);
2130         }
2131         auto sysmsg = to!string(s);
2132         super(message ? message ~ "(" ~ sysmsg ~ ")" : sysmsg);
2133     }
2134
2135 /** Convenience functions that throw an $(D StdioException). */
2136     static void opCall(string msg)
2137     {
2138         throw new StdioException(msg);
2139     }
2140
2141 /// ditto
2142     static void opCall()
2143     {
2144         throw new StdioException(null, .getErrno);
2145     }
2146 }
2147
2148 extern(C) void std_stdio_static_this()
2149 {
2150     //Bind stdin, stdout, stderr
2151     __gshared File.Impl stdinImpl;
2152     stdinImpl.handle = core.stdc.stdio.stdin;
2153     .stdin.p = &stdinImpl;
2154     // stdout
2155     __gshared File.Impl stdoutImpl;
2156     stdoutImpl.handle = core.stdc.stdio.stdout;
2157     .stdout.p = &stdoutImpl;
2158     // stderr
2159     __gshared File.Impl stderrImpl;
2160     stderrImpl.handle = core.stdc.stdio.stderr;
2161     .stderr.p = &stderrImpl;
2162 }
2163
2164 //---------
2165 __gshared
2166 {
2167     File stdin;
2168     File stdout;
2169     File stderr;
2170 }
2171
2172 unittest
2173 {
2174     scope(failure) printf("Failed test at line %d\n", __LINE__);
2175     std.file.write("deleteme", "1 2\n4 1\n5 100");
2176     scope(exit) std.file.remove("deleteme");
2177     {
2178         File f = File("deleteme");
2179         scope(exit) f.close;
2180         auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ];
2181         uint i;
2182         foreach (e; f.byRecord!(int, int)("%s %s"))
2183         {
2184             //writeln(e);
2185             assert(e == t[i++]);
2186         }
2187         assert(i == 3);
2188     }
2189 }
2190
2191 // Private implementation of readln
2192 version (DIGITAL_MARS_STDIO)
2193 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n')
2194 {
2195     FLOCK(fps);
2196     scope(exit) FUNLOCK(fps);
2197
2198     /* Since fps is now locked, we can create an "unshared" version
2199      * of fp.
2200      */
2201     auto fp = cast(_iobuf*)fps;
2202
2203     if (__fhnd_info[fp._file] & FHND_WCHAR)
2204     {   /* Stream is in wide characters.
2205          * Read them and convert to chars.
2206          */
2207         static assert(wchar_t.sizeof == 2);
2208         auto app = appender(buf);
2209         app.clear();
2210         for (int c = void; (c = FGETWC(fp)) != -1; )
2211         {
2212             if ((c & ~0x7F) == 0)
2213             {   app.put(cast(char) c);
2214                 if (c == terminator)
2215                     break;
2216             }
2217             else
2218             {
2219                 if (c >= 0xD800 && c <= 0xDBFF)
2220                 {
2221                     int c2 = void;
2222                     if ((c2 = FGETWC(fp)) != -1 ||
2223                             c2 < 0xDC00 && c2 > 0xDFFF)
2224                     {
2225                         StdioException("unpaired UTF-16 surrogate");
2226                     }
2227                     c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
2228                 }
2229                 //std.utf.encode(buf, c);
2230                 app.put(cast(dchar)c);
2231             }
2232         }
2233         if (ferror(fps))
2234             StdioException();
2235         buf = app.data;
2236         return buf.length;
2237     }
2238
2239     auto sz = GC.sizeOf(buf.ptr);
2240     //auto sz = buf.length;
2241     buf = buf.ptr[0 .. sz];
2242     if (fp._flag & _IONBF)
2243     {
2244         /* Use this for unbuffered I/O, when running
2245          * across buffer boundaries, or for any but the common
2246          * cases.
2247          */
2248       L1:
2249         auto app = appender(buf);
2250         app.clear();
2251         if(app.capacity == 0)
2252             app.reserve(128); // get at least 128 bytes available
2253
2254         int c;
2255         while((c = FGETC(fp)) != -1) {
2256             app.put(cast(char) c);
2257             if(c == terminator) {
2258                 buf = app.data;
2259                 return buf.length;
2260             }
2261
2262         }
2263
2264         if (ferror(fps))
2265             StdioException();
2266         buf = app.data;
2267         return buf.length;
2268     }
2269     else
2270     {
2271         int u = fp._cnt;
2272         char* p = fp._ptr;
2273         int i;
2274         if (fp._flag & _IOTRAN)
2275         {   /* Translated mode ignores \r and treats ^Z as end-of-file
2276              */
2277             char c;
2278             while (1)
2279             {
2280                 if (i == u)                // if end of buffer
2281                     goto L1;        // give up
2282                 c = p[i];
2283                 i++;
2284                 if (c != '\r')
2285                 {
2286                     if (c == terminator)
2287                         break;
2288                     if (c != 0x1A)
2289                         continue;
2290                     goto L1;
2291                 }
2292                 else
2293                 {   if (i != u && p[i] == terminator)
2294                         break;
2295                     goto L1;
2296                 }
2297             }
2298             if (i > sz)
2299             {
2300                 buf = cast(char[])GC.malloc(i, GC.BlkAttr.NO_SCAN)[0 .. i];
2301             }
2302             if (i - 1)
2303                 memcpy(buf.ptr, p, i - 1);
2304             buf[i - 1] = cast(char)terminator;
2305             buf = buf[0 .. i];
2306             if (terminator == '\n' && c == '\r')
2307                 i++;
2308         }
2309         else
2310         {
2311             while (1)
2312             {
2313                 if (i == u)                // if end of buffer
2314                     goto L1;        // give up
2315                 auto c = p[i];
2316                 i++;
2317                 if (c == terminator)
2318                     break;
2319             }
2320             if (i > sz)
2321             {
2322                 buf = cast(char[])GC.malloc(i, GC.BlkAttr.NO_SCAN)[0 .. i];
2323             }
2324             memcpy(buf.ptr, p, i);
2325             buf = buf[0 .. i];
2326         }
2327         fp._cnt -= i;
2328         fp._ptr += i;
2329         return i;
2330     }
2331 }
2332
2333 version (GCC_IO)
2334 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n')
2335 {
2336     if (fwide(fps, 0) > 0)
2337     {   /* Stream is in wide characters.
2338          * Read them and convert to chars.
2339          */
2340         FLOCK(fps);
2341         scope(exit) FUNLOCK(fps);
2342         auto fp = cast(_iobuf*)fps;
2343         version (Windows)
2344         {
2345             buf.length = 0;
2346             for (int c = void; (c = FGETWC(fp)) != -1; )
2347             {
2348                 if ((c & ~0x7F) == 0)
2349                 {   buf ~= c;
2350                     if (c == terminator)
2351                         break;
2352                 }
2353                 else
2354                 {
2355                     if (c >= 0xD800 && c <= 0xDBFF)
2356                     {
2357                         int c2 = void;
2358                         if ((c2 = FGETWC(fp)) != -1 ||
2359                                 c2 < 0xDC00 && c2 > 0xDFFF)
2360                         {
2361                             StdioException("unpaired UTF-16 surrogate");
2362                         }
2363                         c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
2364                     }
2365                     std.utf.encode(buf, c);
2366                 }
2367             }
2368             if (ferror(fp))
2369                 StdioException();
2370             return buf.length;
2371         }
2372         else version (Posix)
2373         {
2374             buf.length = 0;
2375             for (int c; (c = FGETWC(fp)) != -1; )
2376             {
2377                 if ((c & ~0x7F) == 0)
2378                     buf ~= cast(char)c;
2379                 else
2380                     std.utf.encode(buf, cast(dchar)c);
2381                 if (c == terminator)
2382                     break;
2383             }
2384             if (ferror(fps))
2385                 StdioException();
2386             return buf.length;
2387         }
2388         else
2389         {
2390             static assert(0);
2391         }
2392     }
2393
2394     char *lineptr = null;
2395     size_t n = 0;
2396     auto s = getdelim(&lineptr, &n, terminator, fps);
2397     scope(exit) free(lineptr);
2398     if (s < 0)
2399     {
2400         if (ferror(fps))
2401             StdioException();
2402         buf.length = 0;                // end of file
2403         return 0;
2404     }
2405     buf = buf.ptr[0 .. GC.sizeOf(buf.ptr)];
2406     if (s <= buf.length)
2407     {
2408         buf.length = s;
2409         buf[] = lineptr[0 .. s];
2410     }
2411     else
2412     {
2413         buf = lineptr[0 .. s].dup;
2414     }
2415     return s;
2416 }
2417
2418 version (GENERIC_IO)
2419 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n')
2420 {
2421     FLOCK(fps);
2422     scope(exit) FUNLOCK(fps);
2423     auto fp = cast(_iobuf*)fps;
2424     if (fwide(fps, 0) > 0)
2425     {   /* Stream is in wide characters.
2426          * Read them and convert to chars.
2427          */
2428         version (Windows)
2429         {
2430             buf.length = 0;
2431             for (int c; (c = FGETWC(fp)) != -1; )
2432             {
2433                 if ((c & ~0x7F) == 0)
2434                 {   buf ~= c;
2435                     if (c == terminator)
2436                         break;
2437                 }
2438                 else
2439                 {
2440                     if (c >= 0xD800 && c <= 0xDBFF)
2441                     {
2442                         int c2 = void;
2443                         if ((c2 = FGETWC(fp)) != -1 ||
2444                                 c2 < 0xDC00 && c2 > 0xDFFF)
2445                         {
2446                             StdioException("unpaired UTF-16 surrogate");
2447                         }
2448                         c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
2449                     }
2450                     std.utf.encode(buf, c);
2451                 }
2452             }
2453             if (ferror(fp))
2454                 StdioException();
2455             return buf.length;
2456         }
2457         else version (Posix)
2458         {
2459             buf.length = 0;
2460             for (int c; (c = FGETWC(fp)) != -1; )
2461             {
2462                 if ((c & ~0x7F) == 0)
2463                     buf ~= cast(char)c;
2464                 else
2465                     std.utf.encode(buf, cast(dchar)c);
2466                 if (c == terminator)
2467                     break;
2468             }
2469             if (ferror(fps))
2470                 StdioException();
2471             return buf.length;
2472         }
2473         else
2474         {
2475             static assert(0);
2476         }
2477     }
2478
2479     // Narrow stream
2480     buf.length = 0;
2481     for (int c; (c = FGETC(fp)) != -1; )
2482     {
2483         buf ~= cast(char)c;
2484         if (c == terminator)
2485             break;
2486     }
2487     if (ferror(fps))
2488         StdioException();
2489     return buf.length;
2490 }
2491
2492
2493 /** Experimental network access via the File interface
2494
2495         Opens a TCP connection to the given host and port, then returns
2496         a File struct with read and write access through the same interface
2497         as any other file (meaning writef and the byLine ranges work!).
2498
2499         Authors:
2500                 Adam D. Ruppe
2501
2502         Bugs:
2503                 Only works on Linux
2504 */
2505 version(linux) {
2506     static import linux = std.c.linux.linux;
2507     static import sock = std.c.linux.socket;
2508
2509     File openNetwork(string host, ushort port) {
2510         auto h = enforce( sock.gethostbyname(std.string.toStringz(host)),
2511             new StdioException("gethostbyname"));
2512
2513         int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0);
2514         enforce(s != -1, new StdioException("socket"));
2515
2516         scope(failure) {
2517             linux.close(s); // want to make sure it doesn't dangle if
2518                             // something throws. Upon normal exit, the
2519                             // File struct's reference counting takes
2520                             // care of closing, so we don't need to
2521                             // worry about success
2522         }
2523
2524         sock.sockaddr_in addr;
2525
2526         addr.sin_family = sock.AF_INET;
2527         addr.sin_port = sock.htons(port);
2528         std.c.string.memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length);
2529
2530         enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
2531             new StdioException("Connect failed"));
2532
2533         FILE* fp = enforce(fdopen(s, "w+".ptr));
2534
2535         File f;
2536         f.p = new File.Impl(fp, 1, host ~ ":" ~ to!string(port));
2537
2538         return f;
2539     }
2540 }
Note: See TracBrowser for help on using the browser.