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 @safe unittest
2373 {
2374     import std.exception : assertThrown;
2375 
2376     auto dir = deleteme ~ "dir";
2377     auto f = deleteme ~ "f";
2378     scope(exit) dir.rmdir, f.remove;
2379 
2380     assert(!dir.exists);
2381     assertThrown!FileException(dir.isDir);
2382 
2383     dir.mkdir;
2384     assert(dir.isDir);
2385 
2386     f.write(".");
2387     assert(!f.isDir);
2388 }
2389 
2390 @safe unittest
2391 {
2392     static assert(__traits(compiles, TestAliasedString(null).isDir));
2393 }
2394 
2395 @safe unittest
2396 {
2397     version (Windows)
2398     {
2399         if ("C:\\Program Files\\".exists)
2400             assert("C:\\Program Files\\".isDir);
2401 
2402         if ("C:\\Windows\\system.ini".exists)
2403             assert(!"C:\\Windows\\system.ini".isDir);
2404     }
2405     else version (Posix)
2406     {
2407         if (system_directory.exists)
2408             assert(system_directory.isDir);
2409 
2410         if (system_file.exists)
2411             assert(!system_file.isDir);
2412     }
2413 }
2414 
2415 @safe unittest
2416 {
2417     version (Windows)
2418         enum dir = "C:\\Program Files\\";
2419     else version (Posix)
2420         enum dir = system_directory;
2421 
2422     if (dir.exists)
2423     {
2424         DirEntry de = DirEntry(dir);
2425         assert(de.isDir);
2426         assert(DirEntry(dir).isDir);
2427     }
2428 }
2429 
2430 /++
2431     Returns whether the given file _attributes are for a directory.
2432 
2433     Params:
2434         attributes = The file _attributes.
2435 
2436     Returns:
2437         true if attributes specifies a directory
2438 +/
2439 bool attrIsDir(uint attributes) @safe pure nothrow @nogc
2440 {
2441     version (Windows)
2442     {
2443         return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
2444     }
2445     else version (Posix)
2446     {
2447         return (attributes & S_IFMT) == S_IFDIR;
2448     }
2449 }
2450 
2451 ///
2452 @safe unittest
2453 {
2454     import std.exception : assertThrown;
2455 
2456     auto dir = deleteme ~ "dir";
2457     auto f = deleteme ~ "f";
2458     scope(exit) dir.rmdir, f.remove;
2459 
2460     assert(!dir.exists);
2461     assertThrown!FileException(dir.getAttributes.attrIsDir);
2462 
2463     dir.mkdir;
2464     assert(dir.isDir);
2465     assert(dir.getAttributes.attrIsDir);
2466 
2467     f.write(".");
2468     assert(!f.isDir);
2469     assert(!f.getAttributes.attrIsDir);
2470 }
2471 
2472 @safe unittest
2473 {
2474     version (Windows)
2475     {
2476         if ("C:\\Program Files\\".exists)
2477         {
2478             assert(attrIsDir(getAttributes("C:\\Program Files\\")));
2479             assert(attrIsDir(getLinkAttributes("C:\\Program Files\\")));
2480         }
2481 
2482         if ("C:\\Windows\\system.ini".exists)
2483         {
2484             assert(!attrIsDir(getAttributes("C:\\Windows\\system.ini")));
2485             assert(!attrIsDir(getLinkAttributes("C:\\Windows\\system.ini")));
2486         }
2487     }
2488     else version (Posix)
2489     {
2490         if (system_directory.exists)
2491         {
2492             assert(attrIsDir(getAttributes(system_directory)));
2493             assert(attrIsDir(getLinkAttributes(system_directory)));
2494         }
2495 
2496         if (system_file.exists)
2497         {
2498             assert(!attrIsDir(getAttributes(system_file)));
2499             assert(!attrIsDir(getLinkAttributes(system_file)));
2500         }
2501     }
2502 }
2503 
2504 
2505 /++
2506     Returns whether the given file (or directory) is a file.
2507 
2508     On Windows, if a file is not a directory, then it's a file. So,
2509     either `isFile` or `isDir` will return true for any given file.
2510 
2511     On POSIX systems, if `isFile` is `true`, that indicates that the file
2512     is a regular file (e.g. not a block not device). So, on POSIX systems, it's
2513     possible for both `isFile` and `isDir` to be `false` for a
2514     particular file (in which case, it's a special file). You can use
2515     `getAttributes` to get the attributes to figure out what type of special
2516     it is, or you can use `DirEntry` to get at its `statBuf`, which is the
2517     result from `stat`. In either case, see the man page for `stat` for
2518     more information.
2519 
2520     Params:
2521         name = The path to the file.
2522 
2523     Returns:
2524         true if name specifies a file
2525 
2526     Throws:
2527         $(LREF FileException) if the given file does not exist.
2528 +/
2529 @property bool isFile(R)(R name)
2530 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2531 {
2532     version (Windows)
2533         return !name.isDir;
2534     else version (Posix)
2535         return (getAttributes(name) & S_IFMT) == S_IFREG;
2536 }
2537 
2538 /// ditto
2539 @property bool isFile(R)(auto ref R name)
2540 if (isConvertibleToString!R)
2541 {
2542     return isFile!(StringTypeOf!R)(name);
2543 }
2544 
2545 ///
2546 @safe unittest
2547 {
2548     import std.exception : assertThrown;
2549 
2550     auto dir = deleteme ~ "dir";
2551     auto f = deleteme ~ "f";
2552     scope(exit) dir.rmdir, f.remove;
2553 
2554     dir.mkdir;
2555     assert(!dir.isFile);
2556 
2557     assert(!f.exists);
2558     assertThrown!FileException(f.isFile);
2559 
2560     f.write(".");
2561     assert(f.isFile);
2562 }
2563 
2564 // https://issues.dlang.org/show_bug.cgi?id=15658
2565 @safe unittest
2566 {
2567     DirEntry e = DirEntry(".");
2568     static assert(is(typeof(isFile(e))));
2569 }
2570 
2571 @safe unittest
2572 {
2573     static assert(__traits(compiles, TestAliasedString(null).isFile));
2574 }
2575 
2576 @safe unittest
2577 {
2578     version (Windows)
2579     {
2580         if ("C:\\Program Files\\".exists)
2581             assert(!"C:\\Program Files\\".isFile);
2582 
2583         if ("C:\\Windows\\system.ini".exists)
2584             assert("C:\\Windows\\system.ini".isFile);
2585     }
2586     else version (Posix)
2587     {
2588         if (system_directory.exists)
2589             assert(!system_directory.isFile);
2590 
2591         if (system_file.exists)
2592             assert(system_file.isFile);
2593     }
2594 }
2595 
2596 
2597 /++
2598     Returns whether the given file _attributes are for a file.
2599 
2600     On Windows, if a file is not a directory, it's a file. So, either
2601     `attrIsFile` or `attrIsDir` will return `true` for the
2602     _attributes of any given file.
2603 
2604     On POSIX systems, if `attrIsFile` is `true`, that indicates that the
2605     file is a regular file (e.g. not a block not device). So, on POSIX systems,
2606     it's possible for both `attrIsFile` and `attrIsDir` to be `false`
2607     for a particular file (in which case, it's a special file). If a file is a
2608     special file, you can use the _attributes to check what type of special file
2609     it is (see the man page for `stat` for more information).
2610 
2611     Params:
2612         attributes = The file _attributes.
2613 
2614     Returns:
2615         true if the given file _attributes are for a file
2616 
2617 Example:
2618 --------------------
2619 assert(attrIsFile(getAttributes("/etc/fonts/fonts.conf")));
2620 assert(attrIsFile(getLinkAttributes("/etc/fonts/fonts.conf")));
2621 --------------------
2622   +/
2623 bool attrIsFile(uint attributes) @safe pure nothrow @nogc
2624 {
2625     version (Windows)
2626     {
2627         return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
2628     }
2629     else version (Posix)
2630     {
2631         return (attributes & S_IFMT) == S_IFREG;
2632     }
2633 }
2634 
2635 ///
2636 @safe unittest
2637 {
2638     import std.exception : assertThrown;
2639 
2640     auto dir = deleteme ~ "dir";
2641     auto f = deleteme ~ "f";
2642     scope(exit) dir.rmdir, f.remove;
2643 
2644     dir.mkdir;
2645     assert(!dir.isFile);
2646     assert(!dir.getAttributes.attrIsFile);
2647 
2648     assert(!f.exists);
2649     assertThrown!FileException(f.getAttributes.attrIsFile);
2650 
2651     f.write(".");
2652     assert(f.isFile);
2653     assert(f.getAttributes.attrIsFile);
2654 }
2655 
2656 @safe unittest
2657 {
2658     version (Windows)
2659     {
2660         if ("C:\\Program Files\\".exists)
2661         {
2662             assert(!attrIsFile(getAttributes("C:\\Program Files\\")));
2663             assert(!attrIsFile(getLinkAttributes("C:\\Program Files\\")));
2664         }
2665 
2666         if ("C:\\Windows\\system.ini".exists)
2667         {
2668             assert(attrIsFile(getAttributes("C:\\Windows\\system.ini")));
2669             assert(attrIsFile(getLinkAttributes("C:\\Windows\\system.ini")));
2670         }
2671     }
2672     else version (Posix)
2673     {
2674         if (system_directory.exists)
2675         {
2676             assert(!attrIsFile(getAttributes(system_directory)));
2677             assert(!attrIsFile(getLinkAttributes(system_directory)));
2678         }
2679 
2680         if (system_file.exists)
2681         {
2682             assert(attrIsFile(getAttributes(system_file)));
2683             assert(attrIsFile(getLinkAttributes(system_file)));
2684         }
2685     }
2686 }
2687 
2688 
2689 /++
2690     Returns whether the given file is a symbolic link.
2691 
2692     On Windows, returns `true` when the file is either a symbolic link or a
2693     junction point.
2694 
2695     Params:
2696         name = The path to the file.
2697 
2698     Returns:
2699         true if name is a symbolic link
2700 
2701     Throws:
2702         $(LREF FileException) if the given file does not exist.
2703   +/
2704 @property bool isSymlink(R)(R name)
2705 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2706 {
2707     version (Windows)
2708         return (getAttributes(name) & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2709     else version (Posix)
2710         return (getLinkAttributes(name) & S_IFMT) == S_IFLNK;
2711 }
2712 
2713 /// ditto
2714 @property bool isSymlink(R)(auto ref R name)
2715 if (isConvertibleToString!R)
2716 {
2717     return name.isSymlink!(StringTypeOf!R);
2718 }
2719 
2720 @safe unittest
2721 {
2722     static assert(__traits(compiles, TestAliasedString(null).isSymlink));
2723 }
2724 
2725 ///
2726 @safe unittest
2727 {
2728     import std.exception : assertThrown;
2729 
2730     auto source = deleteme ~ "source";
2731     auto target = deleteme ~ "target";
2732 
2733     assert(!source.exists);
2734     assertThrown!FileException(source.isSymlink);
2735 
2736     // symlinking isn't available on Windows
2737     version (Posix)
2738     {
2739         scope(exit) source.remove, target.remove;
2740 
2741         target.write("target");
2742         target.symlink(source);
2743         assert(source.readText == "target");
2744         assert(source.isSymlink);
2745         assert(source.getLinkAttributes.attrIsSymlink);
2746     }
2747 }
2748 
2749 @system unittest
2750 {
2751     version (Windows)
2752     {
2753         if ("C:\\Program Files\\".exists)
2754             assert(!"C:\\Program Files\\".isSymlink);
2755 
2756         if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
2757             assert("C:\\Documents and Settings\\".isSymlink);
2758 
2759         enum fakeSymFile = "C:\\Windows\\system.ini";
2760         if (fakeSymFile.exists)
2761         {
2762             assert(!fakeSymFile.isSymlink);
2763 
2764             assert(!fakeSymFile.isSymlink);
2765             assert(!attrIsSymlink(getAttributes(fakeSymFile)));
2766             assert(!attrIsSymlink(getLinkAttributes(fakeSymFile)));
2767 
2768             assert(attrIsFile(getAttributes(fakeSymFile)));
2769             assert(attrIsFile(getLinkAttributes(fakeSymFile)));
2770             assert(!attrIsDir(getAttributes(fakeSymFile)));
2771             assert(!attrIsDir(getLinkAttributes(fakeSymFile)));
2772 
2773             assert(getAttributes(fakeSymFile) == getLinkAttributes(fakeSymFile));
2774         }
2775     }
2776     else version (Posix)
2777     {
2778         if (system_directory.exists)
2779         {
2780             assert(!system_directory.isSymlink);
2781 
2782             immutable symfile = deleteme ~ "_slink\0";
2783             scope(exit) if (symfile.exists) symfile.remove();
2784 
2785             core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
2786 
2787             assert(symfile.isSymlink);
2788             assert(!attrIsSymlink(getAttributes(symfile)));
2789             assert(attrIsSymlink(getLinkAttributes(symfile)));
2790 
2791             assert(attrIsDir(getAttributes(symfile)));
2792             assert(!attrIsDir(getLinkAttributes(symfile)));
2793 
2794             assert(!attrIsFile(getAttributes(symfile)));
2795             assert(!attrIsFile(getLinkAttributes(symfile)));
2796         }
2797 
2798         if (system_file.exists)
2799         {
2800             assert(!system_file.isSymlink);
2801 
2802             immutable symfile = deleteme ~ "_slink\0";
2803             scope(exit) if (symfile.exists) symfile.remove();
2804 
2805             core.sys.posix.unistd.symlink(system_file, symfile.ptr);
2806 
2807             assert(symfile.isSymlink);
2808             assert(!attrIsSymlink(getAttributes(symfile)));
2809             assert(attrIsSymlink(getLinkAttributes(symfile)));
2810 
2811             assert(!attrIsDir(getAttributes(symfile)));
2812             assert(!attrIsDir(getLinkAttributes(symfile)));
2813 
2814             assert(attrIsFile(getAttributes(symfile)));
2815             assert(!attrIsFile(getLinkAttributes(symfile)));
2816         }
2817     }
2818 
2819     static assert(__traits(compiles, () @safe { return "dummy".isSymlink; }));
2820 }
2821 
2822 
2823 /++
2824     Returns whether the given file attributes are for a symbolic link.
2825 
2826     On Windows, return `true` when the file is either a symbolic link or a
2827     junction point.
2828 
2829     Params:
2830         attributes = The file attributes.
2831 
2832     Returns:
2833         true if attributes are for a symbolic link
2834 
2835 Example:
2836 --------------------
2837 core.sys.posix.unistd.symlink("/etc/fonts/fonts.conf", "/tmp/alink");
2838 
2839 assert(!getAttributes("/tmp/alink").isSymlink);
2840 assert(getLinkAttributes("/tmp/alink").isSymlink);
2841 --------------------
2842   +/
2843 bool attrIsSymlink(uint attributes) @safe pure nothrow @nogc
2844 {
2845     version (Windows)
2846         return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2847     else version (Posix)
2848         return (attributes & S_IFMT) == S_IFLNK;
2849 }
2850 
2851 ///
2852 @safe unittest
2853 {
2854     import std.exception : assertThrown;
2855 
2856     auto source = deleteme ~ "source";
2857     auto target = deleteme ~ "target";
2858 
2859     assert(!source.exists);
2860     assertThrown!FileException(source.getLinkAttributes.attrIsSymlink);
2861 
2862     // symlinking isn't available on Windows
2863     version (Posix)
2864     {
2865         scope(exit) source.remove, target.remove;
2866 
2867         target.write("target");
2868         target.symlink(source);
2869         assert(source.readText == "target");
2870         assert(source.isSymlink);
2871         assert(source.getLinkAttributes.attrIsSymlink);
2872     }
2873 }
2874 
2875 /**
2876 Change directory to `pathname`. Equivalent to `cd` on
2877 Windows and POSIX.
2878 
2879 Params:
2880     pathname = the directory to step into
2881 
2882 Throws: $(LREF FileException) on error.
2883  */
2884 void chdir(R)(R pathname)
2885 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2886 {
2887     // Place outside of @trusted block
2888     auto pathz = pathname.tempCString!FSChar();
2889 
2890     version (Windows)
2891     {
2892         static auto trustedChdir(scope const(FSChar)* pathz) @trusted
2893         {
2894             return SetCurrentDirectoryW(pathz);
2895         }
2896     }
2897     else version (Posix)
2898     {
2899         static auto trustedChdir(scope const(FSChar)* pathz) @trusted
2900         {
2901             return core.sys.posix.unistd.chdir(pathz) == 0;
2902         }
2903     }
2904     static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2905         alias pathStr = pathname;
2906     else
2907         string pathStr = null;
2908     cenforce(trustedChdir(pathz), pathStr, pathz);
2909 }
2910 
2911 /// ditto
2912 void chdir(R)(auto ref R pathname)
2913 if (isConvertibleToString!R)
2914 {
2915     return chdir!(StringTypeOf!R)(pathname);
2916 }
2917 
2918 ///
2919 @system unittest
2920 {
2921     import std.algorithm.comparison : equal;
2922     import std.algorithm.sorting : sort;
2923     import std.array : array;
2924     import std.path : buildPath;
2925 
2926     auto cwd = getcwd;
2927     auto dir = deleteme ~ "dir";
2928     dir.mkdir;
2929     scope(exit) cwd.chdir, dir.rmdirRecurse;
2930 
2931     dir.buildPath("a").write(".");
2932     dir.chdir; // step into dir
2933     "b".write(".");
2934     assert(dirEntries(".", SpanMode.shallow).array.sort.equal(
2935         [".".buildPath("a"), ".".buildPath("b")]
2936     ));
2937 }
2938 
2939 @safe unittest
2940 {
2941     static assert(__traits(compiles, chdir(TestAliasedString(null))));
2942 }
2943 
2944 /**
2945 Make a new directory `pathname`.
2946 
2947 Params:
2948     pathname = the path of the directory to make
2949 
2950 Throws:
2951     $(LREF FileException) on POSIX or $(LREF WindowsException) on Windows
2952     if an error occured.
2953  */
2954 void mkdir(R)(R pathname)
2955 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
2956 {
2957     // Place outside of @trusted block
2958     const pathz = pathname.tempCString!FSChar();
2959 
2960     version (Windows)
2961     {
2962         static auto trustedCreateDirectoryW(scope const(FSChar)* pathz) @trusted
2963         {
2964             return CreateDirectoryW(pathz, null);
2965         }
2966         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2967             alias pathStr = pathname;
2968         else
2969             string pathStr = null;
2970         wenforce(trustedCreateDirectoryW(pathz), pathStr, pathz);
2971     }
2972     else version (Posix)
2973     {
2974         import std.conv : octal;
2975 
2976         static auto trustedMkdir(scope const(FSChar)* pathz, mode_t mode) @trusted
2977         {
2978             return core.sys.posix.sys.stat.mkdir(pathz, mode);
2979         }
2980         static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2981             alias pathStr = pathname;
2982         else
2983             string pathStr = null;
2984         cenforce(trustedMkdir(pathz, octal!777) == 0, pathStr, pathz);
2985     }
2986 }
2987 
2988 /// ditto
2989 void mkdir(R)(auto ref R pathname)
2990 if (isConvertibleToString!R)
2991 {
2992     return mkdir!(StringTypeOf!R)(pathname);
2993 }
2994 
2995 @safe unittest
2996 {
2997     import std.file : mkdir;
2998     static assert(__traits(compiles, mkdir(TestAliasedString(null))));
2999 }
3000 
3001 ///
3002 @safe unittest
3003 {
3004     import std.file : mkdir;
3005 
3006     auto dir = deleteme ~ "dir";
3007     scope(exit) dir.rmdir;
3008 
3009     dir.mkdir;
3010     assert(dir.exists);
3011 }
3012 
3013 ///
3014 @safe unittest
3015 {
3016     import std.exception : assertThrown;
3017     assertThrown("a/b/c/d/e".mkdir);
3018 }
3019 
3020 // Same as mkdir but ignores "already exists" errors.
3021 // Returns: "true" if the directory was created,
3022 //   "false" if it already existed.
3023 private bool ensureDirExists()(scope const(char)[] pathname)
3024 {
3025     import std.exception : enforce;
3026     const pathz = pathname.tempCString!FSChar();
3027 
3028     version (Windows)
3029     {
3030         if (() @trusted { return CreateDirectoryW(pathz, null); }())
3031             return true;
3032         cenforce(GetLastError() == ERROR_ALREADY_EXISTS, pathname.idup);
3033     }
3034     else version (Posix)
3035     {
3036         import std.conv : octal;
3037 
3038         if (() @trusted { return core.sys.posix.sys.stat.mkdir(pathz, octal!777); }() == 0)
3039             return true;
3040         cenforce(errno == EEXIST || errno == EISDIR, pathname);
3041     }
3042     enforce(pathname.isDir, new FileException(pathname.idup));
3043     return false;
3044 }
3045 
3046 /**
3047 Make directory and all parent directories as needed.
3048 
3049 Does nothing if the directory specified by
3050 `pathname` already exists.
3051 
3052 Params:
3053     pathname = the full path of the directory to create
3054 
3055 Throws: $(LREF FileException) on error.
3056  */
3057 void mkdirRecurse(scope const(char)[] pathname) @safe
3058 {
3059     import std.path : dirName, baseName;
3060 
3061     const left = dirName(pathname);
3062     if (left.length != pathname.length && !exists(left))
3063     {
3064         mkdirRecurse(left);
3065     }
3066     if (!baseName(pathname).empty)
3067     {
3068         ensureDirExists(pathname);
3069     }
3070 }
3071 
3072 ///
3073 @safe unittest
3074 {
3075     import std.path : buildPath;
3076 
3077     auto dir = deleteme ~ "dir";
3078     scope(exit) dir.rmdirRecurse;
3079 
3080     dir.mkdir;
3081     assert(dir.exists);
3082     dir.mkdirRecurse; // does nothing
3083 
3084     // creates all parent directories as needed
3085     auto nested = dir.buildPath("a", "b", "c");
3086     nested.mkdirRecurse;
3087     assert(nested.exists);
3088 }
3089 
3090 ///
3091 @safe unittest
3092 {
3093     import std.exception : assertThrown;
3094 
3095     scope(exit) deleteme.remove;
3096     deleteme.write("a");
3097 
3098     // cannot make directory as it's already a file
3099     assertThrown!FileException(deleteme.mkdirRecurse);
3100 }
3101 
3102 @safe unittest
3103 {
3104     import std.exception : assertThrown;
3105     {
3106         import std.path : buildPath, buildNormalizedPath;
3107 
3108         immutable basepath = deleteme ~ "_dir";
3109         scope(exit) () @trusted { rmdirRecurse(basepath); }();
3110 
3111         auto path = buildPath(basepath, "a", "..", "b");
3112         mkdirRecurse(path);
3113         path = path.buildNormalizedPath;
3114         assert(path.isDir);
3115 
3116         path = buildPath(basepath, "c");
3117         write(path, "");
3118         assertThrown!FileException(mkdirRecurse(path));
3119 
3120         path = buildPath(basepath, "d");
3121         mkdirRecurse(path);
3122         mkdirRecurse(path); // should not throw
3123     }
3124 
3125     version (Windows)
3126     {
3127         assertThrown!FileException(mkdirRecurse(`1:\foobar`));
3128     }
3129 
3130     // https://issues.dlang.org/show_bug.cgi?id=3570
3131     {
3132         immutable basepath = deleteme ~ "_dir";
3133         version (Windows)
3134         {
3135             immutable path = basepath ~ "\\fake\\here\\";
3136         }
3137         else version (Posix)
3138         {
3139             immutable path = basepath ~ `/fake/here/`;
3140         }
3141 
3142         mkdirRecurse(path);
3143         assert(basepath.exists && basepath.isDir);
3144         scope(exit) () @trusted { rmdirRecurse(basepath); }();
3145         assert(path.exists && path.isDir);
3146     }
3147 }
3148 
3149 /****************************************************
3150 Remove directory `pathname`.
3151 
3152 Params:
3153     pathname = Range or string specifying the directory name
3154 
3155 Throws: $(LREF FileException) on error.
3156  */
3157 void rmdir(R)(R pathname)
3158 if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)
3159 {
3160     // Place outside of @trusted block
3161     auto pathz = pathname.tempCString!FSChar();
3162 
3163     version (Windows)
3164     {
3165         static auto trustedRmdir(scope const(FSChar)* pathz) @trusted
3166         {
3167             return RemoveDirectoryW(pathz);
3168         }
3169     }
3170     else version (Posix)
3171     {
3172         static auto trustedRmdir(scope const(FSChar)* pathz) @trusted
3173         {
3174             return core.sys.posix.unistd.rmdir(pathz) == 0;
3175         }
3176     }
3177     static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
3178         alias pathStr = pathname;
3179     else
3180         string pathStr = null;
3181     cenforce(trustedRmdir(pathz), pathStr, pathz);
3182 }
3183 
3184 /// ditto
3185 void rmdir(R)(auto ref R pathname)
3186 if (isConvertibleToString!R)
3187 {
3188     rmdir!(StringTypeOf!R)(pathname);
3189 }
3190 
3191 @safe unittest
3192 {
3193     static assert(__traits(compiles, rmdir(TestAliasedString(null))));
3194 }
3195 
3196 ///
3197 @safe unittest
3198 {
3199     auto dir = deleteme ~ "dir";
3200 
3201     dir.mkdir;
3202     assert(dir.exists);
3203     dir.rmdir;
3204     assert(!dir.exists);
3205 }
3206 
3207 /++
3208     $(BLUE This function is POSIX-Only.)
3209 
3210     Creates a symbolic _link (_symlink).
3211 
3212     Params:
3213         original = The file that is being linked. This is the target path that's
3214             stored in the _symlink. A relative path is relative to the created
3215             _symlink.
3216         link = The _symlink to create. A relative path is relative to the
3217             current working directory.
3218 
3219     Throws:
3220         $(LREF FileException) on error (which includes if the _symlink already
3221         exists).
3222   +/
3223 version (StdDdoc) void symlink(RO, RL)(RO original, RL link)
3224 if ((isSomeFiniteCharInputRange!RO || isConvertibleToString!RO) &&
3225     (isSomeFiniteCharInputRange!RL || isConvertibleToString!RL));
3226 else version (Posix) void symlink(RO, RL)(RO original, RL link)
3227 if ((isSomeFiniteCharInputRange!RO || isConvertibleToString!RO) &&
3228     (isSomeFiniteCharInputRange!RL || isConvertibleToString!RL))
3229 {
3230     static if (isConvertibleToString!RO || isConvertibleToString!RL)
3231     {
3232         import std.meta : staticMap;
3233         alias Types = staticMap!(convertToString, RO, RL);
3234         symlink!Types(original, link);
3235     }
3236     else
3237     {
3238         import std.conv : text;
3239         auto oz = original.tempCString();
3240         auto lz = link.tempCString();
3241         alias posixSymlink = core.sys.posix.unistd.symlink;
3242         immutable int result = () @trusted { return posixSymlink(oz, lz); } ();
3243         cenforce(result == 0, text(link));
3244     }
3245 }
3246 
3247 version (Posix) @safe unittest
3248 {
3249     if (system_directory.exists)
3250     {
3251         immutable symfile = deleteme ~ "_slink\0";
3252         scope(exit) if (symfile.exists) symfile.remove();
3253 
3254         symlink(system_directory, symfile);
3255 
3256         assert(symfile.exists);
3257         assert(symfile.isSymlink);
3258         assert(!attrIsSymlink(getAttributes(symfile)));
3259         assert(attrIsSymlink(getLinkAttributes(symfile)));
3260 
3261         assert(attrIsDir(getAttributes(symfile)));
3262         assert(!attrIsDir(getLinkAttributes(symfile)));
3263 
3264         assert(!attrIsFile(getAttributes(symfile)));
3265         assert(!attrIsFile(getLinkAttributes(symfile)));
3266     }
3267 
3268     if (system_file.exists)
3269     {
3270         assert(!system_file.isSymlink);
3271 
3272         immutable symfile = deleteme ~ "_slink\0";
3273         scope(exit) if (symfile.exists) symfile.remove();
3274 
3275         symlink(system_file, symfile);
3276 
3277         assert(symfile.exists);
3278         assert(symfile.isSymlink);
3279         assert(!attrIsSymlink(getAttributes(symfile)));
3280         assert(attrIsSymlink(getLinkAttributes(symfile)));
3281 
3282         assert(!attrIsDir(getAttributes(symfile)));
3283         assert(!attrIsDir(getLinkAttributes(symfile)));
3284 
3285         assert(attrIsFile(getAttributes(symfile)));
3286         assert(!attrIsFile(getLinkAttributes(symfile)));
3287     }
3288 }
3289 
3290 version (Posix) @safe unittest
3291 {
3292     static assert(__traits(compiles,
3293         symlink(TestAliasedString(null), TestAliasedString(null))));
3294 }
3295 
3296 
3297 /++
3298     $(BLUE This function is POSIX-Only.)
3299 
3300     Returns the path to the file pointed to by a symlink. Note that the
3301     path could be either relative or absolute depending on the symlink.
3302     If the path is relative, it's relative to the symlink, not the current
3303     working directory.
3304 
3305     Throws:
3306         $(LREF FileException) on error.
3307   +/
3308 version (StdDdoc) string readLink(R)(R link)
3309 if (isSomeFiniteCharInputRange!R || isConvertibleToString!R);
3310 else version (Posix) string readLink(R)(R link)
3311 if (isSomeFiniteCharInputRange!R || isConvertibleToString!R)
3312 {
3313     static if (isConvertibleToString!R)
3314     {
3315         return readLink!(convertToString!R)(link);
3316     }
3317     else
3318     {
3319         import std.conv : to;
3320         import std.exception : assumeUnique;
3321         alias posixReadlink = core.sys.posix.unistd.readlink;
3322         enum bufferLen = 2048;
3323         enum maxCodeUnits = 6;
3324         char[bufferLen] buffer;
3325         const linkz = link.tempCString();
3326         auto size = () @trusted {
3327             return posixReadlink(linkz, buffer.ptr, buffer.length);
3328         } ();
3329         cenforce(size != -1, to!string(link));
3330 
3331         if (size <= bufferLen - maxCodeUnits)
3332             return to!string(buffer[0 .. size]);
3333 
3334         auto dynamicBuffer = new char[](bufferLen * 3 / 2);
3335 
3336         foreach (i; 0 .. 10)
3337         {
3338             size = () @trusted {
3339                 return posixReadlink(linkz, dynamicBuffer.ptr,
3340                     dynamicBuffer.length);
3341             } ();
3342             cenforce(size != -1, to!string(link));
3343 
3344             if (size <= dynamicBuffer.length - maxCodeUnits)
3345             {
3346                 dynamicBuffer.length = size;
3347                 return () @trusted {
3348                     return assumeUnique(dynamicBuffer);
3349                 } ();
3350             }
3351 
3352             dynamicBuffer.length = dynamicBuffer.length * 3 / 2;
3353         }
3354 
3355         throw new FileException(to!string(link), "Path is too long to read.");
3356     }
3357 }
3358 
3359 version (Posix) @safe unittest
3360 {
3361     import std.exception : assertThrown;
3362     import std.string;
3363 
3364     foreach (file; [system_directory, system_file])
3365     {
3366         if (file.exists)
3367         {
3368             immutable symfile = deleteme ~ "_slink\0";
3369             scope(exit) if (symfile.exists) symfile.remove();
3370 
3371             symlink(file, symfile);
3372             assert(readLink(symfile) == file, format("Failed file: %s", file));
3373         }
3374     }
3375 
3376     assertThrown!FileException(readLink("/doesnotexist"));
3377 }
3378 
3379 version (Posix) @safe unittest
3380 {
3381     static assert(__traits(compiles, readLink(TestAliasedString("foo"))));
3382 }
3383 
3384 version (Posix) @system unittest // input range of dchars
3385 {
3386     mkdirRecurse(deleteme);
3387     scope(exit) if (deleteme.exists) rmdirRecurse(deleteme);
3388     write(deleteme ~ "/f", "");
3389     import std.range.interfaces : InputRange, inputRangeObject;
3390     import std.utf : byChar;
3391     immutable string link = deleteme ~ "/l";
3392     symlink("f", link);
3393     InputRange!(ElementType!string) linkr = inputRangeObject(link);
3394     alias R = typeof(linkr);
3395     static assert(isInputRange!R);
3396     static assert(!isForwardRange!R);
3397     assert(readLink(linkr) == "f");
3398 }
3399 
3400 
3401 /****************************************************
3402  * Get the current working directory.
3403  * Throws: $(LREF FileException) on error.
3404  */
3405 version (Windows) string getcwd() @trusted
3406 {
3407     import std.conv : to;
3408     import std.checkedint : checked;
3409     /* GetCurrentDirectory's return value:
3410         1. function succeeds: the number of characters that are written to
3411     the buffer, not including the terminating null character.
3412         2. function fails: zero
3413         3. the buffer (lpBuffer) is not large enough: the required size of
3414     the buffer, in characters, including the null-terminating character.
3415     */
3416     version (StdUnittest)
3417         enum BUF_SIZE = 10;     // trigger reallocation code
3418     else
3419         enum BUF_SIZE = 4096;   // enough for most common case
3420     wchar[BUF_SIZE] buffW = void;
3421     immutable n = cenforce(GetCurrentDirectoryW(to!DWORD(buffW.length), buffW.ptr),
3422             "getcwd");
3423     // we can do it because toUTFX always produces a fresh string
3424     if (n < buffW.length)
3425     {
3426         return buffW[0 .. n].to!string;
3427     }
3428     else //staticBuff isn't enough
3429     {
3430         auto cn = checked(n);
3431         auto ptr = cast(wchar*) malloc((cn * wchar.sizeof).get);
3432         scope(exit) free(ptr);
3433         immutable n2 = GetCurrentDirectoryW(cn.get, ptr);
3434         cenforce(n2 && n2 < cn, "getcwd");
3435         return ptr[0 .. n2].to!string;
3436     }
3437 }
3438 else version (Solaris) string getcwd() @trusted
3439 {
3440     /* BUF_SIZE >= PATH_MAX */
3441     enum BUF_SIZE = 4096;
3442     /* The user should be able to specify any size buffer > 0 */
3443     auto p = cenforce(core.sys.posix.unistd.getcwd(null, BUF_SIZE),
3444             "cannot get cwd");
3445     scope(exit) core.stdc.stdlib.free(p);
3446     return p[0 .. core.stdc..string.strlen(p)].idup;
3447 }
3448 else version (Posix) string getcwd() @trusted
3449 {
3450     auto p = cenforce(core.sys.posix.unistd.getcwd(null, 0),
3451             "cannot get cwd");
3452     scope(exit) core.stdc.stdlib.free(p);
3453     return p[0 .. core.stdc..string.strlen(p)].idup;
3454 }
3455 
3456 ///
3457 @safe unittest
3458 {
3459     auto s = getcwd();
3460     assert(s.length);
3461 }
3462 
3463 /**
3464  * Returns the full path of the current executable.
3465  *
3466  * Returns:
3467  *     The path of the executable as a `string`.
3468  *
3469  * Throws:
3470  * $(REF1 Exception, object)
3471  */
3472 @trusted string thisExePath()
3473 {
3474     version (Darwin)
3475     {
3476         import core.sys.darwin.mach.dyld : _NSGetExecutablePath;
3477         import core.sys.posix.stdlib : realpath;
3478         import std.conv : to;
3479         import std.exception : errnoEnforce;
3480 
3481         uint size;
3482 
3483         _NSGetExecutablePath(null, &size); // get the length of the path
3484         auto buffer = new char[size];
3485         _NSGetExecutablePath(buffer.ptr, &size);
3486 
3487         auto absolutePath = realpath(buffer.ptr, null); // let the function allocate
3488 
3489         scope (exit)
3490         {
3491             if (absolutePath)
3492                 free(absolutePath);
3493         }
3494 
3495         errnoEnforce(absolutePath);
3496         return to!(string)(absolutePath);
3497     }
3498     else version (linux)
3499     {
3500         return readLink("/proc/self/exe");
3501     }
3502     else version (Windows)
3503     {
3504         import std.conv : to;
3505         import std.exception : enforce;
3506 
3507         wchar[MAX_PATH] buf;
3508         wchar[] buffer = buf[];
3509 
3510         while (true)
3511         {
3512             auto len = GetModuleFileNameW(null, buffer.ptr, cast(DWORD) buffer.length);
3513             wenforce(len);
3514             if (len != buffer.length)
3515                 return to!(string)(buffer[0 .. len]);
3516             buffer.length *= 2;
3517         }
3518     }
3519     else version (DragonFlyBSD)
3520     {
3521         import core.sys.dragonflybsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME;
3522         import std.exception : errnoEnforce, assumeUnique;
3523 
3524         int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
3525         size_t len;
3526 
3527         auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
3528         errnoEnforce(result == 0);
3529 
3530         auto buffer = new char[len - 1];
3531         result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
3532         errnoEnforce(result == 0);
3533 
3534         return buffer.assumeUnique;
3535     }
3536     else version (FreeBSD)
3537     {
3538         import core.sys.freebsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME;
3539         import std.exception : errnoEnforce, assumeUnique;
3540 
3541         int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
3542         size_t len;
3543 
3544         auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
3545         errnoEnforce(result == 0);
3546 
3547         auto buffer = new char[len - 1];
3548         result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
3549         errnoEnforce(result == 0);
3550 
3551         return buffer.assumeUnique;
3552     }
3553     else version (NetBSD)
3554     {
3555         import core.sys.netbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_PATHNAME;
3556         import std.exception : errnoEnforce, assumeUnique;
3557 
3558         int[4] mib = [CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME];
3559         size_t len;
3560 
3561         auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
3562         errnoEnforce(result == 0);
3563 
3564         auto buffer = new char[len - 1];
3565         result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
3566         errnoEnforce(result == 0);
3567 
3568         return buffer.assumeUnique;
3569     }
3570     else version (OpenBSD)
3571     {
3572         import core.sys.openbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_ARGV;
3573         import core.sys.posix.unistd : getpid;
3574         import std.conv : to;
3575         import std.exception : enforce, errnoEnforce;
3576         import std.process : searchPathFor;
3577 
3578         int[4] mib = [CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV];
3579         size_t len;
3580 
3581         auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0);
3582         errnoEnforce(result == 0);
3583 
3584         auto argv = new char*[len - 1];
3585         result = sysctl(mib.ptr, mib.length, argv.ptr, &len, null, 0);
3586         errnoEnforce(result == 0);
3587 
3588         auto argv0 = argv[0];
3589         if (*argv0 == '/' || *argv0 == '.')
3590         {
3591             import core.sys.posix.stdlib : realpath;
3592             auto absolutePath = realpath(argv0, null);
3593             scope (exit)
3594             {
3595                 if (absolutePath)
3596                     free(absolutePath);
3597             }
3598             errnoEnforce(absolutePath);
3599             return to!(string)(absolutePath);
3600         }
3601         else
3602         {
3603             auto absolutePath = searchPathFor(to!string(argv0));
3604             errnoEnforce(absolutePath);
3605             return absolutePath;
3606         }
3607     }
3608     else version (Solaris)
3609     {
3610         import core.sys.posix.unistd : getpid;
3611         import std.string : format;
3612 
3613         // Only Solaris 10 and later
3614         return readLink(format("/proc/%d/path/a.out", getpid()));
3615     }
3616     else version (Hurd)
3617     {
3618         return readLink("/proc/self/exe");
3619     }
3620     else
3621         static assert(0, "thisExePath is not supported on this platform");
3622 }
3623 
3624 ///
3625 @safe unittest
3626 {
3627     import std.path : isAbsolute;
3628     auto path = thisExePath();
3629 
3630     assert(path.exists);
3631     assert(path.isAbsolute);
3632     assert(path.isFile);
3633 }
3634 
3635 version (StdDdoc)
3636 {
3637     /++
3638         Info on a file, similar to what you'd get from stat on a POSIX system.
3639       +/
3640     struct DirEntry
3641     {
3642         @safe:
3643         /++
3644             Constructs a `DirEntry` for the given file (or directory).
3645 
3646             Params:
3647                 path = The file (or directory) to get a DirEntry for.
3648 
3649             Throws:
3650                 $(LREF FileException) if the file does not exist.
3651         +/
3652         this(return scope string path);
3653 
3654         version (Windows)
3655         {
3656             private this(string path, in WIN32_FIND_DATAW *fd);
3657         }
3658         else version (Posix)
3659         {
3660             private this(string path, core.sys.posix.dirent.dirent* fd);
3661         }
3662 
3663         /++
3664             Returns the path to the file represented by this `DirEntry`.
3665 
3666 Example:
3667 --------------------
3668 auto de1 = DirEntry("/etc/fonts/fonts.conf");
3669 assert(de1.name == "/etc/fonts/fonts.conf");
3670 
3671 auto de2 = DirEntry("/usr/share/include");
3672 assert(de2.name == "/usr/share/include");
3673 --------------------
3674           +/
3675         @property string name() const return scope;
3676 
3677 
3678         /++
3679             Returns whether the file represented by this `DirEntry` is a
3680             directory.
3681 
3682 Example:
3683 --------------------
3684 auto de1 = DirEntry("/etc/fonts/fonts.conf");
3685 assert(!de1.isDir);
3686 
3687 auto de2 = DirEntry("/usr/share/include");
3688 assert(de2.isDir);
3689 --------------------
3690           +/
3691         @property bool isDir() scope;
3692 
3693 
3694         /++
3695             Returns whether the file represented by this `DirEntry` is a file.
3696 
3697             On Windows, if a file is not a directory, then it's a file. So,
3698             either `isFile` or `isDir` will return `true`.
3699 
3700             On POSIX systems, if `isFile` is `true`, that indicates that
3701             the file is a regular file (e.g. not a block not device). So, on
3702             POSIX systems, it's possible for both `isFile` and `isDir` to
3703             be `false` for a particular file (in which case, it's a special
3704             file). You can use `attributes` or `statBuf` to get more
3705             information about a special file (see the stat man page for more
3706             details).
3707 
3708 Example:
3709 --------------------
3710 auto de1 = DirEntry("/etc/fonts/fonts.conf");
3711 assert(de1.isFile);
3712 
3713 auto de2 = DirEntry("/usr/share/include");
3714 assert(!de2.isFile);
3715 --------------------
3716           +/
3717         @property bool isFile() scope;
3718 
3719         /++
3720             Returns whether the file represented by this `DirEntry` is a
3721             symbolic link.
3722 
3723             On Windows, return `true` when the file is either a symbolic
3724             link or a junction point.
3725           +/
3726         @property bool isSymlink() scope;
3727 
3728         /++
3729             Returns the size of the file represented by this `DirEntry`
3730             in bytes.
3731           +/
3732         @property ulong size() scope;
3733 
3734         /++
3735             $(BLUE This function is Windows-Only.)
3736 
3737             Returns the creation time of the file represented by this
3738             `DirEntry`.
3739           +/
3740         @property SysTime timeCreated() const scope;
3741 
3742         /++
3743             Returns the time that the file represented by this `DirEntry` was
3744             last accessed.
3745 
3746             Note that many file systems do not update the access time for files
3747             (generally for performance reasons), so there's a good chance that
3748             `timeLastAccessed` will return the same value as
3749             `timeLastModified`.
3750           +/
3751         @property SysTime timeLastAccessed() scope;
3752 
3753         /++
3754             Returns the time that the file represented by this `DirEntry` was
3755             last modified.
3756           +/
3757         @property SysTime timeLastModified() scope;
3758 
3759         /++
3760             $(BLUE This function is POSIX-Only.)
3761 
3762             Returns the time that the file represented by this `DirEntry` was
3763             last changed (not only in contents, but also in permissions or ownership).
3764           +/
3765         @property SysTime timeStatusChanged() const scope;
3766 
3767         /++
3768             Returns the _attributes of the file represented by this `DirEntry`.
3769 
3770             Note that the file _attributes on Windows and POSIX systems are
3771             completely different. On, Windows, they're what is returned by
3772             `GetFileAttributes`
3773             $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, GetFileAttributes)
3774             Whereas, an POSIX systems, they're the `st_mode` value which is
3775             part of the `stat` struct gotten by calling `stat`.
3776 
3777             On POSIX systems, if the file represented by this `DirEntry` is a
3778             symbolic link, then _attributes are the _attributes of the file
3779             pointed to by the symbolic link.
3780           +/
3781         @property uint attributes() scope;
3782 
3783         /++
3784             On POSIX systems, if the file represented by this `DirEntry` is a
3785             symbolic link, then `linkAttributes` are the attributes of the
3786             symbolic link itself. Otherwise, `linkAttributes` is identical to
3787             `attributes`.
3788 
3789             On Windows, `linkAttributes` is identical to `attributes`. It
3790             exists on Windows so that you don't have to special-case code for
3791             Windows when dealing with symbolic links.
3792           +/
3793         @property uint linkAttributes() scope;
3794 
3795         version (Windows)
3796             alias stat_t = void*;
3797 
3798         /++
3799             $(BLUE This function is POSIX-Only.)
3800 
3801             The `stat` struct gotten from calling `stat`.
3802           +/
3803         @property stat_t statBuf() scope;
3804     }
3805 }
3806 else version (Windows)
3807 {
3808     struct DirEntry
3809     {
3810     @safe:
3811     public:
3812         alias name this;
3813 
3814         this(return scope string path)
3815         {
3816             import std.datetime.systime : FILETIMEToSysTime;
3817 
3818             if (!path.exists())
3819                 throw new FileException(path, "File does not exist");
3820 
3821             _name = path;
3822 
3823             with (getFileAttributesWin(path))
3824             {
3825                 _size = makeUlong(nFileSizeLow, nFileSizeHigh);
3826                 _timeCreated = FILETIMEToSysTime(&ftCreationTime);
3827                 _timeLastAccessed = FILETIMEToSysTime(&ftLastAccessTime);
3828                 _timeLastModified = FILETIMEToSysTime(&ftLastWriteTime);
3829                 _attributes = dwFileAttributes;
3830             }
3831         }
3832 
3833         private this(string path, WIN32_FIND_DATAW *fd) @trusted
3834         {
3835             import core.stdc.wchar_ : wcslen;
3836             import std.conv : to;
3837             import std.datetime.systime : FILETIMEToSysTime;
3838             import std.path : buildPath;
3839 
3840             fd.cFileName[$ - 1] = 0;
3841 
3842             size_t clength = wcslen(&fd.cFileName[0]);
3843             _name = buildPath(path, fd.cFileName[0 .. clength].to!string);
3844             _size = (cast(ulong) fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
3845             _timeCreated = FILETIMEToSysTime(&fd.ftCreationTime);
3846             _timeLastAccessed = FILETIMEToSysTime(&fd.ftLastAccessTime);
3847             _timeLastModified = FILETIMEToSysTime(&fd.ftLastWriteTime);
3848             _attributes = fd.dwFileAttributes;
3849         }
3850 
3851         @property string name() const pure nothrow return scope
3852         {
3853             return _name;
3854         }
3855 
3856         @property bool isDir() const pure nothrow scope
3857         {
3858             return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
3859         }
3860 
3861         @property bool isFile() const pure nothrow scope
3862         {
3863             //Are there no options in Windows other than directory and file?
3864             //If there are, then this probably isn't the best way to determine
3865             //whether this DirEntry is a file or not.
3866             return !isDir;
3867         }
3868 
3869         @property bool isSymlink() const pure nothrow scope
3870         {
3871             return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
3872         }
3873 
3874         @property ulong size() const pure nothrow scope
3875         {
3876             return _size;
3877         }
3878 
3879         @property SysTime timeCreated() const pure nothrow return scope
3880         {
3881             return cast(SysTime)_timeCreated;
3882         }
3883 
3884         @property SysTime timeLastAccessed() const pure nothrow return scope
3885         {
3886             return cast(SysTime)_timeLastAccessed;
3887         }
3888 
3889         @property SysTime timeLastModified() const pure nothrow return scope
3890         {
3891             return cast(SysTime)_timeLastModified;
3892         }
3893 
3894         @property uint attributes() const pure nothrow scope
3895         {
3896             return _attributes;
3897         }
3898 
3899         @property uint linkAttributes() const pure nothrow scope
3900         {
3901             return _attributes;
3902         }
3903 
3904     private:
3905         string _name; /// The file or directory represented by this DirEntry.
3906 
3907         SysTime _timeCreated;      /// The time when the file was created.
3908         SysTime _timeLastAccessed; /// The time when the file was last accessed.
3909         SysTime _timeLastModified; /// The time when the file was last modified.
3910 
3911         ulong _size;       /// The size of the file in bytes.
3912         uint  _attributes; /// The file attributes from WIN32_FIND_DATAW.
3913     }
3914 }
3915 else version (Posix)
3916 {
3917     struct DirEntry
3918     {
3919     @safe:
3920     public:
3921         alias name this;
3922 
3923         this(return scope string path)
3924         {
3925             if (!path.exists)
3926                 throw new FileException(path, "File does not exist");
3927 
3928             _name = path;
3929 
3930             _didLStat = false;
3931             _didStat = false;
3932             _dTypeSet = false;
3933         }
3934 
3935         private this(string path, core.sys.posix.dirent.dirent* fd) @safe
3936         {
3937             import std.path : buildPath;
3938 
3939             static if (is(typeof(fd.d_namlen)))
3940                 immutable len = fd.d_namlen;
3941             else
3942                 immutable len = (() @trusted => core.stdc..string.strlen(fd.d_name.ptr))();
3943 
3944             _name = buildPath(path, (() @trusted => fd.d_name.ptr[0 .. len])());
3945 
3946             _didLStat = false;
3947             _didStat = false;
3948 
3949             //fd_d_type doesn't work for all file systems,
3950             //in which case the result is DT_UNKOWN. But we
3951             //can determine the correct type from lstat, so
3952             //we'll only set the dtype here if we could
3953             //correctly determine it (not lstat in the case
3954             //of DT_UNKNOWN in case we don't ever actually
3955             //need the dtype, thus potentially avoiding the
3956             //cost of calling lstat).
3957             static if (__traits(compiles, fd.d_type != DT_UNKNOWN))
3958             {
3959                 if (fd.d_type != DT_UNKNOWN)
3960                 {
3961                     _dType = fd.d_type;
3962                     _dTypeSet = true;
3963                 }
3964                 else
3965                     _dTypeSet = false;
3966             }
3967             else
3968             {
3969                 // e.g. Solaris does not have the d_type member
3970                 _dTypeSet = false;
3971             }
3972         }
3973 
3974         @property string name() const pure nothrow return scope
3975         {
3976             return _name;
3977         }
3978 
3979         @property bool isDir() scope
3980         {
3981             _ensureStatOrLStatDone();
3982 
3983             return (_statBuf.st_mode & S_IFMT) == S_IFDIR;
3984         }
3985 
3986         @property bool isFile() scope
3987         {
3988             _ensureStatOrLStatDone();
3989 
3990             return (_statBuf.st_mode & S_IFMT) == S_IFREG;
3991         }
3992 
3993         @property bool isSymlink() scope
3994         {
3995             _ensureLStatDone();
3996 
3997             return (_lstatMode & S_IFMT) == S_IFLNK;
3998         }
3999 
4000         @property ulong size() scope
4001         {
4002             _ensureStatDone();
4003             return _statBuf.st_size;
4004         }
4005 
4006         @property SysTime timeStatusChanged() scope
4007         {
4008             _ensureStatDone();
4009 
4010             return statTimeToStdTime!'c'(_statBuf);
4011         }
4012 
4013         @property SysTime timeLastAccessed() scope
4014         {
4015             _ensureStatDone();
4016 
4017             return statTimeToStdTime!'a'(_statBuf);
4018         }
4019 
4020         @property SysTime timeLastModified() scope
4021         {
4022             _ensureStatDone();
4023 
4024             return statTimeToStdTime!'m'(_statBuf);
4025         }
4026 
4027         @property uint attributes() scope
4028         {
4029             _ensureStatDone();
4030 
4031             return _statBuf.st_mode;
4032         }
4033 
4034         @property uint linkAttributes() scope
4035         {
4036             _ensureLStatDone();
4037 
4038             return _lstatMode;
4039         }
4040 
4041         @property stat_t statBuf() scope
4042         {
4043             _ensureStatDone();
4044 
4045             return _statBuf;
4046         }
4047 
4048     private:
4049         /++
4050             This is to support lazy evaluation, because doing stat's is
4051             expensive and not always needed.
4052          +/
4053         void _ensureStatDone() @trusted scope
4054         {
4055             if (_didStat)
4056                 return;
4057 
4058             cenforce(stat(_name.tempCString(), &_statBuf) == 0,
4059                     "Failed to stat file `" ~ _name ~ "'");
4060 
4061             _didStat = true;
4062         }
4063 
4064         /++
4065             This is to support lazy evaluation, because doing stat's is
4066             expensive and not always needed.
4067 
4068             Try both stat and lstat for isFile and isDir
4069             to detect broken symlinks.
4070          +/
4071         void _ensureStatOrLStatDone() @trusted scope
4072         {
4073             if (_didStat)
4074                 return;
4075 
4076             if (stat(_name.tempCString(), &_statBuf) != 0)
4077             {
4078                 _ensureLStatDone();
4079 
4080                 _statBuf = stat_t.init;
4081                 _statBuf.st_mode = S_IFLNK;
4082             }
4083             else
4084             {
4085                 _didStat = true;
4086             }
4087         }
4088 
4089         /++
4090             This is to support lazy evaluation, because doing stat's is
4091             expensive and not always needed.
4092          +/
4093         void _ensureLStatDone() @trusted scope
4094         {
4095             if (_didLStat)
4096                 return;
4097 
4098             stat_t statbuf = void;
4099             cenforce(lstat(_name.tempCString(), &statbuf) == 0,
4100                 "Failed to stat file `" ~ _name ~ "'");
4101 
4102             _lstatMode = statbuf.st_mode;
4103 
4104             _dTypeSet = true;
4105             _didLStat = true;
4106         }
4107 
4108         string _name; /// The file or directory represented by this DirEntry.
4109 
4110         stat_t _statBuf = void;   /// The result of stat().
4111         uint  _lstatMode;         /// The stat mode from lstat().
4112         ubyte _dType;             /// The type of the file.
4113 
4114         bool _didLStat = false;   /// Whether lstat() has been called for this DirEntry.
4115         bool _didStat = false;    /// Whether stat() has been called for this DirEntry.
4116         bool _dTypeSet = false;   /// Whether the dType of the file has been set.
4117     }
4118 }
4119 
4120 @system unittest
4121 {
4122     version (Windows)
4123     {
4124         if ("C:\\Program Files\\".exists)
4125         {
4126             auto de = DirEntry("C:\\Program Files\\");
4127             assert(!de.isFile);
4128             assert(de.isDir);
4129             assert(!de.isSymlink);
4130         }
4131 
4132         if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
4133         {
4134             auto de = DirEntry("C:\\Documents and Settings\\");
4135             assert(de.isSymlink);
4136         }
4137 
4138         if ("C:\\Windows\\system.ini".exists)
4139         {
4140             auto de = DirEntry("C:\\Windows\\system.ini");
4141             assert(de.isFile);
4142             assert(!de.isDir);
4143             assert(!de.isSymlink);
4144         }
4145     }
4146     else version (Posix)
4147     {
4148         import std.exception : assertThrown;
4149 
4150         if (system_directory.exists)
4151         {
4152             {
4153                 auto de = DirEntry(system_directory);
4154                 assert(!de.isFile);
4155                 assert(de.isDir);
4156                 assert(!de.isSymlink);
4157             }
4158 
4159             immutable symfile = deleteme ~ "_slink\0";
4160             scope(exit) if (symfile.exists) symfile.remove();
4161 
4162             core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
4163 
4164             {
4165                 auto de = DirEntry(symfile);
4166                 assert(!de.isFile);
4167                 assert(de.isDir);
4168                 assert(de.isSymlink);
4169             }
4170 
4171             symfile.remove();
4172             core.sys.posix.unistd.symlink((deleteme ~ "_broken_symlink\0").ptr, symfile.ptr);
4173 
4174             {
4175                 // https://issues.dlang.org/show_bug.cgi?id=8298
4176                 DirEntry de = DirEntry(symfile);
4177 
4178                 assert(!de.isFile);
4179                 assert(!de.isDir);
4180                 assert(de.isSymlink);
4181                 assertThrown!FileException(de.size);
4182                 assertThrown!FileException(de.timeStatusChanged);
4183                 assertThrown!FileException(de.timeLastAccessed);
4184                 assertThrown!FileException(de.timeLastModified);
4185                 assertThrown!FileException(de.attributes);
4186                 assertThrown!FileException(de.statBuf);
4187                 assert(symfile.exists);
4188                 symfile.remove();
4189             }
4190         }
4191 
4192         if (system_file.exists)
4193         {
4194             auto de = DirEntry(system_file);
4195             assert(de.isFile);
4196             assert(!de.isDir);
4197             assert(!de.isSymlink);
4198         }
4199     }
4200 }
4201 
4202 alias PreserveAttributes = Flag!"preserveAttributes";
4203 
4204 version (StdDdoc)
4205 {
4206     /// Defaults to `Yes.preserveAttributes` on Windows, and the opposite on all other platforms.
4207     PreserveAttributes preserveAttributesDefault;
4208 }
4209 else version (Windows)
4210 {
4211     enum preserveAttributesDefault = Yes.preserveAttributes;
4212 }
4213 else
4214 {
4215     enum preserveAttributesDefault = No.preserveAttributes;
4216 }
4217 
4218 /***************************************************
4219 Copy file `from` _to file `to`. File timestamps are preserved.
4220 File attributes are preserved, if `preserve` equals `Yes.preserveAttributes`.
4221 On Windows only `Yes.preserveAttributes` (the default on Windows) is supported.
4222 If the target file exists, it is overwritten.
4223 
4224 Params:
4225     from = string or range of characters representing the existing file name
4226     to = string or range of characters representing the target file name
4227     preserve = whether to _preserve the file attributes
4228 
4229 Throws: $(LREF FileException) on error.
4230  */
4231 void copy(RF, RT)(RF from, RT to, PreserveAttributes preserve = preserveAttributesDefault)
4232 if (isSomeFiniteCharInputRange!RF && !isConvertibleToString!RF &&
4233     isSomeFiniteCharInputRange!RT && !isConvertibleToString!RT)
4234 {
4235     // Place outside of @trusted block
4236     auto fromz = from.tempCString!FSChar();
4237     auto toz = to.tempCString!FSChar();
4238 
4239     static if (isNarrowString!RF && is(immutable ElementEncodingType!RF == immutable char))
4240         alias f = from;
4241     else
4242         enum string f = null;
4243 
4244     static if (isNarrowString!RT && is(immutable ElementEncodingType!RT == immutable char))
4245         alias t = to;
4246     else
4247         enum string t = null;
4248 
4249     copyImpl(f, t, fromz, toz, preserve);
4250 }
4251 
4252 /// ditto
4253 void copy(RF, RT)(auto ref RF from, auto ref RT to, PreserveAttributes preserve = preserveAttributesDefault)
4254 if (isConvertibleToString!RF || isConvertibleToString!RT)
4255 {
4256     import std.meta : staticMap;
4257     alias Types = staticMap!(convertToString, RF, RT);
4258     copy!Types(from, to, preserve);
4259 }
4260 
4261 ///
4262 @safe unittest
4263 {
4264     auto source = deleteme ~ "source";
4265     auto target = deleteme ~ "target";
4266     auto targetNonExistent = deleteme ~ "target2";
4267 
4268     scope(exit) source.remove, target.remove, targetNonExistent.remove;
4269 
4270     source.write("source");
4271     target.write("target");
4272 
4273     assert(target.readText == "target");
4274 
4275     source.copy(target);
4276     assert(target.readText == "source");
4277 
4278     source.copy(targetNonExistent);
4279     assert(targetNonExistent.readText == "source");
4280 }
4281 
4282 // https://issues.dlang.org/show_bug.cgi?id=15319
4283 @safe unittest
4284 {
4285     assert(__traits(compiles, copy("from.txt", "to.txt")));
4286 }
4287 
4288 private void copyImpl(scope const(char)[] f, scope const(char)[] t,
4289                       scope const(FSChar)* fromz, scope const(FSChar)* toz,
4290                       PreserveAttributes preserve) @trusted
4291 {
4292     version (Windows)
4293     {
4294         assert(preserve == Yes.preserveAttributes);
4295         immutable result = CopyFileW(fromz, toz, false);
4296         if (!result)
4297         {
4298             import core.stdc.wchar_ : wcslen;
4299             import std.conv : to;
4300             import std.format : format;
4301 
4302             /++
4303             Reference resources: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-copyfilew
4304             Because OS copyfilew handles both source and destination paths,
4305             the GetLastError does not accurately locate whether the error is for the source or destination.
4306             +/
4307             if (!f)
4308                 f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]);
4309             if (!t)
4310                 t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
4311 
4312             throw new FileException(format!"Copy from %s to %s"(f, t));
4313         }
4314     }
4315     else version (Posix)
4316     {
4317         static import core.stdc.stdio;
4318         import std.conv : to, octal;
4319 
4320         immutable fdr = core.sys.posix.fcntl.open(fromz, O_RDONLY);
4321         cenforce(fdr != -1, f, fromz);
4322         scope(exit) core.sys.posix.unistd.close(fdr);
4323 
4324         stat_t statbufr = void;
4325         cenforce(fstat(fdr, &statbufr) == 0, f, fromz);
4326         //cenforce(core.sys.posix.sys.stat.fstat(fdr, &statbufr) == 0, f, fromz);
4327 
4328         immutable fdw = core.sys.posix.fcntl.open(toz,
4329                 O_CREAT | O_WRONLY, octal!666);
4330         cenforce(fdw != -1, t, toz);
4331         {
4332             scope(failure) core.sys.posix.unistd.close(fdw);
4333 
4334             stat_t statbufw = void;
4335             cenforce(fstat(fdw, &statbufw) == 0, t, toz);
4336             if (statbufr.st_dev == statbufw.st_dev && statbufr.st_ino == statbufw.st_ino)
4337                 throw new FileException(t, "Source and destination are the same file");
4338         }
4339 
4340         scope(failure) core.stdc.stdio.remove(toz);
4341         {
4342             scope(failure) core.sys.posix.unistd.close(fdw);
4343             cenforce(ftruncate(fdw, 0) == 0, t, toz);
4344 
4345             auto BUFSIZ = 4096u * 16;
4346             auto buf = core.stdc.stdlib.malloc(BUFSIZ);
4347             if (!buf)
4348             {
4349                 BUFSIZ = 4096;
4350                 buf = core.stdc.stdlib.malloc(BUFSIZ);
4351                 if (!buf)
4352                 {
4353                     import core.exception : onOutOfMemoryError;
4354                     onOutOfMemoryError();
4355                 }
4356             }
4357             scope(exit) core.stdc.stdlib.free(buf);
4358 
4359             for (auto size = statbufr.st_size; size; )
4360             {
4361                 immutable toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size;
4362                 cenforce(
4363                     core.sys.posix.unistd.read(fdr, buf, toxfer) == toxfer
4364                     && core.sys.posix.unistd.write(fdw, buf, toxfer) == toxfer,
4365                     f, fromz);
4366                 assert(size >= toxfer);
4367                 size -= toxfer;
4368             }
4369             if (preserve)
4370                 cenforce(fchmod(fdw, to!mode_t(statbufr.st_mode)) == 0, f, fromz);
4371         }
4372 
4373         cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz);
4374 
4375         setTimesImpl(t, toz, statbufr.statTimeToStdTime!'a', statbufr.statTimeToStdTime!'m');
4376     }
4377 }
4378 
4379 // https://issues.dlang.org/show_bug.cgi?id=14817
4380 @safe unittest
4381 {
4382     import std.algorithm, std.file;
4383     auto t1 = deleteme, t2 = deleteme~"2";
4384     scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
4385     write(t1, "11");
4386     copy(t1, t2);
4387     assert(readText(t2) == "11");
4388     write(t1, "2");
4389     copy(t1, t2);
4390     assert(readText(t2) == "2");
4391 
4392     import std.utf : byChar;
4393     copy(t1.byChar, t2.byChar);
4394     assert(readText(t2.byChar) == "2");
4395 
4396 // https://issues.dlang.org/show_bug.cgi?id=20370
4397     version (Windows)
4398         assert(t1.timeLastModified == t2.timeLastModified);
4399     else static if (is(typeof(&utimensat)) || is(typeof(&setattrlist)))
4400         assert(t1.timeLastModified == t2.timeLastModified);
4401     else
4402         assert(abs(t1.timeLastModified - t2.timeLastModified) < dur!"usecs"(1));
4403 }
4404 
4405 // https://issues.dlang.org/show_bug.cgi?id=11434
4406 @safe version (Posix) @safe unittest
4407 {
4408     import std.conv : octal;
4409     auto t1 = deleteme, t2 = deleteme~"2";
4410     scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
4411     write(t1, "1");
4412     setAttributes(t1, octal!767);
4413     copy(t1, t2, Yes.preserveAttributes);
4414     assert(readText(t2) == "1");
4415     assert(getAttributes(t2) == octal!100767);
4416 }
4417 
4418 // https://issues.dlang.org/show_bug.cgi?id=15865
4419 @safe unittest
4420 {
4421     import std.exception : assertThrown;
4422     auto t = deleteme;
4423     write(t, "a");
4424     scope(exit) t.remove();
4425     assertThrown!FileException(copy(t, t));
4426     assert(readText(t) == "a");
4427 }
4428 
4429 // https://issues.dlang.org/show_bug.cgi?id=19834
4430 version (Windows) @safe unittest
4431 {
4432     import std.exception : collectException;
4433     import std.algorithm.searching : startsWith;
4434     import std.format : format;
4435 
4436     auto f = deleteme;
4437     auto t = f ~ "2";
4438     auto ex = collectException(copy(f, t));
4439     assert(ex.msg.startsWith(format!"Copy from %s to %s"(f, t)));
4440 }
4441 
4442 /++
4443     Remove directory and all of its content and subdirectories,
4444     recursively.
4445 
4446     Params:
4447         pathname = the path of the directory to completely remove
4448         de = The $(LREF DirEntry) to remove
4449 
4450     Throws:
4451         $(LREF FileException) if there is an error (including if the given
4452         file is not a directory).
4453  +/
4454 void rmdirRecurse(scope const(char)[] pathname) @safe
4455 {
4456     //No references to pathname will be kept after rmdirRecurse,
4457     //so the cast is safe
4458     rmdirRecurse(DirEntry((() @trusted => cast(string) pathname)()));
4459 }
4460 
4461 /// ditto
4462 void rmdirRecurse(ref scope DirEntry de) @safe
4463 {
4464     if (!de.isDir)
4465         throw new FileException(de.name, "Not a directory");
4466 
4467     if (de.isSymlink)
4468     {
4469         version (Windows)
4470             rmdir(de.name);
4471         else
4472             remove(de.name);
4473     }
4474     else
4475     {
4476         // dirEntries is @system without DIP1000 because it uses
4477         // a DirIterator with a SafeRefCounted variable, but here, no
4478         // references to the payload are escaped to the outside, so this should
4479         // be @trusted
4480         () @trusted {
4481             // all children, recursively depth-first
4482             foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false))
4483             {
4484                 attrIsDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name);
4485             }
4486         }();
4487 
4488         // the dir itself
4489         rmdir(de.name);
4490     }
4491 }
4492 ///ditto
4493 //Note, without this overload, passing an RValue DirEntry still works, but
4494 //actually fully reconstructs a DirEntry inside the
4495 //"rmdirRecurse(in char[] pathname)" implementation. That is needlessly
4496 //expensive.
4497 //A DirEntry is a bit big (72B), so keeping the "by ref" signature is desirable.
4498 void rmdirRecurse(scope DirEntry de) @safe
4499 {
4500     rmdirRecurse(de);
4501 }
4502 
4503 ///
4504 @system unittest
4505 {
4506     import std.path : buildPath;
4507 
4508     auto dir = deleteme.buildPath("a", "b", "c");
4509 
4510     dir.mkdirRecurse;
4511     assert(dir.exists);
4512 
4513     deleteme.rmdirRecurse;
4514     assert(!dir.exists);
4515     assert(!deleteme.exists);
4516 }
4517 
4518 version (Windows) @system unittest
4519 {
4520     import std.exception : enforce;
4521     auto d = deleteme ~ r".dir\a\b\c\d\e\f\g";
4522     mkdirRecurse(d);
4523     rmdirRecurse(deleteme ~ ".dir");
4524     enforce(!exists(deleteme ~ ".dir"));
4525 }
4526 
4527 version (Posix) @system unittest
4528 {
4529     import std.exception : enforce, collectException;
4530 
4531     collectException(rmdirRecurse(deleteme));
4532     auto d = deleteme~"/a/b/c/d/e/f/g";
4533     enforce(collectException(mkdir(d)));
4534     mkdirRecurse(d);
4535     core.sys.posix.unistd.symlink((deleteme~"/a/b/c\0").ptr,
4536             (deleteme~"/link\0").ptr);
4537     rmdirRecurse(deleteme~"/link");
4538     enforce(exists(d));
4539     rmdirRecurse(deleteme);
4540     enforce(!exists(deleteme));
4541 
4542     d = deleteme~"/a/b/c/d/e/f/g";
4543     mkdirRecurse(d);
4544     const linkTarget = deleteme ~ "/link";
4545     symlink(deleteme ~ "/a/b/c", linkTarget);
4546     rmdirRecurse(deleteme);
4547     enforce(!exists(deleteme));
4548 }
4549 
4550 @safe unittest
4551 {
4552     ubyte[] buf = new ubyte[10];
4553     buf[] = 3;
4554     string unit_file = deleteme ~ "-unittest_write.tmp";
4555     if (exists(unit_file)) remove(unit_file);
4556     write(unit_file, cast(void[]) buf);
4557     void[] buf2 = read(unit_file);
4558     assert(cast(void[]) buf == buf2);
4559 
4560     string unit2_file = deleteme ~ "-unittest_write2.tmp";
4561     copy(unit_file, unit2_file);
4562     buf2 = read(unit2_file);
4563     assert(cast(void[]) buf == buf2);
4564 
4565     remove(unit_file);
4566     assert(!exists(unit_file));
4567     remove(unit2_file);
4568     assert(!exists(unit2_file));
4569 }
4570 
4571 /**
4572  * Dictates directory spanning policy for $(D_PARAM dirEntries) (see below).
4573  */
4574 enum SpanMode
4575 {
4576     /** Only spans one directory. */
4577     shallow,
4578     /** Spans the directory in
4579      $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Post-order,
4580      _depth-first $(B post)-order), i.e. the content of any
4581      subdirectory is spanned before that subdirectory itself. Useful
4582      e.g. when recursively deleting files.  */
4583     depth,
4584     /** Spans the directory in
4585     $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Pre-order, depth-first
4586     $(B pre)-order), i.e. the content of any subdirectory is spanned
4587     right after that subdirectory itself.
4588 
4589     Note that `SpanMode.breadth` will not result in all directory
4590     members occurring before any subdirectory members, i.e. it is not
4591     _true
4592     $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search,
4593     _breadth-first traversal).
4594     */
4595     breadth,
4596 }
4597 
4598 ///
4599 @system unittest
4600 {
4601     import std.algorithm.comparison : equal;
4602     import std.algorithm.iteration : map;
4603     import std.algorithm.sorting : sort;
4604     import std.array : array;
4605     import std.path : buildPath, relativePath;
4606 
4607     auto root = deleteme ~ "root";
4608     scope(exit) root.rmdirRecurse;
4609     root.mkdir;
4610 
4611     root.buildPath("animals").mkdir;
4612     root.buildPath("animals", "cat").mkdir;
4613 
4614     alias removeRoot = (return scope e) => e.relativePath(root);
4615 
4616     assert(root.dirEntries(SpanMode.depth).map!removeRoot.equal(
4617         [buildPath("animals", "cat"), "animals"]));
4618 
4619     assert(root.dirEntries(SpanMode.breadth).map!removeRoot.equal(
4620         ["animals", buildPath("animals", "cat")]));
4621 
4622     root.buildPath("plants").mkdir;
4623 
4624     assert(root.dirEntries(SpanMode.shallow).array.sort.map!removeRoot.equal(
4625         ["animals", "plants"]));
4626 }
4627 
4628 private struct DirIteratorImpl
4629 {
4630   @safe:
4631     SpanMode _mode;
4632     // Whether we should follow symlinked directories while iterating.
4633     // It also indicates whether we should avoid functions which call
4634     // stat (since we should only need lstat in this case and it would
4635     // be more efficient to not call stat in addition to lstat).
4636     bool _followSymlink;
4637     DirEntry _cur;
4638     DirHandle[] _stack;
4639     DirEntry[] _stashed; //used in depth first mode
4640     string _pathPrefix = null;
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(string pathname, SpanMode mode, bool followSymlink)
4797     {
4798         _mode = mode;
4799         _followSymlink = followSymlink;
4800 
4801         if (stepIn(pathname))
4802         {
4803             if (_mode == SpanMode.depth)
4804                 while (mayStepIn())
4805                 {
4806                     auto thisDir = _cur;
4807                     if (stepIn(_cur.name))
4808                     {
4809                         pushExtra(thisDir);
4810                     }
4811                     else
4812                         break;
4813                 }
4814         }
4815     }
4816 
4817     @property bool empty()
4818     {
4819         return _stashed.length == 0 && _stack.length == 0;
4820     }
4821 
4822     @property DirEntry front()
4823     {
4824         return _cur;
4825     }
4826 
4827     void popFront()
4828     {
4829         switch (_mode)
4830         {
4831         case SpanMode.depth:
4832             if (next())
4833             {
4834                 while (mayStepIn())
4835                 {
4836                     auto thisDir = _cur;
4837                     if (stepIn(_cur.name))
4838                     {
4839                         pushExtra(thisDir);
4840                     }
4841                     else
4842                         break;
4843                 }
4844             }
4845             else if (hasExtra())
4846                 _cur = popExtra();
4847             break;
4848         case SpanMode.breadth:
4849             if (mayStepIn())
4850             {
4851                 if (!stepIn(_cur.name))
4852                     while (!empty && !next()){}
4853             }
4854             else
4855                 while (!empty && !next()){}
4856             break;
4857         default:
4858             next();
4859         }
4860     }
4861 
4862     ~this()
4863     {
4864         releaseDirStack();
4865     }
4866 }
4867 
4868 // Must be a template, because the destructor is unsafe or safe depending on
4869 // whether `-preview=dip1000` is in use. Otherwise, linking errors would
4870 // result.
4871 struct _DirIterator(bool useDIP1000)
4872 {
4873     static assert(useDIP1000 == dip1000Enabled,
4874         "Please don't override useDIP1000 to disagree with compiler switch.");
4875 
4876 private:
4877     SafeRefCounted!(DirIteratorImpl, RefCountedAutoInitialize.no) impl;
4878 
4879     this(string pathname, SpanMode mode, bool followSymlink) @trusted
4880     {
4881         impl = typeof(impl)(pathname, mode, followSymlink);
4882     }
4883 public:
4884     @property bool empty() @trusted { return impl.empty; }
4885     @property DirEntry front() @trusted { return impl.front; }
4886     void popFront() @trusted { impl.popFront(); }
4887 }
4888 
4889 // This has the client code to automatically use and link to the correct
4890 // template instance
4891 alias DirIterator = _DirIterator!dip1000Enabled;
4892 
4893 /++
4894     Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
4895     of `DirEntry` that lazily iterates a given directory,
4896     also provides two ways of foreach iteration. The iteration variable can be of
4897     type `string` if only the name is needed, or `DirEntry`
4898     if additional details are needed. The span _mode dictates how the
4899     directory is traversed. The name of each iterated directory entry
4900     contains the absolute or relative _path (depending on _pathname).
4901 
4902     Note: The order of returned directory entries is as it is provided by the
4903     operating system / filesystem, and may not follow any particular sorting.
4904 
4905     Params:
4906         useDIP1000 = used to instantiate this function separately for code with
4907                      and without -preview=dip1000 compiler switch, because it
4908                      affects the ABI of this function. Set automatically -
4909                      don't touch.
4910 
4911         path = The directory to iterate over.
4912                If empty, the current directory will be iterated.
4913 
4914         pattern = Optional string with wildcards, such as $(RED
4915                   "*.d"). When present, it is used to filter the
4916                   results by their file name. The supported wildcard
4917                   strings are described under $(REF globMatch,
4918                   std,_path).
4919 
4920         mode = Whether the directory's sub-directories should be
4921                iterated in depth-first post-order ($(LREF depth)),
4922                depth-first pre-order ($(LREF breadth)), or not at all
4923                ($(LREF shallow)).
4924 
4925         followSymlink = Whether symbolic links which point to directories
4926                          should be treated as directories and their contents
4927                          iterated over.
4928 
4929     Returns:
4930         An $(REF_ALTTEXT input range, isInputRange,std,range,primitives) of
4931         $(LREF DirEntry).
4932 
4933     Throws:
4934         $(UL
4935         $(LI $(LREF FileException) if the $(B path) directory does not exist or read permission is denied.)
4936         $(LI $(LREF FileException) if $(B mode) is not `shallow` and a subdirectory cannot be read.)
4937         )
4938 
4939 Example:
4940 --------------------
4941 // Iterate a directory in depth
4942 foreach (string name; dirEntries("destroy/me", SpanMode.depth))
4943 {
4944     remove(name);
4945 }
4946 
4947 // Iterate the current directory in breadth
4948 foreach (string name; dirEntries("", SpanMode.breadth))
4949 {
4950     writeln(name);
4951 }
4952 
4953 // Iterate a directory and get detailed info about it
4954 foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth))
4955 {
4956     writeln(e.name, "\t", e.size);
4957 }
4958 
4959 // Iterate over all *.d files in current directory and all its subdirectories
4960 auto dFiles = dirEntries("", SpanMode.depth).filter!(f => f.name.endsWith(".d"));
4961 foreach (d; dFiles)
4962     writeln(d.name);
4963 
4964 // Hook it up with std.parallelism to compile them all in parallel:
4965 foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread
4966 {
4967     string cmd = "dmd -c "  ~ d.name;
4968     writeln(cmd);
4969     std.process.executeShell(cmd);
4970 }
4971 
4972 // Iterate over all D source files in current directory and all its
4973 // subdirectories
4974 auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
4975 foreach (d; dFiles)
4976     writeln(d.name);
4977 --------------------
4978 To handle subdirectories with denied read permission, use `SpanMode.shallow`:
4979 ---
4980 void scan(string path)
4981 {
4982     foreach (DirEntry entry; dirEntries(path, SpanMode.shallow))
4983     {
4984         try
4985         {
4986             writeln(entry.name);
4987             if (entry.isDir)
4988                 scan(entry.name);
4989         }
4990         catch (FileException fe) { continue; } // ignore
4991     }
4992 }
4993 
4994 scan("");
4995 ---
4996 +/
4997 
4998 // For some reason, doing the same alias-to-a-template trick as with DirIterator
4999 // does not work here.
5000 auto dirEntries(bool useDIP1000 = dip1000Enabled)
5001     (string path, SpanMode mode, bool followSymlink = true)
5002 {
5003     return _DirIterator!useDIP1000(path, mode, followSymlink);
5004 }
5005 
5006 /// Duplicate functionality of D1's `std.file.listdir()`:
5007 @safe unittest
5008 {
5009     string[] listdir(string pathname)
5010     {
5011         import std.algorithm.iteration : map, filter;
5012         import std.array : array;
5013         import std.path : baseName;
5014 
5015         return dirEntries(pathname, SpanMode.shallow)
5016             .filter!(a => a.isFile)
5017             .map!((return a) => baseName(a.name))
5018             .array;
5019     }
5020 
5021     // Can be safe only with -preview=dip1000
5022     @safe void main(string[] args)
5023     {
5024         import std.stdio : writefln;
5025 
5026         string[] files = listdir(args[1]);
5027         writefln("%s", files);
5028     }
5029 }
5030 
5031 @system unittest
5032 {
5033     import std.algorithm.comparison : equal;
5034     import std.algorithm.iteration : map;
5035     import std.algorithm.searching : startsWith;
5036     import std.array : array;
5037     import std.conv : to;
5038     import std.path : buildPath, absolutePath;
5039     import std.file : dirEntries;
5040     import std.process : thisProcessID;
5041     import std.range.primitives : walkLength;
5042 
5043     version (Android)
5044         string testdir = deleteme; // This has to be an absolute path when
5045                                    // called from a shared library on Android,
5046                                    // ie an apk
5047     else
5048         string testdir = tempDir.buildPath("deleteme.dmd.unittest.std.file" ~ to!string(thisProcessID));
5049     mkdirRecurse(buildPath(testdir, "somedir"));
5050     scope(exit) rmdirRecurse(testdir);
5051     write(buildPath(testdir, "somefile"), null);
5052     write(buildPath(testdir, "somedir", "somedeepfile"), null);
5053 
5054     // testing range interface
5055     size_t equalEntries(string relpath, SpanMode mode)
5056     {
5057         import std.exception : enforce;
5058         auto len = enforce(walkLength(dirEntries(absolutePath(relpath), mode)));
5059         assert(walkLength(dirEntries(relpath, mode)) == len);
5060         assert(equal(
5061                    map!((return a) => absolutePath(a.name))(dirEntries(relpath, mode)),
5062                    map!(a => a.name)(dirEntries(absolutePath(relpath), mode))));
5063         return len;
5064     }
5065 
5066     assert(equalEntries(testdir, SpanMode.shallow) == 2);
5067     assert(equalEntries(testdir, SpanMode.depth) == 3);
5068     assert(equalEntries(testdir, SpanMode.breadth) == 3);
5069 
5070     // testing opApply
5071     foreach (string name; dirEntries(testdir, SpanMode.breadth))
5072     {
5073         //writeln(name);
5074         assert(name.startsWith(testdir));
5075     }
5076     foreach (DirEntry e; dirEntries(absolutePath(testdir), SpanMode.breadth))
5077     {
5078         //writeln(name);
5079         assert(e.isFile || e.isDir, e.name);
5080     }
5081 
5082     // https://issues.dlang.org/show_bug.cgi?id=7264
5083     foreach (string name; dirEntries(testdir, "*.d", SpanMode.breadth))
5084     {
5085 
5086     }
5087     foreach (entry; dirEntries(testdir, SpanMode.breadth))
5088     {
5089         static assert(is(typeof(entry) == DirEntry));
5090     }
5091     // https://issues.dlang.org/show_bug.cgi?id=7138
5092     auto a = array(dirEntries(testdir, SpanMode.shallow));
5093 
5094     // https://issues.dlang.org/show_bug.cgi?id=11392
5095     auto dFiles = dirEntries(testdir, SpanMode.shallow);
5096     foreach (d; dFiles){}
5097 
5098     // https://issues.dlang.org/show_bug.cgi?id=15146
5099     dirEntries("", SpanMode.shallow).walkLength();
5100 
5101     // https://github.com/dlang/phobos/issues/9584
5102     string cwd = getcwd();
5103     foreach (string entry; dirEntries(testdir, SpanMode.shallow))
5104     {
5105         if (entry.isDir)
5106             chdir(entry);
5107     }
5108     chdir(cwd); // needed for the directories to be removed
5109 }
5110 
5111 /// Ditto
5112 auto dirEntries(bool useDIP1000 = dip1000Enabled)
5113     (string path, string pattern, SpanMode mode,
5114     bool followSymlink = true)
5115 {
5116     import std.algorithm.iteration : filter;
5117     import std.path : globMatch, baseName;
5118 
5119     bool f(DirEntry de) { return globMatch(baseName(de.name), pattern); }
5120     return filter!f(_DirIterator!useDIP1000(path, mode, followSymlink));
5121 }
5122 
5123 @safe unittest
5124 {
5125     import std.stdio : writefln;
5126     immutable dpath = deleteme ~ "_dir";
5127     immutable fpath = deleteme ~ "_file";
5128     immutable sdpath = deleteme ~ "_sdir";
5129     immutable sfpath = deleteme ~ "_sfile";
5130     scope(exit)
5131     {
5132         if (dpath.exists) rmdirRecurse(dpath);
5133         if (fpath.exists) remove(fpath);
5134         if (sdpath.exists) remove(sdpath);
5135         if (sfpath.exists) remove(sfpath);
5136     }
5137 
5138     mkdir(dpath);
5139     write(fpath, "hello world");
5140     version (Posix) () @trusted
5141     {
5142         core.sys.posix.unistd.symlink((dpath ~ '\0').ptr, (sdpath ~ '\0').ptr);
5143         core.sys.posix.unistd.symlink((fpath ~ '\0').ptr, (sfpath ~ '\0').ptr);
5144     } ();
5145 
5146     static struct Flags { bool dir, file, link; }
5147     auto tests = [dpath : Flags(true), fpath : Flags(false, true)];
5148     version (Posix)
5149     {
5150         tests[sdpath] = Flags(true, false, true);
5151         tests[sfpath] = Flags(false, true, true);
5152     }
5153 
5154     auto past = Clock.currTime() - 2.seconds;
5155     auto future = past + 4.seconds;
5156 
5157     foreach (path, flags; tests)
5158     {
5159         auto de = DirEntry(path);
5160         assert(de.name == path);
5161         assert(de.isDir == flags.dir);
5162         assert(de.isFile == flags.file);
5163         assert(de.isSymlink == flags.link);
5164 
5165         assert(de.isDir == path.isDir);
5166         assert(de.isFile == path.isFile);
5167         assert(de.isSymlink == path.isSymlink);
5168         assert(de.size == path.getSize());
5169         assert(de.attributes == getAttributes(path));
5170         assert(de.linkAttributes == getLinkAttributes(path));
5171 
5172         scope(failure) writefln("[%s] [%s] [%s] [%s]", past, de.timeLastAccessed, de.timeLastModified, future);
5173         assert(de.timeLastAccessed > past);
5174         assert(de.timeLastAccessed < future);
5175         assert(de.timeLastModified > past);
5176         assert(de.timeLastModified < future);
5177 
5178         assert(attrIsDir(de.attributes) == flags.dir);
5179         assert(attrIsDir(de.linkAttributes) == (flags.dir && !flags.link));
5180         assert(attrIsFile(de.attributes) == flags.file);
5181         assert(attrIsFile(de.linkAttributes) == (flags.file && !flags.link));
5182         assert(!attrIsSymlink(de.attributes));
5183         assert(attrIsSymlink(de.linkAttributes) == flags.link);
5184 
5185         version (Windows)
5186         {
5187             assert(de.timeCreated > past);
5188             assert(de.timeCreated < future);
5189         }
5190         else version (Posix)
5191         {
5192             assert(de.timeStatusChanged > past);
5193             assert(de.timeStatusChanged < future);
5194             assert(de.attributes == de.statBuf.st_mode);
5195         }
5196     }
5197 }
5198 
5199 // Make sure that dirEntries does not butcher Unicode file names
5200 // https://issues.dlang.org/show_bug.cgi?id=17962
5201 @safe unittest
5202 {
5203     import std.algorithm.comparison : equal;
5204     import std.algorithm.iteration : map;
5205     import std.algorithm.sorting : sort;
5206     import std.array : array;
5207     import std.path : buildPath;
5208     import std.uni : normalize;
5209 
5210     // The Unicode normalization is required to make the tests pass on Mac OS X.
5211     auto dir = deleteme ~ normalize("𐐷");
5212     scope(exit) if (dir.exists) rmdirRecurse(dir);
5213     mkdir(dir);
5214     auto files = ["Hello World",
5215                   "Ma Chérie.jpeg",
5216                   "さいごの果実.txt"].map!(a => buildPath(dir, normalize(a)))().array();
5217     sort(files);
5218     foreach (file; files)
5219         write(file, "nothing");
5220 
5221     auto result = dirEntries(dir, SpanMode.shallow).map!((return a) => a.name.normalize()).array();
5222     sort(result);
5223 
5224     assert(equal(files, result));
5225 }
5226 
5227 // https://issues.dlang.org/show_bug.cgi?id=21250
5228 @system unittest
5229 {
5230     import std.exception : assertThrown;
5231     assertThrown!Exception(dirEntries("237f5babd6de21f40915826699582e36", "*.bin", SpanMode.depth));
5232 }
5233 
5234 /**
5235  * Reads a file line by line and parses the line into a single value or a
5236  * $(REF Tuple, std,typecons) of values depending on the length of `Types`.
5237  * The lines are parsed using the specified format string. The format string is
5238  * passed to $(REF formattedRead, std,_format), and therefore must conform to the
5239  * _format string specification outlined in $(MREF std, _format).
5240  *
5241  * Params:
5242  *     Types = the types that each of the elements in the line should be returned as
5243  *     filename = the name of the file to read
5244  *     format = the _format string to use when reading
5245  *
5246  * Returns:
5247  *     If only one type is passed, then an array of that type. Otherwise, an
5248  *     array of $(REF Tuple, std,typecons)s.
5249  *
5250  * Throws:
5251  *     `Exception` if the format string is malformed. Also, throws `Exception`
5252  *     if any of the lines in the file are not fully consumed by the call
5253  *     to $(REF formattedRead, std,_format). Meaning that no empty lines or lines
5254  *     with extra characters are allowed.
5255  */
5256 Select!(Types.length == 1, Types[0][], Tuple!(Types)[])
5257 slurp(Types...)(string filename, scope const(char)[] format)
5258 {
5259     import std.array : appender;
5260     import std.conv : text;
5261     import std.exception : enforce;
5262     import std.format.read : formattedRead;
5263     import std.stdio : File;
5264     import std.string : stripRight;
5265 
5266     auto app = appender!(typeof(return))();
5267     ElementType!(typeof(return)) toAdd;
5268     auto f = File(filename);
5269     scope(exit) f.close();
5270     foreach (line; f.byLine())
5271     {
5272         formattedRead(line, format, &toAdd);
5273         enforce(line.stripRight("\r").empty,
5274                 text("Trailing characters at the end of line: `", line,
5275                         "'"));
5276         app.put(toAdd);
5277     }
5278     return app.data;
5279 }
5280 
5281 ///
5282 @system unittest
5283 {
5284     import std.typecons : tuple;
5285 
5286     scope(exit)
5287     {
5288         assert(exists(deleteme));
5289         remove(deleteme);
5290     }
5291 
5292     write(deleteme, "12 12.25\n345 1.125"); // deleteme is the name of a temporary file
5293 
5294     // Load file; each line is an int followed by comma, whitespace and a
5295     // double.
5296     auto a = slurp!(int, double)(deleteme, "%s %s");
5297     assert(a.length == 2);
5298     assert(a[0] == tuple(12, 12.25));
5299     assert(a[1] == tuple(345, 1.125));
5300 }
5301 
5302 @system unittest
5303 {
5304     import std.typecons : tuple;
5305 
5306     scope(exit)
5307     {
5308         assert(exists(deleteme));
5309         remove(deleteme);
5310     }
5311     write(deleteme, "10\r\n20");
5312     assert(slurp!(int)(deleteme, "%d") == [10, 20]);
5313 }
5314 
5315 /**
5316 Returns the path to a directory for temporary files.
5317 On POSIX platforms, it searches through the following list of directories
5318 and returns the first one which is found to exist:
5319 $(OL
5320     $(LI The directory given by the `TMPDIR` environment variable.)
5321     $(LI The directory given by the `TEMP` environment variable.)
5322     $(LI The directory given by the `TMP` environment variable.)
5323     $(LI `/tmp/`)
5324     $(LI `/var/tmp/`)
5325     $(LI `/usr/tmp/`)
5326 )
5327 
5328 On all platforms, `tempDir` returns the current working directory on failure.
5329 
5330 The return value of the function is cached, so the procedures described
5331 below will only be performed the first time the function is called.  All
5332 subsequent runs will return the same string, regardless of whether
5333 environment variables and directory structures have changed in the
5334 meantime.
5335 
5336 The POSIX `tempDir` algorithm is inspired by Python's
5337 $(LINK2 http://docs.python.org/library/tempfile.html#tempfile.tempdir, `tempfile.tempdir`).
5338 
5339 Returns:
5340     On Windows, this function returns the result of calling the Windows API function
5341     $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992.aspx, `GetTempPath`).
5342 
5343     On POSIX platforms, it searches through the following list of directories
5344     and returns the first one which is found to exist:
5345     $(OL
5346         $(LI The directory given by the `TMPDIR` environment variable.)
5347         $(LI The directory given by the `TEMP` environment variable.)
5348         $(LI The directory given by the `TMP` environment variable.)
5349         $(LI `/tmp`)
5350         $(LI `/var/tmp`)
5351         $(LI `/usr/tmp`)
5352     )
5353 
5354     On all platforms, `tempDir` returns `"."` on failure, representing
5355     the current working directory.
5356 */
5357 string tempDir() @trusted
5358 {
5359     // We must check that the end of a path is not a separator, before adding another
5360     // If we don't we end up with https://issues.dlang.org/show_bug.cgi?id=22738
5361     static string addSeparator(string input)
5362     {
5363         import std.path : dirSeparator;
5364         import std.algorithm.searching : endsWith;
5365 
5366         // It is very rare a directory path will reach this point with a directory separator at the end
5367         // However on OSX this can happen, so we must verify lest we break user code i.e. https://github.com/dlang/dub/pull/2208
5368         if (!input.endsWith(dirSeparator))
5369             return input ~ dirSeparator;
5370         else
5371             return input;
5372     }
5373 
5374     static string cache;
5375     if (cache is null)
5376     {
5377         version (Windows)
5378         {
5379             import std.conv : to;
5380             // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992(v=vs.85).aspx
5381             wchar[MAX_PATH + 2] buf;
5382             DWORD len = GetTempPathW(buf.length, buf.ptr);
5383             if (len) cache = buf[0 .. len].to!string;
5384         }
5385         else version (Posix)
5386         {
5387             import std.process : environment;
5388             // This function looks through the list of alternative directories
5389             // and returns the first one which exists and is a directory.
5390             static string findExistingDir(T...)(lazy T alternatives)
5391             {
5392                 foreach (dir; alternatives)
5393                     if (!dir.empty && exists(dir)) return addSeparator(dir);
5394                 return null;
5395             }
5396 
5397             cache = findExistingDir(environment.get("TMPDIR"),
5398                                     environment.get("TEMP"),
5399                                     environment.get("TMP"),
5400                                     "/tmp",
5401                                     "/var/tmp",
5402                                     "/usr/tmp");
5403         }
5404         else static assert(false, "Unsupported platform");
5405 
5406         if (cache is null)
5407         {
5408             cache = addSeparator(getcwd());
5409         }
5410     }
5411     return cache;
5412 }
5413 
5414 ///
5415 @safe unittest
5416 {
5417     import std.ascii : letters;
5418     import std.conv : to;
5419     import std.path : buildPath;
5420     import std.random : randomSample;
5421     import std.utf : byCodeUnit;
5422 
5423     // random id with 20 letters
5424     auto id = letters.byCodeUnit.randomSample(20).to!string;
5425     auto myFile = tempDir.buildPath(id ~ "my_tmp_file");
5426     scope(exit) myFile.remove;
5427 
5428     myFile.write("hello");
5429     assert(myFile.readText == "hello");
5430 }
5431 
5432 @safe unittest
5433 {
5434     import std.algorithm.searching : endsWith;
5435     import std.path : dirSeparator;
5436     assert(tempDir.endsWith(dirSeparator));
5437 
5438     // https://issues.dlang.org/show_bug.cgi?id=22738
5439     assert(!tempDir.endsWith(dirSeparator ~ dirSeparator));
5440 }
5441 
5442 /**
5443 Returns the available disk space based on a given path.
5444 On Windows, `path` must be a directory; on POSIX systems, it can be a file or directory.
5445 
5446 Params:
5447     path = on Windows, it must be a directory; on POSIX it can be a file or directory
5448 Returns:
5449     Available space in bytes
5450 
5451 Throws:
5452     $(LREF FileException) in case of failure
5453 */
5454 ulong getAvailableDiskSpace(scope const(char)[] path) @safe
5455 {
5456     version (Windows)
5457     {
5458         import core.sys.windows.winbase : GetDiskFreeSpaceExW;
5459         import core.sys.windows.winnt : ULARGE_INTEGER;
5460         import std.internal.cstring : tempCStringW;
5461 
5462         ULARGE_INTEGER freeBytesAvailable;
5463         auto err = () @trusted {
5464             return GetDiskFreeSpaceExW(path.tempCStringW(), &freeBytesAvailable, null, null);
5465         } ();
5466         cenforce(err != 0, "Cannot get available disk space");
5467 
5468         return freeBytesAvailable.QuadPart;
5469     }
5470     else version (Posix)
5471     {
5472         import std.internal.cstring : tempCString;
5473 
5474         version (FreeBSD)
5475         {
5476             import core.sys.freebsd.sys.mount : statfs, statfs_t;
5477 
5478             statfs_t stats;
5479             auto err = () @trusted {
5480                 return statfs(path.tempCString(), &stats);
5481             } ();
5482             cenforce(err == 0, "Cannot get available disk space");
5483 
5484             return stats.f_bavail * stats.f_bsize;
5485         }
5486         else
5487         {
5488             import core.sys.posix.sys.statvfs : statvfs, statvfs_t;
5489 
5490             statvfs_t stats;
5491             auto err = () @trusted {
5492                 return statvfs(path.tempCString(), &stats);
5493             } ();
5494             cenforce(err == 0, "Cannot get available disk space");
5495 
5496             return stats.f_bavail * stats.f_frsize;
5497         }
5498     }
5499     else static assert(0, "Unsupported platform");
5500 }
5501 
5502 ///
5503 @safe unittest
5504 {
5505     import std.exception : assertThrown;
5506 
5507     auto space = getAvailableDiskSpace(".");
5508     assert(space > 0);
5509 
5510     assertThrown!FileException(getAvailableDiskSpace("ThisFileDoesNotExist123123"));
5511 }