root/trunk/phobos/std/file.d

Revision 2371, 97.3 kB (checked in by braddr, 3 years ago)

remove assert that was copied from d1, sizes are different? Definitly needs a cleanup.

  • Property svn:eol-style set to native
Line 
1 // Written in the D programming language.
2
3 /**
4 Utilities for manipulating files and scanning directories. Functions
5 in this module handle files as a unit, e.g., read or write one _file
6 at a time. For opening files and manipulating them via handles refer
7 to module $(D $(LINK2 std_stdio.html,std.stdio)).
8
9 Macros:
10 WIKI = Phobos/StdFile
11
12 Copyright: Copyright Digital Mars 2007 - 2009.
13 License:   $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
14 Authors:   $(WEB digitalmars.com, Walter Bright),
15            $(WEB erdani.org, Andrei Alexandrescu),
16            Jonathan M Davis
17  */
18 module std.file;
19
20 import core.memory;
21 import core.stdc.stdio, core.stdc.stdlib, core.stdc.string,
22        core.stdc.errno, std.algorithm, std.array, std.conv,
23        std.datetime, std.exception, std.format, std.path, std.process,
24        std.range, std.regexp, std.stdio, std.string, std.traits, std.typecons,
25        std.typetuple, std.utf;
26
27 version (Win32)
28 {
29     import core.sys.windows.windows, std.windows.charset,
30         std.windows.syserror, std.__fileinit : useWfuncs;
31 /*
32  * Since Win 9x does not support the "W" API's, first convert
33  * to wchar, then convert to multibyte using the current code
34  * page.
35  * (Thanks to yaneurao for this)
36  */
37     version(Windows) alias std.windows.charset.toMBSz toMBSz;
38 }
39 version (Posix)
40 {
41     import core.sys.posix.dirent, core.sys.posix.fcntl, core.sys.posix.sys.stat,
42         core.sys.posix.sys.time, core.sys.posix.unistd, core.sys.posix.utime;
43 }
44
45 version (unittest)
46 {
47     import core.thread : Thread;
48
49     private string deleteme()
50     {
51         static _deleteme = "deleteme.dmd.unittest";
52         static _first = true;
53
54         if(_first)
55         {
56             version(Windows)
57                 _deleteme = std.path.join(std.process.getenv("TEMP"), _deleteme);
58             else
59                 _deleteme = "/tmp/" ~ _deleteme;
60
61             _first = false;
62         }
63
64
65         return _deleteme;
66     }
67 }
68
69
70 // @@@@ TEMPORARY - THIS SHOULD BE IN THE CORE @@@
71 // {{{
72 version (Posix)
73 {
74     version (OSX)
75     {
76         struct struct_stat64        // distinguish it from the stat() function
77         {
78             uint st_dev;        /// device
79             ushort st_mode;
80             ushort st_nlink;        /// link count
81             ulong st_ino;        /// file serial number
82             uint st_uid;        /// user ID of file's owner
83             uint st_gid;        /// user ID of group's owner
84             uint st_rdev;        /// if device then device number
85
86             int st_atime;
87             uint st_atimensec;
88             int st_mtime;
89             uint st_mtimensec;
90             int st_ctime;
91             uint st_ctimensec;
92             int st_birthtime;
93             uint st_birthtimensec;
94
95             ulong st_size;
96             long st_blocks;        /// number of allocated 512 byte blocks
97             int st_blksize;        /// optimal I/O block size
98
99             ulong st_ino64;
100             uint st_flags;
101             uint st_gen;
102             int st_lspare; /* RESERVED: DO NOT USE! */
103             long st_qspare[2]; /* RESERVED: DO NOT USE! */
104         }
105
106         extern(C) int fstat64(int, struct_stat64*);
107         extern(C) int stat64(in char*, struct_stat64*);
108     }
109     else version (FreeBSD)
110     {
111         alias core.sys.posix.sys.stat.stat_t struct_stat64;
112         alias core.sys.posix.sys.stat.fstat  fstat64;
113         alias core.sys.posix.sys.stat.stat   stat64;
114     }
115     else
116     {
117         version(X86)
118         {
119             struct struct_stat64        // distinguish it from the stat() function
120             {
121                 ulong st_dev;        /// device
122                 uint __pad1;
123                 uint st_ino;        /// file serial number
124                 uint st_mode;        /// file mode
125                 uint st_nlink;        /// link count
126                 uint st_uid;        /// user ID of file's owner
127                 uint st_gid;        /// user ID of group's owner
128                 ulong st_rdev;        /// if device then device number
129                 uint __pad2;
130                 align(4) ulong st_size;
131                 int st_blksize;        /// optimal I/O block size
132                 ulong st_blocks;        /// number of allocated 512 byte blocks
133                 int st_atime;
134                 uint st_atimensec;
135                 int st_mtime;
136                 uint st_mtimensec;
137                 int st_ctime;
138                 uint st_ctimensec;
139
140                 ulong st_ino64;
141             }
142             //static assert(struct_stat64.sizeof == 88); // copied from d1, but it's currently 96 bytes, not 88.
143         }   
144         else version (X86_64)
145         {
146             struct struct_stat64
147             {
148                 ulong st_dev;
149                 ulong st_ino;
150                 ulong st_nlink;
151                 uint  st_mode;
152                 uint  st_uid;
153                 uint  st_gid;
154                 int   __pad0;
155                 ulong st_rdev;
156                 long  st_size;
157                 long  st_blksize;
158                 long  st_blocks;
159                 long  st_atime;
160                 ulong st_atimensec;
161                 long  st_mtime;
162                 ulong st_mtimensec;
163                 long  st_ctime;
164                 ulong st_ctimensec;
165                 long[3]  __unused;
166             }
167             static assert(struct_stat64.sizeof == 144);
168         }
169
170         extern(C) int fstat64(int, struct_stat64*);
171         extern(C) int stat64(in char*, struct_stat64*);
172         extern(C) int lstat64(in char*, struct_stat64*);
173     }
174 }
175 // }}}
176
177
178 /++
179     Exception thrown for file I/O errors.
180  +/
181 class FileException : Exception
182 {
183     /++
184         OS error code.
185      +/
186     immutable uint errno;
187
188     /++
189         Constructor which takes an error message.
190
191         Params:
192             name = Name of file for which the error occurred.
193             msg  = Message describing the error.
194             file = The file where the error occurred.
195             line = The line where the error occurred.
196      +/
197     this(in char[] name, in char[] msg, string file = __FILE__, size_t line = __LINE__)
198     {
199         if(msg.empty)
200             super(name.idup, file, line);
201         else
202             super(text(name, ": ", msg), file, line);
203
204         errno = 0;
205     }
206
207     /++
208         Constructor which takes the error number ($(LUCKY GetLastError)
209         in Windows, $(D_PARAM getErrno) in Posix).
210
211         Params:
212             name = Name of file for which the error occurred.
213             msg  = Message describing the error.
214             file = The file where the error occurred.
215             line = The line where the error occurred.
216      +/
217     version(Windows) this(in char[] name,
218                           uint errno = GetLastError,
219                           string file = __FILE__,
220                           size_t line = __LINE__)
221     {
222         this(name, sysErrorString(errno), file, line);
223         this.errno = errno;
224     }
225
226     /++
227         Constructor which takes the error number ($(LUCKY GetLastError)
228         in Windows, $(D_PARAM getErrno) in Posix).
229
230         Params:
231             name = Name of file for which the error occurred.
232             msg  = Message describing the error.
233             file = The file where the error occurred.
234             line = The line where the error occurred.
235      +/
236     version(Posix) this(in char[] name,
237                         uint errno = .getErrno,
238                         string file = __FILE__,
239                         size_t line = __LINE__)
240     {
241         auto s = strerror(errno);
242         this(name, to!string(s), file, line);
243         this.errno = errno;
244     }
245 }
246
247 private T cenforce(T)(T condition, lazy const(char)[] name, string file = __FILE__, size_t line = __LINE__)
248 {
249     if (!condition)
250     {
251         throw new FileException(name, "", file, line);
252     }
253     return condition;
254 }
255
256 /* **********************************
257  * Basic File operations.
258  */
259
260 /********************************************
261 Read entire contents of file $(D name) and returns it as an untyped
262 array. If the file size is larger than $(D upTo), only $(D upTo)
263 bytes are read.
264
265 Example:
266
267 ----
268 import std.file, std.stdio;
269 void main()
270 {
271    auto bytes = cast(ubyte[]) read("filename", 5);
272    if (bytes.length == 5)
273        writefln("The fifth byte of the file is 0x%x", bytes[4]);
274 }
275 ----
276
277 Returns: Untyped array of bytes _read.
278
279 Throws: $(D FileException) on error.
280  */
281
282 version(Windows) void[] read(in char[] name, size_t upTo = size_t.max)
283 {
284     alias TypeTuple!(GENERIC_READ,
285             FILE_SHARE_READ, (SECURITY_ATTRIBUTES*).init, OPEN_EXISTING,
286             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
287             HANDLE.init)
288         defaults;
289     auto h = useWfuncs
290         ? CreateFileW(std.utf.toUTF16z(name), defaults)
291         : CreateFileA(toMBSz(name), defaults);
292
293     cenforce(h != INVALID_HANDLE_VALUE, name);
294     scope(exit) cenforce(CloseHandle(h), name);
295     auto size = GetFileSize(h, null);
296     cenforce(size != INVALID_FILE_SIZE, name);
297     size = min(upTo, size);
298     auto buf = GC.malloc(size, GC.BlkAttr.NO_SCAN)[0 .. size];
299     scope(failure) delete buf;
300
301     DWORD numread = void;
302     cenforce(ReadFile(h,buf.ptr, size, &numread, null) == 1
303             && numread == size, name);
304     return buf[0 .. size];
305 }
306
307 version(Posix) void[] read(in char[] name, in size_t upTo = size_t.max)
308 {
309     // A few internal configuration parameters {
310     enum size_t
311         minInitialAlloc = 1024 * 4,
312         maxInitialAlloc = size_t.max / 2,
313         sizeIncrement = 1024 * 16,
314         maxSlackMemoryAllowed = 1024;
315     // }
316
317     immutable fd = core.sys.posix.fcntl.open(toStringz(name),
318             core.sys.posix.fcntl.O_RDONLY);
319     cenforce(fd != -1, name);
320     scope(exit) core.sys.posix.unistd.close(fd);
321
322     struct_stat64 statbuf = void;
323     cenforce(fstat64(fd, &statbuf) == 0, name);
324     //cenforce(core.sys.posix.sys.stat.fstat(fd, &statbuf) == 0, name);
325
326     immutable initialAlloc = to!size_t(statbuf.st_size
327         ? min(statbuf.st_size + 1, maxInitialAlloc)
328         : minInitialAlloc);
329     auto result = GC.malloc(initialAlloc, GC.BlkAttr.NO_SCAN)
330         [0 .. initialAlloc];
331     scope(failure) delete result;
332     size_t size = 0;
333
334     for (;;)
335     {
336         immutable actual = core.sys.posix.unistd.read(fd, result.ptr + size,
337                 min(result.length, upTo) - size);
338         cenforce(actual != -1, name);
339         if (actual == 0) break;
340         size += actual;
341         if (size < result.length) continue;
342         immutable newAlloc = size + sizeIncrement;
343         result = GC.realloc(result.ptr, newAlloc, GC.BlkAttr.NO_SCAN)
344             [0 .. newAlloc];
345     }
346
347     return result.length - size >= maxSlackMemoryAllowed
348         ? GC.realloc(result.ptr, size, GC.BlkAttr.NO_SCAN)[0 .. size]
349         : result[0 .. size];
350 }
351
352 unittest
353 {
354     write(deleteme, "1234");
355     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
356     assert(read(deleteme, 2) == "12");
357     assert(read(deleteme) == "1234");
358 }
359
360 version (linux) unittest
361 {
362     // A file with "zero" length that doesn't have 0 length at all
363     auto s = std.file.readText("/proc/sys/kernel/osrelease");
364     assert(s.length > 0);
365     //writefln("'%s'", s);
366 }
367
368 /********************************************
369 Read and validates (using $(XREF utf, validate)) a text file. $(D S)
370 can be a type of array of characters of any width and constancy. No
371 width conversion is performed; if the width of the characters in file
372 $(D name) is different from the width of elements of $(D S),
373 validation will fail.
374
375 Returns: Array of characters read.
376
377 Throws: $(D FileException) on file error, $(D UtfException) on UTF
378 decoding error.
379
380 Example:
381
382 ----
383 enforce(system("echo abc>deleteme") == 0);
384 scope(exit) remove("deleteme");
385 enforce(chomp(readText("deleteme")) == "abc");
386 ----
387  */
388
389 S readText(S = string)(in char[] name)
390 {
391     auto result = cast(S) read(name);
392     std.utf.validate(result);
393     return result;
394 }
395
396 unittest
397 {
398     write(deleteme, "abc\n");
399     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
400     enforce(chomp(readText(deleteme)) == "abc");
401 }
402
403 /*********************************************
404 Write $(D buffer) to file $(D name).
405 Throws: $(D FileException) on error.
406
407 Example:
408
409 ----
410 import std.file;
411 void main()
412 {
413    int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
414    write("filename", a);
415    assert(cast(int[]) read("filename") == a);
416 }
417 ----
418  */
419
420 version(Windows) void write(in char[] name, const void[] buffer)
421 {
422     alias TypeTuple!(GENERIC_WRITE, 0, null, CREATE_ALWAYS,
423             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
424             HANDLE.init)
425         defaults;
426     auto h = useWfuncs
427         ? CreateFileW(std.utf.toUTF16z(name), defaults)
428         : CreateFileA(toMBSz(name), defaults);
429
430     cenforce(h != INVALID_HANDLE_VALUE, name);
431     scope(exit) cenforce(CloseHandle(h), name);
432     DWORD numwritten;
433     cenforce(WriteFile(h, buffer.ptr, buffer.length, &numwritten, null) == 1
434             && buffer.length == numwritten,
435             name);
436 }
437
438 version(Posix) void write(in char[] name, in void[] buffer)
439 {
440     return writeImpl(name, buffer, O_CREAT | O_WRONLY | O_TRUNC);
441 }
442
443 /*********************************************
444 Appends $(D buffer) to file $(D name).
445 Throws: $(D FileException) on error.
446
447 Example:
448
449 ----
450 import std.file;
451 void main()
452 {
453    int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
454    write("filename", a);
455    int[] b = [ 13, 21 ];
456    append("filename", b);
457    assert(cast(int[]) read("filename") == a ~ b);
458 }
459 ----
460  */
461
462 version(Windows) void append(in char[] name, in void[] buffer)
463 {
464     alias TypeTuple!(GENERIC_WRITE,0,null,OPEN_ALWAYS,
465             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,HANDLE.init)
466         defaults;
467
468     auto h = useWfuncs
469         ? CreateFileW(std.utf.toUTF16z(name), defaults)
470         : CreateFileA(toMBSz(name), defaults);
471
472     cenforce(h != INVALID_HANDLE_VALUE, name);
473     scope(exit) cenforce(CloseHandle(h), name);
474     DWORD numwritten;
475     cenforce(SetFilePointer(h, 0, null, FILE_END) != INVALID_SET_FILE_POINTER
476             && WriteFile(h,buffer.ptr,buffer.length,&numwritten,null) == 1
477             && buffer.length == numwritten,
478             name);
479 }
480
481 version(Posix) void append(in char[] name, in void[] buffer)
482 {
483     return writeImpl(name, buffer, O_APPEND | O_WRONLY | O_CREAT);
484 }
485
486 // Posix implementation helper for write and append
487
488 version(Posix) private void writeImpl(in char[] name,
489         in void[] buffer, in uint mode)
490 {
491     immutable fd = core.sys.posix.fcntl.open(toStringz(name),
492             mode, octal!666);
493     cenforce(fd != -1, name);
494     {
495         scope(failure) core.sys.posix.unistd.close(fd);
496         immutable size = buffer.length;
497         cenforce(
498             core.sys.posix.unistd.write(fd, buffer.ptr, size) == size,
499             name);
500     }
501     cenforce(core.sys.posix.unistd.close(fd) == 0, name);
502 }
503
504 /***************************************************
505  * Rename file $(D from) to $(D to).
506  * Throws: $(D FileException) on error.
507  */
508
509 version(Windows) void rename(in char[] from, in char[] to)
510 {
511     enforce(useWfuncs
512             ? MoveFileW(std.utf.toUTF16z(from), std.utf.toUTF16z(to))
513             : MoveFileA(toMBSz(from), toMBSz(to)),
514             new FileException(
515                 text("Attempting to rename file ", from, " to ",
516                         to)));
517 }
518
519 version(Posix) void rename(in char[] from, in char[] to)
520 {
521     cenforce(std.c.stdio.rename(toStringz(from), toStringz(to)) == 0, to);
522 }
523
524 /***************************************************
525 Delete file $(D name).
526 Throws: $(D FileException) on error.
527  */
528
529 version(Windows) void remove(in char[] name)
530 {
531     cenforce(useWfuncs
532             ? DeleteFileW(std.utf.toUTF16z(name))
533             : DeleteFileA(toMBSz(name)),
534             name);
535 }
536
537 version(Posix) void remove(in char[] name)
538 {
539     cenforce(std.c.stdio.remove(toStringz(name)) == 0, name);
540 }
541
542 /***************************************************
543 Get size of file $(D name) in bytes.
544
545 Throws: $(D FileException) on error (e.g., file not found).
546  */
547
548 version(Windows) ulong getSize(in char[] name)
549 {
550     HANDLE findhndl = void;
551     uint resulth = void;
552     uint resultl = void;
553     const (char)[] file = name[];
554
555     //FindFirstFileX can't handle file names which end in a backslash.
556     if(file.endsWith(sep))
557         file.popBackN(sep.length);
558
559     if (useWfuncs)
560     {
561         WIN32_FIND_DATAW filefindbuf;
562
563         findhndl = FindFirstFileW(std.utf.toUTF16z(file), &filefindbuf);
564         resulth = filefindbuf.nFileSizeHigh;
565         resultl = filefindbuf.nFileSizeLow;
566     }
567     else
568     {
569         WIN32_FIND_DATA filefindbuf;
570
571         findhndl = FindFirstFileA(toMBSz(file), &filefindbuf);
572         resulth = filefindbuf.nFileSizeHigh;
573         resultl = filefindbuf.nFileSizeLow;
574     }
575
576     cenforce(findhndl != cast(HANDLE)-1 && FindClose(findhndl), file);
577     return (cast(ulong) resulth << 32) + resultl;
578 }
579
580 version(Posix) ulong getSize(in char[] name)
581 {
582     struct_stat64 statbuf = void;
583     cenforce(stat64(toStringz(name), &statbuf) == 0, name);
584     return statbuf.st_size;
585 }
586
587 unittest
588 {
589     // create a file of size 1
590     write(deleteme, "a");
591     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
592     assert(getSize(deleteme) == 1);
593     // create a file of size 3
594     write(deleteme, "abc");
595     assert(getSize(deleteme) == 3);
596 }
597
598 /*************************
599  * $(RED Scheduled for deprecation. Please use getTimesWin (for Windows)
600  *       or getTimesPosix (for Posix) instead.)
601  */
602 version(D_Ddoc)
603 {
604     void getTimes(in char[] name,
605             out d_time ftc,
606             out d_time fta,
607             out d_time ftm);
608 }
609 else version(Windows)
610 {
611     void getTimes(C)(in C[] name,
612                      out d_time ftc,
613                      out d_time fta,
614                      out d_time ftm)
615         if(is(Unqual!C == char))
616     {
617         pragma(msg, "std.file.getTimes has been scheduled for deprecation. " ~
618                     "Please use getTimesWin (for Windows) or getTimesPosix (for Posix) "
619                     "instead.");
620
621         HANDLE findhndl = void;
622
623         if (useWfuncs)
624         {
625             WIN32_FIND_DATAW filefindbuf;
626
627             findhndl = FindFirstFileW(std.utf.toUTF16z(name), &filefindbuf);
628             ftc = FILETIME2d_time(&filefindbuf.ftCreationTime);
629             fta = FILETIME2d_time(&filefindbuf.ftLastAccessTime);
630             ftm = FILETIME2d_time(&filefindbuf.ftLastWriteTime);
631         }
632         else
633         {
634             WIN32_FIND_DATA filefindbuf;
635
636             findhndl = FindFirstFileA(toMBSz(name), &filefindbuf);
637             ftc = FILETIME2d_time(&filefindbuf.ftCreationTime);
638             fta = FILETIME2d_time(&filefindbuf.ftLastAccessTime);
639             ftm = FILETIME2d_time(&filefindbuf.ftLastWriteTime);
640         }
641
642         if (findhndl == cast(HANDLE)-1)
643         {
644             throw new FileException(name.idup);
645         }
646         FindClose(findhndl);
647     }
648 }
649 else version(Posix)
650 {
651     void getTimes(C)(in C[] name, out d_time ftc, out d_time fta, out d_time ftm)
652         if(is(Unqual!C == char))
653     {
654         pragma(msg, "std.file.getTimes has been scheduled for deprecation. " ~
655                     "Please use getTimesWin (for Windows) or getTimesPosix (for Posix) "
656                     "instead.");
657
658         struct_stat64 statbuf = void;
659         cenforce(stat64(toStringz(name), &statbuf) == 0, name);
660         ftc = cast(d_time) statbuf.st_ctime * ticksPerSecond;
661         fta = cast(d_time) statbuf.st_atime * ticksPerSecond;
662         ftm = cast(d_time) statbuf.st_mtime * ticksPerSecond;
663     }
664 }
665 else
666     static assert(0, "Unsupported/Unknown OS");
667
668
669 version(D_Ddoc)
670 {
671     /++
672         $(BLUE This function is Windows-Only.)
673
674         Get creation/access/modified times of file $(D name).
675
676         Note that the Windows and Posix versions of getTimesX differ
677         because Posix has no file creation time whereas Windows
678         has no file status changed time.
679
680         Params:
681             name                 = File name to get times for.
682             fileCreationTime     = Time the file was created.
683             fileAccessTime       = Time the file was last accessed.
684             fileModificationTime = Time the file was last modified.
685
686         Throws:
687             $(D FileException) on error.
688      +/
689     void getTimesWin(in char[] name,
690             out SysTime fileCreationTime,
691             out SysTime fileAccessTime,
692             out SysTime fileModificationTime);
693
694
695     /++
696         $(BLUE This function is Posix-Only.)
697
698         Get file status change time, acces time, and modification times
699         of file $(D name).
700
701         Note that the Windows and Posix versions of getTimesX differ
702         because Posix has no file creation time whereas Windows
703         has no file status changed time.
704
705         Params:
706             name                 = File name to get times for.
707             fileStatusChangeTime = Time the file's status was last changed.
708             fileAccessTime       = Time the file was last accessed.
709             fileModificationTime = Time the file was last modified.
710
711         Throws:
712             $(D FileException) on error.
713      +/
714     version(Posix) void getTimesPosix(in char[] name,
715                                       out SysTime fileStatusChangeTime,
716                                       out SysTime fileAccessTime,
717             out SysTime fileModificationTime);
718 }
719 else version(Windows)
720 {
721     void getTimesWin(in char[] name,
722                      out SysTime fileCreationTime,
723                      out SysTime fileAccessTime,
724                      out SysTime fileModificationTime)
725     {
726         HANDLE findhndl = void;
727
728         if (useWfuncs)
729         {
730             WIN32_FIND_DATAW filefindbuf;
731
732             findhndl = FindFirstFileW(std.utf.toUTF16z(name), &filefindbuf);
733             fileCreationTime = std.datetime.FILETIMEToSysTime(&filefindbuf.ftCreationTime);
734             fileAccessTime = std.datetime.FILETIMEToSysTime(&filefindbuf.ftLastAccessTime);
735             fileModificationTime = std.datetime.FILETIMEToSysTime(&filefindbuf.ftLastWriteTime);
736         }
737         else
738         {
739             WIN32_FIND_DATA filefindbuf;
740
741             findhndl = FindFirstFileA(toMBSz(name), &filefindbuf);
742             fileCreationTime = std.datetime.FILETIMEToSysTime(&filefindbuf.ftCreationTime);
743             fileAccessTime = std.datetime.FILETIMEToSysTime(&filefindbuf.ftLastAccessTime);
744             fileModificationTime = std.datetime.FILETIMEToSysTime(&filefindbuf.ftLastWriteTime);
745         }
746
747         if(findhndl == cast(HANDLE)-1)
748         {
749             throw new FileException(name.idup);
750         }
751
752         FindClose(findhndl);
753     }
754
755     unittest
756     {
757         version(Windows)
758         {
759             auto currTime = Clock.currTime();
760
761             write(deleteme, "a");
762             scope(exit) { assert(exists(deleteme)); remove(deleteme); }
763
764             SysTime creationTime1 = void;
765             SysTime accessTime1 = void;
766             SysTime modificationTime1 = void;
767
768             getTimesWin(deleteme, creationTime1, accessTime1, modificationTime1);
769
770             enum leeway = dur!"seconds"(2);
771
772             {
773                 auto diffc = creationTime1 - currTime;
774                 auto diffa = accessTime1 - currTime;
775                 auto diffm = modificationTime1 - currTime;
776
777                 assert(abs(diffc) <= leeway);
778                 assert(abs(diffa) <= leeway);
779                 assert(abs(diffm) <= leeway);
780             }
781
782             Thread.sleep(dur!"seconds"(1));
783
784             currTime = Clock.currTime();
785             write(deleteme, "b");
786
787             SysTime creationTime2 = void;
788             SysTime accessTime2 = void;
789             SysTime modificationTime2 = void;
790
791             getTimesWin(deleteme, creationTime2, accessTime2, modificationTime2);
792
793             {
794                 auto diffa = accessTime2 - currTime;
795                 auto diffm = modificationTime2 - currTime;
796
797                 assert(abs(diffa) <= leeway);
798                 assert(abs(diffm) <= leeway);
799             }
800
801             assert(creationTime1 <= creationTime2);
802             assert(accessTime1 <= accessTime2);
803             assert(modificationTime1 <= modificationTime2);
804         }
805     }
806 }
807 else version(Posix)
808 {
809     void getTimesPosix(in char[] name,
810             out SysTime fileStatusChangeTime,
811             out SysTime fileAccessTime,
812             out SysTime fileModificationTime)
813     {
814         struct_stat64 statbuf = void;
815
816         cenforce(stat64(toStringz(name), &statbuf) == 0, name);
817
818         fileStatusChangeTime = SysTime(unixTimeToStdTime(statbuf.st_ctime));
819         fileAccessTime = SysTime(unixTimeToStdTime(statbuf.st_atime));
820         fileModificationTime = SysTime(unixTimeToStdTime(statbuf.st_mtime));
821     }
822
823     unittest
824     {
825         auto currTime = Clock.currTime();
826
827         write(deleteme, "a");
828         scope(exit) { assert(exists(deleteme)); remove(deleteme); }
829
830         SysTime statusChangedTime1 = void;
831         SysTime accessTime1 = void;
832         SysTime modificationTime1 = void;
833
834         getTimesPosix(deleteme, statusChangedTime1, accessTime1, modificationTime1);
835
836         enum leeway = dur!"seconds"(2);
837
838         {
839             auto diffc = statusChangedTime1 - currTime;
840             auto diffa = accessTime1 - currTime;
841             auto diffm = modificationTime1 - currTime;
842
843             assert(abs(diffc) <= leeway);
844             assert(abs(diffa) <= leeway);
845             assert(abs(diffm) <= leeway);
846         }
847
848         Thread.sleep(dur!"seconds"(1));
849
850         currTime = Clock.currTime();
851         write(deleteme, "b");
852
853         SysTime statusChangedTime2 = void;
854         SysTime accessTime2 = void;
855         SysTime modificationTime2 = void;
856
857         getTimesPosix(deleteme, statusChangedTime2, accessTime2, modificationTime2);
858
859         {
860             auto diffc = statusChangedTime2 - currTime;
861             auto diffa = accessTime2 - currTime;
862             auto diffm = modificationTime2 - currTime;
863
864             assert(abs(diffc) <= leeway);
865             assert(abs(diffa) <= leeway);
866             assert(abs(diffm) <= leeway);
867         }
868
869         assert(statusChangedTime1 <= statusChangedTime2);
870         assert(accessTime1 <= accessTime2);
871             assert(modificationTime1 <= modificationTime2);
872     }
873 }
874 else
875     static assert(0, "Unsupported/Unknown OS");
876
877
878 version(D_Ddoc)
879 {
880     /++
881      $(RED Scheduled for deprecation. Please use
882      $(XREF file,timeLastModified) instead.)
883      +/
884     d_time lastModified(in char[] name);
885 }
886 else
887 {
888     d_time lastModified(C)(in C[] name)
889         if(is(Unqual!C == char))
890     {
891         pragma(msg, "std.file.lastModified has been scheduled for deprecation. " ~
892                     "Please use timeLastModified instead.");
893
894         version(Windows)
895         {
896             d_time dummy = void, ftm = void;
897             getTimes(name, dummy, dummy, ftm);
898             return ftm;
899         }
900         else version(Posix)
901         {
902             struct_stat64 statbuf = void;
903             cenforce(stat64(toStringz(name), &statbuf) == 0, name);
904             return cast(d_time) statbuf.st_mtime * ticksPerSecond;
905         }
906         else
907             static assert(0, "Unsupported/Unknown OS");
908     }
909 }
910
911
912 version(D_Ddoc)
913 {
914     /++
915         $(RED Scheduled for deprecation. Please use timeLastModified instead.)
916     +/
917     d_time lastModified(in char[] name, d_time returnIfMissing);
918 }
919 else version(Windows)
920 {
921     d_time lastModified(C)(in C[] name, d_time returnIfMissing)
922         if(is(Unqual!C == char))
923     {
924         pragma(msg, "std.file.lastModified has been scheduled for deprecation. " ~
925                     "Please use timeLastModified instead.");
926
927         version(Windows)
928         {
929             if (!exists(name)) return returnIfMissing;
930             d_time dummy = void, ftm = void;
931             getTimes(name, dummy, dummy, ftm);
932             return ftm;
933         }
934         else version(Posix)
935         {
936             struct_stat64 statbuf = void;
937             return stat64(toStringz(name), &statbuf) != 0
938                 ? returnIfMissing
939                 : cast(d_time) statbuf.st_mtime * ticksPerSecond;
940         }
941         else
942             static assert(0, "Unsupported/Unknown OS");
943     }
944 }
945
946 unittest
947 {
948     //std.process.system("echo a>deleteme") == 0 || assert(false);
949     if (exists(deleteme)) remove(deleteme);
950     write(deleteme, "a\n");
951     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
952     // assert(lastModified("deleteme") >
953     //         lastModified("this file does not exist", d_time.min));
954     //assert(lastModified("deleteme") > lastModified(__FILE__));
955 }
956
957 /++
958 Returns the time that the given file was last modified.
959
960 Throws:
961 $(D FileException) if the given file does not exist.
962 +/
963 SysTime timeLastModified(in char[] name)
964 {
965     version(Windows)
966     {
967         SysTime dummy = void;
968         SysTime ftm = void;
969
970         getTimesWin(name, dummy, dummy, ftm);
971
972         return ftm;
973     }
974     else version(Posix)
975     {
976         struct_stat64 statbuf = void;
977
978         cenforce(stat64(toStringz(name), &statbuf) == 0, name);
979
980         return SysTime(unixTimeToStdTime(statbuf.st_mtime));
981     }
982     else
983         static assert(0, "Unsupported/Unknown OS");
984 }
985
986
987 /++
988     Returns the time that the given file was last modified. If the
989     file does not exist, returns $(D returnIfMissing).
990
991     A frequent usage pattern occurs in build automation tools such as
992     $(WEB gnu.org/software/make, make) or $(WEB
993     en.wikipedia.org/wiki/Apache_Ant, ant). To check whether file $(D
994     target) must be rebuilt from file $(D source) (i.e., $(D target) is
995     older than $(D source) or does not exist), use the comparison
996     below. The code throws a $(D FileException) if $(D source) does not
997     exist (as it should). On the other hand, the $(D SysTime.min) default
998     makes a non-existing $(D target) seem infinitely old so the test
999     correctly prompts building it.
1000
1001     Params:
1002         name = The name of the file to get the modification time for.
1003         returnIfMissing = The time to return if the given file does not exist.
1004
1005 Examples:
1006 --------------------
1007 if(timeLastModified(source) >= timeLastModified(target, SysTime.min))
1008 {
1009     // must (re)build
1010 }
1011 else
1012 {
1013     // target is up-to-date
1014 }
1015 --------------------
1016 +/
1017 SysTime timeLastModified(in char[] name, SysTime returnIfMissing)
1018 {
1019     version(Windows)
1020     {
1021         if(!exists(name))
1022             return returnIfMissing;
1023
1024         SysTime dummy = void;
1025         SysTime ftm = void;
1026
1027         getTimesWin(name, dummy, dummy, ftm);
1028
1029         return ftm;
1030     }
1031     else version(Posix)
1032     {
1033         struct_stat64 statbuf = void;
1034
1035         return stat64(toStringz(name), &statbuf) != 0 ?
1036                returnIfMissing :
1037                SysTime(unixTimeToStdTime(statbuf.st_mtime));
1038     }
1039     else
1040         static assert(0, "Unsupported/Unknown OS");
1041
1042 }
1043
1044 unittest
1045 {
1046     //std.process.system("echo a > deleteme") == 0 || assert(false);
1047     if(exists(deleteme))
1048         remove(deleteme);
1049
1050     write(deleteme, "a\n");
1051
1052     scope(exit)
1053     {
1054         assert(exists(deleteme));
1055         remove(deleteme);
1056     }
1057
1058     // assert(lastModified("deleteme") >
1059     //         lastModified("this file does not exist", SysTime.min));
1060     //assert(lastModified("deleteme") > lastModified(__FILE__));
1061 }
1062
1063
1064 /++
1065     Returns whether the given file (or directory) exists.
1066  +/
1067 @property bool exists(in char[] name)
1068 {
1069     version(Windows)
1070     {
1071         auto result = useWfuncs
1072 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
1073 // fileio/base/getfileattributes.asp
1074             ? GetFileAttributesW(std.utf.toUTF16z(name))
1075             : GetFileAttributesA(toMBSz(name));
1076         return result != 0xFFFFFFFF;
1077     }
1078     else version(Posix)
1079     {
1080         return access(toStringz(name), 0) == 0;
1081     }
1082     else
1083         static assert(0, "Unsupported/Unknown OS");
1084 }
1085
1086 unittest
1087 {
1088     assert(exists("."));
1089     assert(!exists("this file does not exist"));
1090     write(deleteme, "a\n");
1091     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1092     assert(exists(deleteme));
1093 }
1094
1095
1096 /++
1097  Returns the attributes of the given file.
1098
1099  Note that the file attributes on Windows and Posix systems are
1100  completely different. On Windows, they're what is returned by $(WEB
1101  msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx,
1102  GetFileAttributes), whereas on Posix systems, they're the $(LUCKY
1103  st_mode) value which is part of the $(D stat struct) gotten by
1104  calling the $(WEB en.wikipedia.org/wiki/Stat_%28Unix%29, $(D stat))
1105  function.
1106
1107  On Posix systems, if the given file is a symbolic link, then
1108  attributes are the attributes of the file pointed to by the symbolic
1109  link.
1110
1111  Params:
1112  name = The file to get the attributes of.
1113   +/
1114 uint getAttributes(in char[] name)
1115 {
1116     version(Windows)
1117     {
1118         auto result = useWfuncs ?
1119                       GetFileAttributesW(std.utf.toUTF16z(name)) :
1120                       GetFileAttributesA(toMBSz(name));
1121
1122         enforce(result != uint.max, new FileException(name.idup));
1123
1124         return result;
1125     }
1126     else version(Posix)
1127     {
1128         struct_stat64 statbuf = void;
1129
1130         cenforce(stat64(toStringz(name), &statbuf) == 0, name);
1131
1132         return statbuf.st_mode;
1133     }
1134     else
1135         static assert(0, "Unsupported/Unknown OS");
1136 }
1137
1138
1139 version(D_Ddoc)
1140 {
1141     /++
1142         If the given file is a symbolic link, then this returns the attributes of the
1143         symbolic link itself rather than file that it points to. If the given file
1144         is $(I not) a symbolic link, then this function returns the same result
1145         as getAttributes.
1146
1147         On Windows, getLinkAttributes is identical to getAttributes. It exists on
1148         Windows so that you don't have to special-case code for Windows when dealing
1149         with symbolic links.
1150
1151         Params:
1152             name = The file to get the symbolic link attributes of.
1153
1154         Throws:
1155             FileException on error.
1156      +/
1157     uint getLinkAttributes(in char[] name);
1158 }
1159 else
1160 {
1161     uint getLinkAttributes(in char[] name)
1162     {
1163         version(Windows)
1164         {
1165             return getAttributes(name);
1166         }
1167         else version(OSX)
1168         {
1169             struct_stat64 lstatbuf = void;
1170             cenforce(stat64(toStringz(name), &lstatbuf) == 0, name);
1171             return lstatbuf.st_mode;
1172         }
1173         else version(Posix)
1174         {
1175             struct_stat64 lstatbuf = void;
1176
1177             cenforce(lstat64(toStringz(name), &lstatbuf) == 0, name);
1178
1179             return lstatbuf.st_mode;
1180         }
1181         else
1182             static assert(0, "Unsupported/Unknown OS");
1183     }
1184 }
1185
1186
1187 /++
1188     Returns whether the given file is a directory.
1189
1190     Params:
1191         name = The path to the file.
1192
1193     Throws:
1194         FileException if the given file does not exist.
1195
1196 Examples:
1197 --------------------
1198 assert(!"/etc/fonts/fonts.conf".isDir);
1199 assert("/usr/share/include".isDir);
1200 --------------------
1201   +/
1202 @property bool isDir(in char[] name)
1203 {
1204     version(Windows)
1205     {
1206         return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) != 0;
1207     }
1208     else version(Posix)
1209     {
1210         return (getAttributes(name) & S_IFMT) == S_IFDIR;
1211     }
1212     else
1213         static assert(0, "Unsupported/Unknown OS");
1214 }
1215
1216 unittest
1217 {
1218     version(Windows)
1219     {
1220         if("C:\\Program Files\\".exists)
1221             assert("C:\\Program Files\\".isDir);
1222
1223         if("C:\\Windows\\system.ini".exists)
1224             assert(!"C:\\Windows\\system.ini".isDir);
1225     }
1226     else version(Posix)
1227     {
1228         if("/usr/include".exists)
1229             assert("/usr/include".isDir);
1230
1231         if("/usr/include/assert.h".exists)
1232             assert(!"/usr/include/assert.h".isDir);
1233     }
1234     else
1235         static assert(0, "Unsupported/Unknown OS");
1236 }
1237
1238
1239 /++
1240     Returns whether the given file attributes are for a directory.
1241
1242     Params:
1243         attributes = The file attributes.
1244
1245 Examples:
1246 --------------------
1247 assert(!getAttributes("/etc/fonts/fonts.conf").isDir);
1248 assert(!getLinkAttributes("/etc/fonts/fonts.conf").isDir);
1249 --------------------
1250   +/
1251 @property bool isDir(uint attributes) nothrow
1252 {
1253     version(Windows)
1254     {
1255         return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
1256     }
1257     else version(Posix)
1258     {
1259         return (attributes & S_IFMT) == S_IFDIR;
1260     }
1261     else
1262         static assert(0, "Unsupported/Unknown OS");
1263 }
1264
1265 unittest
1266 {
1267     version(Windows)
1268     {
1269         if("C:\\Program Files\\".exists)
1270         {
1271             assert(isDir(getAttributes("C:\\Program Files\\")));
1272             assert(isDir(getLinkAttributes("C:\\Program Files\\")));
1273         }
1274
1275         if("C:\\Windows\\system.ini".exists)
1276         {
1277             assert(!isDir(getAttributes("C:\\Windows\\system.ini")));
1278             assert(!isDir(getLinkAttributes("C:\\Windows\\system.ini")));
1279         }
1280     }
1281     else version(Posix)
1282     {
1283         if("/usr/include".exists)
1284         {
1285             assert(isDir(getAttributes("/usr/include")));
1286             assert(isDir(getLinkAttributes("/usr/include")));
1287         }
1288
1289         if("/usr/include/assert.h".exists)
1290         {
1291             assert(!isDir(getAttributes("/usr/include/assert.h")));
1292             assert(!isDir(getLinkAttributes("/usr/include/assert.h")));
1293         }
1294     }
1295     else
1296         static assert(0, "Unsupported/Unknown OS");
1297 }
1298
1299 /++
1300     $(RED Scheduled for deprecation. Please use isDir instead.)
1301  +/
1302 alias isDir isdir;
1303
1304
1305 /++
1306     Returns whether the given file (or directory) is a file.
1307
1308     On Windows, if a file is not a directory, then it's a file. So,
1309     either isFile or isDir will return true for any given file.
1310
1311     On Posix systems, if isFile is true, that indicates that the file is
1312     a regular file (e.g. not a block not device). So, on Posix systems,
1313     it's possible for both isFile and isDir to be false for a particular
1314     file (in which case, it's a special file). You can use getAttributes
1315     to get the attributes to figure out what type of special it is, or
1316     you can use dirEntry to get at its statBuf, which is the result from
1317     stat. In either case, see the stat man page for more details.
1318
1319     Params:
1320         name = The path to the file.
1321
1322     Throws:
1323         FileException if the given file does not exist.
1324
1325 Examples:
1326 --------------------
1327 assert("/etc/fonts/fonts.conf".isFile);
1328 assert(!"/usr/share/include".isFile);
1329 --------------------
1330   +/
1331 @property bool isFile(in char[] name)
1332 {
1333     version(Windows)
1334     {
1335         return !name.isDir;
1336     }
1337     else version(Posix)
1338     {
1339         return (getAttributes(name) & S_IFMT) == S_IFREG;
1340     }
1341     else
1342         static assert(0, "Unsupported/Unknown OS");
1343 }
1344
1345 unittest
1346 {
1347     version(Windows)
1348     {
1349         if("C:\\Program Files\\".exists)
1350             assert(!"C:\\Program Files\\".isFile);
1351
1352         if("C:\\Windows\\system.ini".exists)
1353             assert("C:\\Windows\\system.ini".isFile);
1354     }
1355     else version(Posix)
1356     {
1357         if("/usr/include".exists)
1358             assert(!"/usr/include".isFile);
1359
1360         if("/usr/include/assert.h".exists)
1361             assert("/usr/include/assert.h".isFile);
1362     }
1363     else
1364         static assert(0, "Unsupported/Unknown OS");
1365 }
1366
1367
1368 /++
1369     Returns whether the given file attributes are for a file.
1370
1371     On Windows, if a file is not a directory, it's a file. So,
1372     either isFile or isDir will return true for any given file.
1373
1374     On Posix systems, if isFile is true, that indicates that the file is
1375     a regular file (e.g. not a block not device). So, on Posix systems,
1376     it's possible for both isFile and isDir to be false for a particular
1377     file (in which case, it's a special file). If a file is a special
1378     file, you can use the attributes to check what type of special
1379     file it is (see the stat man page for more information).
1380
1381     Params:
1382         attributes = The file attributes.
1383
1384 Examples:
1385 --------------------
1386 assert(getAttributes("/etc/fonts/fonts.conf").isFile);
1387 assert(getLinkAttributes("/etc/fonts/fonts.conf").isFile);
1388 --------------------
1389   +/
1390 @property bool isFile(uint attributes) nothrow
1391 {
1392     version(Windows)
1393     {
1394         return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
1395     }
1396     else version(Posix)
1397     {
1398         return (attributes & S_IFMT) == S_IFREG;
1399     }
1400     else
1401         static assert(0, "Unsupported/Unknown OS");
1402 }
1403
1404 unittest
1405 {
1406     version(Windows)
1407     {
1408         if("C:\\Program Files\\".exists)
1409         {
1410             assert(!isFile(getAttributes("C:\\Program Files\\")));
1411             assert(!isFile(getLinkAttributes("C:\\Program Files\\")));
1412         }
1413
1414         if("C:\\Windows\\system.ini".exists)
1415         {
1416             assert(isFile(getAttributes("C:\\Windows\\system.ini")));
1417             assert(isFile(getLinkAttributes("C:\\Windows\\system.ini")));
1418         }
1419     }
1420     else version(Posix)
1421     {
1422         if("/usr/include".exists)
1423         {
1424             assert(!isFile(getAttributes("/usr/include")));
1425             assert(!isFile(getLinkAttributes("/usr/include")));
1426         }
1427
1428         if("/usr/include/assert.h".exists)
1429         {
1430             assert(isFile(getAttributes("/usr/include/assert.h")));
1431             assert(isFile(getLinkAttributes("/usr/include/assert.h")));
1432         }
1433     }
1434     else
1435         static assert(0, "Unsupported/Unknown OS");
1436 }
1437
1438 /++
1439     $(RED Scheduled for deprecation. Please use isFile instead.)
1440
1441     Same is isFile.
1442  +/
1443 alias isFile isfile;
1444
1445
1446 /++
1447     Returns whether the given file is a symbolic link.
1448
1449     Always return false on Windows. It exists on Windows so that you don't
1450     have to special-case code for Windows when dealing with symbolic links.
1451
1452     Params:
1453         name = The path to the file.
1454
1455     Throws:
1456         FileException if the given file does not exist.
1457   +/
1458 @property bool isSymLink(in char[] name)
1459 {
1460     version(Windows)
1461     {
1462         return false;
1463     }
1464     else version(Posix)
1465     {
1466         return (getLinkAttributes(name) & S_IFMT) == S_IFLNK;
1467     }
1468     else
1469         static assert(0, "Unsupported/Unknown OS");
1470 }
1471
1472 unittest
1473 {
1474     version(Windows)
1475     {
1476         if("C:\\Program Files\\".exists)
1477             assert(!"C:\\Program Files\\".isSymLink);
1478
1479         enum fakeSymFile = "C:\\Windows\\system.ini";
1480         if(fakeSymFile.exists)
1481         {
1482             assert(!fakeSymFile.isSymLink);
1483
1484             assert(!fakeSymFile.isSymLink);
1485             assert(!isSymLink(getAttributes(fakeSymFile)));
1486             assert(!isSymLink(getLinkAttributes(fakeSymFile)));
1487
1488             assert(isFile(getAttributes(fakeSymFile)));
1489             assert(isFile(getLinkAttributes(fakeSymFile)));
1490             assert(!isDir(getAttributes(fakeSymFile)));
1491             assert(!isDir(getLinkAttributes(fakeSymFile)));
1492
1493             assert(getAttributes(fakeSymFile) == getLinkAttributes(fakeSymFile));
1494         }
1495     }
1496     else version(OSX)
1497     {
1498     }
1499     else version(Posix)
1500     {
1501         if("/usr/include".exists)
1502         {
1503             assert(!"/usr/include".isSymLink);
1504
1505             immutable symfile = deleteme ~ "_slink\0";
1506             scope(exit) if(symfile.exists) symfile.remove();
1507
1508             core.sys.posix.unistd.symlink("/usr/include", symfile.ptr);
1509
1510             assert(symfile.isSymLink);
1511             assert(!isSymLink(getAttributes(symfile)));
1512             assert(isSymLink(getLinkAttributes(symfile)));
1513
1514             assert(isDir(getAttributes(symfile)));
1515             assert(!isDir(getLinkAttributes(symfile)));
1516
1517             assert(!isFile(getAttributes(symfile)));
1518             assert(!isFile(getLinkAttributes(symfile)));
1519         }
1520
1521         if("/usr/include/assert.h".exists)
1522         {
1523             assert(!"/usr/include/assert.h".isSymLink);
1524
1525             immutable symfile = deleteme ~ "_slink\0";
1526             scope(exit) if(symfile.exists) symfile.remove();
1527
1528             core.sys.posix.unistd.symlink("/usr/include/assert.h", symfile.ptr);
1529
1530             assert(symfile.isSymLink);
1531             assert(!isSymLink(getAttributes(symfile)));
1532             assert(isSymLink(getLinkAttributes(symfile)));
1533
1534             assert(!isDir(getAttributes(symfile)));
1535             assert(!isDir(getLinkAttributes(symfile)));
1536
1537             assert(isFile(getAttributes(symfile)));
1538             assert(!isFile(getLinkAttributes(symfile)));
1539         }
1540     }
1541     else
1542         static assert(0, "Unsupported/Unknown OS");
1543 }
1544
1545
1546 /++
1547     Returns whether the given file attributes are for a symbolic link.
1548
1549     Always return false on Windows. It exists on Windows so that you don't
1550     have to special-case code for Windows when dealing with symbolic links.
1551
1552     Params:
1553         attributes = The file attributes.
1554
1555 Examples:
1556 --------------------
1557 core.sys.posix.unistd.symlink("/etc/fonts/fonts.conf", "/tmp/alink");
1558
1559 assert(!getAttributes("/tmp/alink").isSymLink);
1560 assert(getLinkAttributes("/tmp/alink").isSymLink);
1561 --------------------
1562   +/
1563 @property bool isSymLink(uint attributes) nothrow
1564 {
1565     version(Windows)
1566     {
1567         return false;
1568     }
1569     else version(Posix)
1570     {
1571         return (attributes & S_IFMT) == S_IFLNK;
1572     }
1573     else
1574         static assert(0, "Unsupported/Unknown OS");
1575 }
1576
1577
1578 /****************************************************
1579  * Change directory to $(D pathname).
1580  * Throws: $(D FileException) on error.
1581  */
1582
1583 version(Windows) void chdir(in char[] pathname)
1584 {
1585     enforce(useWfuncs
1586             ? SetCurrentDirectoryW(std.utf.toUTF16z(pathname))
1587             : SetCurrentDirectoryA(toMBSz(pathname)),
1588             new FileException(pathname.idup));
1589 }
1590
1591 version(Posix) void chdir(in char[] pathname)
1592 {
1593     cenforce(core.sys.posix.unistd.chdir(toStringz(pathname)) == 0,
1594             pathname);
1595 }
1596
1597 /****************************************************
1598 Make directory $(D pathname).
1599
1600 Throws: $(D FileException) on error.
1601  */
1602
1603 version(Windows) void mkdir(in char[] pathname)
1604 {
1605     enforce(useWfuncs
1606             ? CreateDirectoryW(std.utf.toUTF16z(pathname), null)
1607             : CreateDirectoryA(toMBSz(pathname), null),
1608             new FileException(pathname.idup));
1609 }
1610
1611 version(Posix) void mkdir(in char[] pathname)
1612 {
1613     cenforce(core.sys.posix.sys.stat.mkdir(toStringz(pathname), 0777) == 0,
1614             pathname);
1615 }
1616
1617 /****************************************************
1618  * Make directory and all parent directories as needed.
1619  */
1620
1621 void mkdirRecurse(in char[] pathname)
1622 {
1623     const left = dirname(pathname);
1624     if (!exists(left))
1625     {
1626         version (Windows)
1627         {   /* Prevent infinite recursion if left is "d:\" and
1628              * drive d does not exist.
1629              */
1630             if (left.length >= 3 && left[$ - 2] == ':')
1631                 throw new FileException(left.idup);
1632         }
1633         mkdirRecurse(left);
1634     }
1635     if (!basename(pathname).empty)
1636     {
1637         mkdir(pathname);
1638     }
1639 }
1640
1641 unittest
1642 {
1643     // bug3570
1644     {
1645         immutable basepath = deleteme ~ "_dir";
1646         version (Windows)
1647         {
1648             immutable path = basepath ~ `\fake\here\`;
1649         }
1650         else version (Posix)
1651         {
1652             immutable path = basepath ~ `/fake/here/`;
1653         }
1654
1655         mkdirRecurse(path);
1656         assert(basepath.exists && basepath.isDir);
1657         scope(exit) rmdirRecurse(basepath);
1658         assert(path.exists && path.isDir);
1659     }
1660 }
1661
1662 /****************************************************
1663 Remove directory $(D pathname).
1664
1665 Throws: $(D FileException) on error.
1666  */
1667
1668 version(Windows) void rmdir(in char[] pathname)
1669 {
1670     cenforce(useWfuncs
1671             ? RemoveDirectoryW(std.utf.toUTF16z(pathname))
1672             : RemoveDirectoryA(toMBSz(pathname)),
1673             pathname);
1674 }
1675
1676 version(Posix) void rmdir(in char[] pathname)
1677 {
1678     cenforce(core.sys.posix.unistd.rmdir(toStringz(pathname)) == 0,
1679             pathname);
1680 }
1681
1682 /****************************************************
1683  * Get current directory.
1684  * Throws: $(D FileException) on error.
1685  */
1686
1687 version(Windows) string getcwd()
1688 {
1689     // A bit odd API: calling GetCurrentDirectory(0, null) returns
1690     // length including the \0, whereas calling with non-zero
1691     // params returns length excluding the \0.
1692     if (useWfuncs)
1693     {
1694         auto dir =
1695             new wchar[enforce(GetCurrentDirectoryW(0, null), "getcwd")];
1696         dir = dir[0 .. GetCurrentDirectoryW(dir.length, dir.ptr)];
1697         cenforce(dir.length, "getcwd");
1698         return to!string(dir);
1699     }
1700     else
1701     {
1702         auto dir =
1703             new char[enforce(GetCurrentDirectoryA(0, null), "getcwd")];
1704         dir = dir[0 .. GetCurrentDirectoryA(dir.length, dir.ptr)];
1705         cenforce(dir.length, "getcwd");
1706         return assumeUnique(dir);
1707     }
1708 }
1709
1710 version(Posix) string getcwd()
1711 {
1712     auto p = cenforce(core.sys.posix.unistd.getcwd(null, 0),
1713             "cannot get cwd");
1714     scope(exit) std.c.stdlib.free(p);
1715     return p[0 .. std.c.string.strlen(p)].idup;
1716 }
1717
1718 unittest
1719 {
1720     auto s = getcwd();
1721     assert(s.length);
1722 }
1723
1724
1725 version(D_Ddoc)
1726 {
1727     /++
1728         Info on a file, similar to what you'd get from stat on a Posix system.
1729
1730         A DirEntry is obtained by using the functions dirEntry (to get the DirEntry
1731         for a specific file) or dirEntries (to get a DirEntry for each file/directory
1732         in a particular directory).
1733       +/
1734     struct DirEntry
1735     {
1736         void _init(T...)(T);
1737     public:
1738
1739         /++
1740             Returns the path to the file represented by this DirEntry.
1741
1742 Examples:
1743 --------------------
1744 auto de1 = dirEntry("/etc/fonts/fonts.conf");
1745 assert(de1.name == "/etc/fonts/fonts.conf");
1746
1747 auto de2 = dirEntry("/usr/share/include");
1748 assert(de2.name == "/usr/share/include");
1749 --------------------
1750           +/
1751         @property string name() const;
1752
1753
1754         /++
1755             Returns whether the file represented by this DirEntry is a directory.
1756
1757 Examples:
1758 --------------------
1759 auto de1 = dirEntry("/etc/fonts/fonts.conf");
1760 assert(!de1.isDir);
1761
1762 auto de2 = dirEntry("/usr/share/include");
1763 assert(de2.isDir);
1764 --------------------
1765           +/
1766         @property bool isDir();
1767
1768         /++
1769             $(RED Scheduled for deprecation. Please use isDir instead.)
1770
1771             Same as isdir.
1772           +/
1773         alias isDir isdir;
1774
1775
1776         /++
1777             Returns whether the file represented by this DirEntry is a file.
1778
1779             On Windows, if a file is not a directory, then it's a file. So,
1780             either isFile or isDir will return true.
1781
1782             On Posix systems, if isFile is true, that indicates that the file is
1783             a regular file (e.g. not a block not device). So, on Posix systems,
1784             it's possible for both isFile and isDir to be false for a particular
1785             file (in which case, it's a special file). You can use attributes
1786             or statBuf to get more information about a special file (see the
1787             stat man page for more details).
1788
1789 Examples:
1790 --------------------
1791 auto de1 = dirEntry("/etc/fonts/fonts.conf");
1792 assert(de1.isFile);
1793
1794 auto de2 = dirEntry("/usr/share/include");
1795 assert(!de2.isFile);
1796 --------------------
1797           +/
1798         @property bool isFile();
1799
1800         /++
1801             $(RED Scheduled for deprecation. Please use isFile instead.)
1802
1803             Same as isfile.
1804           +/
1805         alias isFile isfile;
1806
1807         /++
1808             Returns whether the file represented by this DirEntry is a symbol link.
1809
1810             Always return false on Windows. It exists on Windows so that you don't
1811             have to special-case code for Windows when dealing with symbolic links.
1812           +/
1813         @property bool isSymLink();
1814
1815         /++
1816             Returns the size of the the file represented by this DirEntry in bytes.
1817           +/
1818         @property ulong size();
1819
1820         /++
1821             $(RED Scheduled for deprecation. Please use timeCreated instead.)
1822
1823             Returns the creation time of the file represented by this DirEntry.
1824
1825             $(RED Note that this property has existed for both Windows and Posix systems
1826                   but that it is $(I incorrect) on Posix systems. Posix systems do not have
1827                   access to the creation time of a file. On Posix systems this property
1828                   has incorrectly been the time that the file's status status last changed.
1829                   If you want that value, use timeStatusChanged.)
1830           +/
1831         @property d_time creationTime() const;
1832
1833         /++
1834             $(BLUE This function is Windows-Only.)
1835
1836             Returns the creation time of the file represented by this DirEntry.
1837           +/
1838         @property SysTime timeCreated() const;
1839
1840
1841         /++
1842             $(BLUE This function is Posix-Only.)
1843
1844             Returns the last time that the status of file represented by this DirEntry
1845             was changed (i.e. owner, group, link count, mode, etc.).
1846           +/
1847         @property SysTime timeStatusChanged();
1848
1849         /++
1850             $(RED Scheduled for deprecation. Please use timeLastAccessed instead.)
1851
1852             Returns the time that the file represented by this DirEntry was last accessed.
1853
1854             Note that many file systems do not update the access time for files (generally
1855             for performance reasons), so there's a good chance that lastAccessTime will
1856             return the same value as lastWriteTime.
1857           +/
1858         @property d_time lastAccessTime();
1859         /++
1860             Returns the time that the file represented by this DirEntry was last accessed.
1861
1862             Note that many file systems do not update the access time for files (generally
1863             for performance reasons), so there's a good chance that timeLastAccessed will
1864             return the same value as timeLastModified.
1865           +/
1866         @property SysTime timeLastAccessed();
1867         /++
1868             $(RED Scheduled for deprecation. Please use timeLastAccessed instead.)
1869
1870             Returns the time that the file represented by this DirEntry was last modified.
1871           +/
1872         @property d_time lastWriteTime();
1873         /++
1874             Returns the time that the file represented by this DirEntry was last modified.
1875           +/
1876         @property SysTime timeLastModified();
1877
1878         /++
1879             Returns the attributes of the file represented by this DirEntry.
1880
1881             Note that the file attributes on Windows and Posix systems are completely
1882             different. On, Windows, they're what is returned by GetFileAttributes
1883             <a href="http://msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx">GetFileAttributes</a>.
1884             Whereas, an Posix systems, they're the st_mode value which is part of the
1885             stat struct gotten by calling stat.
1886
1887             On Posix systems, if the file represented by this DirEntry is a symbolic link,
1888             then attributes are the attributes of the file pointed to by the symbolic link.
1889           +/
1890         @property uint attributes();
1891
1892         /++
1893             On Posix systems, if the file represented by this DirEntry is a symbolic link,
1894             then linkAttributes are the attributes of the symbolic link itself. Otherwise,
1895             linkAttributes is identical to attributes.
1896
1897             On Windows, linkAttributes is identical to attributes. It exists on
1898             Windows so that you don't have to special-case code for Windows when dealing
1899             with symbolic links.
1900           +/
1901         @property uint linkAttributes();
1902
1903         version(Windows) alias void* struct_stat64;
1904
1905         /++
1906             $(BLUE This function is Posix-Only.)
1907
1908             The stat struct gotten from calling stat.
1909           +/
1910         @property struct_stat64 statBuf();
1911     }
1912 }
1913 else version(Windows)
1914 {
1915     struct DirEntry
1916     {
1917     public:
1918
1919         void init(C)(in C[] path)
1920             if(is(Unqual!C == char))
1921         {
1922             pragma(msg, "std.file.DirEntry.init has been scheduled for deprecation. " ~
1923                         "It was not documented before, and you shouldn't need it. " ~
1924                         "Just use std.file.dirEntry to get a DirEntry for an arbitrary file.");
1925
1926             _init(path);
1927         }
1928
1929         void init(C)(in C[] path, in WIN32_FIND_DATA* fd)
1930             if(is(Unqual!C == char))
1931         {
1932             pragma(msg, "std.file.DirEntry.init has been scheduled for deprecation. " ~
1933                         "It was not documented before, and you shouldn't need it. " ~
1934                         "Just use std.file.dirEntry to get a DirEntry for an arbitrary file.");
1935
1936             _init(path, fd);
1937         }
1938
1939         void init(C)(in C[] path, in WIN32_FIND_DATAW* fd)
1940             if(is(Unqual!C == char))
1941         {
1942             pragma(msg, "std.file.DirEntry.init has been scheduled for deprecation. " ~
1943                         "It was not documented before, and you shouldn't need it. " ~
1944                         "Just use std.file.dirEntry to get a DirEntry for an arbitrary file.");
1945
1946             _init(path, fd);
1947         }
1948
1949         @property string name() const
1950         {
1951             return _name;
1952         }
1953
1954         @property bool isDir() const
1955         {
1956             return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
1957         }
1958
1959         alias isDir isdir;
1960
1961         @property bool isFile() const
1962         {
1963             //Are there no options in Windows other than directory and file?
1964             //If there are, then this probably isn't the best way to determine
1965             //whether this DirEntry is a file or not.
1966             return !isDir;
1967         }
1968
1969         alias isFile isfile;
1970
1971         @property bool isSymLink() const
1972         {
1973             return false;
1974         }
1975
1976         @property ulong size() const
1977         {
1978             return _size;
1979         }
1980
1981         @property d_time creationTime() const
1982         {
1983             return sysTimeToDTime(_timeCreated);
1984         }
1985
1986         @property SysTime timeCreated() const
1987         {
1988             return cast(SysTime)_timeCreated;
1989         }
1990
1991         @property d_time lastAccessTime() const
1992         {
1993             return sysTimeToDTime(_timeLastAccessed);
1994         }
1995
1996         @property SysTime timeLastAccessed() const
1997         {
1998             return cast(SysTime)_timeLastAccessed;
1999         }
2000
2001         @property d_time lastWriteTime() const
2002         {
2003             return sysTimeToDTime(_timeLastModified);
2004         }
2005
2006         @property SysTime timeLastModified() const
2007         {
2008             return cast(SysTime)_timeLastModified;
2009         }
2010
2011         @property uint attributes() const
2012         {
2013             return _attributes;
2014         }
2015
2016         @property uint linkAttributes() const
2017         {
2018             return _attributes;
2019         }
2020
2021
2022     private:
2023
2024         void _init(in char[] path)
2025         {
2026             HANDLE findhndl = void;
2027             uint resulth = void;
2028             uint resultl = void;
2029             _name = path.idup;
2030
2031             //FindFirstFileX can't handle file names which end in a backslash.
2032             if(_name.endsWith(sep))
2033                 _name.popBackN(sep.length);
2034
2035             if(useWfuncs)
2036             {
2037                 WIN32_FIND_DATAW fd;
2038
2039                 findhndl = FindFirstFileW(std.utf.toUTF16z(_name), &fd);
2040                 enforce(findhndl != INVALID_HANDLE_VALUE);
2041
2042                 _size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
2043                 _timeCreated = std.datetime.FILETIMEToSysTime(&fd.ftCreationTime);
2044                 _timeLastAccessed = std.datetime.FILETIMEToSysTime(&fd.ftLastAccessTime);
2045                 _timeLastModified = std.datetime.FILETIMEToSysTime(&fd.ftLastWriteTime);
2046                 _attributes = fd.dwFileAttributes;
2047             }
2048             else
2049             {
2050                 WIN32_FIND_DATA fd;
2051
2052                 findhndl = FindFirstFileA(toMBSz(_name), &fd);
2053                 enforce(findhndl != INVALID_HANDLE_VALUE);
2054
2055                 _size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
2056                 _timeCreated = std.datetime.FILETIMEToSysTime(&fd.ftCreationTime);
2057                 _timeLastAccessed = std.datetime.FILETIMEToSysTime(&fd.ftLastAccessTime);
2058                 _timeLastModified = std.datetime.FILETIMEToSysTime(&fd.ftLastWriteTime);
2059                 _attributes = fd.dwFileAttributes;
2060             }
2061
2062             cenforce(findhndl != cast(HANDLE)-1 && FindClose(findhndl), _name);
2063         }
2064
2065         void _init(in char[] path, in WIN32_FIND_DATA* fd)
2066         {
2067             auto clength = std.c.string.strlen(fd.cFileName.ptr);
2068
2069             // Convert cFileName[] to unicode
2070             const wlength = MultiByteToWideChar(0, 0, fd.cFileName.ptr, clength, null, 0);
2071             auto wbuf = new wchar[wlength];
2072             const n = MultiByteToWideChar(0, 0, fd.cFileName.ptr, clength, wbuf.ptr, wlength);
2073             assert(n == wlength);
2074             // toUTF8() returns a new buffer
2075             _name = std.path.join(path, std.utf.toUTF8(wbuf[0 .. wlength]));
2076             _size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
2077             _timeCreated = std.datetime.FILETIMEToSysTime(&fd.ftCreationTime);
2078             _timeLastAccessed = std.datetime.FILETIMEToSysTime(&fd.ftLastAccessTime);
2079             _timeLastModified = std.datetime.FILETIMEToSysTime(&fd.ftLastWriteTime);
2080             _attributes = fd.dwFileAttributes;
2081         }
2082
2083         void _init(in char[] path, in WIN32_FIND_DATAW *fd)
2084         {
2085             size_t clength = std.string.wcslen(fd.cFileName.ptr);
2086             _name = std.utf.toUTF8(fd.cFileName[0 .. clength]);
2087             _name = std.path.join(path, std.utf.toUTF8(fd.cFileName[0 .. clength]));
2088             _size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
2089             _timeCreated = std.datetime.FILETIMEToSysTime(&fd.ftCreationTime);
2090             _timeLastAccessed = std.datetime.FILETIMEToSysTime(&fd.ftLastAccessTime);
2091             _timeLastModified = std.datetime.FILETIMEToSysTime(&fd.ftLastWriteTime);
2092             _attributes = fd.dwFileAttributes;
2093         }
2094
2095
2096         string _name; /// The file or directory represented by this DirEntry.
2097
2098         SysTime _timeCreated;      /// The time when the file was created.
2099         SysTime _timeLastAccessed; /// The time when the file was last accessed.
2100         SysTime _timeLastModified; /// The time when the file was last modified.
2101
2102         ulong _size;       /// The size of the file in bytes.
2103         uint  _attributes; /// The file attributes from WIN32_FIND_DATAW.
2104     }
2105 }
2106 else version(Posix)
2107 {
2108     struct DirEntry
2109     {
2110     public:
2111
2112         void init(C)(in C[] path)
2113             if(is(Unqual!C == char))
2114         {
2115             pragma(msg, "std.file.DirEntry.init has been scheduled for deprecation. " ~
2116                         "It was not documented before, and you shouldn't need it. " ~
2117                         "Just use std.file.dirEntry to get a DirEntry for an arbitrary file.");
2118
2119             _init(path);
2120         }
2121
2122         void init(C)(in C[] path, core.sys.posix.dirent.dirent* fd)
2123             if(is(Unqual!C == char))
2124         {
2125             pragma(msg, "std.file.DirEntry.init has been scheduled for deprecation. " ~
2126                         "It was not documented before, and you shouldn't need it. " ~
2127                         "Just use std.file.dirEntry to get a DirEntry for an arbitrary file.");
2128
2129             _init(path, fd);
2130         }
2131
2132         @property string name() const
2133         {
2134             return _name;
2135         }
2136
2137         @property bool isDir()
2138         {
2139             _ensureStatDone();
2140
2141             return (_statBuf.st_mode & S_IFMT) == S_IFDIR;
2142         }
2143
2144         alias isDir isdir;
2145
2146         @property bool isFile()
2147         {
2148             _ensureStatDone();
2149
2150             return (_statBuf.st_mode & S_IFMT) == S_IFREG;
2151         }
2152
2153         alias isFile isfile;
2154
2155         @property bool isSymLink()
2156         {
2157             _ensureLStatDone();
2158
2159             return (_lstatMode & S_IFMT) == S_IFLNK;
2160         }
2161
2162         // This property was not documented before, and it's almost
2163         // worthless, since the odds are high that it will be DT_UNKNOWN,
2164         // so it continues to be left undocumented.
2165         //
2166         // For the moment, it's still here for backwards compatability, if
2167         // nothing else. It may or may not be deprecated later.
2168         @property ubyte d_type()
2169         {
2170             return _dType;
2171         }
2172
2173         @property ulong size()
2174         {
2175             _ensureStatDone();
2176             return _statBuf.st_size;
2177         }
2178
2179         @property d_time creationTime()
2180         {
2181             _ensureStatDone();
2182
2183             return cast(d_time)_statBuf.st_ctime * ticksPerSecond;
2184         }
2185
2186         @property SysTime timeStatusChanged()
2187         {
2188             _ensureStatDone();
2189
2190             return SysTime(unixTimeToStdTime(_statBuf.st_ctime));
2191         }
2192
2193         @property d_time lastAccessTime()
2194         {
2195             _ensureStatDone();
2196
2197             return cast(d_time)_statBuf.st_atime * ticksPerSecond;
2198         }
2199
2200         @property SysTime timeLastAccessed()
2201         {
2202             _ensureStatDone();
2203
2204             return SysTime(unixTimeToStdTime(_statBuf.st_ctime));
2205         }
2206
2207         @property d_time lastWriteTime()
2208         {
2209             _ensureStatDone();
2210
2211             return cast(d_time)_statBuf.st_mtime * ticksPerSecond;
2212         }
2213
2214         @property SysTime timeLastModified()
2215         {
2216             _ensureStatDone();
2217
2218             return SysTime(unixTimeToStdTime(_statBuf.st_mtime));
2219         }
2220
2221         @property uint attributes()
2222         {
2223             _ensureStatDone();
2224
2225             return _statBuf.st_mode;
2226         }
2227
2228         @property uint linkAttributes()
2229         {
2230             _ensureLStatDone();
2231
2232             return _lstatMode;
2233         }
2234
2235         @property struct_stat64 statBuf()
2236         {
2237             _ensureStatDone();
2238
2239             return _statBuf;
2240         }
2241
2242     private:
2243
2244         void _init(in char[] path)
2245         {
2246             _name = path.idup;
2247
2248             _didLStat = false;
2249             _didStat = false;
2250             _dTypeSet = false;
2251         }
2252
2253         void _init(in char[] path, core.sys.posix.dirent.dirent* fd)
2254         {
2255             immutable len = std.c.string.strlen(fd.d_name.ptr);
2256             _name = std.path.join(path, fd.d_name[0 .. len]);
2257
2258             _didLStat = false;
2259             _didStat = false;
2260
2261             //fd_d_type doesn't work for all file systems,
2262             //in which case the result is DT_UNKOWN. But we
2263             //can determine the correct type from lstat, so
2264             //we'll only set the dtype here if we could
2265             //correctly determine it (not lstat in the case
2266             //of DT_UNKNOWN in case we don't ever actually
2267             //need the dtype, thus potentially avoiding the
2268             //cost of calling lstat).
2269             if(fd.d_type != DT_UNKNOWN)
2270             {
2271                 _dType = fd.d_type;
2272                 _dTypeSet = true;
2273             }
2274             else
2275                 _dTypeSet = false;
2276         }
2277
2278         /++
2279             This is to support lazy evaluation, because doing stat's is
2280             expensive and not always needed.
2281          +/
2282         void _ensureStatDone()
2283         {
2284             if(_didStat)
2285                 return;
2286
2287             enforce(stat64(toStringz(_name), &_statBuf) == 0,
2288                     "Failed to stat file `" ~ _name ~ "'");
2289
2290             _didStat = true;
2291         }
2292
2293         /++
2294             This is to support lazy evaluation, because doing stat's is
2295             expensive and not always needed.
2296          +/
2297         void _ensureLStatDone()
2298         {
2299             if(_didLStat)
2300                 return;
2301
2302             struct_stat64 statbuf = void;
2303
2304             version (OSX)
2305             {
2306                 enforce(stat64(toStringz(_name), &statbuf) == 0,
2307                         "Failed to stat file `" ~ _name ~ "'");
2308             }
2309             else
2310             {
2311                 enforce(lstat64(toStringz(_name), &statbuf) == 0,
2312                         "Failed to stat file `" ~ _name ~ "'");
2313             }
2314
2315             _lstatMode = statbuf.st_mode;
2316
2317             _dTypeSet = true;
2318             _didLStat = true;
2319         }
2320
2321
2322         string _name; /// The file or directory represented by this DirEntry.
2323
2324         struct_stat64 _statBuf = void;  /// The result of stat().
2325         uint  _lstatMode;               /// The stat mode from lstat().
2326         ubyte _dType;                   /// The type of the file.
2327
2328         bool _didLStat = false;   /// Whether lstat() has been called for this DirEntry.
2329         bool _didStat = false;    /// Whether stat() has been called for this DirEntry.
2330         bool _dTypeSet = false;   /// Whether the dType of the file has been set.
2331     }
2332 }
2333 else
2334 {
2335     static assert(0, "Unsupported/Unknown OS");
2336 }
2337
2338 unittest
2339 {
2340     version(Windows)
2341     {
2342         if("C:\\Program Files\\".exists)
2343         {
2344             auto de = dirEntry("C:\\Program Files\\");
2345             assert(!de.isFile);
2346             assert(de.isDir);
2347             assert(!de.isSymLink);
2348         }
2349
2350         if("C:\\Windows\\system.ini".exists)
2351         {
2352             auto de = dirEntry("C:\\Windows\\system.ini");
2353             assert(de.isFile);
2354             assert(!de.isDir);
2355             assert(!de.isSymLink);
2356         }
2357     }
2358     else version(OSX)
2359     {
2360     }
2361     else version(Posix)
2362     {
2363         if("/usr/include".exists)
2364         {
2365             {
2366                 auto de = dirEntry("/usr/include");
2367                 assert(!de.isFile);
2368                 assert(de.isDir);
2369                 assert(!de.isSymLink);
2370             }
2371
2372             immutable symfile = deleteme ~ "_slink\0";
2373             scope(exit) if(symfile.exists) symfile.remove();
2374
2375             core.sys.posix.unistd.symlink("/usr/include", symfile.ptr);
2376
2377             {
2378                 auto de = dirEntry(symfile);
2379                 assert(!de.isFile);
2380                 assert(de.isDir);
2381                 assert(de.isSymLink);
2382             }
2383         }
2384
2385         if("/usr/include/assert.h".exists)
2386         {
2387             auto de = dirEntry("/usr/include/assert.h");
2388             assert(de.isFile);
2389             assert(!de.isDir);
2390             assert(!de.isSymLink);
2391         }
2392     }
2393     else
2394         static assert(0, "Unsupported/Unknown OS");
2395 }
2396
2397
2398 /******************************************************
2399  * $(RED Scheduled for deprecation. Please use dirEntries instead.)
2400  *
2401  * For each file and directory DirEntry in pathname[],
2402  * pass it to the callback delegate.
2403  *
2404  * Params:
2405  *        callback =        Delegate that processes each
2406  *                        DirEntry in turn. Returns true to
2407  *                        continue, false to stop.
2408  * Example:
2409  *        This program lists all the files in its
2410  *        path argument and all subdirectories thereof.
2411  * ----
2412  * import std.stdio;
2413  * import std.file;
2414  *
2415  * void main(string[] args)
2416  * {
2417  *    bool callback(DirEntry* de)
2418  *    {
2419  *      if(de.isDir)
2420  *        listdir(de.name, &callback);
2421  *      else
2422  *        writefln(de.name);
2423
2424  *      return true;
2425  *    }
2426  *
2427  *    listdir(args[1], &callback);
2428  * }
2429  * ----
2430  */
2431 void listdir(in char[] pathname, bool delegate(DirEntry* de) callback)
2432 {
2433     _listDir(pathname, callback);
2434 }
2435
2436
2437 /***************************************************
2438 Copy file $(D from) to file $(D to). File timestamps are preserved.
2439  */
2440
2441 version(Windows) void copy(in char[] from, in char[] to)
2442 {
2443     immutable result = useWfuncs
2444         ? CopyFileW(std.utf.toUTF16z(from), std.utf.toUTF16z(to), false)
2445         : CopyFileA(toMBSz(from), toMBSz(to), false);
2446     if (!result)
2447         throw new FileException(to.idup);
2448 }
2449
2450 version(Posix) void copy(in char[] from, in char[] to)
2451 {
2452     immutable fd = core.sys.posix.fcntl.open(toStringz(from), O_RDONLY);
2453     cenforce(fd != -1, from);
2454     scope(exit) core.sys.posix.unistd.close(fd);
2455
2456     struct_stat64 statbuf = void;
2457     cenforce(fstat64(fd, &statbuf) == 0, from);
2458     //cenforce(core.sys.posix.sys.stat.fstat(fd, &statbuf) == 0, from);
2459
2460     auto toz = toStringz(to);
2461     immutable fdw = core.sys.posix.fcntl.open(toz,
2462             O_CREAT | O_WRONLY | O_TRUNC, octal!666);
2463     cenforce(fdw != -1, from);
2464     scope(failure) std.c.stdio.remove(toz);
2465     {
2466         scope(failure) core.sys.posix.unistd.close(fdw);
2467         auto BUFSIZ = 4096u * 16;
2468         auto buf = std.c.stdlib.malloc(BUFSIZ);
2469         if (!buf)
2470         {
2471             BUFSIZ = 4096;
2472             buf = std.c.stdlib.malloc(BUFSIZ);
2473             buf || assert(false, "Out of memory in std.file.copy");
2474         }
2475         scope(exit) std.c.stdlib.free(buf);
2476
2477         for (auto size = statbuf.st_size; size; )
2478         {
2479             immutable toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size;
2480             cenforce(
2481                 core.sys.posix.unistd.read(fd, buf, toxfer) == toxfer
2482                 && core.sys.posix.unistd.write(fdw, buf, toxfer) == toxfer,
2483                 from);
2484             assert(size >= toxfer);
2485             size -= toxfer;
2486         }
2487     }
2488
2489     cenforce(core.sys.posix.unistd.close(fdw) != -1, from);
2490
2491     utimbuf utim = void;
2492     utim.actime = cast(time_t)statbuf.st_atime;
2493     utim.modtime = cast(time_t)statbuf.st_mtime;
2494
2495     cenforce(utime(toz, &utim) != -1, from);
2496 }
2497
2498 version(D_Ddoc)
2499 {
2500     /++
2501         $(RED Scheduled for deprecation. Please use the version which takes
2502               std.datetime.SysTime instead).
2503
2504         Set access/modified times of file $(D_PARAM name).
2505
2506         Throws:
2507             $(D_PARAM FileException) on error.
2508      +/
2509     void setTimes(in char[] name, d_time fta, d_time ftm);
2510 }
2511 else
2512 {
2513     void setTimes(C)(in C[] name, d_time fta, d_time ftm)
2514         if(is(Unqual!C == char))
2515     {
2516         pragma(msg, "The version of std.file.setTimes which takes std.date.d_time " ~
2517                     "has been scheduled for deprecation. " ~
2518                     "Please use the version which takes std.datetime.SysTime instead.");
2519
2520         version(Windows)
2521         {
2522             const ta = d_time2FILETIME(fta);
2523             const tm = d_time2FILETIME(ftm);
2524             alias TypeTuple!(GENERIC_WRITE, 0, null, OPEN_EXISTING,
2525                     FILE_ATTRIBUTE_NORMAL, HANDLE.init)
2526                 defaults;
2527             auto h = useWfuncs
2528                 ? CreateFileW(std.utf.toUTF16z(name), defaults)
2529                 : CreateFileA(toMBSz(name), defaults);
2530             cenforce(h != INVALID_HANDLE_VALUE, name);
2531             scope(exit) cenforce(CloseHandle(h), name);
2532
2533             cenforce(SetFileTime(h, null, &ta, &tm), name);
2534         }
2535         else version(Posix)
2536         {
2537             timeval[2] t = void;
2538             t[0].tv_sec = to!int(fta / ticksPerSecond);
2539             t[0].tv_usec = cast(int)
2540                 (cast(long) ((cast(double) fta / ticksPerSecond)
2541                         * 1_000_000) % 1_000_000);
2542             t[1].tv_sec = to!int(ftm / ticksPerSecond);
2543             t[1].tv_usec = cast(int)
2544                 (cast(long) ((cast(double) ftm / ticksPerSecond)
2545                         * 1_000_000) % 1_000_000);
2546             enforce(utimes(toStringz(name), t) == 0);
2547         }
2548         else
2549             static assert(0, "Unsupported/Unknown OS");
2550     }
2551 }
2552
2553
2554 version(D_Ddoc)
2555 {
2556     /++
2557         Set access/modified times of file $(D name).
2558
2559         Params:
2560             fileAccessTime       = Time the file was last accessed.
2561             fileModificationTime = Time the file was last modified.
2562
2563         Throws:
2564             $(D FileException) on error.
2565      +/
2566     void setTimes(in char[] name,
2567                   SysTime fileAccessTime,
2568             SysTime fileModificationTime);
2569 }
2570 else
2571 {
2572     void setTimes(C)(in C[] name,
2573                      SysTime fileAccessTime,
2574                      SysTime fileModificationTime)
2575         if(is(Unqual!C == char))
2576     {
2577         version(Windows)
2578         {
2579             const ta = SysTimeToFILETIME(fileAccessTime);
2580             const tm = SysTimeToFILETIME(fileModificationTime);
2581             alias TypeTuple!(GENERIC_WRITE,
2582                              0,
2583                              null,
2584                              OPEN_EXISTING,
2585                              FILE_ATTRIBUTE_NORMAL, HANDLE.init)
2586                   defaults;
2587             auto h = useWfuncs ?
2588                      CreateFileW(std.utf.toUTF16z(name), defaults) :
2589                      CreateFileA(toMBSz(name), defaults);
2590
2591             cenforce(h != INVALID_HANDLE_VALUE, name);
2592
2593             scope(exit)
2594                 cenforce(CloseHandle(h), name);
2595
2596             cenforce(SetFileTime(h, null, &ta, &tm), name);
2597         }
2598         else version(Posix)
2599         {
2600             timeval[2] t = void;
2601
2602             t[0] = fileAccessTime.toTimeVal();
2603             t[1] = fileModificationTime.toTimeVal();
2604
2605             enforce(utimes(toStringz(name), t) == 0);
2606         }
2607         else
2608             static assert(0, "Unsupported/Unknown OS");
2609     }
2610 }
2611
2612 /+
2613 unittest
2614 {
2615     write(deleteme, "a\n");
2616     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
2617     SysTime ftc1, fta1, ftm1;
2618     getTimes(deleteme, ftc1, fta1, ftm1);
2619     enforce(collectException(setTimes("nonexistent", fta1, ftm1)));
2620     setTimes(deleteme, fta1 + dur!"seconds"(50), ftm1 + dur!"seconds"(50));
2621     SysTime ftc2, fta2, ftm2;
2622     getTimes(deleteme, ftc2, fta2, ftm2);
2623     assert(fta1 + dur!"seconds(50) == fta2, text(fta1 + dur!"seconds(50), "!=", fta2));
2624     assert(ftm1 + dur!"seconds(50) == ftm2);
2625 }
2626 +/
2627
2628
2629 /++
2630     Remove directory and all of its content and subdirectories,
2631     recursively.
2632
2633     Throws:
2634         FileException if there is an error (including if the given
2635         file is not a directory).
2636  +/
2637 void rmdirRecurse(in char[] pathname)
2638 {
2639     DirEntry de = dirEntry(pathname);
2640
2641     rmdirRecurse(de);
2642 }
2643
2644
2645 /++
2646     Remove directory and all of its content and subdirectories,
2647     recursively.
2648
2649     Throws:
2650         FileException if there is an error (including if the given
2651         file is not a directory).
2652  +/
2653 void rmdirRecurse(ref DirEntry de)
2654 {
2655     if(!de.isDir)
2656         throw new FileException(text("File ", de.name, " is not a directory"));
2657
2658     if(de.isSymLink())
2659         remove(de.name);
2660     else
2661     {
2662         // all children, recursively depth-first
2663         foreach(DirEntry e; dirEntries(de.name, SpanMode.depth, false))
2664         {
2665             isDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name);
2666         }
2667
2668         // the dir itself
2669         rmdir(de.name);
2670     }
2671 }
2672
2673 version(Windows) unittest
2674 {
2675     auto d = deleteme ~ r".dir\a\b\c\d\e\f\g";
2676
2677     mkdirRecurse(d);
2678     rmdirRecurse(deleteme ~ ".dir");
2679     enforce(!exists(deleteme ~ ".dir"));
2680 }
2681
2682 version(Posix) unittest
2683 {
2684     version(OSX)
2685     {
2686     }
2687     else
2688     {
2689         auto d = "/tmp/deleteme/a/b/c/d/e/f/g";
2690         enforce(collectException(mkdir(d)));
2691         mkdirRecurse(d);
2692         core.sys.posix.unistd.symlink("/tmp/deleteme/a/b/c", "/tmp/deleteme/link");
2693         rmdirRecurse("/tmp/deleteme/link");
2694         enforce(exists(d));
2695         rmdirRecurse("/tmp/deleteme");
2696         enforce(!exists("/tmp/deleteme"));
2697
2698         d = "/tmp/deleteme/a/b/c/d/e/f/g";
2699         mkdirRecurse(d);
2700         std.process.system("ln -sf /tmp/deleteme/a/b/c /tmp/deleteme/link");
2701         rmdirRecurse("/tmp/deleteme");
2702         enforce(!exists("/tmp/deleteme"));
2703     }
2704 }
2705
2706 unittest
2707 {
2708     void[] buf;
2709
2710     buf = new void[10];
2711     (cast(byte[])buf)[] = 3;
2712     if (exists("unittest_write.tmp")) remove("unittest_write.tmp");
2713     write("unittest_write.tmp", buf);
2714     void buf2[] = read("unittest_write.tmp");
2715     assert(buf == buf2);
2716
2717     copy("unittest_write.tmp", "unittest_write2.tmp");
2718     buf2 = read("unittest_write2.tmp");
2719     assert(buf == buf2);
2720
2721     remove("unittest_write.tmp");
2722     assert(!exists("unittest_write.tmp"));
2723     remove("unittest_write2.tmp");
2724     assert(!exists("unittest_write2.tmp"));
2725 }
2726
2727 unittest
2728 {
2729     _listDir(".", delegate bool (DirEntry * de)
2730     {
2731         version(Windows)
2732         {
2733             auto s = std.string.format("%s : c %s, w %s, a %s",
2734                                        de.name,
2735                                        de.timeCreated,
2736                                        de.timeLastModified,
2737                                        de.timeLastAccessed);
2738         }
2739         else version(Posix)
2740         {
2741             auto s = std.string.format("%s : c %s, w %s, a %s",
2742                                        de.name,
2743                                        de.timeStatusChanged,
2744                                        de.timeLastModified,
2745                                        de.timeLastAccessed);
2746         }
2747         else
2748             static assert(0, "Unsupported/Unknown OS");
2749
2750         return true;
2751     }
2752     );
2753 }
2754
2755 /**
2756  * Dictates directory spanning policy for $(D_PARAM dirEntries) (see below).
2757  */
2758 enum SpanMode
2759 {
2760     /** Only spans one directory. */
2761     shallow,
2762     /** Spans the directory depth-first, i.e. the content of any
2763      subdirectory is spanned before that subdirectory itself. Useful
2764      e.g. when recursively deleting files.  */
2765     depth,
2766     /** Spans the directory breadth-first, i.e. the content of any
2767      subdirectory is spanned right after that subdirectory itself. */
2768     breadth,
2769 }
2770
2771 struct DirIterator
2772 {
2773     string pathname;
2774     SpanMode mode;
2775
2776     // Whether we should follow symlinked directories while iterating.
2777     // It also indicates whether we should avoid functions which call
2778     // stat (since we should only need lstat in this case and it would
2779     // be more efficient to not call stat in addition to lstat).
2780     bool followSymLinks;
2781
2782     private int doIt(D)(D dg, DirEntry* de)
2783     {
2784         alias ParameterTypeTuple!D Parms;
2785
2786         static if(is(Parms[0] : const(char)[]))
2787         {
2788             return dg(de.name[]);
2789         }
2790         else static if(is(Parms[0] : DirEntry))
2791         {
2792             return dg(*de);
2793         }
2794         else
2795         {
2796             static assert(0, "Dunno how to enumerate directory entries "
2797                              "against type " ~ Parms[0].stringof);
2798         }
2799     }
2800
2801     int opApply(D)(scope D dg)
2802     {
2803         int result = 0;
2804         // worklist used only in breadth-first traversal
2805         string[] worklist = [pathname];
2806
2807         bool callback(DirEntry* de)
2808         {
2809             switch(mode)
2810             {
2811                 case SpanMode.shallow:
2812                 {
2813                     result = doIt(dg, de);
2814                     break;
2815                 }
2816                 case SpanMode.breadth:
2817                 {
2818                     result = doIt(dg, de);
2819
2820                     if(!result && (followSymLinks ? de.isDir
2821                                                   : isDir(de.linkAttributes)))
2822                     {
2823                         worklist ~= de.name;
2824                     }
2825
2826                     break;
2827                 }
2828                 default:
2829                 {
2830                     assert(mode == SpanMode.depth);
2831
2832                     if(followSymLinks ? de.isdir
2833                                       : isdir(de.linkAttributes))
2834                     {
2835                         _listDir(de.name, &callback);
2836                     }
2837
2838                     if(!result)
2839                         result = doIt(dg, de);
2840
2841                     break;
2842                 }
2843             }
2844
2845             return result == 0;
2846         }
2847
2848         while(!worklist.empty)
2849         {
2850             auto listThis = worklist.back;
2851             worklist.popBack();
2852             _listDir(listThis, &callback);
2853         }
2854
2855         return result;
2856     }
2857 }
2858
2859
2860 /++
2861     Iterates a directory using foreach. The iteration variable can be
2862     of type $(D_PARAM string) if only the name is needed, or $(D_PARAM
2863     DirEntry) if additional details are needed. The span mode dictates
2864     the how the directory is traversed. The name of the directory entry
2865     includes the $(D_PARAM path) prefix.
2866
2867     Params:
2868         path = The directory to iterato over.
2869         mode = Whether the directory's sub-directories should be iterated
2870                over depth-first ($(D_PARAM depth)), breadth-first
2871                ($(D_PARAM breadth)), or not at all ($(D_PARAM shallow)).
2872         followSymLinks = Whether symbolic links which point to directories
2873                          should be treated as directories and their contents
2874                          iterated over. Ignored on Windows.
2875
2876 Examples:
2877 --------------------
2878 // Iterate a directory in depth
2879 foreach (string name; dirEntries("destroy/me", SpanMode.depth))
2880 {
2881  remove(name);
2882 }
2883 // Iterate a directory in breadth
2884 foreach (string name; dirEntries(".", SpanMode.breadth))
2885 {
2886  writeln(name);
2887 }
2888 // Iterate a directory and get detailed info about it
2889 foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth))
2890 {
2891  writeln(e.name, "\t", e.size);
2892 }
2893 --------------------
2894  +/
2895 DirIterator dirEntries(string path, SpanMode mode, bool followSymLinks = true)
2896 {
2897     DirIterator result;
2898
2899     result.pathname = path;
2900     result.mode = mode;
2901     result.followSymLinks = followSymLinks;
2902
2903     return result;
2904 }
2905
2906 unittest
2907 {
2908     version (linux)
2909     {
2910         assert(std.process.system("mkdir --parents dmd-testing") == 0);
2911         scope(exit) std.process.system("rm -rf dmd-testing");
2912         assert(std.process.system("mkdir --parents dmd-testing/somedir") == 0);
2913         assert(std.process.system("touch dmd-testing/somefile") == 0);
2914         assert(std.process.system("touch dmd-testing/somedir/somedeepfile")
2915                 == 0);
2916         foreach (string name; dirEntries("dmd-testing", SpanMode.shallow))
2917         {
2918         }
2919         foreach (string name; dirEntries("dmd-testing", SpanMode.depth))
2920         {
2921             //writeln(name);
2922         }
2923         foreach (string name; dirEntries("dmd-testing", SpanMode.breadth))
2924         {
2925             //writeln(name);
2926         }
2927         foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth))
2928         {
2929             //writeln(e.name);
2930         }
2931
2932         foreach (DirEntry e; dirEntries("/usr/share/zoneinfo", SpanMode.depth))
2933         {
2934             assert(e.isFile || e.isDir, e.name);
2935         }
2936     }
2937 }
2938
2939 /++
2940     Returns a DirEntry for the given file (or directory).
2941
2942     Params:
2943         name = The file (or directory) to get a DirEntry for.
2944
2945     Throws:
2946         FileException) if the file does not exist.
2947  +/
2948 DirEntry dirEntry(in char[] name)
2949 {
2950     if(!name.exists)
2951         throw new FileException(text("File ", name, " does not exist."));
2952
2953     DirEntry dirEntry;
2954
2955     dirEntry._init(name);
2956
2957     return dirEntry;
2958 }
2959
2960 //Test dirEntry with a directory.
2961 unittest
2962 {
2963     auto before = Clock.currTime();
2964     Thread.sleep(dur!"seconds"(1));
2965     immutable path = deleteme ~ "_dir";
2966     scope(exit) { if(path.exists) rmdirRecurse(path); }
2967
2968     mkdir(path);
2969     Thread.sleep(dur!"seconds"(1));
2970     auto de = dirEntry(path);
2971     assert(de.name == path);
2972     assert(de.isDir);
2973     assert(!de.isFile);
2974     assert(!de.isSymLink);
2975
2976     assert(de.isDir == path.isDir);
2977     assert(de.isFile == path.isFile);
2978     assert(de.isSymLink == path.isSymLink);
2979     assert(de.size == path.getSize());
2980     assert(de.attributes == getAttributes(path));
2981     assert(de.linkAttributes == getLinkAttributes(path));
2982
2983     auto now = Clock.currTime();
2984     assert(de.timeLastAccessed > before);
2985     assert(de.timeLastAccessed < now);
2986     assert(de.timeLastModified > before);
2987     assert(de.timeLastModified < now);
2988
2989     assert(isDir(de.attributes));
2990     assert(isDir(de.linkAttributes));
2991     assert(!isFile(de.attributes));
2992     assert(!isFile(de.linkAttributes));
2993     assert(!isSymLink(de.attributes));
2994     assert(!isSymLink(de.linkAttributes));
2995
2996     version(Windows)
2997     {
2998         assert(de.timeCreated > before);
2999         assert(de.timeCreated < now);
3000     }
3001     else version(Posix)
3002     {
3003         assert(de.timeStatusChanged > before);
3004         assert(de.timeStatusChanged < now);
3005         assert(de.attributes == de.statBuf.st_mode);
3006     }
3007 }
3008
3009 //Test dirEntry with a file.
3010 unittest
3011 {
3012     auto before = Clock.currTime();
3013     Thread.sleep(dur!"seconds"(1));
3014     immutable path = deleteme ~ "_file";
3015     scope(exit) { if(path.exists) remove(path); }
3016
3017     write(path, "hello world");
3018     Thread.sleep(dur!"seconds"(1));
3019     auto de = dirEntry(path);
3020     assert(de.name == path);
3021     assert(!de.isDir);
3022     assert(de.isFile);
3023     assert(!de.isSymLink);
3024
3025     assert(de.isDir == path.isDir);
3026     assert(de.isFile == path.isFile);
3027     assert(de.isSymLink == path.isSymLink);
3028     assert(de.size == path.getSize());
3029     assert(de.attributes == getAttributes(path));
3030     assert(de.linkAttributes == getLinkAttributes(path));
3031
3032     auto now = Clock.currTime();
3033     assert(de.timeLastAccessed > before);
3034     assert(de.timeLastAccessed < now);
3035     assert(de.timeLastModified > before);
3036     assert(de.timeLastModified < now);
3037
3038     assert(!isDir(de.attributes));
3039     assert(!isDir(de.linkAttributes));
3040     assert(isFile(de.attributes));
3041     assert(isFile(de.linkAttributes));
3042     assert(!isSymLink(de.attributes));
3043     assert(!isSymLink(de.linkAttributes));
3044
3045     version(Windows)
3046     {
3047         assert(de.timeCreated > before);
3048         assert(de.timeCreated < now);
3049     }
3050     else version(Posix)
3051     {
3052         assert(de.timeStatusChanged > before);
3053         assert(de.timeStatusChanged < now);
3054         assert(de.attributes == de.statBuf.st_mode);
3055     }
3056 }
3057
3058 //Test dirEntry with a symlink to a directory.
3059 version(linux) unittest
3060 {
3061     auto before = Clock.currTime();
3062     Thread.sleep(dur!"seconds"(1));
3063     immutable orig = deleteme ~ "_dir";
3064     mkdir(orig);
3065     immutable path = deleteme ~ "_slink";
3066     scope(exit) { if(orig.exists) rmdirRecurse(orig); }
3067     scope(exit) { if(path.exists) remove(path); }
3068
3069     core.sys.posix.unistd.symlink((orig ~ "\0").ptr, (path ~ "\0").ptr);
3070     Thread.sleep(dur!"seconds"(1));
3071     auto de = dirEntry(path);
3072     assert(de.name == path);
3073     assert(de.isDir);
3074     assert(!de.isFile);
3075     assert(de.isSymLink);
3076
3077     assert(de.isDir == path.isDir);
3078     assert(de.isFile == path.isFile);
3079     assert(de.isSymLink == path.isSymLink);
3080     assert(de.size == path.getSize());
3081     assert(de.attributes == getAttributes(path));
3082     assert(de.linkAttributes == getLinkAttributes(path));
3083
3084     auto now = Clock.currTime();
3085     assert(de.timeLastAccessed > before);
3086     assert(de.timeLastAccessed < now);
3087     assert(de.timeLastModified > before);
3088     assert(de.timeLastModified < now);
3089
3090     assert(isDir(de.attributes));
3091     assert(!isDir(de.linkAttributes));
3092     assert(!isFile(de.attributes));
3093     assert(!isFile(de.linkAttributes));
3094     assert(!isSymLink(de.attributes));
3095     assert(isSymLink(de.linkAttributes));
3096
3097     assert(de.timeStatusChanged > before);
3098     assert(de.timeStatusChanged < now);
3099     assert(de.attributes == de.statBuf.st_mode);
3100 }
3101
3102 //Test dirEntry with a symlink to a file.
3103 version(linux) unittest
3104 {
3105     auto before = Clock.currTime();
3106     Thread.sleep(dur!"seconds"(1));
3107     immutable orig = deleteme ~ "_file";
3108     write(orig, "hello world");
3109     immutable path = deleteme ~ "_slink";
3110     scope(exit) { if(orig.exists) remove(orig); }
3111     scope(exit) { if(path.exists) remove(path); }
3112
3113     core.sys.posix.unistd.symlink((orig ~ "\0").ptr, (path ~ "\0").ptr);
3114     Thread.sleep(dur!"seconds"(1));
3115     auto de = dirEntry(path);
3116     assert(de.name == path);
3117     assert(!de.isDir);
3118     assert(de.isFile);
3119     assert(de.isSymLink);
3120
3121     assert(de.isDir == path.isDir);
3122     assert(de.isFile == path.isFile);
3123     assert(de.isSymLink == path.isSymLink);
3124     assert(de.size == path.getSize());
3125     assert(de.attributes == getAttributes(path));
3126     assert(de.linkAttributes == getLinkAttributes(path));
3127
3128     auto now = Clock.currTime();
3129     assert(de.timeLastAccessed > before);
3130     assert(de.timeLastAccessed < now);
3131     assert(de.timeLastModified > before);
3132     assert(de.timeLastModified < now);
3133
3134     assert(!isDir(de.attributes));
3135     assert(!isDir(de.linkAttributes));
3136     assert(isFile(de.attributes));
3137     assert(!isFile(de.linkAttributes));
3138     assert(!isSymLink(de.attributes));
3139     assert(isSymLink(de.linkAttributes));
3140
3141     assert(de.timeStatusChanged > before);
3142     assert(de.timeStatusChanged < now);
3143     assert(de.attributes == de.statBuf.st_mode);
3144 }
3145
3146
3147 /**
3148 Reads an entire file into an array.
3149
3150 Example:
3151 ----
3152 // Load file; each line is an int followed by comma, whitespace and a
3153 // double.
3154 auto a = slurp!(int, double)("filename", "%s, %s");
3155 ----
3156  */
3157 Select!(Types.length == 1, Types[0][], Tuple!(Types)[])
3158 slurp(Types...)(string filename, in char[] format)
3159 {
3160     typeof(return) result;
3161     auto app = appender!(typeof(return))();
3162     ElementType!(typeof(return)) toAdd;
3163     auto f = File(filename);
3164     scope(exit) f.close;
3165     foreach (line; f.byLine())
3166     {
3167         formattedRead(line, format, &toAdd);
3168         enforce(line.empty,
3169                 text("Trailing characters at the end of line: `", line,
3170                         "'"));
3171         app.put(toAdd);
3172     }
3173     return app.data;
3174 }
3175
3176 unittest
3177 {
3178     // Tuple!(int, double)[] x;
3179     // auto app = appender(&x);
3180     write(deleteme, "12 12.25\n345 1.125");
3181     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
3182     auto a = slurp!(int, double)(deleteme, "%s %s");
3183     assert(a.length == 2);
3184     assert(a[0] == tuple(12, 12.25));
3185     assert(a[1] == tuple(345, 1.125));
3186 }
3187
3188
3189 /++
3190     Returns the contents of the given directory.
3191
3192     The names in the contents do not include the pathname.
3193
3194     Throws:
3195         FileException on error.
3196
3197 Examples:
3198     This program lists all the files and subdirectories in its
3199     path argument.
3200 --------------------
3201 import std.stdio;
3202 import std.file;
3203
3204 void main(string[] args)
3205 {
3206     auto dirs = std.file.listdir(args[1]);
3207
3208     foreach(d; dirs)
3209         writefln(d);
3210 }
3211 --------------------
3212  +/
3213
3214 string[] listDir(in char[] pathname)
3215 {
3216     auto result = appender!(string[])();
3217
3218     bool listing(string filename)
3219     {
3220         result.put(filename);
3221         return true; // continue
3222     }
3223
3224     _listDir(pathname, &listing);
3225
3226     return result.data;
3227 }
3228
3229 /++
3230     $(RED Scheduled for deprecation. Please use listDir instead.)
3231
3232     Same is listDir.
3233  +/
3234 alias listDir listdir;
3235
3236 unittest
3237 {
3238     assert(listDir(".").length > 0);
3239 }
3240
3241
3242 /++
3243     Returns all the files in the directory and its sub-directories
3244     which match pattern or regular expression r.
3245
3246     Params:
3247         pathname = The path of the directory to search.
3248         pattern  = String with wildcards, such as $(RED "*.d"). The supported
3249                    wildcard strings are described under fnmatch() in
3250                    $(LINK2 std_path.html, std.path).
3251         r        = Regular expression, for more powerful pattern matching.
3252         followSymLinks = Whether symbolic links which point to directories
3253                          should be treated as directories and their contents
3254                          iterated over. Ignored on Windows.
3255
3256 Examples:
3257     This program lists all the files with a "d" extension in
3258     the path passed as the first argument.
3259 --------------------
3260 import std.stdio;
3261 import std.file;
3262
3263 void main(string[] args)
3264 {
3265   auto d_source_files = std.file.listDir(args[1], "*.d");
3266
3267   foreach(d; d_source_files)
3268       writefln(d);
3269 }
3270 --------------------
3271
3272     A regular expression version that searches for all files with "d" or
3273     "obj" extensions:
3274 --------------------
3275 import std.stdio;
3276 import std.file;
3277 import std.regexp;
3278
3279 void main(string[] args)
3280 {
3281   auto d_source_files = std.file.listDir(args[1], RegExp(r"\.(d|obj)$"));
3282
3283   foreach(d; d_source_files)
3284       writefln(d);
3285 }
3286 --------------------
3287  +/
3288 string[] listDir(in char[] pathname, in char[] pattern, bool followSymLinks = true)
3289 {
3290     auto result = appender!(string[])();
3291
3292     bool callback(DirEntry* de)
3293     {
3294         if(followSymLinks ? de.isDir : isDir(de.linkAttributes))
3295         {
3296             _listDir(de.name, &callback);
3297         }
3298         else if(std.path.fnmatch(de.name, pattern))
3299         {
3300             result.put(de.name);
3301         }
3302
3303         return true; // continue
3304     }
3305
3306     _listDir(pathname, &callback);
3307
3308     return result.data;
3309 }
3310
3311 /++ Ditto +/
3312 string[] listDir(in char[] pathname, RegExp r, bool followSymLinks = true)
3313 {
3314     auto result = appender!(string[])();
3315
3316     bool callback(DirEntry* de)
3317     {
3318         if(followSymLinks ? de.isdir : isDir(de.linkAttributes))
3319         {
3320             _listDir(de.name, &callback);
3321         }
3322         else if(r.test(de.name))
3323         {
3324             result.put(de.name);
3325         }
3326
3327         return true; // continue
3328     }
3329
3330     _listDir(pathname, &callback);
3331
3332     return result.data;
3333 }
3334
3335
3336 /******************************************************
3337  * $(RED Scheduled for deprecation. Please use dirEntries instead.)
3338  *
3339  * For each file and directory name in pathname[],
3340  * pass it to the callback delegate.
3341  *
3342  * Params:
3343  *        callback =        Delegate that processes each
3344  *                        filename in turn. Returns true to
3345  *                        continue, false to stop.
3346  * Example:
3347  *        This program lists all the files in its
3348  *        path argument, including the path.
3349  * ----
3350  * import std.stdio;
3351  * import std.path;
3352  * import std.file;
3353  *
3354  * void main(string[] args)
3355  * {
3356  *    auto pathname = args[1];
3357  *    string[] result;
3358  *
3359  *    bool listing(string filename)
3360  *    {
3361  *      result ~= std.path.join(pathname, filename);
3362  *      return true; // continue
3363  *    }
3364  *
3365  *    listdir(pathname, &listing);
3366  *
3367  *    foreach (name; result)
3368  *      writefln("%s", name);
3369  * }
3370  * ----
3371  */
3372 void listdir(in char[] pathname, bool delegate(string filename) callback)
3373 {
3374     _listDir(pathname, callback);
3375 }
3376
3377
3378 //==============================================================================
3379 // Private Section.
3380 //==============================================================================
3381 private:
3382
3383
3384 void _listDir(in char[] pathname, bool delegate(string filename) callback)
3385 {
3386     bool listing(DirEntry* de)
3387     {
3388         return callback(de.name.basename);
3389     }
3390
3391     _listDir(pathname, &listing);
3392 }
3393
3394
3395 version(Windows)
3396 {
3397     void _listDir(in char[] pathname, bool delegate(DirEntry* de) callback)
3398     {
3399         DirEntry de;
3400         auto c = std.path.join(pathname, "*.*");
3401
3402         if(useWfuncs)
3403         {
3404             WIN32_FIND_DATAW fileinfo;
3405
3406             auto h = FindFirstFileW(std.utf.toUTF16z(c), &fileinfo);
3407             if(h == INVALID_HANDLE_VALUE)
3408                 return;
3409
3410             scope(exit) FindClose(h);
3411
3412             do
3413             {
3414                 // Skip "." and ".."
3415                 if(std.string.wcscmp(fileinfo.cFileName.ptr, ".") == 0 ||
3416                    std.string.wcscmp(fileinfo.cFileName.ptr, "..") == 0)
3417                 {
3418                     continue;
3419                 }
3420
3421                 de._init(pathname, &fileinfo);
3422
3423                 if(!callback(&de))
3424                     break;
3425
3426             } while(FindNextFileW(h, &fileinfo) != FALSE);
3427         }
3428         else
3429         {
3430             WIN32_FIND_DATA fileinfo;
3431
3432             auto h = FindFirstFileA(toMBSz(c), &fileinfo);
3433
3434             if(h == INVALID_HANDLE_VALUE)
3435                 return;
3436
3437             scope(exit) FindClose(h);
3438
3439             do
3440             {
3441                 // Skip "." and ".."
3442                 if(std.c.string.strcmp(fileinfo.cFileName.ptr, ".") == 0 ||
3443                    std.c.string.strcmp(fileinfo.cFileName.ptr, "..") == 0)
3444                 {
3445                     continue;
3446                 }
3447
3448                 de._init(pathname, &fileinfo);
3449
3450                 if(!callback(&de))
3451                     break;
3452
3453             } while(FindNextFileA(h, &fileinfo) != FALSE);
3454         }
3455     }
3456 }
3457 else version(Posix)
3458 {
3459     void _listDir(in char[] pathname, bool delegate(DirEntry* de) callback)
3460     {
3461         auto h = cenforce(opendir(toStringz(pathname)), pathname);
3462         scope(exit) closedir(h);
3463
3464         DirEntry de;
3465
3466         for(dirent* fdata; (fdata = readdir(h)) != null; )
3467         {
3468             // Skip "." and ".."
3469             if(!std.c.string.strcmp(fdata.d_name.ptr, ".") ||
3470                !std.c.string.strcmp(fdata.d_name.ptr, ".."))
3471             {
3472                 continue;
3473             }
3474
3475             de._init(pathname, fdata);
3476
3477             if(!callback(&de))
3478                 break;
3479         }
3480     }
3481 }
3482 else
3483     static assert(0, "Unsupported/Unknown OS");
3484
3485
3486 //==============================================================================
3487 // Stuff from std.date so that we don't have to import std.date and end up with
3488 // the "scheduled for deprecation" pragma message from std.date showing up just
3489 // because someone imports std.file.
3490 //
3491 // These will be removed either when the functions in std.file which use them
3492 // are removed.
3493 //==============================================================================
3494 //
3495
3496 alias long d_time;
3497 enum d_time d_time_nan = long.min;
3498 enum ticksPerSecond = 1000;
3499
3500 version(Windows)
3501 {
3502     d_time FILETIME2d_time(const FILETIME *ft)
3503     {
3504         auto sysTime = FILETIMEToSysTime(ft);
3505
3506         return sysTimeToDTime(sysTime);
3507     }
3508 }
Note: See TracBrowser for help on using the browser.