The OpenD Programming Language

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 $(DIVC quickindex,
11 $(BOOKTABLE,
12 $(TR $(TH Category) $(TH Functions))
13 $(TR $(TD General) $(TD
14           $(LREF exists)
15           $(LREF isDir)
16           $(LREF isFile)
17           $(LREF isSymlink)
18           $(LREF rename)
19           $(LREF thisExePath)
20 ))
21 $(TR $(TD Directories) $(TD
22           $(LREF chdir)
23           $(LREF dirEntries)
24           $(LREF getcwd)
25           $(LREF mkdir)
26           $(LREF mkdirRecurse)
27           $(LREF rmdir)
28           $(LREF rmdirRecurse)
29           $(LREF tempDir)
30 ))
31 $(TR $(TD Files) $(TD
32           $(LREF append)
33           $(LREF copy)
34           $(LREF read)
35           $(LREF readText)
36           $(LREF remove)
37           $(LREF slurp)
38           $(LREF write)
39 ))
40 $(TR $(TD Symlinks) $(TD
41           $(LREF symlink)
42           $(LREF readLink)
43 ))
44 $(TR $(TD Attributes) $(TD
45           $(LREF attrIsDir)
46           $(LREF attrIsFile)
47           $(LREF attrIsSymlink)
48           $(LREF getAttributes)
49           $(LREF getLinkAttributes)
50           $(LREF getSize)
51           $(LREF setAttributes)
52 ))
53 $(TR $(TD Timestamp) $(TD
54           $(LREF getTimes)
55           $(LREF getTimesWin)
56           $(LREF setTimes)
57           $(LREF timeLastModified)
58           $(LREF timeLastAccessed)
59           $(LREF timeStatusChanged)
60 ))
61 $(TR $(TD Other) $(TD
62           $(LREF DirEntry)
63           $(LREF FileException)
64           $(LREF PreserveAttributes)
65           $(LREF SpanMode)
66           $(LREF getAvailableDiskSpace)
67 ))
68 ))
69 
70 
71 Copyright: Copyright The D Language Foundation 2007 - 2011.
72 See_Also:  The $(HTTP ddili.org/ders/d.en/files.html, official tutorial) for an
73 introduction to working with files in D, module
74 $(MREF std, stdio) for opening files and manipulating them via handles,
75 and module $(MREF std, path) for manipulating path strings.
76 
77 License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
78 Authors:   $(HTTP digitalmars.com, Walter Bright),
79            $(HTTP erdani.org, Andrei Alexandrescu),
80            $(HTTP jmdavisprog.com, Jonathan M Davis)
81 Source:    $(PHOBOSSRC std/file.d)
82  */
83 module std.file;
84 
85 import core.stdc.errno, core.stdc.stdlib, core.stdc.string;
86 import core.time : abs, dur, hnsecs, seconds;
87 
88 import std.datetime.date : DateTime;
89 import std.datetime.systime : Clock, SysTime, unixTimeToStdTime;
90 import std.internal.cstring;
91 import std.meta;
92 import std.range;
93 import std.traits;
94 import std.typecons;
95 
96 version (OSX)
97     version = Darwin;
98 else version (iOS)
99     version = Darwin;
100 else version (TVOS)
101     version = Darwin;
102 else version (WatchOS)
103     version = Darwin;
104 
105 version (Windows)
106 {
107     import core.sys.windows.winbase, core.sys.windows.winnt, std.windows.syserror;
108 }
109 else version (Posix)
110 {
111     import core.sys.posix.dirent, core.sys.posix.fcntl, core.sys.posix.sys.stat,
112         core.sys.posix.sys.time, core.sys.posix.unistd, core.sys.posix.utime;
113 }
114 else
115     static assert(false, "Module " ~ .stringof ~ " not implemented for this OS.");
116 
117 // Character type used for operating system filesystem APIs
118 version (Windows)
119 {
120     private alias FSChar = WCHAR;       // WCHAR can be aliased to wchar or wchar_t
121 }
122 else version (Posix)
123 {
124     private alias FSChar = char;
125 }
126 else
127     static assert(0);
128 
129 // Purposefully not documented. Use at your own risk
130 version(WebAssembly) {} else
131 @property string deleteme() @safe
132 {
133     import std.conv : text;
134     import std.path : buildPath;
135     import std.process : thisProcessID;
136 
137     enum base = "deleteme.dmd.unittest.pid";
138     static string fileName;
139 
140     if (!fileName)
141         fileName = text(buildPath(tempDir(), base), thisProcessID);
142     return fileName;
143 }
144 
145 version (StdUnittest) private struct TestAliasedString
146 {
147     string get() @safe @nogc pure nothrow return scope { return _s; }
148     alias get this;
149     @disable this(this);
150     string _s;
151 }
152 
153 version (Android)
154 {
155     package enum system_directory = "/system/etc";
156     package enum system_file      = "/system/etc/hosts";
157 }
158 else version (Posix)
159 {
160     package enum system_directory = "/usr/include";
161     package enum system_file      = "/usr/include/assert.h";
162 }
163 
164 
165 /++
166     Exception thrown for file I/O errors.
167  +/
168 class FileException : Exception
169 {
170     import std.conv : text, to;
171 
172     /++
173         OS error code.
174      +/
175     immutable uint errno;
176 
177     private this(scope const(char)[] name, scope const(char)[] msg, string file, size_t line, uint errno) @safe pure
178     {
179         if (msg.empty)
180             super(name is null ? "(null)" : name.idup, file, line);
181         else
182             super(text(name is null ? "(null)" : name, ": ", msg), file, line);
183 
184         this.errno = errno;
185     }
186 
187     /++
188         Constructor which takes an error message.
189 
190         Params:
191             name = Name of file for which the error occurred.
192             msg  = Message describing the error.
193             file = The file where the error occurred.
194             line = The _line where the error occurred.
195      +/
196     this(scope const(char)[] name, scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__) @safe pure
197     {
198         this(name, msg, file, line, 0);
199     }
200 
201     /++
202         Constructor which takes the error number ($(LUCKY GetLastError)
203         in Windows, $(D_PARAM errno) in POSIX).
204 
205         Params:
206             name  = Name of file for which the error occurred.
207             errno = The error number.
208             file  = The file where the error occurred.
209                     Defaults to `__FILE__`.
210             line  = The _line where the error occurred.
211                     Defaults to `__LINE__`.
212      +/
213     version (Windows) this(scope const(char)[] name,
214                           uint errno = .GetLastError(),
215                           string file = __FILE__,
216                           size_t line = __LINE__) @safe
217     {
218         this(name, generateSysErrorMsg(errno), file, line, errno);
219     }
220     else version (Posix) this(scope const(char)[] name,
221                              uint errno = .errno,
222                              string file = __FILE__,
223                              size_t line = __LINE__) @trusted
224     {
225         import std.exception : errnoString;
226         this(name, errnoString(errno), file, line, errno);
227     }
228 }
229 
230 ///
231 @safe unittest
232 {
233     import std.exception : assertThrown;
234 
235     assertThrown!FileException("non.existing.file.".readText);
236 }
237 
238 private T cenforce(T)(T condition, lazy scope const(char)[] name, string file = __FILE__, size_t line = __LINE__)
239 {
240     if (condition)
241         return condition;
242     version (Windows)
243     {
244         throw new FileException(name, .GetLastError(), file, line);
245     }
246     else version (Posix)
247     {
248         throw new FileException(name, .errno, file, line);
249     }
250 }
251 
252 version (Windows)
253 @trusted
254 private T cenforce(T)(T condition, scope const(char)[] name, scope const(FSChar)* namez,
255     string file = __FILE__, size_t line = __LINE__)
256 {
257     if (condition)
258         return condition;
259     if (!name)
260     {
261         import core.stdc.wchar_ : wcslen;
262         import std.conv : to;
263 
264         auto len = namez ? wcslen(namez) : 0;
265         name = to!string(namez[0 .. len]);
266     }
267     throw new FileException(name, .GetLastError(), file, line);
268 }
269 
270 version (Posix)
271 @trusted
272 private T cenforce(T)(T condition, scope const(char)[] name, scope const(FSChar)* namez,
273     string file = __FILE__, size_t line = __LINE__)
274 {
275     if (condition)
276         return condition;
277     if (!name)
278     {
279         import core.stdc.string : strlen;
280 
281         auto len = namez ? strlen(namez) : 0;
282         name = namez[0 .. len].idup;
283     }
284     throw new FileException(name, .errno, file, line);
285 }
286 
287 // https://issues.dlang.org/show_bug.cgi?id=17102
288 @safe unittest
289 {
290     try
291     {
292         cenforce(false, null, null,
293                 __FILE__, __LINE__);
294     }
295     catch (FileException) {}
296 }
297 
298 /* **********************************
299  * Basic File operations.
300  */
301 
302 /********************************************
303 Read entire contents of file `name` and returns it as an untyped
304 array. If the file size is larger than `upTo`, only `upTo`
305 bytes are _read.
306 
307 Params:
308     name = string or range of characters representing the file _name
309     upTo = if present, the maximum number of bytes to _read
310 
311 Returns: Untyped array of bytes _read.
312 
313 Throws: $(LREF FileException) on error.
314 
315 See_Also: $(REF readText, std,file) for reading and validating a text file.
316  */
317 
318 void[] read(R)(R name, size_t upTo = size_t.max)
319 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
320 {
321     static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
322         return readImpl(name, name.tempCString!FSChar(), upTo);
323     else
324         return readImpl(null, name.tempCString!FSChar(), upTo);
325 }
326 
327 ///
328 @safe unittest
329 {
330     import std.utf : byChar;
331     scope(exit)
332     {
333         assert(exists(deleteme));
334         remove(deleteme);
335     }
336 
337     std.file.write(deleteme, "1234"); // deleteme is the name of a temporary file
338     assert(read(deleteme, 2) == "12");
339     assert(read(deleteme.byChar) == "1234");
340     assert((cast(const(ubyte)[])read(deleteme)).length == 4);
341 }
342 
343 /// ditto
344 void[] read(R)(auto ref R name, size_t upTo = size_t.max)
345 if (isConvertibleToString!R)
346 {
347     return read!(StringTypeOf!R)(name, upTo);
348 }
349 
350 @safe unittest
351 {
352     static assert(__traits(compiles, read(TestAliasedString(null))));
353 }
354 
355 version (Posix) private void[] readImpl(scope const(char)[] name, scope const(FSChar)* namez,
356                                         size_t upTo = size_t.max) @trusted
357 {
358     import core.memory : GC;
359     import std.algorithm.comparison : min;
360     import std.conv : to;
361     import std.checkedint : checked;
362 
363     // A few internal configuration parameters {
364     enum size_t
365         minInitialAlloc = 1024 * 4,
366         maxInitialAlloc = size_t.max / 2,
367         sizeIncrement = 1024 * 16,
368         maxSlackMemoryAllowed = 1024;
369     // }
370 
371     immutable fd = core.sys.posix.fcntl.open(namez,
372             core.sys.posix.fcntl.O_RDONLY);
373     cenforce(fd != -1, name);
374     scope(exit) core.sys.posix.unistd.close(fd);
375 
376     stat_t statbuf = void;
377     cenforce(fstat(fd, &statbuf) == 0, name, namez);
378 
379     immutable initialAlloc = min(upTo, to!size_t(statbuf.st_size
380         ? min(statbuf.st_size + 1, maxInitialAlloc)
381         : minInitialAlloc));
382     void[] result = GC.malloc(initialAlloc, GC.BlkAttr.NO_SCAN)[0 .. initialAlloc];
383     scope(failure) GC.free(result.ptr);
384 
385     auto size = checked(size_t(0));
386 
387     for (;;)
388     {
389         immutable actual = core.sys.posix.unistd.read(fd, result.ptr + size.get,
390                 (min(result.length, upTo) - size).get);
391         cenforce(actual != -1, name, namez);
392         if (actual == 0) break;
393         size += actual;
394         if (size >= upTo) break;
395         if (size < result.length) continue;
396         immutable newAlloc = size + sizeIncrement;
397         result = GC.realloc(result.ptr, newAlloc.get, GC.BlkAttr.NO_SCAN)[0 .. newAlloc.get];
398     }
399 
400     return result.length - size >= maxSlackMemoryAllowed
401         ? GC.realloc(result.ptr, size.get, GC.BlkAttr.NO_SCAN)[0 .. size.get]
402         : result[0 .. size.get];
403 }
404 
405 version (Windows)
406 private extern (Windows) @nogc nothrow
407 {
408     pragma(mangle, CreateFileW.mangleof)
409     HANDLE trustedCreateFileW(scope const(wchar)* namez, DWORD dwDesiredAccess,
410         DWORD dwShareMode, SECURITY_ATTRIBUTES* lpSecurityAttributes,
411         DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
412         HANDLE hTemplateFile)  @trusted;
413 
414     pragma(mangle, CloseHandle.mangleof) BOOL trustedCloseHandle(HANDLE) @trusted;
415 }
416 
417 version (Windows) private void[] readImpl(scope const(char)[] name, scope const(FSChar)* namez,
418                                           size_t upTo = size_t.max) @trusted
419 {
420     import core.memory : GC;
421     import std.algorithm.comparison : min;
422     static trustedGetFileSize(HANDLE hFile, out ulong fileSize)
423     {
424         DWORD sizeHigh;
425         DWORD sizeLow = GetFileSize(hFile, &sizeHigh);
426         const bool result = sizeLow != INVALID_FILE_SIZE;
427         if (result)
428             fileSize = makeUlong(sizeLow, sizeHigh);
429         return result;
430     }
431     static trustedReadFile(HANDLE hFile, void *lpBuffer, size_t nNumberOfBytesToRead)
432     {
433         // Read by chunks of size < 4GB (Windows API limit)
434         size_t totalNumRead = 0;
435         while (totalNumRead != nNumberOfBytesToRead)
436         {
437             const uint chunkSize = min(nNumberOfBytesToRead - totalNumRead, 0xffff_0000);
438             DWORD numRead = void;
439             const result = ReadFile(hFile, lpBuffer + totalNumRead, chunkSize, &numRead, null);
440             if (result == 0 || numRead != chunkSize)
441                 return false;
442             totalNumRead += chunkSize;
443         }
444         return true;
445     }
446 
447     alias defaults =
448         AliasSeq!(GENERIC_READ,
449             FILE_SHARE_READ | FILE_SHARE_WRITE, (SECURITY_ATTRIBUTES*).init,
450             OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
451             HANDLE.init);
452     auto h = trustedCreateFileW(namez, defaults);
453 
454     cenforce(h != INVALID_HANDLE_VALUE, name, namez);
455     scope(exit) cenforce(trustedCloseHandle(h), name, namez);
456     ulong fileSize = void;
457     cenforce(trustedGetFileSize(h, fileSize), name, namez);
458     size_t size = min(upTo, fileSize);
459     auto buf = () { return GC.malloc(size, GC.BlkAttr.NO_SCAN)[0 .. size]; } ();
460 
461     scope(failure)
462     {
463         () { GC.free(buf.ptr); } ();
464     }
465 
466     if (size)
467         cenforce(trustedReadFile(h, &buf[0], size), name, namez);
468     return buf[0 .. size];
469 }
470 
471 version (linux) @safe unittest
472 {
473     // A file with "zero" length that doesn't have 0 length at all
474     auto s = std.file.readText("/proc/cpuinfo");
475     assert(s.length > 0);
476     //writefln("'%s'", s);
477 }
478 
479 @safe unittest
480 {
481     scope(exit) if (exists(deleteme)) remove(deleteme);
482     import std.stdio;
483     auto f = File(deleteme, "w");
484     f.write("abcd"); f.flush();
485     assert(read(deleteme) == "abcd");
486 }
487 
488 /++
489     Reads and validates (using $(REF validate, std, utf)) a text file. S can be
490     an array of any character type. However, no width or endian conversions are
491     performed. So, if the width or endianness of the characters in the given
492     file differ from the width or endianness of the element type of S, then
493     validation will fail.
494 
495     Params:
496         S = the string type of the file
497         name = string or range of characters representing the file _name
498 
499     Returns: Array of characters read.
500 
501     Throws: $(LREF FileException) if there is an error reading the file,
502             $(REF UTFException, std, utf) on UTF decoding error.
503 
504     See_Also: $(REF read, std,file) for reading a binary file.
505 +/
506 S readText(S = string, R)(auto ref R name)
507 if (isSomeString!S && (isSomeFiniteCharInputRange!R || is(StringTypeOf!R)))
508 {
509     import std.algorithm.searching : startsWith;
510     import std.encoding : getBOM, BOM;
511     import std.exception : enforce;
512     import std.format : format;
513     import std.utf : UTFException, validate;
514 
515     static if (is(StringTypeOf!R))
516         StringTypeOf!R filename = name;
517     else
518         auto filename = name;
519 
520     static auto trustedCast(T)(void[] buf) @trusted { return cast(T) buf; }
521     auto data = trustedCast!(ubyte[])(read(filename));
522 
523     immutable bomSeq = getBOM(data);
524     immutable bom = bomSeq.schema;
525 
526     static if (is(immutable ElementEncodingType!S == immutable char))
527     {
528         with(BOM) switch (bom)
529         {
530             case utf16be:
531             case utf16le: throw new UTFException("UTF-8 requested. BOM is for UTF-16");
532             case utf32be:
533             case utf32le: throw new UTFException("UTF-8 requested. BOM is for UTF-32");
534             default: break;
535         }
536     }
537     else static if (is(immutable ElementEncodingType!S == immutable wchar))
538     {
539         with(BOM) switch (bom)
540         {
541             case utf8: throw new UTFException("UTF-16 requested. BOM is for UTF-8");
542             case utf16be:
543             {
544                 version (BigEndian)
545                     break;
546                 else
547                     throw new UTFException("BOM is for UTF-16 LE on Big Endian machine");
548             }
549             case utf16le:
550             {
551                 version (BigEndian)
552                     throw new UTFException("BOM is for UTF-16 BE on Little Endian machine");
553                 else
554                     break;
555             }
556             case utf32be:
557             case utf32le: throw new UTFException("UTF-8 requested. BOM is for UTF-32");
558             default: break;
559         }
560     }
561     else
562     {
563         with(BOM) switch (bom)
564         {
565             case utf8: throw new UTFException("UTF-16 requested. BOM is for UTF-8");
566             case utf16be:
567             case utf16le: throw new UTFException("UTF-8 requested. BOM is for UTF-16");
568             case utf32be:
569             {
570                 version (BigEndian)
571                     break;
572                 else
573                     throw new UTFException("BOM is for UTF-32 LE on Big Endian machine");
574             }
575             case utf32le:
576             {
577                 version (BigEndian)
578                     throw new UTFException("BOM is for UTF-32 BE on Little Endian machine");
579                 else
580                     break;
581             }
582             default: break;
583         }
584     }
585 
586     if (data.length % ElementEncodingType!S.sizeof != 0)
587         throw new UTFException(format!"The content of %s is not UTF-%s"(filename, ElementEncodingType!S.sizeof * 8));
588 
589     auto result = trustedCast!S(data);
590     validate(result);
591     return result;
592 }
593 
594 /// Read file with UTF-8 text.
595 @safe unittest
596 {
597     write(deleteme, "abc"); // deleteme is the name of a temporary file
598     scope(exit) remove(deleteme);
599     string content = readText(deleteme);
600     assert(content == "abc");
601 }
602 
603 // Read file with UTF-8 text but try to read it as UTF-16.
604 @safe unittest
605 {
606     import std.exception : assertThrown;
607     import std.utf : UTFException;
608 
609     write(deleteme, "abc");
610     scope(exit) remove(deleteme);
611     // Throws because the file is not valid UTF-16.
612     assertThrown!UTFException(readText!wstring(deleteme));
613 }
614 
615 // Read file with UTF-16 text.
616 @safe unittest
617 {
618     import std.algorithm.searching : skipOver;
619 
620     write(deleteme, "\uFEFFabc"w); // With BOM
621     scope(exit) remove(deleteme);
622     auto content = readText!wstring(deleteme);
623     assert(content == "\uFEFFabc"w);
624     // Strips BOM if present.
625     content.skipOver('\uFEFF');
626     assert(content == "abc"w);
627 }
628 
629 @safe unittest
630 {
631     static assert(__traits(compiles, readText(TestAliasedString(null))));
632 }
633 
634 @safe unittest
635 {
636     import std.array : appender;
637     import std.bitmanip : append, Endian;
638     import std.exception : assertThrown;
639     import std.path : buildPath;
640     import std.string : representation;
641     import std.utf : UTFException;
642 
643     mkdir(deleteme);
644     scope(exit) rmdirRecurse(deleteme);
645 
646     immutable none8 = buildPath(deleteme, "none8");
647     immutable none16 = buildPath(deleteme, "none16");
648     immutable utf8 = buildPath(deleteme, "utf8");
649     immutable utf16be = buildPath(deleteme, "utf16be");
650     immutable utf16le = buildPath(deleteme, "utf16le");
651     immutable utf32be = buildPath(deleteme, "utf32be");
652     immutable utf32le = buildPath(deleteme, "utf32le");
653     immutable utf7 = buildPath(deleteme, "utf7");
654 
655     write(none8, "京都市");
656     write(none16, "京都市"w);
657     write(utf8, (cast(char[])[0xEF, 0xBB, 0xBF]) ~ "京都市");
658     {
659         auto str = "\uFEFF京都市"w;
660         auto arr = appender!(ubyte[])();
661         foreach (c; str)
662             arr.append(c);
663         write(utf16be, arr.data);
664     }
665     {
666         auto str = "\uFEFF京都市"w;
667         auto arr = appender!(ubyte[])();
668         foreach (c; str)
669             arr.append!(ushort, Endian.littleEndian)(c);
670         write(utf16le, arr.data);
671     }
672     {
673         auto str = "\U0000FEFF京都市"d;
674         auto arr = appender!(ubyte[])();
675         foreach (c; str)
676             arr.append(c);
677         write(utf32be, arr.data);
678     }
679     {
680         auto str = "\U0000FEFF京都市"d;
681         auto arr = appender!(ubyte[])();
682         foreach (c; str)
683             arr.append!(uint, Endian.littleEndian)(c);
684         write(utf32le, arr.data);
685     }
686     write(utf7, (cast(ubyte[])[0x2B, 0x2F, 0x76, 0x38, 0x2D]) ~ "foobar".representation);
687 
688     assertThrown!UTFException(readText(none16));
689     assert(readText(utf8) == (cast(char[])[0xEF, 0xBB, 0xBF]) ~ "京都市");
690     assertThrown!UTFException(readText(utf16be));
691     assertThrown!UTFException(readText(utf16le));
692     assertThrown!UTFException(readText(utf32be));
693     assertThrown!UTFException(readText(utf32le));
694     assert(readText(utf7) == (cast(char[])[0x2B, 0x2F, 0x76, 0x38, 0x2D]) ~ "foobar");
695 
696     assertThrown!UTFException(readText!wstring(none8));
697     assert(readText!wstring(none16) == "京都市"w);
698     assertThrown!UTFException(readText!wstring(utf8));
699     version (BigEndian)
700     {
701         assert(readText!wstring(utf16be) == "\uFEFF京都市"w);
702         assertThrown!UTFException(readText!wstring(utf16le));
703     }
704     else
705     {
706         assertThrown!UTFException(readText!wstring(utf16be));
707         assert(readText!wstring(utf16le) == "\uFEFF京都市"w);
708     }
709     assertThrown!UTFException(readText!wstring(utf32be));
710     assertThrown!UTFException(readText!wstring(utf32le));
711     assertThrown!UTFException(readText!wstring(utf7));
712 
713     assertThrown!UTFException(readText!dstring(utf8));
714     assertThrown!UTFException(readText!dstring(utf16be));
715     assertThrown!UTFException(readText!dstring(utf16le));
716     version (BigEndian)
717     {
718        assert(readText!dstring(utf32be) == "\U0000FEFF京都市"d);
719        assertThrown!UTFException(readText!dstring(utf32le));
720     }
721     else
722     {
723        assertThrown!UTFException(readText!dstring(utf32be));
724        assert(readText!dstring(utf32le) == "\U0000FEFF京都市"d);
725     }
726     assertThrown!UTFException(readText!dstring(utf7));
727 }
728 
729 /*********************************************
730 Write `buffer` to file `name`.
731 
732 Creates the file if it does not already exist.
733 
734 Params:
735     name = string or range of characters representing the file _name
736     buffer = data to be written to file
737 
738 Throws: $(LREF FileException) on error.
739 
740 See_also: $(REF toFile, std,stdio)
741  */
742 void write(R)(R name, const void[] buffer)
743 if ((isSomeFiniteCharInputRange!R || isSomeString!R) && !isConvertibleToString!R)
744 {
745     static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
746         writeImpl(name, name.tempCString!FSChar(), buffer, false);
747     else
748         writeImpl(null, name.tempCString!FSChar(), buffer, false);
749 }
750 
751 ///
752 @safe unittest
753 {
754    scope(exit)
755    {
756        assert(exists(deleteme));
757        remove(deleteme);
758    }
759 
760    int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
761    write(deleteme, a); // deleteme is the name of a temporary file
762    const bytes = read(deleteme);
763    const fileInts = () @trusted { return cast(int[]) bytes; }();
764    assert(fileInts == a);
765 }
766 
767 /// ditto
768 void write(R)(auto ref R name, const void[] buffer)
769 if (isConvertibleToString!R)
770 {
771     write!(StringTypeOf!R)(name, buffer);
772 }
773 
774 @safe unittest
775 {
776     static assert(__traits(compiles, write(TestAliasedString(null), null)));
777 }
778 
779 /*********************************************
780 Appends `buffer` to file `name`.
781 
782 Creates the file if it does not already exist.
783 
784 Params:
785     name = string or range of characters representing the file _name
786     buffer = data to be appended to file
787 
788 Throws: $(LREF FileException) on error.
789  */
790 void append(R)(R name, const void[] buffer)
791 if ((isSomeFiniteCharInputRange!R || isSomeString!R) && !isConvertibleToString!R)
792 {
793     static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
794         writeImpl(name, name.tempCString!FSChar(), buffer, true);
795     else
796         writeImpl(null, name.tempCString!FSChar(), buffer, true);
797 }
798 
799 ///
800 @safe unittest
801 {
802    scope(exit)
803    {
804        assert(exists(deleteme));
805        remove(deleteme);
806    }
807 
808    int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
809    write(deleteme, a); // deleteme is the name of a temporary file
810    int[] b = [ 13, 21 ];
811    append(deleteme, b);
812    const bytes = read(deleteme);
813    const fileInts = () @trusted { return cast(int[]) bytes; }();
814    assert(fileInts == a ~ b);
815 }
816 
817 /// ditto
818 void append(R)(auto ref R name, const void[] buffer)
819 if (isConvertibleToString!R)
820 {
821     append!(StringTypeOf!R)(name, buffer);
822 }
823 
824 @safe unittest
825 {
826     static assert(__traits(compiles, append(TestAliasedString("foo"), [0, 1, 2, 3])));
827 }
828 
829 // POSIX implementation helper for write and append
830 
831 version (Posix) private void writeImpl(scope const(char)[] name, scope const(FSChar)* namez,
832         scope const(void)[] buffer, bool append) @trusted
833 {
834     import std.conv : octal;
835 
836     // append or write
837     auto mode = append ? O_CREAT | O_WRONLY | O_APPEND
838                        : O_CREAT | O_WRONLY | O_TRUNC;
839 
840     immutable fd = core.sys.posix.fcntl.open(namez, mode, octal!666);
841     cenforce(fd != -1, name, namez);
842     {
843         scope(failure) core.sys.posix.unistd.close(fd);
844 
845         immutable size = buffer.length;
846         size_t sum, cnt = void;
847         while (sum != size)
848         {
849             cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30;
850             const numwritten = core.sys.posix.unistd.write(fd, buffer.ptr + sum, cnt);
851             if (numwritten != cnt)
852                 break;
853             sum += numwritten;
854         }
855         cenforce(sum == size, name, namez);
856     }
857     cenforce(core.sys.posix.unistd.close(fd) == 0, name, namez);
858 }
859 
860 // Windows implementation helper for write and append
861 
862 version (Windows) private void writeImpl(scope const(char)[] name, scope const(FSChar)* namez,
863         scope const(void)[] buffer, bool append) @trusted
864 {
865     HANDLE h;
866     if (append)
867     {
868         alias defaults =
869             AliasSeq!(GENERIC_WRITE, 0, null, OPEN_ALWAYS,
870                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
871                 HANDLE.init);
872 
873         h = CreateFileW(namez, defaults);
874         cenforce(h != INVALID_HANDLE_VALUE, name, namez);
875         cenforce(SetFilePointer(h, 0, null, FILE_END) != INVALID_SET_FILE_POINTER,
876             name, namez);
877     }
878     else // write
879     {
880         alias defaults =
881             AliasSeq!(GENERIC_WRITE, 0, null, CREATE_ALWAYS,
882                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
883                 HANDLE.init);
884 
885         h = CreateFileW(namez, defaults);
886         cenforce(h != INVALID_HANDLE_VALUE, name, namez);
887     }
888     immutable size = buffer.length;
889     size_t sum, cnt = void;
890     DWORD numwritten = void;
891     while (sum != size)
892     {
893         cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30;
894         WriteFile(h, buffer.ptr + sum, cast(uint) cnt, &numwritten, null);
895         if (numwritten != cnt)
896             break;
897         sum += numwritten;
898     }
899     cenforce(sum == size && CloseHandle(h), name, namez);
900 }
901 
902 /***************************************************
903  * Rename file `from` _to `to`, moving it between directories if required.
904  * If the target file exists, it is overwritten.
905  *
906  * It is not possible to rename a file across different mount points
907  * or drives. On POSIX, the operation is atomic. That means, if `to`
908  * already exists there will be no time period during the operation
909  * where `to` is missing. See
910  * $(HTTP man7.org/linux/man-pages/man2/rename.2.html, manpage for rename)
911  * for more details.
912  *
913  * Params:
914  *    from = string or range of characters representing the existing file name
915  *    to = string or range of characters representing the target file name
916  *
917  * Throws: $(LREF FileException) on error.
918  */
919 void rename(RF, RT)(RF from, RT to)
920 if ((isSomeFiniteCharInputRange!RF || isSomeString!RF) && !isConvertibleToString!RF &&
921     (isSomeFiniteCharInputRange!RT || isSomeString!RT) && !isConvertibleToString!RT)
922 {
923     // Place outside of @trusted block
924     auto fromz = from.tempCString!FSChar();
925     auto toz = to.tempCString!FSChar();
926 
927     static if (isNarrowString!RF && is(immutable ElementEncodingType!RF == immutable char))
928         alias f = from;
929     else
930         enum string f = null;
931 
932     static if (isNarrowString!RT && is(immutable ElementEncodingType!RT == immutable char))
933         alias t = to;
934     else
935         enum string t = null;
936 
937     renameImpl(f, t, fromz, toz);
938 }
939 
940 /// ditto
941 void rename(RF, RT)(auto ref RF from, auto ref RT to)
942 if (isConvertibleToString!RF || isConvertibleToString!RT)
943 {
944     import std.meta : staticMap;
945     alias Types = staticMap!(convertToString, RF, RT);
946     rename!Types(from, to);
947 }
948 
949 @safe unittest
950 {
951     static assert(__traits(compiles, rename(TestAliasedString(null), TestAliasedString(null))));
952     static assert(__traits(compiles, rename("", TestAliasedString(null))));
953     static assert(__traits(compiles, rename(TestAliasedString(null), "")));
954     import std.utf : byChar;
955     static assert(__traits(compiles, rename(TestAliasedString(null), "".byChar)));
956 }
957 
958 ///
959 @safe unittest
960 {
961     auto t1 = deleteme, t2 = deleteme~"2";
962     scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
963 
964     t1.write("1");
965     t1.rename(t2);
966     assert(t2.readText == "1");
967 
968     t1.write("2");
969     t1.rename(t2);
970     assert(t2.readText == "2");
971 }
972 
973 private void renameImpl(scope const(char)[] f, scope const(char)[] t,
974                         scope const(FSChar)* fromz, scope const(FSChar)* toz) @trusted
975 {
976     version (Windows)
977     {
978         import std.exception : enforce;
979 
980         const result = MoveFileExW(fromz, toz, MOVEFILE_REPLACE_EXISTING);
981         if (!result)
982         {
983             import core.stdc.wchar_ : wcslen;
984             import std.conv : to, text;
985 
986             if (!f)
987                 f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]);
988 
989             if (!t)
990                 t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
991 
992             enforce(false,
993                 new FileException(
994                     text("Attempting to rename file ", f, " to ", t)));
995         }
996     }
997     else version (Posix)
998     {
999         static import core.stdc.stdio;
1000 
1001         cenforce(core.stdc.stdio.rename(fromz, toz) == 0, t, toz);
1002     }
1003 }
1004 
1005 @safe unittest
1006 {
1007     import std.utf : byWchar;
1008 
1009     auto t1 = deleteme, t2 = deleteme~"2";
1010     scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
1011 
1012     write(t1, "1");
1013     rename(t1, t2);
1014     assert(readText(t2) == "1");
1015 
1016     write(t1, "2");
1017     rename(t1, t2.byWchar);
1018     assert(readText(t2) == "2");
1019 }
1020 
1021 /***************************************************
1022 Delete file `name`.
1023 
1024 Params:
1025     name = string or range of characters representing the file _name
1026 
1027 Throws: $(LREF FileException) on error.
1028  */
1029 void remove(R)(R name)
1030 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
1031 {
1032     static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1033         removeImpl(name, name.tempCString!FSChar());
1034     else
1035         removeImpl(null, name.tempCString!FSChar());
1036 }
1037 
1038 /// ditto
1039 void remove(R)(auto ref R name)
1040 if (isConvertibleToString!R)
1041 {
1042     remove!(StringTypeOf!R)(name);
1043 }
1044 
1045 ///
1046 @safe unittest
1047 {
1048     import std.exception : assertThrown;
1049 
1050     deleteme.write("Hello");
1051     assert(deleteme.readText == "Hello");
1052 
1053     deleteme.remove;
1054     assertThrown!FileException(deleteme.readText);
1055 }
1056 
1057 @safe unittest
1058 {
1059     static assert(__traits(compiles, remove(TestAliasedString("foo"))));
1060 }
1061 
1062 private void removeImpl(scope const(char)[] name, scope const(FSChar)* namez) @trusted
1063 {
1064     version (Windows)
1065     {
1066         cenforce(DeleteFileW(namez), name, namez);
1067     }
1068     else version (Posix)
1069     {
1070         static import core.stdc.stdio;
1071 
1072         if (!name)
1073         {
1074             import core.stdc.string : strlen;
1075 
1076             auto len = namez ? strlen(namez) : 0;
1077             name = namez[0 .. len];
1078         }
1079         cenforce(core.stdc.stdio.remove(namez) == 0,
1080             "Failed to remove file " ~ (name is null ? "(null)" : name));
1081     }
1082 }
1083 
1084 @safe unittest
1085 {
1086     import std.exception : collectExceptionMsg, assertThrown;
1087 
1088     string filename = null; // e.g. as returned by File.tmpfile.name
1089 
1090     version (linux)
1091     {
1092         // exact exception message is OS-dependent
1093         auto msg = filename.remove.collectExceptionMsg!FileException;
1094         assert("Failed to remove file (null): Bad address" == msg, msg);
1095     }
1096     else version (Windows)
1097     {
1098         import std.algorithm.searching : startsWith;
1099 
1100         // don't test exact message on windows, it's language dependent
1101         auto msg = filename.remove.collectExceptionMsg!FileException;
1102         assert(msg.startsWith("(null):"), msg);
1103     }
1104     else
1105     {
1106         assertThrown!FileException(filename.remove);
1107     }
1108 }
1109 
1110 version (Windows) private WIN32_FILE_ATTRIBUTE_DATA getFileAttributesWin(R)(R name)
1111 if (isSomeFiniteCharInputRange!R)
1112 {
1113     auto namez = name.tempCString!FSChar();
1114 
1115     WIN32_FILE_ATTRIBUTE_DATA fad = void;
1116 
1117     static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1118     {
1119         static void getFA(scope const(char)[] name, scope const(FSChar)* namez,
1120                           out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
1121         {
1122             import std.exception : enforce;
1123             enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad),
1124                 new FileException(name.idup));
1125         }
1126         getFA(name, namez, fad);
1127     }
1128     else
1129     {
1130         static void getFA(scope const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
1131         {
1132             import core.stdc.wchar_ : wcslen;
1133             import std.conv : to;
1134             import std.exception : enforce;
1135 
1136             enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad),
1137                 new FileException(namez[0 .. wcslen(namez)].to!string));
1138         }
1139         getFA(namez, fad);
1140     }
1141     return fad;
1142 }
1143 
1144 version (Windows) private ulong makeUlong(DWORD dwLow, DWORD dwHigh) @safe pure nothrow @nogc
1145 {
1146     ULARGE_INTEGER li;
1147     li.LowPart  = dwLow;
1148     li.HighPart = dwHigh;
1149     return li.QuadPart;
1150 }
1151 
1152 version (Posix) private extern (C) pragma(mangle, stat.mangleof)
1153 int trustedStat(scope const(FSChar)* namez, ref stat_t buf) @nogc nothrow @trusted;
1154 
1155 /**
1156 Get size of file `name` in bytes.
1157 
1158 Params:
1159     name = string or range of characters representing the file _name
1160 Returns:
1161     The size of file in bytes.
1162 Throws:
1163     $(LREF FileException) on error (e.g., file not found).
1164  */
1165 ulong getSize(R)(R name)
1166 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
1167 {
1168     version (Windows)
1169     {
1170         with (getFileAttributesWin(name))
1171             return makeUlong(nFileSizeLow, nFileSizeHigh);
1172     }
1173     else version (Posix)
1174     {
1175         auto namez = name.tempCString();
1176 
1177         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1178             alias names = name;
1179         else
1180             string names = null;
1181         stat_t statbuf = void;
1182         cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1183         return statbuf.st_size;
1184     }
1185 }
1186 
1187 /// ditto
1188 ulong getSize(R)(auto ref R name)
1189 if (isConvertibleToString!R)
1190 {
1191     return getSize!(StringTypeOf!R)(name);
1192 }
1193 
1194 @safe unittest
1195 {
1196     static assert(__traits(compiles, getSize(TestAliasedString("foo"))));
1197 }
1198 
1199 ///
1200 @safe unittest
1201 {
1202     scope(exit) deleteme.remove;
1203 
1204     // create a file of size 1
1205     write(deleteme, "a");
1206     assert(getSize(deleteme) == 1);
1207 
1208     // create a file of size 3
1209     write(deleteme, "abc");
1210     assert(getSize(deleteme) == 3);
1211 }
1212 
1213 @safe unittest
1214 {
1215     // create a file of size 1
1216     write(deleteme, "a");
1217     scope(exit) deleteme.exists && deleteme.remove;
1218     assert(getSize(deleteme) == 1);
1219     // create a file of size 3
1220     write(deleteme, "abc");
1221     import std.utf : byChar;
1222     assert(getSize(deleteme.byChar) == 3);
1223 }
1224 
1225 // Reads a time field from a stat_t with full precision.
1226 version (Posix)
1227 private SysTime statTimeToStdTime(char which)(ref const stat_t statbuf)
1228 {
1229     auto unixTime = mixin(`statbuf.st_` ~ which ~ `time`);
1230     long stdTime = unixTimeToStdTime(unixTime);
1231 
1232     static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `tim`))))
1233         stdTime += mixin(`statbuf.st_` ~ which ~ `tim.tv_nsec`) / 100;
1234     else
1235     static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `timensec`))))
1236         stdTime += mixin(`statbuf.st_` ~ which ~ `timensec`) / 100;
1237     else
1238     static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `time_nsec`))))
1239         stdTime += mixin(`statbuf.st_` ~ which ~ `time_nsec`) / 100;
1240     else
1241     static if (is(typeof(mixin(`statbuf.__st_` ~ which ~ `timensec`))))
1242         stdTime += mixin(`statbuf.__st_` ~ which ~ `timensec`) / 100;
1243 
1244     return SysTime(stdTime);
1245 }
1246 
1247 /++
1248     Get the access and modified times of file or folder `name`.
1249 
1250     Params:
1251         name             = File/Folder _name to get times for.
1252         accessTime       = Time the file/folder was last accessed.
1253         modificationTime = Time the file/folder was last modified.
1254 
1255     Throws:
1256         $(LREF FileException) on error.
1257  +/
1258 void getTimes(R)(R name,
1259               out SysTime accessTime,
1260               out SysTime modificationTime)
1261 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
1262 {
1263     version (Windows)
1264     {
1265         import std.datetime.systime : FILETIMEToSysTime;
1266 
1267         with (getFileAttributesWin(name))
1268         {
1269             accessTime = FILETIMEToSysTime(&ftLastAccessTime);
1270             modificationTime = FILETIMEToSysTime(&ftLastWriteTime);
1271         }
1272     }
1273     else version (Posix)
1274     {
1275         auto namez = name.tempCString();
1276 
1277         stat_t statbuf = void;
1278 
1279         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1280             alias names = name;
1281         else
1282             string names = null;
1283         cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1284 
1285         accessTime = statTimeToStdTime!'a'(statbuf);
1286         modificationTime = statTimeToStdTime!'m'(statbuf);
1287     }
1288 }
1289 
1290 /// ditto
1291 void getTimes(R)(auto ref R name,
1292               out SysTime accessTime,
1293               out SysTime modificationTime)
1294 if (isConvertibleToString!R)
1295 {
1296     return getTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
1297 }
1298 
1299 ///
1300 @safe unittest
1301 {
1302     import std.datetime : abs, SysTime;
1303 
1304     scope(exit) deleteme.remove;
1305     write(deleteme, "a");
1306 
1307     SysTime accessTime, modificationTime;
1308 
1309     getTimes(deleteme, accessTime, modificationTime);
1310 
1311     import std.datetime : Clock, seconds;
1312     auto currTime = Clock.currTime();
1313     enum leeway = 5.seconds;
1314 
1315     auto diffAccess = accessTime - currTime;
1316     auto diffModification = modificationTime - currTime;
1317     assert(abs(diffAccess) <= leeway);
1318     assert(abs(diffModification) <= leeway);
1319 }
1320 
1321 @safe unittest
1322 {
1323     SysTime atime, mtime;
1324     static assert(__traits(compiles, getTimes(TestAliasedString("foo"), atime, mtime)));
1325 }
1326 
1327 @safe unittest
1328 {
1329     import std.stdio : writefln;
1330 
1331     auto currTime = Clock.currTime();
1332 
1333     write(deleteme, "a");
1334     scope(exit) assert(deleteme.exists), deleteme.remove;
1335 
1336     SysTime accessTime1;
1337     SysTime modificationTime1;
1338 
1339     getTimes(deleteme, accessTime1, modificationTime1);
1340 
1341     enum leeway = 5.seconds;
1342 
1343     {
1344         auto diffa = accessTime1 - currTime;
1345         auto diffm = modificationTime1 - currTime;
1346         scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime1, modificationTime1, currTime, diffa, diffm);
1347 
1348         assert(abs(diffa) <= leeway);
1349         assert(abs(diffm) <= leeway);
1350     }
1351 
1352     version (fullFileTests)
1353     {
1354         import core.thread;
1355         enum sleepTime = dur!"seconds"(2);
1356         Thread.sleep(sleepTime);
1357 
1358         currTime = Clock.currTime();
1359         write(deleteme, "b");
1360 
1361         SysTime accessTime2 = void;
1362         SysTime modificationTime2 = void;
1363 
1364         getTimes(deleteme, accessTime2, modificationTime2);
1365 
1366         {
1367             auto diffa = accessTime2 - currTime;
1368             auto diffm = modificationTime2 - currTime;
1369             scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime2, modificationTime2, currTime, diffa, diffm);
1370 
1371             //There is no guarantee that the access time will be updated.
1372             assert(abs(diffa) <= leeway + sleepTime);
1373             assert(abs(diffm) <= leeway);
1374         }
1375 
1376         assert(accessTime1 <= accessTime2);
1377         assert(modificationTime1 <= modificationTime2);
1378     }
1379 }
1380 
1381 
1382 version (StdDdoc)
1383 {
1384     /++
1385      $(BLUE This function is Windows-Only.)
1386 
1387      Get creation/access/modified times of file `name`.
1388 
1389      This is the same as `getTimes` except that it also gives you the file
1390      creation time - which isn't possible on POSIX systems.
1391 
1392      Params:
1393      name                 = File _name to get times for.
1394      fileCreationTime     = Time the file was created.
1395      fileAccessTime       = Time the file was last accessed.
1396      fileModificationTime = Time the file was last modified.
1397 
1398      Throws:
1399      $(LREF FileException) on error.
1400      +/
1401     void getTimesWin(R)(R name,
1402                         out SysTime fileCreationTime,
1403                         out SysTime fileAccessTime,
1404                         out SysTime fileModificationTime)
1405     if (isSomeFiniteCharInputRange!R || isConvertibleToString!R);
1406     // above line contains both constraints for docs
1407     // (so users know how it can be called)
1408 }
1409 else version (Windows)
1410 {
1411     void getTimesWin(R)(R name,
1412                         out SysTime fileCreationTime,
1413                         out SysTime fileAccessTime,
1414                         out SysTime fileModificationTime)
1415     if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
1416     {
1417         import std.datetime.systime : FILETIMEToSysTime;
1418 
1419         with (getFileAttributesWin(name))
1420         {
1421             fileCreationTime = FILETIMEToSysTime(&ftCreationTime);
1422             fileAccessTime = FILETIMEToSysTime(&ftLastAccessTime);
1423             fileModificationTime = FILETIMEToSysTime(&ftLastWriteTime);
1424         }
1425     }
1426 
1427     void getTimesWin(R)(auto ref R name,
1428                         out SysTime fileCreationTime,
1429                         out SysTime fileAccessTime,
1430                         out SysTime fileModificationTime)
1431     if (isConvertibleToString!R)
1432     {
1433         getTimesWin!(StringTypeOf!R)(name, fileCreationTime, fileAccessTime, fileModificationTime);
1434     }
1435 }
1436 
1437 version (Windows) @system unittest
1438 {
1439     import std.stdio : writefln;
1440     auto currTime = Clock.currTime();
1441 
1442     write(deleteme, "a");
1443     scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1444 
1445     SysTime creationTime1 = void;
1446     SysTime accessTime1 = void;
1447     SysTime modificationTime1 = void;
1448 
1449     getTimesWin(deleteme, creationTime1, accessTime1, modificationTime1);
1450 
1451     enum leeway = dur!"seconds"(5);
1452 
1453     {
1454         auto diffc = creationTime1 - currTime;
1455         auto diffa = accessTime1 - currTime;
1456         auto diffm = modificationTime1 - currTime;
1457         scope(failure)
1458         {
1459             writefln("[%s] [%s] [%s] [%s] [%s] [%s] [%s]",
1460                      creationTime1, accessTime1, modificationTime1, currTime, diffc, diffa, diffm);
1461         }
1462 
1463         // Deleting and recreating a file doesn't seem to always reset the "file creation time"
1464         //assert(abs(diffc) <= leeway);
1465         assert(abs(diffa) <= leeway);
1466         assert(abs(diffm) <= leeway);
1467     }
1468 
1469     version (fullFileTests)
1470     {
1471         import core.thread;
1472         Thread.sleep(dur!"seconds"(2));
1473 
1474         currTime = Clock.currTime();
1475         write(deleteme, "b");
1476 
1477         SysTime creationTime2 = void;
1478         SysTime accessTime2 = void;
1479         SysTime modificationTime2 = void;
1480 
1481         getTimesWin(deleteme, creationTime2, accessTime2, modificationTime2);
1482 
1483         {
1484             auto diffa = accessTime2 - currTime;
1485             auto diffm = modificationTime2 - currTime;
1486             scope(failure)
1487             {
1488                 writefln("[%s] [%s] [%s] [%s] [%s]",
1489                          accessTime2, modificationTime2, currTime, diffa, diffm);
1490             }
1491 
1492             assert(abs(diffa) <= leeway);
1493             assert(abs(diffm) <= leeway);
1494         }
1495 
1496         assert(creationTime1 == creationTime2);
1497         assert(accessTime1 <= accessTime2);
1498         assert(modificationTime1 <= modificationTime2);
1499     }
1500 
1501     {
1502         SysTime ctime, atime, mtime;
1503         static assert(__traits(compiles, getTimesWin(TestAliasedString("foo"), ctime, atime, mtime)));
1504     }
1505 }
1506 
1507 version (Darwin)
1508 private
1509 {
1510     import core.stdc.config : c_ulong;
1511     enum ATTR_CMN_MODTIME  = 0x00000400, ATTR_CMN_ACCTIME  = 0x00001000;
1512     alias attrgroup_t = uint;
1513     static struct attrlist
1514     {
1515         ushort bitmapcount, reserved;
1516         attrgroup_t commonattr, volattr, dirattr, fileattr, forkattr;
1517     }
1518     extern(C) int setattrlist(scope const(char)* path, scope ref attrlist attrs,
1519         scope void* attrbuf, size_t attrBufSize, c_ulong options) nothrow @nogc @system;
1520 }
1521 
1522 /++
1523     Set access/modified times of file or folder `name`.
1524 
1525     Params:
1526         name             = File/Folder _name to get times for.
1527         accessTime       = Time the file/folder was last accessed.
1528         modificationTime = Time the file/folder was last modified.
1529 
1530     Throws:
1531         $(LREF FileException) on error.
1532  +/
1533 void setTimes(R)(R name,
1534               SysTime accessTime,
1535               SysTime modificationTime)
1536 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
1537 {
1538     auto namez = name.tempCString!FSChar();
1539     static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1540         alias names = name;
1541     else
1542         string names = null;
1543     setTimesImpl(names, namez, accessTime, modificationTime);
1544 }
1545 
1546 ///
1547 @safe unittest
1548 {
1549     import std.datetime : DateTime, hnsecs, SysTime;
1550 
1551     scope(exit) deleteme.remove;
1552     write(deleteme, "a");
1553 
1554     SysTime accessTime = SysTime(DateTime(2010, 10, 4, 0, 0, 30));
1555     SysTime modificationTime = SysTime(DateTime(2018, 10, 4, 0, 0, 30));
1556     setTimes(deleteme, accessTime, modificationTime);
1557 
1558     SysTime accessTimeResolved, modificationTimeResolved;
1559     getTimes(deleteme, accessTimeResolved, modificationTimeResolved);
1560 
1561     assert(accessTime == accessTimeResolved);
1562     assert(modificationTime == modificationTimeResolved);
1563 }
1564 
1565 /// ditto
1566 void setTimes(R)(auto ref R name,
1567               SysTime accessTime,
1568               SysTime modificationTime)
1569 if (isConvertibleToString!R)
1570 {
1571     setTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
1572 }
1573 
1574 private void setTimesImpl(scope const(char)[] names, scope const(FSChar)* namez,
1575     SysTime accessTime, SysTime modificationTime) @trusted
1576 {
1577     version (Windows)
1578     {
1579         import std.datetime.systime : SysTimeToFILETIME;
1580         const ta = SysTimeToFILETIME(accessTime);
1581         const tm = SysTimeToFILETIME(modificationTime);
1582         alias defaults =
1583             AliasSeq!(FILE_WRITE_ATTRIBUTES,
1584                       0,
1585                       null,
1586                       OPEN_EXISTING,
1587                       FILE_ATTRIBUTE_NORMAL |
1588                       FILE_ATTRIBUTE_DIRECTORY |
1589                       FILE_FLAG_BACKUP_SEMANTICS,
1590                       HANDLE.init);
1591         auto h = CreateFileW(namez, defaults);
1592 
1593         cenforce(h != INVALID_HANDLE_VALUE, names, namez);
1594 
1595         scope(exit)
1596             cenforce(CloseHandle(h), names, namez);
1597 
1598         cenforce(SetFileTime(h, null, &ta, &tm), names, namez);
1599     }
1600     else
1601     {
1602         static if (is(typeof(&utimensat)))
1603         {
1604             timespec[2] t = void;
1605             t[0] = accessTime.toTimeSpec();
1606             t[1] = modificationTime.toTimeSpec();
1607             cenforce(utimensat(AT_FDCWD, namez, t, 0) == 0, names, namez);
1608         }
1609         else
1610         {
1611             version (Darwin)
1612             {
1613                 // Set modification & access times with setattrlist to avoid precision loss.
1614                 attrlist attrs = { bitmapcount: 5, reserved: 0,
1615                         commonattr: ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME,
1616                         volattr: 0, dirattr: 0, fileattr: 0, forkattr: 0 };
1617                 timespec[2] attrbuf = [modificationTime.toTimeSpec(), accessTime.toTimeSpec()];
1618                 if (0 == setattrlist(namez, attrs, &attrbuf, attrbuf.sizeof, 0))
1619                     return;
1620                 if (.errno != ENOTSUP)
1621                     cenforce(false, names, namez);
1622                 // Not all volumes support setattrlist. In such cases
1623                 // fall through to the utimes implementation.
1624             }
1625             timeval[2] t = void;
1626             t[0] = accessTime.toTimeVal();
1627             t[1] = modificationTime.toTimeVal();
1628             cenforce(utimes(namez, t) == 0, names, namez);
1629         }
1630     }
1631 }
1632 
1633 @safe unittest
1634 {
1635     if (false) // Test instatiation
1636         setTimes(TestAliasedString("foo"), SysTime.init, SysTime.init);
1637 }
1638 
1639 @safe unittest
1640 {
1641     import std.stdio : File;
1642     string newdir = deleteme ~ r".dir";
1643     string dir = newdir ~ r"/a/b/c";
1644     string file = dir ~ "/file";
1645 
1646     if (!exists(dir)) mkdirRecurse(dir);
1647     { auto f = File(file, "w"); }
1648 
1649     void testTimes(int hnsecValue)
1650     {
1651         foreach (path; [file, dir])  // test file and dir
1652         {
1653             SysTime atime = SysTime(DateTime(2010, 10, 4, 0, 0, 30), hnsecs(hnsecValue));
1654             SysTime mtime = SysTime(DateTime(2011, 10, 4, 0, 0, 30), hnsecs(hnsecValue));
1655             setTimes(path, atime, mtime);
1656 
1657             SysTime atime_res;
1658             SysTime mtime_res;
1659             getTimes(path, atime_res, mtime_res);
1660             assert(atime == atime_res);
1661             assert(mtime == mtime_res);
1662         }
1663     }
1664 
1665     testTimes(0);
1666     version (linux)
1667         testTimes(123_456_7);
1668 
1669     rmdirRecurse(newdir);
1670 }
1671 
1672 // https://issues.dlang.org/show_bug.cgi?id=23683
1673 @safe unittest
1674 {
1675     scope(exit) deleteme.remove;
1676     import std.stdio : File;
1677     auto f = File(deleteme, "wb");
1678     SysTime time = SysTime(DateTime(2018, 10, 4, 0, 0, 30));
1679     setTimes(deleteme, time, time);
1680 }
1681 
1682 /++
1683     Returns the time that the given file was last modified.
1684 
1685     Params:
1686         name = the name of the file to check
1687     Returns:
1688         A $(REF SysTime,std,datetime,systime).
1689     Throws:
1690         $(LREF FileException) if the given file does not exist.
1691 +/
1692 SysTime timeLastModified(R)(R name)
1693 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
1694 {
1695     version (Windows)
1696     {
1697         SysTime dummy;
1698         SysTime ftm;
1699 
1700         getTimesWin(name, dummy, dummy, ftm);
1701 
1702         return ftm;
1703     }
1704     else version (Posix)
1705     {
1706         auto namez = name.tempCString!FSChar();
1707         stat_t statbuf = void;
1708 
1709         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1710             alias names = name;
1711         else
1712             string names = null;
1713         cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1714 
1715         return statTimeToStdTime!'m'(statbuf);
1716     }
1717 }
1718 
1719 /// ditto
1720 SysTime timeLastModified(R)(auto ref R name)
1721 if (isConvertibleToString!R)
1722 {
1723     return timeLastModified!(StringTypeOf!R)(name);
1724 }
1725 
1726 ///
1727 @safe unittest
1728 {
1729     import std.datetime : abs, DateTime, hnsecs, SysTime;
1730     scope(exit) deleteme.remove;
1731 
1732     import std.datetime : Clock, seconds;
1733     auto currTime = Clock.currTime();
1734     enum leeway = 5.seconds;
1735     deleteme.write("bb");
1736     assert(abs(deleteme.timeLastModified - currTime) <= leeway);
1737 }
1738 
1739 @safe unittest
1740 {
1741     static assert(__traits(compiles, timeLastModified(TestAliasedString("foo"))));
1742 }
1743 
1744 /++
1745     Returns the time that the given file was last modified. If the
1746     file does not exist, returns `returnIfMissing`.
1747 
1748     A frequent usage pattern occurs in build automation tools such as
1749     $(HTTP gnu.org/software/make, make) or $(HTTP
1750     en.wikipedia.org/wiki/Apache_Ant, ant). To check whether file $(D
1751     target) must be rebuilt from file `source` (i.e., `target` is
1752     older than `source` or does not exist), use the comparison
1753     below. The code throws a $(LREF FileException) if `source` does not
1754     exist (as it should). On the other hand, the `SysTime.min` default
1755     makes a non-existing `target` seem infinitely old so the test
1756     correctly prompts building it.
1757 
1758     Params:
1759         name = The name of the file to get the modification time for.
1760         returnIfMissing = The time to return if the given file does not exist.
1761     Returns:
1762         A $(REF SysTime,std,datetime,systime).
1763 
1764 Example:
1765 --------------------
1766 if (source.timeLastModified >= target.timeLastModified(SysTime.min))
1767 {
1768     // must (re)build
1769 }
1770 else
1771 {
1772     // target is up-to-date
1773 }
1774 --------------------
1775 +/
1776 SysTime timeLastModified(R)(R name, SysTime returnIfMissing)
1777 if (isSomeFiniteCharInputRange!R)
1778 {
1779     version (Windows)
1780     {
1781         if (!exists(name))
1782             return returnIfMissing;
1783 
1784         SysTime dummy;
1785         SysTime ftm;
1786 
1787         getTimesWin(name, dummy, dummy, ftm);
1788 
1789         return ftm;
1790     }
1791     else version (Posix)
1792     {
1793         auto namez = name.tempCString!FSChar();
1794         stat_t statbuf = void;
1795 
1796         return trustedStat(namez, statbuf) != 0 ?
1797                returnIfMissing :
1798                statTimeToStdTime!'m'(statbuf);
1799     }
1800 }
1801 
1802 ///
1803 @safe unittest
1804 {
1805     import std.datetime : SysTime;
1806 
1807     assert("file.does.not.exist".timeLastModified(SysTime.min) == SysTime.min);
1808 
1809     auto source = deleteme ~ "source";
1810     auto target = deleteme ~ "target";
1811     scope(exit) source.remove, target.remove;
1812 
1813     source.write(".");
1814     assert(target.timeLastModified(SysTime.min) < source.timeLastModified);
1815     target.write(".");
1816     assert(target.timeLastModified(SysTime.min) >= source.timeLastModified);
1817 }
1818 
1819 version (StdDdoc)
1820 {
1821     /++
1822      $(BLUE This function is POSIX-Only.)
1823 
1824      Returns the time that the given file was last modified.
1825      Params:
1826         statbuf = stat_t retrieved from file.
1827      +/
1828     SysTime timeLastModified()(auto ref stat_t statbuf) pure nothrow {assert(false);}
1829     /++
1830      $(BLUE This function is POSIX-Only.)
1831 
1832      Returns the time that the given file was last accessed.
1833      Params:
1834         statbuf = stat_t retrieved from file.
1835      +/
1836     SysTime timeLastAccessed()(auto ref stat_t statbuf) pure nothrow {assert(false);}
1837     /++
1838      $(BLUE This function is POSIX-Only.)
1839 
1840      Returns the time that the given file was last changed.
1841      Params:
1842         statbuf = stat_t retrieved from file.
1843      +/
1844     SysTime timeStatusChanged()(auto ref stat_t statbuf) pure nothrow {assert(false);}
1845 }
1846 else version (Posix)
1847 {
1848     SysTime timeLastModified()(auto ref stat_t statbuf) pure nothrow
1849     {
1850         return statTimeToStdTime!'m'(statbuf);
1851     }
1852     SysTime timeLastAccessed()(auto ref stat_t statbuf) pure nothrow
1853     {
1854         return statTimeToStdTime!'a'(statbuf);
1855     }
1856     SysTime timeStatusChanged()(auto ref stat_t statbuf) pure nothrow
1857     {
1858         return statTimeToStdTime!'c'(statbuf);
1859     }
1860 
1861     @safe unittest
1862     {
1863         stat_t statbuf;
1864         // check that both lvalues and rvalues work
1865         timeLastAccessed(statbuf);
1866         cast(void) timeLastAccessed(stat_t.init);
1867     }
1868 }
1869 
1870 @safe unittest
1871 {
1872     //std.process.executeShell("echo a > deleteme");
1873     if (exists(deleteme))
1874         remove(deleteme);
1875 
1876     write(deleteme, "a\n");
1877 
1878     scope(exit)
1879     {
1880         assert(exists(deleteme));
1881         remove(deleteme);
1882     }
1883 
1884     // assert(lastModified("deleteme") >
1885     //         lastModified("this file does not exist", SysTime.min));
1886     //assert(lastModified("deleteme") > lastModified(__FILE__));
1887 }
1888 
1889 
1890 // Tests sub-second precision of querying file times.
1891 // Should pass on most modern systems running on modern filesystems.
1892 // Exceptions:
1893 // - FreeBSD, where one would need to first set the
1894 //   vfs.timestamp_precision sysctl to a value greater than zero.
1895 // - OS X, where the native filesystem (HFS+) stores filesystem
1896 //   timestamps with 1-second precision.
1897 //
1898 // Note: on linux systems, although in theory a change to a file date
1899 // can be tracked with precision of 4 msecs, this test waits 20 msecs
1900 // to prevent possible problems relative to the CI services the dlang uses,
1901 // as they may have the HZ setting that controls the software clock set to 100
1902 // (instead of the more common 250).
1903 // see https://man7.org/linux/man-pages/man7/time.7.html
1904 //     https://stackoverflow.com/a/14393315,
1905 //     https://issues.dlang.org/show_bug.cgi?id=21148
1906 version (FreeBSD) {} else
1907 version (DragonFlyBSD) {} else
1908 version (OSX) {} else
1909 @safe unittest
1910 {
1911     import core.thread;
1912 
1913     if (exists(deleteme))
1914         remove(deleteme);
1915 
1916     SysTime lastTime;
1917     foreach (n; 0 .. 3)
1918     {
1919         write(deleteme, "a");
1920         auto time = timeLastModified(deleteme);
1921         remove(deleteme);
1922         assert(time != lastTime);
1923         lastTime = time;
1924         () @trusted { Thread.sleep(20.msecs); }();
1925     }
1926 }
1927 
1928 
1929 /**
1930  * Determine whether the given file (or directory) _exists.
1931  * Params:
1932  *    name = string or range of characters representing the file _name
1933  * Returns:
1934  *    true if the file _name specified as input _exists
1935  */
1936 bool exists(R)(R name)
1937 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
1938 {
1939     return existsImpl(name.tempCString!FSChar());
1940 }
1941 
1942 /// ditto
1943 bool exists(R)(auto ref R name)
1944 if (isConvertibleToString!R)
1945 {
1946     return exists!(StringTypeOf!R)(name);
1947 }
1948 
1949 ///
1950 @safe unittest
1951 {
1952     auto f = deleteme ~ "does.not.exist";
1953     assert(!f.exists);
1954 
1955     f.write("hello");
1956     assert(f.exists);
1957 
1958     f.remove;
1959     assert(!f.exists);
1960 }
1961 
1962 private bool existsImpl(scope const(FSChar)* namez) @trusted nothrow @nogc
1963 {
1964     version (Windows)
1965     {
1966         // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
1967         // fileio/base/getfileattributes.asp
1968         return GetFileAttributesW(namez) != 0xFFFFFFFF;
1969     }
1970     else version (Posix)
1971     {
1972         /*
1973             The reason why we use stat (and not access) here is
1974             the quirky behavior of access for SUID programs: if
1975             we used access, a file may not appear to "exist",
1976             despite that the program would be able to open it
1977             just fine. The behavior in question is described as
1978             follows in the access man page:
1979 
1980             > The check is done using the calling process's real
1981             > UID and GID, rather than the effective IDs as is
1982             > done when actually attempting an operation (e.g.,
1983             > open(2)) on the file. This allows set-user-ID
1984             > programs to easily determine the invoking user's
1985             > authority.
1986 
1987             While various operating systems provide eaccess or
1988             euidaccess functions, these are not part of POSIX -
1989             so it's safer to use stat instead.
1990         */
1991 
1992         stat_t statbuf = void;
1993         return lstat(namez, &statbuf) == 0;
1994     }
1995     else
1996         static assert(0);
1997 }
1998 
1999 ///
2000 @safe unittest
2001 {
2002     assert(".".exists);
2003     assert(!"this file does not exist".exists);
2004     deleteme.write("a\n");
2005     scope(exit) deleteme.remove;
2006     assert(deleteme.exists);
2007 }
2008 
2009 // https://issues.dlang.org/show_bug.cgi?id=16573
2010 @safe unittest
2011 {
2012     enum S : string { foo = "foo" }
2013     assert(__traits(compiles, S.foo.exists));
2014 }
2015 
2016 /++
2017  Returns the attributes of the given file.
2018 
2019  Note that the file attributes on Windows and POSIX systems are
2020  completely different. On Windows, they're what is returned by
2021  $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx,
2022  GetFileAttributes), whereas on POSIX systems, they're the
2023  `st_mode` value which is part of the $(D stat struct) gotten by
2024  calling the $(HTTP en.wikipedia.org/wiki/Stat_%28Unix%29, `stat`)
2025  function.
2026 
2027  On POSIX systems, if the given file is a symbolic link, then
2028  attributes are the attributes of the file pointed to by the symbolic
2029  link.
2030 
2031  Params:
2032     name = The file to get the attributes of.
2033  Returns:
2034     The attributes of the file as a `uint`.
2035  Throws: $(LREF FileException) on error.
2036   +/
2037 uint getAttributes(R)(R name)
2038 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2039 {
2040     version (Windows)
2041     {
2042         auto namez = name.tempCString!FSChar();
2043         static auto trustedGetFileAttributesW(scope const(FSChar)* namez) @trusted
2044         {
2045             return GetFileAttributesW(namez);
2046         }
2047         immutable result = trustedGetFileAttributesW(namez);
2048 
2049         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2050             alias names = name;
2051         else
2052             string names = null;
2053         cenforce(result != INVALID_FILE_ATTRIBUTES, names, namez);
2054 
2055         return result;
2056     }
2057     else version (Posix)
2058     {
2059         auto namez = name.tempCString!FSChar();
2060         stat_t statbuf = void;
2061 
2062         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2063             alias names = name;
2064         else
2065             string names = null;
2066         cenforce(trustedStat(namez, statbuf) == 0, names, namez);
2067 
2068         return statbuf.st_mode;
2069     }
2070 }
2071 
2072 /// ditto
2073 uint getAttributes(R)(auto ref R name)
2074 if (isConvertibleToString!R)
2075 {
2076     return getAttributes!(StringTypeOf!R)(name);
2077 }
2078 
2079 /// getAttributes with a file
2080 @safe unittest
2081 {
2082     import std.exception : assertThrown;
2083 
2084     auto f = deleteme ~ "file";
2085     scope(exit) f.remove;
2086 
2087     assert(!f.exists);
2088     assertThrown!FileException(f.getAttributes);
2089 
2090     f.write(".");
2091     auto attributes = f.getAttributes;
2092     assert(!attributes.attrIsDir);
2093     assert(attributes.attrIsFile);
2094 }
2095 
2096 /// getAttributes with a directory
2097 @safe unittest
2098 {
2099     import std.exception : assertThrown;
2100 
2101     auto dir = deleteme ~ "dir";
2102     scope(exit) dir.rmdir;
2103 
2104     assert(!dir.exists);
2105     assertThrown!FileException(dir.getAttributes);
2106 
2107     dir.mkdir;
2108     auto attributes = dir.getAttributes;
2109     assert(attributes.attrIsDir);
2110     assert(!attributes.attrIsFile);
2111 }
2112 
2113 @safe unittest
2114 {
2115     static assert(__traits(compiles, getAttributes(TestAliasedString(null))));
2116 }
2117 
2118 /++
2119     If the given file is a symbolic link, then this returns the attributes of the
2120     symbolic link itself rather than file that it points to. If the given file
2121     is $(I not) a symbolic link, then this function returns the same result
2122     as getAttributes.
2123 
2124     On Windows, getLinkAttributes is identical to getAttributes. It exists on
2125     Windows so that you don't have to special-case code for Windows when dealing
2126     with symbolic links.
2127 
2128     Params:
2129         name = The file to get the symbolic link attributes of.
2130 
2131     Returns:
2132         the attributes
2133 
2134     Throws:
2135         $(LREF FileException) on error.
2136  +/
2137 uint getLinkAttributes(R)(R name)
2138 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2139 {
2140     version (Windows)
2141     {
2142         return getAttributes(name);
2143     }
2144     else version (Posix)
2145     {
2146         auto namez = name.tempCString!FSChar();
2147         static auto trustedLstat(const(FSChar)* namez, ref stat_t buf) @trusted
2148         {
2149             return lstat(namez, &buf);
2150         }
2151         stat_t lstatbuf = void;
2152         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2153             alias names = name;
2154         else
2155             string names = null;
2156         cenforce(trustedLstat(namez, lstatbuf) == 0, names, namez);
2157         return lstatbuf.st_mode;
2158     }
2159 }
2160 
2161 /// ditto
2162 uint getLinkAttributes(R)(auto ref R name)
2163 if (isConvertibleToString!R)
2164 {
2165     return getLinkAttributes!(StringTypeOf!R)(name);
2166 }
2167 
2168 ///
2169 @safe unittest
2170 {
2171     import std.exception : assertThrown;
2172 
2173     auto source = deleteme ~ "source";
2174     auto target = deleteme ~ "target";
2175 
2176     assert(!source.exists);
2177     assertThrown!FileException(source.getLinkAttributes);
2178 
2179     // symlinking isn't available on Windows
2180     version (Posix)
2181     {
2182         scope(exit) source.remove, target.remove;
2183 
2184         target.write("target");
2185         target.symlink(source);
2186         assert(source.readText == "target");
2187         assert(source.isSymlink);
2188         assert(source.getLinkAttributes.attrIsSymlink);
2189     }
2190 }
2191 
2192 /// if the file is no symlink, getLinkAttributes behaves like getAttributes
2193 @safe unittest
2194 {
2195     import std.exception : assertThrown;
2196 
2197     auto f = deleteme ~ "file";
2198     scope(exit) f.remove;
2199 
2200     assert(!f.exists);
2201     assertThrown!FileException(f.getLinkAttributes);
2202 
2203     f.write(".");
2204     auto attributes = f.getLinkAttributes;
2205     assert(!attributes.attrIsDir);
2206     assert(attributes.attrIsFile);
2207 }
2208 
2209 /// if the file is no symlink, getLinkAttributes behaves like getAttributes
2210 @safe unittest
2211 {
2212     import std.exception : assertThrown;
2213 
2214     auto dir = deleteme ~ "dir";
2215     scope(exit) dir.rmdir;
2216 
2217     assert(!dir.exists);
2218     assertThrown!FileException(dir.getLinkAttributes);
2219 
2220     dir.mkdir;
2221     auto attributes = dir.getLinkAttributes;
2222     assert(attributes.attrIsDir);
2223     assert(!attributes.attrIsFile);
2224 }
2225 
2226 @safe unittest
2227 {
2228     static assert(__traits(compiles, getLinkAttributes(TestAliasedString(null))));
2229 }
2230 
2231 /++
2232     Set the _attributes of the given file.
2233 
2234     For example, a programmatic equivalent of Unix's `chmod +x name`
2235     to make a file executable is
2236     `name.setAttributes(name.getAttributes | octal!700)`.
2237 
2238     Params:
2239         name = the file _name
2240         attributes = the _attributes to set the file to
2241 
2242     Throws:
2243         $(LREF FileException) if the given file does not exist.
2244  +/
2245 void setAttributes(R)(R name, uint attributes)
2246 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2247 {
2248     version (Windows)
2249     {
2250         auto namez = name.tempCString!FSChar();
2251         static auto trustedSetFileAttributesW(scope const(FSChar)* namez, uint dwFileAttributes) @trusted
2252         {
2253             return SetFileAttributesW(namez, dwFileAttributes);
2254         }
2255         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2256             alias names = name;
2257         else
2258             string names = null;
2259         cenforce(trustedSetFileAttributesW(namez, attributes), names, namez);
2260     }
2261     else version (Posix)
2262     {
2263         auto namez = name.tempCString!FSChar();
2264         static auto trustedChmod(scope const(FSChar)* namez, mode_t mode) @trusted
2265         {
2266             return chmod(namez, mode);
2267         }
2268         assert(attributes <= mode_t.max);
2269         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2270             alias names = name;
2271         else
2272             string names = null;
2273         cenforce(!trustedChmod(namez, cast(mode_t) attributes), names, namez);
2274     }
2275 }
2276 
2277 /// ditto
2278 void setAttributes(R)(auto ref R name, uint attributes)
2279 if (isConvertibleToString!R)
2280 {
2281     return setAttributes!(StringTypeOf!R)(name, attributes);
2282 }
2283 
2284 @safe unittest
2285 {
2286     static assert(__traits(compiles, setAttributes(TestAliasedString(null), 0)));
2287 }
2288 
2289 /// setAttributes with a file
2290 @safe unittest
2291 {
2292     import std.exception : assertThrown;
2293     import std.conv : octal;
2294 
2295     auto f = deleteme ~ "file";
2296     version (Posix)
2297     {
2298         scope(exit) f.remove;
2299 
2300         assert(!f.exists);
2301         assertThrown!FileException(f.setAttributes(octal!777));
2302 
2303         f.write(".");
2304         auto attributes = f.getAttributes;
2305         assert(!attributes.attrIsDir);
2306         assert(attributes.attrIsFile);
2307 
2308         f.setAttributes(octal!777);
2309         attributes = f.getAttributes;
2310 
2311         assert((attributes & 1023) == octal!777);
2312     }
2313 }
2314 
2315 /// setAttributes with a directory
2316 @safe unittest
2317 {
2318     import std.exception : assertThrown;
2319     import std.conv : octal;
2320 
2321     auto dir = deleteme ~ "dir";
2322     version (Posix)
2323     {
2324         scope(exit) dir.rmdir;
2325 
2326         assert(!dir.exists);
2327         assertThrown!FileException(dir.setAttributes(octal!777));
2328 
2329         dir.mkdir;
2330         auto attributes = dir.getAttributes;
2331         assert(attributes.attrIsDir);
2332         assert(!attributes.attrIsFile);
2333 
2334         dir.setAttributes(octal!777);
2335         attributes = dir.getAttributes;
2336 
2337         assert((attributes & 1023) == octal!777);
2338     }
2339 }
2340 
2341 /++
2342     Returns whether the given file is a directory.
2343 
2344     Params:
2345         name = The path to the file.
2346 
2347     Returns:
2348         true if name specifies a directory
2349 
2350     Throws:
2351         $(LREF FileException) if the given file does not exist.
2352   +/
2353 @property bool isDir(R)(R name)
2354 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2355 {
2356     version (Windows)
2357     {
2358         return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) != 0;
2359     }
2360     else version (Posix)
2361     {
2362         return (getAttributes(name) & S_IFMT) == S_IFDIR;
2363     }
2364 }
2365 
2366 /// ditto
2367 @property bool isDir(R)(auto ref R name)
2368 if (isConvertibleToString!R)
2369 {
2370     return name.isDir!(StringTypeOf!R);
2371 }
2372 
2373 ///
2374 
2375 @safe unittest
2376 {
2377     import std.exception : assertThrown;
2378 
2379     auto dir = deleteme ~ "dir";
2380     auto f = deleteme ~ "f";
2381     scope(exit) dir.rmdir, f.remove;
2382 
2383     assert(!dir.exists);
2384     assertThrown!FileException(dir.isDir);
2385 
2386     dir.mkdir;
2387     assert(dir.isDir);
2388 
2389     f.write(".");
2390     assert(!f.isDir);
2391 }
2392 
2393 @safe unittest
2394 {
2395     static assert(__traits(compiles, TestAliasedString(null).isDir));
2396 }
2397 
2398 @safe unittest
2399 {
2400     version (Windows)
2401     {
2402         if ("C:\\Program Files\\".exists)
2403             assert("C:\\Program Files\\".isDir);
2404 
2405         if ("C:\\Windows\\system.ini".exists)
2406             assert(!"C:\\Windows\\system.ini".isDir);
2407     }
2408     else version (Posix)
2409     {
2410         if (system_directory.exists)
2411             assert(system_directory.isDir);
2412 
2413         if (system_file.exists)
2414             assert(!system_file.isDir);
2415     }
2416 }
2417 
2418 @safe unittest
2419 {
2420     version (Windows)
2421         enum dir = "C:\\Program Files\\";
2422     else version (Posix)
2423         enum dir = system_directory;
2424 
2425     if (dir.exists)
2426     {
2427         DirEntry de = DirEntry(dir);
2428         assert(de.isDir);
2429         assert(DirEntry(dir).isDir);
2430     }
2431 }
2432 
2433 /++
2434     Returns whether the given file _attributes are for a directory.
2435 
2436     Params:
2437         attributes = The file _attributes.
2438 
2439     Returns:
2440         true if attributes specifies a directory
2441 +/
2442 bool attrIsDir(uint attributes) @safe pure nothrow @nogc
2443 {
2444     version (Windows)
2445     {
2446         return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
2447     }
2448     else version (Posix)
2449     {
2450         return (attributes & S_IFMT) == S_IFDIR;
2451     }
2452 }
2453 
2454 ///
2455 @safe unittest
2456 {
2457     import std.exception : assertThrown;
2458 
2459     auto dir = deleteme ~ "dir";
2460     auto f = deleteme ~ "f";
2461     scope(exit) dir.rmdir, f.remove;
2462 
2463     assert(!dir.exists);
2464     assertThrown!FileException(dir.getAttributes.attrIsDir);
2465 
2466     dir.mkdir;
2467     assert(dir.isDir);
2468     assert(dir.getAttributes.attrIsDir);
2469 
2470     f.write(".");
2471     assert(!f.isDir);
2472     assert(!f.getAttributes.attrIsDir);
2473 }
2474 
2475 @safe unittest
2476 {
2477     version (Windows)
2478     {
2479         if ("C:\\Program Files\\".exists)
2480         {
2481             assert(attrIsDir(getAttributes("C:\\Program Files\\")));
2482             assert(attrIsDir(getLinkAttributes("C:\\Program Files\\")));
2483         }
2484 
2485         if ("C:\\Windows\\system.ini".exists)
2486         {
2487             assert(!attrIsDir(getAttributes("C:\\Windows\\system.ini")));
2488             assert(!attrIsDir(getLinkAttributes("C:\\Windows\\system.ini")));
2489         }
2490     }
2491     else version (Posix)
2492     {
2493         if (system_directory.exists)
2494         {
2495             assert(attrIsDir(getAttributes(system_directory)));
2496             assert(attrIsDir(getLinkAttributes(system_directory)));
2497         }
2498 
2499         if (system_file.exists)
2500         {
2501             assert(!attrIsDir(getAttributes(system_file)));
2502             assert(!attrIsDir(getLinkAttributes(system_file)));
2503         }
2504     }
2505 }
2506 
2507 
2508 /++
2509     Returns whether the given file (or directory) is a file.
2510 
2511     On Windows, if a file is not a directory, then it's a file. So,
2512     either `isFile` or `isDir` will return true for any given file.
2513 
2514     On POSIX systems, if `isFile` is `true`, that indicates that the file
2515     is a regular file (e.g. not a block not device). So, on POSIX systems, it's
2516     possible for both `isFile` and `isDir` to be `false` for a
2517     particular file (in which case, it's a special file). You can use
2518     `getAttributes` to get the attributes to figure out what type of special
2519     it is, or you can use `DirEntry` to get at its `statBuf`, which is the
2520     result from `stat`. In either case, see the man page for `stat` for
2521     more information.
2522 
2523     Params:
2524         name = The path to the file.
2525 
2526     Returns:
2527         true if name specifies a file
2528 
2529     Throws:
2530         $(LREF FileException) if the given file does not exist.
2531 +/
2532 @property bool isFile(R)(R name)
2533 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2534 {
2535     version (Windows)
2536         return !name.isDir;
2537     else version (Posix)
2538         return (getAttributes(name) & S_IFMT) == S_IFREG;
2539 }
2540 
2541 /// ditto
2542 @property bool isFile(R)(auto ref R name)
2543 if (isConvertibleToString!R)
2544 {
2545     return isFile!(StringTypeOf!R)(name);
2546 }
2547 
2548 ///
2549 @safe unittest
2550 {
2551     import std.exception : assertThrown;
2552 
2553     auto dir = deleteme ~ "dir";
2554     auto f = deleteme ~ "f";
2555     scope(exit) dir.rmdir, f.remove;
2556 
2557     dir.mkdir;
2558     assert(!dir.isFile);
2559 
2560     assert(!f.exists);
2561     assertThrown!FileException(f.isFile);
2562 
2563     f.write(".");
2564     assert(f.isFile);
2565 }
2566 
2567 // https://issues.dlang.org/show_bug.cgi?id=15658
2568 @safe unittest
2569 {
2570     DirEntry e = DirEntry(".");
2571     static assert(is(typeof(isFile(e))));
2572 }
2573 
2574 @safe unittest
2575 {
2576     static assert(__traits(compiles, TestAliasedString(null).isFile));
2577 }
2578 
2579 @safe unittest
2580 {
2581     version (Windows)
2582     {
2583         if ("C:\\Program Files\\".exists)
2584             assert(!"C:\\Program Files\\".isFile);
2585 
2586         if ("C:\\Windows\\system.ini".exists)
2587             assert("C:\\Windows\\system.ini".isFile);
2588     }
2589     else version (Posix)
2590     {
2591         if (system_directory.exists)
2592             assert(!system_directory.isFile);
2593 
2594         if (system_file.exists)
2595             assert(system_file.isFile);
2596     }
2597 }
2598 
2599 
2600 /++
2601     Returns whether the given file _attributes are for a file.
2602 
2603     On Windows, if a file is not a directory, it's a file. So, either
2604     `attrIsFile` or `attrIsDir` will return `true` for the
2605     _attributes of any given file.
2606 
2607     On POSIX systems, if `attrIsFile` is `true`, that indicates that the
2608     file is a regular file (e.g. not a block not device). So, on POSIX systems,
2609     it's possible for both `attrIsFile` and `attrIsDir` to be `false`
2610     for a particular file (in which case, it's a special file). If a file is a
2611     special file, you can use the _attributes to check what type of special file
2612     it is (see the man page for `stat` for more information).
2613 
2614     Params:
2615         attributes = The file _attributes.
2616 
2617     Returns:
2618         true if the given file _attributes are for a file
2619 
2620 Example:
2621 --------------------
2622 assert(attrIsFile(getAttributes("/etc/fonts/fonts.conf")));
2623 assert(attrIsFile(getLinkAttributes("/etc/fonts/fonts.conf")));
2624 --------------------
2625   +/
2626 bool attrIsFile(uint attributes) @safe pure nothrow @nogc
2627 {
2628     version (Windows)
2629     {
2630         return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
2631     }
2632     else version (Posix)
2633     {
2634         return (attributes & S_IFMT) == S_IFREG;
2635     }
2636 }
2637 
2638 ///
2639 @safe unittest
2640 {
2641     import std.exception : assertThrown;
2642 
2643     auto dir = deleteme ~ "dir";
2644     auto f = deleteme ~ "f";
2645     scope(exit) dir.rmdir, f.remove;
2646 
2647     dir.mkdir;
2648     assert(!dir.isFile);
2649     assert(!dir.getAttributes.attrIsFile);
2650 
2651     assert(!f.exists);
2652     assertThrown!FileException(f.getAttributes.attrIsFile);
2653 
2654     f.write(".");
2655     assert(f.isFile);
2656     assert(f.getAttributes.attrIsFile);
2657 }
2658 
2659 @safe unittest
2660 {
2661     version (Windows)
2662     {
2663         if ("C:\\Program Files\\".exists)
2664         {
2665             assert(!attrIsFile(getAttributes("C:\\Program Files\\")));
2666             assert(!attrIsFile(getLinkAttributes("C:\\Program Files\\")));
2667         }
2668 
2669         if ("C:\\Windows\\system.ini".exists)
2670         {
2671             assert(attrIsFile(getAttributes("C:\\Windows\\system.ini")));
2672             assert(attrIsFile(getLinkAttributes("C:\\Windows\\system.ini")));
2673         }
2674     }
2675     else version (Posix)
2676     {
2677         if (system_directory.exists)
2678         {
2679             assert(!attrIsFile(getAttributes(system_directory)));
2680             assert(!attrIsFile(getLinkAttributes(system_directory)));
2681         }
2682 
2683         if (system_file.exists)
2684         {
2685             assert(attrIsFile(getAttributes(system_file)));
2686             assert(attrIsFile(getLinkAttributes(system_file)));
2687         }
2688     }
2689 }
2690 
2691 
2692 /++
2693     Returns whether the given file is a symbolic link.
2694 
2695     On Windows, returns `true` when the file is either a symbolic link or a
2696     junction point.
2697 
2698     Params:
2699         name = The path to the file.
2700 
2701     Returns:
2702         true if name is a symbolic link
2703 
2704     Throws:
2705         $(LREF FileException) if the given file does not exist.
2706   +/
2707 @property bool isSymlink(R)(R name)
2708 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2709 {
2710     version (Windows)
2711         return (getAttributes(name) & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2712     else version (Posix)
2713         return (getLinkAttributes(name) & S_IFMT) == S_IFLNK;
2714 }
2715 
2716 /// ditto
2717 @property bool isSymlink(R)(auto ref R name)
2718 if (isConvertibleToString!R)
2719 {
2720     return name.isSymlink!(StringTypeOf!R);
2721 }
2722 
2723 @safe unittest
2724 {
2725     static assert(__traits(compiles, TestAliasedString(null).isSymlink));
2726 }
2727 
2728 ///
2729 @safe unittest
2730 {
2731     import std.exception : assertThrown;
2732 
2733     auto source = deleteme ~ "source";
2734     auto target = deleteme ~ "target";
2735 
2736     assert(!source.exists);
2737     assertThrown!FileException(source.isSymlink);
2738 
2739     // symlinking isn't available on Windows
2740     version (Posix)
2741     {
2742         scope(exit) source.remove, target.remove;
2743 
2744         target.write("target");
2745         target.symlink(source);
2746         assert(source.readText == "target");
2747         assert(source.isSymlink);
2748         assert(source.getLinkAttributes.attrIsSymlink);
2749     }
2750 }
2751 
2752 @system unittest
2753 {
2754     version (Windows)
2755     {
2756         if ("C:\\Program Files\\".exists)
2757             assert(!"C:\\Program Files\\".isSymlink);
2758 
2759         if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
2760             assert("C:\\Documents and Settings\\".isSymlink);
2761 
2762         enum fakeSymFile = "C:\\Windows\\system.ini";
2763         if (fakeSymFile.exists)
2764         {
2765             assert(!fakeSymFile.isSymlink);
2766 
2767             assert(!fakeSymFile.isSymlink);
2768             assert(!attrIsSymlink(getAttributes(fakeSymFile)));
2769             assert(!attrIsSymlink(getLinkAttributes(fakeSymFile)));
2770 
2771             assert(attrIsFile(getAttributes(fakeSymFile)));
2772             assert(attrIsFile(getLinkAttributes(fakeSymFile)));
2773             assert(!attrIsDir(getAttributes(fakeSymFile)));
2774             assert(!attrIsDir(getLinkAttributes(fakeSymFile)));
2775 
2776             assert(getAttributes(fakeSymFile) == getLinkAttributes(fakeSymFile));
2777         }
2778     }
2779     else version (Posix)
2780     {
2781         if (system_directory.exists)
2782         {
2783             assert(!system_directory.isSymlink);
2784 
2785             immutable symfile = deleteme ~ "_slink\0";
2786             scope(exit) if (symfile.exists) symfile.remove();
2787 
2788             core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
2789 
2790             assert(symfile.isSymlink);
2791             assert(!attrIsSymlink(getAttributes(symfile)));
2792             assert(attrIsSymlink(getLinkAttributes(symfile)));
2793 
2794             assert(attrIsDir(getAttributes(symfile)));
2795             assert(!attrIsDir(getLinkAttributes(symfile)));
2796 
2797             assert(!attrIsFile(getAttributes(symfile)));
2798             assert(!attrIsFile(getLinkAttributes(symfile)));
2799         }
2800 
2801         if (system_file.exists)
2802         {
2803             assert(!system_file.isSymlink);
2804 
2805             immutable symfile = deleteme ~ "_slink\0";
2806             scope(exit) if (symfile.exists) symfile.remove();
2807 
2808             core.sys.posix.unistd.symlink(system_file, symfile.ptr);
2809 
2810             assert(symfile.isSymlink);
2811             assert(!attrIsSymlink(getAttributes(symfile)));
2812             assert(attrIsSymlink(getLinkAttributes(symfile)));
2813 
2814             assert(!attrIsDir(getAttributes(symfile)));
2815             assert(!attrIsDir(getLinkAttributes(symfile)));
2816 
2817             assert(attrIsFile(getAttributes(symfile)));
2818             assert(!attrIsFile(getLinkAttributes(symfile)));
2819         }
2820     }
2821 
2822     static assert(__traits(compiles, () @safe { return "dummy".isSymlink; }));
2823 }
2824 
2825 
2826 /++
2827     Returns whether the given file attributes are for a symbolic link.
2828 
2829     On Windows, return `true` when the file is either a symbolic link or a
2830     junction point.
2831 
2832     Params:
2833         attributes = The file attributes.
2834 
2835     Returns:
2836         true if attributes are for a symbolic link
2837 
2838 Example:
2839 --------------------
2840 core.sys.posix.unistd.symlink("/etc/fonts/fonts.conf", "/tmp/alink");
2841 
2842 assert(!getAttributes("/tmp/alink").isSymlink);
2843 assert(getLinkAttributes("/tmp/alink").isSymlink);
2844 --------------------
2845   +/
2846 bool attrIsSymlink(uint attributes) @safe pure nothrow @nogc
2847 {
2848     version (Windows)
2849         return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2850     else version (Posix)
2851         return (attributes & S_IFMT) == S_IFLNK;
2852 }
2853 
2854 ///
2855 @safe unittest
2856 {
2857     import std.exception : assertThrown;
2858 
2859     auto source = deleteme ~ "source";
2860     auto target = deleteme ~ "target";
2861 
2862     assert(!source.exists);
2863     assertThrown!FileException(source.getLinkAttributes.attrIsSymlink);
2864 
2865     // symlinking isn't available on Windows
2866     version (Posix)
2867     {
2868         scope(exit) source.remove, target.remove;
2869 
2870         target.write("target");
2871         target.symlink(source);
2872         assert(source.readText == "target");
2873         assert(source.isSymlink);
2874         assert(source.getLinkAttributes.attrIsSymlink);
2875     }
2876 }
2877 
2878 /**
2879 Change directory to `pathname`. Equivalent to `cd` on
2880 Windows and POSIX.
2881 
2882 Params:
2883     pathname = the directory to step into
2884 
2885 Throws: $(LREF FileException) on error.
2886  */
2887 void chdir(R)(R pathname)
2888 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2889 {
2890     // Place outside of @trusted block
2891     auto pathz = pathname.tempCString!FSChar();
2892 
2893     version (Windows)
2894     {
2895         static auto trustedChdir(scope const(FSChar)* pathz) @trusted
2896         {
2897             return SetCurrentDirectoryW(pathz);
2898         }
2899     }
2900     else version (Posix)
2901     {
2902         static auto trustedChdir(scope const(FSChar)* pathz) @trusted
2903         {
2904             return core.sys.posix.unistd.chdir(pathz) == 0;
2905         }
2906     }
2907     static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2908         alias pathStr = pathname;
2909     else
2910         string pathStr = null;
2911     cenforce(trustedChdir(pathz), pathStr, pathz);
2912 }
2913 
2914 /// ditto
2915 void chdir(R)(auto ref R pathname)
2916 if (isConvertibleToString!R)
2917 {
2918     return chdir!(StringTypeOf!R)(pathname);
2919 }
2920 
2921 ///
2922 @system unittest
2923 {
2924     import std.algorithm.comparison : equal;
2925     import std.algorithm.sorting : sort;
2926     import std.array : array;
2927     import std.path : buildPath;
2928 
2929     auto cwd = getcwd;
2930     auto dir = deleteme ~ "dir";
2931     dir.mkdir;
2932     scope(exit) cwd.chdir, dir.rmdirRecurse;
2933 
2934     dir.buildPath("a").write(".");
2935     dir.chdir; // step into dir
2936     "b".write(".");
2937     assert(dirEntries(".", SpanMode.shallow).array.sort.equal(
2938         [".".buildPath("a"), ".".buildPath("b")]
2939     ));
2940 }
2941 
2942 @safe unittest
2943 {
2944     static assert(__traits(compiles, chdir(TestAliasedString(null))));
2945 }
2946 
2947 /**
2948 Make a new directory `pathname`.
2949 
2950 Params:
2951     pathname = the path of the directory to make
2952 
2953 Throws:
2954     $(LREF FileException) on POSIX or $(LREF WindowsException) on Windows
2955     if an error occured.
2956  */
2957 void mkdir(R)(R pathname)
2958 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2959 {
2960     // Place outside of @trusted block
2961     const pathz = pathname.tempCString!FSChar();
2962 
2963     version (Windows)
2964     {
2965         static auto trustedCreateDirectoryW(scope const(FSChar)* pathz) @trusted
2966         {
2967             return CreateDirectoryW(pathz, null);
2968         }
2969         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2970             alias pathStr = pathname;
2971         else
2972             string pathStr = null;
2973         wenforce(trustedCreateDirectoryW(pathz), pathStr, pathz);
2974     }
2975     else version (Posix)
2976     {
2977         import std.conv : octal;
2978 
2979         static auto trustedMkdir(scope const(FSChar)* pathz, mode_t mode) @trusted
2980         {
2981             return core.sys.posix.sys.stat.mkdir(pathz, mode);
2982         }
2983         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2984             alias pathStr = pathname;
2985         else
2986             string pathStr = null;
2987         cenforce(trustedMkdir(pathz, octal!777) == 0, pathStr, pathz);
2988     }
2989 }
2990 
2991 /// ditto
2992 void mkdir(R)(auto ref R pathname)
2993 if (isConvertibleToString!R)
2994 {
2995     return mkdir!(StringTypeOf!R)(pathname);
2996 }
2997 
2998 @safe unittest
2999 {
3000     import std.file : mkdir;
3001     static assert(__traits(compiles, mkdir(TestAliasedString(null))));
3002 }
3003 
3004 ///
3005 @safe unittest
3006 {
3007     import std.file : mkdir;
3008 
3009     auto dir = deleteme ~ "dir";
3010     scope(exit) dir.rmdir;
3011 
3012     dir.mkdir;
3013     assert(dir.exists);
3014 }
3015 
3016 ///
3017 @safe unittest
3018 {
3019     import std.exception : assertThrown;
3020     assertThrown("a/b/c/d/e".mkdir);
3021 }
3022 
3023 // Same as mkdir but ignores "already exists" errors.
3024 // Returns: "true" if the directory was created,
3025 //   "false" if it already existed.
3026 private bool ensureDirExists()(scope const(char)[] pathname)
3027 {
3028     import std.exception : enforce;
3029     const pathz = pathname.tempCString!FSChar();
3030 
3031     version (Windows)
3032     {
3033         if (() @trusted { return CreateDirectoryW(pathz, null); }())
3034             return true;
3035         cenforce(GetLastError() == ERROR_ALREADY_EXISTS, pathname.idup);
3036     }
3037     else version (Posix)
3038     {
3039         import std.conv : octal;
3040 
3041         if (() @trusted { return core.sys.posix.sys.stat.mkdir(pathz, octal!777); }() == 0)
3042             return true;
3043         cenforce(errno == EEXIST || errno == EISDIR, pathname);
3044     }
3045     enforce(pathname.isDir, new FileException(pathname.idup));
3046     return false;
3047 }
3048 
3049 /**
3050 Make directory and all parent directories as needed.
3051 
3052 Does nothing if the directory specified by
3053 `pathname` already exists.
3054 
3055 Params:
3056     pathname = the full path of the directory to create
3057 
3058 Throws: $(LREF FileException) on error.
3059  */
3060 void mkdirRecurse(scope const(char)[] pathname) @safe
3061 {
3062     import std.path : dirName, baseName;
3063 
3064     const left = dirName(pathname);
3065     if (left.length != pathname.length && !exists(left))
3066     {
3067         mkdirRecurse(left);
3068     }
3069     if (!baseName(pathname).empty)
3070     {
3071         ensureDirExists(pathname);
3072     }
3073 }
3074 
3075 ///
3076 @safe unittest
3077 {
3078     import std.path : buildPath;
3079 
3080     auto dir = deleteme ~ "dir";
3081     scope(exit) dir.rmdirRecurse;
3082 
3083     dir.mkdir;
3084     assert(dir.exists);
3085     dir.mkdirRecurse; // does nothing
3086 
3087     // creates all parent directories as needed
3088     auto nested = dir.buildPath("a", "b", "c");
3089     nested.mkdirRecurse;
3090     assert(nested.exists);
3091 }
3092 
3093 ///
3094 @safe unittest
3095 {
3096     import std.exception : assertThrown;
3097 
3098     scope(exit) deleteme.remove;
3099     deleteme.write("a");
3100 
3101     // cannot make directory as it's already a file
3102     assertThrown!FileException(deleteme.mkdirRecurse);
3103 }
3104 
3105 @safe unittest
3106 {
3107     import std.exception : assertThrown;
3108     {
3109         import std.path : buildPath, buildNormalizedPath;
3110 
3111         immutable basepath = deleteme ~ "_dir";
3112         scope(exit) () @trusted { rmdirRecurse(basepath); }();
3113 
3114         auto path = buildPath(basepath, "a", "..", "b");
3115         mkdirRecurse(path);
3116         path = path.buildNormalizedPath;
3117         assert(path.isDir);
3118 
3119         path = buildPath(basepath, "c");
3120         write(path, "");
3121         assertThrown!FileException(mkdirRecurse(path));
3122 
3123         path = buildPath(basepath, "d");
3124         mkdirRecurse(path);
3125         mkdirRecurse(path); // should not throw
3126     }
3127 
3128     version (Windows)
3129     {
3130         assertThrown!FileException(mkdirRecurse(`1:\foobar`));
3131     }
3132 
3133     // https://issues.dlang.org/show_bug.cgi?id=3570
3134     {
3135         immutable basepath = deleteme ~ "_dir";
3136         version (Windows)
3137         {
3138             immutable path = basepath ~ "\\fake\\here\\";
3139         }
3140         else version (Posix)
3141         {
3142             immutable path = basepath ~ `/fake/here/`;
3143         }
3144 
3145         mkdirRecurse(path);
3146         assert(basepath.exists && basepath.isDir);
3147         scope(exit) () @trusted { rmdirRecurse(basepath); }();
3148         assert(path.exists && path.isDir);
3149     }
3150 }
3151 
3152 /****************************************************
3153 Remove directory `pathname`.
3154 
3155 Params:
3156     pathname = Range or string specifying the directory name
3157 
3158 Throws: $(LREF FileException) on error.
3159  */
3160 void rmdir(R)(R pathname)
3161 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
3162 {
3163     // Place outside of @trusted block
3164     auto pathz = pathname.tempCString!FSChar();
3165 
3166     version (Windows)
3167     {
3168         static auto trustedRmdir(scope const(FSChar)* pathz) @trusted
3169         {
3170             return RemoveDirectoryW(pathz);
3171         }
3172     }
3173     else version (Posix)
3174     {
3175         static auto trustedRmdir(scope const(FSChar)* pathz) @trusted
3176         {
3177             return core.sys.posix.unistd.rmdir(pathz) == 0;
3178         }
3179     }
3180     static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
3181         alias pathStr = pathname;
3182     else
3183         string pathStr = null;
3184     cenforce(trustedRmdir(pathz), pathStr, pathz);
3185 }
3186 
3187 /// ditto
3188 void rmdir(R)(auto ref R pathname)
3189 if (isConvertibleToString!R)
3190 {
3191     rmdir!(StringTypeOf!R)(pathname);
3192 }
3193 
3194 @safe unittest
3195 {
3196     static assert(__traits(compiles, rmdir(TestAliasedString(null))));
3197 }
3198 
3199 ///
3200 @safe unittest
3201 {
3202     auto dir = deleteme ~ "dir";
3203 
3204     dir.mkdir;
3205     assert(dir.exists);
3206     dir.rmdir;
3207     assert(!dir.exists);
3208 }
3209 
3210 /++
3211     $(BLUE This function is POSIX-Only.)
3212 
3213     Creates a symbolic _link (_symlink).
3214 
3215     Params:
3216         original = The file that is being linked. This is the target path that's
3217             stored in the _symlink. A relative path is relative to the created
3218             _symlink.
3219         link = The _symlink to create. A relative path is relative to the
3220             current working directory.
3221 
3222     Throws:
3223         $(LREF FileException) on error (which includes if the _symlink already
3224         exists).
3225   +/
3226 version (StdDdoc) void symlink(RO, RL)(RO original, RL link)
3227 if ((isSomeFiniteCharInputRange!RO || isConvertibleToString!RO) &&
3228     (isSomeFiniteCharInputRange!RL || isConvertibleToString!RL));
3229 else version (Posix) void symlink(RO, RL)(RO original, RL link)
3230 if ((isSomeFiniteCharInputRange!RO || isConvertibleToString!RO) &&
3231     (isSomeFiniteCharInputRange!RL || isConvertibleToString!RL))
3232 {
3233     static if (isConvertibleToString!RO || isConvertibleToString!RL)
3234     {
3235         import std.meta : staticMap;
3236         alias Types = staticMap!(convertToString, RO, RL);
3237         symlink!Types(original, link);
3238     }
3239     else
3240     {
3241         import std.conv : text;
3242         auto oz = original.tempCString();
3243         auto lz = link.tempCString();
3244         alias posixSymlink = core.sys.posix.unistd.symlink;
3245         immutable int result = () @trusted { return posixSymlink(oz, lz); } ();
3246         cenforce(result == 0, text(link));
3247     }
3248 }
3249 
3250 version (Posix) @safe unittest
3251 {
3252     if (system_directory.exists)
3253     {
3254         immutable symfile = deleteme ~ "_slink\0";
3255         scope(exit) if (symfile.exists) symfile.remove();
3256 
3257         symlink(system_directory, symfile);
3258 
3259         assert(symfile.exists);
3260         assert(symfile.isSymlink);
3261         assert(!attrIsSymlink(getAttributes(symfile)));
3262         assert(attrIsSymlink(getLinkAttributes(symfile)));
3263 
3264         assert(attrIsDir(getAttributes(symfile)));
3265         assert(!attrIsDir(getLinkAttributes(symfile)));
3266 
3267         assert(!attrIsFile(getAttributes(symfile)));
3268         assert(!attrIsFile(getLinkAttributes(symfile)));
3269     }
3270 
3271     if (system_file.exists)
3272     {
3273         assert(!system_file.isSymlink);
3274 
3275         immutable symfile = deleteme ~ "_slink\0";
3276         scope(exit) if (symfile.exists) symfile.remove();
3277 
3278         symlink(system_file, symfile);
3279 
3280         assert(symfile.exists);
3281         assert(symfile.isSymlink);
3282         assert(!attrIsSymlink(getAttributes(symfile)));
3283         assert(attrIsSymlink(getLinkAttributes(symfile)));
3284 
3285         assert(!attrIsDir(getAttributes(symfile)));
3286         assert(!attrIsDir(getLinkAttributes(symfile)));
3287 
3288         assert(attrIsFile(getAttributes(symfile)));
3289         assert(!attrIsFile(getLinkAttributes(symfile)));
3290     }
3291 }
3292 
3293 version (Posix) @safe unittest
3294 {
3295     static assert(__traits(compiles,
3296         symlink(TestAliasedString(null), TestAliasedString(null))));
3297 }
3298 
3299 
3300 /++
3301     $(BLUE This function is POSIX-Only.)
3302 
3303     Returns the path to the file pointed to by a symlink. Note that the
3304     path could be either relative or absolute depending on the symlink.
3305     If the path is relative, it's relative to the symlink, not the current
3306     working directory.
3307 
3308     Throws:
3309         $(LREF FileException) on error.
3310   +/
3311 version (StdDdoc) string readLink(R)(R link)
3312 if (isSomeFiniteCharInputRange!R || isConvertibleToString!R);
3313 else version (Posix) string readLink(R)(R link)
3314 if (isSomeFiniteCharInputRange!R || isConvertibleToString!R)
3315 {
3316     static if (isConvertibleToString!R)
3317     {
3318         return readLink!(convertToString!R)(link);
3319     }
3320     else
3321     {
3322         import std.conv : to;
3323         import std.exception : assumeUnique;
3324         alias posixReadlink = core.sys.posix.unistd.readlink;
3325         enum bufferLen = 2048;
3326         enum maxCodeUnits = 6;
3327         char[bufferLen] buffer;
3328         const linkz = link.tempCString();
3329         auto size = () @trusted {
3330             return posixReadlink(linkz, buffer.ptr, buffer.length);
3331         } ();
3332         cenforce(size != -1, to!string(link));
3333 
3334         if (size <= bufferLen - maxCodeUnits)
3335             return to!string(buffer[0 .. size]);
3336 
3337         auto dynamicBuffer = new char[](bufferLen * 3 / 2);
3338 
3339         foreach (i; 0 .. 10)
3340         {
3341             size = () @trusted {
3342                 return posixReadlink(linkz, dynamicBuffer.ptr,
3343                     dynamicBuffer.length);
3344             } ();
3345             cenforce(size != -1, to!string(link));
3346 
3347             if (size <= dynamicBuffer.length - maxCodeUnits)
3348             {
3349                 dynamicBuffer.length = size;
3350                 return () @trusted {
3351                     return assumeUnique(dynamicBuffer);
3352                 } ();
3353             }
3354 
3355             dynamicBuffer.length = dynamicBuffer.length * 3 / 2;
3356         }
3357 
3358         throw new FileException(to!string(link), "Path is too long to read.");
3359     }
3360 }
3361 
3362 version (Posix) @safe unittest
3363 {
3364     import std.exception : assertThrown;
3365     import std.string;
3366 
3367     foreach (file; [system_directory, system_file])
3368     {
3369         if (file.exists)
3370         {
3371             immutable symfile = deleteme ~ "_slink\0";
3372             scope(exit) if (symfile.exists) symfile.remove();
3373 
3374             symlink(file, symfile);
3375             assert(readLink(symfile) == file, format("Failed file: %s", file));
3376         }
3377     }
3378 
3379     assertThrown!FileException(readLink("/doesnotexist"));
3380 }
3381 
3382 version (Posix) @safe unittest
3383 {
3384     static assert(__traits(compiles, readLink(TestAliasedString("foo"))));
3385 }
3386 
3387 version (Posix) @system unittest // input range of dchars
3388 {
3389     mkdirRecurse(deleteme);
3390     scope(exit) if (deleteme.exists) rmdirRecurse(deleteme);
3391     write(deleteme ~ "/f", "");
3392     import std.range.interfaces : InputRange, inputRangeObject;
3393     import std.utf : byChar;
3394     immutable string link = deleteme ~ "/l";
3395     symlink("f", link);
3396     InputRange!(ElementType!string) linkr = inputRangeObject(link);
3397     alias R = typeof(linkr);
3398     static assert(isInputRange!R);
3399     static assert(!isForwardRange!R);
3400     assert(readLink(linkr) == "f");
3401 }
3402 
3403 
3404 /****************************************************
3405  * Get the current working directory.
3406  * Throws: $(LREF FileException) on error.
3407  */
3408 version (Windows) string getcwd() @trusted
3409 {
3410     import std.conv : to;
3411     import std.checkedint : checked;
3412     /* GetCurrentDirectory's return value:
3413         1. function succeeds: the number of characters that are written to
3414     the buffer, not including the terminating null character.
3415         2. function fails: zero
3416         3. the buffer (lpBuffer) is not large enough: the required size of
3417     the buffer, in characters, including the null-terminating character.
3418     */
3419     version (StdUnittest)
3420         enum BUF_SIZE = 10;     // trigger reallocation code
3421     else
3422         enum BUF_SIZE = 4096;   // enough for most common case
3423     wchar[BUF_SIZE] buffW = void;
3424     immutable n = cenforce(GetCurrentDirectoryW(to!DWORD(buffW.length), buffW.ptr),
3425             "getcwd");
3426     // we can do it because toUTFX always produces a fresh string
3427     if (n < buffW.length)
3428     {
3429         return buffW[0 .. n].to!string;
3430     }
3431     else //staticBuff isn't enough
3432     {
3433         auto cn = checked(n);
3434         auto ptr = cast(wchar*) malloc((cn * wchar.sizeof).get);
3435         scope(exit) free(ptr);
3436         immutable n2 = GetCurrentDirectoryW(cn.get, ptr);
3437         cenforce(n2 && n2 < cn, "getcwd");
3438         return ptr[0 .. n2].to!string;
3439     }
3440 }
3441 else version (Solaris) string getcwd() @trusted
3442 {
3443     /* BUF_SIZE >= PATH_MAX */
3444     enum BUF_SIZE = 4096;
3445     /* The user should be able to specify any size buffer > 0 */
3446     auto p = cenforce(core.sys.posix.unistd.getcwd(null, BUF_SIZE),
3447             "cannot get cwd");
3448     scope(exit) core.stdc.stdlib.free(p);
3449     return p[0 .. core.stdc..string.strlen(p)].idup;
3450 }
3451 else version (Posix) string getcwd() @trusted
3452 {
3453     auto p = cenforce(core.sys.posix.unistd.getcwd(null, 0),
3454             "cannot get cwd");
3455     scope(exit) core.stdc.stdlib.free(p);
3456     return p[0 .. core.stdc..string.strlen(p)].idup;
3457 }
3458 
3459 ///
3460 @safe unittest
3461 {
3462     auto s = getcwd();
3463     assert(s.length);
3464 }
3465 
3466 /**
3467  * Returns the full path of the current executable.
3468  *
3469  * Returns:
3470  *     The path of the executable as a `string`.
3471  *
3472  * Throws:
3473  * $(REF1 Exception, object)
3474  */
3475 @trusted string thisExePath()
3476 {
3477     version (Darwin)
3478     {
3479         import core.sys.darwin.mach.dyld : _NSGetExecutablePath;
3480         import core.sys.posix.stdlib : realpath;
3481         import std.conv : to;
3482         import std.exception : errnoEnforce;
3483 
3484         uint size;
3485 
3486         _NSGetExecutablePath(null, &size); // get the length of the path
3487         auto buffer = new char[size];
3488         _NSGetExecutablePath(buffer.ptr, &size);
3489 
3490         auto absolutePath = realpath(buffer.ptr, null); // let the function allocate
3491 
3492         scope (exit)
3493         {
3494             if (absolutePath)
3495                 free(absolutePath);
3496         }
3497 
3498         errnoEnforce(absolutePath);
3499         return to!(string)(absolutePath);
3500     }
3501     else version (linux)
3502     {
3503         return readLink("/proc/self/exe");
3504     }
3505     else version (Windows)
3506     {
3507         import std.conv : to;
3508         import std.exception : enforce;
3509 
3510         wchar[MAX_PATH] buf;
3511         wchar[] buffer = buf[];
3512 
3513         while (true)
3514         {
3515             auto len = GetModuleFileNameW(null, buffer.ptr, cast(DWORD) buffer.length);
3516             wenforce(len);
3517             if (len != buffer.length)
3518                 return to!(string)(buffer[0 .. len]);
3519             buffer.length *= 2;
3520         }
3521     }
3522     else version (DragonFlyBSD)
3523     {
3524         import core.sys.dragonflybsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME;
3525         import std.exception : errnoEnforce, assumeUnique;
3526 
3527         int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
3528         size_t len;
3529 
3530         auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
3531         errnoEnforce(result == 0);
3532 
3533         auto buffer = new char[len - 1];
3534         result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
3535         errnoEnforce(result == 0);
3536 
3537         return buffer.assumeUnique;
3538     }
3539     else version (FreeBSD)
3540     {
3541         import core.sys.freebsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME;
3542         import std.exception : errnoEnforce, assumeUnique;
3543 
3544         int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
3545         size_t len;
3546 
3547         auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
3548         errnoEnforce(result == 0);
3549 
3550         auto buffer = new char[len - 1];
3551         result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
3552         errnoEnforce(result == 0);
3553 
3554         return buffer.assumeUnique;
3555     }
3556     else version (NetBSD)
3557     {
3558         import core.sys.netbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_PATHNAME;
3559         import std.exception : errnoEnforce, assumeUnique;
3560 
3561         int[4] mib = [CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME];
3562         size_t len;
3563 
3564         auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
3565         errnoEnforce(result == 0);
3566 
3567         auto buffer = new char[len - 1];
3568         result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
3569         errnoEnforce(result == 0);
3570 
3571         return buffer.assumeUnique;
3572     }
3573     else version (OpenBSD)
3574     {
3575         import core.sys.openbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_ARGV;
3576         import core.sys.posix.unistd : getpid;
3577         import std.conv : to;
3578         import std.exception : enforce, errnoEnforce;
3579         import std.process : searchPathFor;
3580 
3581         int[4] mib = [CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV];
3582         size_t len;
3583 
3584         auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0);
3585         errnoEnforce(result == 0);
3586 
3587         auto argv = new char*[len - 1];
3588         result = sysctl(mib.ptr, mib.length, argv.ptr, &len, null, 0);
3589         errnoEnforce(result == 0);
3590 
3591         auto argv0 = argv[0];
3592         if (*argv0 == '/' || *argv0 == '.')
3593         {
3594             import core.sys.posix.stdlib : realpath;
3595             auto absolutePath = realpath(argv0, null);
3596             scope (exit)
3597             {
3598                 if (absolutePath)
3599                     free(absolutePath);
3600             }
3601             errnoEnforce(absolutePath);
3602             return to!(string)(absolutePath);
3603         }
3604         else
3605         {
3606             auto absolutePath = searchPathFor(to!string(argv0));
3607             errnoEnforce(absolutePath);
3608             return absolutePath;
3609         }
3610     }
3611     else version (Solaris)
3612     {
3613         import core.sys.posix.unistd : getpid;
3614         import std.string : format;
3615 
3616         // Only Solaris 10 and later
3617         return readLink(format("/proc/%d/path/a.out", getpid()));
3618     }
3619     else version (Hurd)
3620     {
3621         return readLink("/proc/self/exe");
3622     }
3623     else
3624         static assert(0, "thisExePath is not supported on this platform");
3625 }
3626 
3627 ///
3628 @safe unittest
3629 {
3630     import std.path : isAbsolute;
3631     auto path = thisExePath();
3632 
3633     assert(path.exists);
3634     assert(path.isAbsolute);
3635     assert(path.isFile);
3636 }
3637 
3638 version (StdDdoc)
3639 {
3640     /++
3641         Info on a file, similar to what you'd get from stat on a POSIX system.
3642       +/
3643     struct DirEntry
3644     {
3645         @safe:
3646         /++
3647             Constructs a `DirEntry` for the given file (or directory).
3648 
3649             Params:
3650                 path = The file (or directory) to get a DirEntry for.
3651 
3652             Throws:
3653                 $(LREF FileException) if the file does not exist.
3654         +/
3655         this(return scope string path);
3656 
3657         version (Windows)
3658         {
3659             private this(string path, in WIN32_FIND_DATAW *fd);
3660         }
3661         else version (Posix)
3662         {
3663             private this(string path, core.sys.posix.dirent.dirent* fd);
3664         }
3665 
3666         /++
3667             Returns the path to the file represented by this `DirEntry`.
3668 
3669 Example:
3670 --------------------
3671 auto de1 = DirEntry("/etc/fonts/fonts.conf");
3672 assert(de1.name == "/etc/fonts/fonts.conf");
3673 
3674 auto de2 = DirEntry("/usr/share/include");
3675 assert(de2.name == "/usr/share/include");
3676 --------------------
3677           +/
3678         @property string name() const return scope;
3679 
3680 
3681         /++
3682             Returns whether the file represented by this `DirEntry` is a
3683             directory.
3684 
3685 Example:
3686 --------------------
3687 auto de1 = DirEntry("/etc/fonts/fonts.conf");
3688 assert(!de1.isDir);
3689 
3690 auto de2 = DirEntry("/usr/share/include");
3691 assert(de2.isDir);
3692 --------------------
3693           +/
3694         @property bool isDir() scope;
3695 
3696 
3697         /++
3698             Returns whether the file represented by this `DirEntry` is a file.
3699 
3700             On Windows, if a file is not a directory, then it's a file. So,
3701             either `isFile` or `isDir` will return `true`.
3702 
3703             On POSIX systems, if `isFile` is `true`, that indicates that
3704             the file is a regular file (e.g. not a block not device). So, on
3705             POSIX systems, it's possible for both `isFile` and `isDir` to
3706             be `false` for a particular file (in which case, it's a special
3707             file). You can use `attributes` or `statBuf` to get more
3708             information about a special file (see the stat man page for more
3709             details).
3710 
3711 Example:
3712 --------------------
3713 auto de1 = DirEntry("/etc/fonts/fonts.conf");
3714 assert(de1.isFile);
3715 
3716 auto de2 = DirEntry("/usr/share/include");
3717 assert(!de2.isFile);
3718 --------------------
3719           +/
3720         @property bool isFile() scope;
3721 
3722         /++
3723             Returns whether the file represented by this `DirEntry` is a
3724             symbolic link.
3725 
3726             On Windows, return `true` when the file is either a symbolic
3727             link or a junction point.
3728           +/
3729         @property bool isSymlink() scope;
3730 
3731         /++
3732             Returns the size of the file represented by this `DirEntry`
3733             in bytes.
3734           +/
3735         @property ulong size() scope;
3736 
3737         /++
3738             $(BLUE This function is Windows-Only.)
3739 
3740             Returns the creation time of the file represented by this
3741             `DirEntry`.
3742           +/
3743         @property SysTime timeCreated() const scope;
3744 
3745         /++
3746             Returns the time that the file represented by this `DirEntry` was
3747             last accessed.
3748 
3749             Note that many file systems do not update the access time for files
3750             (generally for performance reasons), so there's a good chance that
3751             `timeLastAccessed` will return the same value as
3752             `timeLastModified`.
3753           +/
3754         @property SysTime timeLastAccessed() scope;
3755 
3756         /++
3757             Returns the time that the file represented by this `DirEntry` was
3758             last modified.
3759           +/
3760         @property SysTime timeLastModified() scope;
3761 
3762         /++
3763             $(BLUE This function is POSIX-Only.)
3764 
3765             Returns the time that the file represented by this `DirEntry` was
3766             last changed (not only in contents, but also in permissions or ownership).
3767           +/
3768         @property SysTime timeStatusChanged() const scope;
3769 
3770         /++
3771             Returns the _attributes of the file represented by this `DirEntry`.
3772 
3773             Note that the file _attributes on Windows and POSIX systems are
3774             completely different. On, Windows, they're what is returned by
3775             `GetFileAttributes`
3776             $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, GetFileAttributes)
3777             Whereas, an POSIX systems, they're the `st_mode` value which is
3778             part of the `stat` struct gotten by calling `stat`.
3779 
3780             On POSIX systems, if the file represented by this `DirEntry` is a
3781             symbolic link, then _attributes are the _attributes of the file
3782             pointed to by the symbolic link.
3783           +/
3784         @property uint attributes() scope;
3785 
3786         /++
3787             On POSIX systems, if the file represented by this `DirEntry` is a
3788             symbolic link, then `linkAttributes` are the attributes of the
3789             symbolic link itself. Otherwise, `linkAttributes` is identical to
3790             `attributes`.
3791 
3792             On Windows, `linkAttributes` is identical to `attributes`. It
3793             exists on Windows so that you don't have to special-case code for
3794             Windows when dealing with symbolic links.
3795           +/
3796         @property uint linkAttributes() scope;
3797 
3798         version (Windows)
3799             alias stat_t = void*;
3800 
3801         /++
3802             $(BLUE This function is POSIX-Only.)
3803 
3804             The `stat` struct gotten from calling `stat`.
3805           +/
3806         @property stat_t statBuf() scope;
3807     }
3808 }
3809 else version (Windows)
3810 {
3811     struct DirEntry
3812     {
3813     @safe:
3814     public:
3815         alias name this;
3816 
3817         this(return scope string path)
3818         {
3819             import std.datetime.systime : FILETIMEToSysTime;
3820 
3821             if (!path.exists())
3822                 throw new FileException(path, "File does not exist");
3823 
3824             _name = path;
3825 
3826             with (getFileAttributesWin(path))
3827             {
3828                 _size = makeUlong(nFileSizeLow, nFileSizeHigh);
3829                 _timeCreated = FILETIMEToSysTime(&ftCreationTime);
3830                 _timeLastAccessed = FILETIMEToSysTime(&ftLastAccessTime);
3831                 _timeLastModified = FILETIMEToSysTime(&ftLastWriteTime);
3832                 _attributes = dwFileAttributes;
3833             }
3834         }
3835 
3836         private this(string path, WIN32_FIND_DATAW *fd) @trusted
3837         {
3838             import core.stdc.wchar_ : wcslen;
3839             import std.conv : to;
3840             import std.datetime.systime : FILETIMEToSysTime;
3841             import std.path : buildPath;
3842 
3843             fd.cFileName[$ - 1] = 0;
3844 
3845             size_t clength = wcslen(&fd.cFileName[0]);
3846             _name = buildPath(path, fd.cFileName[0 .. clength].to!string);
3847             _size = (cast(ulong) fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
3848             _timeCreated = FILETIMEToSysTime(&fd.ftCreationTime);
3849             _timeLastAccessed = FILETIMEToSysTime(&fd.ftLastAccessTime);
3850             _timeLastModified = FILETIMEToSysTime(&fd.ftLastWriteTime);
3851             _attributes = fd.dwFileAttributes;
3852         }
3853 
3854         @property string name() const pure nothrow return scope
3855         {
3856             return _name;
3857         }
3858 
3859         @property bool isDir() const pure nothrow scope
3860         {
3861             return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
3862         }
3863 
3864         @property bool isFile() const pure nothrow scope
3865         {
3866             //Are there no options in Windows other than directory and file?
3867             //If there are, then this probably isn't the best way to determine
3868             //whether this DirEntry is a file or not.
3869             return !isDir;
3870         }
3871 
3872         @property bool isSymlink() const pure nothrow scope
3873         {
3874             return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
3875         }
3876 
3877         @property ulong size() const pure nothrow scope
3878         {
3879             return _size;
3880         }
3881 
3882         @property SysTime timeCreated() const pure nothrow return scope
3883         {
3884             return cast(SysTime)_timeCreated;
3885         }
3886 
3887         @property SysTime timeLastAccessed() const pure nothrow return scope
3888         {
3889             return cast(SysTime)_timeLastAccessed;
3890         }
3891 
3892         @property SysTime timeLastModified() const pure nothrow return scope
3893         {
3894             return cast(SysTime)_timeLastModified;
3895         }
3896 
3897         @property uint attributes() const pure nothrow scope
3898         {
3899             return _attributes;
3900         }
3901 
3902         @property uint linkAttributes() const pure nothrow scope
3903         {
3904             return _attributes;
3905         }
3906 
3907     private:
3908         string _name; /// The file or directory represented by this DirEntry.
3909 
3910         SysTime _timeCreated;      /// The time when the file was created.
3911         SysTime _timeLastAccessed; /// The time when the file was last accessed.
3912         SysTime _timeLastModified; /// The time when the file was last modified.
3913 
3914         ulong _size;       /// The size of the file in bytes.
3915         uint  _attributes; /// The file attributes from WIN32_FIND_DATAW.
3916     }
3917 }
3918 else version (Posix)
3919 {
3920     struct DirEntry
3921     {
3922     @safe:
3923     public:
3924         alias name this;
3925 
3926         this(return scope string path)
3927         {
3928             if (!path.exists)
3929                 throw new FileException(path, "File does not exist");
3930 
3931             _name = path;
3932 
3933             _didLStat = false;
3934             _didStat = false;
3935             _dTypeSet = false;
3936         }
3937 
3938         private this(string path, core.sys.posix.dirent.dirent* fd) @safe
3939         {
3940             import std.path : buildPath;
3941 
3942             static if (is(typeof(fd.d_namlen)))
3943                 immutable len = fd.d_namlen;
3944             else
3945                 immutable len = (() @trusted => core.stdc..string.strlen(fd.d_name.ptr))();
3946 
3947             _name = buildPath(path, (() @trusted => fd.d_name.ptr[0 .. len])());
3948 
3949             _didLStat = false;
3950             _didStat = false;
3951 
3952             //fd_d_type doesn't work for all file systems,
3953             //in which case the result is DT_UNKOWN. But we
3954             //can determine the correct type from lstat, so
3955             //we'll only set the dtype here if we could
3956             //correctly determine it (not lstat in the case
3957             //of DT_UNKNOWN in case we don't ever actually
3958             //need the dtype, thus potentially avoiding the
3959             //cost of calling lstat).
3960             static if (__traits(compiles, fd.d_type != DT_UNKNOWN))
3961             {
3962                 if (fd.d_type != DT_UNKNOWN)
3963                 {
3964                     _dType = fd.d_type;
3965                     _dTypeSet = true;
3966                 }
3967                 else
3968                     _dTypeSet = false;
3969             }
3970             else
3971             {
3972                 // e.g. Solaris does not have the d_type member
3973                 _dTypeSet = false;
3974             }
3975         }
3976 
3977         @property string name() const pure nothrow return scope
3978         {
3979             return _name;
3980         }
3981 
3982         @property bool isDir() scope
3983         {
3984             _ensureStatOrLStatDone();
3985 
3986             return (_statBuf.st_mode & S_IFMT) == S_IFDIR;
3987         }
3988 
3989         @property bool isFile() scope
3990         {
3991             _ensureStatOrLStatDone();
3992 
3993             return (_statBuf.st_mode & S_IFMT) == S_IFREG;
3994         }
3995 
3996         @property bool isSymlink() scope
3997         {
3998             _ensureLStatDone();
3999 
4000             return (_lstatMode & S_IFMT) == S_IFLNK;
4001         }
4002 
4003         @property ulong size() scope
4004         {
4005             _ensureStatDone();
4006             return _statBuf.st_size;
4007         }
4008 
4009         @property SysTime timeStatusChanged() scope
4010         {
4011             _ensureStatDone();
4012 
4013             return statTimeToStdTime!'c'(_statBuf);
4014         }
4015 
4016         @property SysTime timeLastAccessed() scope
4017         {
4018             _ensureStatDone();
4019 
4020             return statTimeToStdTime!'a'(_statBuf);
4021         }
4022 
4023         @property SysTime timeLastModified() scope
4024         {
4025             _ensureStatDone();
4026 
4027             return statTimeToStdTime!'m'(_statBuf);
4028         }
4029 
4030         @property uint attributes() scope
4031         {
4032             _ensureStatDone();
4033 
4034             return _statBuf.st_mode;
4035         }
4036 
4037         @property uint linkAttributes() scope
4038         {
4039             _ensureLStatDone();
4040 
4041             return _lstatMode;
4042         }
4043 
4044         @property stat_t statBuf() scope
4045         {
4046             _ensureStatDone();
4047 
4048             return _statBuf;
4049         }
4050 
4051     private:
4052         /++
4053             This is to support lazy evaluation, because doing stat's is
4054             expensive and not always needed.
4055          +/
4056         void _ensureStatDone() @trusted scope
4057         {
4058             import std.exception : enforce;
4059 
4060             if (_didStat)
4061                 return;
4062 
4063             enforce(stat(_name.tempCString(), &_statBuf) == 0,
4064                     "Failed to stat file `" ~ _name ~ "'");
4065 
4066             _didStat = true;
4067         }
4068 
4069         /++
4070             This is to support lazy evaluation, because doing stat's is
4071             expensive and not always needed.
4072 
4073             Try both stat and lstat for isFile and isDir
4074             to detect broken symlinks.
4075          +/
4076         void _ensureStatOrLStatDone() @trusted scope
4077         {
4078             if (_didStat)
4079                 return;
4080 
4081             if (stat(_name.tempCString(), &_statBuf) != 0)
4082             {
4083                 _ensureLStatDone();
4084 
4085                 _statBuf = stat_t.init;
4086                 _statBuf.st_mode = S_IFLNK;
4087             }
4088             else
4089             {
4090                 _didStat = true;
4091             }
4092         }
4093 
4094         /++
4095             This is to support lazy evaluation, because doing stat's is
4096             expensive and not always needed.
4097          +/
4098         void _ensureLStatDone() @trusted scope
4099         {
4100             import std.exception : enforce;
4101 
4102             if (_didLStat)
4103                 return;
4104 
4105             stat_t statbuf = void;
4106             enforce(lstat(_name.tempCString(), &statbuf) == 0,
4107                 "Failed to stat file `" ~ _name ~ "'");
4108 
4109             _lstatMode = statbuf.st_mode;
4110 
4111             _dTypeSet = true;
4112             _didLStat = true;
4113         }
4114 
4115         string _name; /// The file or directory represented by this DirEntry.
4116 
4117         stat_t _statBuf = void;   /// The result of stat().
4118         uint  _lstatMode;         /// The stat mode from lstat().
4119         ubyte _dType;             /// The type of the file.
4120 
4121         bool _didLStat = false;   /// Whether lstat() has been called for this DirEntry.
4122         bool _didStat = false;    /// Whether stat() has been called for this DirEntry.
4123         bool _dTypeSet = false;   /// Whether the dType of the file has been set.
4124     }
4125 }
4126 
4127 @system unittest
4128 {
4129     version (Windows)
4130     {
4131         if ("C:\\Program Files\\".exists)
4132         {
4133             auto de = DirEntry("C:\\Program Files\\");
4134             assert(!de.isFile);
4135             assert(de.isDir);
4136             assert(!de.isSymlink);
4137         }
4138 
4139         if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
4140         {
4141             auto de = DirEntry("C:\\Documents and Settings\\");
4142             assert(de.isSymlink);
4143         }
4144 
4145         if ("C:\\Windows\\system.ini".exists)
4146         {
4147             auto de = DirEntry("C:\\Windows\\system.ini");
4148             assert(de.isFile);
4149             assert(!de.isDir);
4150             assert(!de.isSymlink);
4151         }
4152     }
4153     else version (Posix)
4154     {
4155         import std.exception : assertThrown;
4156 
4157         if (system_directory.exists)
4158         {
4159             {
4160                 auto de = DirEntry(system_directory);
4161                 assert(!de.isFile);
4162                 assert(de.isDir);
4163                 assert(!de.isSymlink);
4164             }
4165 
4166             immutable symfile = deleteme ~ "_slink\0";
4167             scope(exit) if (symfile.exists) symfile.remove();
4168 
4169             core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
4170 
4171             {
4172                 auto de = DirEntry(symfile);
4173                 assert(!de.isFile);
4174                 assert(de.isDir);
4175                 assert(de.isSymlink);
4176             }
4177 
4178             symfile.remove();
4179             core.sys.posix.unistd.symlink((deleteme ~ "_broken_symlink\0").ptr, symfile.ptr);
4180 
4181             {
4182                 // https://issues.dlang.org/show_bug.cgi?id=8298
4183                 DirEntry de = DirEntry(symfile);
4184 
4185                 assert(!de.isFile);
4186                 assert(!de.isDir);
4187                 assert(de.isSymlink);
4188                 assertThrown(de.size);
4189                 assertThrown(de.timeStatusChanged);
4190                 assertThrown(de.timeLastAccessed);
4191                 assertThrown(de.timeLastModified);
4192                 assertThrown(de.attributes);
4193                 assertThrown(de.statBuf);
4194                 assert(symfile.exists);
4195                 symfile.remove();
4196             }
4197         }
4198 
4199         if (system_file.exists)
4200         {
4201             auto de = DirEntry(system_file);
4202             assert(de.isFile);
4203             assert(!de.isDir);
4204             assert(!de.isSymlink);
4205         }
4206     }
4207 }
4208 
4209 alias PreserveAttributes = Flag!"preserveAttributes";
4210 
4211 version (StdDdoc)
4212 {
4213     /// Defaults to `Yes.preserveAttributes` on Windows, and the opposite on all other platforms.
4214     PreserveAttributes preserveAttributesDefault;
4215 }
4216 else version (Windows)
4217 {
4218     enum preserveAttributesDefault = Yes.preserveAttributes;
4219 }
4220 else
4221 {
4222     enum preserveAttributesDefault = No.preserveAttributes;
4223 }
4224 
4225 /***************************************************
4226 Copy file `from` _to file `to`. File timestamps are preserved.
4227 File attributes are preserved, if `preserve` equals `Yes.preserveAttributes`.
4228 On Windows only `Yes.preserveAttributes` (the default on Windows) is supported.
4229 If the target file exists, it is overwritten.
4230 
4231 Params:
4232     from = string or range of characters representing the existing file name
4233     to = string or range of characters representing the target file name
4234     preserve = whether to _preserve the file attributes
4235 
4236 Throws: $(LREF FileException) on error.
4237  */
4238 void copy(RF, RT)(RF from, RT to, PreserveAttributes preserve = preserveAttributesDefault)
4239 if (isSomeFiniteCharInputRange!RF && !isConvertibleToString!RF &&
4240     isSomeFiniteCharInputRange!RT && !isConvertibleToString!RT)
4241 {
4242     // Place outside of @trusted block
4243     auto fromz = from.tempCString!FSChar();
4244     auto toz = to.tempCString!FSChar();
4245 
4246     static if (isNarrowString!RF && is(immutable ElementEncodingType!RF == immutable char))
4247         alias f = from;
4248     else
4249         enum string f = null;
4250 
4251     static if (isNarrowString!RT && is(immutable ElementEncodingType!RT == immutable char))
4252         alias t = to;
4253     else
4254         enum string t = null;
4255 
4256     copyImpl(f, t, fromz, toz, preserve);
4257 }
4258 
4259 /// ditto
4260 void copy(RF, RT)(auto ref RF from, auto ref RT to, PreserveAttributes preserve = preserveAttributesDefault)
4261 if (isConvertibleToString!RF || isConvertibleToString!RT)
4262 {
4263     import std.meta : staticMap;
4264     alias Types = staticMap!(convertToString, RF, RT);
4265     copy!Types(from, to, preserve);
4266 }
4267 
4268 ///
4269 @safe unittest
4270 {
4271     auto source = deleteme ~ "source";
4272     auto target = deleteme ~ "target";
4273     auto targetNonExistent = deleteme ~ "target2";
4274 
4275     scope(exit) source.remove, target.remove, targetNonExistent.remove;
4276 
4277     source.write("source");
4278     target.write("target");
4279 
4280     assert(target.readText == "target");
4281 
4282     source.copy(target);
4283     assert(target.readText == "source");
4284 
4285     source.copy(targetNonExistent);
4286     assert(targetNonExistent.readText == "source");
4287 }
4288 
4289 // https://issues.dlang.org/show_bug.cgi?id=15319
4290 @safe unittest
4291 {
4292     assert(__traits(compiles, copy("from.txt", "to.txt")));
4293 }
4294 
4295 private void copyImpl(scope const(char)[] f, scope const(char)[] t,
4296                       scope const(FSChar)* fromz, scope const(FSChar)* toz,
4297                       PreserveAttributes preserve) @trusted
4298 {
4299     version (Windows)
4300     {
4301         assert(preserve == Yes.preserveAttributes);
4302         immutable result = CopyFileW(fromz, toz, false);
4303         if (!result)
4304         {
4305             import core.stdc.wchar_ : wcslen;
4306             import std.conv : to;
4307             import std.format : format;
4308 
4309             /++
4310             Reference resources: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-copyfilew
4311             Because OS copyfilew handles both source and destination paths,
4312             the GetLastError does not accurately locate whether the error is for the source or destination.
4313             +/
4314             if (!f)
4315                 f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]);
4316             if (!t)
4317                 t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
4318 
4319             throw new FileException(format!"Copy from %s to %s"(f, t));
4320         }
4321     }
4322     else version (Posix)
4323     {
4324         static import core.stdc.stdio;
4325         import std.conv : to, octal;
4326 
4327         immutable fdr = core.sys.posix.fcntl.open(fromz, O_RDONLY);
4328         cenforce(fdr != -1, f, fromz);
4329         scope(exit) core.sys.posix.unistd.close(fdr);
4330 
4331         stat_t statbufr = void;
4332         cenforce(fstat(fdr, &statbufr) == 0, f, fromz);
4333         //cenforce(core.sys.posix.sys.stat.fstat(fdr, &statbufr) == 0, f, fromz);
4334 
4335         immutable fdw = core.sys.posix.fcntl.open(toz,
4336                 O_CREAT | O_WRONLY, octal!666);
4337         cenforce(fdw != -1, t, toz);
4338         {
4339             scope(failure) core.sys.posix.unistd.close(fdw);
4340 
4341             stat_t statbufw = void;
4342             cenforce(fstat(fdw, &statbufw) == 0, t, toz);
4343             if (statbufr.st_dev == statbufw.st_dev && statbufr.st_ino == statbufw.st_ino)
4344                 throw new FileException(t, "Source and destination are the same file");
4345         }
4346 
4347         scope(failure) core.stdc.stdio.remove(toz);
4348         {
4349             scope(failure) core.sys.posix.unistd.close(fdw);
4350             cenforce(ftruncate(fdw, 0) == 0, t, toz);
4351 
4352             auto BUFSIZ = 4096u * 16;
4353             auto buf = core.stdc.stdlib.malloc(BUFSIZ);
4354             if (!buf)
4355             {
4356                 BUFSIZ = 4096;
4357                 buf = core.stdc.stdlib.malloc(BUFSIZ);
4358                 if (!buf)
4359                 {
4360                     import core.exception : onOutOfMemoryError;
4361                     onOutOfMemoryError();
4362                 }
4363             }
4364             scope(exit) core.stdc.stdlib.free(buf);
4365 
4366             for (auto size = statbufr.st_size; size; )
4367             {
4368                 immutable toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size;
4369                 cenforce(
4370                     core.sys.posix.unistd.read(fdr, buf, toxfer) == toxfer
4371                     && core.sys.posix.unistd.write(fdw, buf, toxfer) == toxfer,
4372                     f, fromz);
4373                 assert(size >= toxfer);
4374                 size -= toxfer;
4375             }
4376             if (preserve)
4377                 cenforce(fchmod(fdw, to!mode_t(statbufr.st_mode)) == 0, f, fromz);
4378         }
4379 
4380         cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz);
4381 
4382         setTimesImpl(t, toz, statbufr.statTimeToStdTime!'a', statbufr.statTimeToStdTime!'m');
4383     }
4384 }
4385 
4386 // https://issues.dlang.org/show_bug.cgi?id=14817
4387 @safe unittest
4388 {
4389     import std.algorithm, std.file;
4390     auto t1 = deleteme, t2 = deleteme~"2";
4391     scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
4392     write(t1, "11");
4393     copy(t1, t2);
4394     assert(readText(t2) == "11");
4395     write(t1, "2");
4396     copy(t1, t2);
4397     assert(readText(t2) == "2");
4398 
4399     import std.utf : byChar;
4400     copy(t1.byChar, t2.byChar);
4401     assert(readText(t2.byChar) == "2");
4402 
4403 // https://issues.dlang.org/show_bug.cgi?id=20370
4404     version (Windows)
4405         assert(t1.timeLastModified == t2.timeLastModified);
4406     else static if (is(typeof(&utimensat)) || is(typeof(&setattrlist)))
4407         assert(t1.timeLastModified == t2.timeLastModified);
4408     else
4409         assert(abs(t1.timeLastModified - t2.timeLastModified) < dur!"usecs"(1));
4410 }
4411 
4412 // https://issues.dlang.org/show_bug.cgi?id=11434
4413 @safe version (Posix) @safe unittest
4414 {
4415     import std.conv : octal;
4416     auto t1 = deleteme, t2 = deleteme~"2";
4417     scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
4418     write(t1, "1");
4419     setAttributes(t1, octal!767);
4420     copy(t1, t2, Yes.preserveAttributes);
4421     assert(readText(t2) == "1");
4422     assert(getAttributes(t2) == octal!100767);
4423 }
4424 
4425 // https://issues.dlang.org/show_bug.cgi?id=15865
4426 @safe unittest
4427 {
4428     import std.exception : assertThrown;
4429     auto t = deleteme;
4430     write(t, "a");
4431     scope(exit) t.remove();
4432     assertThrown!FileException(copy(t, t));
4433     assert(readText(t) == "a");
4434 }
4435 
4436 // https://issues.dlang.org/show_bug.cgi?id=19834
4437 version (Windows) @safe unittest
4438 {
4439     import std.exception : collectException;
4440     import std.algorithm.searching : startsWith;
4441     import std.format : format;
4442 
4443     auto f = deleteme;
4444     auto t = f ~ "2";
4445     auto ex = collectException(copy(f, t));
4446     assert(ex.msg.startsWith(format!"Copy from %s to %s"(f, t)));
4447 }
4448 
4449 /++
4450     Remove directory and all of its content and subdirectories,
4451     recursively.
4452 
4453     Params:
4454         pathname = the path of the directory to completely remove
4455         de = The $(LREF DirEntry) to remove
4456 
4457     Throws:
4458         $(LREF FileException) if there is an error (including if the given
4459         file is not a directory).
4460  +/
4461 void rmdirRecurse(scope const(char)[] pathname) @safe
4462 {
4463     //No references to pathname will be kept after rmdirRecurse,
4464     //so the cast is safe
4465     rmdirRecurse(DirEntry((() @trusted => cast(string) pathname)()));
4466 }
4467 
4468 /// ditto
4469 void rmdirRecurse(ref scope DirEntry de) @safe
4470 {
4471     if (!de.isDir)
4472         throw new FileException(de.name, "Not a directory");
4473 
4474     if (de.isSymlink)
4475     {
4476         version (Windows)
4477             rmdir(de.name);
4478         else
4479             remove(de.name);
4480     }
4481     else
4482     {
4483         // dirEntries is @system without DIP1000 because it uses
4484         // a DirIterator with a SafeRefCounted variable, but here, no
4485         // references to the payload are escaped to the outside, so this should
4486         // be @trusted
4487         () @trusted {
4488             // all children, recursively depth-first
4489             foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false))
4490             {
4491                 attrIsDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name);
4492             }
4493         }();
4494 
4495         // the dir itself
4496         rmdir(de.name);
4497     }
4498 }
4499 ///ditto
4500 //Note, without this overload, passing an RValue DirEntry still works, but
4501 //actually fully reconstructs a DirEntry inside the
4502 //"rmdirRecurse(in char[] pathname)" implementation. That is needlessly
4503 //expensive.
4504 //A DirEntry is a bit big (72B), so keeping the "by ref" signature is desirable.
4505 void rmdirRecurse(scope DirEntry de) @safe
4506 {
4507     rmdirRecurse(de);
4508 }
4509 
4510 ///
4511 @system unittest
4512 {
4513     import std.path : buildPath;
4514 
4515     auto dir = deleteme.buildPath("a", "b", "c");
4516 
4517     dir.mkdirRecurse;
4518     assert(dir.exists);
4519 
4520     deleteme.rmdirRecurse;
4521     assert(!dir.exists);
4522     assert(!deleteme.exists);
4523 }
4524 
4525 version (Windows) @system unittest
4526 {
4527     import std.exception : enforce;
4528     auto d = deleteme ~ r".dir\a\b\c\d\e\f\g";
4529     mkdirRecurse(d);
4530     rmdirRecurse(deleteme ~ ".dir");
4531     enforce(!exists(deleteme ~ ".dir"));
4532 }
4533 
4534 version (Posix) @system unittest
4535 {
4536     import std.exception : enforce, collectException;
4537 
4538     collectException(rmdirRecurse(deleteme));
4539     auto d = deleteme~"/a/b/c/d/e/f/g";
4540     enforce(collectException(mkdir(d)));
4541     mkdirRecurse(d);
4542     core.sys.posix.unistd.symlink((deleteme~"/a/b/c\0").ptr,
4543             (deleteme~"/link\0").ptr);
4544     rmdirRecurse(deleteme~"/link");
4545     enforce(exists(d));
4546     rmdirRecurse(deleteme);
4547     enforce(!exists(deleteme));
4548 
4549     d = deleteme~"/a/b/c/d/e/f/g";
4550     mkdirRecurse(d);
4551     const linkTarget = deleteme ~ "/link";
4552     symlink(deleteme ~ "/a/b/c", linkTarget);
4553     rmdirRecurse(deleteme);
4554     enforce(!exists(deleteme));
4555 }
4556 
4557 @safe unittest
4558 {
4559     ubyte[] buf = new ubyte[10];
4560     buf[] = 3;
4561     string unit_file = deleteme ~ "-unittest_write.tmp";
4562     if (exists(unit_file)) remove(unit_file);
4563     write(unit_file, cast(void[]) buf);
4564     void[] buf2 = read(unit_file);
4565     assert(cast(void[]) buf == buf2);
4566 
4567     string unit2_file = deleteme ~ "-unittest_write2.tmp";
4568     copy(unit_file, unit2_file);
4569     buf2 = read(unit2_file);
4570     assert(cast(void[]) buf == buf2);
4571 
4572     remove(unit_file);
4573     assert(!exists(unit_file));
4574     remove(unit2_file);
4575     assert(!exists(unit2_file));
4576 }
4577 
4578 /**
4579  * Dictates directory spanning policy for $(D_PARAM dirEntries) (see below).
4580  */
4581 enum SpanMode
4582 {
4583     /** Only spans one directory. */
4584     shallow,
4585     /** Spans the directory in
4586      $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Post-order,
4587      _depth-first $(B post)-order), i.e. the content of any
4588      subdirectory is spanned before that subdirectory itself. Useful
4589      e.g. when recursively deleting files.  */
4590     depth,
4591     /** Spans the directory in
4592     $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Pre-order, depth-first
4593     $(B pre)-order), i.e. the content of any subdirectory is spanned
4594     right after that subdirectory itself.
4595 
4596     Note that `SpanMode.breadth` will not result in all directory
4597     members occurring before any subdirectory members, i.e. it is not
4598     _true
4599     $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search,
4600     _breadth-first traversal).
4601     */
4602     breadth,
4603 }
4604 
4605 ///
4606 @system unittest
4607 {
4608     import std.algorithm.comparison : equal;
4609     import std.algorithm.iteration : map;
4610     import std.algorithm.sorting : sort;
4611     import std.array : array;
4612     import std.path : buildPath, relativePath;
4613 
4614     auto root = deleteme ~ "root";
4615     scope(exit) root.rmdirRecurse;
4616     root.mkdir;
4617 
4618     root.buildPath("animals").mkdir;
4619     root.buildPath("animals", "cat").mkdir;
4620 
4621     alias removeRoot = (return scope e) => e.relativePath(root);
4622 
4623     assert(root.dirEntries(SpanMode.depth).map!removeRoot.equal(
4624         [buildPath("animals", "cat"), "animals"]));
4625 
4626     assert(root.dirEntries(SpanMode.breadth).map!removeRoot.equal(
4627         ["animals", buildPath("animals", "cat")]));
4628 
4629     root.buildPath("plants").mkdir;
4630 
4631     assert(root.dirEntries(SpanMode.shallow).array.sort.map!removeRoot.equal(
4632         ["animals", "plants"]));
4633 }
4634 
4635 private struct DirIteratorImpl
4636 {
4637   @safe:
4638     SpanMode _mode;
4639     // Whether we should follow symlinked directories while iterating.
4640     // It also indicates whether we should avoid functions which call
4641     // stat (since we should only need lstat in this case and it would
4642     // be more efficient to not call stat in addition to lstat).
4643     bool _followSymlink;
4644     DirEntry _cur;
4645     DirHandle[] _stack;
4646     DirEntry[] _stashed; //used in depth first mode
4647 
4648     //stack helpers
4649     void pushExtra(DirEntry de)
4650     {
4651         _stashed ~= de;
4652     }
4653 
4654     //ditto
4655     bool hasExtra()
4656     {
4657         return _stashed.length != 0;
4658     }
4659 
4660     //ditto
4661     DirEntry popExtra()
4662     {
4663         DirEntry de;
4664         de = _stashed[$-1];
4665         _stashed.popBack();
4666         return de;
4667     }
4668 
4669     version (Windows)
4670     {
4671         WIN32_FIND_DATAW _findinfo;
4672         struct DirHandle
4673         {
4674             string dirpath;
4675             HANDLE h;
4676         }
4677 
4678         bool stepIn(string directory) @safe
4679         {
4680             import std.path : chainPath;
4681             auto searchPattern = chainPath(directory, "*.*");
4682 
4683             static auto trustedFindFirstFileW(typeof(searchPattern) pattern, scope WIN32_FIND_DATAW* findinfo) @trusted
4684             {
4685                 return FindFirstFileW(pattern.tempCString!FSChar(), findinfo);
4686             }
4687 
4688             HANDLE h = trustedFindFirstFileW(searchPattern, &_findinfo);
4689             cenforce(h != INVALID_HANDLE_VALUE, directory);
4690             _stack ~= DirHandle(directory, h);
4691             return toNext(false, &_findinfo);
4692         }
4693 
4694         bool next()
4695         {
4696             if (_stack.length == 0)
4697                 return false;
4698             return toNext(true, &_findinfo);
4699         }
4700 
4701         bool toNext(bool fetch, scope WIN32_FIND_DATAW* findinfo) @trusted
4702         {
4703             import core.stdc.wchar_ : wcscmp;
4704 
4705             if (fetch)
4706             {
4707                 if (FindNextFileW(_stack[$-1].h, findinfo) == FALSE)
4708                 {
4709                     popDirStack();
4710                     return false;
4711                 }
4712             }
4713             while (wcscmp(&findinfo.cFileName[0], ".") == 0 ||
4714                    wcscmp(&findinfo.cFileName[0], "..") == 0)
4715                 if (FindNextFileW(_stack[$-1].h, findinfo) == FALSE)
4716                 {
4717                     popDirStack();
4718                     return false;
4719                 }
4720             _cur = DirEntry(_stack[$-1].dirpath, findinfo);
4721             return true;
4722         }
4723 
4724         void popDirStack() @trusted
4725         {
4726             assert(_stack.length != 0);
4727             FindClose(_stack[$-1].h);
4728             _stack.popBack();
4729         }
4730 
4731         void releaseDirStack() @trusted
4732         {
4733             foreach (d; _stack)
4734                 FindClose(d.h);
4735         }
4736 
4737         bool mayStepIn()
4738         {
4739             return _followSymlink ? _cur.isDir : _cur.isDir && !_cur.isSymlink;
4740         }
4741     }
4742     else version (Posix)
4743     {
4744         struct DirHandle
4745         {
4746             string dirpath;
4747             DIR*   h;
4748         }
4749 
4750         bool stepIn(string directory)
4751         {
4752             static auto trustedOpendir(string dir) @trusted
4753             {
4754                 return opendir(dir.tempCString());
4755             }
4756 
4757             auto h = directory.length ? trustedOpendir(directory) : trustedOpendir(".");
4758             cenforce(h, directory);
4759             _stack ~= (DirHandle(directory, h));
4760             return next();
4761         }
4762 
4763         bool next() @trusted
4764         {
4765             if (_stack.length == 0)
4766                 return false;
4767 
4768             for (dirent* fdata; (fdata = readdir(_stack[$-1].h)) != null; )
4769             {
4770                 // Skip "." and ".."
4771                 if (core.stdc..string.strcmp(&fdata.d_name[0], ".") &&
4772                     core.stdc..string.strcmp(&fdata.d_name[0], ".."))
4773                 {
4774                     _cur = DirEntry(_stack[$-1].dirpath, fdata);
4775                     return true;
4776                 }
4777             }
4778 
4779             popDirStack();
4780             return false;
4781         }
4782 
4783         void popDirStack() @trusted
4784         {
4785             assert(_stack.length != 0);
4786             closedir(_stack[$-1].h);
4787             _stack.popBack();
4788         }
4789 
4790         void releaseDirStack() @trusted
4791         {
4792             foreach (d; _stack)
4793                 closedir(d.h);
4794         }
4795 
4796         bool mayStepIn()
4797         {
4798             return _followSymlink ? _cur.isDir : attrIsDir(_cur.linkAttributes);
4799         }
4800     }
4801 
4802     this(R)(R pathname, SpanMode mode, bool followSymlink)
4803         if (isSomeFiniteCharInputRange!R)
4804     {
4805         _mode = mode;
4806         _followSymlink = followSymlink;
4807 
4808         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
4809             alias pathnameStr = pathname;
4810         else
4811         {
4812             import std.array : array;
4813             string pathnameStr = pathname.array;
4814         }
4815         if (stepIn(pathnameStr))
4816         {
4817             if (_mode == SpanMode.depth)
4818                 while (mayStepIn())
4819                 {
4820                     auto thisDir = _cur;
4821                     if (stepIn(_cur.name))
4822                     {
4823                         pushExtra(thisDir);
4824                     }
4825                     else
4826                         break;
4827                 }
4828         }
4829     }
4830 
4831     @property bool empty()
4832     {
4833         return _stashed.length == 0 && _stack.length == 0;
4834     }
4835 
4836     @property DirEntry front()
4837     {
4838         return _cur;
4839     }
4840 
4841     void popFront()
4842     {
4843         switch (_mode)
4844         {
4845         case SpanMode.depth:
4846             if (next())
4847             {
4848                 while (mayStepIn())
4849                 {
4850                     auto thisDir = _cur;
4851                     if (stepIn(_cur.name))
4852                     {
4853                         pushExtra(thisDir);
4854                     }
4855                     else
4856                         break;
4857                 }
4858             }
4859             else if (hasExtra())
4860                 _cur = popExtra();
4861             break;
4862         case SpanMode.breadth:
4863             if (mayStepIn())
4864             {
4865                 if (!stepIn(_cur.name))
4866                     while (!empty && !next()){}
4867             }
4868             else
4869                 while (!empty && !next()){}
4870             break;
4871         default:
4872             next();
4873         }
4874     }
4875 
4876     ~this()
4877     {
4878         releaseDirStack();
4879     }
4880 }
4881 
4882 // Must be a template, because the destructor is unsafe or safe depending on
4883 // whether `-preview=dip1000` is in use. Otherwise, linking errors would
4884 // result.
4885 struct _DirIterator(bool useDIP1000)
4886 {
4887     static assert(useDIP1000 == dip1000Enabled,
4888         "Please don't override useDIP1000 to disagree with compiler switch.");
4889 
4890 private:
4891     SafeRefCounted!(DirIteratorImpl, RefCountedAutoInitialize.no) impl;
4892 
4893     this(string pathname, SpanMode mode, bool followSymlink) @trusted
4894     {
4895         impl = typeof(impl)(pathname, mode, followSymlink);
4896     }
4897 public:
4898     @property bool empty() @trusted { return impl.empty; }
4899     @property DirEntry front() @trusted { return impl.front; }
4900     void popFront() @trusted { impl.popFront(); }
4901 }
4902 
4903 // This has the client code to automatically use and link to the correct
4904 // template instance
4905 alias DirIterator = _DirIterator!dip1000Enabled;
4906 
4907 /++
4908     Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
4909     of `DirEntry` that lazily iterates a given directory,
4910     also provides two ways of foreach iteration. The iteration variable can be of
4911     type `string` if only the name is needed, or `DirEntry`
4912     if additional details are needed. The span _mode dictates how the
4913     directory is traversed. The name of each iterated directory entry
4914     contains the absolute or relative _path (depending on _pathname).
4915 
4916     Note: The order of returned directory entries is as it is provided by the
4917     operating system / filesystem, and may not follow any particular sorting.
4918 
4919     Params:
4920         useDIP1000 = used to instantiate this function separately for code with
4921                      and without -preview=dip1000 compiler switch, because it
4922                      affects the ABI of this function. Set automatically -
4923                      don't touch.
4924 
4925         path = The directory to iterate over.
4926                If empty, the current directory will be iterated.
4927 
4928         pattern = Optional string with wildcards, such as $(RED
4929                   "*.d"). When present, it is used to filter the
4930                   results by their file name. The supported wildcard
4931                   strings are described under $(REF globMatch,
4932                   std,_path).
4933 
4934         mode = Whether the directory's sub-directories should be
4935                iterated in depth-first post-order ($(LREF depth)),
4936                depth-first pre-order ($(LREF breadth)), or not at all
4937                ($(LREF shallow)).
4938 
4939         followSymlink = Whether symbolic links which point to directories
4940                          should be treated as directories and their contents
4941                          iterated over.
4942 
4943     Returns:
4944         An $(REF_ALTTEXT input range, isInputRange,std,range,primitives) of
4945         $(LREF DirEntry).
4946 
4947     Throws:
4948         $(UL
4949         $(LI $(LREF FileException) if the $(B path) directory does not exist or read permission is denied.)
4950         $(LI $(LREF FileException) if $(B mode) is not `shallow` and a subdirectory cannot be read.)
4951         )
4952 
4953 Example:
4954 --------------------
4955 // Iterate a directory in depth
4956 foreach (string name; dirEntries("destroy/me", SpanMode.depth))
4957 {
4958     remove(name);
4959 }
4960 
4961 // Iterate the current directory in breadth
4962 foreach (string name; dirEntries("", SpanMode.breadth))
4963 {
4964     writeln(name);
4965 }
4966 
4967 // Iterate a directory and get detailed info about it
4968 foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth))
4969 {
4970     writeln(e.name, "\t", e.size);
4971 }
4972 
4973 // Iterate over all *.d files in current directory and all its subdirectories
4974 auto dFiles = dirEntries("", SpanMode.depth).filter!(f => f.name.endsWith(".d"));
4975 foreach (d; dFiles)
4976     writeln(d.name);
4977 
4978 // Hook it up with std.parallelism to compile them all in parallel:
4979 foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread
4980 {
4981     string cmd = "dmd -c "  ~ d.name;
4982     writeln(cmd);
4983     std.process.executeShell(cmd);
4984 }
4985 
4986 // Iterate over all D source files in current directory and all its
4987 // subdirectories
4988 auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
4989 foreach (d; dFiles)
4990     writeln(d.name);
4991 --------------------
4992 To handle subdirectories with denied read permission, use `SpanMode.shallow`:
4993 ---
4994 void scan(string path)
4995 {
4996     foreach (DirEntry entry; dirEntries(path, SpanMode.shallow))
4997     {
4998         try
4999         {
5000             writeln(entry.name);
5001             if (entry.isDir)
5002                 scan(entry.name);
5003         }
5004         catch (FileException fe) { continue; } // ignore
5005     }
5006 }
5007 
5008 scan("");
5009 ---
5010 +/
5011 
5012 // For some reason, doing the same alias-to-a-template trick as with DirIterator
5013 // does not work here.
5014 auto dirEntries(bool useDIP1000 = dip1000Enabled)
5015     (string path, SpanMode mode, bool followSymlink = true)
5016 {
5017     return _DirIterator!useDIP1000(path, mode, followSymlink);
5018 }
5019 
5020 /// Duplicate functionality of D1's `std.file.listdir()`:
5021 @safe unittest
5022 {
5023     string[] listdir(string pathname)
5024     {
5025         import std.algorithm.iteration : map, filter;
5026         import std.array : array;
5027         import std.path : baseName;
5028 
5029         return dirEntries(pathname, SpanMode.shallow)
5030             .filter!(a => a.isFile)
5031             .map!((return a) => baseName(a.name))
5032             .array;
5033     }
5034 
5035     // Can be safe only with -preview=dip1000
5036     @safe void main(string[] args)
5037     {
5038         import std.stdio : writefln;
5039 
5040         string[] files = listdir(args[1]);
5041         writefln("%s", files);
5042     }
5043 }
5044 
5045 @system unittest
5046 {
5047     import std.algorithm.comparison : equal;
5048     import std.algorithm.iteration : map;
5049     import std.algorithm.searching : startsWith;
5050     import std.array : array;
5051     import std.conv : to;
5052     import std.path : buildPath, absolutePath;
5053     import std.file : dirEntries;
5054     import std.process : thisProcessID;
5055     import std.range.primitives : walkLength;
5056 
5057     version (Android)
5058         string testdir = deleteme; // This has to be an absolute path when
5059                                    // called from a shared library on Android,
5060                                    // ie an apk
5061     else
5062         string testdir = tempDir.buildPath("deleteme.dmd.unittest.std.file" ~ to!string(thisProcessID));
5063     mkdirRecurse(buildPath(testdir, "somedir"));
5064     scope(exit) rmdirRecurse(testdir);
5065     write(buildPath(testdir, "somefile"), null);
5066     write(buildPath(testdir, "somedir", "somedeepfile"), null);
5067 
5068     // testing range interface
5069     size_t equalEntries(string relpath, SpanMode mode)
5070     {
5071         import std.exception : enforce;
5072         auto len = enforce(walkLength(dirEntries(absolutePath(relpath), mode)));
5073         assert(walkLength(dirEntries(relpath, mode)) == len);
5074         assert(equal(
5075                    map!((return a) => absolutePath(a.name))(dirEntries(relpath, mode)),
5076                    map!(a => a.name)(dirEntries(absolutePath(relpath), mode))));
5077         return len;
5078     }
5079 
5080     assert(equalEntries(testdir, SpanMode.shallow) == 2);
5081     assert(equalEntries(testdir, SpanMode.depth) == 3);
5082     assert(equalEntries(testdir, SpanMode.breadth) == 3);
5083 
5084     // testing opApply
5085     foreach (string name; dirEntries(testdir, SpanMode.breadth))
5086     {
5087         //writeln(name);
5088         assert(name.startsWith(testdir));
5089     }
5090     foreach (DirEntry e; dirEntries(absolutePath(testdir), SpanMode.breadth))
5091     {
5092         //writeln(name);
5093         assert(e.isFile || e.isDir, e.name);
5094     }
5095 
5096     // https://issues.dlang.org/show_bug.cgi?id=7264
5097     foreach (string name; dirEntries(testdir, "*.d", SpanMode.breadth))
5098     {
5099 
5100     }
5101     foreach (entry; dirEntries(testdir, SpanMode.breadth))
5102     {
5103         static assert(is(typeof(entry) == DirEntry));
5104     }
5105     // https://issues.dlang.org/show_bug.cgi?id=7138
5106     auto a = array(dirEntries(testdir, SpanMode.shallow));
5107 
5108     // https://issues.dlang.org/show_bug.cgi?id=11392
5109     auto dFiles = dirEntries(testdir, SpanMode.shallow);
5110     foreach (d; dFiles){}
5111 
5112     // https://issues.dlang.org/show_bug.cgi?id=15146
5113     dirEntries("", SpanMode.shallow).walkLength();
5114 }
5115 
5116 /// Ditto
5117 auto dirEntries(bool useDIP1000 = dip1000Enabled)
5118     (string path, string pattern, SpanMode mode,
5119     bool followSymlink = true)
5120 {
5121     import std.algorithm.iteration : filter;
5122     import std.path : globMatch, baseName;
5123 
5124     bool f(DirEntry de) { return globMatch(baseName(de.name), pattern); }
5125     return filter!f(_DirIterator!useDIP1000(path, mode, followSymlink));
5126 }
5127 
5128 @safe unittest
5129 {
5130     import std.stdio : writefln;
5131     immutable dpath = deleteme ~ "_dir";
5132     immutable fpath = deleteme ~ "_file";
5133     immutable sdpath = deleteme ~ "_sdir";
5134     immutable sfpath = deleteme ~ "_sfile";
5135     scope(exit)
5136     {
5137         if (dpath.exists) rmdirRecurse(dpath);
5138         if (fpath.exists) remove(fpath);
5139         if (sdpath.exists) remove(sdpath);
5140         if (sfpath.exists) remove(sfpath);
5141     }
5142 
5143     mkdir(dpath);
5144     write(fpath, "hello world");
5145     version (Posix) () @trusted
5146     {
5147         core.sys.posix.unistd.symlink((dpath ~ '\0').ptr, (sdpath ~ '\0').ptr);
5148         core.sys.posix.unistd.symlink((fpath ~ '\0').ptr, (sfpath ~ '\0').ptr);
5149     } ();
5150 
5151     static struct Flags { bool dir, file, link; }
5152     auto tests = [dpath : Flags(true), fpath : Flags(false, true)];
5153     version (Posix)
5154     {
5155         tests[sdpath] = Flags(true, false, true);
5156         tests[sfpath] = Flags(false, true, true);
5157     }
5158 
5159     auto past = Clock.currTime() - 2.seconds;
5160     auto future = past + 4.seconds;
5161 
5162     foreach (path, flags; tests)
5163     {
5164         auto de = DirEntry(path);
5165         assert(de.name == path);
5166         assert(de.isDir == flags.dir);
5167         assert(de.isFile == flags.file);
5168         assert(de.isSymlink == flags.link);
5169 
5170         assert(de.isDir == path.isDir);
5171         assert(de.isFile == path.isFile);
5172         assert(de.isSymlink == path.isSymlink);
5173         assert(de.size == path.getSize());
5174         assert(de.attributes == getAttributes(path));
5175         assert(de.linkAttributes == getLinkAttributes(path));
5176 
5177         scope(failure) writefln("[%s] [%s] [%s] [%s]", past, de.timeLastAccessed, de.timeLastModified, future);
5178         assert(de.timeLastAccessed > past);
5179         assert(de.timeLastAccessed < future);
5180         assert(de.timeLastModified > past);
5181         assert(de.timeLastModified < future);
5182 
5183         assert(attrIsDir(de.attributes) == flags.dir);
5184         assert(attrIsDir(de.linkAttributes) == (flags.dir && !flags.link));
5185         assert(attrIsFile(de.attributes) == flags.file);
5186         assert(attrIsFile(de.linkAttributes) == (flags.file && !flags.link));
5187         assert(!attrIsSymlink(de.attributes));
5188         assert(attrIsSymlink(de.linkAttributes) == flags.link);
5189 
5190         version (Windows)
5191         {
5192             assert(de.timeCreated > past);
5193             assert(de.timeCreated < future);
5194         }
5195         else version (Posix)
5196         {
5197             assert(de.timeStatusChanged > past);
5198             assert(de.timeStatusChanged < future);
5199             assert(de.attributes == de.statBuf.st_mode);
5200         }
5201     }
5202 }
5203 
5204 // Make sure that dirEntries does not butcher Unicode file names
5205 // https://issues.dlang.org/show_bug.cgi?id=17962
5206 @safe unittest
5207 {
5208     import std.algorithm.comparison : equal;
5209     import std.algorithm.iteration : map;
5210     import std.algorithm.sorting : sort;
5211     import std.array : array;
5212     import std.path : buildPath;
5213     import std.uni : normalize;
5214 
5215     // The Unicode normalization is required to make the tests pass on Mac OS X.
5216     auto dir = deleteme ~ normalize("𐐷");
5217     scope(exit) if (dir.exists) rmdirRecurse(dir);
5218     mkdir(dir);
5219     auto files = ["Hello World",
5220                   "Ma Chérie.jpeg",
5221                   "さいごの果実.txt"].map!(a => buildPath(dir, normalize(a)))().array();
5222     sort(files);
5223     foreach (file; files)
5224         write(file, "nothing");
5225 
5226     auto result = dirEntries(dir, SpanMode.shallow).map!((return a) => a.name.normalize()).array();
5227     sort(result);
5228 
5229     assert(equal(files, result));
5230 }
5231 
5232 // https://issues.dlang.org/show_bug.cgi?id=21250
5233 @system unittest
5234 {
5235     import std.exception : assertThrown;
5236     assertThrown!Exception(dirEntries("237f5babd6de21f40915826699582e36", "*.bin", SpanMode.depth));
5237 }
5238 
5239 /**
5240  * Reads a file line by line and parses the line into a single value or a
5241  * $(REF Tuple, std,typecons) of values depending on the length of `Types`.
5242  * The lines are parsed using the specified format string. The format string is
5243  * passed to $(REF formattedRead, std,_format), and therefore must conform to the
5244  * _format string specification outlined in $(MREF std, _format).
5245  *
5246  * Params:
5247  *     Types = the types that each of the elements in the line should be returned as
5248  *     filename = the name of the file to read
5249  *     format = the _format string to use when reading
5250  *
5251  * Returns:
5252  *     If only one type is passed, then an array of that type. Otherwise, an
5253  *     array of $(REF Tuple, std,typecons)s.
5254  *
5255  * Throws:
5256  *     `Exception` if the format string is malformed. Also, throws `Exception`
5257  *     if any of the lines in the file are not fully consumed by the call
5258  *     to $(REF formattedRead, std,_format). Meaning that no empty lines or lines
5259  *     with extra characters are allowed.
5260  */
5261 Select!(Types.length == 1, Types[0][], Tuple!(Types)[])
5262 slurp(Types...)(string filename, scope const(char)[] format)
5263 {
5264     import std.array : appender;
5265     import std.conv : text;
5266     import std.exception : enforce;
5267     import std.format.read : formattedRead;
5268     import std.stdio : File;
5269     import std.string : stripRight;
5270 
5271     auto app = appender!(typeof(return))();
5272     ElementType!(typeof(return)) toAdd;
5273     auto f = File(filename);
5274     scope(exit) f.close();
5275     foreach (line; f.byLine())
5276     {
5277         formattedRead(line, format, &toAdd);
5278         enforce(line.stripRight("\r").empty,
5279                 text("Trailing characters at the end of line: `", line,
5280                         "'"));
5281         app.put(toAdd);
5282     }
5283     return app.data;
5284 }
5285 
5286 ///
5287 @system unittest
5288 {
5289     import std.typecons : tuple;
5290 
5291     scope(exit)
5292     {
5293         assert(exists(deleteme));
5294         remove(deleteme);
5295     }
5296 
5297     write(deleteme, "12 12.25\n345 1.125"); // deleteme is the name of a temporary file
5298 
5299     // Load file; each line is an int followed by comma, whitespace and a
5300     // double.
5301     auto a = slurp!(int, double)(deleteme, "%s %s");
5302     assert(a.length == 2);
5303     assert(a[0] == tuple(12, 12.25));
5304     assert(a[1] == tuple(345, 1.125));
5305 }
5306 
5307 @system unittest
5308 {
5309     import std.typecons : tuple;
5310 
5311     scope(exit)
5312     {
5313         assert(exists(deleteme));
5314         remove(deleteme);
5315     }
5316     write(deleteme, "10\r\n20");
5317     assert(slurp!(int)(deleteme, "%d") == [10, 20]);
5318 }
5319 
5320 /**
5321 Returns the path to a directory for temporary files.
5322 On POSIX platforms, it searches through the following list of directories
5323 and returns the first one which is found to exist:
5324 $(OL
5325     $(LI The directory given by the `TMPDIR` environment variable.)
5326     $(LI The directory given by the `TEMP` environment variable.)
5327     $(LI The directory given by the `TMP` environment variable.)
5328     $(LI `/tmp/`)
5329     $(LI `/var/tmp/`)
5330     $(LI `/usr/tmp/`)
5331 )
5332 
5333 On all platforms, `tempDir` returns the current working directory on failure.
5334 
5335 The return value of the function is cached, so the procedures described
5336 below will only be performed the first time the function is called.  All
5337 subsequent runs will return the same string, regardless of whether
5338 environment variables and directory structures have changed in the
5339 meantime.
5340 
5341 The POSIX `tempDir` algorithm is inspired by Python's
5342 $(LINK2 http://docs.python.org/library/tempfile.html#tempfile.tempdir, `tempfile.tempdir`).
5343 
5344 Returns:
5345     On Windows, this function returns the result of calling the Windows API function
5346     $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992.aspx, `GetTempPath`).
5347 
5348     On POSIX platforms, it searches through the following list of directories
5349     and returns the first one which is found to exist:
5350     $(OL
5351         $(LI The directory given by the `TMPDIR` environment variable.)
5352         $(LI The directory given by the `TEMP` environment variable.)
5353         $(LI The directory given by the `TMP` environment variable.)
5354         $(LI `/tmp`)
5355         $(LI `/var/tmp`)
5356         $(LI `/usr/tmp`)
5357     )
5358 
5359     On all platforms, `tempDir` returns `"."` on failure, representing
5360     the current working directory.
5361 */
5362 string tempDir() @trusted
5363 {
5364     // We must check that the end of a path is not a separator, before adding another
5365     // If we don't we end up with https://issues.dlang.org/show_bug.cgi?id=22738
5366     static string addSeparator(string input)
5367     {
5368         import std.path : dirSeparator;
5369         import std.algorithm.searching : endsWith;
5370 
5371         // It is very rare a directory path will reach this point with a directory separator at the end
5372         // However on OSX this can happen, so we must verify lest we break user code i.e. https://github.com/dlang/dub/pull/2208
5373         if (!input.endsWith(dirSeparator))
5374             return input ~ dirSeparator;
5375         else
5376             return input;
5377     }
5378 
5379     static string cache;
5380     if (cache is null)
5381     {
5382         version (Windows)
5383         {
5384             import std.conv : to;
5385             // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992(v=vs.85).aspx
5386             wchar[MAX_PATH + 2] buf;
5387             DWORD len = GetTempPathW(buf.length, buf.ptr);
5388             if (len) cache = buf[0 .. len].to!string;
5389         }
5390 	else version (Emscripten)
5391 		assert(0, "function std.file.tempDir not implemented on Emscripten");
5392         else version (Posix)
5393         {
5394             import std.process : environment;
5395             // This function looks through the list of alternative directories
5396             // and returns the first one which exists and is a directory.
5397             static string findExistingDir(T...)(lazy T alternatives)
5398             {
5399                 foreach (dir; alternatives)
5400                     if (!dir.empty && exists(dir)) return addSeparator(dir);
5401                 return null;
5402             }
5403 
5404             cache = findExistingDir(environment.get("TMPDIR"),
5405                                     environment.get("TEMP"),
5406                                     environment.get("TMP"),
5407                                     "/tmp",
5408                                     "/var/tmp",
5409                                     "/usr/tmp");
5410         }
5411         else static assert(false, "Unsupported platform");
5412 
5413         if (cache is null)
5414         {
5415             cache = addSeparator(getcwd());
5416         }
5417     }
5418     return cache;
5419 }
5420 
5421 ///
5422 @safe unittest
5423 {
5424     import std.ascii : letters;
5425     import std.conv : to;
5426     import std.path : buildPath;
5427     import std.random : randomSample;
5428     import std.utf : byCodeUnit;
5429 
5430     // random id with 20 letters
5431     auto id = letters.byCodeUnit.randomSample(20).to!string;
5432     auto myFile = tempDir.buildPath(id ~ "my_tmp_file");
5433     scope(exit) myFile.remove;
5434 
5435     myFile.write("hello");
5436     assert(myFile.readText == "hello");
5437 }
5438 
5439 @safe unittest
5440 {
5441     import std.algorithm.searching : endsWith;
5442     import std.path : dirSeparator;
5443     assert(tempDir.endsWith(dirSeparator));
5444 
5445     // https://issues.dlang.org/show_bug.cgi?id=22738
5446     assert(!tempDir.endsWith(dirSeparator ~ dirSeparator));
5447 }
5448 
5449 /**
5450 Returns the available disk space based on a given path.
5451 On Windows, `path` must be a directory; on POSIX systems, it can be a file or directory.
5452 
5453 Params:
5454     path = on Windows, it must be a directory; on POSIX it can be a file or directory
5455 Returns:
5456     Available space in bytes
5457 
5458 Throws:
5459     $(LREF FileException) in case of failure
5460 */
5461 ulong getAvailableDiskSpace(scope const(char)[] path) @safe
5462 {
5463     version (Windows)
5464     {
5465         import core.sys.windows.winbase : GetDiskFreeSpaceExW;
5466         import core.sys.windows.winnt : ULARGE_INTEGER;
5467         import std.internal.cstring : tempCStringW;
5468 
5469         ULARGE_INTEGER freeBytesAvailable;
5470         auto err = () @trusted {
5471             return GetDiskFreeSpaceExW(path.tempCStringW(), &freeBytesAvailable, null, null);
5472         } ();
5473         cenforce(err != 0, "Cannot get available disk space");
5474 
5475         return freeBytesAvailable.QuadPart;
5476     }
5477     else version (Posix)
5478     {
5479         import std.internal.cstring : tempCString;
5480 
5481         version (FreeBSD)
5482         {
5483             import core.sys.freebsd.sys.mount : statfs, statfs_t;
5484 
5485             statfs_t stats;
5486             auto err = () @trusted {
5487                 return statfs(path.tempCString(), &stats);
5488             } ();
5489             cenforce(err == 0, "Cannot get available disk space");
5490 
5491             return stats.f_bavail * stats.f_bsize;
5492         }
5493         else
5494         {
5495             import core.sys.posix.sys.statvfs : statvfs, statvfs_t;
5496 
5497             statvfs_t stats;
5498             auto err = () @trusted {
5499                 return statvfs(path.tempCString(), &stats);
5500             } ();
5501             cenforce(err == 0, "Cannot get available disk space");
5502 
5503             return stats.f_bavail * stats.f_frsize;
5504         }
5505     }
5506     else static assert(0, "Unsupported platform");
5507 }
5508 
5509 ///
5510 @safe unittest
5511 {
5512     import std.exception : assertThrown;
5513 
5514     auto space = getAvailableDiskSpace(".");
5515     assert(space > 0);
5516 
5517     assertThrown!FileException(getAvailableDiskSpace("ThisFileDoesNotExist123123"));
5518 }