The OpenD Programming Language

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