1 // Written in the D programming language.
4 Utilities for manipulating files and scanning directories. Functions
5 in this module handle files as a unit, e.g., read or write one _file
6 at a time. For opening files and manipulating them via handles refer
7 to module $(MREF std, stdio).
9 $(SCRIPT inhibitQuickIndex = 1;)
11 $(TR $(TH Category) $(TH Functions))
12 $(TR $(TD General) $(TD
20 $(TR $(TD Directories) $(TD
39 $(TR $(TD Symlinks) $(TD
43 $(TR $(TD Attributes) $(TD
48 $(LREF getLinkAttributes)
52 $(TR $(TD Timestamp) $(TD
56 $(LREF timeLastModified)
61 $(LREF PreserveAttributes)
67 Copyright: Copyright Digital Mars 2007 - 2011.
68 See_Also: The $(HTTP ddili.org/ders/d.en/files.html, official tutorial) for an
69 introduction to working with files in D, module
70 $(MREF std, stdio) for opening files and manipulating them via handles,
71 and module $(MREF std, path) for manipulating path strings.
73 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
74 Authors: $(HTTP digitalmars.com, Walter Bright),
75 $(HTTP erdani.org, Andrei Alexandrescu),
77 Source: $(PHOBOSSRC std/_file.d)
81 import core.stdc.errno, core.stdc.stdlib, core.stdc.string;
82 import core.time : abs, dur, hnsecs, seconds;
84 import std.datetime.date : DateTime;
85 import std.datetime.systime : Clock, SysTime, unixTimeToStdTime;
86 import std.internal.cstring;
88 import std.range.primitives;
94 import core.sys.windows.windows, std.windows.syserror;
98 import core.sys.posix.dirent, core.sys.posix.fcntl, core.sys.posix.sys.stat,
99 core.sys.posix.sys.time, core.sys.posix.unistd, core.sys.posix.utime;
102 static assert(false, "Module " ~ .stringof ~ " not implemented for this OS.");
104 // Character type used for operating system filesystem APIs
107 private alias FSChar = wchar;
111 private alias FSChar = char;
116 // Purposefully not documented. Use at your own risk
117 @property string deleteme() @safe
119 import std.conv : to;
120 import std.path : buildPath;
121 import std.process : thisProcessID;
123 static _deleteme = "deleteme.dmd.unittest.pid";
124 static _first = true;
128 _deleteme = buildPath(tempDir(), _deleteme) ~ to!string(thisProcessID);
135 version (unittest) private struct TestAliasedString
137 string get() @safe @nogc pure nothrow { return _s; }
145 package enum system_directory = "/system/etc";
146 package enum system_file = "/system/etc/hosts";
150 package enum system_directory = "/usr/include";
151 package enum system_file = "/usr/include/assert.h";
156 Exception thrown for file I/O errors.
158 class FileException : Exception
160 import std.conv : text, to;
165 immutable uint errno;
168 Constructor which takes an error message.
171 name = Name of file for which the error occurred.
172 msg = Message describing the error.
173 file = The _file where the error occurred.
174 line = The _line where the error occurred.
176 this(in char[] name, in char[] msg, string file = __FILE__, size_t line = __LINE__) @safe pure
179 super(name.idup, file, line);
181 super(text(name, ": ", msg), file, line);
187 Constructor which takes the error number ($(LUCKY GetLastError)
188 in Windows, $(D_PARAM errno) in Posix).
191 name = Name of file for which the error occurred.
192 errno = The error number.
193 file = The _file where the error occurred.
194 Defaults to $(D __FILE__).
195 line = The _line where the error occurred.
196 Defaults to $(D __LINE__).
198 version (Windows) this(in char[] name,
199 uint errno = .GetLastError(),
200 string file = __FILE__,
201 size_t line = __LINE__) @safe
203 this(name, sysErrorString(errno), file, line);
206 else version (Posix) this(in char[] name,
208 string file = __FILE__,
209 size_t line = __LINE__) @trusted
211 import std.exception : errnoString;
212 this(name, errnoString(errno), file, line);
217 private T cenforce(T)(T condition, lazy const(char)[] name, string file = __FILE__, size_t line = __LINE__)
223 throw new FileException(name, .GetLastError(), file, line);
227 throw new FileException(name, .errno, file, line);
233 private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez,
234 string file = __FILE__, size_t line = __LINE__)
240 import core.stdc.wchar_ : wcslen;
241 import std.conv : to;
243 auto len = namez ? wcslen(namez) : 0;
244 name = to!string(namez[0 .. len]);
246 throw new FileException(name, .GetLastError(), file, line);
251 private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez,
252 string file = __FILE__, size_t line = __LINE__)
258 import core.stdc.string : strlen;
260 auto len = namez ? strlen(namez) : 0;
261 name = namez[0 .. len].idup;
263 throw new FileException(name, .errno, file, line);
271 cenforce(false, null, null,
274 catch (FileException) {}
277 /* **********************************
278 * Basic File operations.
281 /********************************************
282 Read entire contents of file $(D name) and returns it as an untyped
283 array. If the file size is larger than $(D upTo), only $(D upTo)
287 name = string or range of characters representing the file _name
288 upTo = if present, the maximum number of bytes to _read
290 Returns: Untyped array of bytes _read.
292 Throws: $(LREF FileException) on error.
295 void[] read(R)(R name, size_t upTo = size_t.max)
296 if (isInputRange!R && isSomeChar!(ElementEncodingType!R) && !isInfinite!R &&
297 !isConvertibleToString!R)
299 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
300 return readImpl(name, name.tempCString!FSChar(), upTo);
302 return readImpl(null, name.tempCString!FSChar(), upTo);
308 import std.utf : byChar;
311 assert(exists(deleteme));
315 write(deleteme, "1234"); // deleteme is the name of a temporary file
316 assert(read(deleteme, 2) == "12");
317 assert(read(deleteme.byChar) == "1234");
318 assert((cast(const(ubyte)[])read(deleteme)).length == 4);
322 void[] read(R)(auto ref R name, size_t upTo = size_t.max)
323 if (isConvertibleToString!R)
325 return read!(StringTypeOf!R)(name, upTo);
330 static assert(__traits(compiles, read(TestAliasedString(null))));
333 version (Posix) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @trusted
335 import core.memory : GC;
336 import std.algorithm.comparison : min;
337 import std.array : uninitializedArray;
338 import std.conv : to;
340 // A few internal configuration parameters {
342 minInitialAlloc = 1024 * 4,
343 maxInitialAlloc = size_t.max / 2,
344 sizeIncrement = 1024 * 16,
345 maxSlackMemoryAllowed = 1024;
348 immutable fd = core.sys.posix.fcntl.open(namez,
349 core.sys.posix.fcntl.O_RDONLY);
350 cenforce(fd != -1, name);
351 scope(exit) core.sys.posix.unistd.close(fd);
353 stat_t statbuf = void;
354 cenforce(fstat(fd, &statbuf) == 0, name, namez);
356 immutable initialAlloc = min(upTo, to!size_t(statbuf.st_size
357 ? min(statbuf.st_size + 1, maxInitialAlloc)
359 void[] result = uninitializedArray!(ubyte[])(initialAlloc);
360 scope(failure) GC.free(result.ptr);
365 immutable actual = core.sys.posix.unistd.read(fd, result.ptr + size,
366 min(result.length, upTo) - size);
367 cenforce(actual != -1, name, namez);
368 if (actual == 0) break;
370 if (size >= upTo) break;
371 if (size < result.length) continue;
372 immutable newAlloc = size + sizeIncrement;
373 result = GC.realloc(result.ptr, newAlloc, GC.BlkAttr.NO_SCAN)[0 .. newAlloc];
376 return result.length - size >= maxSlackMemoryAllowed
377 ? GC.realloc(result.ptr, size, GC.BlkAttr.NO_SCAN)[0 .. size]
382 version (Windows) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @safe
384 import core.memory : GC;
385 import std.algorithm.comparison : min;
386 import std.array : uninitializedArray;
387 static trustedCreateFileW(const(wchar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
388 SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition,
389 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted
391 return CreateFileW(namez, dwDesiredAccess, dwShareMode,
392 lpSecurityAttributes, dwCreationDisposition,
393 dwFlagsAndAttributes, hTemplateFile);
396 static trustedCloseHandle(HANDLE hObject) @trusted
398 return CloseHandle(hObject);
400 static trustedGetFileSize(HANDLE hFile, out ulong fileSize) @trusted
403 DWORD sizeLow = GetFileSize(hFile, &sizeHigh);
404 const bool result = sizeLow != INVALID_FILE_SIZE;
406 fileSize = makeUlong(sizeLow, sizeHigh);
409 static trustedReadFile(HANDLE hFile, void *lpBuffer, ulong nNumberOfBytesToRead) @trusted
411 // Read by chunks of size < 4GB (Windows API limit)
412 ulong totalNumRead = 0;
413 while (totalNumRead != nNumberOfBytesToRead)
415 const uint chunkSize = min(nNumberOfBytesToRead - totalNumRead, 0xffff_0000);
416 DWORD numRead = void;
417 const result = ReadFile(hFile, lpBuffer + totalNumRead, chunkSize, &numRead, null);
418 if (result == 0 || numRead != chunkSize)
420 totalNumRead += chunkSize;
426 AliasSeq!(GENERIC_READ,
427 FILE_SHARE_READ | FILE_SHARE_WRITE, (SECURITY_ATTRIBUTES*).init,
428 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
430 auto h = trustedCreateFileW(namez, defaults);
432 cenforce(h != INVALID_HANDLE_VALUE, name, namez);
433 scope(exit) cenforce(trustedCloseHandle(h), name, namez);
434 ulong fileSize = void;
435 cenforce(trustedGetFileSize(h, fileSize), name, namez);
436 size_t size = min(upTo, fileSize);
437 auto buf = uninitializedArray!(ubyte[])(size);
441 () @trusted { GC.free(buf.ptr); } ();
445 cenforce(trustedReadFile(h, &buf[0], size), name, namez);
446 return buf[0 .. size];
449 version (linux) @safe unittest
451 // A file with "zero" length that doesn't have 0 length at all
452 auto s = std.file.readText("/proc/sys/kernel/osrelease");
453 assert(s.length > 0);
454 //writefln("'%s'", s);
459 scope(exit) if (exists(deleteme)) remove(deleteme);
461 auto f = File(deleteme, "w");
462 f.write("abcd"); f.flush();
463 assert(read(deleteme) == "abcd");
466 /********************************************
467 Read and validates (using $(REF validate, std,utf)) a text file. $(D S)
468 can be a type of array of characters of any width and constancy. No
469 width conversion is performed; if the width of the characters in file
470 $(D name) is different from the width of elements of $(D S),
471 validation will fail.
474 name = string or range of characters representing the file _name
476 Returns: Array of characters read.
478 Throws: $(D FileException) on file error, $(D UTFException) on UTF
482 S readText(S = string, R)(R name)
483 if (isSomeString!S &&
484 (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
485 !isConvertibleToString!R)
487 import std.utf : validate;
488 static auto trustedCast(void[] buf) @trusted { return cast(S) buf; }
489 auto result = trustedCast(read(name));
497 import std.exception : enforce;
498 write(deleteme, "abc"); // deleteme is the name of a temporary file
499 scope(exit) remove(deleteme);
500 string content = readText(deleteme);
501 enforce(content == "abc");
505 S readText(S = string, R)(auto ref R name)
506 if (isConvertibleToString!R)
508 return readText!(S, StringTypeOf!R)(name);
513 static assert(__traits(compiles, readText(TestAliasedString(null))));
516 /*********************************************
517 Write $(D buffer) to file $(D name).
519 Creates the file if it does not already exist.
522 name = string or range of characters representing the file _name
523 buffer = data to be written to file
525 Throws: $(D FileException) on error.
527 See_also: $(REF toFile, std,stdio)
529 void write(R)(R name, const void[] buffer)
530 if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
531 !isConvertibleToString!R)
533 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
534 writeImpl(name, name.tempCString!FSChar(), buffer, false);
536 writeImpl(null, name.tempCString!FSChar(), buffer, false);
544 assert(exists(deleteme));
548 int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
549 write(deleteme, a); // deleteme is the name of a temporary file
550 assert(cast(int[]) read(deleteme) == a);
554 void write(R)(auto ref R name, const void[] buffer)
555 if (isConvertibleToString!R)
557 write!(StringTypeOf!R)(name, buffer);
562 static assert(__traits(compiles, write(TestAliasedString(null), null)));
565 /*********************************************
566 Appends $(D buffer) to file $(D name).
568 Creates the file if it does not already exist.
571 name = string or range of characters representing the file _name
572 buffer = data to be appended to file
574 Throws: $(D FileException) on error.
576 void append(R)(R name, const void[] buffer)
577 if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
578 !isConvertibleToString!R)
580 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
581 writeImpl(name, name.tempCString!FSChar(), buffer, true);
583 writeImpl(null, name.tempCString!FSChar(), buffer, true);
591 assert(exists(deleteme));
595 int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
596 write(deleteme, a); // deleteme is the name of a temporary file
597 int[] b = [ 13, 21 ];
599 assert(cast(int[]) read(deleteme) == a ~ b);
603 void append(R)(auto ref R name, const void[] buffer)
604 if (isConvertibleToString!R)
606 append!(StringTypeOf!R)(name, buffer);
611 static assert(__traits(compiles, append(TestAliasedString("foo"), [0, 1, 2, 3])));
614 // Posix implementation helper for write and append
616 version (Posix) private void writeImpl(const(char)[] name, const(FSChar)* namez,
617 in void[] buffer, bool append) @trusted
619 import std.conv : octal;
622 auto mode = append ? O_CREAT | O_WRONLY | O_APPEND
623 : O_CREAT | O_WRONLY | O_TRUNC;
625 immutable fd = core.sys.posix.fcntl.open(namez, mode, octal!666);
626 cenforce(fd != -1, name, namez);
628 scope(failure) core.sys.posix.unistd.close(fd);
630 immutable size = buffer.length;
631 size_t sum, cnt = void;
634 cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30;
635 const numwritten = core.sys.posix.unistd.write(fd, buffer.ptr + sum, cnt);
636 if (numwritten != cnt)
640 cenforce(sum == size, name, namez);
642 cenforce(core.sys.posix.unistd.close(fd) == 0, name, namez);
645 // Windows implementation helper for write and append
647 version (Windows) private void writeImpl(const(char)[] name, const(FSChar)* namez,
648 in void[] buffer, bool append) @trusted
654 AliasSeq!(GENERIC_WRITE, 0, null, OPEN_ALWAYS,
655 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
658 h = CreateFileW(namez, defaults);
659 cenforce(h != INVALID_HANDLE_VALUE, name, namez);
660 cenforce(SetFilePointer(h, 0, null, FILE_END) != INVALID_SET_FILE_POINTER,
666 AliasSeq!(GENERIC_WRITE, 0, null, CREATE_ALWAYS,
667 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
670 h = CreateFileW(namez, defaults);
671 cenforce(h != INVALID_HANDLE_VALUE, name, namez);
673 immutable size = buffer.length;
674 size_t sum, cnt = void;
675 DWORD numwritten = void;
678 cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30;
679 WriteFile(h, buffer.ptr + sum, cast(uint) cnt, &numwritten, null);
680 if (numwritten != cnt)
684 cenforce(sum == size && CloseHandle(h), name, namez);
687 /***************************************************
688 * Rename file $(D from) _to $(D to).
689 * If the target file exists, it is overwritten.
691 * from = string or range of characters representing the existing file name
692 * to = string or range of characters representing the target file name
693 * Throws: $(D FileException) on error.
695 void rename(RF, RT)(RF from, RT to)
696 if ((isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) || isSomeString!RF)
697 && !isConvertibleToString!RF &&
698 (isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) || isSomeString!RT)
699 && !isConvertibleToString!RT)
701 // Place outside of @trusted block
702 auto fromz = from.tempCString!FSChar();
703 auto toz = to.tempCString!FSChar();
705 static if (isNarrowString!RF && is(Unqual!(ElementEncodingType!RF) == char))
708 enum string f = null;
710 static if (isNarrowString!RT && is(Unqual!(ElementEncodingType!RT) == char))
713 enum string t = null;
715 renameImpl(f, t, fromz, toz);
719 void rename(RF, RT)(auto ref RF from, auto ref RT to)
720 if (isConvertibleToString!RF || isConvertibleToString!RT)
722 import std.meta : staticMap;
723 alias Types = staticMap!(convertToString, RF, RT);
724 rename!Types(from, to);
729 static assert(__traits(compiles, rename(TestAliasedString(null), TestAliasedString(null))));
730 static assert(__traits(compiles, rename("", TestAliasedString(null))));
731 static assert(__traits(compiles, rename(TestAliasedString(null), "")));
732 import std.utf : byChar;
733 static assert(__traits(compiles, rename(TestAliasedString(null), "".byChar)));
736 private void renameImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, const(FSChar)* toz) @trusted
740 import std.exception : enforce;
742 const result = MoveFileExW(fromz, toz, MOVEFILE_REPLACE_EXISTING);
745 import core.stdc.wchar_ : wcslen;
746 import std.conv : to, text;
749 f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]);
752 t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
756 text("Attempting to rename file ", f, " to ", t)));
761 static import core.stdc.stdio;
763 cenforce(core.stdc.stdio.rename(fromz, toz) == 0, t, toz);
769 import std.utf : byWchar;
771 auto t1 = deleteme, t2 = deleteme~"2";
772 scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
775 assert(readText(t2) == "1");
777 rename(t1, t2.byWchar);
778 assert(readText(t2) == "2");
782 /***************************************************
783 Delete file $(D name).
786 name = string or range of characters representing the file _name
788 Throws: $(D FileException) on error.
790 void remove(R)(R name)
791 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
792 !isConvertibleToString!R)
794 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
795 removeImpl(name, name.tempCString!FSChar());
797 removeImpl(null, name.tempCString!FSChar());
801 void remove(R)(auto ref R name)
802 if (isConvertibleToString!R)
804 remove!(StringTypeOf!R)(name);
809 static assert(__traits(compiles, remove(TestAliasedString("foo"))));
812 private void removeImpl(const(char)[] name, const(FSChar)* namez) @trusted
816 cenforce(DeleteFileW(namez), name, namez);
820 static import core.stdc.stdio;
824 import core.stdc.string : strlen;
825 auto len = strlen(namez);
826 name = namez[0 .. len];
828 cenforce(core.stdc.stdio.remove(namez) == 0,
829 "Failed to remove file " ~ name);
833 version (Windows) private WIN32_FILE_ATTRIBUTE_DATA getFileAttributesWin(R)(R name)
834 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
836 auto namez = name.tempCString!FSChar();
838 WIN32_FILE_ATTRIBUTE_DATA fad = void;
840 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
842 static void getFA(const(char)[] name, const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
844 import std.exception : enforce;
845 enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad),
846 new FileException(name.idup));
848 getFA(name, namez, fad);
852 static void getFA(const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
854 import core.stdc.wchar_ : wcslen;
855 import std.conv : to;
856 import std.exception : enforce;
858 enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad),
859 new FileException(namez[0 .. wcslen(namez)].to!string));
866 version (Windows) private ulong makeUlong(DWORD dwLow, DWORD dwHigh) @safe pure nothrow @nogc
870 li.HighPart = dwHigh;
874 /***************************************************
875 Get size of file $(D name) in bytes.
878 name = string or range of characters representing the file _name
880 Throws: $(D FileException) on error (e.g., file not found).
882 ulong getSize(R)(R name)
883 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
884 !isConvertibleToString!R)
888 with (getFileAttributesWin(name))
889 return makeUlong(nFileSizeLow, nFileSizeHigh);
893 auto namez = name.tempCString();
895 static trustedStat(const(FSChar)* namez, out stat_t buf) @trusted
897 return stat(namez, &buf);
899 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
903 stat_t statbuf = void;
904 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
905 return statbuf.st_size;
910 ulong getSize(R)(auto ref R name)
911 if (isConvertibleToString!R)
913 return getSize!(StringTypeOf!R)(name);
918 static assert(__traits(compiles, getSize(TestAliasedString("foo"))));
923 // create a file of size 1
924 write(deleteme, "a");
925 scope(exit) { assert(exists(deleteme)); remove(deleteme); }
926 assert(getSize(deleteme) == 1);
927 // create a file of size 3
928 write(deleteme, "abc");
929 import std.utf : byChar;
930 assert(getSize(deleteme.byChar) == 3);
934 // Reads a time field from a stat_t with full precision.
936 private SysTime statTimeToStdTime(char which)(ref stat_t statbuf)
938 auto unixTime = mixin(`statbuf.st_` ~ which ~ `time`);
939 long stdTime = unixTimeToStdTime(unixTime);
941 static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `tim`))))
942 stdTime += mixin(`statbuf.st_` ~ which ~ `tim.tv_nsec`) / 100;
944 static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `timensec`))))
945 stdTime += mixin(`statbuf.st_` ~ which ~ `timensec`) / 100;
947 static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `time_nsec`))))
948 stdTime += mixin(`statbuf.st_` ~ which ~ `time_nsec`) / 100;
950 static if (is(typeof(mixin(`statbuf.__st_` ~ which ~ `timensec`))))
951 stdTime += mixin(`statbuf.__st_` ~ which ~ `timensec`) / 100;
953 return SysTime(stdTime);
957 Get the access and modified times of file or folder $(D name).
960 name = File/Folder _name to get times for.
961 accessTime = Time the file/folder was last accessed.
962 modificationTime = Time the file/folder was last modified.
965 $(D FileException) on error.
967 void getTimes(R)(R name,
968 out SysTime accessTime,
969 out SysTime modificationTime)
970 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
971 !isConvertibleToString!R)
975 import std.datetime.systime : FILETIMEToSysTime;
977 with (getFileAttributesWin(name))
979 accessTime = FILETIMEToSysTime(&ftLastAccessTime);
980 modificationTime = FILETIMEToSysTime(&ftLastWriteTime);
985 auto namez = name.tempCString();
987 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
989 return stat(namez, &buf);
991 stat_t statbuf = void;
993 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
997 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
999 accessTime = statTimeToStdTime!'a'(statbuf);
1000 modificationTime = statTimeToStdTime!'m'(statbuf);
1005 void getTimes(R)(auto ref R name,
1006 out SysTime accessTime,
1007 out SysTime modificationTime)
1008 if (isConvertibleToString!R)
1010 return getTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
1015 SysTime atime, mtime;
1016 static assert(__traits(compiles, getTimes(TestAliasedString("foo"), atime, mtime)));
1021 import std.stdio : writefln;
1023 auto currTime = Clock.currTime();
1025 write(deleteme, "a");
1026 scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1028 SysTime accessTime1 = void;
1029 SysTime modificationTime1 = void;
1031 getTimes(deleteme, accessTime1, modificationTime1);
1033 enum leeway = dur!"seconds"(5);
1036 auto diffa = accessTime1 - currTime;
1037 auto diffm = modificationTime1 - currTime;
1038 scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime1, modificationTime1, currTime, diffa, diffm);
1040 assert(abs(diffa) <= leeway);
1041 assert(abs(diffm) <= leeway);
1044 version (fullFileTests)
1047 enum sleepTime = dur!"seconds"(2);
1048 Thread.sleep(sleepTime);
1050 currTime = Clock.currTime();
1051 write(deleteme, "b");
1053 SysTime accessTime2 = void;
1054 SysTime modificationTime2 = void;
1056 getTimes(deleteme, accessTime2, modificationTime2);
1059 auto diffa = accessTime2 - currTime;
1060 auto diffm = modificationTime2 - currTime;
1061 scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime2, modificationTime2, currTime, diffa, diffm);
1063 //There is no guarantee that the access time will be updated.
1064 assert(abs(diffa) <= leeway + sleepTime);
1065 assert(abs(diffm) <= leeway);
1068 assert(accessTime1 <= accessTime2);
1069 assert(modificationTime1 <= modificationTime2);
1077 $(BLUE This function is Windows-Only.)
1079 Get creation/access/modified times of file $(D name).
1081 This is the same as $(D getTimes) except that it also gives you the file
1082 creation time - which isn't possible on Posix systems.
1085 name = File _name to get times for.
1086 fileCreationTime = Time the file was created.
1087 fileAccessTime = Time the file was last accessed.
1088 fileModificationTime = Time the file was last modified.
1091 $(D FileException) on error.
1093 void getTimesWin(R)(R name,
1094 out SysTime fileCreationTime,
1095 out SysTime fileAccessTime,
1096 out SysTime fileModificationTime)
1097 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1098 !isConvertibleToString!R);
1100 else version (Windows)
1102 void getTimesWin(R)(R name,
1103 out SysTime fileCreationTime,
1104 out SysTime fileAccessTime,
1105 out SysTime fileModificationTime)
1106 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1107 !isConvertibleToString!R)
1109 import std.datetime.systime : FILETIMEToSysTime;
1111 with (getFileAttributesWin(name))
1113 fileCreationTime = FILETIMEToSysTime(&ftCreationTime);
1114 fileAccessTime = FILETIMEToSysTime(&ftLastAccessTime);
1115 fileModificationTime = FILETIMEToSysTime(&ftLastWriteTime);
1119 void getTimesWin(R)(auto ref R name,
1120 out SysTime fileCreationTime,
1121 out SysTime fileAccessTime,
1122 out SysTime fileModificationTime)
1123 if (isConvertibleToString!R)
1125 getTimesWin!(StringTypeOf!R)(name, fileCreationTime, fileAccessTime, fileModificationTime);
1129 version (Windows) @system unittest
1131 import std.stdio : writefln;
1132 auto currTime = Clock.currTime();
1134 write(deleteme, "a");
1135 scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1137 SysTime creationTime1 = void;
1138 SysTime accessTime1 = void;
1139 SysTime modificationTime1 = void;
1141 getTimesWin(deleteme, creationTime1, accessTime1, modificationTime1);
1143 enum leeway = dur!"seconds"(5);
1146 auto diffc = creationTime1 - currTime;
1147 auto diffa = accessTime1 - currTime;
1148 auto diffm = modificationTime1 - currTime;
1151 writefln("[%s] [%s] [%s] [%s] [%s] [%s] [%s]",
1152 creationTime1, accessTime1, modificationTime1, currTime, diffc, diffa, diffm);
1155 // Deleting and recreating a file doesn't seem to always reset the "file creation time"
1156 //assert(abs(diffc) <= leeway);
1157 assert(abs(diffa) <= leeway);
1158 assert(abs(diffm) <= leeway);
1161 version (fullFileTests)
1164 Thread.sleep(dur!"seconds"(2));
1166 currTime = Clock.currTime();
1167 write(deleteme, "b");
1169 SysTime creationTime2 = void;
1170 SysTime accessTime2 = void;
1171 SysTime modificationTime2 = void;
1173 getTimesWin(deleteme, creationTime2, accessTime2, modificationTime2);
1176 auto diffa = accessTime2 - currTime;
1177 auto diffm = modificationTime2 - currTime;
1180 writefln("[%s] [%s] [%s] [%s] [%s]",
1181 accessTime2, modificationTime2, currTime, diffa, diffm);
1184 assert(abs(diffa) <= leeway);
1185 assert(abs(diffm) <= leeway);
1188 assert(creationTime1 == creationTime2);
1189 assert(accessTime1 <= accessTime2);
1190 assert(modificationTime1 <= modificationTime2);
1194 SysTime ctime, atime, mtime;
1195 static assert(__traits(compiles, getTimesWin(TestAliasedString("foo"), ctime, atime, mtime)));
1201 Set access/modified times of file or folder $(D name).
1204 name = File/Folder _name to get times for.
1205 accessTime = Time the file/folder was last accessed.
1206 modificationTime = Time the file/folder was last modified.
1209 $(D FileException) on error.
1211 void setTimes(R)(R name,
1213 SysTime modificationTime)
1214 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1215 !isConvertibleToString!R)
1219 import std.datetime.systime : SysTimeToFILETIME;
1221 auto namez = name.tempCString!FSChar();
1222 static auto trustedCreateFileW(const(FSChar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
1223 SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition,
1224 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted
1226 return CreateFileW(namez, dwDesiredAccess, dwShareMode,
1227 lpSecurityAttributes, dwCreationDisposition,
1228 dwFlagsAndAttributes, hTemplateFile);
1231 static auto trustedCloseHandle(HANDLE hObject) @trusted
1233 return CloseHandle(hObject);
1235 static auto trustedSetFileTime(HANDLE hFile, in FILETIME *lpCreationTime,
1236 in ref FILETIME lpLastAccessTime, in ref FILETIME lpLastWriteTime) @trusted
1238 return SetFileTime(hFile, lpCreationTime, &lpLastAccessTime, &lpLastWriteTime);
1241 const ta = SysTimeToFILETIME(accessTime);
1242 const tm = SysTimeToFILETIME(modificationTime);
1244 AliasSeq!(GENERIC_WRITE,
1248 FILE_ATTRIBUTE_NORMAL |
1249 FILE_ATTRIBUTE_DIRECTORY |
1250 FILE_FLAG_BACKUP_SEMANTICS,
1252 auto h = trustedCreateFileW(namez, defaults);
1254 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1257 string names = null;
1258 cenforce(h != INVALID_HANDLE_VALUE, names, namez);
1261 cenforce(trustedCloseHandle(h), names, namez);
1263 cenforce(trustedSetFileTime(h, null, ta, tm), names, namez);
1265 else version (Posix)
1267 auto namez = name.tempCString!FSChar();
1268 static if (is(typeof(&utimensat)))
1270 static auto trustedUtimensat(int fd, const(FSChar)* namez, const ref timespec[2] times, int flags) @trusted
1272 return utimensat(fd, namez, times, flags);
1274 timespec[2] t = void;
1276 t[0] = accessTime.toTimeSpec();
1277 t[1] = modificationTime.toTimeSpec();
1279 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1282 string names = null;
1283 cenforce(trustedUtimensat(AT_FDCWD, namez, t, 0) == 0, names, namez);
1287 static auto trustedUtimes(const(FSChar)* namez, const ref timeval[2] times) @trusted
1289 return utimes(namez, times);
1291 timeval[2] t = void;
1293 t[0] = accessTime.toTimeVal();
1294 t[1] = modificationTime.toTimeVal();
1296 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1299 string names = null;
1300 cenforce(trustedUtimes(namez, t) == 0, names, namez);
1306 void setTimes(R)(auto ref R name,
1308 SysTime modificationTime)
1309 if (isConvertibleToString!R)
1311 setTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
1316 if (false) // Test instatiation
1317 setTimes(TestAliasedString("foo"), SysTime.init, SysTime.init);
1322 import std.stdio : File;
1323 string newdir = deleteme ~ r".dir";
1324 string dir = newdir ~ r"/a/b/c";
1325 string file = dir ~ "/file";
1327 if (!exists(dir)) mkdirRecurse(dir);
1328 { auto f = File(file, "w"); }
1330 void testTimes(int hnsecValue)
1332 foreach (path; [file, dir]) // test file and dir
1334 SysTime atime = SysTime(DateTime(2010, 10, 4, 0, 0, 30), hnsecs(hnsecValue));
1335 SysTime mtime = SysTime(DateTime(2011, 10, 4, 0, 0, 30), hnsecs(hnsecValue));
1336 setTimes(path, atime, mtime);
1340 getTimes(path, atime_res, mtime_res);
1341 assert(atime == atime_res);
1342 assert(mtime == mtime_res);
1348 testTimes(123_456_7);
1350 rmdirRecurse(newdir);
1354 Returns the time that the given file was last modified.
1357 $(D FileException) if the given file does not exist.
1359 SysTime timeLastModified(R)(R name)
1360 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1361 !isConvertibleToString!R)
1368 getTimesWin(name, dummy, dummy, ftm);
1372 else version (Posix)
1374 auto namez = name.tempCString!FSChar();
1375 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1377 return stat(namez, &buf);
1379 stat_t statbuf = void;
1381 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1384 string names = null;
1385 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1387 return statTimeToStdTime!'m'(statbuf);
1392 SysTime timeLastModified(R)(auto ref R name)
1393 if (isConvertibleToString!R)
1395 return timeLastModified!(StringTypeOf!R)(name);
1400 static assert(__traits(compiles, timeLastModified(TestAliasedString("foo"))));
1404 Returns the time that the given file was last modified. If the
1405 file does not exist, returns $(D returnIfMissing).
1407 A frequent usage pattern occurs in build automation tools such as
1408 $(HTTP gnu.org/software/make, make) or $(HTTP
1409 en.wikipedia.org/wiki/Apache_Ant, ant). To check whether file $(D
1410 target) must be rebuilt from file $(D source) (i.e., $(D target) is
1411 older than $(D source) or does not exist), use the comparison
1412 below. The code throws a $(D FileException) if $(D source) does not
1413 exist (as it should). On the other hand, the $(D SysTime.min) default
1414 makes a non-existing $(D target) seem infinitely old so the test
1415 correctly prompts building it.
1418 name = The _name of the file to get the modification time for.
1419 returnIfMissing = The time to return if the given file does not exist.
1422 --------------------
1423 if (timeLastModified(source) >= timeLastModified(target, SysTime.min))
1429 // target is up-to-date
1431 --------------------
1433 SysTime timeLastModified(R)(R name, SysTime returnIfMissing)
1434 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
1439 return returnIfMissing;
1444 getTimesWin(name, dummy, dummy, ftm);
1448 else version (Posix)
1450 auto namez = name.tempCString!FSChar();
1451 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1453 return stat(namez, &buf);
1455 stat_t statbuf = void;
1457 return trustedStat(namez, statbuf) != 0 ?
1459 statTimeToStdTime!'m'(statbuf);
1465 //std.process.system("echo a > deleteme") == 0 || assert(false);
1466 if (exists(deleteme))
1469 write(deleteme, "a\n");
1473 assert(exists(deleteme));
1477 // assert(lastModified("deleteme") >
1478 // lastModified("this file does not exist", SysTime.min));
1479 //assert(lastModified("deleteme") > lastModified(__FILE__));
1483 // Tests sub-second precision of querying file times.
1484 // Should pass on most modern systems running on modern filesystems.
1486 // - FreeBSD, where one would need to first set the
1487 // vfs.timestamp_precision sysctl to a value greater than zero.
1488 // - OS X, where the native filesystem (HFS+) stores filesystem
1489 // timestamps with 1-second precision.
1490 version (FreeBSD) {} else
1491 version (OSX) {} else
1496 if (exists(deleteme))
1502 write(deleteme, "a");
1503 auto time = timeLastModified(deleteme);
1505 assert(time != lastTime);
1507 Thread.sleep(10.msecs);
1513 * Determine whether the given file (or directory) _exists.
1515 * name = string or range of characters representing the file _name
1517 * true if the file _name specified as input _exists
1519 bool exists(R)(R name)
1520 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1521 !isConvertibleToString!R)
1523 return existsImpl(name.tempCString!FSChar());
1527 bool exists(R)(auto ref R name)
1528 if (isConvertibleToString!R)
1530 return exists!(StringTypeOf!R)(name);
1533 private bool existsImpl(const(FSChar)* namez) @trusted nothrow @nogc
1537 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
1538 // fileio/base/getfileattributes.asp
1539 return GetFileAttributesW(namez) != 0xFFFFFFFF;
1541 else version (Posix)
1544 The reason why we use stat (and not access) here is
1545 the quirky behavior of access for SUID programs: if
1546 we used access, a file may not appear to "exist",
1547 despite that the program would be able to open it
1548 just fine. The behavior in question is described as
1549 follows in the access man page:
1551 > The check is done using the calling process's real
1552 > UID and GID, rather than the effective IDs as is
1553 > done when actually attempting an operation (e.g.,
1554 > open(2)) on the file. This allows set-user-ID
1555 > programs to easily determine the invoking user's
1558 While various operating systems provide eaccess or
1559 euidaccess functions, these are not part of POSIX -
1560 so it's safer to use stat instead.
1563 stat_t statbuf = void;
1564 return lstat(namez, &statbuf) == 0;
1572 assert(exists("."));
1573 assert(!exists("this file does not exist"));
1574 write(deleteme, "a\n");
1575 scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1576 assert(exists(deleteme));
1579 @safe unittest // Bugzilla 16573
1581 enum S : string { foo = "foo" }
1582 assert(__traits(compiles, S.foo.exists));
1586 Returns the attributes of the given file.
1588 Note that the file attributes on Windows and Posix systems are
1589 completely different. On Windows, they're what is returned by
1590 $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx,
1591 GetFileAttributes), whereas on Posix systems, they're the $(LUCKY
1592 st_mode) value which is part of the $(D stat struct) gotten by
1593 calling the $(HTTP en.wikipedia.org/wiki/Stat_%28Unix%29, $(D stat))
1596 On Posix systems, if the given file is a symbolic link, then
1597 attributes are the attributes of the file pointed to by the symbolic
1601 name = The file to get the attributes of.
1603 Throws: $(D FileException) on error.
1605 uint getAttributes(R)(R name)
1606 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1607 !isConvertibleToString!R)
1611 auto namez = name.tempCString!FSChar();
1612 static auto trustedGetFileAttributesW(const(FSChar)* namez) @trusted
1614 return GetFileAttributesW(namez);
1616 immutable result = trustedGetFileAttributesW(namez);
1618 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1621 string names = null;
1622 cenforce(result != INVALID_FILE_ATTRIBUTES, names, namez);
1626 else version (Posix)
1628 auto namez = name.tempCString!FSChar();
1629 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1631 return stat(namez, &buf);
1633 stat_t statbuf = void;
1635 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1638 string names = null;
1639 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1641 return statbuf.st_mode;
1646 uint getAttributes(R)(auto ref R name)
1647 if (isConvertibleToString!R)
1649 return getAttributes!(StringTypeOf!R)(name);
1654 static assert(__traits(compiles, getAttributes(TestAliasedString(null))));
1658 If the given file is a symbolic link, then this returns the attributes of the
1659 symbolic link itself rather than file that it points to. If the given file
1660 is $(I not) a symbolic link, then this function returns the same result
1663 On Windows, getLinkAttributes is identical to getAttributes. It exists on
1664 Windows so that you don't have to special-case code for Windows when dealing
1665 with symbolic links.
1668 name = The file to get the symbolic link attributes of.
1674 $(D FileException) on error.
1676 uint getLinkAttributes(R)(R name)
1677 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1678 !isConvertibleToString!R)
1682 return getAttributes(name);
1684 else version (Posix)
1686 auto namez = name.tempCString!FSChar();
1687 static auto trustedLstat(const(FSChar)* namez, ref stat_t buf) @trusted
1689 return lstat(namez, &buf);
1691 stat_t lstatbuf = void;
1692 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1695 string names = null;
1696 cenforce(trustedLstat(namez, lstatbuf) == 0, names, namez);
1697 return lstatbuf.st_mode;
1702 uint getLinkAttributes(R)(auto ref R name)
1703 if (isConvertibleToString!R)
1705 return getLinkAttributes!(StringTypeOf!R)(name);
1710 static assert(__traits(compiles, getLinkAttributes(TestAliasedString(null))));
1714 Set the _attributes of the given file.
1717 name = the file _name
1718 attributes = the _attributes to set the file to
1721 $(D FileException) if the given file does not exist.
1723 void setAttributes(R)(R name, uint attributes)
1724 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1725 !isConvertibleToString!R)
1729 auto namez = name.tempCString!FSChar();
1730 static auto trustedSetFileAttributesW(const(FSChar)* namez, uint dwFileAttributes) @trusted
1732 return SetFileAttributesW(namez, dwFileAttributes);
1734 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1737 string names = null;
1738 cenforce(trustedSetFileAttributesW(namez, attributes), names, namez);
1740 else version (Posix)
1742 auto namez = name.tempCString!FSChar();
1743 static auto trustedChmod(const(FSChar)* namez, mode_t mode) @trusted
1745 return chmod(namez, mode);
1747 assert(attributes <= mode_t.max);
1748 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1751 string names = null;
1752 cenforce(!trustedChmod(namez, cast(mode_t) attributes), names, namez);
1757 void setAttributes(R)(auto ref R name, uint attributes)
1758 if (isConvertibleToString!R)
1760 return setAttributes!(StringTypeOf!R)(name, attributes);
1765 static assert(__traits(compiles, setAttributes(TestAliasedString(null), 0)));
1769 Returns whether the given file is a directory.
1772 name = The path to the file.
1775 true if name specifies a directory
1778 $(D FileException) if the given file does not exist.
1781 --------------------
1782 assert(!"/etc/fonts/fonts.conf".isDir);
1783 assert("/usr/share/include".isDir);
1784 --------------------
1786 @property bool isDir(R)(R name)
1787 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1788 !isConvertibleToString!R)
1792 return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) != 0;
1794 else version (Posix)
1796 return (getAttributes(name) & S_IFMT) == S_IFDIR;
1801 @property bool isDir(R)(auto ref R name)
1802 if (isConvertibleToString!R)
1804 return name.isDir!(StringTypeOf!R);
1809 static assert(__traits(compiles, TestAliasedString(null).isDir));
1816 if ("C:\\Program Files\\".exists)
1817 assert("C:\\Program Files\\".isDir);
1819 if ("C:\\Windows\\system.ini".exists)
1820 assert(!"C:\\Windows\\system.ini".isDir);
1822 else version (Posix)
1824 if (system_directory.exists)
1825 assert(system_directory.isDir);
1827 if (system_file.exists)
1828 assert(!system_file.isDir);
1835 enum dir = "C:\\Program Files\\";
1836 else version (Posix)
1837 enum dir = system_directory;
1841 DirEntry de = DirEntry(dir);
1843 assert(DirEntry(dir).isDir);
1848 Returns whether the given file _attributes are for a directory.
1851 attributes = The file _attributes.
1854 true if attributes specifies a directory
1857 --------------------
1858 assert(!attrIsDir(getAttributes("/etc/fonts/fonts.conf")));
1859 assert(!attrIsDir(getLinkAttributes("/etc/fonts/fonts.conf")));
1860 --------------------
1862 bool attrIsDir(uint attributes) @safe pure nothrow @nogc
1866 return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
1868 else version (Posix)
1870 return (attributes & S_IFMT) == S_IFDIR;
1878 if ("C:\\Program Files\\".exists)
1880 assert(attrIsDir(getAttributes("C:\\Program Files\\")));
1881 assert(attrIsDir(getLinkAttributes("C:\\Program Files\\")));
1884 if ("C:\\Windows\\system.ini".exists)
1886 assert(!attrIsDir(getAttributes("C:\\Windows\\system.ini")));
1887 assert(!attrIsDir(getLinkAttributes("C:\\Windows\\system.ini")));
1890 else version (Posix)
1892 if (system_directory.exists)
1894 assert(attrIsDir(getAttributes(system_directory)));
1895 assert(attrIsDir(getLinkAttributes(system_directory)));
1898 if (system_file.exists)
1900 assert(!attrIsDir(getAttributes(system_file)));
1901 assert(!attrIsDir(getLinkAttributes(system_file)));
1908 Returns whether the given file (or directory) is a file.
1910 On Windows, if a file is not a directory, then it's a file. So,
1911 either $(D isFile) or $(D isDir) will return true for any given file.
1913 On Posix systems, if $(D isFile) is $(D true), that indicates that the file
1914 is a regular file (e.g. not a block not device). So, on Posix systems, it's
1915 possible for both $(D isFile) and $(D isDir) to be $(D false) for a
1916 particular file (in which case, it's a special file). You can use
1917 $(D getAttributes) to get the attributes to figure out what type of special
1918 it is, or you can use $(D DirEntry) to get at its $(D statBuf), which is the
1919 result from $(D stat). In either case, see the man page for $(D stat) for
1923 name = The path to the file.
1926 true if name specifies a file
1929 $(D FileException) if the given file does not exist.
1932 --------------------
1933 assert("/etc/fonts/fonts.conf".isFile);
1934 assert(!"/usr/share/include".isFile);
1935 --------------------
1937 @property bool isFile(R)(R name)
1938 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1939 !isConvertibleToString!R)
1943 else version (Posix)
1944 return (getAttributes(name) & S_IFMT) == S_IFREG;
1948 @property bool isFile(R)(auto ref R name)
1949 if (isConvertibleToString!R)
1951 return isFile!(StringTypeOf!R)(name);
1954 @system unittest // bugzilla 15658
1956 DirEntry e = DirEntry(".");
1957 static assert(is(typeof(isFile(e))));
1962 static assert(__traits(compiles, TestAliasedString(null).isFile));
1969 if ("C:\\Program Files\\".exists)
1970 assert(!"C:\\Program Files\\".isFile);
1972 if ("C:\\Windows\\system.ini".exists)
1973 assert("C:\\Windows\\system.ini".isFile);
1975 else version (Posix)
1977 if (system_directory.exists)
1978 assert(!system_directory.isFile);
1980 if (system_file.exists)
1981 assert(system_file.isFile);
1987 Returns whether the given file _attributes are for a file.
1989 On Windows, if a file is not a directory, it's a file. So, either
1990 $(D attrIsFile) or $(D attrIsDir) will return $(D true) for the
1991 _attributes of any given file.
1993 On Posix systems, if $(D attrIsFile) is $(D true), that indicates that the
1994 file is a regular file (e.g. not a block not device). So, on Posix systems,
1995 it's possible for both $(D attrIsFile) and $(D attrIsDir) to be $(D false)
1996 for a particular file (in which case, it's a special file). If a file is a
1997 special file, you can use the _attributes to check what type of special file
1998 it is (see the man page for $(D stat) for more information).
2001 attributes = The file _attributes.
2004 true if the given file _attributes are for a file
2007 --------------------
2008 assert(attrIsFile(getAttributes("/etc/fonts/fonts.conf")));
2009 assert(attrIsFile(getLinkAttributes("/etc/fonts/fonts.conf")));
2010 --------------------
2012 bool attrIsFile(uint attributes) @safe pure nothrow @nogc
2016 return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
2018 else version (Posix)
2020 return (attributes & S_IFMT) == S_IFREG;
2028 if ("C:\\Program Files\\".exists)
2030 assert(!attrIsFile(getAttributes("C:\\Program Files\\")));
2031 assert(!attrIsFile(getLinkAttributes("C:\\Program Files\\")));
2034 if ("C:\\Windows\\system.ini".exists)
2036 assert(attrIsFile(getAttributes("C:\\Windows\\system.ini")));
2037 assert(attrIsFile(getLinkAttributes("C:\\Windows\\system.ini")));
2040 else version (Posix)
2042 if (system_directory.exists)
2044 assert(!attrIsFile(getAttributes(system_directory)));
2045 assert(!attrIsFile(getLinkAttributes(system_directory)));
2048 if (system_file.exists)
2050 assert(attrIsFile(getAttributes(system_file)));
2051 assert(attrIsFile(getLinkAttributes(system_file)));
2058 Returns whether the given file is a symbolic link.
2060 On Windows, returns $(D true) when the file is either a symbolic link or a
2064 name = The path to the file.
2067 true if name is a symbolic link
2070 $(D FileException) if the given file does not exist.
2072 @property bool isSymlink(R)(R name)
2073 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2074 !isConvertibleToString!R)
2077 return (getAttributes(name) & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2078 else version (Posix)
2079 return (getLinkAttributes(name) & S_IFMT) == S_IFLNK;
2083 @property bool isSymlink(R)(auto ref R name)
2084 if (isConvertibleToString!R)
2086 return name.isSymlink!(StringTypeOf!R);
2091 static assert(__traits(compiles, TestAliasedString(null).isSymlink));
2098 if ("C:\\Program Files\\".exists)
2099 assert(!"C:\\Program Files\\".isSymlink);
2101 if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
2102 assert("C:\\Documents and Settings\\".isSymlink);
2104 enum fakeSymFile = "C:\\Windows\\system.ini";
2105 if (fakeSymFile.exists)
2107 assert(!fakeSymFile.isSymlink);
2109 assert(!fakeSymFile.isSymlink);
2110 assert(!attrIsSymlink(getAttributes(fakeSymFile)));
2111 assert(!attrIsSymlink(getLinkAttributes(fakeSymFile)));
2113 assert(attrIsFile(getAttributes(fakeSymFile)));
2114 assert(attrIsFile(getLinkAttributes(fakeSymFile)));
2115 assert(!attrIsDir(getAttributes(fakeSymFile)));
2116 assert(!attrIsDir(getLinkAttributes(fakeSymFile)));
2118 assert(getAttributes(fakeSymFile) == getLinkAttributes(fakeSymFile));
2121 else version (Posix)
2123 if (system_directory.exists)
2125 assert(!system_directory.isSymlink);
2127 immutable symfile = deleteme ~ "_slink\0";
2128 scope(exit) if (symfile.exists) symfile.remove();
2130 core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
2132 assert(symfile.isSymlink);
2133 assert(!attrIsSymlink(getAttributes(symfile)));
2134 assert(attrIsSymlink(getLinkAttributes(symfile)));
2136 assert(attrIsDir(getAttributes(symfile)));
2137 assert(!attrIsDir(getLinkAttributes(symfile)));
2139 assert(!attrIsFile(getAttributes(symfile)));
2140 assert(!attrIsFile(getLinkAttributes(symfile)));
2143 if (system_file.exists)
2145 assert(!system_file.isSymlink);
2147 immutable symfile = deleteme ~ "_slink\0";
2148 scope(exit) if (symfile.exists) symfile.remove();
2150 core.sys.posix.unistd.symlink(system_file, symfile.ptr);
2152 assert(symfile.isSymlink);
2153 assert(!attrIsSymlink(getAttributes(symfile)));
2154 assert(attrIsSymlink(getLinkAttributes(symfile)));
2156 assert(!attrIsDir(getAttributes(symfile)));
2157 assert(!attrIsDir(getLinkAttributes(symfile)));
2159 assert(attrIsFile(getAttributes(symfile)));
2160 assert(!attrIsFile(getLinkAttributes(symfile)));
2164 static assert(__traits(compiles, () @safe { return "dummy".isSymlink; }));
2169 Returns whether the given file attributes are for a symbolic link.
2171 On Windows, return $(D true) when the file is either a symbolic link or a
2175 attributes = The file attributes.
2178 true if attributes are for a symbolic link
2181 --------------------
2182 core.sys.posix.unistd.symlink("/etc/fonts/fonts.conf", "/tmp/alink");
2184 assert(!getAttributes("/tmp/alink").isSymlink);
2185 assert(getLinkAttributes("/tmp/alink").isSymlink);
2186 --------------------
2188 bool attrIsSymlink(uint attributes) @safe pure nothrow @nogc
2191 return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2192 else version (Posix)
2193 return (attributes & S_IFMT) == S_IFLNK;
2197 /****************************************************
2198 * Change directory to $(D pathname).
2199 * Throws: $(D FileException) on error.
2201 void chdir(R)(R pathname)
2202 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2203 !isConvertibleToString!R)
2205 // Place outside of @trusted block
2206 auto pathz = pathname.tempCString!FSChar();
2210 static auto trustedChdir(const(FSChar)* pathz) @trusted
2212 return SetCurrentDirectoryW(pathz);
2215 else version (Posix)
2217 static auto trustedChdir(const(FSChar)* pathz) @trusted
2219 return core.sys.posix.unistd.chdir(pathz) == 0;
2222 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2223 alias pathStr = pathname;
2225 string pathStr = null;
2226 cenforce(trustedChdir(pathz), pathStr, pathz);
2230 void chdir(R)(auto ref R pathname)
2231 if (isConvertibleToString!R)
2233 return chdir!(StringTypeOf!R)(pathname);
2238 static assert(__traits(compiles, chdir(TestAliasedString(null))));
2241 /****************************************************
2242 Make directory $(D pathname).
2244 Throws: $(D FileException) on Posix or $(D WindowsException) on Windows
2245 if an error occured.
2247 void mkdir(R)(R pathname)
2248 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2249 !isConvertibleToString!R)
2251 // Place outside of @trusted block
2252 const pathz = pathname.tempCString!FSChar();
2256 static auto trustedCreateDirectoryW(const(FSChar)* pathz) @trusted
2258 return CreateDirectoryW(pathz, null);
2260 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2261 alias pathStr = pathname;
2263 string pathStr = null;
2264 wenforce(trustedCreateDirectoryW(pathz), pathStr, pathz);
2266 else version (Posix)
2268 import std.conv : octal;
2270 static auto trustedMkdir(const(FSChar)* pathz, mode_t mode) @trusted
2272 return core.sys.posix.sys.stat.mkdir(pathz, mode);
2274 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2275 alias pathStr = pathname;
2277 string pathStr = null;
2278 cenforce(trustedMkdir(pathz, octal!777) == 0, pathStr, pathz);
2283 void mkdir(R)(auto ref R pathname)
2284 if (isConvertibleToString!R)
2286 return mkdir!(StringTypeOf!R)(pathname);
2291 import std.path : mkdir;
2292 static assert(__traits(compiles, mkdir(TestAliasedString(null))));
2295 // Same as mkdir but ignores "already exists" errors.
2296 // Returns: "true" if the directory was created,
2297 // "false" if it already existed.
2298 private bool ensureDirExists()(in char[] pathname)
2300 import std.exception : enforce;
2301 const pathz = pathname.tempCString!FSChar();
2305 if (() @trusted { return CreateDirectoryW(pathz, null); }())
2307 cenforce(GetLastError() == ERROR_ALREADY_EXISTS, pathname.idup);
2309 else version (Posix)
2311 import std.conv : octal;
2313 if (() @trusted { return core.sys.posix.sys.stat.mkdir(pathz, octal!777); }() == 0)
2315 cenforce(errno == EEXIST || errno == EISDIR, pathname);
2317 enforce(pathname.isDir, new FileException(pathname.idup));
2321 /****************************************************
2322 * Make directory and all parent directories as needed.
2324 * Does nothing if the directory specified by
2325 * $(D pathname) already exists.
2327 * Throws: $(D FileException) on error.
2330 void mkdirRecurse(in char[] pathname) @safe
2332 import std.path : dirName, baseName;
2334 const left = dirName(pathname);
2335 if (left.length != pathname.length && !exists(left))
2339 if (!baseName(pathname).empty)
2341 ensureDirExists(pathname);
2347 import std.exception : assertThrown;
2349 import std.path : buildPath, buildNormalizedPath;
2351 immutable basepath = deleteme ~ "_dir";
2352 scope(exit) () @trusted { rmdirRecurse(basepath); }();
2354 auto path = buildPath(basepath, "a", "..", "b");
2356 path = path.buildNormalizedPath;
2359 path = buildPath(basepath, "c");
2361 assertThrown!FileException(mkdirRecurse(path));
2363 path = buildPath(basepath, "d");
2365 mkdirRecurse(path); // should not throw
2370 assertThrown!FileException(mkdirRecurse(`1:\foobar`));
2375 immutable basepath = deleteme ~ "_dir";
2378 immutable path = basepath ~ "\\fake\\here\\";
2380 else version (Posix)
2382 immutable path = basepath ~ `/fake/here/`;
2386 assert(basepath.exists && basepath.isDir);
2387 scope(exit) () @trusted { rmdirRecurse(basepath); }();
2388 assert(path.exists && path.isDir);
2392 /****************************************************
2393 Remove directory $(D pathname).
2396 pathname = Range or string specifying the directory name
2398 Throws: $(D FileException) on error.
2400 void rmdir(R)(R pathname)
2401 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2402 !isConvertibleToString!R)
2404 // Place outside of @trusted block
2405 auto pathz = pathname.tempCString!FSChar();
2409 static auto trustedRmdir(const(FSChar)* pathz) @trusted
2411 return RemoveDirectoryW(pathz);
2414 else version (Posix)
2416 static auto trustedRmdir(const(FSChar)* pathz) @trusted
2418 return core.sys.posix.unistd.rmdir(pathz) == 0;
2421 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2422 alias pathStr = pathname;
2424 string pathStr = null;
2425 cenforce(trustedRmdir(pathz), pathStr, pathz);
2429 void rmdir(R)(auto ref R pathname)
2430 if (isConvertibleToString!R)
2432 rmdir!(StringTypeOf!R)(pathname);
2437 static assert(__traits(compiles, rmdir(TestAliasedString(null))));
2441 $(BLUE This function is Posix-Only.)
2443 Creates a symbolic _link (_symlink).
2446 original = The file that is being linked. This is the target path that's
2447 stored in the _symlink. A relative path is relative to the created
2449 link = The _symlink to create. A relative path is relative to the
2450 current working directory.
2453 $(D FileException) on error (which includes if the _symlink already
2456 version (StdDdoc) void symlink(RO, RL)(RO original, RL link)
2457 if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) ||
2458 isConvertibleToString!RO) &&
2459 (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) ||
2460 isConvertibleToString!RL));
2461 else version (Posix) void symlink(RO, RL)(RO original, RL link)
2462 if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) ||
2463 isConvertibleToString!RO) &&
2464 (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) ||
2465 isConvertibleToString!RL))
2467 static if (isConvertibleToString!RO || isConvertibleToString!RL)
2469 import std.meta : staticMap;
2470 alias Types = staticMap!(convertToString, RO, RL);
2471 symlink!Types(original, link);
2475 import std.conv : text;
2476 auto oz = original.tempCString();
2477 auto lz = link.tempCString();
2478 alias posixSymlink = core.sys.posix.unistd.symlink;
2479 immutable int result = () @trusted { return posixSymlink(oz, lz); } ();
2480 cenforce(result == 0, text(link));
2484 version (Posix) @safe unittest
2486 if (system_directory.exists)
2488 immutable symfile = deleteme ~ "_slink\0";
2489 scope(exit) if (symfile.exists) symfile.remove();
2491 symlink(system_directory, symfile);
2493 assert(symfile.exists);
2494 assert(symfile.isSymlink);
2495 assert(!attrIsSymlink(getAttributes(symfile)));
2496 assert(attrIsSymlink(getLinkAttributes(symfile)));
2498 assert(attrIsDir(getAttributes(symfile)));
2499 assert(!attrIsDir(getLinkAttributes(symfile)));
2501 assert(!attrIsFile(getAttributes(symfile)));
2502 assert(!attrIsFile(getLinkAttributes(symfile)));
2505 if (system_file.exists)
2507 assert(!system_file.isSymlink);
2509 immutable symfile = deleteme ~ "_slink\0";
2510 scope(exit) if (symfile.exists) symfile.remove();
2512 symlink(system_file, symfile);
2514 assert(symfile.exists);
2515 assert(symfile.isSymlink);
2516 assert(!attrIsSymlink(getAttributes(symfile)));
2517 assert(attrIsSymlink(getLinkAttributes(symfile)));
2519 assert(!attrIsDir(getAttributes(symfile)));
2520 assert(!attrIsDir(getLinkAttributes(symfile)));
2522 assert(attrIsFile(getAttributes(symfile)));
2523 assert(!attrIsFile(getLinkAttributes(symfile)));
2527 version (Posix) @safe unittest
2529 static assert(__traits(compiles,
2530 symlink(TestAliasedString(null), TestAliasedString(null))));
2535 $(BLUE This function is Posix-Only.)
2537 Returns the path to the file pointed to by a symlink. Note that the
2538 path could be either relative or absolute depending on the symlink.
2539 If the path is relative, it's relative to the symlink, not the current
2543 $(D FileException) on error.
2545 version (StdDdoc) string readLink(R)(R link)
2546 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) ||
2547 isConvertibleToString!R);
2548 else version (Posix) string readLink(R)(R link)
2549 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) ||
2550 isConvertibleToString!R)
2552 static if (isConvertibleToString!R)
2554 return readLink!(convertToString!R)(link);
2558 import std.conv : to;
2559 import std.exception : assumeUnique;
2560 alias posixReadlink = core.sys.posix.unistd.readlink;
2561 enum bufferLen = 2048;
2562 enum maxCodeUnits = 6;
2563 char[bufferLen] buffer;
2564 const linkz = link.tempCString();
2565 auto size = () @trusted {
2566 return posixReadlink(linkz, buffer.ptr, buffer.length);
2568 cenforce(size != -1, to!string(link));
2570 if (size <= bufferLen - maxCodeUnits)
2571 return to!string(buffer[0 .. size]);
2573 auto dynamicBuffer = new char[](bufferLen * 3 / 2);
2575 foreach (i; 0 .. 10)
2577 size = () @trusted {
2578 return posixReadlink(linkz, dynamicBuffer.ptr,
2579 dynamicBuffer.length);
2581 cenforce(size != -1, to!string(link));
2583 if (size <= dynamicBuffer.length - maxCodeUnits)
2585 dynamicBuffer.length = size;
2586 return () @trusted {
2587 return assumeUnique(dynamicBuffer);
2591 dynamicBuffer.length = dynamicBuffer.length * 3 / 2;
2594 throw new FileException(to!string(link), "Path is too long to read.");
2598 version (Posix) @safe unittest
2600 import std.exception : assertThrown;
2603 foreach (file; [system_directory, system_file])
2607 immutable symfile = deleteme ~ "_slink\0";
2608 scope(exit) if (symfile.exists) symfile.remove();
2610 symlink(file, symfile);
2611 assert(readLink(symfile) == file, format("Failed file: %s", file));
2615 assertThrown!FileException(readLink("/doesnotexist"));
2618 version (Posix) @safe unittest
2620 static assert(__traits(compiles, readLink(TestAliasedString("foo"))));
2623 version (Posix) @system unittest // input range of dchars
2625 mkdirRecurse(deleteme);
2626 scope(exit) if (deleteme.exists) rmdirRecurse(deleteme);
2627 write(deleteme ~ "/f", "");
2628 import std.range.interfaces : InputRange, inputRangeObject;
2629 import std.utf : byChar;
2630 immutable string link = deleteme ~ "/l";
2632 InputRange!dchar linkr = inputRangeObject(link);
2633 alias R = typeof(linkr);
2634 static assert(isInputRange!R);
2635 static assert(!isForwardRange!R);
2636 assert(readLink(linkr) == "f");
2640 /****************************************************
2641 * Get the current working directory.
2642 * Throws: $(D FileException) on error.
2644 version (Windows) string getcwd()
2646 import std.conv : to;
2647 /* GetCurrentDirectory's return value:
2648 1. function succeeds: the number of characters that are written to
2649 the buffer, not including the terminating null character.
2650 2. function fails: zero
2651 3. the buffer (lpBuffer) is not large enough: the required size of
2652 the buffer, in characters, including the null-terminating character.
2654 wchar[4096] buffW = void; //enough for most common case
2655 immutable n = cenforce(GetCurrentDirectoryW(to!DWORD(buffW.length), buffW.ptr),
2657 // we can do it because toUTFX always produces a fresh string
2658 if (n < buffW.length)
2660 return buffW[0 .. n].to!string;
2662 else //staticBuff isn't enough
2664 auto ptr = cast(wchar*) malloc(wchar.sizeof * n);
2665 scope(exit) free(ptr);
2666 immutable n2 = GetCurrentDirectoryW(n, ptr);
2667 cenforce(n2 && n2 < n, "getcwd");
2668 return ptr[0 .. n2].to!string;
2671 else version (Solaris) string getcwd()
2673 /* BUF_SIZE >= PATH_MAX */
2674 enum BUF_SIZE = 4096;
2675 /* The user should be able to specify any size buffer > 0 */
2676 auto p = cenforce(core.sys.posix.unistd.getcwd(null, BUF_SIZE),
2678 scope(exit) core.stdc.stdlib.free(p);
2679 return p[0 .. core.stdc.string.strlen(p)].idup;
2681 else version (Posix) string getcwd()
2683 auto p = cenforce(core.sys.posix.unistd.getcwd(null, 0),
2685 scope(exit) core.stdc.stdlib.free(p);
2686 return p[0 .. core.stdc.string.strlen(p)].idup;
2696 private extern (C) int _NSGetExecutablePath(char* buf, uint* bufsize);
2697 else version (FreeBSD)
2698 private extern (C) int sysctl (const int* name, uint namelen, void* oldp,
2699 size_t* oldlenp, const void* newp, size_t newlen);
2700 else version (NetBSD)
2701 private extern (C) int sysctl (const int* name, uint namelen, void* oldp,
2702 size_t* oldlenp, const void* newp, size_t newlen);
2705 * Returns the full path of the current executable.
2708 * $(REF1 Exception, object)
2710 @trusted string thisExePath ()
2714 import core.sys.posix.stdlib : realpath;
2715 import std.conv : to;
2716 import std.exception : errnoEnforce;
2720 _NSGetExecutablePath(null, &size); // get the length of the path
2721 auto buffer = new char[size];
2722 _NSGetExecutablePath(buffer.ptr, &size);
2724 auto absolutePath = realpath(buffer.ptr, null); // let the function allocate
2732 errnoEnforce(absolutePath);
2733 return to!(string)(absolutePath);
2735 else version (linux)
2737 return readLink("/proc/self/exe");
2739 else version (Windows)
2741 import std.conv : to;
2742 import std.exception : enforce;
2744 wchar[MAX_PATH] buf;
2745 wchar[] buffer = buf[];
2749 auto len = GetModuleFileNameW(null, buffer.ptr, cast(DWORD) buffer.length);
2750 enforce(len, sysErrorString(GetLastError()));
2751 if (len != buffer.length)
2752 return to!(string)(buffer[0 .. len]);
2756 else version (FreeBSD)
2758 import std.exception : errnoEnforce, assumeUnique;
2763 KERN_PROC_PATHNAME = 12
2766 int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
2769 auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
2770 errnoEnforce(result == 0);
2772 auto buffer = new char[len - 1];
2773 result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
2774 errnoEnforce(result == 0);
2776 return buffer.assumeUnique;
2778 else version (NetBSD)
2780 return readLink("/proc/self/exe");
2782 else version (Solaris)
2784 import core.sys.posix.unistd : getpid;
2785 import std.string : format;
2787 // Only Solaris 10 and later
2788 return readLink(format("/proc/%d/path/a.out", getpid()));
2791 static assert(0, "thisExePath is not supported on this platform");
2796 import std.path : isAbsolute;
2797 auto path = thisExePath();
2799 assert(path.exists);
2800 assert(path.isAbsolute);
2801 assert(path.isFile);
2807 Info on a file, similar to what you'd get from stat on a Posix system.
2812 Constructs a $(D DirEntry) for the given file (or directory).
2815 path = The file (or directory) to get a DirEntry for.
2818 $(D FileException) if the file does not exist.
2824 private this(string path, in WIN32_FIND_DATAW *fd);
2826 else version (Posix)
2828 private this(string path, core.sys.posix.dirent.dirent* fd);
2832 Returns the path to the file represented by this $(D DirEntry).
2835 --------------------
2836 auto de1 = DirEntry("/etc/fonts/fonts.conf");
2837 assert(de1.name == "/etc/fonts/fonts.conf");
2839 auto de2 = DirEntry("/usr/share/include");
2840 assert(de2.name == "/usr/share/include");
2841 --------------------
2843 @property string name() const;
2847 Returns whether the file represented by this $(D DirEntry) is a
2851 --------------------
2852 auto de1 = DirEntry("/etc/fonts/fonts.conf");
2855 auto de2 = DirEntry("/usr/share/include");
2857 --------------------
2859 @property bool isDir();
2863 Returns whether the file represented by this $(D DirEntry) is a file.
2865 On Windows, if a file is not a directory, then it's a file. So,
2866 either $(D isFile) or $(D isDir) will return $(D true).
2868 On Posix systems, if $(D isFile) is $(D true), that indicates that
2869 the file is a regular file (e.g. not a block not device). So, on
2870 Posix systems, it's possible for both $(D isFile) and $(D isDir) to
2871 be $(D false) for a particular file (in which case, it's a special
2872 file). You can use $(D attributes) or $(D statBuf) to get more
2873 information about a special file (see the stat man page for more
2877 --------------------
2878 auto de1 = DirEntry("/etc/fonts/fonts.conf");
2881 auto de2 = DirEntry("/usr/share/include");
2882 assert(!de2.isFile);
2883 --------------------
2885 @property bool isFile();
2888 Returns whether the file represented by this $(D DirEntry) is a
2891 On Windows, return $(D true) when the file is either a symbolic
2892 link or a junction point.
2894 @property bool isSymlink();
2897 Returns the size of the the file represented by this $(D DirEntry)
2900 @property ulong size();
2903 $(BLUE This function is Windows-Only.)
2905 Returns the creation time of the file represented by this
2908 @property SysTime timeCreated() const;
2911 Returns the time that the file represented by this $(D DirEntry) was
2914 Note that many file systems do not update the access time for files
2915 (generally for performance reasons), so there's a good chance that
2916 $(D timeLastAccessed) will return the same value as
2917 $(D timeLastModified).
2919 @property SysTime timeLastAccessed();
2922 Returns the time that the file represented by this $(D DirEntry) was
2925 @property SysTime timeLastModified();
2928 Returns the _attributes of the file represented by this $(D DirEntry).
2930 Note that the file _attributes on Windows and Posix systems are
2931 completely different. On, Windows, they're what is returned by
2932 $(D GetFileAttributes)
2933 $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, GetFileAttributes)
2934 Whereas, an Posix systems, they're the $(D st_mode) value which is
2935 part of the $(D stat) struct gotten by calling $(D stat).
2937 On Posix systems, if the file represented by this $(D DirEntry) is a
2938 symbolic link, then _attributes are the _attributes of the file
2939 pointed to by the symbolic link.
2941 @property uint attributes();
2944 On Posix systems, if the file represented by this $(D DirEntry) is a
2945 symbolic link, then $(D linkAttributes) are the attributes of the
2946 symbolic link itself. Otherwise, $(D linkAttributes) is identical to
2949 On Windows, $(D linkAttributes) is identical to $(D attributes). It
2950 exists on Windows so that you don't have to special-case code for
2951 Windows when dealing with symbolic links.
2953 @property uint linkAttributes();
2956 alias stat_t = void*;
2959 $(BLUE This function is Posix-Only.)
2961 The $(D stat) struct gotten from calling $(D stat).
2963 @property stat_t statBuf();
2966 else version (Windows)
2975 import std.datetime.systime : FILETIMEToSysTime;
2978 throw new FileException(path, "File does not exist");
2982 with (getFileAttributesWin(path))
2984 _size = makeUlong(nFileSizeLow, nFileSizeHigh);
2985 _timeCreated = FILETIMEToSysTime(&ftCreationTime);
2986 _timeLastAccessed = FILETIMEToSysTime(&ftLastAccessTime);
2987 _timeLastModified = FILETIMEToSysTime(&ftLastWriteTime);
2988 _attributes = dwFileAttributes;
2992 private this(string path, in WIN32_FIND_DATAW *fd)
2994 import core.stdc.wchar_ : wcslen;
2995 import std.conv : to;
2996 import std.datetime.systime : FILETIMEToSysTime;
2997 import std.path : buildPath;
2999 size_t clength = wcslen(fd.cFileName.ptr);
3000 _name = buildPath(path, fd.cFileName[0 .. clength].to!string);
3001 _size = (cast(ulong) fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
3002 _timeCreated = FILETIMEToSysTime(&fd.ftCreationTime);
3003 _timeLastAccessed = FILETIMEToSysTime(&fd.ftLastAccessTime);
3004 _timeLastModified = FILETIMEToSysTime(&fd.ftLastWriteTime);
3005 _attributes = fd.dwFileAttributes;
3008 @property string name() const pure nothrow
3013 @property bool isDir() const pure nothrow
3015 return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
3018 @property bool isFile() const pure nothrow
3020 //Are there no options in Windows other than directory and file?
3021 //If there are, then this probably isn't the best way to determine
3022 //whether this DirEntry is a file or not.
3026 @property bool isSymlink() const pure nothrow
3028 return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
3031 @property ulong size() const pure nothrow
3036 @property SysTime timeCreated() const pure nothrow
3038 return cast(SysTime)_timeCreated;
3041 @property SysTime timeLastAccessed() const pure nothrow
3043 return cast(SysTime)_timeLastAccessed;
3046 @property SysTime timeLastModified() const pure nothrow
3048 return cast(SysTime)_timeLastModified;
3051 @property uint attributes() const pure nothrow
3056 @property uint linkAttributes() const pure nothrow
3062 string _name; /// The file or directory represented by this DirEntry.
3064 SysTime _timeCreated; /// The time when the file was created.
3065 SysTime _timeLastAccessed; /// The time when the file was last accessed.
3066 SysTime _timeLastModified; /// The time when the file was last modified.
3068 ulong _size; /// The size of the file in bytes.
3069 uint _attributes; /// The file attributes from WIN32_FIND_DATAW.
3072 else version (Posix)
3082 throw new FileException(path, "File does not exist");
3091 private this(string path, core.sys.posix.dirent.dirent* fd)
3093 import std.path : buildPath;
3095 immutable len = core.stdc.string.strlen(fd.d_name.ptr);
3096 _name = buildPath(path, fd.d_name[0 .. len]);
3101 //fd_d_type doesn't work for all file systems,
3102 //in which case the result is DT_UNKOWN. But we
3103 //can determine the correct type from lstat, so
3104 //we'll only set the dtype here if we could
3105 //correctly determine it (not lstat in the case
3106 //of DT_UNKNOWN in case we don't ever actually
3107 //need the dtype, thus potentially avoiding the
3108 //cost of calling lstat).
3109 static if (__traits(compiles, fd.d_type != DT_UNKNOWN))
3111 if (fd.d_type != DT_UNKNOWN)
3121 // e.g. Solaris does not have the d_type member
3126 @property string name() const pure nothrow
3131 @property bool isDir()
3133 _ensureStatOrLStatDone();
3135 return (_statBuf.st_mode & S_IFMT) == S_IFDIR;
3138 @property bool isFile()
3140 _ensureStatOrLStatDone();
3142 return (_statBuf.st_mode & S_IFMT) == S_IFREG;
3145 @property bool isSymlink()
3149 return (_lstatMode & S_IFMT) == S_IFLNK;
3152 @property ulong size()
3155 return _statBuf.st_size;
3158 @property SysTime timeStatusChanged()
3162 return statTimeToStdTime!'c'(_statBuf);
3165 @property SysTime timeLastAccessed()
3169 return statTimeToStdTime!'a'(_statBuf);
3172 @property SysTime timeLastModified()
3176 return statTimeToStdTime!'m'(_statBuf);
3179 @property uint attributes()
3183 return _statBuf.st_mode;
3186 @property uint linkAttributes()
3193 @property stat_t statBuf()
3202 This is to support lazy evaluation, because doing stat's is
3203 expensive and not always needed.
3205 void _ensureStatDone() @safe
3207 import std.exception : enforce;
3209 static auto trustedStat(in char[] path, stat_t* buf) @trusted
3211 return stat(path.tempCString(), buf);
3216 enforce(trustedStat(_name, &_statBuf) == 0,
3217 "Failed to stat file `" ~ _name ~ "'");
3223 This is to support lazy evaluation, because doing stat's is
3224 expensive and not always needed.
3226 Try both stat and lstat for isFile and isDir
3227 to detect broken symlinks.
3229 void _ensureStatOrLStatDone()
3234 if ( stat(_name.tempCString(), &_statBuf) != 0 )
3238 _statBuf = stat_t.init;
3239 _statBuf.st_mode = S_IFLNK;
3248 This is to support lazy evaluation, because doing stat's is
3249 expensive and not always needed.
3251 void _ensureLStatDone()
3253 import std.exception : enforce;
3258 stat_t statbuf = void;
3260 enforce(lstat(_name.tempCString(), &statbuf) == 0,
3261 "Failed to stat file `" ~ _name ~ "'");
3263 _lstatMode = statbuf.st_mode;
3269 string _name; /// The file or directory represented by this DirEntry.
3271 stat_t _statBuf = void; /// The result of stat().
3272 uint _lstatMode; /// The stat mode from lstat().
3273 ubyte _dType; /// The type of the file.
3275 bool _didLStat = false; /// Whether lstat() has been called for this DirEntry.
3276 bool _didStat = false; /// Whether stat() has been called for this DirEntry.
3277 bool _dTypeSet = false; /// Whether the dType of the file has been set.
3285 if ("C:\\Program Files\\".exists)
3287 auto de = DirEntry("C:\\Program Files\\");
3290 assert(!de.isSymlink);
3293 if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
3295 auto de = DirEntry("C:\\Documents and Settings\\");
3296 assert(de.isSymlink);
3299 if ("C:\\Windows\\system.ini".exists)
3301 auto de = DirEntry("C:\\Windows\\system.ini");
3304 assert(!de.isSymlink);
3307 else version (Posix)
3309 import std.exception : assertThrown;
3311 if (system_directory.exists)
3314 auto de = DirEntry(system_directory);
3317 assert(!de.isSymlink);
3320 immutable symfile = deleteme ~ "_slink\0";
3321 scope(exit) if (symfile.exists) symfile.remove();
3323 core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
3326 auto de = DirEntry(symfile);
3329 assert(de.isSymlink);
3333 core.sys.posix.unistd.symlink((deleteme ~ "_broken_symlink\0").ptr, symfile.ptr);
3337 DirEntry de = DirEntry(symfile);
3341 assert(de.isSymlink);
3342 assertThrown(de.size);
3343 assertThrown(de.timeStatusChanged);
3344 assertThrown(de.timeLastAccessed);
3345 assertThrown(de.timeLastModified);
3346 assertThrown(de.attributes);
3347 assertThrown(de.statBuf);
3348 assert(symfile.exists);
3353 if (system_file.exists)
3355 auto de = DirEntry(system_file);
3358 assert(!de.isSymlink);
3363 alias PreserveAttributes = Flag!"preserveAttributes";
3367 /// Defaults to $(D Yes.preserveAttributes) on Windows, and the opposite on all other platforms.
3368 PreserveAttributes preserveAttributesDefault;
3370 else version (Windows)
3372 enum preserveAttributesDefault = Yes.preserveAttributes;
3376 enum preserveAttributesDefault = No.preserveAttributes;
3379 /***************************************************
3380 Copy file $(D from) _to file $(D to). File timestamps are preserved.
3381 File attributes are preserved, if $(D preserve) equals $(D Yes.preserveAttributes).
3382 On Windows only $(D Yes.preserveAttributes) (the default on Windows) is supported.
3383 If the target file exists, it is overwritten.
3386 from = string or range of characters representing the existing file name
3387 to = string or range of characters representing the target file name
3388 preserve = whether to _preserve the file attributes
3390 Throws: $(D FileException) on error.
3392 void copy(RF, RT)(RF from, RT to, PreserveAttributes preserve = preserveAttributesDefault)
3393 if (isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) && !isConvertibleToString!RF &&
3394 isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) && !isConvertibleToString!RT)
3396 // Place outside of @trusted block
3397 auto fromz = from.tempCString!FSChar();
3398 auto toz = to.tempCString!FSChar();
3400 static if (isNarrowString!RF && is(Unqual!(ElementEncodingType!RF) == char))
3403 enum string f = null;
3405 static if (isNarrowString!RT && is(Unqual!(ElementEncodingType!RT) == char))
3408 enum string t = null;
3410 copyImpl(f, t, fromz, toz, preserve);
3414 void copy(RF, RT)(auto ref RF from, auto ref RT to, PreserveAttributes preserve = preserveAttributesDefault)
3415 if (isConvertibleToString!RF || isConvertibleToString!RT)
3417 import std.meta : staticMap;
3418 alias Types = staticMap!(convertToString, RF, RT);
3419 copy!Types(from, to, preserve);
3422 @safe unittest // issue 15319
3424 assert(__traits(compiles, copy("from.txt", "to.txt")));
3427 private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, const(FSChar)* toz,
3428 PreserveAttributes preserve) @trusted
3432 assert(preserve == Yes.preserveAttributes);
3433 immutable result = CopyFileW(fromz, toz, false);
3436 import core.stdc.wchar_ : wcslen;
3437 import std.conv : to;
3440 t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
3442 throw new FileException(t);
3445 else version (Posix)
3447 static import core.stdc.stdio;
3448 import std.conv : to, octal;
3450 immutable fdr = core.sys.posix.fcntl.open(fromz, O_RDONLY);
3451 cenforce(fdr != -1, f, fromz);
3452 scope(exit) core.sys.posix.unistd.close(fdr);
3454 stat_t statbufr = void;
3455 cenforce(fstat(fdr, &statbufr) == 0, f, fromz);
3456 //cenforce(core.sys.posix.sys.stat.fstat(fdr, &statbufr) == 0, f, fromz);
3458 immutable fdw = core.sys.posix.fcntl.open(toz,
3459 O_CREAT | O_WRONLY, octal!666);
3460 cenforce(fdw != -1, t, toz);
3462 scope(failure) core.sys.posix.unistd.close(fdw);
3464 stat_t statbufw = void;
3465 cenforce(fstat(fdw, &statbufw) == 0, t, toz);
3466 if (statbufr.st_dev == statbufw.st_dev && statbufr.st_ino == statbufw.st_ino)
3467 throw new FileException(t, "Source and destination are the same file");
3470 scope(failure) core.stdc.stdio.remove(toz);
3472 scope(failure) core.sys.posix.unistd.close(fdw);
3473 cenforce(ftruncate(fdw, 0) == 0, t, toz);
3475 auto BUFSIZ = 4096u * 16;
3476 auto buf = core.stdc.stdlib.malloc(BUFSIZ);
3480 buf = core.stdc.stdlib.malloc(BUFSIZ);
3483 import core.exception : onOutOfMemoryError;
3484 onOutOfMemoryError();
3487 scope(exit) core.stdc.stdlib.free(buf);
3489 for (auto size = statbufr.st_size; size; )
3491 immutable toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size;
3493 core.sys.posix.unistd.read(fdr, buf, toxfer) == toxfer
3494 && core.sys.posix.unistd.write(fdw, buf, toxfer) == toxfer,
3496 assert(size >= toxfer);
3500 cenforce(fchmod(fdw, to!mode_t(statbufr.st_mode)) == 0, f, fromz);
3503 cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz);
3505 utimbuf utim = void;
3506 utim.actime = cast(time_t) statbufr.st_atime;
3507 utim.modtime = cast(time_t) statbufr.st_mtime;
3509 cenforce(utime(toz, &utim) != -1, f, fromz);
3515 import std.algorithm, std.file; // issue 14817
3516 auto t1 = deleteme, t2 = deleteme~"2";
3517 scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
3520 assert(readText(t2) == "11");
3523 assert(readText(t2) == "2");
3525 import std.utf : byChar;
3526 copy(t1.byChar, t2.byChar);
3527 assert(readText(t2.byChar) == "2");
3530 @safe version (Posix) @safe unittest //issue 11434
3532 import std.conv : octal;
3533 auto t1 = deleteme, t2 = deleteme~"2";
3534 scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
3536 setAttributes(t1, octal!767);
3537 copy(t1, t2, Yes.preserveAttributes);
3538 assert(readText(t2) == "1");
3539 assert(getAttributes(t2) == octal!100767);
3542 @safe unittest // issue 15865
3544 import std.exception : assertThrown;
3547 scope(exit) t.remove();
3548 assertThrown!FileException(copy(t, t));
3549 assert(readText(t) == "a");
3553 Remove directory and all of its content and subdirectories,
3557 $(D FileException) if there is an error (including if the given
3558 file is not a directory).
3560 void rmdirRecurse(in char[] pathname)
3562 //No references to pathname will be kept after rmdirRecurse,
3563 //so the cast is safe
3564 rmdirRecurse(DirEntry(cast(string) pathname));
3568 Remove directory and all of its content and subdirectories,
3572 $(D FileException) if there is an error (including if the given
3573 file is not a directory).
3575 void rmdirRecurse(ref DirEntry de)
3578 throw new FileException(de.name, "Not a directory");
3589 // all children, recursively depth-first
3590 foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false))
3592 attrIsDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name);
3600 //Note, without this overload, passing an RValue DirEntry still works, but
3601 //actually fully reconstructs a DirEntry inside the
3602 //"rmdirRecurse(in char[] pathname)" implementation. That is needlessly
3604 //A DirEntry is a bit big (72B), so keeping the "by ref" signature is desirable.
3605 void rmdirRecurse(DirEntry de)
3610 version (Windows) @system unittest
3612 import std.exception : enforce;
3613 auto d = deleteme ~ r".dir\a\b\c\d\e\f\g";
3615 rmdirRecurse(deleteme ~ ".dir");
3616 enforce(!exists(deleteme ~ ".dir"));
3619 version (Posix) @system unittest
3621 import std.exception : enforce, collectException;
3622 import std.process : executeShell;
3623 collectException(rmdirRecurse(deleteme));
3624 auto d = deleteme~"/a/b/c/d/e/f/g";
3625 enforce(collectException(mkdir(d)));
3627 core.sys.posix.unistd.symlink((deleteme~"/a/b/c\0").ptr,
3628 (deleteme~"/link\0").ptr);
3629 rmdirRecurse(deleteme~"/link");
3631 rmdirRecurse(deleteme);
3632 enforce(!exists(deleteme));
3634 d = deleteme~"/a/b/c/d/e/f/g";
3636 version (Android) string link_cmd = "ln -s ";
3637 else string link_cmd = "ln -sf ";
3638 executeShell(link_cmd~deleteme~"/a/b/c "~deleteme~"/link");
3639 rmdirRecurse(deleteme);
3640 enforce(!exists(deleteme));
3648 (cast(byte[]) buf)[] = 3;
3649 string unit_file = deleteme ~ "-unittest_write.tmp";
3650 if (exists(unit_file)) remove(unit_file);
3651 write(unit_file, buf);
3652 void[] buf2 = read(unit_file);
3653 assert(buf == buf2);
3655 string unit2_file = deleteme ~ "-unittest_write2.tmp";
3656 copy(unit_file, unit2_file);
3657 buf2 = read(unit2_file);
3658 assert(buf == buf2);
3661 assert(!exists(unit_file));
3663 assert(!exists(unit2_file));
3667 * Dictates directory spanning policy for $(D_PARAM dirEntries) (see below).
3671 /** Only spans one directory. */
3673 /** Spans the directory in
3674 $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Post-order,
3675 _depth-first $(B post)-order), i.e. the content of any
3676 subdirectory is spanned before that subdirectory itself. Useful
3677 e.g. when recursively deleting files. */
3679 /** Spans the directory in
3680 $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Pre-order, depth-first
3681 $(B pre)-order), i.e. the content of any subdirectory is spanned
3682 right after that subdirectory itself.
3684 Note that $(D SpanMode.breadth) will not result in all directory
3685 members occurring before any subdirectory members, i.e. it is not
3687 $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search,
3688 _breadth-first traversal).
3693 private struct DirIteratorImpl
3695 import std.array : Appender, appender;
3697 // Whether we should follow symlinked directories while iterating.
3698 // It also indicates whether we should avoid functions which call
3699 // stat (since we should only need lstat in this case and it would
3700 // be more efficient to not call stat in addition to lstat).
3701 bool _followSymlink;
3703 Appender!(DirHandle[]) _stack;
3704 Appender!(DirEntry[]) _stashed; //used in depth first mode
3706 void pushExtra(DirEntry de){ _stashed.put(de); }
3708 bool hasExtra(){ return !_stashed.data.empty; }
3713 de = _stashed.data[$-1];
3714 _stashed.shrinkTo(_stashed.data.length - 1);
3726 bool stepIn(string directory)
3728 import std.path : chainPath;
3730 auto search_pattern = chainPath(directory, "*.*");
3731 WIN32_FIND_DATAW findinfo;
3732 HANDLE h = FindFirstFileW(search_pattern.tempCString!FSChar(), &findinfo);
3733 cenforce(h != INVALID_HANDLE_VALUE, directory);
3734 _stack.put(DirHandle(directory, h));
3735 return toNext(false, &findinfo);
3740 if (_stack.data.empty)
3742 WIN32_FIND_DATAW findinfo;
3743 return toNext(true, &findinfo);
3746 bool toNext(bool fetch, WIN32_FIND_DATAW* findinfo)
3748 import core.stdc.wchar_ : wcscmp;
3752 if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE)
3758 while ( wcscmp(findinfo.cFileName.ptr, ".") == 0
3759 || wcscmp(findinfo.cFileName.ptr, "..") == 0)
3760 if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE)
3765 _cur = DirEntry(_stack.data[$-1].dirpath, findinfo);
3771 assert(!_stack.data.empty);
3772 FindClose(_stack.data[$-1].h);
3773 _stack.shrinkTo(_stack.data.length-1);
3776 void releaseDirStack()
3778 foreach ( d; _stack.data)
3784 return _followSymlink ? _cur.isDir : _cur.isDir && !_cur.isSymlink;
3787 else version (Posix)
3795 bool stepIn(string directory)
3797 auto h = directory.length ? opendir(directory.tempCString()) : opendir(".");
3798 cenforce(h, directory);
3799 _stack.put(DirHandle(directory, h));
3805 if (_stack.data.empty)
3807 for (dirent* fdata; (fdata = readdir(_stack.data[$-1].h)) != null; )
3809 // Skip "." and ".."
3810 if (core.stdc.string.strcmp(fdata.d_name.ptr, ".") &&
3811 core.stdc.string.strcmp(fdata.d_name.ptr, "..") )
3813 _cur = DirEntry(_stack.data[$-1].dirpath, fdata);
3823 assert(!_stack.data.empty);
3824 closedir(_stack.data[$-1].h);
3825 _stack.shrinkTo(_stack.data.length-1);
3828 void releaseDirStack()
3830 foreach ( d; _stack.data)
3836 return _followSymlink ? _cur.isDir : attrIsDir(_cur.linkAttributes);
3840 this(R)(R pathname, SpanMode mode, bool followSymlink)
3841 if (isInputRange!R && isSomeChar!(ElementEncodingType!R))
3844 _followSymlink = followSymlink;
3845 _stack = appender(cast(DirHandle[])[]);
3846 if (_mode == SpanMode.depth)
3847 _stashed = appender(cast(DirEntry[])[]);
3849 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
3850 alias pathnameStr = pathname;
3853 import std.array : array;
3854 string pathnameStr = pathname.array;
3856 if (stepIn(pathnameStr))
3858 if (_mode == SpanMode.depth)
3861 auto thisDir = _cur;
3862 if (stepIn(_cur.name))
3871 @property bool empty(){ return _stashed.data.empty && _stack.data.empty; }
3872 @property DirEntry front(){ return _cur; }
3877 case SpanMode.depth:
3882 auto thisDir = _cur;
3883 if (stepIn(_cur.name))
3891 else if (hasExtra())
3894 case SpanMode.breadth:
3897 if (!stepIn(_cur.name))
3898 while (!empty && !next()){}
3901 while (!empty && !next()){}
3917 RefCounted!(DirIteratorImpl, RefCountedAutoInitialize.no) impl;
3918 this(string pathname, SpanMode mode, bool followSymlink)
3920 impl = typeof(impl)(pathname, mode, followSymlink);
3923 @property bool empty(){ return impl.empty; }
3924 @property DirEntry front(){ return impl.front; }
3925 void popFront(){ impl.popFront(); }
3929 Returns an input range of $(D DirEntry) that lazily iterates a given directory,
3930 also provides two ways of foreach iteration. The iteration variable can be of
3931 type $(D string) if only the name is needed, or $(D DirEntry)
3932 if additional details are needed. The span _mode dictates how the
3933 directory is traversed. The name of each iterated directory entry
3934 contains the absolute _path.
3937 path = The directory to iterate over.
3938 If empty, the current directory will be iterated.
3940 pattern = Optional string with wildcards, such as $(RED
3941 "*.d"). When present, it is used to filter the
3942 results by their file name. The supported wildcard
3943 strings are described under $(REF globMatch,
3946 mode = Whether the directory's sub-directories should be
3947 iterated in depth-first port-order ($(LREF depth)),
3948 depth-first pre-order ($(LREF breadth)), or not at all
3951 followSymlink = Whether symbolic links which point to directories
3952 should be treated as directories and their contents
3956 $(D FileException) if the directory does not exist.
3959 --------------------
3960 // Iterate a directory in depth
3961 foreach (string name; dirEntries("destroy/me", SpanMode.depth))
3966 // Iterate the current directory in breadth
3967 foreach (string name; dirEntries("", SpanMode.breadth))
3972 // Iterate a directory and get detailed info about it
3973 foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth))
3975 writeln(e.name, "\t", e.size);
3978 // Iterate over all *.d files in current directory and all its subdirectories
3979 auto dFiles = dirEntries("", SpanMode.depth).filter!(f => f.name.endsWith(".d"));
3983 // Hook it up with std.parallelism to compile them all in parallel:
3984 foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread
3986 string cmd = "dmd -c " ~ d.name;
3988 std.process.system(cmd);
3991 // Iterate over all D source files in current directory and all its
3993 auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
3996 --------------------
3998 auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
4000 return DirIterator(path, mode, followSymlink);
4003 /// Duplicate functionality of D1's $(D std.file.listdir()):
4006 string[] listdir(string pathname)
4008 import std.algorithm;
4013 return std.file.dirEntries(pathname, SpanMode.shallow)
4014 .filter!(a => a.isFile)
4015 .map!(a => std.path.baseName(a.name))
4019 void main(string[] args)
4023 string[] files = listdir(args[1]);
4024 writefln("%s", files);
4030 import std.algorithm.comparison : equal;
4031 import std.algorithm.iteration : map;
4032 import std.algorithm.searching : startsWith;
4033 import std.array : array;
4034 import std.conv : to;
4035 import std.path : dirEntries, buildPath, absolutePath;
4036 import std.process : thisProcessID;
4037 import std.range.primitives : walkLength;
4040 string testdir = deleteme; // This has to be an absolute path when
4041 // called from a shared library on Android,
4044 string testdir = "deleteme.dmd.unittest.std.file" ~ to!string(thisProcessID); // needs to be relative
4045 mkdirRecurse(buildPath(testdir, "somedir"));
4046 scope(exit) rmdirRecurse(testdir);
4047 write(buildPath(testdir, "somefile"), null);
4048 write(buildPath(testdir, "somedir", "somedeepfile"), null);
4050 // testing range interface
4051 size_t equalEntries(string relpath, SpanMode mode)
4053 import std.exception : enforce;
4054 auto len = enforce(walkLength(dirEntries(absolutePath(relpath), mode)));
4055 assert(walkLength(dirEntries(relpath, mode)) == len);
4057 map!(a => absolutePath(a.name))(dirEntries(relpath, mode)),
4058 map!(a => a.name)(dirEntries(absolutePath(relpath), mode))));
4062 assert(equalEntries(testdir, SpanMode.shallow) == 2);
4063 assert(equalEntries(testdir, SpanMode.depth) == 3);
4064 assert(equalEntries(testdir, SpanMode.breadth) == 3);
4067 foreach (string name; dirEntries(testdir, SpanMode.breadth))
4070 assert(name.startsWith(testdir));
4072 foreach (DirEntry e; dirEntries(absolutePath(testdir), SpanMode.breadth))
4075 assert(e.isFile || e.isDir, e.name);
4079 foreach (string name; dirEntries(testdir, "*.d", SpanMode.breadth))
4083 foreach (entry; dirEntries(testdir, SpanMode.breadth))
4085 static assert(is(typeof(entry) == DirEntry));
4088 auto a = array(dirEntries(testdir, SpanMode.shallow));
4091 auto dFiles = dirEntries(testdir, SpanMode.shallow);
4092 foreach (d; dFiles){}
4095 dirEntries("", SpanMode.shallow).walkLength();
4099 auto dirEntries(string path, string pattern, SpanMode mode,
4100 bool followSymlink = true)
4102 import std.algorithm.iteration : filter;
4103 import std.path : globMatch, baseName;
4105 bool f(DirEntry de) { return globMatch(baseName(de.name), pattern); }
4106 return filter!f(DirIterator(path, mode, followSymlink));
4111 import std.stdio : writefln;
4112 immutable dpath = deleteme ~ "_dir";
4113 immutable fpath = deleteme ~ "_file";
4114 immutable sdpath = deleteme ~ "_sdir";
4115 immutable sfpath = deleteme ~ "_sfile";
4118 if (dpath.exists) rmdirRecurse(dpath);
4119 if (fpath.exists) remove(fpath);
4120 if (sdpath.exists) remove(sdpath);
4121 if (sfpath.exists) remove(sfpath);
4125 write(fpath, "hello world");
4128 core.sys.posix.unistd.symlink((dpath ~ '\0').ptr, (sdpath ~ '\0').ptr);
4129 core.sys.posix.unistd.symlink((fpath ~ '\0').ptr, (sfpath ~ '\0').ptr);
4132 static struct Flags { bool dir, file, link; }
4133 auto tests = [dpath : Flags(true), fpath : Flags(false, true)];
4136 tests[sdpath] = Flags(true, false, true);
4137 tests[sfpath] = Flags(false, true, true);
4140 auto past = Clock.currTime() - 2.seconds;
4141 auto future = past + 4.seconds;
4143 foreach (path, flags; tests)
4145 auto de = DirEntry(path);
4146 assert(de.name == path);
4147 assert(de.isDir == flags.dir);
4148 assert(de.isFile == flags.file);
4149 assert(de.isSymlink == flags.link);
4151 assert(de.isDir == path.isDir);
4152 assert(de.isFile == path.isFile);
4153 assert(de.isSymlink == path.isSymlink);
4154 assert(de.size == path.getSize());
4155 assert(de.attributes == getAttributes(path));
4156 assert(de.linkAttributes == getLinkAttributes(path));
4158 scope(failure) writefln("[%s] [%s] [%s] [%s]", past, de.timeLastAccessed, de.timeLastModified, future);
4159 assert(de.timeLastAccessed > past);
4160 assert(de.timeLastAccessed < future);
4161 assert(de.timeLastModified > past);
4162 assert(de.timeLastModified < future);
4164 assert(attrIsDir(de.attributes) == flags.dir);
4165 assert(attrIsDir(de.linkAttributes) == (flags.dir && !flags.link));
4166 assert(attrIsFile(de.attributes) == flags.file);
4167 assert(attrIsFile(de.linkAttributes) == (flags.file && !flags.link));
4168 assert(!attrIsSymlink(de.attributes));
4169 assert(attrIsSymlink(de.linkAttributes) == flags.link);
4173 assert(de.timeCreated > past);
4174 assert(de.timeCreated < future);
4176 else version (Posix)
4178 assert(de.timeStatusChanged > past);
4179 assert(de.timeStatusChanged < future);
4180 assert(de.attributes == de.statBuf.st_mode);
4187 * Reads a file line by line and parses the line into a single value or a
4188 * $(REF Tuple, std,typecons) of values depending on the length of `Types`.
4189 * The lines are parsed using the specified format string. The format string is
4190 * passed to $(REF formattedRead, std,_format), and therefore must conform to the
4191 * _format string specification outlined in $(MREF std, _format).
4194 * Types = the types that each of the elements in the line should be returned as
4195 * filename = the name of the file to read
4196 * format = the _format string to use when reading
4199 * If only one type is passed, then an array of that type. Otherwise, an
4200 * array of $(REF Tuple, std,typecons)s.
4203 * `Exception` if the format string is malformed. Also, throws `Exception`
4204 * if any of the lines in the file are not fully consumed by the call
4205 * to $(REF formattedRead, std,_format). Meaning that no empty lines or lines
4206 * with extra characters are allowed.
4208 Select!(Types.length == 1, Types[0][], Tuple!(Types)[])
4209 slurp(Types...)(string filename, in char[] format)
4211 import std.array : appender;
4212 import std.conv : text;
4213 import std.exception : enforce;
4214 import std.format : formattedRead;
4215 import std.stdio : File;
4217 auto app = appender!(typeof(return))();
4218 ElementType!(typeof(return)) toAdd;
4219 auto f = File(filename);
4220 scope(exit) f.close();
4221 foreach (line; f.byLine())
4223 formattedRead(line, format, &toAdd);
4225 text("Trailing characters at the end of line: `", line,
4235 import std.typecons : tuple;
4239 assert(exists(deleteme));
4243 write(deleteme, "12 12.25\n345 1.125"); // deleteme is the name of a temporary file
4245 // Load file; each line is an int followed by comma, whitespace and a
4247 auto a = slurp!(int, double)(deleteme, "%s %s");
4248 assert(a.length == 2);
4249 assert(a[0] == tuple(12, 12.25));
4250 assert(a[1] == tuple(345, 1.125));
4255 Returns the path to a directory for temporary files.
4257 On Windows, this function returns the result of calling the Windows API function
4258 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992.aspx, $(D GetTempPath)).
4260 On POSIX platforms, it searches through the following list of directories
4261 and returns the first one which is found to exist:
4263 $(LI The directory given by the $(D TMPDIR) environment variable.)
4264 $(LI The directory given by the $(D TEMP) environment variable.)
4265 $(LI The directory given by the $(D TMP) environment variable.)
4271 On all platforms, $(D tempDir) returns $(D ".") on failure, representing
4272 the current working directory.
4274 The return value of the function is cached, so the procedures described
4275 above will only be performed the first time the function is called. All
4276 subsequent runs will return the same string, regardless of whether
4277 environment variables and directory structures have changed in the
4280 The POSIX $(D tempDir) algorithm is inspired by Python's
4281 $(LINK2 http://docs.python.org/library/tempfile.html#tempfile.tempdir, $(D tempfile.tempdir)).
4283 string tempDir() @trusted
4285 static string cache;
4290 import std.conv : to;
4291 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992(v=vs.85).aspx
4292 wchar[MAX_PATH + 2] buf;
4293 DWORD len = GetTempPathW(buf.length, buf.ptr);
4294 if (len) cache = buf[0 .. len].to!string;
4296 else version (Android)
4298 // Don't check for a global temporary directory as
4299 // Android doesn't have one.
4301 else version (Posix)
4303 import std.process : environment;
4304 // This function looks through the list of alternative directories
4305 // and returns the first one which exists and is a directory.
4306 static string findExistingDir(T...)(lazy T alternatives)
4308 foreach (dir; alternatives)
4309 if (!dir.empty && exists(dir)) return dir;
4313 cache = findExistingDir(environment.get("TMPDIR"),
4314 environment.get("TEMP"),
4315 environment.get("TMP"),
4320 else static assert(false, "Unsupported platform");
4322 if (cache is null) cache = getcwd();