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