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;)
12 $(TR $(TH Category) $(TH Functions))
13 $(TR $(TD General) $(TD
21 $(TR $(TD Directories) $(TD
40 $(TR $(TD Symlinks) $(TD
44 $(TR $(TD Attributes) $(TD
49 $(LREF getLinkAttributes)
53 $(TR $(TD Timestamp) $(TD
57 $(LREF timeLastModified)
58 $(LREF timeLastAccessed)
59 $(LREF timeStatusChanged)
64 $(LREF PreserveAttributes)
66 $(LREF getAvailableDiskSpace)
71 Copyright: Copyright The D Language Foundation 2007 - 2011.
72 See_Also: The $(HTTP ddili.org/ders/d.en/files.html, official tutorial) for an
73 introduction to working with files in D, module
74 $(MREF std, stdio) for opening files and manipulating them via handles,
75 and module $(MREF std, path) for manipulating path strings.
77 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
78 Authors: $(HTTP digitalmars.com, Walter Bright),
79 $(HTTP erdani.org, Andrei Alexandrescu),
80 $(HTTP jmdavisprog.com, Jonathan M Davis)
81 Source: $(PHOBOSSRC std/file.d)
85 import core.stdc.errno, core.stdc.stdlib, core.stdc.string;
86 import core.time : abs, dur, hnsecs, seconds;
88 import std.datetime.date : DateTime;
89 import std.datetime.systime : Clock, SysTime, unixTimeToStdTime;
90 import std.internal.cstring;
92 import std.range.primitives;
102 else version (WatchOS)
107 import core.sys.windows.winbase, core.sys.windows.winnt, std.windows.syserror;
111 import core.sys.posix.dirent, core.sys.posix.fcntl, core.sys.posix.sys.stat,
112 core.sys.posix.sys.time, core.sys.posix.unistd, core.sys.posix.utime;
115 static assert(false, "Module " ~ .stringof ~ " not implemented for this OS.");
117 // Character type used for operating system filesystem APIs
120 private alias FSChar = WCHAR; // WCHAR can be aliased to wchar or wchar_t
124 private alias FSChar = char;
129 // Purposefully not documented. Use at your own risk
130 @property string deleteme() @safe
132 import std.conv : text;
133 import std.path : buildPath;
134 import std.process : thisProcessID;
136 enum base = "deleteme.dmd.unittest.pid";
137 static string fileName;
140 fileName = text(buildPath(tempDir(), base), thisProcessID);
144 version (StdUnittest) private struct TestAliasedString
146 string get() @safe @nogc pure nothrow return scope { return _s; }
154 package enum system_directory = "/system/etc";
155 package enum system_file = "/system/etc/hosts";
159 package enum system_directory = "/usr/include";
160 package enum system_file = "/usr/include/assert.h";
165 Exception thrown for file I/O errors.
167 class FileException : Exception
169 import std.conv : text, to;
174 immutable uint errno;
176 private this(scope const(char)[] name, scope const(char)[] msg, string file, size_t line, uint errno) @safe pure
179 super(name.idup, file, line);
181 super(text(name, ": ", msg), file, line);
187 Constructor which takes an error message.
190 name = Name of file for which the error occurred.
191 msg = Message describing the error.
192 file = The file where the error occurred.
193 line = The _line where the error occurred.
195 this(scope const(char)[] name, scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__) @safe pure
197 this(name, msg, file, line, 0);
201 Constructor which takes the error number ($(LUCKY GetLastError)
202 in Windows, $(D_PARAM errno) in POSIX).
205 name = Name of file for which the error occurred.
206 errno = The error number.
207 file = The file where the error occurred.
208 Defaults to `__FILE__`.
209 line = The _line where the error occurred.
210 Defaults to `__LINE__`.
212 version (Windows) this(scope const(char)[] name,
213 uint errno = .GetLastError(),
214 string file = __FILE__,
215 size_t line = __LINE__) @safe
217 this(name, sysErrorString(errno), file, line, errno);
219 else version (Posix) this(scope const(char)[] name,
221 string file = __FILE__,
222 size_t line = __LINE__) @trusted
224 import std.exception : errnoString;
225 this(name, errnoString(errno), file, line, errno);
232 import std.exception : assertThrown;
234 assertThrown!FileException("non.existing.file.".readText);
237 private T cenforce(T)(T condition, lazy scope const(char)[] name, string file = __FILE__, size_t line = __LINE__)
243 throw new FileException(name, .GetLastError(), file, line);
247 throw new FileException(name, .errno, file, line);
253 private T cenforce(T)(T condition, scope const(char)[] name, scope const(FSChar)* namez,
254 string file = __FILE__, size_t line = __LINE__)
260 import core.stdc.wchar_ : wcslen;
261 import std.conv : to;
263 auto len = namez ? wcslen(namez) : 0;
264 name = to!string(namez[0 .. len]);
266 throw new FileException(name, .GetLastError(), file, line);
271 private T cenforce(T)(T condition, scope const(char)[] name, scope const(FSChar)* namez,
272 string file = __FILE__, size_t line = __LINE__)
278 import core.stdc.string : strlen;
280 auto len = namez ? strlen(namez) : 0;
281 name = namez[0 .. len].idup;
283 throw new FileException(name, .errno, file, line);
286 // https://issues.dlang.org/show_bug.cgi?id=17102
291 cenforce(false, null, null,
294 catch (FileException) {}
297 /* **********************************
298 * Basic File operations.
301 /********************************************
302 Read entire contents of file `name` and returns it as an untyped
303 array. If the file size is larger than `upTo`, only `upTo`
307 name = string or range of characters representing the file _name
308 upTo = if present, the maximum number of bytes to _read
310 Returns: Untyped array of bytes _read.
312 Throws: $(LREF FileException) on error.
315 void[] read(R)(R name, size_t upTo = size_t.max)
316 if (isInputRange!R && isSomeChar!(ElementEncodingType!R) && !isInfinite!R &&
317 !isConvertibleToString!R)
319 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
320 return readImpl(name, name.tempCString!FSChar(), upTo);
322 return readImpl(null, name.tempCString!FSChar(), upTo);
328 import std.utf : byChar;
331 assert(exists(deleteme));
335 std.file.write(deleteme, "1234"); // deleteme is the name of a temporary file
336 assert(read(deleteme, 2) == "12");
337 assert(read(deleteme.byChar) == "1234");
338 assert((cast(const(ubyte)[])read(deleteme)).length == 4);
342 void[] read(R)(auto ref R name, size_t upTo = size_t.max)
343 if (isConvertibleToString!R)
345 return read!(StringTypeOf!R)(name, upTo);
350 static assert(__traits(compiles, read(TestAliasedString(null))));
353 version (Posix) private void[] readImpl(scope const(char)[] name, scope const(FSChar)* namez,
354 size_t upTo = size_t.max) @trusted
356 import core.memory : GC;
357 import std.algorithm.comparison : min;
358 import std.conv : to;
359 import std.experimental.checkedint : checked;
361 // A few internal configuration parameters {
363 minInitialAlloc = 1024 * 4,
364 maxInitialAlloc = size_t.max / 2,
365 sizeIncrement = 1024 * 16,
366 maxSlackMemoryAllowed = 1024;
369 immutable fd = core.sys.posix.fcntl.open(namez,
370 core.sys.posix.fcntl.O_RDONLY);
371 cenforce(fd != -1, name);
372 scope(exit) core.sys.posix.unistd.close(fd);
374 stat_t statbuf = void;
375 cenforce(fstat(fd, &statbuf) == 0, name, namez);
377 immutable initialAlloc = min(upTo, to!size_t(statbuf.st_size
378 ? min(statbuf.st_size + 1, maxInitialAlloc)
380 void[] result = GC.malloc(initialAlloc, GC.BlkAttr.NO_SCAN)[0 .. initialAlloc];
381 scope(failure) GC.free(result.ptr);
383 auto size = checked(size_t(0));
387 immutable actual = core.sys.posix.unistd.read(fd, result.ptr + size.get,
388 (min(result.length, upTo) - size).get);
389 cenforce(actual != -1, name, namez);
390 if (actual == 0) break;
392 if (size >= upTo) break;
393 if (size < result.length) continue;
394 immutable newAlloc = size + sizeIncrement;
395 result = GC.realloc(result.ptr, newAlloc.get, GC.BlkAttr.NO_SCAN)[0 .. newAlloc.get];
398 return result.length - size >= maxSlackMemoryAllowed
399 ? GC.realloc(result.ptr, size.get, GC.BlkAttr.NO_SCAN)[0 .. size.get]
400 : result[0 .. size.get];
404 version (Windows) private void[] readImpl(scope const(char)[] name, scope const(FSChar)* namez,
405 size_t upTo = size_t.max) @trusted
407 import core.memory : GC;
408 import std.algorithm.comparison : min;
409 static trustedCreateFileW(scope const(wchar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
410 SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition,
411 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
413 return CreateFileW(namez, dwDesiredAccess, dwShareMode,
414 lpSecurityAttributes, dwCreationDisposition,
415 dwFlagsAndAttributes, hTemplateFile);
418 static trustedCloseHandle(HANDLE hObject)
420 return CloseHandle(hObject);
422 static trustedGetFileSize(HANDLE hFile, out ulong fileSize)
425 DWORD sizeLow = GetFileSize(hFile, &sizeHigh);
426 const bool result = sizeLow != INVALID_FILE_SIZE;
428 fileSize = makeUlong(sizeLow, sizeHigh);
431 static trustedReadFile(HANDLE hFile, void *lpBuffer, ulong nNumberOfBytesToRead)
433 // Read by chunks of size < 4GB (Windows API limit)
434 ulong totalNumRead = 0;
435 while (totalNumRead != nNumberOfBytesToRead)
437 const uint chunkSize = min(nNumberOfBytesToRead - totalNumRead, 0xffff_0000);
438 DWORD numRead = void;
439 const result = ReadFile(hFile, lpBuffer + totalNumRead, chunkSize, &numRead, null);
440 if (result == 0 || numRead != chunkSize)
442 totalNumRead += chunkSize;
448 AliasSeq!(GENERIC_READ,
449 FILE_SHARE_READ | FILE_SHARE_WRITE, (SECURITY_ATTRIBUTES*).init,
450 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
452 auto h = trustedCreateFileW(namez, defaults);
454 cenforce(h != INVALID_HANDLE_VALUE, name, namez);
455 scope(exit) cenforce(trustedCloseHandle(h), name, namez);
456 ulong fileSize = void;
457 cenforce(trustedGetFileSize(h, fileSize), name, namez);
458 size_t size = min(upTo, fileSize);
459 auto buf = () { return GC.malloc(size, GC.BlkAttr.NO_SCAN)[0 .. size]; } ();
463 () { GC.free(buf.ptr); } ();
467 cenforce(trustedReadFile(h, &buf[0], size), name, namez);
468 return buf[0 .. size];
471 version (linux) @safe unittest
473 // A file with "zero" length that doesn't have 0 length at all
474 auto s = std.file.readText("/proc/cpuinfo");
475 assert(s.length > 0);
476 //writefln("'%s'", s);
481 scope(exit) if (exists(deleteme)) remove(deleteme);
483 auto f = File(deleteme, "w");
484 f.write("abcd"); f.flush();
485 assert(read(deleteme) == "abcd");
489 Reads and validates (using $(REF validate, std, utf)) a text file. S can be
490 an array of any character type. However, no width or endian conversions are
491 performed. So, if the width or endianness of the characters in the given
492 file differ from the width or endianness of the element type of S, then
493 validation will fail.
496 S = the string type of the file
497 name = string or range of characters representing the file _name
499 Returns: Array of characters read.
501 Throws: $(LREF FileException) if there is an error reading the file,
502 $(REF UTFException, std, utf) on UTF decoding error.
504 S readText(S = string, R)(auto ref R name)
505 if (isSomeString!S && (isInputRange!R && !isInfinite!R && isSomeChar!(ElementType!R) || is(StringTypeOf!R)))
507 import std.algorithm.searching : startsWith;
508 import std.encoding : getBOM, BOM;
509 import std.exception : enforce;
510 import std.format : format;
511 import std.utf : UTFException, validate;
513 static if (is(StringTypeOf!R))
514 StringTypeOf!R filename = name;
516 auto filename = name;
518 static auto trustedCast(T)(void[] buf) @trusted { return cast(T) buf; }
519 auto data = trustedCast!(ubyte[])(read(filename));
521 immutable bomSeq = getBOM(data);
522 immutable bom = bomSeq.schema;
524 static if (is(immutable ElementEncodingType!S == immutable char))
526 with(BOM) switch (bom)
529 case utf16le: throw new UTFException("UTF-8 requested. BOM is for UTF-16");
531 case utf32le: throw new UTFException("UTF-8 requested. BOM is for UTF-32");
535 else static if (is(immutable ElementEncodingType!S == immutable wchar))
537 with(BOM) switch (bom)
539 case utf8: throw new UTFException("UTF-16 requested. BOM is for UTF-8");
545 throw new UTFException("BOM is for UTF-16 LE on Big Endian machine");
550 throw new UTFException("BOM is for UTF-16 BE on Little Endian machine");
555 case utf32le: throw new UTFException("UTF-8 requested. BOM is for UTF-32");
561 with(BOM) switch (bom)
563 case utf8: throw new UTFException("UTF-16 requested. BOM is for UTF-8");
565 case utf16le: throw new UTFException("UTF-8 requested. BOM is for UTF-16");
571 throw new UTFException("BOM is for UTF-32 LE on Big Endian machine");
576 throw new UTFException("BOM is for UTF-32 BE on Little Endian machine");
584 if (data.length % ElementEncodingType!S.sizeof != 0)
585 throw new UTFException(format!"The content of %s is not UTF-%s"(filename, ElementEncodingType!S.sizeof * 8));
587 auto result = trustedCast!S(data);
592 /// Read file with UTF-8 text.
595 write(deleteme, "abc"); // deleteme is the name of a temporary file
596 scope(exit) remove(deleteme);
597 string content = readText(deleteme);
598 assert(content == "abc");
601 // Read file with UTF-8 text but try to read it as UTF-16.
604 import std.exception : assertThrown;
605 import std.utf : UTFException;
607 write(deleteme, "abc");
608 scope(exit) remove(deleteme);
609 // Throws because the file is not valid UTF-16.
610 assertThrown!UTFException(readText!wstring(deleteme));
613 // Read file with UTF-16 text.
616 import std.algorithm.searching : skipOver;
618 write(deleteme, "\uFEFFabc"w); // With BOM
619 scope(exit) remove(deleteme);
620 auto content = readText!wstring(deleteme);
621 assert(content == "\uFEFFabc"w);
622 // Strips BOM if present.
623 content.skipOver('\uFEFF');
624 assert(content == "abc"w);
629 static assert(__traits(compiles, readText(TestAliasedString(null))));
634 import std.array : appender;
635 import std.bitmanip : append, Endian;
636 import std.exception : assertThrown;
637 import std.path : buildPath;
638 import std.string : representation;
639 import std.utf : UTFException;
642 scope(exit) rmdirRecurse(deleteme);
644 immutable none8 = buildPath(deleteme, "none8");
645 immutable none16 = buildPath(deleteme, "none16");
646 immutable utf8 = buildPath(deleteme, "utf8");
647 immutable utf16be = buildPath(deleteme, "utf16be");
648 immutable utf16le = buildPath(deleteme, "utf16le");
649 immutable utf32be = buildPath(deleteme, "utf32be");
650 immutable utf32le = buildPath(deleteme, "utf32le");
651 immutable utf7 = buildPath(deleteme, "utf7");
654 write(none16, "京都市"w);
655 write(utf8, (cast(char[])[0xEF, 0xBB, 0xBF]) ~ "京都市");
657 auto str = "\uFEFF京都市"w;
658 auto arr = appender!(ubyte[])();
661 write(utf16be, arr.data);
664 auto str = "\uFEFF京都市"w;
665 auto arr = appender!(ubyte[])();
667 arr.append!(ushort, Endian.littleEndian)(c);
668 write(utf16le, arr.data);
671 auto str = "\U0000FEFF京都市"d;
672 auto arr = appender!(ubyte[])();
675 write(utf32be, arr.data);
678 auto str = "\U0000FEFF京都市"d;
679 auto arr = appender!(ubyte[])();
681 arr.append!(uint, Endian.littleEndian)(c);
682 write(utf32le, arr.data);
684 write(utf7, (cast(ubyte[])[0x2B, 0x2F, 0x76, 0x38, 0x2D]) ~ "foobar".representation);
686 assertThrown!UTFException(readText(none16));
687 assert(readText(utf8) == (cast(char[])[0xEF, 0xBB, 0xBF]) ~ "京都市");
688 assertThrown!UTFException(readText(utf16be));
689 assertThrown!UTFException(readText(utf16le));
690 assertThrown!UTFException(readText(utf32be));
691 assertThrown!UTFException(readText(utf32le));
692 assert(readText(utf7) == (cast(char[])[0x2B, 0x2F, 0x76, 0x38, 0x2D]) ~ "foobar");
694 assertThrown!UTFException(readText!wstring(none8));
695 assert(readText!wstring(none16) == "京都市"w);
696 assertThrown!UTFException(readText!wstring(utf8));
699 assert(readText!wstring(utf16be) == "\uFEFF京都市"w);
700 assertThrown!UTFException(readText!wstring(utf16le));
704 assertThrown!UTFException(readText!wstring(utf16be));
705 assert(readText!wstring(utf16le) == "\uFEFF京都市"w);
707 assertThrown!UTFException(readText!wstring(utf32be));
708 assertThrown!UTFException(readText!wstring(utf32le));
709 assertThrown!UTFException(readText!wstring(utf7));
711 assertThrown!UTFException(readText!dstring(utf8));
712 assertThrown!UTFException(readText!dstring(utf16be));
713 assertThrown!UTFException(readText!dstring(utf16le));
716 assert(readText!dstring(utf32be) == "\U0000FEFF京都市"d);
717 assertThrown!UTFException(readText!dstring(utf32le));
721 assertThrown!UTFException(readText!dstring(utf32be));
722 assert(readText!dstring(utf32le) == "\U0000FEFF京都市"d);
724 assertThrown!UTFException(readText!dstring(utf7));
727 /*********************************************
728 Write `buffer` to file `name`.
730 Creates the file if it does not already exist.
733 name = string or range of characters representing the file _name
734 buffer = data to be written to file
736 Throws: $(LREF FileException) on error.
738 See_also: $(REF toFile, std,stdio)
740 void write(R)(R name, const void[] buffer)
741 if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
742 !isConvertibleToString!R)
744 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
745 writeImpl(name, name.tempCString!FSChar(), buffer, false);
747 writeImpl(null, name.tempCString!FSChar(), buffer, false);
755 assert(exists(deleteme));
759 int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
760 write(deleteme, a); // deleteme is the name of a temporary file
761 const bytes = read(deleteme);
762 const fileInts = () @trusted { return cast(int[]) bytes; }();
763 assert(fileInts == a);
767 void write(R)(auto ref R name, const void[] buffer)
768 if (isConvertibleToString!R)
770 write!(StringTypeOf!R)(name, buffer);
775 static assert(__traits(compiles, write(TestAliasedString(null), null)));
778 /*********************************************
779 Appends `buffer` to file `name`.
781 Creates the file if it does not already exist.
784 name = string or range of characters representing the file _name
785 buffer = data to be appended to file
787 Throws: $(LREF FileException) on error.
789 void append(R)(R name, const void[] buffer)
790 if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
791 !isConvertibleToString!R)
793 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
794 writeImpl(name, name.tempCString!FSChar(), buffer, true);
796 writeImpl(null, name.tempCString!FSChar(), buffer, true);
804 assert(exists(deleteme));
808 int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
809 write(deleteme, a); // deleteme is the name of a temporary file
810 int[] b = [ 13, 21 ];
812 const bytes = read(deleteme);
813 const fileInts = () @trusted { return cast(int[]) bytes; }();
814 assert(fileInts == a ~ b);
818 void append(R)(auto ref R name, const void[] buffer)
819 if (isConvertibleToString!R)
821 append!(StringTypeOf!R)(name, buffer);
826 static assert(__traits(compiles, append(TestAliasedString("foo"), [0, 1, 2, 3])));
829 // POSIX implementation helper for write and append
831 version (Posix) private void writeImpl(scope const(char)[] name, scope const(FSChar)* namez,
832 scope const(void)[] buffer, bool append) @trusted
834 import std.conv : octal;
837 auto mode = append ? O_CREAT | O_WRONLY | O_APPEND
838 : O_CREAT | O_WRONLY | O_TRUNC;
840 immutable fd = core.sys.posix.fcntl.open(namez, mode, octal!666);
841 cenforce(fd != -1, name, namez);
843 scope(failure) core.sys.posix.unistd.close(fd);
845 immutable size = buffer.length;
846 size_t sum, cnt = void;
849 cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30;
850 const numwritten = core.sys.posix.unistd.write(fd, buffer.ptr + sum, cnt);
851 if (numwritten != cnt)
855 cenforce(sum == size, name, namez);
857 cenforce(core.sys.posix.unistd.close(fd) == 0, name, namez);
860 // Windows implementation helper for write and append
862 version (Windows) private void writeImpl(scope const(char)[] name, scope const(FSChar)* namez,
863 scope const(void)[] buffer, bool append) @trusted
869 AliasSeq!(GENERIC_WRITE, 0, null, OPEN_ALWAYS,
870 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
873 h = CreateFileW(namez, defaults);
874 cenforce(h != INVALID_HANDLE_VALUE, name, namez);
875 cenforce(SetFilePointer(h, 0, null, FILE_END) != INVALID_SET_FILE_POINTER,
881 AliasSeq!(GENERIC_WRITE, 0, null, CREATE_ALWAYS,
882 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
885 h = CreateFileW(namez, defaults);
886 cenforce(h != INVALID_HANDLE_VALUE, name, namez);
888 immutable size = buffer.length;
889 size_t sum, cnt = void;
890 DWORD numwritten = void;
893 cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30;
894 WriteFile(h, buffer.ptr + sum, cast(uint) cnt, &numwritten, null);
895 if (numwritten != cnt)
899 cenforce(sum == size && CloseHandle(h), name, namez);
902 /***************************************************
903 * Rename file `from` _to `to`, moving it between directories if required.
904 * If the target file exists, it is overwritten.
906 * It is not possible to rename a file across different mount points
907 * or drives. On POSIX, the operation is atomic. That means, if `to`
908 * already exists there will be no time period during the operation
909 * where `to` is missing. See
910 * $(HTTP man7.org/linux/man-pages/man2/rename.2.html, manpage for rename)
914 * from = string or range of characters representing the existing file name
915 * to = string or range of characters representing the target file name
917 * Throws: $(LREF FileException) on error.
919 void rename(RF, RT)(RF from, RT to)
920 if ((isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) || isSomeString!RF)
921 && !isConvertibleToString!RF &&
922 (isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) || isSomeString!RT)
923 && !isConvertibleToString!RT)
925 // Place outside of @trusted block
926 auto fromz = from.tempCString!FSChar();
927 auto toz = to.tempCString!FSChar();
929 static if (isNarrowString!RF && is(immutable ElementEncodingType!RF == immutable char))
932 enum string f = null;
934 static if (isNarrowString!RT && is(immutable ElementEncodingType!RT == immutable char))
937 enum string t = null;
939 renameImpl(f, t, fromz, toz);
943 void rename(RF, RT)(auto ref RF from, auto ref RT to)
944 if (isConvertibleToString!RF || isConvertibleToString!RT)
946 import std.meta : staticMap;
947 alias Types = staticMap!(convertToString, RF, RT);
948 rename!Types(from, to);
953 static assert(__traits(compiles, rename(TestAliasedString(null), TestAliasedString(null))));
954 static assert(__traits(compiles, rename("", TestAliasedString(null))));
955 static assert(__traits(compiles, rename(TestAliasedString(null), "")));
956 import std.utf : byChar;
957 static assert(__traits(compiles, rename(TestAliasedString(null), "".byChar)));
963 auto t1 = deleteme, t2 = deleteme~"2";
964 scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
968 assert(t2.readText == "1");
972 assert(t2.readText == "2");
975 private void renameImpl(scope const(char)[] f, scope const(char)[] t,
976 scope const(FSChar)* fromz, scope const(FSChar)* toz) @trusted
980 import std.exception : enforce;
982 const result = MoveFileExW(fromz, toz, MOVEFILE_REPLACE_EXISTING);
985 import core.stdc.wchar_ : wcslen;
986 import std.conv : to, text;
989 f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]);
992 t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
996 text("Attempting to rename file ", f, " to ", t)));
1001 static import core.stdc.stdio;
1003 cenforce(core.stdc.stdio.rename(fromz, toz) == 0, t, toz);
1009 import std.utf : byWchar;
1011 auto t1 = deleteme, t2 = deleteme~"2";
1012 scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
1016 assert(readText(t2) == "1");
1019 rename(t1, t2.byWchar);
1020 assert(readText(t2) == "2");
1023 /***************************************************
1027 name = string or range of characters representing the file _name
1029 Throws: $(LREF FileException) on error.
1031 void remove(R)(R name)
1032 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1033 !isConvertibleToString!R)
1035 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1036 removeImpl(name, name.tempCString!FSChar());
1038 removeImpl(null, name.tempCString!FSChar());
1042 void remove(R)(auto ref R name)
1043 if (isConvertibleToString!R)
1045 remove!(StringTypeOf!R)(name);
1051 import std.exception : assertThrown;
1053 deleteme.write("Hello");
1054 assert(deleteme.readText == "Hello");
1057 assertThrown!FileException(deleteme.readText);
1062 static assert(__traits(compiles, remove(TestAliasedString("foo"))));
1065 private void removeImpl(scope const(char)[] name, scope const(FSChar)* namez) @trusted
1069 cenforce(DeleteFileW(namez), name, namez);
1071 else version (Posix)
1073 static import core.stdc.stdio;
1077 import core.stdc.string : strlen;
1078 auto len = strlen(namez);
1079 name = namez[0 .. len];
1081 cenforce(core.stdc.stdio.remove(namez) == 0,
1082 "Failed to remove file " ~ name);
1086 version (Windows) private WIN32_FILE_ATTRIBUTE_DATA getFileAttributesWin(R)(R name)
1087 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
1089 auto namez = name.tempCString!FSChar();
1091 WIN32_FILE_ATTRIBUTE_DATA fad = void;
1093 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1095 static void getFA(scope const(char)[] name, scope const(FSChar)* namez,
1096 out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
1098 import std.exception : enforce;
1099 enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad),
1100 new FileException(name.idup));
1102 getFA(name, namez, fad);
1106 static void getFA(scope const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
1108 import core.stdc.wchar_ : wcslen;
1109 import std.conv : to;
1110 import std.exception : enforce;
1112 enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad),
1113 new FileException(namez[0 .. wcslen(namez)].to!string));
1120 version (Windows) private ulong makeUlong(DWORD dwLow, DWORD dwHigh) @safe pure nothrow @nogc
1124 li.HighPart = dwHigh;
1129 Get size of file `name` in bytes.
1132 name = string or range of characters representing the file _name
1134 The size of file in bytes.
1136 $(LREF FileException) on error (e.g., file not found).
1138 ulong getSize(R)(R name)
1139 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1140 !isConvertibleToString!R)
1144 with (getFileAttributesWin(name))
1145 return makeUlong(nFileSizeLow, nFileSizeHigh);
1147 else version (Posix)
1149 auto namez = name.tempCString();
1151 static trustedStat(const(FSChar)* namez, out stat_t buf) @trusted
1153 return stat(namez, &buf);
1155 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1158 string names = null;
1159 stat_t statbuf = void;
1160 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1161 return statbuf.st_size;
1166 ulong getSize(R)(auto ref R name)
1167 if (isConvertibleToString!R)
1169 return getSize!(StringTypeOf!R)(name);
1174 static assert(__traits(compiles, getSize(TestAliasedString("foo"))));
1180 scope(exit) deleteme.remove;
1182 // create a file of size 1
1183 write(deleteme, "a");
1184 assert(getSize(deleteme) == 1);
1186 // create a file of size 3
1187 write(deleteme, "abc");
1188 assert(getSize(deleteme) == 3);
1193 // create a file of size 1
1194 write(deleteme, "a");
1195 scope(exit) deleteme.exists && deleteme.remove;
1196 assert(getSize(deleteme) == 1);
1197 // create a file of size 3
1198 write(deleteme, "abc");
1199 import std.utf : byChar;
1200 assert(getSize(deleteme.byChar) == 3);
1203 // Reads a time field from a stat_t with full precision.
1205 private SysTime statTimeToStdTime(char which)(ref const stat_t statbuf)
1207 auto unixTime = mixin(`statbuf.st_` ~ which ~ `time`);
1208 long stdTime = unixTimeToStdTime(unixTime);
1210 static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `tim`))))
1211 stdTime += mixin(`statbuf.st_` ~ which ~ `tim.tv_nsec`) / 100;
1213 static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `timensec`))))
1214 stdTime += mixin(`statbuf.st_` ~ which ~ `timensec`) / 100;
1216 static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `time_nsec`))))
1217 stdTime += mixin(`statbuf.st_` ~ which ~ `time_nsec`) / 100;
1219 static if (is(typeof(mixin(`statbuf.__st_` ~ which ~ `timensec`))))
1220 stdTime += mixin(`statbuf.__st_` ~ which ~ `timensec`) / 100;
1222 return SysTime(stdTime);
1226 Get the access and modified times of file or folder `name`.
1229 name = File/Folder _name to get times for.
1230 accessTime = Time the file/folder was last accessed.
1231 modificationTime = Time the file/folder was last modified.
1234 $(LREF FileException) on error.
1236 void getTimes(R)(R name,
1237 out SysTime accessTime,
1238 out SysTime modificationTime)
1239 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1240 !isConvertibleToString!R)
1244 import std.datetime.systime : FILETIMEToSysTime;
1246 with (getFileAttributesWin(name))
1248 accessTime = FILETIMEToSysTime(&ftLastAccessTime);
1249 modificationTime = FILETIMEToSysTime(&ftLastWriteTime);
1252 else version (Posix)
1254 auto namez = name.tempCString();
1256 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1258 return stat(namez, &buf);
1260 stat_t statbuf = void;
1262 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1265 string names = null;
1266 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1268 accessTime = statTimeToStdTime!'a'(statbuf);
1269 modificationTime = statTimeToStdTime!'m'(statbuf);
1274 void getTimes(R)(auto ref R name,
1275 out SysTime accessTime,
1276 out SysTime modificationTime)
1277 if (isConvertibleToString!R)
1279 return getTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
1285 import std.datetime : abs, SysTime;
1287 scope(exit) deleteme.remove;
1288 write(deleteme, "a");
1290 SysTime accessTime, modificationTime;
1292 getTimes(deleteme, accessTime, modificationTime);
1294 import std.datetime : Clock, seconds;
1295 auto currTime = Clock.currTime();
1296 enum leeway = 5.seconds;
1298 auto diffAccess = accessTime - currTime;
1299 auto diffModification = modificationTime - currTime;
1300 assert(abs(diffAccess) <= leeway);
1301 assert(abs(diffModification) <= leeway);
1306 SysTime atime, mtime;
1307 static assert(__traits(compiles, getTimes(TestAliasedString("foo"), atime, mtime)));
1312 import std.stdio : writefln;
1314 auto currTime = Clock.currTime();
1316 write(deleteme, "a");
1317 scope(exit) assert(deleteme.exists), deleteme.remove;
1319 SysTime accessTime1;
1320 SysTime modificationTime1;
1322 getTimes(deleteme, accessTime1, modificationTime1);
1324 enum leeway = 5.seconds;
1327 auto diffa = accessTime1 - currTime;
1328 auto diffm = modificationTime1 - currTime;
1329 scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime1, modificationTime1, currTime, diffa, diffm);
1331 assert(abs(diffa) <= leeway);
1332 assert(abs(diffm) <= leeway);
1335 version (fullFileTests)
1338 enum sleepTime = dur!"seconds"(2);
1339 Thread.sleep(sleepTime);
1341 currTime = Clock.currTime();
1342 write(deleteme, "b");
1344 SysTime accessTime2 = void;
1345 SysTime modificationTime2 = void;
1347 getTimes(deleteme, accessTime2, modificationTime2);
1350 auto diffa = accessTime2 - currTime;
1351 auto diffm = modificationTime2 - currTime;
1352 scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime2, modificationTime2, currTime, diffa, diffm);
1354 //There is no guarantee that the access time will be updated.
1355 assert(abs(diffa) <= leeway + sleepTime);
1356 assert(abs(diffm) <= leeway);
1359 assert(accessTime1 <= accessTime2);
1360 assert(modificationTime1 <= modificationTime2);
1368 $(BLUE This function is Windows-Only.)
1370 Get creation/access/modified times of file `name`.
1372 This is the same as `getTimes` except that it also gives you the file
1373 creation time - which isn't possible on POSIX systems.
1376 name = File _name to get times for.
1377 fileCreationTime = Time the file was created.
1378 fileAccessTime = Time the file was last accessed.
1379 fileModificationTime = Time the file was last modified.
1382 $(LREF FileException) on error.
1384 void getTimesWin(R)(R name,
1385 out SysTime fileCreationTime,
1386 out SysTime fileAccessTime,
1387 out SysTime fileModificationTime)
1388 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1389 !isConvertibleToString!R);
1391 else version (Windows)
1393 void getTimesWin(R)(R name,
1394 out SysTime fileCreationTime,
1395 out SysTime fileAccessTime,
1396 out SysTime fileModificationTime)
1397 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1398 !isConvertibleToString!R)
1400 import std.datetime.systime : FILETIMEToSysTime;
1402 with (getFileAttributesWin(name))
1404 fileCreationTime = FILETIMEToSysTime(&ftCreationTime);
1405 fileAccessTime = FILETIMEToSysTime(&ftLastAccessTime);
1406 fileModificationTime = FILETIMEToSysTime(&ftLastWriteTime);
1410 void getTimesWin(R)(auto ref R name,
1411 out SysTime fileCreationTime,
1412 out SysTime fileAccessTime,
1413 out SysTime fileModificationTime)
1414 if (isConvertibleToString!R)
1416 getTimesWin!(StringTypeOf!R)(name, fileCreationTime, fileAccessTime, fileModificationTime);
1420 version (Windows) @system unittest
1422 import std.stdio : writefln;
1423 auto currTime = Clock.currTime();
1425 write(deleteme, "a");
1426 scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1428 SysTime creationTime1 = void;
1429 SysTime accessTime1 = void;
1430 SysTime modificationTime1 = void;
1432 getTimesWin(deleteme, creationTime1, accessTime1, modificationTime1);
1434 enum leeway = dur!"seconds"(5);
1437 auto diffc = creationTime1 - currTime;
1438 auto diffa = accessTime1 - currTime;
1439 auto diffm = modificationTime1 - currTime;
1442 writefln("[%s] [%s] [%s] [%s] [%s] [%s] [%s]",
1443 creationTime1, accessTime1, modificationTime1, currTime, diffc, diffa, diffm);
1446 // Deleting and recreating a file doesn't seem to always reset the "file creation time"
1447 //assert(abs(diffc) <= leeway);
1448 assert(abs(diffa) <= leeway);
1449 assert(abs(diffm) <= leeway);
1452 version (fullFileTests)
1455 Thread.sleep(dur!"seconds"(2));
1457 currTime = Clock.currTime();
1458 write(deleteme, "b");
1460 SysTime creationTime2 = void;
1461 SysTime accessTime2 = void;
1462 SysTime modificationTime2 = void;
1464 getTimesWin(deleteme, creationTime2, accessTime2, modificationTime2);
1467 auto diffa = accessTime2 - currTime;
1468 auto diffm = modificationTime2 - currTime;
1471 writefln("[%s] [%s] [%s] [%s] [%s]",
1472 accessTime2, modificationTime2, currTime, diffa, diffm);
1475 assert(abs(diffa) <= leeway);
1476 assert(abs(diffm) <= leeway);
1479 assert(creationTime1 == creationTime2);
1480 assert(accessTime1 <= accessTime2);
1481 assert(modificationTime1 <= modificationTime2);
1485 SysTime ctime, atime, mtime;
1486 static assert(__traits(compiles, getTimesWin(TestAliasedString("foo"), ctime, atime, mtime)));
1493 import core.stdc.config : c_ulong;
1494 enum ATTR_CMN_MODTIME = 0x00000400, ATTR_CMN_ACCTIME = 0x00001000;
1495 alias attrgroup_t = uint;
1496 static struct attrlist
1498 ushort bitmapcount, reserved;
1499 attrgroup_t commonattr, volattr, dirattr, fileattr, forkattr;
1501 extern(C) int setattrlist(in char* path, scope ref attrlist attrs,
1502 scope void* attrbuf, size_t attrBufSize, c_ulong options) nothrow @nogc @system;
1506 Set access/modified times of file or folder `name`.
1509 name = File/Folder _name to get times for.
1510 accessTime = Time the file/folder was last accessed.
1511 modificationTime = Time the file/folder was last modified.
1514 $(LREF FileException) on error.
1516 void setTimes(R)(R name,
1518 SysTime modificationTime)
1519 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1520 !isConvertibleToString!R)
1522 auto namez = name.tempCString!FSChar();
1523 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1526 string names = null;
1527 setTimesImpl(names, namez, accessTime, modificationTime);
1533 import std.datetime : DateTime, hnsecs, SysTime;
1535 scope(exit) deleteme.remove;
1536 write(deleteme, "a");
1538 SysTime accessTime = SysTime(DateTime(2010, 10, 4, 0, 0, 30));
1539 SysTime modificationTime = SysTime(DateTime(2018, 10, 4, 0, 0, 30));
1540 setTimes(deleteme, accessTime, modificationTime);
1542 SysTime accessTimeResolved, modificationTimeResolved;
1543 getTimes(deleteme, accessTimeResolved, modificationTimeResolved);
1545 assert(accessTime == accessTimeResolved);
1546 assert(modificationTime == modificationTimeResolved);
1550 void setTimes(R)(auto ref R name,
1552 SysTime modificationTime)
1553 if (isConvertibleToString!R)
1555 setTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
1558 private void setTimesImpl(scope const(char)[] names, scope const(FSChar)* namez,
1559 SysTime accessTime, SysTime modificationTime) @trusted
1563 import std.datetime.systime : SysTimeToFILETIME;
1564 const ta = SysTimeToFILETIME(accessTime);
1565 const tm = SysTimeToFILETIME(modificationTime);
1567 AliasSeq!(GENERIC_WRITE,
1571 FILE_ATTRIBUTE_NORMAL |
1572 FILE_ATTRIBUTE_DIRECTORY |
1573 FILE_FLAG_BACKUP_SEMANTICS,
1575 auto h = CreateFileW(namez, defaults);
1577 cenforce(h != INVALID_HANDLE_VALUE, names, namez);
1580 cenforce(CloseHandle(h), names, namez);
1582 cenforce(SetFileTime(h, null, &ta, &tm), names, namez);
1586 static if (is(typeof(&utimensat)))
1588 timespec[2] t = void;
1589 t[0] = accessTime.toTimeSpec();
1590 t[1] = modificationTime.toTimeSpec();
1591 cenforce(utimensat(AT_FDCWD, namez, t, 0) == 0, names, namez);
1597 // Set modification & access times with setattrlist to avoid precision loss.
1598 attrlist attrs = { bitmapcount: 5, reserved: 0,
1599 commonattr: ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME,
1600 volattr: 0, dirattr: 0, fileattr: 0, forkattr: 0 };
1601 timespec[2] attrbuf = [modificationTime.toTimeSpec(), accessTime.toTimeSpec()];
1602 if (0 == setattrlist(namez, attrs, &attrbuf, attrbuf.sizeof, 0))
1604 if (.errno != ENOTSUP)
1605 cenforce(false, names, namez);
1606 // Not all volumes support setattrlist. In such cases
1607 // fall through to the utimes implementation.
1609 timeval[2] t = void;
1610 t[0] = accessTime.toTimeVal();
1611 t[1] = modificationTime.toTimeVal();
1612 cenforce(utimes(namez, t) == 0, names, namez);
1619 if (false) // Test instatiation
1620 setTimes(TestAliasedString("foo"), SysTime.init, SysTime.init);
1625 import std.stdio : File;
1626 string newdir = deleteme ~ r".dir";
1627 string dir = newdir ~ r"/a/b/c";
1628 string file = dir ~ "/file";
1630 if (!exists(dir)) mkdirRecurse(dir);
1631 { auto f = File(file, "w"); }
1633 void testTimes(int hnsecValue)
1635 foreach (path; [file, dir]) // test file and dir
1637 SysTime atime = SysTime(DateTime(2010, 10, 4, 0, 0, 30), hnsecs(hnsecValue));
1638 SysTime mtime = SysTime(DateTime(2011, 10, 4, 0, 0, 30), hnsecs(hnsecValue));
1639 setTimes(path, atime, mtime);
1643 getTimes(path, atime_res, mtime_res);
1644 assert(atime == atime_res);
1645 assert(mtime == mtime_res);
1651 testTimes(123_456_7);
1653 rmdirRecurse(newdir);
1657 Returns the time that the given file was last modified.
1660 name = the name of the file to check
1662 A $(REF SysTime,std,datetime,systime).
1664 $(LREF FileException) if the given file does not exist.
1666 SysTime timeLastModified(R)(R name)
1667 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1668 !isConvertibleToString!R)
1675 getTimesWin(name, dummy, dummy, ftm);
1679 else version (Posix)
1681 auto namez = name.tempCString!FSChar();
1682 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1684 return stat(namez, &buf);
1686 stat_t statbuf = void;
1688 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
1691 string names = null;
1692 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1694 return statTimeToStdTime!'m'(statbuf);
1699 SysTime timeLastModified(R)(auto ref R name)
1700 if (isConvertibleToString!R)
1702 return timeLastModified!(StringTypeOf!R)(name);
1708 import std.datetime : abs, DateTime, hnsecs, SysTime;
1709 scope(exit) deleteme.remove;
1711 import std.datetime : Clock, seconds;
1712 auto currTime = Clock.currTime();
1713 enum leeway = 5.seconds;
1714 deleteme.write("bb");
1715 assert(abs(deleteme.timeLastModified - currTime) <= leeway);
1720 static assert(__traits(compiles, timeLastModified(TestAliasedString("foo"))));
1724 Returns the time that the given file was last modified. If the
1725 file does not exist, returns `returnIfMissing`.
1727 A frequent usage pattern occurs in build automation tools such as
1728 $(HTTP gnu.org/software/make, make) or $(HTTP
1729 en.wikipedia.org/wiki/Apache_Ant, ant). To check whether file $(D
1730 target) must be rebuilt from file `source` (i.e., `target` is
1731 older than `source` or does not exist), use the comparison
1732 below. The code throws a $(LREF FileException) if `source` does not
1733 exist (as it should). On the other hand, the `SysTime.min` default
1734 makes a non-existing `target` seem infinitely old so the test
1735 correctly prompts building it.
1738 name = The name of the file to get the modification time for.
1739 returnIfMissing = The time to return if the given file does not exist.
1741 A $(REF SysTime,std,datetime,systime).
1744 --------------------
1745 if (source.timeLastModified >= target.timeLastModified(SysTime.min))
1751 // target is up-to-date
1753 --------------------
1755 SysTime timeLastModified(R)(R name, SysTime returnIfMissing)
1756 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
1761 return returnIfMissing;
1766 getTimesWin(name, dummy, dummy, ftm);
1770 else version (Posix)
1772 auto namez = name.tempCString!FSChar();
1773 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1775 return stat(namez, &buf);
1777 stat_t statbuf = void;
1779 return trustedStat(namez, statbuf) != 0 ?
1781 statTimeToStdTime!'m'(statbuf);
1788 import std.datetime : SysTime;
1790 assert("file.does.not.exist".timeLastModified(SysTime.min) == SysTime.min);
1792 auto source = deleteme ~ "source";
1793 auto target = deleteme ~ "target";
1794 scope(exit) source.remove, target.remove;
1797 assert(target.timeLastModified(SysTime.min) < source.timeLastModified);
1799 assert(target.timeLastModified(SysTime.min) >= source.timeLastModified);
1805 $(BLUE This function is POSIX-Only.)
1807 Returns the time that the given file was last modified.
1809 statbuf = stat_t retrieved from file.
1811 SysTime timeLastModified()(auto ref stat_t statbuf) pure nothrow {assert(false);}
1813 $(BLUE This function is POSIX-Only.)
1815 Returns the time that the given file was last accessed.
1817 statbuf = stat_t retrieved from file.
1819 SysTime timeLastAccessed()(auto ref stat_t statbuf) pure nothrow {assert(false);}
1821 $(BLUE This function is POSIX-Only.)
1823 Returns the time that the given file was last changed.
1825 statbuf = stat_t retrieved from file.
1827 SysTime timeStatusChanged()(auto ref stat_t statbuf) pure nothrow {assert(false);}
1829 else version (Posix)
1831 SysTime timeLastModified()(auto ref stat_t statbuf) pure nothrow
1833 return statTimeToStdTime!'m'(statbuf);
1835 SysTime timeLastAccessed()(auto ref stat_t statbuf) pure nothrow
1837 return statTimeToStdTime!'a'(statbuf);
1839 SysTime timeStatusChanged()(auto ref stat_t statbuf) pure nothrow
1841 return statTimeToStdTime!'c'(statbuf);
1847 // check that both lvalues and rvalues work
1848 timeLastAccessed(statbuf);
1849 cast(void) timeLastAccessed(stat_t.init);
1855 //std.process.executeShell("echo a > deleteme");
1856 if (exists(deleteme))
1859 write(deleteme, "a\n");
1863 assert(exists(deleteme));
1867 // assert(lastModified("deleteme") >
1868 // lastModified("this file does not exist", SysTime.min));
1869 //assert(lastModified("deleteme") > lastModified(__FILE__));
1873 // Tests sub-second precision of querying file times.
1874 // Should pass on most modern systems running on modern filesystems.
1876 // - FreeBSD, where one would need to first set the
1877 // vfs.timestamp_precision sysctl to a value greater than zero.
1878 // - OS X, where the native filesystem (HFS+) stores filesystem
1879 // timestamps with 1-second precision.
1881 // Note: on linux systems, although in theory a change to a file date
1882 // can be tracked with precision of 4 msecs, this test waits 20 msecs
1883 // to prevent possible problems relative to the CI services the dlang uses,
1884 // as they may have the HZ setting that controls the software clock set to 100
1885 // (instead of the more common 250).
1886 // see https://man7.org/linux/man-pages/man7/time.7.html
1887 // https://stackoverflow.com/a/14393315,
1888 // https://issues.dlang.org/show_bug.cgi?id=21148
1889 version (FreeBSD) {} else
1890 version (DragonFlyBSD) {} else
1891 version (OSX) {} else
1896 if (exists(deleteme))
1902 write(deleteme, "a");
1903 auto time = timeLastModified(deleteme);
1905 assert(time != lastTime);
1907 () @trusted { Thread.sleep(20.msecs); }();
1913 * Determine whether the given file (or directory) _exists.
1915 * name = string or range of characters representing the file _name
1917 * true if the file _name specified as input _exists
1919 bool exists(R)(R name)
1920 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1921 !isConvertibleToString!R)
1923 return existsImpl(name.tempCString!FSChar());
1927 bool exists(R)(auto ref R name)
1928 if (isConvertibleToString!R)
1930 return exists!(StringTypeOf!R)(name);
1936 auto f = deleteme ~ "does.not.exist";
1946 private bool existsImpl(const(FSChar)* namez) @trusted nothrow @nogc
1950 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
1951 // fileio/base/getfileattributes.asp
1952 return GetFileAttributesW(namez) != 0xFFFFFFFF;
1954 else version (Posix)
1957 The reason why we use stat (and not access) here is
1958 the quirky behavior of access for SUID programs: if
1959 we used access, a file may not appear to "exist",
1960 despite that the program would be able to open it
1961 just fine. The behavior in question is described as
1962 follows in the access man page:
1964 > The check is done using the calling process's real
1965 > UID and GID, rather than the effective IDs as is
1966 > done when actually attempting an operation (e.g.,
1967 > open(2)) on the file. This allows set-user-ID
1968 > programs to easily determine the invoking user's
1971 While various operating systems provide eaccess or
1972 euidaccess functions, these are not part of POSIX -
1973 so it's safer to use stat instead.
1976 stat_t statbuf = void;
1977 return lstat(namez, &statbuf) == 0;
1987 assert(!"this file does not exist".exists);
1988 deleteme.write("a\n");
1989 scope(exit) deleteme.remove;
1990 assert(deleteme.exists);
1993 // https://issues.dlang.org/show_bug.cgi?id=16573
1996 enum S : string { foo = "foo" }
1997 assert(__traits(compiles, S.foo.exists));
2001 Returns the attributes of the given file.
2003 Note that the file attributes on Windows and POSIX systems are
2004 completely different. On Windows, they're what is returned by
2005 $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx,
2006 GetFileAttributes), whereas on POSIX systems, they're the
2007 `st_mode` value which is part of the $(D stat struct) gotten by
2008 calling the $(HTTP en.wikipedia.org/wiki/Stat_%28Unix%29, `stat`)
2011 On POSIX systems, if the given file is a symbolic link, then
2012 attributes are the attributes of the file pointed to by the symbolic
2016 name = The file to get the attributes of.
2018 The attributes of the file as a `uint`.
2019 Throws: $(LREF FileException) on error.
2021 uint getAttributes(R)(R name)
2022 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2023 !isConvertibleToString!R)
2027 auto namez = name.tempCString!FSChar();
2028 static auto trustedGetFileAttributesW(const(FSChar)* namez) @trusted
2030 return GetFileAttributesW(namez);
2032 immutable result = trustedGetFileAttributesW(namez);
2034 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2037 string names = null;
2038 cenforce(result != INVALID_FILE_ATTRIBUTES, names, namez);
2042 else version (Posix)
2044 auto namez = name.tempCString!FSChar();
2045 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
2047 return stat(namez, &buf);
2049 stat_t statbuf = void;
2051 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2054 string names = null;
2055 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
2057 return statbuf.st_mode;
2062 uint getAttributes(R)(auto ref R name)
2063 if (isConvertibleToString!R)
2065 return getAttributes!(StringTypeOf!R)(name);
2068 /// getAttributes with a file
2071 import std.exception : assertThrown;
2073 auto f = deleteme ~ "file";
2074 scope(exit) f.remove;
2077 assertThrown!FileException(f.getAttributes);
2080 auto attributes = f.getAttributes;
2081 assert(!attributes.attrIsDir);
2082 assert(attributes.attrIsFile);
2085 /// getAttributes with a directory
2088 import std.exception : assertThrown;
2090 auto dir = deleteme ~ "dir";
2091 scope(exit) dir.rmdir;
2093 assert(!dir.exists);
2094 assertThrown!FileException(dir.getAttributes);
2097 auto attributes = dir.getAttributes;
2098 assert(attributes.attrIsDir);
2099 assert(!attributes.attrIsFile);
2104 static assert(__traits(compiles, getAttributes(TestAliasedString(null))));
2108 If the given file is a symbolic link, then this returns the attributes of the
2109 symbolic link itself rather than file that it points to. If the given file
2110 is $(I not) a symbolic link, then this function returns the same result
2113 On Windows, getLinkAttributes is identical to getAttributes. It exists on
2114 Windows so that you don't have to special-case code for Windows when dealing
2115 with symbolic links.
2118 name = The file to get the symbolic link attributes of.
2124 $(LREF FileException) on error.
2126 uint getLinkAttributes(R)(R name)
2127 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2128 !isConvertibleToString!R)
2132 return getAttributes(name);
2134 else version (Posix)
2136 auto namez = name.tempCString!FSChar();
2137 static auto trustedLstat(const(FSChar)* namez, ref stat_t buf) @trusted
2139 return lstat(namez, &buf);
2141 stat_t lstatbuf = void;
2142 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2145 string names = null;
2146 cenforce(trustedLstat(namez, lstatbuf) == 0, names, namez);
2147 return lstatbuf.st_mode;
2152 uint getLinkAttributes(R)(auto ref R name)
2153 if (isConvertibleToString!R)
2155 return getLinkAttributes!(StringTypeOf!R)(name);
2161 import std.exception : assertThrown;
2163 auto source = deleteme ~ "source";
2164 auto target = deleteme ~ "target";
2166 assert(!source.exists);
2167 assertThrown!FileException(source.getLinkAttributes);
2169 // symlinking isn't available on Windows
2172 scope(exit) source.remove, target.remove;
2174 target.write("target");
2175 target.symlink(source);
2176 assert(source.readText == "target");
2177 assert(source.isSymlink);
2178 assert(source.getLinkAttributes.attrIsSymlink);
2182 /// if the file is no symlink, getLinkAttributes behaves like getAttributes
2185 import std.exception : assertThrown;
2187 auto f = deleteme ~ "file";
2188 scope(exit) f.remove;
2191 assertThrown!FileException(f.getLinkAttributes);
2194 auto attributes = f.getLinkAttributes;
2195 assert(!attributes.attrIsDir);
2196 assert(attributes.attrIsFile);
2199 /// if the file is no symlink, getLinkAttributes behaves like getAttributes
2202 import std.exception : assertThrown;
2204 auto dir = deleteme ~ "dir";
2205 scope(exit) dir.rmdir;
2207 assert(!dir.exists);
2208 assertThrown!FileException(dir.getLinkAttributes);
2211 auto attributes = dir.getLinkAttributes;
2212 assert(attributes.attrIsDir);
2213 assert(!attributes.attrIsFile);
2218 static assert(__traits(compiles, getLinkAttributes(TestAliasedString(null))));
2222 Set the _attributes of the given file.
2224 For example, a programmatic equivalent of Unix's `chmod +x name`
2225 to make a file executable is
2226 `name.setAttributes(name.getAttributes | octal!700)`.
2229 name = the file _name
2230 attributes = the _attributes to set the file to
2233 $(LREF FileException) if the given file does not exist.
2235 void setAttributes(R)(R name, uint attributes)
2236 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2237 !isConvertibleToString!R)
2241 auto namez = name.tempCString!FSChar();
2242 static auto trustedSetFileAttributesW(const(FSChar)* namez, uint dwFileAttributes) @trusted
2244 return SetFileAttributesW(namez, dwFileAttributes);
2246 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2249 string names = null;
2250 cenforce(trustedSetFileAttributesW(namez, attributes), names, namez);
2252 else version (Posix)
2254 auto namez = name.tempCString!FSChar();
2255 static auto trustedChmod(const(FSChar)* namez, mode_t mode) @trusted
2257 return chmod(namez, mode);
2259 assert(attributes <= mode_t.max);
2260 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2263 string names = null;
2264 cenforce(!trustedChmod(namez, cast(mode_t) attributes), names, namez);
2269 void setAttributes(R)(auto ref R name, uint attributes)
2270 if (isConvertibleToString!R)
2272 return setAttributes!(StringTypeOf!R)(name, attributes);
2277 static assert(__traits(compiles, setAttributes(TestAliasedString(null), 0)));
2280 /// setAttributes with a file
2283 import std.exception : assertThrown;
2284 import std.conv : octal;
2286 auto f = deleteme ~ "file";
2289 scope(exit) f.remove;
2292 assertThrown!FileException(f.setAttributes(octal!777));
2295 auto attributes = f.getAttributes;
2296 assert(!attributes.attrIsDir);
2297 assert(attributes.attrIsFile);
2299 f.setAttributes(octal!777);
2300 attributes = f.getAttributes;
2302 assert((attributes & 1023) == octal!777);
2306 /// setAttributes with a directory
2309 import std.exception : assertThrown;
2310 import std.conv : octal;
2312 auto dir = deleteme ~ "dir";
2315 scope(exit) dir.rmdir;
2317 assert(!dir.exists);
2318 assertThrown!FileException(dir.setAttributes(octal!777));
2321 auto attributes = dir.getAttributes;
2322 assert(attributes.attrIsDir);
2323 assert(!attributes.attrIsFile);
2325 dir.setAttributes(octal!777);
2326 attributes = dir.getAttributes;
2328 assert((attributes & 1023) == octal!777);
2333 Returns whether the given file is a directory.
2336 name = The path to the file.
2339 true if name specifies a directory
2342 $(LREF FileException) if the given file does not exist.
2344 @property bool isDir(R)(R name)
2345 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2346 !isConvertibleToString!R)
2350 return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) != 0;
2352 else version (Posix)
2354 return (getAttributes(name) & S_IFMT) == S_IFDIR;
2359 @property bool isDir(R)(auto ref R name)
2360 if (isConvertibleToString!R)
2362 return name.isDir!(StringTypeOf!R);
2369 import std.exception : assertThrown;
2371 auto dir = deleteme ~ "dir";
2372 auto f = deleteme ~ "f";
2373 scope(exit) dir.rmdir, f.remove;
2375 assert(!dir.exists);
2376 assertThrown!FileException(dir.isDir);
2387 static assert(__traits(compiles, TestAliasedString(null).isDir));
2394 if ("C:\\Program Files\\".exists)
2395 assert("C:\\Program Files\\".isDir);
2397 if ("C:\\Windows\\system.ini".exists)
2398 assert(!"C:\\Windows\\system.ini".isDir);
2400 else version (Posix)
2402 if (system_directory.exists)
2403 assert(system_directory.isDir);
2405 if (system_file.exists)
2406 assert(!system_file.isDir);
2413 enum dir = "C:\\Program Files\\";
2414 else version (Posix)
2415 enum dir = system_directory;
2419 DirEntry de = DirEntry(dir);
2421 assert(DirEntry(dir).isDir);
2426 Returns whether the given file _attributes are for a directory.
2429 attributes = The file _attributes.
2432 true if attributes specifies a directory
2434 bool attrIsDir(uint attributes) @safe pure nothrow @nogc
2438 return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
2440 else version (Posix)
2442 return (attributes & S_IFMT) == S_IFDIR;
2449 import std.exception : assertThrown;
2451 auto dir = deleteme ~ "dir";
2452 auto f = deleteme ~ "f";
2453 scope(exit) dir.rmdir, f.remove;
2455 assert(!dir.exists);
2456 assertThrown!FileException(dir.getAttributes.attrIsDir);
2460 assert(dir.getAttributes.attrIsDir);
2464 assert(!f.getAttributes.attrIsDir);
2471 if ("C:\\Program Files\\".exists)
2473 assert(attrIsDir(getAttributes("C:\\Program Files\\")));
2474 assert(attrIsDir(getLinkAttributes("C:\\Program Files\\")));
2477 if ("C:\\Windows\\system.ini".exists)
2479 assert(!attrIsDir(getAttributes("C:\\Windows\\system.ini")));
2480 assert(!attrIsDir(getLinkAttributes("C:\\Windows\\system.ini")));
2483 else version (Posix)
2485 if (system_directory.exists)
2487 assert(attrIsDir(getAttributes(system_directory)));
2488 assert(attrIsDir(getLinkAttributes(system_directory)));
2491 if (system_file.exists)
2493 assert(!attrIsDir(getAttributes(system_file)));
2494 assert(!attrIsDir(getLinkAttributes(system_file)));
2501 Returns whether the given file (or directory) is a file.
2503 On Windows, if a file is not a directory, then it's a file. So,
2504 either `isFile` or `isDir` will return true for any given file.
2506 On POSIX systems, if `isFile` is `true`, that indicates that the file
2507 is a regular file (e.g. not a block not device). So, on POSIX systems, it's
2508 possible for both `isFile` and `isDir` to be `false` for a
2509 particular file (in which case, it's a special file). You can use
2510 `getAttributes` to get the attributes to figure out what type of special
2511 it is, or you can use `DirEntry` to get at its `statBuf`, which is the
2512 result from `stat`. In either case, see the man page for `stat` for
2516 name = The path to the file.
2519 true if name specifies a file
2522 $(LREF FileException) if the given file does not exist.
2524 @property bool isFile(R)(R name)
2525 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2526 !isConvertibleToString!R)
2530 else version (Posix)
2531 return (getAttributes(name) & S_IFMT) == S_IFREG;
2535 @property bool isFile(R)(auto ref R name)
2536 if (isConvertibleToString!R)
2538 return isFile!(StringTypeOf!R)(name);
2544 import std.exception : assertThrown;
2546 auto dir = deleteme ~ "dir";
2547 auto f = deleteme ~ "f";
2548 scope(exit) dir.rmdir, f.remove;
2551 assert(!dir.isFile);
2554 assertThrown!FileException(f.isFile);
2560 // https://issues.dlang.org/show_bug.cgi?id=15658
2563 DirEntry e = DirEntry(".");
2564 static assert(is(typeof(isFile(e))));
2569 static assert(__traits(compiles, TestAliasedString(null).isFile));
2576 if ("C:\\Program Files\\".exists)
2577 assert(!"C:\\Program Files\\".isFile);
2579 if ("C:\\Windows\\system.ini".exists)
2580 assert("C:\\Windows\\system.ini".isFile);
2582 else version (Posix)
2584 if (system_directory.exists)
2585 assert(!system_directory.isFile);
2587 if (system_file.exists)
2588 assert(system_file.isFile);
2594 Returns whether the given file _attributes are for a file.
2596 On Windows, if a file is not a directory, it's a file. So, either
2597 `attrIsFile` or `attrIsDir` will return `true` for the
2598 _attributes of any given file.
2600 On POSIX systems, if `attrIsFile` is `true`, that indicates that the
2601 file is a regular file (e.g. not a block not device). So, on POSIX systems,
2602 it's possible for both `attrIsFile` and `attrIsDir` to be `false`
2603 for a particular file (in which case, it's a special file). If a file is a
2604 special file, you can use the _attributes to check what type of special file
2605 it is (see the man page for `stat` for more information).
2608 attributes = The file _attributes.
2611 true if the given file _attributes are for a file
2614 --------------------
2615 assert(attrIsFile(getAttributes("/etc/fonts/fonts.conf")));
2616 assert(attrIsFile(getLinkAttributes("/etc/fonts/fonts.conf")));
2617 --------------------
2619 bool attrIsFile(uint attributes) @safe pure nothrow @nogc
2623 return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
2625 else version (Posix)
2627 return (attributes & S_IFMT) == S_IFREG;
2634 import std.exception : assertThrown;
2636 auto dir = deleteme ~ "dir";
2637 auto f = deleteme ~ "f";
2638 scope(exit) dir.rmdir, f.remove;
2641 assert(!dir.isFile);
2642 assert(!dir.getAttributes.attrIsFile);
2645 assertThrown!FileException(f.getAttributes.attrIsFile);
2649 assert(f.getAttributes.attrIsFile);
2656 if ("C:\\Program Files\\".exists)
2658 assert(!attrIsFile(getAttributes("C:\\Program Files\\")));
2659 assert(!attrIsFile(getLinkAttributes("C:\\Program Files\\")));
2662 if ("C:\\Windows\\system.ini".exists)
2664 assert(attrIsFile(getAttributes("C:\\Windows\\system.ini")));
2665 assert(attrIsFile(getLinkAttributes("C:\\Windows\\system.ini")));
2668 else version (Posix)
2670 if (system_directory.exists)
2672 assert(!attrIsFile(getAttributes(system_directory)));
2673 assert(!attrIsFile(getLinkAttributes(system_directory)));
2676 if (system_file.exists)
2678 assert(attrIsFile(getAttributes(system_file)));
2679 assert(attrIsFile(getLinkAttributes(system_file)));
2686 Returns whether the given file is a symbolic link.
2688 On Windows, returns `true` when the file is either a symbolic link or a
2692 name = The path to the file.
2695 true if name is a symbolic link
2698 $(LREF FileException) if the given file does not exist.
2700 @property bool isSymlink(R)(R name)
2701 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2702 !isConvertibleToString!R)
2705 return (getAttributes(name) & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2706 else version (Posix)
2707 return (getLinkAttributes(name) & S_IFMT) == S_IFLNK;
2711 @property bool isSymlink(R)(auto ref R name)
2712 if (isConvertibleToString!R)
2714 return name.isSymlink!(StringTypeOf!R);
2719 static assert(__traits(compiles, TestAliasedString(null).isSymlink));
2725 import std.exception : assertThrown;
2727 auto source = deleteme ~ "source";
2728 auto target = deleteme ~ "target";
2730 assert(!source.exists);
2731 assertThrown!FileException(source.isSymlink);
2733 // symlinking isn't available on Windows
2736 scope(exit) source.remove, target.remove;
2738 target.write("target");
2739 target.symlink(source);
2740 assert(source.readText == "target");
2741 assert(source.isSymlink);
2742 assert(source.getLinkAttributes.attrIsSymlink);
2750 if ("C:\\Program Files\\".exists)
2751 assert(!"C:\\Program Files\\".isSymlink);
2753 if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
2754 assert("C:\\Documents and Settings\\".isSymlink);
2756 enum fakeSymFile = "C:\\Windows\\system.ini";
2757 if (fakeSymFile.exists)
2759 assert(!fakeSymFile.isSymlink);
2761 assert(!fakeSymFile.isSymlink);
2762 assert(!attrIsSymlink(getAttributes(fakeSymFile)));
2763 assert(!attrIsSymlink(getLinkAttributes(fakeSymFile)));
2765 assert(attrIsFile(getAttributes(fakeSymFile)));
2766 assert(attrIsFile(getLinkAttributes(fakeSymFile)));
2767 assert(!attrIsDir(getAttributes(fakeSymFile)));
2768 assert(!attrIsDir(getLinkAttributes(fakeSymFile)));
2770 assert(getAttributes(fakeSymFile) == getLinkAttributes(fakeSymFile));
2773 else version (Posix)
2775 if (system_directory.exists)
2777 assert(!system_directory.isSymlink);
2779 immutable symfile = deleteme ~ "_slink\0";
2780 scope(exit) if (symfile.exists) symfile.remove();
2782 core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
2784 assert(symfile.isSymlink);
2785 assert(!attrIsSymlink(getAttributes(symfile)));
2786 assert(attrIsSymlink(getLinkAttributes(symfile)));
2788 assert(attrIsDir(getAttributes(symfile)));
2789 assert(!attrIsDir(getLinkAttributes(symfile)));
2791 assert(!attrIsFile(getAttributes(symfile)));
2792 assert(!attrIsFile(getLinkAttributes(symfile)));
2795 if (system_file.exists)
2797 assert(!system_file.isSymlink);
2799 immutable symfile = deleteme ~ "_slink\0";
2800 scope(exit) if (symfile.exists) symfile.remove();
2802 core.sys.posix.unistd.symlink(system_file, symfile.ptr);
2804 assert(symfile.isSymlink);
2805 assert(!attrIsSymlink(getAttributes(symfile)));
2806 assert(attrIsSymlink(getLinkAttributes(symfile)));
2808 assert(!attrIsDir(getAttributes(symfile)));
2809 assert(!attrIsDir(getLinkAttributes(symfile)));
2811 assert(attrIsFile(getAttributes(symfile)));
2812 assert(!attrIsFile(getLinkAttributes(symfile)));
2816 static assert(__traits(compiles, () @safe { return "dummy".isSymlink; }));
2821 Returns whether the given file attributes are for a symbolic link.
2823 On Windows, return `true` when the file is either a symbolic link or a
2827 attributes = The file attributes.
2830 true if attributes are for a symbolic link
2833 --------------------
2834 core.sys.posix.unistd.symlink("/etc/fonts/fonts.conf", "/tmp/alink");
2836 assert(!getAttributes("/tmp/alink").isSymlink);
2837 assert(getLinkAttributes("/tmp/alink").isSymlink);
2838 --------------------
2840 bool attrIsSymlink(uint attributes) @safe pure nothrow @nogc
2843 return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2844 else version (Posix)
2845 return (attributes & S_IFMT) == S_IFLNK;
2851 import std.exception : assertThrown;
2853 auto source = deleteme ~ "source";
2854 auto target = deleteme ~ "target";
2856 assert(!source.exists);
2857 assertThrown!FileException(source.getLinkAttributes.attrIsSymlink);
2859 // symlinking isn't available on Windows
2862 scope(exit) source.remove, target.remove;
2864 target.write("target");
2865 target.symlink(source);
2866 assert(source.readText == "target");
2867 assert(source.isSymlink);
2868 assert(source.getLinkAttributes.attrIsSymlink);
2873 Change directory to `pathname`. Equivalent to `cd` on
2877 pathname = the directory to step into
2879 Throws: $(LREF FileException) on error.
2881 void chdir(R)(R pathname)
2882 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2883 !isConvertibleToString!R)
2885 // Place outside of @trusted block
2886 auto pathz = pathname.tempCString!FSChar();
2890 static auto trustedChdir(const(FSChar)* pathz) @trusted
2892 return SetCurrentDirectoryW(pathz);
2895 else version (Posix)
2897 static auto trustedChdir(const(FSChar)* pathz) @trusted
2899 return core.sys.posix.unistd.chdir(pathz) == 0;
2902 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2903 alias pathStr = pathname;
2905 string pathStr = null;
2906 cenforce(trustedChdir(pathz), pathStr, pathz);
2910 void chdir(R)(auto ref R pathname)
2911 if (isConvertibleToString!R)
2913 return chdir!(StringTypeOf!R)(pathname);
2919 import std.algorithm.comparison : equal;
2920 import std.algorithm.sorting : sort;
2921 import std.array : array;
2922 import std.path : buildPath;
2925 auto dir = deleteme ~ "dir";
2927 scope(exit) cwd.chdir, dir.rmdirRecurse;
2929 dir.buildPath("a").write(".");
2930 dir.chdir; // step into dir
2932 assert(dirEntries(".", SpanMode.shallow).array.sort.equal(
2933 [".".buildPath("a"), ".".buildPath("b")]
2939 static assert(__traits(compiles, chdir(TestAliasedString(null))));
2943 Make a new directory `pathname`.
2946 pathname = the path of the directory to make
2949 $(LREF FileException) on POSIX or $(LREF WindowsException) on Windows
2950 if an error occured.
2952 void mkdir(R)(R pathname)
2953 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2954 !isConvertibleToString!R)
2956 // Place outside of @trusted block
2957 const pathz = pathname.tempCString!FSChar();
2961 static auto trustedCreateDirectoryW(const(FSChar)* pathz) @trusted
2963 return CreateDirectoryW(pathz, null);
2965 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2966 alias pathStr = pathname;
2968 string pathStr = null;
2969 wenforce(trustedCreateDirectoryW(pathz), pathStr, pathz);
2971 else version (Posix)
2973 import std.conv : octal;
2975 static auto trustedMkdir(const(FSChar)* pathz, mode_t mode) @trusted
2977 return core.sys.posix.sys.stat.mkdir(pathz, mode);
2979 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
2980 alias pathStr = pathname;
2982 string pathStr = null;
2983 cenforce(trustedMkdir(pathz, octal!777) == 0, pathStr, pathz);
2988 void mkdir(R)(auto ref R pathname)
2989 if (isConvertibleToString!R)
2991 return mkdir!(StringTypeOf!R)(pathname);
2996 import std.file : mkdir;
2997 static assert(__traits(compiles, mkdir(TestAliasedString(null))));
3003 import std.file : mkdir;
3005 auto dir = deleteme ~ "dir";
3006 scope(exit) dir.rmdir;
3015 import std.exception : assertThrown;
3016 assertThrown("a/b/c/d/e".mkdir);
3019 // Same as mkdir but ignores "already exists" errors.
3020 // Returns: "true" if the directory was created,
3021 // "false" if it already existed.
3022 private bool ensureDirExists()(scope const(char)[] pathname)
3024 import std.exception : enforce;
3025 const pathz = pathname.tempCString!FSChar();
3029 if (() @trusted { return CreateDirectoryW(pathz, null); }())
3031 cenforce(GetLastError() == ERROR_ALREADY_EXISTS, pathname.idup);
3033 else version (Posix)
3035 import std.conv : octal;
3037 if (() @trusted { return core.sys.posix.sys.stat.mkdir(pathz, octal!777); }() == 0)
3039 cenforce(errno == EEXIST || errno == EISDIR, pathname);
3041 enforce(pathname.isDir, new FileException(pathname.idup));
3046 Make directory and all parent directories as needed.
3048 Does nothing if the directory specified by
3049 `pathname` already exists.
3052 pathname = the full path of the directory to create
3054 Throws: $(LREF FileException) on error.
3056 void mkdirRecurse(scope const(char)[] pathname) @safe
3058 import std.path : dirName, baseName;
3060 const left = dirName(pathname);
3061 if (left.length != pathname.length && !exists(left))
3065 if (!baseName(pathname).empty)
3067 ensureDirExists(pathname);
3074 import std.path : buildPath;
3076 auto dir = deleteme ~ "dir";
3077 scope(exit) dir.rmdirRecurse;
3081 dir.mkdirRecurse; // does nothing
3083 // creates all parent directories as needed
3084 auto nested = dir.buildPath("a", "b", "c");
3085 nested.mkdirRecurse;
3086 assert(nested.exists);
3092 import std.exception : assertThrown;
3094 scope(exit) deleteme.remove;
3095 deleteme.write("a");
3097 // cannot make directory as it's already a file
3098 assertThrown!FileException(deleteme.mkdirRecurse);
3103 import std.exception : assertThrown;
3105 import std.path : buildPath, buildNormalizedPath;
3107 immutable basepath = deleteme ~ "_dir";
3108 scope(exit) () @trusted { rmdirRecurse(basepath); }();
3110 auto path = buildPath(basepath, "a", "..", "b");
3112 path = path.buildNormalizedPath;
3115 path = buildPath(basepath, "c");
3117 assertThrown!FileException(mkdirRecurse(path));
3119 path = buildPath(basepath, "d");
3121 mkdirRecurse(path); // should not throw
3126 assertThrown!FileException(mkdirRecurse(`1:\foobar`));
3129 // https://issues.dlang.org/show_bug.cgi?id=3570
3131 immutable basepath = deleteme ~ "_dir";
3134 immutable path = basepath ~ "\\fake\\here\\";
3136 else version (Posix)
3138 immutable path = basepath ~ `/fake/here/`;
3142 assert(basepath.exists && basepath.isDir);
3143 scope(exit) () @trusted { rmdirRecurse(basepath); }();
3144 assert(path.exists && path.isDir);
3148 /****************************************************
3149 Remove directory `pathname`.
3152 pathname = Range or string specifying the directory name
3154 Throws: $(LREF FileException) on error.
3156 void rmdir(R)(R pathname)
3157 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
3158 !isConvertibleToString!R)
3160 // Place outside of @trusted block
3161 auto pathz = pathname.tempCString!FSChar();
3165 static auto trustedRmdir(const(FSChar)* pathz) @trusted
3167 return RemoveDirectoryW(pathz);
3170 else version (Posix)
3172 static auto trustedRmdir(const(FSChar)* pathz) @trusted
3174 return core.sys.posix.unistd.rmdir(pathz) == 0;
3177 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
3178 alias pathStr = pathname;
3180 string pathStr = null;
3181 cenforce(trustedRmdir(pathz), pathStr, pathz);
3185 void rmdir(R)(auto ref R pathname)
3186 if (isConvertibleToString!R)
3188 rmdir!(StringTypeOf!R)(pathname);
3193 static assert(__traits(compiles, rmdir(TestAliasedString(null))));
3199 auto dir = deleteme ~ "dir";
3204 assert(!dir.exists);
3208 $(BLUE This function is POSIX-Only.)
3210 Creates a symbolic _link (_symlink).
3213 original = The file that is being linked. This is the target path that's
3214 stored in the _symlink. A relative path is relative to the created
3216 link = The _symlink to create. A relative path is relative to the
3217 current working directory.
3220 $(LREF FileException) on error (which includes if the _symlink already
3223 version (StdDdoc) void symlink(RO, RL)(RO original, RL link)
3224 if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) ||
3225 isConvertibleToString!RO) &&
3226 (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) ||
3227 isConvertibleToString!RL));
3228 else version (Posix) void symlink(RO, RL)(RO original, RL link)
3229 if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) ||
3230 isConvertibleToString!RO) &&
3231 (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) ||
3232 isConvertibleToString!RL))
3234 static if (isConvertibleToString!RO || isConvertibleToString!RL)
3236 import std.meta : staticMap;
3237 alias Types = staticMap!(convertToString, RO, RL);
3238 symlink!Types(original, link);
3242 import std.conv : text;
3243 auto oz = original.tempCString();
3244 auto lz = link.tempCString();
3245 alias posixSymlink = core.sys.posix.unistd.symlink;
3246 immutable int result = () @trusted { return posixSymlink(oz, lz); } ();
3247 cenforce(result == 0, text(link));
3251 version (Posix) @safe unittest
3253 if (system_directory.exists)
3255 immutable symfile = deleteme ~ "_slink\0";
3256 scope(exit) if (symfile.exists) symfile.remove();
3258 symlink(system_directory, symfile);
3260 assert(symfile.exists);
3261 assert(symfile.isSymlink);
3262 assert(!attrIsSymlink(getAttributes(symfile)));
3263 assert(attrIsSymlink(getLinkAttributes(symfile)));
3265 assert(attrIsDir(getAttributes(symfile)));
3266 assert(!attrIsDir(getLinkAttributes(symfile)));
3268 assert(!attrIsFile(getAttributes(symfile)));
3269 assert(!attrIsFile(getLinkAttributes(symfile)));
3272 if (system_file.exists)
3274 assert(!system_file.isSymlink);
3276 immutable symfile = deleteme ~ "_slink\0";
3277 scope(exit) if (symfile.exists) symfile.remove();
3279 symlink(system_file, symfile);
3281 assert(symfile.exists);
3282 assert(symfile.isSymlink);
3283 assert(!attrIsSymlink(getAttributes(symfile)));
3284 assert(attrIsSymlink(getLinkAttributes(symfile)));
3286 assert(!attrIsDir(getAttributes(symfile)));
3287 assert(!attrIsDir(getLinkAttributes(symfile)));
3289 assert(attrIsFile(getAttributes(symfile)));
3290 assert(!attrIsFile(getLinkAttributes(symfile)));
3294 version (Posix) @safe unittest
3296 static assert(__traits(compiles,
3297 symlink(TestAliasedString(null), TestAliasedString(null))));
3302 $(BLUE This function is POSIX-Only.)
3304 Returns the path to the file pointed to by a symlink. Note that the
3305 path could be either relative or absolute depending on the symlink.
3306 If the path is relative, it's relative to the symlink, not the current
3310 $(LREF FileException) on error.
3312 version (StdDdoc) string readLink(R)(R link)
3313 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) ||
3314 isConvertibleToString!R);
3315 else version (Posix) string readLink(R)(R link)
3316 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) ||
3317 isConvertibleToString!R)
3319 static if (isConvertibleToString!R)
3321 return readLink!(convertToString!R)(link);
3325 import std.conv : to;
3326 import std.exception : assumeUnique;
3327 alias posixReadlink = core.sys.posix.unistd.readlink;
3328 enum bufferLen = 2048;
3329 enum maxCodeUnits = 6;
3330 char[bufferLen] buffer;
3331 const linkz = link.tempCString();
3332 auto size = () @trusted {
3333 return posixReadlink(linkz, buffer.ptr, buffer.length);
3335 cenforce(size != -1, to!string(link));
3337 if (size <= bufferLen - maxCodeUnits)
3338 return to!string(buffer[0 .. size]);
3340 auto dynamicBuffer = new char[](bufferLen * 3 / 2);
3342 foreach (i; 0 .. 10)
3344 size = () @trusted {
3345 return posixReadlink(linkz, dynamicBuffer.ptr,
3346 dynamicBuffer.length);
3348 cenforce(size != -1, to!string(link));
3350 if (size <= dynamicBuffer.length - maxCodeUnits)
3352 dynamicBuffer.length = size;
3353 return () @trusted {
3354 return assumeUnique(dynamicBuffer);
3358 dynamicBuffer.length = dynamicBuffer.length * 3 / 2;
3361 throw new FileException(to!string(link), "Path is too long to read.");
3365 version (Posix) @safe unittest
3367 import std.exception : assertThrown;
3370 foreach (file; [system_directory, system_file])
3374 immutable symfile = deleteme ~ "_slink\0";
3375 scope(exit) if (symfile.exists) symfile.remove();
3377 symlink(file, symfile);
3378 assert(readLink(symfile) == file, format("Failed file: %s", file));
3382 assertThrown!FileException(readLink("/doesnotexist"));
3385 version (Posix) @safe unittest
3387 static assert(__traits(compiles, readLink(TestAliasedString("foo"))));
3390 version (Posix) @system unittest // input range of dchars
3392 mkdirRecurse(deleteme);
3393 scope(exit) if (deleteme.exists) rmdirRecurse(deleteme);
3394 write(deleteme ~ "/f", "");
3395 import std.range.interfaces : InputRange, inputRangeObject;
3396 import std.utf : byChar;
3397 immutable string link = deleteme ~ "/l";
3399 InputRange!(ElementType!string) linkr = inputRangeObject(link);
3400 alias R = typeof(linkr);
3401 static assert(isInputRange!R);
3402 static assert(!isForwardRange!R);
3403 assert(readLink(linkr) == "f");
3407 /****************************************************
3408 * Get the current working directory.
3409 * Throws: $(LREF FileException) on error.
3411 version (Windows) string getcwd() @trusted
3413 import std.conv : to;
3414 import std.experimental.checkedint : checked;
3415 /* GetCurrentDirectory's return value:
3416 1. function succeeds: the number of characters that are written to
3417 the buffer, not including the terminating null character.
3418 2. function fails: zero
3419 3. the buffer (lpBuffer) is not large enough: the required size of
3420 the buffer, in characters, including the null-terminating character.
3422 version (StdUnittest)
3423 enum BUF_SIZE = 10; // trigger reallocation code
3425 enum BUF_SIZE = 4096; // enough for most common case
3426 wchar[BUF_SIZE] buffW = void;
3427 immutable n = cenforce(GetCurrentDirectoryW(to!DWORD(buffW.length), buffW.ptr),
3429 // we can do it because toUTFX always produces a fresh string
3430 if (n < buffW.length)
3432 return buffW[0 .. n].to!string;
3434 else //staticBuff isn't enough
3436 auto cn = checked(n);
3437 auto ptr = cast(wchar*) malloc((cn * wchar.sizeof).get);
3438 scope(exit) free(ptr);
3439 immutable n2 = GetCurrentDirectoryW(cn.get, ptr);
3440 cenforce(n2 && n2 < cn, "getcwd");
3441 return ptr[0 .. n2].to!string;
3444 else version (Solaris) string getcwd() @trusted
3446 /* BUF_SIZE >= PATH_MAX */
3447 enum BUF_SIZE = 4096;
3448 /* The user should be able to specify any size buffer > 0 */
3449 auto p = cenforce(core.sys.posix.unistd.getcwd(null, BUF_SIZE),
3451 scope(exit) core.stdc.stdlib.free(p);
3452 return p[0 .. core.stdc.string.strlen(p)].idup;
3454 else version (Posix) string getcwd() @trusted
3456 auto p = cenforce(core.sys.posix.unistd.getcwd(null, 0),
3458 scope(exit) core.stdc.stdlib.free(p);
3459 return p[0 .. core.stdc.string.strlen(p)].idup;
3470 * Returns the full path of the current executable.
3473 * The path of the executable as a `string`.
3476 * $(REF1 Exception, object)
3478 @trusted string thisExePath()
3482 import core.sys.darwin.mach.dyld : _NSGetExecutablePath;
3483 import core.sys.posix.stdlib : realpath;
3484 import std.conv : to;
3485 import std.exception : errnoEnforce;
3489 _NSGetExecutablePath(null, &size); // get the length of the path
3490 auto buffer = new char[size];
3491 _NSGetExecutablePath(buffer.ptr, &size);
3493 auto absolutePath = realpath(buffer.ptr, null); // let the function allocate
3501 errnoEnforce(absolutePath);
3502 return to!(string)(absolutePath);
3504 else version (linux)
3506 return readLink("/proc/self/exe");
3508 else version (Windows)
3510 import std.conv : to;
3511 import std.exception : enforce;
3513 wchar[MAX_PATH] buf;
3514 wchar[] buffer = buf[];
3518 auto len = GetModuleFileNameW(null, buffer.ptr, cast(DWORD) buffer.length);
3519 enforce(len, sysErrorString(GetLastError()));
3520 if (len != buffer.length)
3521 return to!(string)(buffer[0 .. len]);
3525 else version (DragonFlyBSD)
3527 import core.sys.dragonflybsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME;
3528 import std.exception : errnoEnforce, assumeUnique;
3530 int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
3533 auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
3534 errnoEnforce(result == 0);
3536 auto buffer = new char[len - 1];
3537 result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
3538 errnoEnforce(result == 0);
3540 return buffer.assumeUnique;
3542 else version (FreeBSD)
3544 import core.sys.freebsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME;
3545 import std.exception : errnoEnforce, assumeUnique;
3547 int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
3550 auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
3551 errnoEnforce(result == 0);
3553 auto buffer = new char[len - 1];
3554 result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
3555 errnoEnforce(result == 0);
3557 return buffer.assumeUnique;
3559 else version (NetBSD)
3561 import core.sys.netbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_PATHNAME;
3562 import std.exception : errnoEnforce, assumeUnique;
3564 int[4] mib = [CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME];
3567 auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
3568 errnoEnforce(result == 0);
3570 auto buffer = new char[len - 1];
3571 result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
3572 errnoEnforce(result == 0);
3574 return buffer.assumeUnique;
3576 else version (OpenBSD)
3578 import core.sys.openbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_ARGV;
3579 import core.sys.posix.unistd : getpid;
3580 import std.conv : to;
3581 import std.exception : enforce, errnoEnforce;
3582 import std.process : searchPathFor;
3584 int[4] mib = [CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV];
3587 auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0);
3588 errnoEnforce(result == 0);
3590 auto argv = new char*[len - 1];
3591 result = sysctl(mib.ptr, mib.length, argv.ptr, &len, null, 0);
3592 errnoEnforce(result == 0);
3594 auto argv0 = argv[0];
3595 if (*argv0 == '/' || *argv0 == '.')
3597 import core.sys.posix.stdlib : realpath;
3598 auto absolutePath = realpath(argv0, null);
3604 errnoEnforce(absolutePath);
3605 return to!(string)(absolutePath);
3609 auto absolutePath = searchPathFor(to!string(argv0));
3610 errnoEnforce(absolutePath);
3611 return absolutePath;
3614 else version (Solaris)
3616 import core.sys.posix.unistd : getpid;
3617 import std.string : format;
3619 // Only Solaris 10 and later
3620 return readLink(format("/proc/%d/path/a.out", getpid()));
3623 static assert(0, "thisExePath is not supported on this platform");
3629 import std.path : isAbsolute;
3630 auto path = thisExePath();
3632 assert(path.exists);
3633 assert(path.isAbsolute);
3634 assert(path.isFile);
3640 Info on a file, similar to what you'd get from stat on a POSIX system.
3646 Constructs a `DirEntry` for the given file (or directory).
3649 path = The file (or directory) to get a DirEntry for.
3652 $(LREF FileException) if the file does not exist.
3658 private this(string path, in WIN32_FIND_DATAW *fd);
3660 else version (Posix)
3662 private this(string path, core.sys.posix.dirent.dirent* fd);
3666 Returns the path to the file represented by this `DirEntry`.
3669 --------------------
3670 auto de1 = DirEntry("/etc/fonts/fonts.conf");
3671 assert(de1.name == "/etc/fonts/fonts.conf");
3673 auto de2 = DirEntry("/usr/share/include");
3674 assert(de2.name == "/usr/share/include");
3675 --------------------
3677 @property string name() const return scope;
3681 Returns whether the file represented by this `DirEntry` is a
3685 --------------------
3686 auto de1 = DirEntry("/etc/fonts/fonts.conf");
3689 auto de2 = DirEntry("/usr/share/include");
3691 --------------------
3693 @property bool isDir() scope;
3697 Returns whether the file represented by this `DirEntry` is a file.
3699 On Windows, if a file is not a directory, then it's a file. So,
3700 either `isFile` or `isDir` will return `true`.
3702 On POSIX systems, if `isFile` is `true`, that indicates that
3703 the file is a regular file (e.g. not a block not device). So, on
3704 POSIX systems, it's possible for both `isFile` and `isDir` to
3705 be `false` for a particular file (in which case, it's a special
3706 file). You can use `attributes` or `statBuf` to get more
3707 information about a special file (see the stat man page for more
3711 --------------------
3712 auto de1 = DirEntry("/etc/fonts/fonts.conf");
3715 auto de2 = DirEntry("/usr/share/include");
3716 assert(!de2.isFile);
3717 --------------------
3719 @property bool isFile() scope;
3722 Returns whether the file represented by this `DirEntry` is a
3725 On Windows, return `true` when the file is either a symbolic
3726 link or a junction point.
3728 @property bool isSymlink() scope;
3731 Returns the size of the the file represented by this `DirEntry`
3734 @property ulong size() scope;
3737 $(BLUE This function is Windows-Only.)
3739 Returns the creation time of the file represented by this
3742 @property SysTime timeCreated() const scope;
3745 Returns the time that the file represented by this `DirEntry` was
3748 Note that many file systems do not update the access time for files
3749 (generally for performance reasons), so there's a good chance that
3750 `timeLastAccessed` will return the same value as
3753 @property SysTime timeLastAccessed() scope;
3756 Returns the time that the file represented by this `DirEntry` was
3759 @property SysTime timeLastModified() scope;
3762 $(BLUE This function is POSIX-Only.)
3764 Returns the time that the file represented by this `DirEntry` was
3765 last changed (not only in contents, but also in permissions or ownership).
3767 @property SysTime timeStatusChanged() const scope;
3770 Returns the _attributes of the file represented by this `DirEntry`.
3772 Note that the file _attributes on Windows and POSIX systems are
3773 completely different. On, Windows, they're what is returned by
3775 $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, GetFileAttributes)
3776 Whereas, an POSIX systems, they're the `st_mode` value which is
3777 part of the `stat` struct gotten by calling `stat`.
3779 On POSIX systems, if the file represented by this `DirEntry` is a
3780 symbolic link, then _attributes are the _attributes of the file
3781 pointed to by the symbolic link.
3783 @property uint attributes() scope;
3786 On POSIX systems, if the file represented by this `DirEntry` is a
3787 symbolic link, then `linkAttributes` are the attributes of the
3788 symbolic link itself. Otherwise, `linkAttributes` is identical to
3791 On Windows, `linkAttributes` is identical to `attributes`. It
3792 exists on Windows so that you don't have to special-case code for
3793 Windows when dealing with symbolic links.
3795 @property uint linkAttributes() scope;
3798 alias stat_t = void*;
3801 $(BLUE This function is POSIX-Only.)
3803 The `stat` struct gotten from calling `stat`.
3805 @property stat_t statBuf() scope;
3808 else version (Windows)
3818 import std.datetime.systime : FILETIMEToSysTime;
3821 throw new FileException(path, "File does not exist");
3825 with (getFileAttributesWin(path))
3827 _size = makeUlong(nFileSizeLow, nFileSizeHigh);
3828 _timeCreated = FILETIMEToSysTime(&ftCreationTime);
3829 _timeLastAccessed = FILETIMEToSysTime(&ftLastAccessTime);
3830 _timeLastModified = FILETIMEToSysTime(&ftLastWriteTime);
3831 _attributes = dwFileAttributes;
3835 private this(string path, WIN32_FIND_DATAW *fd) @trusted
3837 import core.stdc.wchar_ : wcslen;
3838 import std.conv : to;
3839 import std.datetime.systime : FILETIMEToSysTime;
3840 import std.path : buildPath;
3842 fd.cFileName[$ - 1] = 0;
3844 size_t clength = wcslen(&fd.cFileName[0]);
3845 _name = buildPath(path, fd.cFileName[0 .. clength].to!string);
3846 _size = (cast(ulong) fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
3847 _timeCreated = FILETIMEToSysTime(&fd.ftCreationTime);
3848 _timeLastAccessed = FILETIMEToSysTime(&fd.ftLastAccessTime);
3849 _timeLastModified = FILETIMEToSysTime(&fd.ftLastWriteTime);
3850 _attributes = fd.dwFileAttributes;
3853 @property string name() const pure nothrow return scope
3858 @property bool isDir() const pure nothrow scope
3860 return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
3863 @property bool isFile() const pure nothrow scope
3865 //Are there no options in Windows other than directory and file?
3866 //If there are, then this probably isn't the best way to determine
3867 //whether this DirEntry is a file or not.
3871 @property bool isSymlink() const pure nothrow scope
3873 return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
3876 @property ulong size() const pure nothrow scope
3881 @property SysTime timeCreated() const pure nothrow scope
3883 return cast(SysTime)_timeCreated;
3886 @property SysTime timeLastAccessed() const pure nothrow scope
3888 return cast(SysTime)_timeLastAccessed;
3891 @property SysTime timeLastModified() const pure nothrow scope
3893 return cast(SysTime)_timeLastModified;
3896 @property uint attributes() const pure nothrow scope
3901 @property uint linkAttributes() const pure nothrow scope
3907 string _name; /// The file or directory represented by this DirEntry.
3909 SysTime _timeCreated; /// The time when the file was created.
3910 SysTime _timeLastAccessed; /// The time when the file was last accessed.
3911 SysTime _timeLastModified; /// The time when the file was last modified.
3913 ulong _size; /// The size of the file in bytes.
3914 uint _attributes; /// The file attributes from WIN32_FIND_DATAW.
3917 else version (Posix)
3928 throw new FileException(path, "File does not exist");
3937 private this(string path, core.sys.posix.dirent.dirent* fd) @safe
3939 import std.path : buildPath;
3941 static if (is(typeof(fd.d_namlen)))
3942 immutable len = fd.d_namlen;
3944 immutable len = (() @trusted => core.stdc.string.strlen(fd.d_name.ptr))();
3946 _name = buildPath(path, (() @trusted => fd.d_name.ptr[0 .. len])());
3951 //fd_d_type doesn't work for all file systems,
3952 //in which case the result is DT_UNKOWN. But we
3953 //can determine the correct type from lstat, so
3954 //we'll only set the dtype here if we could
3955 //correctly determine it (not lstat in the case
3956 //of DT_UNKNOWN in case we don't ever actually
3957 //need the dtype, thus potentially avoiding the
3958 //cost of calling lstat).
3959 static if (__traits(compiles, fd.d_type != DT_UNKNOWN))
3961 if (fd.d_type != DT_UNKNOWN)
3971 // e.g. Solaris does not have the d_type member
3976 @property string name() const pure nothrow return scope
3981 @property bool isDir() scope
3983 _ensureStatOrLStatDone();
3985 return (_statBuf.st_mode & S_IFMT) == S_IFDIR;
3988 @property bool isFile() scope
3990 _ensureStatOrLStatDone();
3992 return (_statBuf.st_mode & S_IFMT) == S_IFREG;
3995 @property bool isSymlink() scope
3999 return (_lstatMode & S_IFMT) == S_IFLNK;
4002 @property ulong size() scope
4005 return _statBuf.st_size;
4008 @property SysTime timeStatusChanged() scope
4012 return statTimeToStdTime!'c'(_statBuf);
4015 @property SysTime timeLastAccessed() scope
4019 return statTimeToStdTime!'a'(_statBuf);
4022 @property SysTime timeLastModified() scope
4026 return statTimeToStdTime!'m'(_statBuf);
4029 @property uint attributes() scope
4033 return _statBuf.st_mode;
4036 @property uint linkAttributes() scope
4043 @property stat_t statBuf() scope
4052 This is to support lazy evaluation, because doing stat's is
4053 expensive and not always needed.
4055 void _ensureStatDone() @trusted scope
4057 import std.exception : enforce;
4062 enforce(stat(_name.tempCString(), &_statBuf) == 0,
4063 "Failed to stat file `" ~ _name ~ "'");
4069 This is to support lazy evaluation, because doing stat's is
4070 expensive and not always needed.
4072 Try both stat and lstat for isFile and isDir
4073 to detect broken symlinks.
4075 void _ensureStatOrLStatDone() @trusted scope
4080 if (stat(_name.tempCString(), &_statBuf) != 0)
4084 _statBuf = stat_t.init;
4085 _statBuf.st_mode = S_IFLNK;
4094 This is to support lazy evaluation, because doing stat's is
4095 expensive and not always needed.
4097 void _ensureLStatDone() @trusted scope
4099 import std.exception : enforce;
4104 stat_t statbuf = void;
4105 enforce(lstat(_name.tempCString(), &statbuf) == 0,
4106 "Failed to stat file `" ~ _name ~ "'");
4108 _lstatMode = statbuf.st_mode;
4114 string _name; /// The file or directory represented by this DirEntry.
4116 stat_t _statBuf = void; /// The result of stat().
4117 uint _lstatMode; /// The stat mode from lstat().
4118 ubyte _dType; /// The type of the file.
4120 bool _didLStat = false; /// Whether lstat() has been called for this DirEntry.
4121 bool _didStat = false; /// Whether stat() has been called for this DirEntry.
4122 bool _dTypeSet = false; /// Whether the dType of the file has been set.
4130 if ("C:\\Program Files\\".exists)
4132 auto de = DirEntry("C:\\Program Files\\");
4135 assert(!de.isSymlink);
4138 if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
4140 auto de = DirEntry("C:\\Documents and Settings\\");
4141 assert(de.isSymlink);
4144 if ("C:\\Windows\\system.ini".exists)
4146 auto de = DirEntry("C:\\Windows\\system.ini");
4149 assert(!de.isSymlink);
4152 else version (Posix)
4154 import std.exception : assertThrown;
4156 if (system_directory.exists)
4159 auto de = DirEntry(system_directory);
4162 assert(!de.isSymlink);
4165 immutable symfile = deleteme ~ "_slink\0";
4166 scope(exit) if (symfile.exists) symfile.remove();
4168 core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
4171 auto de = DirEntry(symfile);
4174 assert(de.isSymlink);
4178 core.sys.posix.unistd.symlink((deleteme ~ "_broken_symlink\0").ptr, symfile.ptr);
4181 // https://issues.dlang.org/show_bug.cgi?id=8298
4182 DirEntry de = DirEntry(symfile);
4186 assert(de.isSymlink);
4187 assertThrown(de.size);
4188 assertThrown(de.timeStatusChanged);
4189 assertThrown(de.timeLastAccessed);
4190 assertThrown(de.timeLastModified);
4191 assertThrown(de.attributes);
4192 assertThrown(de.statBuf);
4193 assert(symfile.exists);
4198 if (system_file.exists)
4200 auto de = DirEntry(system_file);
4203 assert(!de.isSymlink);
4208 alias PreserveAttributes = Flag!"preserveAttributes";
4212 /// Defaults to `Yes.preserveAttributes` on Windows, and the opposite on all other platforms.
4213 PreserveAttributes preserveAttributesDefault;
4215 else version (Windows)
4217 enum preserveAttributesDefault = Yes.preserveAttributes;
4221 enum preserveAttributesDefault = No.preserveAttributes;
4224 /***************************************************
4225 Copy file `from` _to file `to`. File timestamps are preserved.
4226 File attributes are preserved, if `preserve` equals `Yes.preserveAttributes`.
4227 On Windows only `Yes.preserveAttributes` (the default on Windows) is supported.
4228 If the target file exists, it is overwritten.
4231 from = string or range of characters representing the existing file name
4232 to = string or range of characters representing the target file name
4233 preserve = whether to _preserve the file attributes
4235 Throws: $(LREF FileException) on error.
4237 void copy(RF, RT)(RF from, RT to, PreserveAttributes preserve = preserveAttributesDefault)
4238 if (isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) && !isConvertibleToString!RF &&
4239 isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) && !isConvertibleToString!RT)
4241 // Place outside of @trusted block
4242 auto fromz = from.tempCString!FSChar();
4243 auto toz = to.tempCString!FSChar();
4245 static if (isNarrowString!RF && is(immutable ElementEncodingType!RF == immutable char))
4248 enum string f = null;
4250 static if (isNarrowString!RT && is(immutable ElementEncodingType!RT == immutable char))
4253 enum string t = null;
4255 copyImpl(f, t, fromz, toz, preserve);
4259 void copy(RF, RT)(auto ref RF from, auto ref RT to, PreserveAttributes preserve = preserveAttributesDefault)
4260 if (isConvertibleToString!RF || isConvertibleToString!RT)
4262 import std.meta : staticMap;
4263 alias Types = staticMap!(convertToString, RF, RT);
4264 copy!Types(from, to, preserve);
4270 auto source = deleteme ~ "source";
4271 auto target = deleteme ~ "target";
4272 auto targetNonExistent = deleteme ~ "target2";
4274 scope(exit) source.remove, target.remove, targetNonExistent.remove;
4276 source.write("source");
4277 target.write("target");
4279 assert(target.readText == "target");
4281 source.copy(target);
4282 assert(target.readText == "source");
4284 source.copy(targetNonExistent);
4285 assert(targetNonExistent.readText == "source");
4288 // https://issues.dlang.org/show_bug.cgi?id=15319
4291 assert(__traits(compiles, copy("from.txt", "to.txt")));
4294 private void copyImpl(scope const(char)[] f, scope const(char)[] t,
4295 scope const(FSChar)* fromz, scope const(FSChar)* toz,
4296 PreserveAttributes preserve) @trusted
4300 assert(preserve == Yes.preserveAttributes);
4301 immutable result = CopyFileW(fromz, toz, false);
4304 import core.stdc.wchar_ : wcslen;
4305 import std.conv : to;
4306 import std.format : format;
4309 Reference resources: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-copyfilew
4310 Because OS copyfilew handles both source and destination paths,
4311 the GetLastError does not accurately locate whether the error is for the source or destination.
4314 f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]);
4316 t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
4318 throw new FileException(format!"Copy from %s to %s"(f, t));
4321 else version (Posix)
4323 static import core.stdc.stdio;
4324 import std.conv : to, octal;
4326 immutable fdr = core.sys.posix.fcntl.open(fromz, O_RDONLY);
4327 cenforce(fdr != -1, f, fromz);
4328 scope(exit) core.sys.posix.unistd.close(fdr);
4330 stat_t statbufr = void;
4331 cenforce(fstat(fdr, &statbufr) == 0, f, fromz);
4332 //cenforce(core.sys.posix.sys.stat.fstat(fdr, &statbufr) == 0, f, fromz);
4334 immutable fdw = core.sys.posix.fcntl.open(toz,
4335 O_CREAT | O_WRONLY, octal!666);
4336 cenforce(fdw != -1, t, toz);
4338 scope(failure) core.sys.posix.unistd.close(fdw);
4340 stat_t statbufw = void;
4341 cenforce(fstat(fdw, &statbufw) == 0, t, toz);
4342 if (statbufr.st_dev == statbufw.st_dev && statbufr.st_ino == statbufw.st_ino)
4343 throw new FileException(t, "Source and destination are the same file");
4346 scope(failure) core.stdc.stdio.remove(toz);
4348 scope(failure) core.sys.posix.unistd.close(fdw);
4349 cenforce(ftruncate(fdw, 0) == 0, t, toz);
4351 auto BUFSIZ = 4096u * 16;
4352 auto buf = core.stdc.stdlib.malloc(BUFSIZ);
4356 buf = core.stdc.stdlib.malloc(BUFSIZ);
4359 import core.exception : onOutOfMemoryError;
4360 onOutOfMemoryError();
4363 scope(exit) core.stdc.stdlib.free(buf);
4365 for (auto size = statbufr.st_size; size; )
4367 immutable toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size;
4369 core.sys.posix.unistd.read(fdr, buf, toxfer) == toxfer
4370 && core.sys.posix.unistd.write(fdw, buf, toxfer) == toxfer,
4372 assert(size >= toxfer);
4376 cenforce(fchmod(fdw, to!mode_t(statbufr.st_mode)) == 0, f, fromz);
4379 cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz);
4381 setTimesImpl(t, toz, statbufr.statTimeToStdTime!'a', statbufr.statTimeToStdTime!'m');
4385 // https://issues.dlang.org/show_bug.cgi?id=14817
4388 import std.algorithm, std.file;
4389 auto t1 = deleteme, t2 = deleteme~"2";
4390 scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
4393 assert(readText(t2) == "11");
4396 assert(readText(t2) == "2");
4398 import std.utf : byChar;
4399 copy(t1.byChar, t2.byChar);
4400 assert(readText(t2.byChar) == "2");
4402 // https://issues.dlang.org/show_bug.cgi?id=20370
4404 assert(t1.timeLastModified == t2.timeLastModified);
4405 else static if (is(typeof(&utimensat)) || is(typeof(&setattrlist)))
4406 assert(t1.timeLastModified == t2.timeLastModified);
4408 assert(abs(t1.timeLastModified - t2.timeLastModified) < dur!"usecs"(1));
4411 // https://issues.dlang.org/show_bug.cgi?id=11434
4412 @safe version (Posix) @safe unittest
4414 import std.conv : octal;
4415 auto t1 = deleteme, t2 = deleteme~"2";
4416 scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
4418 setAttributes(t1, octal!767);
4419 copy(t1, t2, Yes.preserveAttributes);
4420 assert(readText(t2) == "1");
4421 assert(getAttributes(t2) == octal!100767);
4424 // https://issues.dlang.org/show_bug.cgi?id=15865
4427 import std.exception : assertThrown;
4430 scope(exit) t.remove();
4431 assertThrown!FileException(copy(t, t));
4432 assert(readText(t) == "a");
4435 // https://issues.dlang.org/show_bug.cgi?id=19834
4436 version (Windows) @safe unittest
4438 import std.exception : collectException;
4439 import std.algorithm.searching : startsWith;
4440 import std.format : format;
4444 auto ex = collectException(copy(f, t));
4445 assert(ex.msg.startsWith(format!"Copy from %s to %s"(f, t)));
4449 Remove directory and all of its content and subdirectories,
4453 pathname = the path of the directory to completely remove
4454 de = The $(LREF DirEntry) to remove
4457 $(LREF FileException) if there is an error (including if the given
4458 file is not a directory).
4460 void rmdirRecurse(scope const(char)[] pathname) @safe
4462 //No references to pathname will be kept after rmdirRecurse,
4463 //so the cast is safe
4464 rmdirRecurse(DirEntry((() @trusted => cast(string) pathname)()));
4468 void rmdirRecurse(ref DirEntry de) @safe
4471 throw new FileException(de.name, "Not a directory");
4482 // dirEntries is @system because it uses a DirIterator with a
4483 // RefCounted variable, but here, no references to the payload is
4484 // escaped to the outside, so this should be @trusted
4486 // all children, recursively depth-first
4487 foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false))
4489 attrIsDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name);
4498 //Note, without this overload, passing an RValue DirEntry still works, but
4499 //actually fully reconstructs a DirEntry inside the
4500 //"rmdirRecurse(in char[] pathname)" implementation. That is needlessly
4502 //A DirEntry is a bit big (72B), so keeping the "by ref" signature is desirable.
4503 void rmdirRecurse(DirEntry de) @safe
4511 import std.path : buildPath;
4513 auto dir = deleteme.buildPath("a", "b", "c");
4518 deleteme.rmdirRecurse;
4519 assert(!dir.exists);
4520 assert(!deleteme.exists);
4523 version (Windows) @system unittest
4525 import std.exception : enforce;
4526 auto d = deleteme ~ r".dir\a\b\c\d\e\f\g";
4528 rmdirRecurse(deleteme ~ ".dir");
4529 enforce(!exists(deleteme ~ ".dir"));
4532 version (Posix) @system unittest
4534 import std.exception : enforce, collectException;
4536 collectException(rmdirRecurse(deleteme));
4537 auto d = deleteme~"/a/b/c/d/e/f/g";
4538 enforce(collectException(mkdir(d)));
4540 core.sys.posix.unistd.symlink((deleteme~"/a/b/c\0").ptr,
4541 (deleteme~"/link\0").ptr);
4542 rmdirRecurse(deleteme~"/link");
4544 rmdirRecurse(deleteme);
4545 enforce(!exists(deleteme));
4547 d = deleteme~"/a/b/c/d/e/f/g";
4549 const linkTarget = deleteme ~ "/link";
4550 symlink(deleteme ~ "/a/b/c", linkTarget);
4551 rmdirRecurse(deleteme);
4552 enforce(!exists(deleteme));
4560 (cast(byte[]) buf)[] = 3;
4561 string unit_file = deleteme ~ "-unittest_write.tmp";
4562 if (exists(unit_file)) remove(unit_file);
4563 write(unit_file, buf);
4564 void[] buf2 = read(unit_file);
4565 assert(buf == buf2);
4567 string unit2_file = deleteme ~ "-unittest_write2.tmp";
4568 copy(unit_file, unit2_file);
4569 buf2 = read(unit2_file);
4570 assert(buf == buf2);
4573 assert(!exists(unit_file));
4575 assert(!exists(unit2_file));
4579 * Dictates directory spanning policy for $(D_PARAM dirEntries) (see below).
4583 /** Only spans one directory. */
4585 /** Spans the directory in
4586 $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Post-order,
4587 _depth-first $(B post)-order), i.e. the content of any
4588 subdirectory is spanned before that subdirectory itself. Useful
4589 e.g. when recursively deleting files. */
4591 /** Spans the directory in
4592 $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Pre-order, depth-first
4593 $(B pre)-order), i.e. the content of any subdirectory is spanned
4594 right after that subdirectory itself.
4596 Note that `SpanMode.breadth` will not result in all directory
4597 members occurring before any subdirectory members, i.e. it is not
4599 $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search,
4600 _breadth-first traversal).
4608 import std.algorithm.comparison : equal;
4609 import std.algorithm.iteration : map;
4610 import std.algorithm.sorting : sort;
4611 import std.array : array;
4612 import std.path : buildPath, relativePath;
4614 auto root = deleteme ~ "root";
4615 scope(exit) root.rmdirRecurse;
4618 root.buildPath("animals").mkdir;
4619 root.buildPath("animals", "cat").mkdir;
4621 alias removeRoot = (return scope e) => e.relativePath(root);
4623 assert(root.dirEntries(SpanMode.depth).map!removeRoot.equal(
4624 [buildPath("animals", "cat"), "animals"]));
4626 assert(root.dirEntries(SpanMode.breadth).map!removeRoot.equal(
4627 ["animals", buildPath("animals", "cat")]));
4629 root.buildPath("plants").mkdir;
4631 assert(root.dirEntries(SpanMode.shallow).array.sort.map!removeRoot.equal(
4632 ["animals", "plants"]));
4635 private struct DirIteratorImpl
4639 // Whether we should follow symlinked directories while iterating.
4640 // It also indicates whether we should avoid functions which call
4641 // stat (since we should only need lstat in this case and it would
4642 // be more efficient to not call stat in addition to lstat).
4643 bool _followSymlink;
4646 DirEntry[] _stashed; //used in depth first mode
4649 void pushExtra(DirEntry de)
4657 return _stashed.length != 0;
4671 WIN32_FIND_DATAW _findinfo;
4678 bool stepIn(string directory) @safe
4680 import std.path : chainPath;
4681 auto searchPattern = chainPath(directory, "*.*");
4683 static auto trustedFindFirstFileW(typeof(searchPattern) pattern, WIN32_FIND_DATAW* findinfo) @trusted
4685 return FindFirstFileW(pattern.tempCString!FSChar(), findinfo);
4688 HANDLE h = trustedFindFirstFileW(searchPattern, &_findinfo);
4689 cenforce(h != INVALID_HANDLE_VALUE, directory);
4690 _stack ~= DirHandle(directory, h);
4691 return toNext(false, &_findinfo);
4696 if (_stack.length == 0)
4698 return toNext(true, &_findinfo);
4701 bool toNext(bool fetch, WIN32_FIND_DATAW* findinfo) @trusted
4703 import core.stdc.wchar_ : wcscmp;
4707 if (FindNextFileW(_stack[$-1].h, findinfo) == FALSE)
4713 while (wcscmp(&findinfo.cFileName[0], ".") == 0 ||
4714 wcscmp(&findinfo.cFileName[0], "..") == 0)
4715 if (FindNextFileW(_stack[$-1].h, findinfo) == FALSE)
4720 _cur = DirEntry(_stack[$-1].dirpath, findinfo);
4724 void popDirStack() @trusted
4726 assert(_stack.length != 0);
4727 FindClose(_stack[$-1].h);
4731 void releaseDirStack() @trusted
4739 return _followSymlink ? _cur.isDir : _cur.isDir && !_cur.isSymlink;
4742 else version (Posix)
4750 bool stepIn(string directory)
4752 static auto trustedOpendir(string dir) @trusted
4754 return opendir(dir.tempCString());
4757 auto h = directory.length ? trustedOpendir(directory) : trustedOpendir(".");
4758 cenforce(h, directory);
4759 _stack ~= (DirHandle(directory, h));
4763 bool next() @trusted
4765 if (_stack.length == 0)
4768 for (dirent* fdata; (fdata = readdir(_stack[$-1].h)) != null; )
4770 // Skip "." and ".."
4771 if (core.stdc.string.strcmp(&fdata.d_name[0], ".") &&
4772 core.stdc.string.strcmp(&fdata.d_name[0], ".."))
4774 _cur = DirEntry(_stack[$-1].dirpath, fdata);
4783 void popDirStack() @trusted
4785 assert(_stack.length != 0);
4786 closedir(_stack[$-1].h);
4790 void releaseDirStack() @trusted
4798 return _followSymlink ? _cur.isDir : attrIsDir(_cur.linkAttributes);
4802 this(R)(R pathname, SpanMode mode, bool followSymlink)
4803 if (isInputRange!R && isSomeChar!(ElementEncodingType!R))
4806 _followSymlink = followSymlink;
4808 static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
4809 alias pathnameStr = pathname;
4812 import std.array : array;
4813 string pathnameStr = pathname.array;
4815 if (stepIn(pathnameStr))
4817 if (_mode == SpanMode.depth)
4820 auto thisDir = _cur;
4821 if (stepIn(_cur.name))
4831 @property bool empty()
4833 return _stashed.length == 0 && _stack.length == 0;
4836 @property DirEntry front()
4845 case SpanMode.depth:
4850 auto thisDir = _cur;
4851 if (stepIn(_cur.name))
4859 else if (hasExtra())
4862 case SpanMode.breadth:
4865 if (!stepIn(_cur.name))
4866 while (!empty && !next()){}
4869 while (!empty && !next()){}
4886 RefCounted!(DirIteratorImpl, RefCountedAutoInitialize.no) impl;
4887 this(string pathname, SpanMode mode, bool followSymlink) @trusted
4889 impl = typeof(impl)(pathname, mode, followSymlink);
4892 @property bool empty() { return impl.empty; }
4893 @property DirEntry front() { return impl.front; }
4894 void popFront() { impl.popFront(); }
4897 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
4898 of `DirEntry` that lazily iterates a given directory,
4899 also provides two ways of foreach iteration. The iteration variable can be of
4900 type `string` if only the name is needed, or `DirEntry`
4901 if additional details are needed. The span _mode dictates how the
4902 directory is traversed. The name of each iterated directory entry
4903 contains the absolute or relative _path (depending on _pathname).
4905 Note: The order of returned directory entries is as it is provided by the
4906 operating system / filesystem, and may not follow any particular sorting.
4909 path = The directory to iterate over.
4910 If empty, the current directory will be iterated.
4912 pattern = Optional string with wildcards, such as $(RED
4913 "*.d"). When present, it is used to filter the
4914 results by their file name. The supported wildcard
4915 strings are described under $(REF globMatch,
4918 mode = Whether the directory's sub-directories should be
4919 iterated in depth-first post-order ($(LREF depth)),
4920 depth-first pre-order ($(LREF breadth)), or not at all
4923 followSymlink = Whether symbolic links which point to directories
4924 should be treated as directories and their contents
4928 An $(REF_ALTTEXT input range, isInputRange,std,range,primitives) of
4932 $(LREF FileException) if the directory does not exist.
4935 --------------------
4936 // Iterate a directory in depth
4937 foreach (string name; dirEntries("destroy/me", SpanMode.depth))
4942 // Iterate the current directory in breadth
4943 foreach (string name; dirEntries("", SpanMode.breadth))
4948 // Iterate a directory and get detailed info about it
4949 foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth))
4951 writeln(e.name, "\t", e.size);
4954 // Iterate over all *.d files in current directory and all its subdirectories
4955 auto dFiles = dirEntries("", SpanMode.depth).filter!(f => f.name.endsWith(".d"));
4959 // Hook it up with std.parallelism to compile them all in parallel:
4960 foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread
4962 string cmd = "dmd -c " ~ d.name;
4964 std.process.executeShell(cmd);
4967 // Iterate over all D source files in current directory and all its
4969 auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
4972 --------------------
4974 auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
4976 return DirIterator(path, mode, followSymlink);
4979 /// Duplicate functionality of D1's `std.file.listdir()`:
4982 string[] listdir(string pathname)
4984 import std.algorithm;
4989 return std.file.dirEntries(pathname, SpanMode.shallow)
4990 .filter!(a => a.isFile)
4991 .map!((return a) => std.path.baseName(a.name))
4995 void main(string[] args)
4999 string[] files = listdir(args[1]);
5000 writefln("%s", files);
5006 import std.algorithm.comparison : equal;
5007 import std.algorithm.iteration : map;
5008 import std.algorithm.searching : startsWith;
5009 import std.array : array;
5010 import std.conv : to;
5011 import std.path : buildPath, absolutePath;
5012 import std.file : dirEntries;
5013 import std.process : thisProcessID;
5014 import std.range.primitives : walkLength;
5017 string testdir = deleteme; // This has to be an absolute path when
5018 // called from a shared library on Android,
5021 string testdir = tempDir.buildPath("deleteme.dmd.unittest.std.file" ~ to!string(thisProcessID));
5022 mkdirRecurse(buildPath(testdir, "somedir"));
5023 scope(exit) rmdirRecurse(testdir);
5024 write(buildPath(testdir, "somefile"), null);
5025 write(buildPath(testdir, "somedir", "somedeepfile"), null);
5027 // testing range interface
5028 size_t equalEntries(string relpath, SpanMode mode)
5030 import std.exception : enforce;
5031 auto len = enforce(walkLength(dirEntries(absolutePath(relpath), mode)));
5032 assert(walkLength(dirEntries(relpath, mode)) == len);
5034 map!((return a) => absolutePath(a.name))(dirEntries(relpath, mode)),
5035 map!(a => a.name)(dirEntries(absolutePath(relpath), mode))));
5039 assert(equalEntries(testdir, SpanMode.shallow) == 2);
5040 assert(equalEntries(testdir, SpanMode.depth) == 3);
5041 assert(equalEntries(testdir, SpanMode.breadth) == 3);
5044 foreach (string name; dirEntries(testdir, SpanMode.breadth))
5047 assert(name.startsWith(testdir));
5049 foreach (DirEntry e; dirEntries(absolutePath(testdir), SpanMode.breadth))
5052 assert(e.isFile || e.isDir, e.name);
5055 // https://issues.dlang.org/show_bug.cgi?id=7264
5056 foreach (string name; dirEntries(testdir, "*.d", SpanMode.breadth))
5060 foreach (entry; dirEntries(testdir, SpanMode.breadth))
5062 static assert(is(typeof(entry) == DirEntry));
5064 // https://issues.dlang.org/show_bug.cgi?id=7138
5065 auto a = array(dirEntries(testdir, SpanMode.shallow));
5067 // https://issues.dlang.org/show_bug.cgi?id=11392
5068 auto dFiles = dirEntries(testdir, SpanMode.shallow);
5069 foreach (d; dFiles){}
5071 // https://issues.dlang.org/show_bug.cgi?id=15146
5072 dirEntries("", SpanMode.shallow).walkLength();
5076 auto dirEntries(string path, string pattern, SpanMode mode,
5077 bool followSymlink = true)
5079 import std.algorithm.iteration : filter;
5080 import std.path : globMatch, baseName;
5082 bool f(DirEntry de) { return globMatch(baseName(de.name), pattern); }
5083 return filter!f(DirIterator(path, mode, followSymlink));
5088 import std.stdio : writefln;
5089 immutable dpath = deleteme ~ "_dir";
5090 immutable fpath = deleteme ~ "_file";
5091 immutable sdpath = deleteme ~ "_sdir";
5092 immutable sfpath = deleteme ~ "_sfile";
5095 if (dpath.exists) rmdirRecurse(dpath);
5096 if (fpath.exists) remove(fpath);
5097 if (sdpath.exists) remove(sdpath);
5098 if (sfpath.exists) remove(sfpath);
5102 write(fpath, "hello world");
5105 core.sys.posix.unistd.symlink((dpath ~ '\0').ptr, (sdpath ~ '\0').ptr);
5106 core.sys.posix.unistd.symlink((fpath ~ '\0').ptr, (sfpath ~ '\0').ptr);
5109 static struct Flags { bool dir, file, link; }
5110 auto tests = [dpath : Flags(true), fpath : Flags(false, true)];
5113 tests[sdpath] = Flags(true, false, true);
5114 tests[sfpath] = Flags(false, true, true);
5117 auto past = Clock.currTime() - 2.seconds;
5118 auto future = past + 4.seconds;
5120 foreach (path, flags; tests)
5122 auto de = DirEntry(path);
5123 assert(de.name == path);
5124 assert(de.isDir == flags.dir);
5125 assert(de.isFile == flags.file);
5126 assert(de.isSymlink == flags.link);
5128 assert(de.isDir == path.isDir);
5129 assert(de.isFile == path.isFile);
5130 assert(de.isSymlink == path.isSymlink);
5131 assert(de.size == path.getSize());
5132 assert(de.attributes == getAttributes(path));
5133 assert(de.linkAttributes == getLinkAttributes(path));
5135 scope(failure) writefln("[%s] [%s] [%s] [%s]", past, de.timeLastAccessed, de.timeLastModified, future);
5136 assert(de.timeLastAccessed > past);
5137 assert(de.timeLastAccessed < future);
5138 assert(de.timeLastModified > past);
5139 assert(de.timeLastModified < future);
5141 assert(attrIsDir(de.attributes) == flags.dir);
5142 assert(attrIsDir(de.linkAttributes) == (flags.dir && !flags.link));
5143 assert(attrIsFile(de.attributes) == flags.file);
5144 assert(attrIsFile(de.linkAttributes) == (flags.file && !flags.link));
5145 assert(!attrIsSymlink(de.attributes));
5146 assert(attrIsSymlink(de.linkAttributes) == flags.link);
5150 assert(de.timeCreated > past);
5151 assert(de.timeCreated < future);
5153 else version (Posix)
5155 assert(de.timeStatusChanged > past);
5156 assert(de.timeStatusChanged < future);
5157 assert(de.attributes == de.statBuf.st_mode);
5162 // Make sure that dirEntries does not butcher Unicode file names
5163 // https://issues.dlang.org/show_bug.cgi?id=17962
5166 import std.algorithm.comparison : equal;
5167 import std.algorithm.iteration : map;
5168 import std.algorithm.sorting : sort;
5169 import std.array : array;
5170 import std.path : buildPath;
5171 import std.uni : normalize;
5173 // The Unicode normalization is required to make the tests pass on Mac OS X.
5174 auto dir = deleteme ~ normalize("𐐷");
5175 scope(exit) if (dir.exists) rmdirRecurse(dir);
5177 auto files = ["Hello World",
5179 "さいごの果実.txt"].map!(a => buildPath(dir, normalize(a)))().array();
5181 foreach (file; files)
5182 write(file, "nothing");
5184 auto result = dirEntries(dir, SpanMode.shallow).map!((return a) => a.name.normalize()).array();
5187 assert(equal(files, result));
5190 // https://issues.dlang.org/show_bug.cgi?id=21250
5193 import std.exception : assertThrown;
5194 assertThrown!Exception(dirEntries("237f5babd6de21f40915826699582e36", "*.bin", SpanMode.depth));
5198 * Reads a file line by line and parses the line into a single value or a
5199 * $(REF Tuple, std,typecons) of values depending on the length of `Types`.
5200 * The lines are parsed using the specified format string. The format string is
5201 * passed to $(REF formattedRead, std,_format), and therefore must conform to the
5202 * _format string specification outlined in $(MREF std, _format).
5205 * Types = the types that each of the elements in the line should be returned as
5206 * filename = the name of the file to read
5207 * format = the _format string to use when reading
5210 * If only one type is passed, then an array of that type. Otherwise, an
5211 * array of $(REF Tuple, std,typecons)s.
5214 * `Exception` if the format string is malformed. Also, throws `Exception`
5215 * if any of the lines in the file are not fully consumed by the call
5216 * to $(REF formattedRead, std,_format). Meaning that no empty lines or lines
5217 * with extra characters are allowed.
5219 Select!(Types.length == 1, Types[0][], Tuple!(Types)[])
5220 slurp(Types...)(string filename, scope const(char)[] format)
5222 import std.array : appender;
5223 import std.conv : text;
5224 import std.exception : enforce;
5225 import std.format.read : formattedRead;
5226 import std.stdio : File;
5227 import std.string : stripRight;
5229 auto app = appender!(typeof(return))();
5230 ElementType!(typeof(return)) toAdd;
5231 auto f = File(filename);
5232 scope(exit) f.close();
5233 foreach (line; f.byLine())
5235 formattedRead(line, format, &toAdd);
5236 enforce(line.stripRight("\r").empty,
5237 text("Trailing characters at the end of line: `", line,
5247 import std.typecons : tuple;
5251 assert(exists(deleteme));
5255 write(deleteme, "12 12.25\n345 1.125"); // deleteme is the name of a temporary file
5257 // Load file; each line is an int followed by comma, whitespace and a
5259 auto a = slurp!(int, double)(deleteme, "%s %s");
5260 assert(a.length == 2);
5261 assert(a[0] == tuple(12, 12.25));
5262 assert(a[1] == tuple(345, 1.125));
5267 import std.typecons : tuple;
5271 assert(exists(deleteme));
5274 write(deleteme, "10\r\n20");
5275 assert(slurp!(int)(deleteme, "%d") == [10, 20]);
5280 Returns the path to a directory for temporary files.
5282 The return value of the function is cached, so the procedures described
5283 below will only be performed the first time the function is called. All
5284 subsequent runs will return the same string, regardless of whether
5285 environment variables and directory structures have changed in the
5288 The POSIX `tempDir` algorithm is inspired by Python's
5289 $(LINK2 http://docs.python.org/library/tempfile.html#tempfile.tempdir, `tempfile.tempdir`).
5292 On Windows, this function returns the result of calling the Windows API function
5293 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992.aspx, `GetTempPath`).
5295 On POSIX platforms, it searches through the following list of directories
5296 and returns the first one which is found to exist:
5298 $(LI The directory given by the `TMPDIR` environment variable.)
5299 $(LI The directory given by the `TEMP` environment variable.)
5300 $(LI The directory given by the `TMP` environment variable.)
5306 On all platforms, `tempDir` returns `"."` on failure, representing
5307 the current working directory.
5309 string tempDir() @trusted
5311 static string cache;
5316 import std.conv : to;
5317 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992(v=vs.85).aspx
5318 wchar[MAX_PATH + 2] buf;
5319 DWORD len = GetTempPathW(buf.length, buf.ptr);
5320 if (len) cache = buf[0 .. len].to!string;
5322 else version (Posix)
5324 import std.process : environment;
5325 // This function looks through the list of alternative directories
5326 // and returns the first one which exists and is a directory.
5327 static string findExistingDir(T...)(lazy T alternatives)
5329 foreach (dir; alternatives)
5330 if (!dir.empty && exists(dir)) return dir;
5334 cache = findExistingDir(environment.get("TMPDIR"),
5335 environment.get("TEMP"),
5336 environment.get("TMP"),
5341 else static assert(false, "Unsupported platform");
5343 if (cache is null) cache = getcwd();
5351 import std.ascii : letters;
5352 import std.conv : to;
5353 import std.path : buildPath;
5354 import std.random : randomSample;
5355 import std.utf : byCodeUnit;
5357 // random id with 20 letters
5358 auto id = letters.byCodeUnit.randomSample(20).to!string;
5359 auto myFile = tempDir.buildPath(id ~ "my_tmp_file");
5360 scope(exit) myFile.remove;
5362 myFile.write("hello");
5363 assert(myFile.readText == "hello");
5367 Returns the available disk space based on a given path.
5368 On Windows, `path` must be a directory; on POSIX systems, it can be a file or directory.
5371 path = on Windows, it must be a directory; on POSIX it can be a file or directory
5373 Available space in bytes
5376 $(LREF FileException) in case of failure
5378 ulong getAvailableDiskSpace(scope const(char)[] path) @safe
5382 import core.sys.windows.winbase : GetDiskFreeSpaceExW;
5383 import core.sys.windows.winnt : ULARGE_INTEGER;
5384 import std.internal.cstring : tempCStringW;
5386 ULARGE_INTEGER freeBytesAvailable;
5387 auto err = () @trusted {
5388 return GetDiskFreeSpaceExW(path.tempCStringW(), &freeBytesAvailable, null, null);
5390 cenforce(err != 0, "Cannot get available disk space");
5392 return freeBytesAvailable.QuadPart;
5394 else version (Posix)
5396 import std.internal.cstring : tempCString;
5400 import core.sys.freebsd.sys.mount : statfs, statfs_t;
5403 auto err = () @trusted {
5404 return statfs(path.tempCString(), &stats);
5406 cenforce(err == 0, "Cannot get available disk space");
5408 return stats.f_bavail * stats.f_bsize;
5412 import core.sys.posix.sys.statvfs : statvfs, statvfs_t;
5415 auto err = () @trusted {
5416 return statvfs(path.tempCString(), &stats);
5418 cenforce(err == 0, "Cannot get available disk space");
5420 return stats.f_bavail * stats.f_frsize;
5423 else static assert(0, "Unsupported platform");
5429 import std.exception : assertThrown;
5431 auto space = getAvailableDiskSpace(".");
5434 assertThrown!FileException(getAvailableDiskSpace("ThisFileDoesNotExist123123"));