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