1 /******************************************************************************* 2 3 Copyright: 4 Copyright (c) 2004 Kris Bell. 5 Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH. 6 All rights reserved. 7 8 License: 9 Tango Dual License: 3-Clause BSD License / Academic Free License v3.0. 10 See LICENSE_TANGO.txt for details. 11 12 Version: 13 Mar 2004: Initial release$(BR) 14 Dec 2006: Outback release$(BR) 15 Nov 2008: relocated and simplified 16 17 Authors: 18 Kris, 19 John Reimer, 20 Anders F Bjorklund (Darwin patches), 21 Chris Sauls (Win95 file support) 22 23 *******************************************************************************/ 24 25 module ocean.io.device.File; 26 27 import ArrayMut = ocean.core.array.Mutation; 28 import ocean.meta.types.Qualifiers; 29 import ocean.core.Verify; 30 31 import ocean.sys.Common; 32 import ocean.text.util.StringC; 33 import ocean.io.device.Device; 34 import ocean.stdc.gnu.string; 35 36 import core.stdc.string; 37 import core.stdc.errno; 38 39 /******************************************************************************* 40 41 platform-specific functions 42 43 *******************************************************************************/ 44 45 import core.sys.posix.unistd; 46 import core.sys.posix.sys.types; 47 48 /******************************************************************************* 49 50 Implements a means of reading and writing a generic file. Conduits 51 are the primary means of accessing external data, and File 52 extends the basic pattern by providing file-specific methods to 53 set the file size, seek to a specific file position and so on. 54 55 Serial input and output is straightforward. In this example we 56 copy a file directly to the console: 57 --- 58 // open a file for reading 59 auto from = new File ("test.txt"); 60 61 // stream directly to console 62 Stdout.copy (from); 63 --- 64 65 And here we copy one file to another: 66 --- 67 // open file for reading 68 auto from = new File ("test.txt"); 69 70 // open another for writing 71 auto to = new File ("copy.txt", File.WriteCreate); 72 73 // copy file and close 74 to.copy.close; 75 from.close; 76 --- 77 78 You can use InputStream.load() to load a file directly into memory: 79 --- 80 auto file = new File ("test.txt"); 81 auto content = file.load; 82 file.close; 83 --- 84 85 Or use a convenience static function within File: 86 --- 87 auto content = File.get ("test.txt"); 88 --- 89 90 A more explicit version with a similar result would be: 91 --- 92 // open file for reading 93 auto file = new File ("test.txt"); 94 95 // create an array to house the entire file 96 auto content = new char [file.length]; 97 98 // read the file content. Return value is the number of bytes read 99 auto bytes = file.read (content); 100 file.close; 101 --- 102 103 Conversely, one may write directly to a File like so: 104 --- 105 // open file for writing 106 auto to = new File ("text.txt", File.WriteCreate); 107 108 // write an array of content to it 109 auto bytes = to.write (content); 110 --- 111 112 There are equivalent static functions, File.set() and 113 File.append(), which set or append file content respectively. 114 115 File can happily handle random I/O. Here we use seek() to 116 relocate the file pointer: 117 --- 118 // open a file for reading and writing 119 auto file = new File ("random.bin", File.ReadWriteCreate); 120 121 // write some data 122 file.write ("testing"); 123 124 // rewind to file start 125 file.seek (0); 126 127 // read data back again 128 char[10] tmp; 129 auto bytes = file.read (tmp); 130 131 file.close; 132 --- 133 134 Note that File is unbuffered by default - wrap an instance within 135 ocean.io.stream.Buffered for buffered I/O. 136 137 Compile with -version=Win32SansUnicode to enable Win95 & Win32s file 138 support. 139 140 *******************************************************************************/ 141 142 class File : Device, Device.Seek, Device.Truncate 143 { 144 static import ocean.core.ExceptionDefinitions; 145 import ocean.sys.CloseOnExec; 146 static immutable O_CLOEXEC = 0x80000; 147 148 public alias Device.read read; 149 public alias Device.write write; 150 151 /*********************************************************************** 152 153 Exception class thrown on errors. 154 155 ***********************************************************************/ 156 157 public static class IOException: ocean.core.ExceptionDefinitions.IOException 158 { 159 import ocean.core.Exception: ReusableExceptionImplementation; 160 161 mixin ReusableExceptionImplementation!() ReusableImpl; 162 163 /******************************************************************* 164 165 Sets the exception instance. 166 167 Params: 168 file_path = path of the file 169 error_num = error code (defaults to .errno) 170 func_name = name of the method that failed 171 msg = message description of the error (uses stderr if empty) 172 file = file where exception is thrown 173 line = line where exception is thrown 174 175 *******************************************************************/ 176 177 178 public typeof(this) set ( cstring file_path, 179 int error_num, istring func_name = "", 180 istring msg = "", 181 istring file = __FILE__, long line = __LINE__) 182 { 183 this.error_num = error_num; 184 this.func_name = func_name; 185 186 this.ReusableImpl.set(this.func_name, file, line); 187 188 if (this.func_name.length) 189 this.ReusableImpl.append(": "); 190 191 if (msg.length == 0) 192 { 193 char[256] buf; 194 auto errmsg = StringC.toDString( 195 strerror_r(this.error_num, buf.ptr, buf.length)); 196 197 this.ReusableImpl.append(errmsg); 198 } 199 else 200 { 201 this.ReusableImpl.append(msg); 202 } 203 204 this.ReusableImpl.append(" on ").append(file_path); 205 206 return this; 207 } 208 } 209 210 /*********************************************************************** 211 212 Fits into 32 bits ... 213 214 ***********************************************************************/ 215 216 align(1) struct Style 217 { 218 align(1): 219 Access access; /// Access rights. 220 Open open; /// How to open. 221 Share share; /// How to share. 222 Cache cache; /// How to cache. 223 } 224 225 /*********************************************************************** 226 227 ***********************************************************************/ 228 229 enum Access : ubyte { 230 Read = 0x01, /// Is readable. 231 Write = 0x02, /// Is writable. 232 ReadWrite = 0x03, /// Both. 233 } 234 235 /*********************************************************************** 236 237 ***********************************************************************/ 238 239 enum Open : ubyte { 240 Exists=0, /// Must exist. 241 Create, /// Create or truncate. 242 Sedate, /// Create if necessary. 243 Append, /// Create if necessary. 244 New, /// Can't exist. 245 }; 246 247 /*********************************************************************** 248 249 ***********************************************************************/ 250 251 enum Share : ubyte { 252 None=0, /// No sharing. 253 Read, /// Shared reading. 254 ReadWrite, /// Open for anything. 255 }; 256 257 /*********************************************************************** 258 259 ***********************************************************************/ 260 261 enum Cache : ubyte { 262 None = 0x00, /// Don't optimize. 263 Random = 0x01, /// Optimize for random. 264 Stream = 0x02, /// Optimize for stream. 265 WriteThru = 0x04, /// Backing-cache flag. 266 }; 267 268 /*********************************************************************** 269 270 Read an existing file. 271 272 ***********************************************************************/ 273 274 static immutable Style ReadExisting = {Access.Read, Open.Exists}; 275 276 /*********************************************************************** 277 278 Read an existing file. 279 280 ***********************************************************************/ 281 282 static immutable Style ReadShared = {Access.Read, Open.Exists, Share.Read}; 283 284 /*********************************************************************** 285 286 Write on an existing file. Do not create. 287 288 ***********************************************************************/ 289 290 static immutable Style WriteExisting = {Access.Write, Open.Exists}; 291 292 /*********************************************************************** 293 294 Write on a clean file. Create if necessary. 295 296 ***********************************************************************/ 297 298 static immutable Style WriteCreate = {Access.Write, Open.Create}; 299 300 /*********************************************************************** 301 302 Read from the beginning, append at the end of the file. 303 304 ***********************************************************************/ 305 306 static immutable Style ReadWriteAppending = {Access.ReadWrite, Open.Append}; 307 308 /*********************************************************************** 309 310 Write at the end of the file. 311 312 ***********************************************************************/ 313 314 static immutable Style WriteAppending = {Access.Write, Open.Append}; 315 316 /*********************************************************************** 317 318 Read and write an existing file. 319 320 ***********************************************************************/ 321 322 static immutable Style ReadWriteExisting = {Access.ReadWrite, Open.Exists}; 323 324 /*********************************************************************** 325 326 Read & write on a clean file. Create if necessary. 327 328 ***********************************************************************/ 329 330 static immutable Style ReadWriteCreate = {Access.ReadWrite, Open.Create}; 331 332 /*********************************************************************** 333 334 Read and Write. Use existing file if present. 335 336 ***********************************************************************/ 337 338 static immutable Style ReadWriteOpen = {Access.ReadWrite, Open.Sedate}; 339 340 341 // the file we're working with 342 private cstring path_; 343 344 // the style we're opened with 345 private Style style_; 346 347 /*********************************************************************** 348 349 Reusable exception instance 350 351 ***********************************************************************/ 352 353 private IOException exception; 354 355 /*********************************************************************** 356 357 Create a File for use with open(). 358 359 Note that File is unbuffered by default - wrap an instance 360 within ocean.io.stream.Buffered for buffered I/O. 361 362 Params: 363 exception = reusable exception instance to use, or null 364 to create a fresh one. 365 366 ***********************************************************************/ 367 368 this (IOException exception = null) 369 { 370 this.exception = exception; 371 } 372 373 /*********************************************************************** 374 375 Create a File with the provided path and style. 376 377 Note that File is unbuffered by default - wrap an instance 378 within ocean.io.stream.Buffered for buffered I/O. 379 380 Params: 381 exception = reusable exception instance to use, or null 382 to create a fresh one. 383 384 ***********************************************************************/ 385 386 this (cstring path, Style style = ReadExisting, 387 IOException exception = null) 388 { 389 this(exception); 390 open (path, style); 391 } 392 393 /*********************************************************************** 394 395 Return the Style used for this file. 396 397 ***********************************************************************/ 398 399 Style style () 400 { 401 return style_; 402 } 403 404 /*********************************************************************** 405 406 Return the path used by this file. 407 408 ***********************************************************************/ 409 410 override istring toString () 411 { 412 return idup(path_); 413 } 414 415 /********************************************************************** 416 417 Returns: 418 path used by this file. 419 420 **********************************************************************/ 421 422 public cstring path () 423 { 424 return this.path_; 425 } 426 427 /*********************************************************************** 428 429 Convenience function to return the content of a file. 430 431 ***********************************************************************/ 432 433 static void[] get (cstring path) 434 { 435 void[] dst; 436 return get(path, dst); 437 } 438 439 /*********************************************************************** 440 441 Convenience function to return the content of a file. 442 443 This overload takes a slice to a reusable buffer which is 444 expanded as needed. 445 Content size is determined via the file-system, per 446 File.length, although that may be misleading for some 447 *nix systems. An alternative is to use File.load which 448 loads content until an Eof is encountered. 449 450 451 ***********************************************************************/ 452 static void[] get (cstring path, ref void[] dst) 453 { 454 scope file = new File (path); 455 456 // allocate enough space for the entire file 457 auto len = cast(size_t) file.length; 458 if (dst.length < len){ 459 if (dst is null){ // avoid setting the noscan attribute, one should maybe change the return type 460 dst=new ubyte[](len); 461 } else { 462 dst.length = len; 463 assumeSafeAppend(dst); 464 } 465 } 466 467 //read the content 468 len = file.read (dst); 469 if (len is file.Eof) 470 file.error ("File.read :: unexpected eof"); 471 472 return dst [0 .. len]; 473 } 474 475 /*********************************************************************** 476 477 Convenience function to set file content and length to 478 reflect the given array. 479 480 ***********************************************************************/ 481 482 static void set (cstring path, const(void)[] content) 483 { 484 scope file = new File (path, ReadWriteCreate); 485 file.write (content); 486 } 487 488 /*********************************************************************** 489 490 Convenience function to append content to a file. 491 492 ***********************************************************************/ 493 494 static void append (cstring path, const(void)[] content) 495 { 496 scope file = new File (path, WriteAppending); 497 file.write (content); 498 } 499 500 501 /*********************************************************************** 502 503 Low level open for sub-classes that need to apply specific attributes. 504 505 Return: 506 False in case of failure. 507 508 ***********************************************************************/ 509 510 protected bool open (cstring path, Style style, 511 int addflags, int access = 0x1B6 /* octal 0666 */) 512 { 513 alias int[] Flags; 514 515 static immutable Flags Access = 516 [ 517 0, // invalid 518 O_RDONLY, 519 O_WRONLY, 520 O_RDWR, 521 ]; 522 523 static immutable Flags Create = 524 [ 525 0, // open existing 526 O_CREAT | O_TRUNC, // truncate always 527 O_CREAT, // create if needed 528 O_APPEND | O_CREAT, // append 529 O_CREAT | O_EXCL, // can't exist 530 ]; 531 532 static immutable short[] Locks = 533 [ 534 F_WRLCK, // no sharing 535 F_RDLCK, // shared read 536 ]; 537 538 // remember our settings 539 verify(path !is null); 540 path_ = path; 541 style_ = style; 542 543 // zero terminate and convert to utf16 544 static mstring buffer; 545 ArrayMut.copy(buffer, path); 546 auto name = StringC.toCString(buffer); 547 auto mode = Access[style.access] | Create[style.open]; 548 549 handle = posix.open (name, mode | setCloExec(addflags, O_CLOEXEC), access); 550 if (handle is -1) 551 return false; 552 553 return true; 554 } 555 556 /*********************************************************************** 557 558 Open a file with the provided style. 559 560 Note that files default to no-sharing. That is, they are locked 561 exclusively to the host process unless otherwise stipulated. 562 We do this in order to expose the same default behaviour as Win32. 563 564 $(B No file locking for borked POSIX.) 565 566 ***********************************************************************/ 567 568 void open (cstring path, Style style = ReadExisting) 569 { 570 if (! open (path, style, 0)) 571 error(.errno, "open"); 572 } 573 574 /*********************************************************************** 575 576 Wraps the already open file descriptor into a File instance. 577 578 Params: 579 fd = file descriptor to wrap the file around 580 581 ***********************************************************************/ 582 583 void setFileHandle ( int fd ) 584 { 585 verify (fd > 0); 586 this.handle = fd; 587 } 588 589 /*********************************************************************** 590 591 Set the file size to be that of the current seek position. 592 The file must be writable for this to succeed. 593 594 ***********************************************************************/ 595 596 void truncate () 597 { 598 truncate (position); 599 } 600 601 /*********************************************************************** 602 603 Set the file size to be the specified length. 604 The file must be writable for this to succeed. 605 606 ***********************************************************************/ 607 608 override void truncate (long size) 609 { 610 // set filesize to be current seek-position 611 if (posix.ftruncate (handle, cast(off_t) size) is -1) 612 error(.errno, "ftruncate"); 613 } 614 615 /*********************************************************************** 616 617 Set the file seek position to the specified offset 618 from the given anchor. 619 620 ***********************************************************************/ 621 622 override long seek (long offset, Anchor anchor = Anchor.Begin) 623 { 624 long result = posix.lseek (handle, cast(off_t) offset, anchor); 625 if (result is -1) 626 error(.errno, "seek"); 627 return result; 628 } 629 630 /*********************************************************************** 631 632 Return the current file position. 633 634 ***********************************************************************/ 635 636 long position () 637 { 638 return seek (0, Anchor.Current); 639 } 640 641 /*********************************************************************** 642 643 Return the total length of this file. 644 645 ***********************************************************************/ 646 647 long length () 648 { 649 stat_t stats = void; 650 if (posix.fstat (handle, &stats)) 651 error(.errno, "fstat"); 652 return cast(long) stats.st_size; 653 } 654 655 /*********************************************************************** 656 657 Instructs the OS to flush it's internal buffers to the disk device. 658 659 NOTE: due to OS and hardware design, data flushed 660 cannot be guaranteed to be actually on disk-platters. 661 Actual durability of data depends on write-caches, 662 barriers, presence of battery-backup, filesystem and 663 OS-support. 664 665 ***********************************************************************/ 666 667 void sync () 668 { 669 if (fsync (handle)) 670 error(.errno, "fsync"); 671 } 672 673 /******************************************************************* 674 675 Throw a potentially reusable IOException, with the provided 676 message, function name and error code. 677 678 Params: 679 error_num = error code 680 func_name = name of the method that failed 681 msg = message description of the error (uses stderr if empty) 682 file = file where exception is thrown 683 line = line where exception is thrown 684 685 *******************************************************************/ 686 687 public override void error ( int error_code, istring func_name, 688 istring msg = "", istring file = __FILE__, long line = __LINE__ ) 689 { 690 if (this.exception is null) 691 { 692 this.exception = new IOException; 693 } 694 695 throw this.exception.set(this.path_, error_code, func_name, msg, file, line); 696 } 697 698 /*********************************************************************** 699 700 Throw an IOException, with the provided message. 701 702 ***********************************************************************/ 703 704 public alias Conduit.error error; 705 706 /*********************************************************************** 707 708 Throw an IOException noting the last error. 709 710 ***********************************************************************/ 711 712 public alias Device.error error; 713 }