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