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 moduleocean.io.device.File;
26 27 importArrayMut = ocean.core.array.Mutation;
28 importocean.meta.types.Qualifiers;
29 importocean.core.Verify;
30 31 importocean.sys.Common;
32 importocean.text.util.StringC;
33 importocean.io.device.Device;
34 importocean.stdc.gnu..string;
35 36 importcore.stdc..string;
37 importcore.stdc.errno;
38 39 /*******************************************************************************
40 41 platform-specific functions
42 43 *******************************************************************************/44 45 importcore.sys.posix.unistd;
46 importcore.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 classFile : Device, Device.Seek, Device.Truncate143 {
144 staticimportocean.core.ExceptionDefinitions;
145 importocean.sys.CloseOnExec;
146 staticimmutableO_CLOEXEC = 0x80000;
147 148 publicaliasDevice.readread;
149 publicaliasDevice.writewrite;
150 151 /***********************************************************************
152 153 Exception class thrown on errors.
154 155 ***********************************************************************/156 157 publicstaticclassIOException: ocean.core.ExceptionDefinitions.IOException158 {
159 importocean.core.Exception: ReusableExceptionImplementation;
160 161 mixinReusableExceptionImplementation!() 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 publictypeof(this) set ( cstringfile_path,
179 interror_num, istringfunc_name = "",
180 istringmsg = "",
181 istringfile = __FILE__, longline = __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 autoerrmsg = StringC.toDString(
195 strerror_r(this.error_num, buf.ptr, buf.length));
196 197 this.ReusableImpl.append(errmsg);
198 }
199 else200 {
201 this.ReusableImpl.append(msg);
202 }
203 204 this.ReusableImpl.append(" on ").append(file_path);
205 206 returnthis;
207 }
208 }
209 210 /***********************************************************************
211 212 Fits into 32 bits ...
213 214 ***********************************************************************/215 216 align(1) structStyle217 {
218 align(1):
219 Accessaccess; /// Access rights.220 Openopen; /// How to open.221 Shareshare; /// How to share.222 Cachecache; /// How to cache.223 }
224 225 /***********************************************************************
226 227 ***********************************************************************/228 229 enumAccess : ubyte {
230 Read = 0x01, /// Is readable.231 Write = 0x02, /// Is writable.232 ReadWrite = 0x03, /// Both.233 }
234 235 /***********************************************************************
236 237 ***********************************************************************/238 239 enumOpen : 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 enumShare : ubyte {
252 None=0, /// No sharing.253 Read, /// Shared reading.254 ReadWrite, /// Open for anything.255 };
256 257 /***********************************************************************
258 259 ***********************************************************************/260 261 enumCache : 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 staticimmutableStyleReadExisting = {Access.Read, Open.Exists};
275 276 /***********************************************************************
277 278 Read an existing file.
279 280 ***********************************************************************/281 282 staticimmutableStyleReadShared = {Access.Read, Open.Exists, Share.Read};
283 284 /***********************************************************************
285 286 Write on an existing file. Do not create.
287 288 ***********************************************************************/289 290 staticimmutableStyleWriteExisting = {Access.Write, Open.Exists};
291 292 /***********************************************************************
293 294 Write on a clean file. Create if necessary.
295 296 ***********************************************************************/297 298 staticimmutableStyleWriteCreate = {Access.Write, Open.Create};
299 300 /***********************************************************************
301 302 Read from the beginning, append at the end of the file.
303 304 ***********************************************************************/305 306 staticimmutableStyleReadWriteAppending = {Access.ReadWrite, Open.Append};
307 308 /***********************************************************************
309 310 Write at the end of the file.
311 312 ***********************************************************************/313 314 staticimmutableStyleWriteAppending = {Access.Write, Open.Append};
315 316 /***********************************************************************
317 318 Read and write an existing file.
319 320 ***********************************************************************/321 322 staticimmutableStyleReadWriteExisting = {Access.ReadWrite, Open.Exists};
323 324 /***********************************************************************
325 326 Read & write on a clean file. Create if necessary.
327 328 ***********************************************************************/329 330 staticimmutableStyleReadWriteCreate = {Access.ReadWrite, Open.Create};
331 332 /***********************************************************************
333 334 Read and Write. Use existing file if present.
335 336 ***********************************************************************/337 338 staticimmutableStyleReadWriteOpen = {Access.ReadWrite, Open.Sedate};
339 340 341 // the file we're working with342 privatecstringpath_;
343 344 // the style we're opened with345 privateStylestyle_;
346 347 /***********************************************************************
348 349 Reusable exception instance
350 351 ***********************************************************************/352 353 privateIOExceptionexception;
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 (IOExceptionexception = 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 (cstringpath, Stylestyle = ReadExisting,
387 IOExceptionexception = 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 Stylestyle ()
400 {
401 returnstyle_;
402 }
403 404 /***********************************************************************
405 406 Return the path used by this file.
407 408 ***********************************************************************/409 410 overrideistringtoString ()
411 {
412 returnidup(path_);
413 }
414 415 /**********************************************************************
416 417 Returns:
418 path used by this file.
419 420 **********************************************************************/421 422 publiccstringpath ()
423 {
424 returnthis.path_;
425 }
426 427 /***********************************************************************
428 429 Convenience function to return the content of a file.
430 431 ***********************************************************************/432 433 staticvoid[] get (cstringpath)
434 {
435 void[] dst;
436 returnget(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 staticvoid[] get (cstringpath, refvoid[] dst)
453 {
454 scopefile = newFile (path);
455 456 // allocate enough space for the entire file457 autolen = cast(size_t) file.length;
458 if (dst.length < len){
459 if (dstisnull){ // avoid setting the noscan attribute, one should maybe change the return type460 dst=newubyte[](len);
461 } else {
462 dst.length = len;
463 assumeSafeAppend(dst);
464 }
465 }
466 467 //read the content468 len = file.read (dst);
469 if (lenisfile.Eof)
470 file.error ("File.read :: unexpected eof");
471 472 returndst [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 staticvoidset (cstringpath, const(void)[] content)
483 {
484 scopefile = newFile (path, ReadWriteCreate);
485 file.write (content);
486 }
487 488 /***********************************************************************
489 490 Convenience function to append content to a file.
491 492 ***********************************************************************/493 494 staticvoidappend (cstringpath, const(void)[] content)
495 {
496 scopefile = newFile (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 protectedboolopen (cstringpath, Stylestyle,
511 intaddflags, intaccess = 0x1B6/* octal 0666 */)
512 {
513 aliasint[] Flags;
514 515 staticimmutableFlagsAccess =
516 [
517 0, // invalid518 O_RDONLY,
519 O_WRONLY,
520 O_RDWR,
521 ];
522 523 staticimmutableFlagsCreate =
524 [
525 0, // open existing526 O_CREAT | O_TRUNC, // truncate always527 O_CREAT, // create if needed528 O_APPEND | O_CREAT, // append529 O_CREAT | O_EXCL, // can't exist530 ];
531 532 staticimmutableshort[] Locks =
533 [
534 F_WRLCK, // no sharing535 F_RDLCK, // shared read536 ];
537 538 // remember our settings539 verify(path !isnull);
540 path_ = path;
541 style_ = style;
542 543 // zero terminate and convert to utf16544 staticmstringbuffer;
545 ArrayMut.copy(buffer, path);
546 autoname = StringC.toCString(buffer);
547 automode = Access[style.access] | Create[style.open];
548 549 handle = posix.open (name, mode | setCloExec(addflags, O_CLOEXEC), access);
550 if (handleis -1)
551 returnfalse;
552 553 returntrue;
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 voidopen (cstringpath, Stylestyle = 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 voidsetFileHandle ( intfd )
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 voidtruncate ()
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 overridevoidtruncate (longsize)
609 {
610 // set filesize to be current seek-position611 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 overridelongseek (longoffset, Anchoranchor = Anchor.Begin)
623 {
624 longresult = posix.lseek (handle, cast(off_t) offset, anchor);
625 if (resultis -1)
626 error(.errno, "seek");
627 returnresult;
628 }
629 630 /***********************************************************************
631 632 Return the current file position.
633 634 ***********************************************************************/635 636 longposition ()
637 {
638 returnseek (0, Anchor.Current);
639 }
640 641 /***********************************************************************
642 643 Return the total length of this file.
644 645 ***********************************************************************/646 647 longlength ()
648 {
649 stat_tstats = void;
650 if (posix.fstat (handle, &stats))
651 error(.errno, "fstat");
652 returncast(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 voidsync ()
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 publicoverridevoiderror ( interror_code, istringfunc_name,
688 istringmsg = "", istringfile = __FILE__, longline = __LINE__ )
689 {
690 if (this.exceptionisnull)
691 {
692 this.exception = newIOException;
693 }
694 695 throwthis.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 publicaliasConduit.errorerror;
705 706 /***********************************************************************
707 708 Throw an IOException noting the last error.
709 710 ***********************************************************************/711 712 publicaliasDevice.errorerror;
713 }