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