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