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 $(MREF std, stdio).
8 
9 $(SCRIPT inhibitQuickIndex = 1;)
10 $(BOOKTABLE,
11 $(TR $(TH Category) $(TH Functions))
12 $(TR $(TD General) $(TD
13           $(LREF exists)
14           $(LREF isDir)
15           $(LREF isFile)
16           $(LREF isSymlink)
17           $(LREF rename)
18           $(LREF thisExePath)
19 ))
20 $(TR $(TD Directories) $(TD
21           $(LREF chdir)
22           $(LREF dirEntries)
23           $(LREF getcwd)
24           $(LREF mkdir)
25           $(LREF mkdirRecurse)
26           $(LREF rmdir)
27           $(LREF rmdirRecurse)
28           $(LREF tempDir)
29 ))
30 $(TR $(TD Files) $(TD
31           $(LREF append)
32           $(LREF copy)
33           $(LREF read)
34           $(LREF readText)
35           $(LREF remove)
36           $(LREF slurp)
37           $(LREF write)
38 ))
39 $(TR $(TD Symlinks) $(TD
40           $(LREF symlink)
41           $(LREF readLink)
42 ))
43 $(TR $(TD Attributes) $(TD
44           $(LREF attrIsDir)
45           $(LREF attrIsFile)
46           $(LREF attrIsSymlink)
47           $(LREF getAttributes)
48           $(LREF getLinkAttributes)
49           $(LREF getSize)
50           $(LREF setAttributes)
51 ))
52 $(TR $(TD Timestamp) $(TD
53           $(LREF getTimes)
54           $(LREF getTimesWin)
55           $(LREF setTimes)
56           $(LREF timeLastModified)
57 ))
58 $(TR $(TD Other) $(TD
59           $(LREF DirEntry)
60           $(LREF FileException)
61           $(LREF PreserveAttributes)
62           $(LREF SpanMode)
63 ))
64 )
65 
66 
67 Copyright: Copyright Digital Mars 2007 - 2011.
68 See_Also:  The $(HTTP ddili.org/ders/d.en/files.html, official tutorial) for an
69 introduction to working with files in D, module
70 $(MREF std, stdio) for opening files and manipulating them via handles,
71 and module $(MREF std, path) for manipulating path strings.
72 
73 License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
74 Authors:   $(HTTP digitalmars.com, Walter Bright),
75            $(HTTP erdani.org, Andrei Alexandrescu),
76            Jonathan M Davis
77 Source:    $(PHOBOSSRC std/_file.d)
78  */
79 module std.file;
80 
81 import core.stdc.errno, core.stdc.stdlib, core.stdc.string;
82 import core.time : abs, dur, hnsecs, seconds;
83 
84 import std.datetime.date : DateTime;
85 import std.datetime.systime : Clock, SysTime, unixTimeToStdTime;
86 import std.internal.cstring;
87 import std.meta;
88 import std.range.primitives;
89 import std.traits;
90 import std.typecons;
91 
version(Windows)92 version (Windows)
93 {
94     import core.sys.windows.windows, std.windows.syserror;
95 }
version(Posix)96 else version (Posix)
97 {
98     import core.sys.posix.dirent, core.sys.posix.fcntl, core.sys.posix.sys.stat,
99         core.sys.posix.sys.time, core.sys.posix.unistd, core.sys.posix.utime;
100 }
101 else
102     static assert(false, "Module " ~ .stringof ~ " not implemented for this OS.");
103 
104 // Character type used for operating system filesystem APIs
version(Windows)105 version (Windows)
106 {
107     private alias FSChar = wchar;
108 }
version(Posix)109 else version (Posix)
110 {
111     private alias FSChar = char;
112 }
113 else
114     static assert(0);
115 
116 // Purposefully not documented. Use at your own risk
deleteme()117 @property string deleteme() @safe
118 {
119     import std.conv : to;
120     import std.path : buildPath;
121     import std.process : thisProcessID;
122 
123     static _deleteme = "deleteme.dmd.unittest.pid";
124     static _first = true;
125 
126     if (_first)
127     {
128         _deleteme = buildPath(tempDir(), _deleteme) ~ to!string(thisProcessID);
129         _first = false;
130     }
131 
132     return _deleteme;
133 }
134 
135 version (unittest) private struct TestAliasedString
136 {
getTestAliasedString137     string get() @safe @nogc pure nothrow { return _s; }
138     alias get this;
139     @disable this(this);
140     string _s;
141 }
142 
version(Android)143 version (Android)
144 {
145     package enum system_directory = "/system/etc";
146     package enum system_file      = "/system/etc/hosts";
147 }
version(Posix)148 else version (Posix)
149 {
150     package enum system_directory = "/usr/include";
151     package enum system_file      = "/usr/include/assert.h";
152 }
153 
154 
155 /++
156     Exception thrown for file I/O errors.
157  +/
158 class FileException : Exception
159 {
160     import std.conv : text, to;
161 
162     /++
163         OS error code.
164      +/
165     immutable uint errno;
166 
167     /++
168         Constructor which takes an error message.
169 
170         Params:
171             name = Name of file for which the error occurred.
172             msg  = Message describing the error.
173             file = The _file where the error occurred.
174             line = The _line where the error occurred.
175      +/
176     this(in char[] name, in char[] msg, string file = __FILE__, size_t line = __LINE__) @safe pure
177     {
178         if (msg.empty)
179             super(name.idup, file, line);
180         else
181             super(text(name, ": ", msg), file, line);
182 
183         errno = 0;
184     }
185 
186     /++
187         Constructor which takes the error number ($(LUCKY GetLastError)
188         in Windows, $(D_PARAM errno) in Posix).
189 
190         Params:
191             name  = Name of file for which the error occurred.
192             errno = The error number.
193             file  = The _file where the error occurred.
194                     Defaults to $(D __FILE__).
195             line  = The _line where the error occurred.
196                     Defaults to $(D __LINE__).
197      +/
198     version (Windows) this(in char[] name,
199                           uint errno = .GetLastError(),
200                           string file = __FILE__,
201                           size_t line = __LINE__) @safe
202     {
203         this(name, sysErrorString(errno), file, line);
204         this.errno = errno;
205     }
206     else version (Posix) this(in char[] name,
207                              uint errno = .errno,
208                              string file = __FILE__,
209                              size_t line = __LINE__) @trusted
210     {
211         import std.exception : errnoString;
212         this(name, errnoString(errno), file, line);
213         this.errno = errno;
214     }
215 }
216 
cenforce(T)217 private T cenforce(T)(T condition, lazy const(char)[] name, string file = __FILE__, size_t line = __LINE__)
218 {
219     if (condition)
220         return condition;
221     version (Windows)
222     {
223         throw new FileException(name, .GetLastError(), file, line);
224     }
225     else version (Posix)
226     {
227         throw new FileException(name, .errno, file, line);
228     }
229 }
230 
version(Windows)231 version (Windows)
232 @trusted
233 private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez,
234     string file = __FILE__, size_t line = __LINE__)
235 {
236     if (condition)
237         return condition;
238     if (!name)
239     {
240         import core.stdc.wchar_ : wcslen;
241         import std.conv : to;
242 
243         auto len = namez ? wcslen(namez) : 0;
244         name = to!string(namez[0 .. len]);
245     }
246     throw new FileException(name, .GetLastError(), file, line);
247 }
248 
version(Posix)249 version (Posix)
250 @trusted
251 private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez,
252     string file = __FILE__, size_t line = __LINE__)
253 {
254     if (condition)
255         return condition;
256     if (!name)
257     {
258         import core.stdc.string : strlen;
259 
260         auto len = namez ? strlen(namez) : 0;
261         name = namez[0 .. len].idup;
262     }
263     throw new FileException(name, .errno, file, line);
264 }
265 
266 @safe unittest
267 {
268     // issue 17102
269     try
270     {
271         cenforce(false, null, null,
272                 __FILE__, __LINE__);
273     }
catch(FileException)274     catch (FileException) {}
275 }
276 
277 /* **********************************
278  * Basic File operations.
279  */
280 
281 /********************************************
282 Read entire contents of file $(D name) and returns it as an untyped
283 array. If the file size is larger than $(D upTo), only $(D upTo)
284 bytes are _read.
285 
286 Params:
287     name = string or range of characters representing the file _name
288     upTo = if present, the maximum number of bytes to _read
289 
290 Returns: Untyped array of bytes _read.
291 
292 Throws: $(LREF FileException) on error.
293  */
294 
295 void[] read(R)(R name, size_t upTo = size_t.max)
296 if (isInputRange!R && isSomeChar!(ElementEncodingType!R) && !isInfinite!R &&
297     !isConvertibleToString!R)
298 {
299     static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
300         return readImpl(name, name.tempCString!FSChar(), upTo);
301     else
302         return readImpl(null, name.tempCString!FSChar(), upTo);
303 }
304 
305 ///
306 @safe unittest
307 {
308     import std.utf : byChar;
scope(exit)309     scope(exit)
310     {
311         assert(exists(deleteme));
312         remove(deleteme);
313     }
314 
315     write(deleteme, "1234"); // deleteme is the name of a temporary file
316     assert(read(deleteme, 2) == "12");
317     assert(read(deleteme.byChar) == "1234");
318     assert((cast(const(ubyte)[])read(deleteme)).length == 4);
319 }
320 
321 /// ditto
322 void[] read(R)(auto ref R name, size_t upTo = size_t.max)
323 if (isConvertibleToString!R)
324 {
325     return read!(StringTypeOf!R)(name, upTo);
326 }
327 
328 @safe unittest
329 {
330     static assert(__traits(compiles, read(TestAliasedString(null))));
331 }
332 
version(Posix)333 version (Posix) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @trusted
334 {
335     import core.memory : GC;
336     import std.algorithm.comparison : min;
337     import std.array : uninitializedArray;
338     import std.conv : to;
339 
340     // A few internal configuration parameters {
341     enum size_t
342         minInitialAlloc = 1024 * 4,
343         maxInitialAlloc = size_t.max / 2,
344         sizeIncrement = 1024 * 16,
345         maxSlackMemoryAllowed = 1024;
346     // }
347 
348     immutable fd = core.sys.posix.fcntl.open(namez,
349             core.sys.posix.fcntl.O_RDONLY);
350     cenforce(fd != -1, name);
351     scope(exit) core.sys.posix.unistd.close(fd);
352 
353     stat_t statbuf = void;
354     cenforce(fstat(fd, &statbuf) == 0, name, namez);
355 
356     immutable initialAlloc = min(upTo, to!size_t(statbuf.st_size
357         ? min(statbuf.st_size + 1, maxInitialAlloc)
358         : minInitialAlloc));
359     void[] result = uninitializedArray!(ubyte[])(initialAlloc);
360     scope(failure) GC.free(result.ptr);
361     size_t size = 0;
362 
363     for (;;)
364     {
365         immutable actual = core.sys.posix.unistd.read(fd, result.ptr + size,
366                 min(result.length, upTo) - size);
367         cenforce(actual != -1, name, namez);
368         if (actual == 0) break;
369         size += actual;
370         if (size >= upTo) break;
371         if (size < result.length) continue;
372         immutable newAlloc = size + sizeIncrement;
373         result = GC.realloc(result.ptr, newAlloc, GC.BlkAttr.NO_SCAN)[0 .. newAlloc];
374     }
375 
376     return result.length - size >= maxSlackMemoryAllowed
377         ? GC.realloc(result.ptr, size, GC.BlkAttr.NO_SCAN)[0 .. size]
378         : result[0 .. size];
379 }
380 
381 
version(Windows)382 version (Windows) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @safe
383 {
384     import core.memory : GC;
385     import std.algorithm.comparison : min;
386     import std.array : uninitializedArray;
387     static trustedCreateFileW(const(wchar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
388                               SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition,
389                               DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted
390     {
391         return CreateFileW(namez, dwDesiredAccess, dwShareMode,
392                            lpSecurityAttributes, dwCreationDisposition,
393                            dwFlagsAndAttributes, hTemplateFile);
394 
395     }
396     static trustedCloseHandle(HANDLE hObject) @trusted
397     {
398         return CloseHandle(hObject);
399     }
400     static trustedGetFileSize(HANDLE hFile, out ulong fileSize) @trusted
401     {
402         DWORD sizeHigh;
403         DWORD sizeLow = GetFileSize(hFile, &sizeHigh);
404         const bool result = sizeLow != INVALID_FILE_SIZE;
405         if (result)
406             fileSize = makeUlong(sizeLow, sizeHigh);
407         return result;
408     }
409     static trustedReadFile(HANDLE hFile, void *lpBuffer, ulong nNumberOfBytesToRead) @trusted
410     {
411         // Read by chunks of size < 4GB (Windows API limit)
412         ulong totalNumRead = 0;
413         while (totalNumRead != nNumberOfBytesToRead)
414         {
415             const uint chunkSize = min(nNumberOfBytesToRead - totalNumRead, 0xffff_0000);
416             DWORD numRead = void;
417             const result = ReadFile(hFile, lpBuffer + totalNumRead, chunkSize, &numRead, null);
418             if (result == 0 || numRead != chunkSize)
419                 return false;
420             totalNumRead += chunkSize;
421         }
422         return true;
423     }
424 
425     alias defaults =
426         AliasSeq!(GENERIC_READ,
427             FILE_SHARE_READ | FILE_SHARE_WRITE, (SECURITY_ATTRIBUTES*).init,
428             OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
429             HANDLE.init);
430     auto h = trustedCreateFileW(namez, defaults);
431 
432     cenforce(h != INVALID_HANDLE_VALUE, name, namez);
433     scope(exit) cenforce(trustedCloseHandle(h), name, namez);
434     ulong fileSize = void;
435     cenforce(trustedGetFileSize(h, fileSize), name, namez);
436     size_t size = min(upTo, fileSize);
437     auto buf = uninitializedArray!(ubyte[])(size);
438 
439     scope(failure)
440     {
441         () @trusted { GC.free(buf.ptr); } ();
442     }
443 
444     if (size)
445         cenforce(trustedReadFile(h, &buf[0], size), name, namez);
446     return buf[0 .. size];
447 }
448 
version(linux)449 version (linux) @safe unittest
450 {
451     // A file with "zero" length that doesn't have 0 length at all
452     auto s = std.file.readText("/proc/sys/kernel/osrelease");
453     assert(s.length > 0);
454     //writefln("'%s'", s);
455 }
456 
457 @safe unittest
458 {
459     scope(exit) if (exists(deleteme)) remove(deleteme);
460     import std.stdio;
461     auto f = File(deleteme, "w");
462     f.write("abcd"); f.flush();
463     assert(read(deleteme) == "abcd");
464 }
465 
466 /********************************************
467 Read and validates (using $(REF validate, std,utf)) a text file. $(D S)
468 can be a type of array of characters of any width and constancy. No
469 width conversion is performed; if the width of the characters in file
470 $(D name) is different from the width of elements of $(D S),
471 validation will fail.
472 
473 Params:
474     name = string or range of characters representing the file _name
475 
476 Returns: Array of characters read.
477 
478 Throws: $(D FileException) on file error, $(D UTFException) on UTF
479 decoding error.
480  */
481 
482 S readText(S = string, R)(R name)
483 if (isSomeString!S &&
484     (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
485     !isConvertibleToString!R)
486 {
487     import std.utf : validate;
trustedCast(void[]buf)488     static auto trustedCast(void[] buf) @trusted { return cast(S) buf; }
489     auto result = trustedCast(read(name));
490     validate(result);
491     return result;
492 }
493 
494 ///
495 @safe unittest
496 {
497     import std.exception : enforce;
498     write(deleteme, "abc"); // deleteme is the name of a temporary file
499     scope(exit) remove(deleteme);
500     string content = readText(deleteme);
501     enforce(content == "abc");
502 }
503 
504 /// ditto
505 S readText(S = string, R)(auto ref R name)
506 if (isConvertibleToString!R)
507 {
508     return readText!(S, StringTypeOf!R)(name);
509 }
510 
511 @safe unittest
512 {
513     static assert(__traits(compiles, readText(TestAliasedString(null))));
514 }
515 
516 /*********************************************
517 Write $(D buffer) to file $(D name).
518 
519 Creates the file if it does not already exist.
520 
521 Params:
522     name = string or range of characters representing the file _name
523     buffer = data to be written to file
524 
525 Throws: $(D FileException) on error.
526 
527 See_also: $(REF toFile, std,stdio)
528  */
529 void write(R)(R name, const void[] buffer)
530 if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
531     !isConvertibleToString!R)
532 {
533     static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
534         writeImpl(name, name.tempCString!FSChar(), buffer, false);
535     else
536         writeImpl(null, name.tempCString!FSChar(), buffer, false);
537 }
538 
539 ///
540 @system unittest
541 {
scope(exit)542    scope(exit)
543    {
544        assert(exists(deleteme));
545        remove(deleteme);
546    }
547 
548    int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
549    write(deleteme, a); // deleteme is the name of a temporary file
550    assert(cast(int[]) read(deleteme) == a);
551 }
552 
553 /// ditto
554 void write(R)(auto ref R name, const void[] buffer)
555 if (isConvertibleToString!R)
556 {
557     write!(StringTypeOf!R)(name, buffer);
558 }
559 
560 @safe unittest
561 {
562     static assert(__traits(compiles, write(TestAliasedString(null), null)));
563 }
564 
565 /*********************************************
566 Appends $(D buffer) to file $(D name).
567 
568 Creates the file if it does not already exist.
569 
570 Params:
571     name = string or range of characters representing the file _name
572     buffer = data to be appended to file
573 
574 Throws: $(D FileException) on error.
575  */
576 void append(R)(R name, const void[] buffer)
577 if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
578     !isConvertibleToString!R)
579 {
580     static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
581         writeImpl(name, name.tempCString!FSChar(), buffer, true);
582     else
583         writeImpl(null, name.tempCString!FSChar(), buffer, true);
584 }
585 
586 ///
587 @system unittest
588 {
scope(exit)589    scope(exit)
590    {
591        assert(exists(deleteme));
592        remove(deleteme);
593    }
594 
595    int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
596    write(deleteme, a); // deleteme is the name of a temporary file
597    int[] b = [ 13, 21 ];
598    append(deleteme, b);
599    assert(cast(int[]) read(deleteme) == a ~ b);
600 }
601 
602 /// ditto
603 void append(R)(auto ref R name, const void[] buffer)
604 if (isConvertibleToString!R)
605 {
606     append!(StringTypeOf!R)(name, buffer);
607 }
608 
609 @safe unittest
610 {
611     static assert(__traits(compiles, append(TestAliasedString("foo"), [0, 1, 2, 3])));
612 }
613 
614 // Posix implementation helper for write and append
615 
version(Posix)616 version (Posix) private void writeImpl(const(char)[] name, const(FSChar)* namez,
617         in void[] buffer, bool append) @trusted
618 {
619     import std.conv : octal;
620 
621     // append or write
622     auto mode = append ? O_CREAT | O_WRONLY | O_APPEND
623                        : O_CREAT | O_WRONLY | O_TRUNC;
624 
625     immutable fd = core.sys.posix.fcntl.open(namez, mode, octal!666);
626     cenforce(fd != -1, name, namez);
627     {
628         scope(failure) core.sys.posix.unistd.close(fd);
629 
630         immutable size = buffer.length;
631         size_t sum, cnt = void;
632         while (sum != size)
633         {
634             cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30;
635             const numwritten = core.sys.posix.unistd.write(fd, buffer.ptr + sum, cnt);
636             if (numwritten != cnt)
637                 break;
638             sum += numwritten;
639         }
640         cenforce(sum == size, name, namez);
641     }
642     cenforce(core.sys.posix.unistd.close(fd) == 0, name, namez);
643 }
644 
645 // Windows implementation helper for write and append
646 
version(Windows)647 version (Windows) private void writeImpl(const(char)[] name, const(FSChar)* namez,
648         in void[] buffer, bool append) @trusted
649 {
650     HANDLE h;
651     if (append)
652     {
653         alias defaults =
654             AliasSeq!(GENERIC_WRITE, 0, null, OPEN_ALWAYS,
655                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
656                 HANDLE.init);
657 
658         h = CreateFileW(namez, defaults);
659         cenforce(h != INVALID_HANDLE_VALUE, name, namez);
660         cenforce(SetFilePointer(h, 0, null, FILE_END) != INVALID_SET_FILE_POINTER,
661             name, namez);
662     }
663     else // write
664     {
665         alias defaults =
666             AliasSeq!(GENERIC_WRITE, 0, null, CREATE_ALWAYS,
667                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
668                 HANDLE.init);
669 
670         h = CreateFileW(namez, defaults);
671         cenforce(h != INVALID_HANDLE_VALUE, name, namez);
672     }
673     immutable size = buffer.length;
674     size_t sum, cnt = void;
675     DWORD numwritten = void;
676     while (sum != size)
677     {
678         cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30;
679         WriteFile(h, buffer.ptr + sum, cast(uint) cnt, &numwritten, null);
680         if (numwritten != cnt)
681             break;
682         sum += numwritten;
683     }
684     cenforce(sum == size && CloseHandle(h), name, namez);
685 }
686 
687 /***************************************************
688  * Rename file $(D from) _to $(D to).
689  * If the target file exists, it is overwritten.
690  * Params:
691  *    from = string or range of characters representing the existing file name
692  *    to = string or range of characters representing the target file name
693  * Throws: $(D FileException) on error.
694  */
695 void rename(RF, RT)(RF from, RT to)
696 if ((isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) || isSomeString!RF)
697     && !isConvertibleToString!RF &&
698     (isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) || isSomeString!RT)
699     && !isConvertibleToString!RT)
700 {
701     // Place outside of @trusted block
702     auto fromz = from.tempCString!FSChar();
703     auto toz = to.tempCString!FSChar();
704 
705     static if (isNarrowString!RF && is(Unqual!(ElementEncodingType!RF) == char))
706         alias f = from;
707     else
708         enum string f = null;
709 
710     static if (isNarrowString!RT && is(Unqual!(ElementEncodingType!RT) == char))
711         alias t = to;
712     else
713         enum string t = null;
714 
715     renameImpl(f, t, fromz, toz);
716 }
717 
718 /// ditto
719 void rename(RF, RT)(auto ref RF from, auto ref RT to)
720 if (isConvertibleToString!RF || isConvertibleToString!RT)
721 {
722     import std.meta : staticMap;
723     alias Types = staticMap!(convertToString, RF, RT);
724     rename!Types(from, to);
725 }
726 
727 @safe unittest
728 {
729     static assert(__traits(compiles, rename(TestAliasedString(null), TestAliasedString(null))));
730     static assert(__traits(compiles, rename("", TestAliasedString(null))));
731     static assert(__traits(compiles, rename(TestAliasedString(null), "")));
732     import std.utf : byChar;
733     static assert(__traits(compiles, rename(TestAliasedString(null), "".byChar)));
734 }
735 
renameImpl(const (char)[]f,const (char)[]t,const (FSChar)* fromz,const (FSChar)* toz)736 private void renameImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, const(FSChar)* toz) @trusted
737 {
738     version (Windows)
739     {
740         import std.exception : enforce;
741 
742         const result = MoveFileExW(fromz, toz, MOVEFILE_REPLACE_EXISTING);
743         if (!result)
744         {
745             import core.stdc.wchar_ : wcslen;
746             import std.conv : to, text;
747 
748             if (!f)
749                 f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]);
750 
751             if (!t)
752                 t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
753 
754             enforce(false,
755                 new FileException(
756                     text("Attempting to rename file ", f, " to ", t)));
757         }
758     }
759     else version (Posix)
760     {
761         static import core.stdc.stdio;
762 
763         cenforce(core.stdc.stdio.rename(fromz, toz) == 0, t, toz);
764     }
765 }
766 
767 @safe unittest
768 {
769     import std.utf : byWchar;
770 
771     auto t1 = deleteme, t2 = deleteme~"2";
772     scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
773     write(t1, "1");
774     rename(t1, t2);
775     assert(readText(t2) == "1");
776     write(t1, "2");
777     rename(t1, t2.byWchar);
778     assert(readText(t2) == "2");
779 }
780 
781 
782 /***************************************************
783 Delete file $(D name).
784 
785 Params:
786     name = string or range of characters representing the file _name
787 
788 Throws: $(D FileException) on error.
789  */
790 void remove(R)(R name)
791 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
792     !isConvertibleToString!R)
793 {
794     static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
795         removeImpl(name, name.tempCString!FSChar());
796     else
797         removeImpl(null, name.tempCString!FSChar());
798 }
799 
800 /// ditto
801 void remove(R)(auto ref R name)
802 if (isConvertibleToString!R)
803 {
804     remove!(StringTypeOf!R)(name);
805 }
806 
807 @safe unittest
808 {
809     static assert(__traits(compiles, remove(TestAliasedString("foo"))));
810 }
811 
removeImpl(const (char)[]name,const (FSChar)* namez)812 private void removeImpl(const(char)[] name, const(FSChar)* namez) @trusted
813 {
814     version (Windows)
815     {
816         cenforce(DeleteFileW(namez), name, namez);
817     }
818     else version (Posix)
819     {
820         static import core.stdc.stdio;
821 
822         if (!name)
823         {
824             import core.stdc.string : strlen;
825             auto len = strlen(namez);
826             name = namez[0 .. len];
827         }
828         cenforce(core.stdc.stdio.remove(namez) == 0,
829             "Failed to remove file " ~ name);
830     }
831 }
832 
833 version (Windows) private WIN32_FILE_ATTRIBUTE_DATA getFileAttributesWin(R)(R name)
834 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
835 {
836     auto namez = name.tempCString!FSChar();
837 
838     WIN32_FILE_ATTRIBUTE_DATA fad = void;
839 
840     static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
841     {
getFA(const (char)[]name,const (FSChar)* namez,out WIN32_FILE_ATTRIBUTE_DATA fad)842         static void getFA(const(char)[] name, const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
843         {
844             import std.exception : enforce;
845             enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad),
846                 new FileException(name.idup));
847         }
848         getFA(name, namez, fad);
849     }
850     else
851     {
getFA(const (FSChar)* namez,out WIN32_FILE_ATTRIBUTE_DATA fad)852         static void getFA(const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
853         {
854             import core.stdc.wchar_ : wcslen;
855             import std.conv : to;
856             import std.exception : enforce;
857 
858             enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad),
859                 new FileException(namez[0 .. wcslen(namez)].to!string));
860         }
861         getFA(namez, fad);
862     }
863     return fad;
864 }
865 
version(Windows)866 version (Windows) private ulong makeUlong(DWORD dwLow, DWORD dwHigh) @safe pure nothrow @nogc
867 {
868     ULARGE_INTEGER li;
869     li.LowPart  = dwLow;
870     li.HighPart = dwHigh;
871     return li.QuadPart;
872 }
873 
874 /***************************************************
875 Get size of file $(D name) in bytes.
876 
877 Params:
878     name = string or range of characters representing the file _name
879 
880 Throws: $(D FileException) on error (e.g., file not found).
881  */
882 ulong getSize(R)(R name)
883 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
884     !isConvertibleToString!R)
885 {
version(Windows)886     version (Windows)
887     {
888         with (getFileAttributesWin(name))
889             return makeUlong(nFileSizeLow, nFileSizeHigh);
890     }
version(Posix)891     else version (Posix)
892     {
893         auto namez = name.tempCString();
894 
895         static trustedStat(const(FSChar)* namez, out stat_t buf) @trusted
896         {
897             return stat(namez, &buf);
898         }
899         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
900             alias names = name;
901         else
902             string names = null;
903         stat_t statbuf = void;
904         cenforce(trustedStat(namez, statbuf) == 0, names, namez);
905         return statbuf.st_size;
906     }
907 }
908 
909 /// ditto
910 ulong getSize(R)(auto ref R name)
911 if (isConvertibleToString!R)
912 {
913     return getSize!(StringTypeOf!R)(name);
914 }
915 
916 @safe unittest
917 {
918     static assert(__traits(compiles, getSize(TestAliasedString("foo"))));
919 }
920 
921 @safe unittest
922 {
923     // create a file of size 1
924     write(deleteme, "a");
scope(exit)925     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
926     assert(getSize(deleteme) == 1);
927     // create a file of size 3
928     write(deleteme, "abc");
929     import std.utf : byChar;
930     assert(getSize(deleteme.byChar) == 3);
931 }
932 
933 
934 // Reads a time field from a stat_t with full precision.
version(Posix)935 version (Posix)
936 private SysTime statTimeToStdTime(char which)(ref stat_t statbuf)
937 {
938     auto unixTime = mixin(`statbuf.st_` ~ which ~ `time`);
939     long stdTime = unixTimeToStdTime(unixTime);
940 
941     static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `tim`))))
942         stdTime += mixin(`statbuf.st_` ~ which ~ `tim.tv_nsec`) / 100;
943     else
944     static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `timensec`))))
945         stdTime += mixin(`statbuf.st_` ~ which ~ `timensec`) / 100;
946     else
947     static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `time_nsec`))))
948         stdTime += mixin(`statbuf.st_` ~ which ~ `time_nsec`) / 100;
949     else
950     static if (is(typeof(mixin(`statbuf.__st_` ~ which ~ `timensec`))))
951         stdTime += mixin(`statbuf.__st_` ~ which ~ `timensec`) / 100;
952 
953     return SysTime(stdTime);
954 }
955 
956 /++
957     Get the access and modified times of file or folder $(D name).
958 
959     Params:
960         name             = File/Folder _name to get times for.
961         accessTime       = Time the file/folder was last accessed.
962         modificationTime = Time the file/folder was last modified.
963 
964     Throws:
965         $(D FileException) on error.
966  +/
967 void getTimes(R)(R name,
968               out SysTime accessTime,
969               out SysTime modificationTime)
970 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
971     !isConvertibleToString!R)
972 {
version(Windows)973     version (Windows)
974     {
975         import std.datetime.systime : FILETIMEToSysTime;
976 
977         with (getFileAttributesWin(name))
978         {
979             accessTime = FILETIMEToSysTime(&ftLastAccessTime);
980             modificationTime = FILETIMEToSysTime(&ftLastWriteTime);
981         }
982     }
version(Posix)983     else version (Posix)
984     {
985         auto namez = name.tempCString();
986 
987         static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
988         {
989             return stat(namez, &buf);
990         }
991         stat_t statbuf = void;
992 
993         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
994             alias names = name;
995         else
996             string names = null;
997         cenforce(trustedStat(namez, statbuf) == 0, names, namez);
998 
999         accessTime = statTimeToStdTime!'a'(statbuf);
1000         modificationTime = statTimeToStdTime!'m'(statbuf);
1001     }
1002 }
1003 
1004 /// ditto
1005 void getTimes(R)(auto ref R name,
1006               out SysTime accessTime,
1007               out SysTime modificationTime)
1008 if (isConvertibleToString!R)
1009 {
1010     return getTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
1011 }
1012 
1013 @safe unittest
1014 {
1015     SysTime atime, mtime;
1016     static assert(__traits(compiles, getTimes(TestAliasedString("foo"), atime, mtime)));
1017 }
1018 
1019 @system unittest
1020 {
1021     import std.stdio : writefln;
1022 
1023     auto currTime = Clock.currTime();
1024 
1025     write(deleteme, "a");
scope(exit)1026     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1027 
1028     SysTime accessTime1 = void;
1029     SysTime modificationTime1 = void;
1030 
1031     getTimes(deleteme, accessTime1, modificationTime1);
1032 
1033     enum leeway = dur!"seconds"(5);
1034 
1035     {
1036         auto diffa = accessTime1 - currTime;
1037         auto diffm = modificationTime1 - currTime;
1038         scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime1, modificationTime1, currTime, diffa, diffm);
1039 
1040         assert(abs(diffa) <= leeway);
1041         assert(abs(diffm) <= leeway);
1042     }
1043 
version(fullFileTests)1044     version (fullFileTests)
1045     {
1046         import core.thread;
1047         enum sleepTime = dur!"seconds"(2);
1048         Thread.sleep(sleepTime);
1049 
1050         currTime = Clock.currTime();
1051         write(deleteme, "b");
1052 
1053         SysTime accessTime2 = void;
1054         SysTime modificationTime2 = void;
1055 
1056         getTimes(deleteme, accessTime2, modificationTime2);
1057 
1058         {
1059             auto diffa = accessTime2 - currTime;
1060             auto diffm = modificationTime2 - currTime;
1061             scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime2, modificationTime2, currTime, diffa, diffm);
1062 
1063             //There is no guarantee that the access time will be updated.
1064             assert(abs(diffa) <= leeway + sleepTime);
1065             assert(abs(diffm) <= leeway);
1066         }
1067 
1068         assert(accessTime1 <= accessTime2);
1069         assert(modificationTime1 <= modificationTime2);
1070     }
1071 }
1072 
1073 
version(StdDdoc)1074 version (StdDdoc)
1075 {
1076     /++
1077      $(BLUE This function is Windows-Only.)
1078 
1079      Get creation/access/modified times of file $(D name).
1080 
1081      This is the same as $(D getTimes) except that it also gives you the file
1082      creation time - which isn't possible on Posix systems.
1083 
1084      Params:
1085      name                 = File _name to get times for.
1086      fileCreationTime     = Time the file was created.
1087      fileAccessTime       = Time the file was last accessed.
1088      fileModificationTime = Time the file was last modified.
1089 
1090      Throws:
1091      $(D FileException) on error.
1092      +/
1093     void getTimesWin(R)(R name,
1094                         out SysTime fileCreationTime,
1095                         out SysTime fileAccessTime,
1096                         out SysTime fileModificationTime)
1097     if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1098         !isConvertibleToString!R);
1099 }
1100 else version (Windows)
1101 {
1102     void getTimesWin(R)(R name,
1103                         out SysTime fileCreationTime,
1104                         out SysTime fileAccessTime,
1105                         out SysTime fileModificationTime)
1106     if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1107         !isConvertibleToString!R)
1108     {
1109         import std.datetime.systime : FILETIMEToSysTime;
1110 
1111         with (getFileAttributesWin(name))
1112         {
1113             fileCreationTime = FILETIMEToSysTime(&ftCreationTime);
1114             fileAccessTime = FILETIMEToSysTime(&ftLastAccessTime);
1115             fileModificationTime = FILETIMEToSysTime(&ftLastWriteTime);
1116         }
1117     }
1118 
1119     void getTimesWin(R)(auto ref R name,
1120                         out SysTime fileCreationTime,
1121                         out SysTime fileAccessTime,
1122                         out SysTime fileModificationTime)
1123     if (isConvertibleToString!R)
1124     {
1125         getTimesWin!(StringTypeOf!R)(name, fileCreationTime, fileAccessTime, fileModificationTime);
1126     }
1127 }
1128 
1129 version (Windows) @system unittest
1130 {
1131     import std.stdio : writefln;
1132     auto currTime = Clock.currTime();
1133 
1134     write(deleteme, "a");
1135     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1136 
1137     SysTime creationTime1 = void;
1138     SysTime accessTime1 = void;
1139     SysTime modificationTime1 = void;
1140 
1141     getTimesWin(deleteme, creationTime1, accessTime1, modificationTime1);
1142 
1143     enum leeway = dur!"seconds"(5);
1144 
1145     {
1146         auto diffc = creationTime1 - currTime;
1147         auto diffa = accessTime1 - currTime;
1148         auto diffm = modificationTime1 - currTime;
1149         scope(failure)
1150         {
1151             writefln("[%s] [%s] [%s] [%s] [%s] [%s] [%s]",
1152                      creationTime1, accessTime1, modificationTime1, currTime, diffc, diffa, diffm);
1153         }
1154 
1155         // Deleting and recreating a file doesn't seem to always reset the "file creation time"
1156         //assert(abs(diffc) <= leeway);
1157         assert(abs(diffa) <= leeway);
1158         assert(abs(diffm) <= leeway);
1159     }
1160 
1161     version (fullFileTests)
1162     {
1163         import core.thread;
1164         Thread.sleep(dur!"seconds"(2));
1165 
1166         currTime = Clock.currTime();
1167         write(deleteme, "b");
1168 
1169         SysTime creationTime2 = void;
1170         SysTime accessTime2 = void;
1171         SysTime modificationTime2 = void;
1172 
1173         getTimesWin(deleteme, creationTime2, accessTime2, modificationTime2);
1174 
1175         {
1176             auto diffa = accessTime2 - currTime;
1177             auto diffm = modificationTime2 - currTime;
1178             scope(failure)
1179             {
1180                 writefln("[%s] [%s] [%s] [%s] [%s]",
1181                          accessTime2, modificationTime2, currTime, diffa, diffm);
1182             }
1183 
1184             assert(abs(diffa) <= leeway);
1185             assert(abs(diffm) <= leeway);
1186         }
1187 
1188         assert(creationTime1 == creationTime2);
1189         assert(accessTime1 <= accessTime2);
1190         assert(modificationTime1 <= modificationTime2);
1191     }
1192 
1193     {
1194         SysTime ctime, atime, mtime;
1195         static assert(__traits(compiles, getTimesWin(TestAliasedString("foo"), ctime, atime, mtime)));
1196     }
1197 }
1198 
1199 
1200 /++
1201     Set access/modified times of file or folder $(D name).
1202 
1203     Params:
1204         name             = File/Folder _name to get times for.
1205         accessTime       = Time the file/folder was last accessed.
1206         modificationTime = Time the file/folder was last modified.
1207 
1208     Throws:
1209         $(D FileException) on error.
1210  +/
1211 void setTimes(R)(R name,
1212               SysTime accessTime,
1213               SysTime modificationTime)
1214 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1215     !isConvertibleToString!R)
1216 {
version(Windows)1217     version (Windows)
1218     {
1219         import std.datetime.systime : SysTimeToFILETIME;
1220 
1221         auto namez = name.tempCString!FSChar();
1222         static auto trustedCreateFileW(const(FSChar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
1223                                        SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition,
1224                                        DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted
1225         {
1226             return CreateFileW(namez, dwDesiredAccess, dwShareMode,
1227                                lpSecurityAttributes, dwCreationDisposition,
1228                                dwFlagsAndAttributes, hTemplateFile);
1229 
1230         }
1231         static auto trustedCloseHandle(HANDLE hObject) @trusted
1232         {
1233             return CloseHandle(hObject);
1234         }
1235         static auto trustedSetFileTime(HANDLE hFile, in FILETIME *lpCreationTime,
1236                                        in ref FILETIME lpLastAccessTime, in ref FILETIME lpLastWriteTime) @trusted
1237         {
1238             return SetFileTime(hFile, lpCreationTime, &lpLastAccessTime, &lpLastWriteTime);
1239         }
1240 
1241         const ta = SysTimeToFILETIME(accessTime);
1242         const tm = SysTimeToFILETIME(modificationTime);
1243         alias defaults =
1244             AliasSeq!(GENERIC_WRITE,
1245                       0,
1246                       null,
1247                       OPEN_EXISTING,
1248                       FILE_ATTRIBUTE_NORMAL |
1249                       FILE_ATTRIBUTE_DIRECTORY |
1250                       FILE_FLAG_BACKUP_SEMANTICS,
1251                       HANDLE.init);
1252         auto h = trustedCreateFileW(namez, defaults);
1253 
1254         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1255             alias names = name;
1256         else
1257             string names = null;
1258         cenforce(h != INVALID_HANDLE_VALUE, names, namez);
1259 
1260         scope(exit)
1261             cenforce(trustedCloseHandle(h), names, namez);
1262 
1263         cenforce(trustedSetFileTime(h, null, ta, tm), names, namez);
1264     }
version(Posix)1265     else version (Posix)
1266     {
1267         auto namez = name.tempCString!FSChar();
1268         static if (is(typeof(&utimensat)))
1269         {
1270             static auto trustedUtimensat(int fd, const(FSChar)* namez, const ref timespec[2] times, int flags) @trusted
1271             {
1272                 return utimensat(fd, namez, times, flags);
1273             }
1274             timespec[2] t = void;
1275 
1276             t[0] = accessTime.toTimeSpec();
1277             t[1] = modificationTime.toTimeSpec();
1278 
1279             static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1280                 alias names = name;
1281             else
1282                 string names = null;
1283             cenforce(trustedUtimensat(AT_FDCWD, namez, t, 0) == 0, names, namez);
1284         }
1285         else
1286         {
1287             static auto trustedUtimes(const(FSChar)* namez, const ref timeval[2] times) @trusted
1288             {
1289                 return utimes(namez, times);
1290             }
1291             timeval[2] t = void;
1292 
1293             t[0] = accessTime.toTimeVal();
1294             t[1] = modificationTime.toTimeVal();
1295 
1296             static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1297                 alias names = name;
1298             else
1299                 string names = null;
1300             cenforce(trustedUtimes(namez, t) == 0, names, namez);
1301         }
1302     }
1303 }
1304 
1305 /// ditto
1306 void setTimes(R)(auto ref R name,
1307               SysTime accessTime,
1308               SysTime modificationTime)
1309 if (isConvertibleToString!R)
1310 {
1311     setTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
1312 }
1313 
1314 @safe unittest
1315 {
1316     if (false) // Test instatiation
1317         setTimes(TestAliasedString("foo"), SysTime.init, SysTime.init);
1318 }
1319 
1320 @system unittest
1321 {
1322     import std.stdio : File;
1323     string newdir = deleteme ~ r".dir";
1324     string dir = newdir ~ r"/a/b/c";
1325     string file = dir ~ "/file";
1326 
1327     if (!exists(dir)) mkdirRecurse(dir);
1328     { auto f = File(file, "w"); }
1329 
testTimes(int hnsecValue)1330     void testTimes(int hnsecValue)
1331     {
1332         foreach (path; [file, dir])  // test file and dir
1333         {
1334             SysTime atime = SysTime(DateTime(2010, 10, 4, 0, 0, 30), hnsecs(hnsecValue));
1335             SysTime mtime = SysTime(DateTime(2011, 10, 4, 0, 0, 30), hnsecs(hnsecValue));
1336             setTimes(path, atime, mtime);
1337 
1338             SysTime atime_res;
1339             SysTime mtime_res;
1340             getTimes(path, atime_res, mtime_res);
1341             assert(atime == atime_res);
1342             assert(mtime == mtime_res);
1343         }
1344     }
1345 
1346     testTimes(0);
1347     version (linux)
1348         testTimes(123_456_7);
1349 
1350     rmdirRecurse(newdir);
1351 }
1352 
1353 /++
1354     Returns the time that the given file was last modified.
1355 
1356     Throws:
1357         $(D FileException) if the given file does not exist.
1358 +/
1359 SysTime timeLastModified(R)(R name)
1360 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1361     !isConvertibleToString!R)
1362 {
version(Windows)1363     version (Windows)
1364     {
1365         SysTime dummy;
1366         SysTime ftm;
1367 
1368         getTimesWin(name, dummy, dummy, ftm);
1369 
1370         return ftm;
1371     }
version(Posix)1372     else version (Posix)
1373     {
1374         auto namez = name.tempCString!FSChar();
1375         static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1376         {
1377             return stat(namez, &buf);
1378         }
1379         stat_t statbuf = void;
1380 
1381         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1382             alias names = name;
1383         else
1384             string names = null;
1385         cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1386 
1387         return statTimeToStdTime!'m'(statbuf);
1388     }
1389 }
1390 
1391 /// ditto
1392 SysTime timeLastModified(R)(auto ref R name)
1393 if (isConvertibleToString!R)
1394 {
1395     return timeLastModified!(StringTypeOf!R)(name);
1396 }
1397 
1398 @safe unittest
1399 {
1400     static assert(__traits(compiles, timeLastModified(TestAliasedString("foo"))));
1401 }
1402 
1403 /++
1404     Returns the time that the given file was last modified. If the
1405     file does not exist, returns $(D returnIfMissing).
1406 
1407     A frequent usage pattern occurs in build automation tools such as
1408     $(HTTP gnu.org/software/make, make) or $(HTTP
1409     en.wikipedia.org/wiki/Apache_Ant, ant). To check whether file $(D
1410     target) must be rebuilt from file $(D source) (i.e., $(D target) is
1411     older than $(D source) or does not exist), use the comparison
1412     below. The code throws a $(D FileException) if $(D source) does not
1413     exist (as it should). On the other hand, the $(D SysTime.min) default
1414     makes a non-existing $(D target) seem infinitely old so the test
1415     correctly prompts building it.
1416 
1417     Params:
1418         name            = The _name of the file to get the modification time for.
1419         returnIfMissing = The time to return if the given file does not exist.
1420 
1421 Example:
1422 --------------------
1423 if (timeLastModified(source) >= timeLastModified(target, SysTime.min))
1424 {
1425     // must (re)build
1426 }
1427 else
1428 {
1429     // target is up-to-date
1430 }
1431 --------------------
1432 +/
1433 SysTime timeLastModified(R)(R name, SysTime returnIfMissing)
1434 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
1435 {
version(Windows)1436     version (Windows)
1437     {
1438         if (!exists(name))
1439             return returnIfMissing;
1440 
1441         SysTime dummy;
1442         SysTime ftm;
1443 
1444         getTimesWin(name, dummy, dummy, ftm);
1445 
1446         return ftm;
1447     }
version(Posix)1448     else version (Posix)
1449     {
1450         auto namez = name.tempCString!FSChar();
1451         static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1452         {
1453             return stat(namez, &buf);
1454         }
1455         stat_t statbuf = void;
1456 
1457         return trustedStat(namez, statbuf) != 0 ?
1458                returnIfMissing :
1459                statTimeToStdTime!'m'(statbuf);
1460     }
1461 }
1462 
1463 @safe unittest
1464 {
1465     //std.process.system("echo a > deleteme") == 0 || assert(false);
1466     if (exists(deleteme))
1467         remove(deleteme);
1468 
1469     write(deleteme, "a\n");
1470 
scope(exit)1471     scope(exit)
1472     {
1473         assert(exists(deleteme));
1474         remove(deleteme);
1475     }
1476 
1477     // assert(lastModified("deleteme") >
1478     //         lastModified("this file does not exist", SysTime.min));
1479     //assert(lastModified("deleteme") > lastModified(__FILE__));
1480 }
1481 
1482 
1483 // Tests sub-second precision of querying file times.
1484 // Should pass on most modern systems running on modern filesystems.
1485 // Exceptions:
1486 // - FreeBSD, where one would need to first set the
1487 //   vfs.timestamp_precision sysctl to a value greater than zero.
1488 // - OS X, where the native filesystem (HFS+) stores filesystem
1489 //   timestamps with 1-second precision.
version(FreeBSD)1490 version (FreeBSD) {} else
version(DragonFlyBSD)1491 version (DragonFlyBSD) {} else
version(OSX)1492 version (OSX) {} else
1493 @system unittest
1494 {
1495     import core.thread;
1496 
1497     if (exists(deleteme))
1498         remove(deleteme);
1499 
1500     SysTime lastTime;
1501     foreach (n; 0 .. 3)
1502     {
1503         write(deleteme, "a");
1504         auto time = timeLastModified(deleteme);
1505         remove(deleteme);
1506         assert(time != lastTime);
1507         lastTime = time;
1508         Thread.sleep(10.msecs);
1509     }
1510 }
1511 
1512 
1513 /**
1514  * Determine whether the given file (or directory) _exists.
1515  * Params:
1516  *    name = string or range of characters representing the file _name
1517  * Returns:
1518  *    true if the file _name specified as input _exists
1519  */
1520 bool exists(R)(R name)
1521 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1522     !isConvertibleToString!R)
1523 {
1524     return existsImpl(name.tempCString!FSChar());
1525 }
1526 
1527 /// ditto
1528 bool exists(R)(auto ref R name)
1529 if (isConvertibleToString!R)
1530 {
1531     return exists!(StringTypeOf!R)(name);
1532 }
1533 
existsImpl(const (FSChar)* namez)1534 private bool existsImpl(const(FSChar)* namez) @trusted nothrow @nogc
1535 {
1536     version (Windows)
1537     {
1538         // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
1539         // fileio/base/getfileattributes.asp
1540         return GetFileAttributesW(namez) != 0xFFFFFFFF;
1541     }
1542     else version (Posix)
1543     {
1544         /*
1545             The reason why we use stat (and not access) here is
1546             the quirky behavior of access for SUID programs: if
1547             we used access, a file may not appear to "exist",
1548             despite that the program would be able to open it
1549             just fine. The behavior in question is described as
1550             follows in the access man page:
1551 
1552             > The check is done using the calling process's real
1553             > UID and GID, rather than the effective IDs as is
1554             > done when actually attempting an operation (e.g.,
1555             > open(2)) on the file. This allows set-user-ID
1556             > programs to easily determine the invoking user's
1557             > authority.
1558 
1559             While various operating systems provide eaccess or
1560             euidaccess functions, these are not part of POSIX -
1561             so it's safer to use stat instead.
1562         */
1563 
1564         stat_t statbuf = void;
1565         return lstat(namez, &statbuf) == 0;
1566     }
1567     else
1568         static assert(0);
1569 }
1570 
1571 @safe unittest
1572 {
1573     assert(exists("."));
1574     assert(!exists("this file does not exist"));
1575     write(deleteme, "a\n");
scope(exit)1576     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1577     assert(exists(deleteme));
1578 }
1579 
1580 @safe unittest // Bugzilla 16573
1581 {
1582     enum S : string { foo = "foo" }
1583     assert(__traits(compiles, S.foo.exists));
1584 }
1585 
1586 /++
1587  Returns the attributes of the given file.
1588 
1589  Note that the file attributes on Windows and Posix systems are
1590  completely different. On Windows, they're what is returned by
1591  $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx,
1592  GetFileAttributes), whereas on Posix systems, they're the $(LUCKY
1593  st_mode) value which is part of the $(D stat struct) gotten by
1594  calling the $(HTTP en.wikipedia.org/wiki/Stat_%28Unix%29, $(D stat))
1595  function.
1596 
1597  On Posix systems, if the given file is a symbolic link, then
1598  attributes are the attributes of the file pointed to by the symbolic
1599  link.
1600 
1601  Params:
1602  name = The file to get the attributes of.
1603 
1604  Throws: $(D FileException) on error.
1605   +/
1606 uint getAttributes(R)(R name)
1607 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1608     !isConvertibleToString!R)
1609 {
version(Windows)1610     version (Windows)
1611     {
1612         auto namez = name.tempCString!FSChar();
1613         static auto trustedGetFileAttributesW(const(FSChar)* namez) @trusted
1614         {
1615             return GetFileAttributesW(namez);
1616         }
1617         immutable result = trustedGetFileAttributesW(namez);
1618 
1619         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1620             alias names = name;
1621         else
1622             string names = null;
1623         cenforce(result != INVALID_FILE_ATTRIBUTES, names, namez);
1624 
1625         return result;
1626     }
version(Posix)1627     else version (Posix)
1628     {
1629         auto namez = name.tempCString!FSChar();
1630         static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1631         {
1632             return stat(namez, &buf);
1633         }
1634         stat_t statbuf = void;
1635 
1636         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1637             alias names = name;
1638         else
1639             string names = null;
1640         cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1641 
1642         return statbuf.st_mode;
1643     }
1644 }
1645 
1646 /// ditto
1647 uint getAttributes(R)(auto ref R name)
1648 if (isConvertibleToString!R)
1649 {
1650     return getAttributes!(StringTypeOf!R)(name);
1651 }
1652 
1653 @safe unittest
1654 {
1655     static assert(__traits(compiles, getAttributes(TestAliasedString(null))));
1656 }
1657 
1658 /++
1659     If the given file is a symbolic link, then this returns the attributes of the
1660     symbolic link itself rather than file that it points to. If the given file
1661     is $(I not) a symbolic link, then this function returns the same result
1662     as getAttributes.
1663 
1664     On Windows, getLinkAttributes is identical to getAttributes. It exists on
1665     Windows so that you don't have to special-case code for Windows when dealing
1666     with symbolic links.
1667 
1668     Params:
1669         name = The file to get the symbolic link attributes of.
1670 
1671     Returns:
1672         the attributes
1673 
1674     Throws:
1675         $(D FileException) on error.
1676  +/
1677 uint getLinkAttributes(R)(R name)
1678 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1679     !isConvertibleToString!R)
1680 {
version(Windows)1681     version (Windows)
1682     {
1683         return getAttributes(name);
1684     }
1685     else version (Posix)
1686     {
1687         auto namez = name.tempCString!FSChar();
1688         static auto trustedLstat(const(FSChar)* namez, ref stat_t buf) @trusted
1689         {
1690             return lstat(namez, &buf);
1691         }
1692         stat_t lstatbuf = void;
1693         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1694             alias names = name;
1695         else
1696             string names = null;
1697         cenforce(trustedLstat(namez, lstatbuf) == 0, names, namez);
1698         return lstatbuf.st_mode;
1699     }
1700 }
1701 
1702 /// ditto
1703 uint getLinkAttributes(R)(auto ref R name)
1704 if (isConvertibleToString!R)
1705 {
1706     return getLinkAttributes!(StringTypeOf!R)(name);
1707 }
1708 
1709 @safe unittest
1710 {
1711     static assert(__traits(compiles, getLinkAttributes(TestAliasedString(null))));
1712 }
1713 
1714 /++
1715     Set the _attributes of the given file.
1716 
1717     Params:
1718         name = the file _name
1719         attributes = the _attributes to set the file to
1720 
1721     Throws:
1722         $(D FileException) if the given file does not exist.
1723  +/
1724 void setAttributes(R)(R name, uint attributes)
1725 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1726     !isConvertibleToString!R)
1727 {
1728     version (Windows)
1729     {
1730         auto namez = name.tempCString!FSChar();
1731         static auto trustedSetFileAttributesW(const(FSChar)* namez, uint dwFileAttributes) @trusted
1732         {
1733             return SetFileAttributesW(namez, dwFileAttributes);
1734         }
1735         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1736             alias names = name;
1737         else
1738             string names = null;
1739         cenforce(trustedSetFileAttributesW(namez, attributes), names, namez);
1740     }
1741     else version (Posix)
1742     {
1743         auto namez = name.tempCString!FSChar();
1744         static auto trustedChmod(const(FSChar)* namez, mode_t mode) @trusted
1745         {
1746             return chmod(namez, mode);
1747         }
1748         assert(attributes <= mode_t.max);
1749         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1750             alias names = name;
1751         else
1752             string names = null;
1753         cenforce(!trustedChmod(namez, cast(mode_t) attributes), names, namez);
1754     }
1755 }
1756 
1757 /// ditto
1758 void setAttributes(R)(auto ref R name, uint attributes)
1759 if (isConvertibleToString!R)
1760 {
1761     return setAttributes!(StringTypeOf!R)(name, attributes);
1762 }
1763 
1764 @safe unittest
1765 {
1766     static assert(__traits(compiles, setAttributes(TestAliasedString(null), 0)));
1767 }
1768 
1769 /++
1770     Returns whether the given file is a directory.
1771 
1772     Params:
1773         name = The path to the file.
1774 
1775     Returns:
1776         true if name specifies a directory
1777 
1778     Throws:
1779         $(D FileException) if the given file does not exist.
1780 
1781 Example:
1782 --------------------
1783 assert(!"/etc/fonts/fonts.conf".isDir);
1784 assert("/usr/share/include".isDir);
1785 --------------------
1786   +/
1787 @property bool isDir(R)(R name)
1788 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1789     !isConvertibleToString!R)
1790 {
1791     version (Windows)
1792     {
1793         return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) != 0;
1794     }
1795     else version (Posix)
1796     {
1797         return (getAttributes(name) & S_IFMT) == S_IFDIR;
1798     }
1799 }
1800 
1801 /// ditto
1802 @property bool isDir(R)(auto ref R name)
1803 if (isConvertibleToString!R)
1804 {
1805     return name.isDir!(StringTypeOf!R);
1806 }
1807 
1808 @safe unittest
1809 {
1810     static assert(__traits(compiles, TestAliasedString(null).isDir));
1811 }
1812 
1813 @safe unittest
1814 {
1815     version (Windows)
1816     {
1817         if ("C:\\Program Files\\".exists)
1818             assert("C:\\Program Files\\".isDir);
1819 
1820         if ("C:\\Windows\\system.ini".exists)
1821             assert(!"C:\\Windows\\system.ini".isDir);
1822     }
1823     else version (Posix)
1824     {
1825         if (system_directory.exists)
1826             assert(system_directory.isDir);
1827 
1828         if (system_file.exists)
1829             assert(!system_file.isDir);
1830     }
1831 }
1832 
1833 @system unittest
1834 {
1835     version (Windows)
1836         enum dir = "C:\\Program Files\\";
1837     else version (Posix)
1838         enum dir = system_directory;
1839 
1840     if (dir.exists)
1841     {
1842         DirEntry de = DirEntry(dir);
1843         assert(de.isDir);
1844         assert(DirEntry(dir).isDir);
1845     }
1846 }
1847 
1848 /++
1849     Returns whether the given file _attributes are for a directory.
1850 
1851     Params:
1852         attributes = The file _attributes.
1853 
1854     Returns:
1855         true if attributes specifies a directory
1856 
1857 Example:
1858 --------------------
1859 assert(!attrIsDir(getAttributes("/etc/fonts/fonts.conf")));
1860 assert(!attrIsDir(getLinkAttributes("/etc/fonts/fonts.conf")));
1861 --------------------
1862   +/
1863 bool attrIsDir(uint attributes) @safe pure nothrow @nogc
1864 {
1865     version (Windows)
1866     {
1867         return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
1868     }
1869     else version (Posix)
1870     {
1871         return (attributes & S_IFMT) == S_IFDIR;
1872     }
1873 }
1874 
1875 @safe unittest
1876 {
1877     version (Windows)
1878     {
1879         if ("C:\\Program Files\\".exists)
1880         {
1881             assert(attrIsDir(getAttributes("C:\\Program Files\\")));
1882             assert(attrIsDir(getLinkAttributes("C:\\Program Files\\")));
1883         }
1884 
1885         if ("C:\\Windows\\system.ini".exists)
1886         {
1887             assert(!attrIsDir(getAttributes("C:\\Windows\\system.ini")));
1888             assert(!attrIsDir(getLinkAttributes("C:\\Windows\\system.ini")));
1889         }
1890     }
1891     else version (Posix)
1892     {
1893         if (system_directory.exists)
1894         {
1895             assert(attrIsDir(getAttributes(system_directory)));
1896             assert(attrIsDir(getLinkAttributes(system_directory)));
1897         }
1898 
1899         if (system_file.exists)
1900         {
1901             assert(!attrIsDir(getAttributes(system_file)));
1902             assert(!attrIsDir(getLinkAttributes(system_file)));
1903         }
1904     }
1905 }
1906 
1907 
1908 /++
1909     Returns whether the given file (or directory) is a file.
1910 
1911     On Windows, if a file is not a directory, then it's a file. So,
1912     either $(D isFile) or $(D isDir) will return true for any given file.
1913 
1914     On Posix systems, if $(D isFile) is $(D true), that indicates that the file
1915     is a regular file (e.g. not a block not device). So, on Posix systems, it's
1916     possible for both $(D isFile) and $(D isDir) to be $(D false) for a
1917     particular file (in which case, it's a special file). You can use
1918     $(D getAttributes) to get the attributes to figure out what type of special
1919     it is, or you can use $(D DirEntry) to get at its $(D statBuf), which is the
1920     result from $(D stat). In either case, see the man page for $(D stat) for
1921     more information.
1922 
1923     Params:
1924         name = The path to the file.
1925 
1926     Returns:
1927         true if name specifies a file
1928 
1929     Throws:
1930         $(D FileException) if the given file does not exist.
1931 
1932 Example:
1933 --------------------
1934 assert("/etc/fonts/fonts.conf".isFile);
1935 assert(!"/usr/share/include".isFile);
1936 --------------------
1937   +/
1938 @property bool isFile(R)(R name)
1939 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1940     !isConvertibleToString!R)
1941 {
1942     version (Windows)
1943         return !name.isDir;
1944     else version (Posix)
1945         return (getAttributes(name) & S_IFMT) == S_IFREG;
1946 }
1947 
1948 /// ditto
1949 @property bool isFile(R)(auto ref R name)
1950 if (isConvertibleToString!R)
1951 {
1952     return isFile!(StringTypeOf!R)(name);
1953 }
1954 
1955 @system unittest // bugzilla 15658
1956 {
1957     DirEntry e = DirEntry(".");
1958     static assert(is(typeof(isFile(e))));
1959 }
1960 
1961 @safe unittest
1962 {
1963     static assert(__traits(compiles, TestAliasedString(null).isFile));
1964 }
1965 
1966 @safe unittest
1967 {
version(Windows)1968     version (Windows)
1969     {
1970         if ("C:\\Program Files\\".exists)
1971             assert(!"C:\\Program Files\\".isFile);
1972 
1973         if ("C:\\Windows\\system.ini".exists)
1974             assert("C:\\Windows\\system.ini".isFile);
1975     }
version(Posix)1976     else version (Posix)
1977     {
1978         if (system_directory.exists)
1979             assert(!system_directory.isFile);
1980 
1981         if (system_file.exists)
1982             assert(system_file.isFile);
1983     }
1984 }
1985 
1986 
1987 /++
1988     Returns whether the given file _attributes are for a file.
1989 
1990     On Windows, if a file is not a directory, it's a file. So, either
1991     $(D attrIsFile) or $(D attrIsDir) will return $(D true) for the
1992     _attributes of any given file.
1993 
1994     On Posix systems, if $(D attrIsFile) is $(D true), that indicates that the
1995     file is a regular file (e.g. not a block not device). So, on Posix systems,
1996     it's possible for both $(D attrIsFile) and $(D attrIsDir) to be $(D false)
1997     for a particular file (in which case, it's a special file). If a file is a
1998     special file, you can use the _attributes to check what type of special file
1999     it is (see the man page for $(D stat) for more information).
2000 
2001     Params:
2002         attributes = The file _attributes.
2003 
2004     Returns:
2005         true if the given file _attributes are for a file
2006 
2007 Example:
2008 --------------------
2009 assert(attrIsFile(getAttributes("/etc/fonts/fonts.conf")));
2010 assert(attrIsFile(getLinkAttributes("/etc/fonts/fonts.conf")));
2011 --------------------
2012   +/
attrIsFile(uint attributes)2013 bool attrIsFile(uint attributes) @safe pure nothrow @nogc
2014 {
2015     version (Windows)
2016     {
2017         return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
2018     }
2019     else version (Posix)
2020     {
2021         return (attributes & S_IFMT) == S_IFREG;
2022     }
2023 }
2024 
2025 @safe unittest
2026 {
2027     version (Windows)
2028     {
2029         if ("C:\\Program Files\\".exists)
2030         {
2031             assert(!attrIsFile(getAttributes("C:\\Program Files\\")));
2032             assert(!attrIsFile(getLinkAttributes("C:\\Program Files\\")));
2033         }
2034 
2035         if ("C:\\Windows\\system.ini".exists)
2036         {
2037             assert(attrIsFile(getAttributes("C:\\Windows\\system.ini")));
2038             assert(attrIsFile(getLinkAttributes("C:\\Windows\\system.ini")));
2039         }
2040     }
2041     else version (Posix)
2042     {
2043         if (system_directory.exists)
2044         {
2045             assert(!attrIsFile(getAttributes(system_directory)));
2046             assert(!attrIsFile(getLinkAttributes(system_directory)));
2047         }
2048 
2049         if (system_file.exists)
2050         {
2051             assert(attrIsFile(getAttributes(system_file)));
2052             assert(attrIsFile(getLinkAttributes(system_file)));
2053         }
2054     }
2055 }
2056 
2057 
2058 /++
2059     Returns whether the given file is a symbolic link.
2060 
2061     On Windows, returns $(D true) when the file is either a symbolic link or a
2062     junction point.
2063 
2064     Params:
2065         name = The path to the file.
2066 
2067     Returns:
2068         true if name is a symbolic link
2069 
2070     Throws:
2071         $(D FileException) if the given file does not exist.
2072   +/
2073 @property bool isSymlink(R)(R name)
2074 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2075     !isConvertibleToString!R)
2076 {
2077     version (Windows)
2078         return (getAttributes(name) & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2079     else version (Posix)
2080         return (getLinkAttributes(name) & S_IFMT) == S_IFLNK;
2081 }
2082 
2083 /// ditto
2084 @property bool isSymlink(R)(auto ref R name)
2085 if (isConvertibleToString!R)
2086 {
2087     return name.isSymlink!(StringTypeOf!R);
2088 }
2089 
2090 @safe unittest
2091 {
2092     static assert(__traits(compiles, TestAliasedString(null).isSymlink));
2093 }
2094 
2095 @system unittest
2096 {
2097     version (Windows)
2098     {
2099         if ("C:\\Program Files\\".exists)
2100             assert(!"C:\\Program Files\\".isSymlink);
2101 
2102         if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
2103             assert("C:\\Documents and Settings\\".isSymlink);
2104 
2105         enum fakeSymFile = "C:\\Windows\\system.ini";
2106         if (fakeSymFile.exists)
2107         {
2108             assert(!fakeSymFile.isSymlink);
2109 
2110             assert(!fakeSymFile.isSymlink);
2111             assert(!attrIsSymlink(getAttributes(fakeSymFile)));
2112             assert(!attrIsSymlink(getLinkAttributes(fakeSymFile)));
2113 
2114             assert(attrIsFile(getAttributes(fakeSymFile)));
2115             assert(attrIsFile(getLinkAttributes(fakeSymFile)));
2116             assert(!attrIsDir(getAttributes(fakeSymFile)));
2117             assert(!attrIsDir(getLinkAttributes(fakeSymFile)));
2118 
2119             assert(getAttributes(fakeSymFile) == getLinkAttributes(fakeSymFile));
2120         }
2121     }
2122     else version (Posix)
2123     {
2124         if (system_directory.exists)
2125         {
2126             assert(!system_directory.isSymlink);
2127 
2128             immutable symfile = deleteme ~ "_slink\0";
2129             scope(exit) if (symfile.exists) symfile.remove();
2130 
2131             core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
2132 
2133             assert(symfile.isSymlink);
2134             assert(!attrIsSymlink(getAttributes(symfile)));
2135             assert(attrIsSymlink(getLinkAttributes(symfile)));
2136 
2137             assert(attrIsDir(getAttributes(symfile)));
2138             assert(!attrIsDir(getLinkAttributes(symfile)));
2139 
2140             assert(!attrIsFile(getAttributes(symfile)));
2141             assert(!attrIsFile(getLinkAttributes(symfile)));
2142         }
2143 
2144         if (system_file.exists)
2145         {
2146             assert(!system_file.isSymlink);
2147 
2148             immutable symfile = deleteme ~ "_slink\0";
2149             scope(exit) if (symfile.exists) symfile.remove();
2150 
2151             core.sys.posix.unistd.symlink(system_file, symfile.ptr);
2152 
2153             assert(symfile.isSymlink);
2154             assert(!attrIsSymlink(getAttributes(symfile)));
2155             assert(attrIsSymlink(getLinkAttributes(symfile)));
2156 
2157             assert(!attrIsDir(getAttributes(symfile)));
2158             assert(!attrIsDir(getLinkAttributes(symfile)));
2159 
2160             assert(attrIsFile(getAttributes(symfile)));
2161             assert(!attrIsFile(getLinkAttributes(symfile)));
2162         }
2163     }
2164 
2165     static assert(__traits(compiles, () @safe { return "dummy".isSymlink; }));
2166 }
2167 
2168 
2169 /++
2170     Returns whether the given file attributes are for a symbolic link.
2171 
2172     On Windows, return $(D true) when the file is either a symbolic link or a
2173     junction point.
2174 
2175     Params:
2176         attributes = The file attributes.
2177 
2178     Returns:
2179         true if attributes are for a symbolic link
2180 
2181 Example:
2182 --------------------
2183 core.sys.posix.unistd.symlink("/etc/fonts/fonts.conf", "/tmp/alink");
2184 
2185 assert(!getAttributes("/tmp/alink").isSymlink);
2186 assert(getLinkAttributes("/tmp/alink").isSymlink);
2187 --------------------
2188   +/
2189 bool attrIsSymlink(uint attributes) @safe pure nothrow @nogc
2190 {
2191     version (Windows)
2192         return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2193     else version (Posix)
2194         return (attributes & S_IFMT) == S_IFLNK;
2195 }
2196 
2197 
2198 /****************************************************
2199  * Change directory to $(D pathname).
2200  * Throws: $(D FileException) on error.
2201  */
2202 void chdir(R)(R pathname)
2203 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2204     !isConvertibleToString!R)
2205 {
2206     // Place outside of @trusted block
2207     auto pathz = pathname.tempCString!FSChar();
2208 
2209     version (Windows)
2210     {
2211         static auto trustedChdir(const(FSChar)* pathz) @trusted
2212         {
2213             return SetCurrentDirectoryW(pathz);
2214         }
2215     }
2216     else version (Posix)
2217     {
2218         static auto trustedChdir(const(FSChar)* pathz) @trusted
2219         {
2220             return core.sys.posix.unistd.chdir(pathz) == 0;
2221         }
2222     }
2223     static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2224         alias pathStr = pathname;
2225     else
2226         string pathStr = null;
2227     cenforce(trustedChdir(pathz), pathStr, pathz);
2228 }
2229 
2230 /// ditto
2231 void chdir(R)(auto ref R pathname)
2232 if (isConvertibleToString!R)
2233 {
2234     return chdir!(StringTypeOf!R)(pathname);
2235 }
2236 
2237 @safe unittest
2238 {
2239     static assert(__traits(compiles, chdir(TestAliasedString(null))));
2240 }
2241 
2242 /****************************************************
2243 Make directory $(D pathname).
2244 
2245 Throws: $(D FileException) on Posix or $(D WindowsException) on Windows
2246         if an error occured.
2247  */
2248 void mkdir(R)(R pathname)
2249 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2250     !isConvertibleToString!R)
2251 {
2252     // Place outside of @trusted block
2253     const pathz = pathname.tempCString!FSChar();
2254 
2255     version (Windows)
2256     {
2257         static auto trustedCreateDirectoryW(const(FSChar)* pathz) @trusted
2258         {
2259             return CreateDirectoryW(pathz, null);
2260         }
2261         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2262             alias pathStr = pathname;
2263         else
2264             string pathStr = null;
2265         wenforce(trustedCreateDirectoryW(pathz), pathStr, pathz);
2266     }
2267     else version (Posix)
2268     {
2269         import std.conv : octal;
2270 
2271         static auto trustedMkdir(const(FSChar)* pathz, mode_t mode) @trusted
2272         {
2273             return core.sys.posix.sys.stat.mkdir(pathz, mode);
2274         }
2275         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2276             alias pathStr = pathname;
2277         else
2278             string pathStr = null;
2279         cenforce(trustedMkdir(pathz, octal!777) == 0, pathStr, pathz);
2280     }
2281 }
2282 
2283 /// ditto
2284 void mkdir(R)(auto ref R pathname)
2285 if (isConvertibleToString!R)
2286 {
2287     return mkdir!(StringTypeOf!R)(pathname);
2288 }
2289 
2290 @safe unittest
2291 {
2292     import std.path : mkdir;
2293     static assert(__traits(compiles, mkdir(TestAliasedString(null))));
2294 }
2295 
2296 // Same as mkdir but ignores "already exists" errors.
2297 // Returns: "true" if the directory was created,
2298 //   "false" if it already existed.
2299 private bool ensureDirExists()(in char[] pathname)
2300 {
2301     import std.exception : enforce;
2302     const pathz = pathname.tempCString!FSChar();
2303 
2304     version (Windows)
2305     {
2306         if (() @trusted { return CreateDirectoryW(pathz, null); }())
2307             return true;
2308         cenforce(GetLastError() == ERROR_ALREADY_EXISTS, pathname.idup);
2309     }
2310     else version (Posix)
2311     {
2312         import std.conv : octal;
2313 
2314         if (() @trusted { return core.sys.posix.sys.stat.mkdir(pathz, octal!777); }() == 0)
2315             return true;
2316         cenforce(errno == EEXIST || errno == EISDIR, pathname);
2317     }
2318     enforce(pathname.isDir, new FileException(pathname.idup));
2319     return false;
2320 }
2321 
2322 /****************************************************
2323  * Make directory and all parent directories as needed.
2324  *
2325  * Does nothing if the directory specified by
2326  * $(D pathname) already exists.
2327  *
2328  * Throws: $(D FileException) on error.
2329  */
2330 
2331 void mkdirRecurse(in char[] pathname) @safe
2332 {
2333     import std.path : dirName, baseName;
2334 
2335     const left = dirName(pathname);
2336     if (left.length != pathname.length && !exists(left))
2337     {
2338         mkdirRecurse(left);
2339     }
2340     if (!baseName(pathname).empty)
2341     {
2342         ensureDirExists(pathname);
2343     }
2344 }
2345 
2346 @safe unittest
2347 {
2348     import std.exception : assertThrown;
2349     {
2350         import std.path : buildPath, buildNormalizedPath;
2351 
2352         immutable basepath = deleteme ~ "_dir";
2353         scope(exit) () @trusted { rmdirRecurse(basepath); }();
2354 
2355         auto path = buildPath(basepath, "a", "..", "b");
2356         mkdirRecurse(path);
2357         path = path.buildNormalizedPath;
2358         assert(path.isDir);
2359 
2360         path = buildPath(basepath, "c");
2361         write(path, "");
2362         assertThrown!FileException(mkdirRecurse(path));
2363 
2364         path = buildPath(basepath, "d");
2365         mkdirRecurse(path);
2366         mkdirRecurse(path); // should not throw
2367     }
2368 
2369     version (Windows)
2370     {
2371         assertThrown!FileException(mkdirRecurse(`1:\foobar`));
2372     }
2373 
2374     // bug3570
2375     {
2376         immutable basepath = deleteme ~ "_dir";
2377         version (Windows)
2378         {
2379             immutable path = basepath ~ "\\fake\\here\\";
2380         }
2381         else version (Posix)
2382         {
2383             immutable path = basepath ~ `/fake/here/`;
2384         }
2385 
2386         mkdirRecurse(path);
2387         assert(basepath.exists && basepath.isDir);
2388         scope(exit) () @trusted { rmdirRecurse(basepath); }();
2389         assert(path.exists && path.isDir);
2390     }
2391 }
2392 
2393 /****************************************************
2394 Remove directory $(D pathname).
2395 
2396 Params:
2397     pathname = Range or string specifying the directory name
2398 
2399 Throws: $(D FileException) on error.
2400  */
2401 void rmdir(R)(R pathname)
2402 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2403     !isConvertibleToString!R)
2404 {
2405     // Place outside of @trusted block
2406     auto pathz = pathname.tempCString!FSChar();
2407 
2408     version (Windows)
2409     {
2410         static auto trustedRmdir(const(FSChar)* pathz) @trusted
2411         {
2412             return RemoveDirectoryW(pathz);
2413         }
2414     }
2415     else version (Posix)
2416     {
2417         static auto trustedRmdir(const(FSChar)* pathz) @trusted
2418         {
2419             return core.sys.posix.unistd.rmdir(pathz) == 0;
2420         }
2421     }
2422     static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2423         alias pathStr = pathname;
2424     else
2425         string pathStr = null;
2426     cenforce(trustedRmdir(pathz), pathStr, pathz);
2427 }
2428 
2429 /// ditto
2430 void rmdir(R)(auto ref R pathname)
2431 if (isConvertibleToString!R)
2432 {
2433     rmdir!(StringTypeOf!R)(pathname);
2434 }
2435 
2436 @safe unittest
2437 {
2438     static assert(__traits(compiles, rmdir(TestAliasedString(null))));
2439 }
2440 
2441 /++
2442     $(BLUE This function is Posix-Only.)
2443 
2444     Creates a symbolic _link (_symlink).
2445 
2446     Params:
2447         original = The file that is being linked. This is the target path that's
2448             stored in the _symlink. A relative path is relative to the created
2449             _symlink.
2450         link = The _symlink to create. A relative path is relative to the
2451             current working directory.
2452 
2453     Throws:
2454         $(D FileException) on error (which includes if the _symlink already
2455         exists).
2456   +/
2457 version (StdDdoc) void symlink(RO, RL)(RO original, RL link)
2458 if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) ||
2459     isConvertibleToString!RO) &&
2460     (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) ||
2461     isConvertibleToString!RL));
2462 else version (Posix) void symlink(RO, RL)(RO original, RL link)
2463 if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) ||
2464     isConvertibleToString!RO) &&
2465     (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) ||
2466     isConvertibleToString!RL))
2467 {
2468     static if (isConvertibleToString!RO || isConvertibleToString!RL)
2469     {
2470         import std.meta : staticMap;
2471         alias Types = staticMap!(convertToString, RO, RL);
2472         symlink!Types(original, link);
2473     }
2474     else
2475     {
2476         import std.conv : text;
2477         auto oz = original.tempCString();
2478         auto lz = link.tempCString();
2479         alias posixSymlink = core.sys.posix.unistd.symlink;
2480         immutable int result = () @trusted { return posixSymlink(oz, lz); } ();
2481         cenforce(result == 0, text(link));
2482     }
2483 }
2484 
version(Posix)2485 version (Posix) @safe unittest
2486 {
2487     if (system_directory.exists)
2488     {
2489         immutable symfile = deleteme ~ "_slink\0";
2490         scope(exit) if (symfile.exists) symfile.remove();
2491 
2492         symlink(system_directory, symfile);
2493 
2494         assert(symfile.exists);
2495         assert(symfile.isSymlink);
2496         assert(!attrIsSymlink(getAttributes(symfile)));
2497         assert(attrIsSymlink(getLinkAttributes(symfile)));
2498 
2499         assert(attrIsDir(getAttributes(symfile)));
2500         assert(!attrIsDir(getLinkAttributes(symfile)));
2501 
2502         assert(!attrIsFile(getAttributes(symfile)));
2503         assert(!attrIsFile(getLinkAttributes(symfile)));
2504     }
2505 
2506     if (system_file.exists)
2507     {
2508         assert(!system_file.isSymlink);
2509 
2510         immutable symfile = deleteme ~ "_slink\0";
2511         scope(exit) if (symfile.exists) symfile.remove();
2512 
2513         symlink(system_file, symfile);
2514 
2515         assert(symfile.exists);
2516         assert(symfile.isSymlink);
2517         assert(!attrIsSymlink(getAttributes(symfile)));
2518         assert(attrIsSymlink(getLinkAttributes(symfile)));
2519 
2520         assert(!attrIsDir(getAttributes(symfile)));
2521         assert(!attrIsDir(getLinkAttributes(symfile)));
2522 
2523         assert(attrIsFile(getAttributes(symfile)));
2524         assert(!attrIsFile(getLinkAttributes(symfile)));
2525     }
2526 }
2527 
version(Posix)2528 version (Posix) @safe unittest
2529 {
2530     static assert(__traits(compiles,
2531         symlink(TestAliasedString(null), TestAliasedString(null))));
2532 }
2533 
2534 
2535 /++
2536     $(BLUE This function is Posix-Only.)
2537 
2538     Returns the path to the file pointed to by a symlink. Note that the
2539     path could be either relative or absolute depending on the symlink.
2540     If the path is relative, it's relative to the symlink, not the current
2541     working directory.
2542 
2543     Throws:
2544         $(D FileException) on error.
2545   +/
2546 version (StdDdoc) string readLink(R)(R link)
2547 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) ||
2548     isConvertibleToString!R);
2549 else version (Posix) string readLink(R)(R link)
2550 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) ||
2551     isConvertibleToString!R)
2552 {
2553     static if (isConvertibleToString!R)
2554     {
2555         return readLink!(convertToString!R)(link);
2556     }
2557     else
2558     {
2559         import std.conv : to;
2560         import std.exception : assumeUnique;
2561         alias posixReadlink = core.sys.posix.unistd.readlink;
2562         enum bufferLen = 2048;
2563         enum maxCodeUnits = 6;
2564         char[bufferLen] buffer;
2565         const linkz = link.tempCString();
2566         auto size = () @trusted {
2567             return posixReadlink(linkz, buffer.ptr, buffer.length);
2568         } ();
2569         cenforce(size != -1, to!string(link));
2570 
2571         if (size <= bufferLen - maxCodeUnits)
2572             return to!string(buffer[0 .. size]);
2573 
2574         auto dynamicBuffer = new char[](bufferLen * 3 / 2);
2575 
2576         foreach (i; 0 .. 10)
2577         {
2578             size = () @trusted {
2579                 return posixReadlink(linkz, dynamicBuffer.ptr,
2580                     dynamicBuffer.length);
2581             } ();
2582             cenforce(size != -1, to!string(link));
2583 
2584             if (size <= dynamicBuffer.length - maxCodeUnits)
2585             {
2586                 dynamicBuffer.length = size;
2587                 return () @trusted {
2588                     return assumeUnique(dynamicBuffer);
2589                 } ();
2590             }
2591 
2592             dynamicBuffer.length = dynamicBuffer.length * 3 / 2;
2593         }
2594 
2595         throw new FileException(to!string(link), "Path is too long to read.");
2596     }
2597 }
2598 
version(Posix)2599 version (Posix) @safe unittest
2600 {
2601     import std.exception : assertThrown;
2602     import std.string;
2603 
2604     foreach (file; [system_directory, system_file])
2605     {
2606         if (file.exists)
2607         {
2608             immutable symfile = deleteme ~ "_slink\0";
2609             scope(exit) if (symfile.exists) symfile.remove();
2610 
2611             symlink(file, symfile);
2612             assert(readLink(symfile) == file, format("Failed file: %s", file));
2613         }
2614     }
2615 
2616     assertThrown!FileException(readLink("/doesnotexist"));
2617 }
2618 
2619 version (Posix) @safe unittest
2620 {
2621     static assert(__traits(compiles, readLink(TestAliasedString("foo"))));
2622 }
2623 
2624 version (Posix) @system unittest // input range of dchars
2625 {
2626     mkdirRecurse(deleteme);
2627     scope(exit) if (deleteme.exists) rmdirRecurse(deleteme);
2628     write(deleteme ~ "/f", "");
2629     import std.range.interfaces : InputRange, inputRangeObject;
2630     import std.utf : byChar;
2631     immutable string link = deleteme ~ "/l";
2632     symlink("f", link);
2633     InputRange!dchar linkr = inputRangeObject(link);
2634     alias R = typeof(linkr);
2635     static assert(isInputRange!R);
2636     static assert(!isForwardRange!R);
2637     assert(readLink(linkr) == "f");
2638 }
2639 
2640 
2641 /****************************************************
2642  * Get the current working directory.
2643  * Throws: $(D FileException) on error.
2644  */
2645 version (Windows) string getcwd()
2646 {
2647     import std.conv : to;
2648     /* GetCurrentDirectory's return value:
2649         1. function succeeds: the number of characters that are written to
2650     the buffer, not including the terminating null character.
2651         2. function fails: zero
2652         3. the buffer (lpBuffer) is not large enough: the required size of
2653     the buffer, in characters, including the null-terminating character.
2654     */
2655     wchar[4096] buffW = void; //enough for most common case
2656     immutable n = cenforce(GetCurrentDirectoryW(to!DWORD(buffW.length), buffW.ptr),
2657             "getcwd");
2658     // we can do it because toUTFX always produces a fresh string
2659     if (n < buffW.length)
2660     {
2661         return buffW[0 .. n].to!string;
2662     }
2663     else //staticBuff isn't enough
2664     {
2665         auto ptr = cast(wchar*) malloc(wchar.sizeof * n);
2666         scope(exit) free(ptr);
2667         immutable n2 = GetCurrentDirectoryW(n, ptr);
2668         cenforce(n2 && n2 < n, "getcwd");
2669         return ptr[0 .. n2].to!string;
2670     }
2671 }
version(Solaris)2672 else version (Solaris) string getcwd()
2673 {
2674     /* BUF_SIZE >= PATH_MAX */
2675     enum BUF_SIZE = 4096;
2676     /* The user should be able to specify any size buffer > 0 */
2677     auto p = cenforce(core.sys.posix.unistd.getcwd(null, BUF_SIZE),
2678             "cannot get cwd");
2679     scope(exit) core.stdc.stdlib.free(p);
2680     return p[0 .. core.stdc.string.strlen(p)].idup;
2681 }
version(Posix)2682 else version (Posix) string getcwd()
2683 {
2684     auto p = cenforce(core.sys.posix.unistd.getcwd(null, 0),
2685             "cannot get cwd");
2686     scope(exit) core.stdc.stdlib.free(p);
2687     return p[0 .. core.stdc.string.strlen(p)].idup;
2688 }
2689 
2690 @system unittest
2691 {
2692     auto s = getcwd();
2693     assert(s.length);
2694 }
2695 
2696 version (OSX)
2697     private extern (C) int _NSGetExecutablePath(char* buf, uint* bufsize);
2698 else version (FreeBSD)
2699     private extern (C) int sysctl (const int* name, uint namelen, void* oldp,
2700         size_t* oldlenp, const void* newp, size_t newlen);
2701 else version (NetBSD)
2702     private extern (C) int sysctl (const int* name, uint namelen, void* oldp,
2703         size_t* oldlenp, const void* newp, size_t newlen);
2704 
2705 /**
2706  * Returns the full path of the current executable.
2707  *
2708  * Throws:
2709  * $(REF1 Exception, object)
2710  */
thisExePath()2711 @trusted string thisExePath ()
2712 {
2713     version (OSX)
2714     {
2715         import core.sys.posix.stdlib : realpath;
2716         import std.conv : to;
2717         import std.exception : errnoEnforce;
2718 
2719         uint size;
2720 
2721         _NSGetExecutablePath(null, &size); // get the length of the path
2722         auto buffer = new char[size];
2723         _NSGetExecutablePath(buffer.ptr, &size);
2724 
2725         auto absolutePath = realpath(buffer.ptr, null); // let the function allocate
2726 
2727         scope (exit)
2728         {
2729             if (absolutePath)
2730                 free(absolutePath);
2731         }
2732 
2733         errnoEnforce(absolutePath);
2734         return to!(string)(absolutePath);
2735     }
2736     else version (linux)
2737     {
2738         return readLink("/proc/self/exe");
2739     }
2740     else version (Windows)
2741     {
2742         import std.conv : to;
2743         import std.exception : enforce;
2744 
2745         wchar[MAX_PATH] buf;
2746         wchar[] buffer = buf[];
2747 
2748         while (true)
2749         {
2750             auto len = GetModuleFileNameW(null, buffer.ptr, cast(DWORD) buffer.length);
2751             enforce(len, sysErrorString(GetLastError()));
2752             if (len != buffer.length)
2753                 return to!(string)(buffer[0 .. len]);
2754             buffer.length *= 2;
2755         }
2756     }
2757     else version (FreeBSD)
2758     {
2759         import std.exception : errnoEnforce, assumeUnique;
2760         enum
2761         {
2762             CTL_KERN = 1,
2763             KERN_PROC = 14,
2764             KERN_PROC_PATHNAME = 12
2765         }
2766 
2767         int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
2768         size_t len;
2769 
2770         auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
2771         errnoEnforce(result == 0);
2772 
2773         auto buffer = new char[len - 1];
2774         result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
2775         errnoEnforce(result == 0);
2776 
2777         return buffer.assumeUnique;
2778     }
2779     else version (NetBSD)
2780     {
2781         return readLink("/proc/self/exe");
2782     }
2783     else version (DragonFlyBSD)
2784     {
2785         return readLink("/proc/curproc/file");
2786     }
2787     else version (Solaris)
2788     {
2789         import core.sys.posix.unistd : getpid;
2790         import std.string : format;
2791 
2792         // Only Solaris 10 and later
2793         return readLink(format("/proc/%d/path/a.out", getpid()));
2794     }
2795     else
2796         static assert(0, "thisExePath is not supported on this platform");
2797 }
2798 
2799 @safe unittest
2800 {
2801     import std.path : isAbsolute;
2802     auto path = thisExePath();
2803 
2804     assert(path.exists);
2805     assert(path.isAbsolute);
2806     assert(path.isFile);
2807 }
2808 
version(StdDdoc)2809 version (StdDdoc)
2810 {
2811     /++
2812         Info on a file, similar to what you'd get from stat on a Posix system.
2813       +/
2814     struct DirEntry
2815     {
2816         /++
2817             Constructs a $(D DirEntry) for the given file (or directory).
2818 
2819             Params:
2820                 path = The file (or directory) to get a DirEntry for.
2821 
2822             Throws:
2823                 $(D FileException) if the file does not exist.
2824         +/
2825         this(string path);
2826 
2827         version (Windows)
2828         {
2829             private this(string path, in WIN32_FIND_DATAW *fd);
2830         }
2831         else version (Posix)
2832         {
2833             private this(string path, core.sys.posix.dirent.dirent* fd);
2834         }
2835 
2836         /++
2837             Returns the path to the file represented by this $(D DirEntry).
2838 
2839 Example:
2840 --------------------
2841 auto de1 = DirEntry("/etc/fonts/fonts.conf");
2842 assert(de1.name == "/etc/fonts/fonts.conf");
2843 
2844 auto de2 = DirEntry("/usr/share/include");
2845 assert(de2.name == "/usr/share/include");
2846 --------------------
2847           +/
2848         @property string name() const;
2849 
2850 
2851         /++
2852             Returns whether the file represented by this $(D DirEntry) is a
2853             directory.
2854 
2855 Example:
2856 --------------------
2857 auto de1 = DirEntry("/etc/fonts/fonts.conf");
2858 assert(!de1.isDir);
2859 
2860 auto de2 = DirEntry("/usr/share/include");
2861 assert(de2.isDir);
2862 --------------------
2863           +/
2864         @property bool isDir();
2865 
2866 
2867         /++
2868             Returns whether the file represented by this $(D DirEntry) is a file.
2869 
2870             On Windows, if a file is not a directory, then it's a file. So,
2871             either $(D isFile) or $(D isDir) will return $(D true).
2872 
2873             On Posix systems, if $(D isFile) is $(D true), that indicates that
2874             the file is a regular file (e.g. not a block not device). So, on
2875             Posix systems, it's possible for both $(D isFile) and $(D isDir) to
2876             be $(D false) for a particular file (in which case, it's a special
2877             file). You can use $(D attributes) or $(D statBuf) to get more
2878             information about a special file (see the stat man page for more
2879             details).
2880 
2881 Example:
2882 --------------------
2883 auto de1 = DirEntry("/etc/fonts/fonts.conf");
2884 assert(de1.isFile);
2885 
2886 auto de2 = DirEntry("/usr/share/include");
2887 assert(!de2.isFile);
2888 --------------------
2889           +/
2890         @property bool isFile();
2891 
2892         /++
2893             Returns whether the file represented by this $(D DirEntry) is a
2894             symbolic link.
2895 
2896             On Windows, return $(D true) when the file is either a symbolic
2897             link or a junction point.
2898           +/
2899         @property bool isSymlink();
2900 
2901         /++
2902             Returns the size of the the file represented by this $(D DirEntry)
2903             in bytes.
2904           +/
2905         @property ulong size();
2906 
2907         /++
2908             $(BLUE This function is Windows-Only.)
2909 
2910             Returns the creation time of the file represented by this
2911             $(D DirEntry).
2912           +/
2913         @property SysTime timeCreated() const;
2914 
2915         /++
2916             Returns the time that the file represented by this $(D DirEntry) was
2917             last accessed.
2918 
2919             Note that many file systems do not update the access time for files
2920             (generally for performance reasons), so there's a good chance that
2921             $(D timeLastAccessed) will return the same value as
2922             $(D timeLastModified).
2923           +/
2924         @property SysTime timeLastAccessed();
2925 
2926         /++
2927             Returns the time that the file represented by this $(D DirEntry) was
2928             last modified.
2929           +/
2930         @property SysTime timeLastModified();
2931 
2932         /++
2933             Returns the _attributes of the file represented by this $(D DirEntry).
2934 
2935             Note that the file _attributes on Windows and Posix systems are
2936             completely different. On, Windows, they're what is returned by
2937             $(D GetFileAttributes)
2938             $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, GetFileAttributes)
2939             Whereas, an Posix systems, they're the $(D st_mode) value which is
2940             part of the $(D stat) struct gotten by calling $(D stat).
2941 
2942             On Posix systems, if the file represented by this $(D DirEntry) is a
2943             symbolic link, then _attributes are the _attributes of the file
2944             pointed to by the symbolic link.
2945           +/
2946         @property uint attributes();
2947 
2948         /++
2949             On Posix systems, if the file represented by this $(D DirEntry) is a
2950             symbolic link, then $(D linkAttributes) are the attributes of the
2951             symbolic link itself. Otherwise, $(D linkAttributes) is identical to
2952             $(D attributes).
2953 
2954             On Windows, $(D linkAttributes) is identical to $(D attributes). It
2955             exists on Windows so that you don't have to special-case code for
2956             Windows when dealing with symbolic links.
2957           +/
2958         @property uint linkAttributes();
2959 
2960         version (Windows)
2961             alias stat_t = void*;
2962 
2963         /++
2964             $(BLUE This function is Posix-Only.)
2965 
2966             The $(D stat) struct gotten from calling $(D stat).
2967           +/
2968         @property stat_t statBuf();
2969     }
2970 }
2971 else version (Windows)
2972 {
2973     struct DirEntry
2974     {
2975     public:
2976         alias name this;
2977 
2978         this(string path)
2979         {
2980             import std.datetime.systime : FILETIMEToSysTime;
2981 
2982             if (!path.exists())
2983                 throw new FileException(path, "File does not exist");
2984 
2985             _name = path;
2986 
2987             with (getFileAttributesWin(path))
2988             {
2989                 _size = makeUlong(nFileSizeLow, nFileSizeHigh);
2990                 _timeCreated = FILETIMEToSysTime(&ftCreationTime);
2991                 _timeLastAccessed = FILETIMEToSysTime(&ftLastAccessTime);
2992                 _timeLastModified = FILETIMEToSysTime(&ftLastWriteTime);
2993                 _attributes = dwFileAttributes;
2994             }
2995         }
2996 
2997         private this(string path, in WIN32_FIND_DATAW *fd)
2998         {
2999             import core.stdc.wchar_ : wcslen;
3000             import std.conv : to;
3001             import std.datetime.systime : FILETIMEToSysTime;
3002             import std.path : buildPath;
3003 
3004             size_t clength = wcslen(fd.cFileName.ptr);
3005             _name = buildPath(path, fd.cFileName[0 .. clength].to!string);
3006             _size = (cast(ulong) fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
3007             _timeCreated = FILETIMEToSysTime(&fd.ftCreationTime);
3008             _timeLastAccessed = FILETIMEToSysTime(&fd.ftLastAccessTime);
3009             _timeLastModified = FILETIMEToSysTime(&fd.ftLastWriteTime);
3010             _attributes = fd.dwFileAttributes;
3011         }
3012 
3013         @property string name() const pure nothrow
3014         {
3015             return _name;
3016         }
3017 
3018         @property bool isDir() const pure nothrow
3019         {
3020             return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
3021         }
3022 
3023         @property bool isFile() const pure nothrow
3024         {
3025             //Are there no options in Windows other than directory and file?
3026             //If there are, then this probably isn't the best way to determine
3027             //whether this DirEntry is a file or not.
3028             return !isDir;
3029         }
3030 
3031         @property bool isSymlink() const pure nothrow
3032         {
3033             return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
3034         }
3035 
3036         @property ulong size() const pure nothrow
3037         {
3038             return _size;
3039         }
3040 
3041         @property SysTime timeCreated() const pure nothrow
3042         {
3043             return cast(SysTime)_timeCreated;
3044         }
3045 
3046         @property SysTime timeLastAccessed() const pure nothrow
3047         {
3048             return cast(SysTime)_timeLastAccessed;
3049         }
3050 
3051         @property SysTime timeLastModified() const pure nothrow
3052         {
3053             return cast(SysTime)_timeLastModified;
3054         }
3055 
3056         @property uint attributes() const pure nothrow
3057         {
3058             return _attributes;
3059         }
3060 
3061         @property uint linkAttributes() const pure nothrow
3062         {
3063             return _attributes;
3064         }
3065 
3066     private:
3067         string _name; /// The file or directory represented by this DirEntry.
3068 
3069         SysTime _timeCreated;      /// The time when the file was created.
3070         SysTime _timeLastAccessed; /// The time when the file was last accessed.
3071         SysTime _timeLastModified; /// The time when the file was last modified.
3072 
3073         ulong _size;       /// The size of the file in bytes.
3074         uint  _attributes; /// The file attributes from WIN32_FIND_DATAW.
3075     }
3076 }
3077 else version (Posix)
3078 {
3079     struct DirEntry
3080     {
3081     public:
3082         alias name this;
3083 
3084         this(string path)
3085         {
3086             if (!path.exists)
3087                 throw new FileException(path, "File does not exist");
3088 
3089             _name = path;
3090 
3091             _didLStat = false;
3092             _didStat = false;
3093             _dTypeSet = false;
3094         }
3095 
3096         private this(string path, core.sys.posix.dirent.dirent* fd)
3097         {
3098             import std.path : buildPath;
3099 
3100             static if (is(typeof(fd.d_namlen)))
3101                 immutable len = fd.d_namlen;
3102             else
3103                 immutable len = (() @trusted => core.stdc.string.strlen(fd.d_name.ptr))();
3104 
3105             _name = buildPath(path, (() @trusted => fd.d_name.ptr[0 .. len])());
3106 
3107             _didLStat = false;
3108             _didStat = false;
3109 
3110             //fd_d_type doesn't work for all file systems,
3111             //in which case the result is DT_UNKOWN. But we
3112             //can determine the correct type from lstat, so
3113             //we'll only set the dtype here if we could
3114             //correctly determine it (not lstat in the case
3115             //of DT_UNKNOWN in case we don't ever actually
3116             //need the dtype, thus potentially avoiding the
3117             //cost of calling lstat).
3118             static if (__traits(compiles, fd.d_type != DT_UNKNOWN))
3119             {
3120                 if (fd.d_type != DT_UNKNOWN)
3121                 {
3122                     _dType = fd.d_type;
3123                     _dTypeSet = true;
3124                 }
3125                 else
3126                     _dTypeSet = false;
3127             }
3128             else
3129             {
3130                 // e.g. Solaris does not have the d_type member
3131                 _dTypeSet = false;
3132             }
3133         }
3134 
3135         @property string name() const pure nothrow
3136         {
3137             return _name;
3138         }
3139 
3140         @property bool isDir()
3141         {
3142             _ensureStatOrLStatDone();
3143 
3144             return (_statBuf.st_mode & S_IFMT) == S_IFDIR;
3145         }
3146 
3147         @property bool isFile()
3148         {
3149             _ensureStatOrLStatDone();
3150 
3151             return (_statBuf.st_mode & S_IFMT) == S_IFREG;
3152         }
3153 
3154         @property bool isSymlink()
3155         {
3156             _ensureLStatDone();
3157 
3158             return (_lstatMode & S_IFMT) == S_IFLNK;
3159         }
3160 
3161         @property ulong size()
3162         {
3163             _ensureStatDone();
3164             return _statBuf.st_size;
3165         }
3166 
3167         @property SysTime timeStatusChanged()
3168         {
3169             _ensureStatDone();
3170 
3171             return statTimeToStdTime!'c'(_statBuf);
3172         }
3173 
3174         @property SysTime timeLastAccessed()
3175         {
3176             _ensureStatDone();
3177 
3178             return statTimeToStdTime!'a'(_statBuf);
3179         }
3180 
3181         @property SysTime timeLastModified()
3182         {
3183             _ensureStatDone();
3184 
3185             return statTimeToStdTime!'m'(_statBuf);
3186         }
3187 
3188         @property uint attributes()
3189         {
3190             _ensureStatDone();
3191 
3192             return _statBuf.st_mode;
3193         }
3194 
3195         @property uint linkAttributes()
3196         {
3197             _ensureLStatDone();
3198 
3199             return _lstatMode;
3200         }
3201 
3202         @property stat_t statBuf()
3203         {
3204             _ensureStatDone();
3205 
3206             return _statBuf;
3207         }
3208 
3209     private:
3210         /++
3211             This is to support lazy evaluation, because doing stat's is
3212             expensive and not always needed.
3213          +/
3214         void _ensureStatDone() @safe
3215         {
3216             import std.exception : enforce;
3217 
3218             static auto trustedStat(in char[] path, stat_t* buf) @trusted
3219             {
3220                 return stat(path.tempCString(), buf);
3221             }
3222             if (_didStat)
3223                 return;
3224 
3225             enforce(trustedStat(_name, &_statBuf) == 0,
3226                     "Failed to stat file `" ~ _name ~ "'");
3227 
3228             _didStat = true;
3229         }
3230 
3231         /++
3232             This is to support lazy evaluation, because doing stat's is
3233             expensive and not always needed.
3234 
3235             Try both stat and lstat for isFile and isDir
3236             to detect broken symlinks.
3237          +/
3238         void _ensureStatOrLStatDone()
3239         {
3240             if (_didStat)
3241                 return;
3242 
3243             if ( stat(_name.tempCString(), &_statBuf) != 0 )
3244             {
3245                 _ensureLStatDone();
3246 
3247                 _statBuf = stat_t.init;
3248                 _statBuf.st_mode = S_IFLNK;
3249             }
3250             else
3251             {
3252                 _didStat = true;
3253             }
3254         }
3255 
3256         /++
3257             This is to support lazy evaluation, because doing stat's is
3258             expensive and not always needed.
3259          +/
3260         void _ensureLStatDone()
3261         {
3262             import std.exception : enforce;
3263 
3264             if (_didLStat)
3265                 return;
3266 
3267             stat_t statbuf = void;
3268 
3269             enforce(lstat(_name.tempCString(), &statbuf) == 0,
3270                 "Failed to stat file `" ~ _name ~ "'");
3271 
3272             _lstatMode = statbuf.st_mode;
3273 
3274             _dTypeSet = true;
3275             _didLStat = true;
3276         }
3277 
3278         string _name; /// The file or directory represented by this DirEntry.
3279 
3280         stat_t _statBuf = void;  /// The result of stat().
3281         uint  _lstatMode;               /// The stat mode from lstat().
3282         ubyte _dType;                   /// The type of the file.
3283 
3284         bool _didLStat = false;   /// Whether lstat() has been called for this DirEntry.
3285         bool _didStat = false;    /// Whether stat() has been called for this DirEntry.
3286         bool _dTypeSet = false;   /// Whether the dType of the file has been set.
3287     }
3288 }
3289 
3290 @system unittest
3291 {
3292     version (Windows)
3293     {
3294         if ("C:\\Program Files\\".exists)
3295         {
3296             auto de = DirEntry("C:\\Program Files\\");
3297             assert(!de.isFile);
3298             assert(de.isDir);
3299             assert(!de.isSymlink);
3300         }
3301 
3302         if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
3303         {
3304             auto de = DirEntry("C:\\Documents and Settings\\");
3305             assert(de.isSymlink);
3306         }
3307 
3308         if ("C:\\Windows\\system.ini".exists)
3309         {
3310             auto de = DirEntry("C:\\Windows\\system.ini");
3311             assert(de.isFile);
3312             assert(!de.isDir);
3313             assert(!de.isSymlink);
3314         }
3315     }
3316     else version (Posix)
3317     {
3318         import std.exception : assertThrown;
3319 
3320         if (system_directory.exists)
3321         {
3322             {
3323                 auto de = DirEntry(system_directory);
3324                 assert(!de.isFile);
3325                 assert(de.isDir);
3326                 assert(!de.isSymlink);
3327             }
3328 
3329             immutable symfile = deleteme ~ "_slink\0";
3330             scope(exit) if (symfile.exists) symfile.remove();
3331 
3332             core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
3333 
3334             {
3335                 auto de = DirEntry(symfile);
3336                 assert(!de.isFile);
3337                 assert(de.isDir);
3338                 assert(de.isSymlink);
3339             }
3340 
3341             symfile.remove();
3342             core.sys.posix.unistd.symlink((deleteme ~ "_broken_symlink\0").ptr, symfile.ptr);
3343 
3344             {
3345                 //Issue 8298
3346                 DirEntry de = DirEntry(symfile);
3347 
3348                 assert(!de.isFile);
3349                 assert(!de.isDir);
3350                 assert(de.isSymlink);
3351                 assertThrown(de.size);
3352                 assertThrown(de.timeStatusChanged);
3353                 assertThrown(de.timeLastAccessed);
3354                 assertThrown(de.timeLastModified);
3355                 assertThrown(de.attributes);
3356                 assertThrown(de.statBuf);
3357                 assert(symfile.exists);
3358                 symfile.remove();
3359             }
3360         }
3361 
3362         if (system_file.exists)
3363         {
3364             auto de = DirEntry(system_file);
3365             assert(de.isFile);
3366             assert(!de.isDir);
3367             assert(!de.isSymlink);
3368         }
3369     }
3370 }
3371 
3372 alias PreserveAttributes = Flag!"preserveAttributes";
3373 
3374 version (StdDdoc)
3375 {
3376     /// Defaults to $(D Yes.preserveAttributes) on Windows, and the opposite on all other platforms.
3377     PreserveAttributes preserveAttributesDefault;
3378 }
3379 else version (Windows)
3380 {
3381     enum preserveAttributesDefault = Yes.preserveAttributes;
3382 }
3383 else
3384 {
3385     enum preserveAttributesDefault = No.preserveAttributes;
3386 }
3387 
3388 /***************************************************
3389 Copy file $(D from) _to file $(D to). File timestamps are preserved.
3390 File attributes are preserved, if $(D preserve) equals $(D Yes.preserveAttributes).
3391 On Windows only $(D Yes.preserveAttributes) (the default on Windows) is supported.
3392 If the target file exists, it is overwritten.
3393 
3394 Params:
3395     from = string or range of characters representing the existing file name
3396     to = string or range of characters representing the target file name
3397     preserve = whether to _preserve the file attributes
3398 
3399 Throws: $(D FileException) on error.
3400  */
3401 void copy(RF, RT)(RF from, RT to, PreserveAttributes preserve = preserveAttributesDefault)
3402 if (isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) && !isConvertibleToString!RF &&
3403     isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) && !isConvertibleToString!RT)
3404 {
3405     // Place outside of @trusted block
3406     auto fromz = from.tempCString!FSChar();
3407     auto toz = to.tempCString!FSChar();
3408 
3409     static if (isNarrowString!RF && is(Unqual!(ElementEncodingType!RF) == char))
3410         alias f = from;
3411     else
3412         enum string f = null;
3413 
3414     static if (isNarrowString!RT && is(Unqual!(ElementEncodingType!RT) == char))
3415         alias t = to;
3416     else
3417         enum string t = null;
3418 
3419     copyImpl(f, t, fromz, toz, preserve);
3420 }
3421 
3422 /// ditto
3423 void copy(RF, RT)(auto ref RF from, auto ref RT to, PreserveAttributes preserve = preserveAttributesDefault)
3424 if (isConvertibleToString!RF || isConvertibleToString!RT)
3425 {
3426     import std.meta : staticMap;
3427     alias Types = staticMap!(convertToString, RF, RT);
3428     copy!Types(from, to, preserve);
3429 }
3430 
3431 @safe unittest // issue 15319
3432 {
3433     assert(__traits(compiles, copy("from.txt", "to.txt")));
3434 }
3435 
3436 private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, const(FSChar)* toz,
3437         PreserveAttributes preserve) @trusted
3438 {
3439     version (Windows)
3440     {
3441         assert(preserve == Yes.preserveAttributes);
3442         immutable result = CopyFileW(fromz, toz, false);
3443         if (!result)
3444         {
3445             import core.stdc.wchar_ : wcslen;
3446             import std.conv : to;
3447 
3448             if (!t)
3449                 t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
3450 
3451             throw new FileException(t);
3452         }
3453     }
3454     else version (Posix)
3455     {
3456         static import core.stdc.stdio;
3457         import std.conv : to, octal;
3458 
3459         immutable fdr = core.sys.posix.fcntl.open(fromz, O_RDONLY);
3460         cenforce(fdr != -1, f, fromz);
3461         scope(exit) core.sys.posix.unistd.close(fdr);
3462 
3463         stat_t statbufr = void;
3464         cenforce(fstat(fdr, &statbufr) == 0, f, fromz);
3465         //cenforce(core.sys.posix.sys.stat.fstat(fdr, &statbufr) == 0, f, fromz);
3466 
3467         immutable fdw = core.sys.posix.fcntl.open(toz,
3468                 O_CREAT | O_WRONLY, octal!666);
3469         cenforce(fdw != -1, t, toz);
3470         {
3471             scope(failure) core.sys.posix.unistd.close(fdw);
3472 
3473             stat_t statbufw = void;
3474             cenforce(fstat(fdw, &statbufw) == 0, t, toz);
3475             if (statbufr.st_dev == statbufw.st_dev && statbufr.st_ino == statbufw.st_ino)
3476                 throw new FileException(t, "Source and destination are the same file");
3477         }
3478 
3479         scope(failure) core.stdc.stdio.remove(toz);
3480         {
3481             scope(failure) core.sys.posix.unistd.close(fdw);
3482             cenforce(ftruncate(fdw, 0) == 0, t, toz);
3483 
3484             auto BUFSIZ = 4096u * 16;
3485             auto buf = core.stdc.stdlib.malloc(BUFSIZ);
3486             if (!buf)
3487             {
3488                 BUFSIZ = 4096;
3489                 buf = core.stdc.stdlib.malloc(BUFSIZ);
3490                 if (!buf)
3491                 {
3492                     import core.exception : onOutOfMemoryError;
3493                     onOutOfMemoryError();
3494                 }
3495             }
3496             scope(exit) core.stdc.stdlib.free(buf);
3497 
3498             for (auto size = statbufr.st_size; size; )
3499             {
3500                 immutable toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size;
3501                 cenforce(
3502                     core.sys.posix.unistd.read(fdr, buf, toxfer) == toxfer
3503                     && core.sys.posix.unistd.write(fdw, buf, toxfer) == toxfer,
3504                     f, fromz);
3505                 assert(size >= toxfer);
3506                 size -= toxfer;
3507             }
3508             if (preserve)
3509                 cenforce(fchmod(fdw, to!mode_t(statbufr.st_mode)) == 0, f, fromz);
3510         }
3511 
3512         cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz);
3513 
3514         utimbuf utim = void;
3515         utim.actime = cast(time_t) statbufr.st_atime;
3516         utim.modtime = cast(time_t) statbufr.st_mtime;
3517 
3518         cenforce(utime(toz, &utim) != -1, f, fromz);
3519     }
3520 }
3521 
3522 @safe unittest
3523 {
3524     import std.algorithm, std.file; // issue 14817
3525     auto t1 = deleteme, t2 = deleteme~"2";
3526     scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
3527     write(t1, "11");
3528     copy(t1, t2);
3529     assert(readText(t2) == "11");
3530     write(t1, "2");
3531     copy(t1, t2);
3532     assert(readText(t2) == "2");
3533 
3534     import std.utf : byChar;
3535     copy(t1.byChar, t2.byChar);
3536     assert(readText(t2.byChar) == "2");
3537 }
3538 
3539 @safe version (Posix) @safe unittest //issue 11434
3540 {
3541     import std.conv : octal;
3542     auto t1 = deleteme, t2 = deleteme~"2";
3543     scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
3544     write(t1, "1");
3545     setAttributes(t1, octal!767);
3546     copy(t1, t2, Yes.preserveAttributes);
3547     assert(readText(t2) == "1");
3548     assert(getAttributes(t2) == octal!100767);
3549 }
3550 
3551 @safe unittest // issue 15865
3552 {
3553     import std.exception : assertThrown;
3554     auto t = deleteme;
3555     write(t, "a");
3556     scope(exit) t.remove();
3557     assertThrown!FileException(copy(t, t));
3558     assert(readText(t) == "a");
3559 }
3560 
3561 /++
3562     Remove directory and all of its content and subdirectories,
3563     recursively.
3564 
3565     Throws:
3566         $(D FileException) if there is an error (including if the given
3567         file is not a directory).
3568  +/
3569 void rmdirRecurse(in char[] pathname)
3570 {
3571     //No references to pathname will be kept after rmdirRecurse,
3572     //so the cast is safe
3573     rmdirRecurse(DirEntry(cast(string) pathname));
3574 }
3575 
3576 /++
3577     Remove directory and all of its content and subdirectories,
3578     recursively.
3579 
3580     Throws:
3581         $(D FileException) if there is an error (including if the given
3582         file is not a directory).
3583  +/
3584 void rmdirRecurse(ref DirEntry de)
3585 {
3586     if (!de.isDir)
3587         throw new FileException(de.name, "Not a directory");
3588 
3589     if (de.isSymlink)
3590     {
3591         version (Windows)
3592             rmdir(de.name);
3593         else
3594             remove(de.name);
3595     }
3596     else
3597     {
3598         // all children, recursively depth-first
3599         foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false))
3600         {
3601             attrIsDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name);
3602         }
3603 
3604         // the dir itself
3605         rmdir(de.name);
3606     }
3607 }
3608 ///ditto
3609 //Note, without this overload, passing an RValue DirEntry still works, but
3610 //actually fully reconstructs a DirEntry inside the
3611 //"rmdirRecurse(in char[] pathname)" implementation. That is needlessly
3612 //expensive.
3613 //A DirEntry is a bit big (72B), so keeping the "by ref" signature is desirable.
3614 void rmdirRecurse(DirEntry de)
3615 {
3616     rmdirRecurse(de);
3617 }
3618 
3619 version (Windows) @system unittest
3620 {
3621     import std.exception : enforce;
3622     auto d = deleteme ~ r".dir\a\b\c\d\e\f\g";
3623     mkdirRecurse(d);
3624     rmdirRecurse(deleteme ~ ".dir");
3625     enforce(!exists(deleteme ~ ".dir"));
3626 }
3627 
3628 version (Posix) @system unittest
3629 {
3630     import std.exception : enforce, collectException;
3631     import std.process : executeShell;
3632     collectException(rmdirRecurse(deleteme));
3633     auto d = deleteme~"/a/b/c/d/e/f/g";
3634     enforce(collectException(mkdir(d)));
3635     mkdirRecurse(d);
3636     core.sys.posix.unistd.symlink((deleteme~"/a/b/c\0").ptr,
3637             (deleteme~"/link\0").ptr);
3638     rmdirRecurse(deleteme~"/link");
3639     enforce(exists(d));
3640     rmdirRecurse(deleteme);
3641     enforce(!exists(deleteme));
3642 
3643     d = deleteme~"/a/b/c/d/e/f/g";
3644     mkdirRecurse(d);
3645     version (Android) string link_cmd = "ln -s ";
3646     else string link_cmd = "ln -sf ";
3647     executeShell(link_cmd~deleteme~"/a/b/c "~deleteme~"/link");
3648     rmdirRecurse(deleteme);
3649     enforce(!exists(deleteme));
3650 }
3651 
3652 @system unittest
3653 {
3654     void[] buf;
3655 
3656     buf = new void[10];
3657     (cast(byte[]) buf)[] = 3;
3658     string unit_file = deleteme ~ "-unittest_write.tmp";
3659     if (exists(unit_file)) remove(unit_file);
3660     write(unit_file, buf);
3661     void[] buf2 = read(unit_file);
3662     assert(buf == buf2);
3663 
3664     string unit2_file = deleteme ~ "-unittest_write2.tmp";
3665     copy(unit_file, unit2_file);
3666     buf2 = read(unit2_file);
3667     assert(buf == buf2);
3668 
3669     remove(unit_file);
3670     assert(!exists(unit_file));
3671     remove(unit2_file);
3672     assert(!exists(unit2_file));
3673 }
3674 
3675 /**
3676  * Dictates directory spanning policy for $(D_PARAM dirEntries) (see below).
3677  */
3678 enum SpanMode
3679 {
3680     /** Only spans one directory. */
3681     shallow,
3682     /** Spans the directory in
3683      $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Post-order,
3684      _depth-first $(B post)-order), i.e. the content of any
3685      subdirectory is spanned before that subdirectory itself. Useful
3686      e.g. when recursively deleting files.  */
3687     depth,
3688     /** Spans the directory in
3689     $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Pre-order, depth-first
3690     $(B pre)-order), i.e. the content of any subdirectory is spanned
3691     right after that subdirectory itself.
3692 
3693     Note that $(D SpanMode.breadth) will not result in all directory
3694     members occurring before any subdirectory members, i.e. it is not
3695     _true
3696     $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search,
3697     _breadth-first traversal).
3698     */
3699     breadth,
3700 }
3701 
3702 private struct DirIteratorImpl
3703 {
3704     import std.array : Appender, appender;
3705     SpanMode _mode;
3706     // Whether we should follow symlinked directories while iterating.
3707     // It also indicates whether we should avoid functions which call
3708     // stat (since we should only need lstat in this case and it would
3709     // be more efficient to not call stat in addition to lstat).
3710     bool _followSymlink;
3711     DirEntry _cur;
3712     Appender!(DirHandle[]) _stack;
3713     Appender!(DirEntry[]) _stashed; //used in depth first mode
3714     //stack helpers
3715     void pushExtra(DirEntry de){ _stashed.put(de); }
3716     //ditto
3717     bool hasExtra(){ return !_stashed.data.empty; }
3718     //ditto
3719     DirEntry popExtra()
3720     {
3721         DirEntry de;
3722         de = _stashed.data[$-1];
3723         _stashed.shrinkTo(_stashed.data.length - 1);
3724         return de;
3725 
3726     }
3727     version (Windows)
3728     {
3729         struct DirHandle
3730         {
3731             string dirpath;
3732             HANDLE h;
3733         }
3734 
3735         bool stepIn(string directory)
3736         {
3737             import std.path : chainPath;
3738 
3739             auto search_pattern = chainPath(directory, "*.*");
3740             WIN32_FIND_DATAW findinfo;
3741             HANDLE h = FindFirstFileW(search_pattern.tempCString!FSChar(), &findinfo);
3742             cenforce(h != INVALID_HANDLE_VALUE, directory);
3743             _stack.put(DirHandle(directory, h));
3744             return toNext(false, &findinfo);
3745         }
3746 
3747         bool next()
3748         {
3749             if (_stack.data.empty)
3750                 return false;
3751             WIN32_FIND_DATAW findinfo;
3752             return toNext(true, &findinfo);
3753         }
3754 
3755         bool toNext(bool fetch, WIN32_FIND_DATAW* findinfo)
3756         {
3757             import core.stdc.wchar_ : wcscmp;
3758 
3759             if (fetch)
3760             {
3761                 if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE)
3762                 {
3763                     popDirStack();
3764                     return false;
3765                 }
3766             }
3767             while ( wcscmp(findinfo.cFileName.ptr, ".") == 0
3768                     || wcscmp(findinfo.cFileName.ptr, "..") == 0)
3769                 if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE)
3770                 {
3771                     popDirStack();
3772                     return false;
3773                 }
3774             _cur = DirEntry(_stack.data[$-1].dirpath, findinfo);
3775             return true;
3776         }
3777 
3778         void popDirStack()
3779         {
3780             assert(!_stack.data.empty);
3781             FindClose(_stack.data[$-1].h);
3782             _stack.shrinkTo(_stack.data.length-1);
3783         }
3784 
3785         void releaseDirStack()
3786         {
3787             foreach ( d;  _stack.data)
3788                 FindClose(d.h);
3789         }
3790 
3791         bool mayStepIn()
3792         {
3793             return _followSymlink ? _cur.isDir : _cur.isDir && !_cur.isSymlink;
3794         }
3795     }
3796     else version (Posix)
3797     {
3798         struct DirHandle
3799         {
3800             string dirpath;
3801             DIR*   h;
3802         }
3803 
3804         bool stepIn(string directory)
3805         {
3806             auto h = directory.length ? opendir(directory.tempCString()) : opendir(".");
3807             cenforce(h, directory);
3808             _stack.put(DirHandle(directory, h));
3809             return next();
3810         }
3811 
3812         bool next()
3813         {
3814             if (_stack.data.empty)
3815                 return false;
3816             for (dirent* fdata; (fdata = readdir(_stack.data[$-1].h)) != null; )
3817             {
3818                 // Skip "." and ".."
3819                 if (core.stdc.string.strcmp(fdata.d_name.ptr, ".")  &&
3820                    core.stdc.string.strcmp(fdata.d_name.ptr, "..") )
3821                 {
3822                     _cur = DirEntry(_stack.data[$-1].dirpath, fdata);
3823                     return true;
3824                 }
3825             }
3826             popDirStack();
3827             return false;
3828         }
3829 
3830         void popDirStack()
3831         {
3832             assert(!_stack.data.empty);
3833             closedir(_stack.data[$-1].h);
3834             _stack.shrinkTo(_stack.data.length-1);
3835         }
3836 
3837         void releaseDirStack()
3838         {
3839             foreach ( d;  _stack.data)
3840                 closedir(d.h);
3841         }
3842 
3843         bool mayStepIn()
3844         {
3845             return _followSymlink ? _cur.isDir : attrIsDir(_cur.linkAttributes);
3846         }
3847     }
3848 
3849     this(R)(R pathname, SpanMode mode, bool followSymlink)
3850         if (isInputRange!R && isSomeChar!(ElementEncodingType!R))
3851     {
3852         _mode = mode;
3853         _followSymlink = followSymlink;
3854         _stack = appender(cast(DirHandle[])[]);
3855         if (_mode == SpanMode.depth)
3856             _stashed = appender(cast(DirEntry[])[]);
3857 
3858         static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
3859             alias pathnameStr = pathname;
3860         else
3861         {
3862             import std.array : array;
3863             string pathnameStr = pathname.array;
3864         }
3865         if (stepIn(pathnameStr))
3866         {
3867             if (_mode == SpanMode.depth)
3868                 while (mayStepIn())
3869                 {
3870                     auto thisDir = _cur;
3871                     if (stepIn(_cur.name))
3872                     {
3873                         pushExtra(thisDir);
3874                     }
3875                     else
3876                         break;
3877                 }
3878         }
3879     }
3880     @property bool empty(){ return _stashed.data.empty && _stack.data.empty; }
3881     @property DirEntry front(){ return _cur; }
3882     void popFront()
3883     {
3884         switch (_mode)
3885         {
3886         case SpanMode.depth:
3887             if (next())
3888             {
3889                 while (mayStepIn())
3890                 {
3891                     auto thisDir = _cur;
3892                     if (stepIn(_cur.name))
3893                     {
3894                         pushExtra(thisDir);
3895                     }
3896                     else
3897                         break;
3898                 }
3899             }
3900             else if (hasExtra())
3901                 _cur = popExtra();
3902             break;
3903         case SpanMode.breadth:
3904             if (mayStepIn())
3905             {
3906                 if (!stepIn(_cur.name))
3907                     while (!empty && !next()){}
3908             }
3909             else
3910                 while (!empty && !next()){}
3911             break;
3912         default:
3913             next();
3914         }
3915     }
3916 
3917     ~this()
3918     {
3919         releaseDirStack();
3920     }
3921 }
3922 
3923 struct DirIterator
3924 {
3925 private:
3926     RefCounted!(DirIteratorImpl, RefCountedAutoInitialize.no) impl;
3927     this(string pathname, SpanMode mode, bool followSymlink)
3928     {
3929         impl = typeof(impl)(pathname, mode, followSymlink);
3930     }
3931 public:
3932     @property bool empty(){ return impl.empty; }
3933     @property DirEntry front(){ return impl.front; }
3934     void popFront(){ impl.popFront(); }
3935 
3936 }
3937 /++
3938     Returns an input range of $(D DirEntry) that lazily iterates a given directory,
3939     also provides two ways of foreach iteration. The iteration variable can be of
3940     type $(D string) if only the name is needed, or $(D DirEntry)
3941     if additional details are needed. The span _mode dictates how the
3942     directory is traversed. The name of each iterated directory entry
3943     contains the absolute _path.
3944 
3945     Params:
3946         path = The directory to iterate over.
3947                If empty, the current directory will be iterated.
3948 
3949         pattern = Optional string with wildcards, such as $(RED
3950                   "*.d"). When present, it is used to filter the
3951                   results by their file name. The supported wildcard
3952                   strings are described under $(REF globMatch,
3953                   std,_path).
3954 
3955         mode = Whether the directory's sub-directories should be
3956                iterated in depth-first port-order ($(LREF depth)),
3957                depth-first pre-order ($(LREF breadth)), or not at all
3958                ($(LREF shallow)).
3959 
3960         followSymlink = Whether symbolic links which point to directories
3961                          should be treated as directories and their contents
3962                          iterated over.
3963 
3964     Throws:
3965         $(D FileException) if the directory does not exist.
3966 
3967 Example:
3968 --------------------
3969 // Iterate a directory in depth
3970 foreach (string name; dirEntries("destroy/me", SpanMode.depth))
3971 {
3972     remove(name);
3973 }
3974 
3975 // Iterate the current directory in breadth
3976 foreach (string name; dirEntries("", SpanMode.breadth))
3977 {
3978     writeln(name);
3979 }
3980 
3981 // Iterate a directory and get detailed info about it
3982 foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth))
3983 {
3984     writeln(e.name, "\t", e.size);
3985 }
3986 
3987 // Iterate over all *.d files in current directory and all its subdirectories
3988 auto dFiles = dirEntries("", SpanMode.depth).filter!(f => f.name.endsWith(".d"));
3989 foreach (d; dFiles)
3990     writeln(d.name);
3991 
3992 // Hook it up with std.parallelism to compile them all in parallel:
3993 foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread
3994 {
3995     string cmd = "dmd -c "  ~ d.name;
3996     writeln(cmd);
3997     std.process.system(cmd);
3998 }
3999 
4000 // Iterate over all D source files in current directory and all its
4001 // subdirectories
4002 auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
4003 foreach (d; dFiles)
4004     writeln(d.name);
4005 --------------------
4006  +/
4007 auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
4008 {
4009     return DirIterator(path, mode, followSymlink);
4010 }
4011 
4012 /// Duplicate functionality of D1's $(D std.file.listdir()):
4013 @safe unittest
4014 {
4015     string[] listdir(string pathname)
4016     {
4017         import std.algorithm;
4018         import std.array;
4019         import std.file;
4020         import std.path;
4021 
4022         return std.file.dirEntries(pathname, SpanMode.shallow)
4023             .filter!(a => a.isFile)
4024             .map!(a => std.path.baseName(a.name))
4025             .array;
4026     }
4027 
4028     void main(string[] args)
4029     {
4030         import std.stdio;
4031 
4032         string[] files = listdir(args[1]);
4033         writefln("%s", files);
4034      }
4035 }
4036 
4037 @system unittest
4038 {
4039     import std.algorithm.comparison : equal;
4040     import std.algorithm.iteration : map;
4041     import std.algorithm.searching : startsWith;
4042     import std.array : array;
4043     import std.conv : to;
4044     import std.path : dirEntries, buildPath, absolutePath;
4045     import std.process : thisProcessID;
4046     import std.range.primitives : walkLength;
4047 
4048     version (Android)
4049         string testdir = deleteme; // This has to be an absolute path when
4050                                    // called from a shared library on Android,
4051                                    // ie an apk
4052     else
4053         string testdir = "deleteme.dmd.unittest.std.file" ~ to!string(thisProcessID); // needs to be relative
4054     mkdirRecurse(buildPath(testdir, "somedir"));
4055     scope(exit) rmdirRecurse(testdir);
4056     write(buildPath(testdir, "somefile"), null);
4057     write(buildPath(testdir, "somedir", "somedeepfile"), null);
4058 
4059     // testing range interface
4060     size_t equalEntries(string relpath, SpanMode mode)
4061     {
4062         import std.exception : enforce;
4063         auto len = enforce(walkLength(dirEntries(absolutePath(relpath), mode)));
4064         assert(walkLength(dirEntries(relpath, mode)) == len);
4065         assert(equal(
4066                    map!(a => absolutePath(a.name))(dirEntries(relpath, mode)),
4067                    map!(a => a.name)(dirEntries(absolutePath(relpath), mode))));
4068         return len;
4069     }
4070 
4071     assert(equalEntries(testdir, SpanMode.shallow) == 2);
4072     assert(equalEntries(testdir, SpanMode.depth) == 3);
4073     assert(equalEntries(testdir, SpanMode.breadth) == 3);
4074 
4075     // testing opApply
4076     foreach (string name; dirEntries(testdir, SpanMode.breadth))
4077     {
4078         //writeln(name);
4079         assert(name.startsWith(testdir));
4080     }
4081     foreach (DirEntry e; dirEntries(absolutePath(testdir), SpanMode.breadth))
4082     {
4083         //writeln(name);
4084         assert(e.isFile || e.isDir, e.name);
4085     }
4086 
4087     //issue 7264
4088     foreach (string name; dirEntries(testdir, "*.d", SpanMode.breadth))
4089     {
4090 
4091     }
4092     foreach (entry; dirEntries(testdir, SpanMode.breadth))
4093     {
4094         static assert(is(typeof(entry) == DirEntry));
4095     }
4096     //issue 7138
4097     auto a = array(dirEntries(testdir, SpanMode.shallow));
4098 
4099     // issue 11392
4100     auto dFiles = dirEntries(testdir, SpanMode.shallow);
4101     foreach (d; dFiles){}
4102 
4103     // issue 15146
4104     dirEntries("", SpanMode.shallow).walkLength();
4105 }
4106 
4107 /// Ditto
4108 auto dirEntries(string path, string pattern, SpanMode mode,
4109     bool followSymlink = true)
4110 {
4111     import std.algorithm.iteration : filter;
4112     import std.path : globMatch, baseName;
4113 
4114     bool f(DirEntry de) { return globMatch(baseName(de.name), pattern); }
4115     return filter!f(DirIterator(path, mode, followSymlink));
4116 }
4117 
4118 @system unittest
4119 {
4120     import std.stdio : writefln;
4121     immutable dpath = deleteme ~ "_dir";
4122     immutable fpath = deleteme ~ "_file";
4123     immutable sdpath = deleteme ~ "_sdir";
4124     immutable sfpath = deleteme ~ "_sfile";
4125     scope(exit)
4126     {
4127         if (dpath.exists) rmdirRecurse(dpath);
4128         if (fpath.exists) remove(fpath);
4129         if (sdpath.exists) remove(sdpath);
4130         if (sfpath.exists) remove(sfpath);
4131     }
4132 
4133     mkdir(dpath);
4134     write(fpath, "hello world");
4135     version (Posix)
4136     {
4137         core.sys.posix.unistd.symlink((dpath ~ '\0').ptr, (sdpath ~ '\0').ptr);
4138         core.sys.posix.unistd.symlink((fpath ~ '\0').ptr, (sfpath ~ '\0').ptr);
4139     }
4140 
4141     static struct Flags { bool dir, file, link; }
4142     auto tests = [dpath : Flags(true), fpath : Flags(false, true)];
4143     version (Posix)
4144     {
4145         tests[sdpath] = Flags(true, false, true);
4146         tests[sfpath] = Flags(false, true, true);
4147     }
4148 
4149     auto past = Clock.currTime() - 2.seconds;
4150     auto future = past + 4.seconds;
4151 
4152     foreach (path, flags; tests)
4153     {
4154         auto de = DirEntry(path);
4155         assert(de.name == path);
4156         assert(de.isDir == flags.dir);
4157         assert(de.isFile == flags.file);
4158         assert(de.isSymlink == flags.link);
4159 
4160         assert(de.isDir == path.isDir);
4161         assert(de.isFile == path.isFile);
4162         assert(de.isSymlink == path.isSymlink);
4163         assert(de.size == path.getSize());
4164         assert(de.attributes == getAttributes(path));
4165         assert(de.linkAttributes == getLinkAttributes(path));
4166 
4167         scope(failure) writefln("[%s] [%s] [%s] [%s]", past, de.timeLastAccessed, de.timeLastModified, future);
4168         assert(de.timeLastAccessed > past);
4169         assert(de.timeLastAccessed < future);
4170         assert(de.timeLastModified > past);
4171         assert(de.timeLastModified < future);
4172 
4173         assert(attrIsDir(de.attributes) == flags.dir);
4174         assert(attrIsDir(de.linkAttributes) == (flags.dir && !flags.link));
4175         assert(attrIsFile(de.attributes) == flags.file);
4176         assert(attrIsFile(de.linkAttributes) == (flags.file && !flags.link));
4177         assert(!attrIsSymlink(de.attributes));
4178         assert(attrIsSymlink(de.linkAttributes) == flags.link);
4179 
4180         version (Windows)
4181         {
4182             assert(de.timeCreated > past);
4183             assert(de.timeCreated < future);
4184         }
4185         else version (Posix)
4186         {
4187             assert(de.timeStatusChanged > past);
4188             assert(de.timeStatusChanged < future);
4189             assert(de.attributes == de.statBuf.st_mode);
4190         }
4191     }
4192 }
4193 
4194 
4195 /**
4196  * Reads a file line by line and parses the line into a single value or a
4197  * $(REF Tuple, std,typecons) of values depending on the length of `Types`.
4198  * The lines are parsed using the specified format string. The format string is
4199  * passed to $(REF formattedRead, std,_format), and therefore must conform to the
4200  * _format string specification outlined in $(MREF std, _format).
4201  *
4202  * Params:
4203  *     Types = the types that each of the elements in the line should be returned as
4204  *     filename = the name of the file to read
4205  *     format = the _format string to use when reading
4206  *
4207  * Returns:
4208  *     If only one type is passed, then an array of that type. Otherwise, an
4209  *     array of $(REF Tuple, std,typecons)s.
4210  *
4211  * Throws:
4212  *     `Exception` if the format string is malformed. Also, throws `Exception`
4213  *     if any of the lines in the file are not fully consumed by the call
4214  *     to $(REF formattedRead, std,_format). Meaning that no empty lines or lines
4215  *     with extra characters are allowed.
4216  */
4217 Select!(Types.length == 1, Types[0][], Tuple!(Types)[])
4218 slurp(Types...)(string filename, in char[] format)
4219 {
4220     import std.array : appender;
4221     import std.conv : text;
4222     import std.exception : enforce;
4223     import std.format : formattedRead;
4224     import std.stdio : File;
4225 
4226     auto app = appender!(typeof(return))();
4227     ElementType!(typeof(return)) toAdd;
4228     auto f = File(filename);
4229     scope(exit) f.close();
4230     foreach (line; f.byLine())
4231     {
4232         formattedRead(line, format, &toAdd);
4233         enforce(line.empty,
4234                 text("Trailing characters at the end of line: `", line,
4235                         "'"));
4236         app.put(toAdd);
4237     }
4238     return app.data;
4239 }
4240 
4241 ///
4242 @system unittest
4243 {
4244     import std.typecons : tuple;
4245 
4246     scope(exit)
4247     {
4248         assert(exists(deleteme));
4249         remove(deleteme);
4250     }
4251 
4252     write(deleteme, "12 12.25\n345 1.125"); // deleteme is the name of a temporary file
4253 
4254     // Load file; each line is an int followed by comma, whitespace and a
4255     // double.
4256     auto a = slurp!(int, double)(deleteme, "%s %s");
4257     assert(a.length == 2);
4258     assert(a[0] == tuple(12, 12.25));
4259     assert(a[1] == tuple(345, 1.125));
4260 }
4261 
4262 
4263 /**
4264 Returns the path to a directory for temporary files.
4265 
4266 On Windows, this function returns the result of calling the Windows API function
4267 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992.aspx, $(D GetTempPath)).
4268 
4269 On POSIX platforms, it searches through the following list of directories
4270 and returns the first one which is found to exist:
4271 $(OL
4272     $(LI The directory given by the $(D TMPDIR) environment variable.)
4273     $(LI The directory given by the $(D TEMP) environment variable.)
4274     $(LI The directory given by the $(D TMP) environment variable.)
4275     $(LI $(D /tmp))
4276     $(LI $(D /var/tmp))
4277     $(LI $(D /usr/tmp))
4278 )
4279 
4280 On all platforms, $(D tempDir) returns $(D ".") on failure, representing
4281 the current working directory.
4282 
4283 The return value of the function is cached, so the procedures described
4284 above will only be performed the first time the function is called.  All
4285 subsequent runs will return the same string, regardless of whether
4286 environment variables and directory structures have changed in the
4287 meantime.
4288 
4289 The POSIX $(D tempDir) algorithm is inspired by Python's
4290 $(LINK2 http://docs.python.org/library/tempfile.html#tempfile.tempdir, $(D tempfile.tempdir)).
4291 */
4292 string tempDir() @trusted
4293 {
4294     static string cache;
4295     if (cache is null)
4296     {
4297         version (Windows)
4298         {
4299             import std.conv : to;
4300             // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992(v=vs.85).aspx
4301             wchar[MAX_PATH + 2] buf;
4302             DWORD len = GetTempPathW(buf.length, buf.ptr);
4303             if (len) cache = buf[0 .. len].to!string;
4304         }
4305         else version (Posix)
4306         {
4307             import std.process : environment;
4308             // This function looks through the list of alternative directories
4309             // and returns the first one which exists and is a directory.
4310             static string findExistingDir(T...)(lazy T alternatives)
4311             {
4312                 foreach (dir; alternatives)
4313                     if (!dir.empty && exists(dir)) return dir;
4314                 return null;
4315             }
4316 
4317             cache = findExistingDir(environment.get("TMPDIR"),
4318                                     environment.get("TEMP"),
4319                                     environment.get("TMP"),
4320                                     "/tmp",
4321                                     "/var/tmp",
4322                                     "/usr/tmp");
4323         }
4324         else static assert(false, "Unsupported platform");
4325 
4326         if (cache is null) cache = getcwd();
4327     }
4328     return cache;
4329 }
4330