For obj-c stage-final re-use the checksum from the previous stage
[official-gcc.git] / libphobos / src / std / file.d
blob99530cbbeb0bb517d73396d125f893b6558f3ce4
1 // Written in the D programming language.
3 /**
4 Utilities for manipulating files and scanning directories. Functions
5 in this module handle files as a unit, e.g., read or write one _file
6 at a time. For opening files and manipulating them via handles refer
7 to module $(MREF std, stdio).
9 $(SCRIPT inhibitQuickIndex = 1;)
10 $(BOOKTABLE,
11 $(TR $(TH Category) $(TH Functions))
12 $(TR $(TD General) $(TD
13 $(LREF exists)
14 $(LREF isDir)
15 $(LREF isFile)
16 $(LREF isSymlink)
17 $(LREF rename)
18 $(LREF thisExePath)
20 $(TR $(TD Directories) $(TD
21 $(LREF chdir)
22 $(LREF dirEntries)
23 $(LREF getcwd)
24 $(LREF mkdir)
25 $(LREF mkdirRecurse)
26 $(LREF rmdir)
27 $(LREF rmdirRecurse)
28 $(LREF tempDir)
30 $(TR $(TD Files) $(TD
31 $(LREF append)
32 $(LREF copy)
33 $(LREF read)
34 $(LREF readText)
35 $(LREF remove)
36 $(LREF slurp)
37 $(LREF write)
39 $(TR $(TD Symlinks) $(TD
40 $(LREF symlink)
41 $(LREF readLink)
43 $(TR $(TD Attributes) $(TD
44 $(LREF attrIsDir)
45 $(LREF attrIsFile)
46 $(LREF attrIsSymlink)
47 $(LREF getAttributes)
48 $(LREF getLinkAttributes)
49 $(LREF getSize)
50 $(LREF setAttributes)
52 $(TR $(TD Timestamp) $(TD
53 $(LREF getTimes)
54 $(LREF getTimesWin)
55 $(LREF setTimes)
56 $(LREF timeLastModified)
58 $(TR $(TD Other) $(TD
59 $(LREF DirEntry)
60 $(LREF FileException)
61 $(LREF PreserveAttributes)
62 $(LREF SpanMode)
67 Copyright: Copyright Digital Mars 2007 - 2011.
68 See_Also: The $(HTTP ddili.org/ders/d.en/files.html, official tutorial) for an
69 introduction to working with files in D, module
70 $(MREF std, stdio) for opening files and manipulating them via handles,
71 and module $(MREF std, path) for manipulating path strings.
73 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
74 Authors: $(HTTP digitalmars.com, Walter Bright),
75 $(HTTP erdani.org, Andrei Alexandrescu),
76 Jonathan M Davis
77 Source: $(PHOBOSSRC std/_file.d)
79 module std.file;
81 import core.stdc.errno, core.stdc.stdlib, core.stdc.string;
82 import core.time : abs, dur, hnsecs, seconds;
84 import std.datetime.date : DateTime;
85 import std.datetime.systime : Clock, SysTime, unixTimeToStdTime;
86 import std.internal.cstring;
87 import std.meta;
88 import std.range.primitives;
89 import std.traits;
90 import std.typecons;
92 version (Windows)
94 import core.sys.windows.windows, std.windows.syserror;
96 else version (Posix)
98 import core.sys.posix.dirent, core.sys.posix.fcntl, core.sys.posix.sys.stat,
99 core.sys.posix.sys.time, core.sys.posix.unistd, core.sys.posix.utime;
101 else
102 static assert(false, "Module " ~ .stringof ~ " not implemented for this OS.");
104 // Character type used for operating system filesystem APIs
105 version (Windows)
107 private alias FSChar = wchar;
109 else version (Posix)
111 private alias FSChar = char;
113 else
114 static assert(0);
116 // Purposefully not documented. Use at your own risk
117 @property string deleteme() @safe
119 import std.conv : to;
120 import std.path : buildPath;
121 import std.process : thisProcessID;
123 static _deleteme = "deleteme.dmd.unittest.pid";
124 static _first = true;
126 if (_first)
128 _deleteme = buildPath(tempDir(), _deleteme) ~ to!string(thisProcessID);
129 _first = false;
132 return _deleteme;
135 version (unittest) private struct TestAliasedString
137 string get() @safe @nogc pure nothrow { return _s; }
138 alias get this;
139 @disable this(this);
140 string _s;
143 version (Android)
145 package enum system_directory = "/system/etc";
146 package enum system_file = "/system/etc/hosts";
148 else version (Posix)
150 package enum system_directory = "/usr/include";
151 package enum system_file = "/usr/include/assert.h";
156 Exception thrown for file I/O errors.
158 class FileException : Exception
160 import std.conv : text, to;
163 OS error code.
165 immutable uint errno;
167 private this(in char[] name, in char[] msg, string file, size_t line, uint errno) @safe pure
169 if (msg.empty)
170 super(name.idup, file, line);
171 else
172 super(text(name, ": ", msg), file, line);
174 this.errno = errno;
178 Constructor which takes an error message.
180 Params:
181 name = Name of file for which the error occurred.
182 msg = Message describing the error.
183 file = The _file where the error occurred.
184 line = The _line where the error occurred.
186 this(in char[] name, in char[] msg, string file = __FILE__, size_t line = __LINE__) @safe pure
188 this(name, msg, file, line, 0);
192 Constructor which takes the error number ($(LUCKY GetLastError)
193 in Windows, $(D_PARAM errno) in Posix).
195 Params:
196 name = Name of file for which the error occurred.
197 errno = The error number.
198 file = The _file where the error occurred.
199 Defaults to $(D __FILE__).
200 line = The _line where the error occurred.
201 Defaults to $(D __LINE__).
203 version (Windows) this(in char[] name,
204 uint errno = .GetLastError(),
205 string file = __FILE__,
206 size_t line = __LINE__) @safe
208 this(name, sysErrorString(errno), file, line, errno);
210 else version (Posix) this(in char[] name,
211 uint errno = .errno,
212 string file = __FILE__,
213 size_t line = __LINE__) @trusted
215 import std.exception : errnoString;
216 this(name, errnoString(errno), file, line, errno);
220 private T cenforce(T)(T condition, lazy const(char)[] name, string file = __FILE__, size_t line = __LINE__)
222 if (condition)
223 return condition;
224 version (Windows)
226 throw new FileException(name, .GetLastError(), file, line);
228 else version (Posix)
230 throw new FileException(name, .errno, file, line);
234 version (Windows)
235 @trusted
236 private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez,
237 string file = __FILE__, size_t line = __LINE__)
239 if (condition)
240 return condition;
241 if (!name)
243 import core.stdc.wchar_ : wcslen;
244 import std.conv : to;
246 auto len = namez ? wcslen(namez) : 0;
247 name = to!string(namez[0 .. len]);
249 throw new FileException(name, .GetLastError(), file, line);
252 version (Posix)
253 @trusted
254 private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez,
255 string file = __FILE__, size_t line = __LINE__)
257 if (condition)
258 return condition;
259 if (!name)
261 import core.stdc.string : strlen;
263 auto len = namez ? strlen(namez) : 0;
264 name = namez[0 .. len].idup;
266 throw new FileException(name, .errno, file, line);
269 @safe unittest
271 // issue 17102
274 cenforce(false, null, null,
275 __FILE__, __LINE__);
277 catch (FileException) {}
280 /* **********************************
281 * Basic File operations.
284 /********************************************
285 Read entire contents of file $(D name) and returns it as an untyped
286 array. If the file size is larger than $(D upTo), only $(D upTo)
287 bytes are _read.
289 Params:
290 name = string or range of characters representing the file _name
291 upTo = if present, the maximum number of bytes to _read
293 Returns: Untyped array of bytes _read.
295 Throws: $(LREF FileException) on error.
298 void[] read(R)(R name, size_t upTo = size_t.max)
299 if (isInputRange!R && isSomeChar!(ElementEncodingType!R) && !isInfinite!R &&
300 !isConvertibleToString!R)
302 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
303 return readImpl(name, name.tempCString!FSChar(), upTo);
304 else
305 return readImpl(null, name.tempCString!FSChar(), upTo);
309 @safe unittest
311 import std.utf : byChar;
312 scope(exit)
314 assert(exists(deleteme));
315 remove(deleteme);
318 write(deleteme, "1234"); // deleteme is the name of a temporary file
319 assert(read(deleteme, 2) == "12");
320 assert(read(deleteme.byChar) == "1234");
321 assert((cast(const(ubyte)[])read(deleteme)).length == 4);
324 /// ditto
325 void[] read(R)(auto ref R name, size_t upTo = size_t.max)
326 if (isConvertibleToString!R)
328 return read!(StringTypeOf!R)(name, upTo);
331 @safe unittest
333 static assert(__traits(compiles, read(TestAliasedString(null))));
336 version (Posix) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @trusted
338 import core.memory : GC;
339 import std.algorithm.comparison : min;
340 import std.array : uninitializedArray;
341 import std.conv : to;
343 // A few internal configuration parameters {
344 enum size_t
345 minInitialAlloc = 1024 * 4,
346 maxInitialAlloc = size_t.max / 2,
347 sizeIncrement = 1024 * 16,
348 maxSlackMemoryAllowed = 1024;
349 // }
351 immutable fd = core.sys.posix.fcntl.open(namez,
352 core.sys.posix.fcntl.O_RDONLY);
353 cenforce(fd != -1, name);
354 scope(exit) core.sys.posix.unistd.close(fd);
356 stat_t statbuf = void;
357 cenforce(fstat(fd, &statbuf) == 0, name, namez);
359 immutable initialAlloc = min(upTo, to!size_t(statbuf.st_size
360 ? min(statbuf.st_size + 1, maxInitialAlloc)
361 : minInitialAlloc));
362 void[] result = uninitializedArray!(ubyte[])(initialAlloc);
363 scope(failure) GC.free(result.ptr);
364 size_t size = 0;
366 for (;;)
368 immutable actual = core.sys.posix.unistd.read(fd, result.ptr + size,
369 min(result.length, upTo) - size);
370 cenforce(actual != -1, name, namez);
371 if (actual == 0) break;
372 size += actual;
373 if (size >= upTo) break;
374 if (size < result.length) continue;
375 immutable newAlloc = size + sizeIncrement;
376 result = GC.realloc(result.ptr, newAlloc, GC.BlkAttr.NO_SCAN)[0 .. newAlloc];
379 return result.length - size >= maxSlackMemoryAllowed
380 ? GC.realloc(result.ptr, size, GC.BlkAttr.NO_SCAN)[0 .. size]
381 : result[0 .. size];
385 version (Windows) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @safe
387 import core.memory : GC;
388 import std.algorithm.comparison : min;
389 import std.array : uninitializedArray;
390 static trustedCreateFileW(const(wchar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
391 SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition,
392 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted
394 return CreateFileW(namez, dwDesiredAccess, dwShareMode,
395 lpSecurityAttributes, dwCreationDisposition,
396 dwFlagsAndAttributes, hTemplateFile);
399 static trustedCloseHandle(HANDLE hObject) @trusted
401 return CloseHandle(hObject);
403 static trustedGetFileSize(HANDLE hFile, out ulong fileSize) @trusted
405 DWORD sizeHigh;
406 DWORD sizeLow = GetFileSize(hFile, &sizeHigh);
407 const bool result = sizeLow != INVALID_FILE_SIZE;
408 if (result)
409 fileSize = makeUlong(sizeLow, sizeHigh);
410 return result;
412 static trustedReadFile(HANDLE hFile, void *lpBuffer, ulong nNumberOfBytesToRead) @trusted
414 // Read by chunks of size < 4GB (Windows API limit)
415 ulong totalNumRead = 0;
416 while (totalNumRead != nNumberOfBytesToRead)
418 const uint chunkSize = min(nNumberOfBytesToRead - totalNumRead, 0xffff_0000);
419 DWORD numRead = void;
420 const result = ReadFile(hFile, lpBuffer + totalNumRead, chunkSize, &numRead, null);
421 if (result == 0 || numRead != chunkSize)
422 return false;
423 totalNumRead += chunkSize;
425 return true;
428 alias defaults =
429 AliasSeq!(GENERIC_READ,
430 FILE_SHARE_READ | FILE_SHARE_WRITE, (SECURITY_ATTRIBUTES*).init,
431 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
432 HANDLE.init);
433 auto h = trustedCreateFileW(namez, defaults);
435 cenforce(h != INVALID_HANDLE_VALUE, name, namez);
436 scope(exit) cenforce(trustedCloseHandle(h), name, namez);
437 ulong fileSize = void;
438 cenforce(trustedGetFileSize(h, fileSize), name, namez);
439 size_t size = min(upTo, fileSize);
440 auto buf = uninitializedArray!(ubyte[])(size);
442 scope(failure)
444 () @trusted { GC.free(buf.ptr); } ();
447 if (size)
448 cenforce(trustedReadFile(h, &buf[0], size), name, namez);
449 return buf[0 .. size];
452 version (linux) @safe unittest
454 // A file with "zero" length that doesn't have 0 length at all
455 auto s = std.file.readText("/proc/sys/kernel/osrelease");
456 assert(s.length > 0);
457 //writefln("'%s'", s);
460 @safe unittest
462 scope(exit) if (exists(deleteme)) remove(deleteme);
463 import std.stdio;
464 auto f = File(deleteme, "w");
465 f.write("abcd"); f.flush();
466 assert(read(deleteme) == "abcd");
469 /********************************************
470 Read and validates (using $(REF validate, std,utf)) a text file. $(D S)
471 can be a type of array of characters of any width and constancy. No
472 width conversion is performed; if the width of the characters in file
473 $(D name) is different from the width of elements of $(D S),
474 validation will fail.
476 Params:
477 name = string or range of characters representing the file _name
479 Returns: Array of characters read.
481 Throws: $(D FileException) on file error, $(D UTFException) on UTF
482 decoding error.
485 S readText(S = string, R)(R name)
486 if (isSomeString!S &&
487 (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
488 !isConvertibleToString!R)
490 import std.utf : validate;
491 static auto trustedCast(void[] buf) @trusted { return cast(S) buf; }
492 auto result = trustedCast(read(name));
493 validate(result);
494 return result;
498 @safe unittest
500 import std.exception : enforce;
501 write(deleteme, "abc"); // deleteme is the name of a temporary file
502 scope(exit) remove(deleteme);
503 string content = readText(deleteme);
504 enforce(content == "abc");
507 /// ditto
508 S readText(S = string, R)(auto ref R name)
509 if (isConvertibleToString!R)
511 return readText!(S, StringTypeOf!R)(name);
514 @safe unittest
516 static assert(__traits(compiles, readText(TestAliasedString(null))));
519 /*********************************************
520 Write $(D buffer) to file $(D name).
522 Creates the file if it does not already exist.
524 Params:
525 name = string or range of characters representing the file _name
526 buffer = data to be written to file
528 Throws: $(D FileException) on error.
530 See_also: $(REF toFile, std,stdio)
532 void write(R)(R name, const void[] buffer)
533 if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
534 !isConvertibleToString!R)
536 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
537 writeImpl(name, name.tempCString!FSChar(), buffer, false);
538 else
539 writeImpl(null, name.tempCString!FSChar(), buffer, false);
543 @system unittest
545 scope(exit)
547 assert(exists(deleteme));
548 remove(deleteme);
551 int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
552 write(deleteme, a); // deleteme is the name of a temporary file
553 assert(cast(int[]) read(deleteme) == a);
556 /// ditto
557 void write(R)(auto ref R name, const void[] buffer)
558 if (isConvertibleToString!R)
560 write!(StringTypeOf!R)(name, buffer);
563 @safe unittest
565 static assert(__traits(compiles, write(TestAliasedString(null), null)));
568 /*********************************************
569 Appends $(D buffer) to file $(D name).
571 Creates the file if it does not already exist.
573 Params:
574 name = string or range of characters representing the file _name
575 buffer = data to be appended to file
577 Throws: $(D FileException) on error.
579 void append(R)(R name, const void[] buffer)
580 if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
581 !isConvertibleToString!R)
583 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
584 writeImpl(name, name.tempCString!FSChar(), buffer, true);
585 else
586 writeImpl(null, name.tempCString!FSChar(), buffer, true);
590 @system unittest
592 scope(exit)
594 assert(exists(deleteme));
595 remove(deleteme);
598 int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
599 write(deleteme, a); // deleteme is the name of a temporary file
600 int[] b = [ 13, 21 ];
601 append(deleteme, b);
602 assert(cast(int[]) read(deleteme) == a ~ b);
605 /// ditto
606 void append(R)(auto ref R name, const void[] buffer)
607 if (isConvertibleToString!R)
609 append!(StringTypeOf!R)(name, buffer);
612 @safe unittest
614 static assert(__traits(compiles, append(TestAliasedString("foo"), [0, 1, 2, 3])));
617 // Posix implementation helper for write and append
619 version (Posix) private void writeImpl(const(char)[] name, const(FSChar)* namez,
620 in void[] buffer, bool append) @trusted
622 import std.conv : octal;
624 // append or write
625 auto mode = append ? O_CREAT | O_WRONLY | O_APPEND
626 : O_CREAT | O_WRONLY | O_TRUNC;
628 immutable fd = core.sys.posix.fcntl.open(namez, mode, octal!666);
629 cenforce(fd != -1, name, namez);
631 scope(failure) core.sys.posix.unistd.close(fd);
633 immutable size = buffer.length;
634 size_t sum, cnt = void;
635 while (sum != size)
637 cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30;
638 const numwritten = core.sys.posix.unistd.write(fd, buffer.ptr + sum, cnt);
639 if (numwritten != cnt)
640 break;
641 sum += numwritten;
643 cenforce(sum == size, name, namez);
645 cenforce(core.sys.posix.unistd.close(fd) == 0, name, namez);
648 // Windows implementation helper for write and append
650 version (Windows) private void writeImpl(const(char)[] name, const(FSChar)* namez,
651 in void[] buffer, bool append) @trusted
653 HANDLE h;
654 if (append)
656 alias defaults =
657 AliasSeq!(GENERIC_WRITE, 0, null, OPEN_ALWAYS,
658 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
659 HANDLE.init);
661 h = CreateFileW(namez, defaults);
662 cenforce(h != INVALID_HANDLE_VALUE, name, namez);
663 cenforce(SetFilePointer(h, 0, null, FILE_END) != INVALID_SET_FILE_POINTER,
664 name, namez);
666 else // write
668 alias defaults =
669 AliasSeq!(GENERIC_WRITE, 0, null, CREATE_ALWAYS,
670 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
671 HANDLE.init);
673 h = CreateFileW(namez, defaults);
674 cenforce(h != INVALID_HANDLE_VALUE, name, namez);
676 immutable size = buffer.length;
677 size_t sum, cnt = void;
678 DWORD numwritten = void;
679 while (sum != size)
681 cnt = (size - sum < 2^^30) ? (size - sum) : 2^^30;
682 WriteFile(h, buffer.ptr + sum, cast(uint) cnt, &numwritten, null);
683 if (numwritten != cnt)
684 break;
685 sum += numwritten;
687 cenforce(sum == size && CloseHandle(h), name, namez);
690 /***************************************************
691 * Rename file $(D from) _to $(D to).
692 * If the target file exists, it is overwritten.
693 * Params:
694 * from = string or range of characters representing the existing file name
695 * to = string or range of characters representing the target file name
696 * Throws: $(D FileException) on error.
698 void rename(RF, RT)(RF from, RT to)
699 if ((isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) || isSomeString!RF)
700 && !isConvertibleToString!RF &&
701 (isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) || isSomeString!RT)
702 && !isConvertibleToString!RT)
704 // Place outside of @trusted block
705 auto fromz = from.tempCString!FSChar();
706 auto toz = to.tempCString!FSChar();
708 static if (isNarrowString!RF && is(Unqual!(ElementEncodingType!RF) == char))
709 alias f = from;
710 else
711 enum string f = null;
713 static if (isNarrowString!RT && is(Unqual!(ElementEncodingType!RT) == char))
714 alias t = to;
715 else
716 enum string t = null;
718 renameImpl(f, t, fromz, toz);
721 /// ditto
722 void rename(RF, RT)(auto ref RF from, auto ref RT to)
723 if (isConvertibleToString!RF || isConvertibleToString!RT)
725 import std.meta : staticMap;
726 alias Types = staticMap!(convertToString, RF, RT);
727 rename!Types(from, to);
730 @safe unittest
732 static assert(__traits(compiles, rename(TestAliasedString(null), TestAliasedString(null))));
733 static assert(__traits(compiles, rename("", TestAliasedString(null))));
734 static assert(__traits(compiles, rename(TestAliasedString(null), "")));
735 import std.utf : byChar;
736 static assert(__traits(compiles, rename(TestAliasedString(null), "".byChar)));
739 private void renameImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, const(FSChar)* toz) @trusted
741 version (Windows)
743 import std.exception : enforce;
745 const result = MoveFileExW(fromz, toz, MOVEFILE_REPLACE_EXISTING);
746 if (!result)
748 import core.stdc.wchar_ : wcslen;
749 import std.conv : to, text;
751 if (!f)
752 f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]);
754 if (!t)
755 t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
757 enforce(false,
758 new FileException(
759 text("Attempting to rename file ", f, " to ", t)));
762 else version (Posix)
764 static import core.stdc.stdio;
766 cenforce(core.stdc.stdio.rename(fromz, toz) == 0, t, toz);
770 @safe unittest
772 import std.utf : byWchar;
774 auto t1 = deleteme, t2 = deleteme~"2";
775 scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
776 write(t1, "1");
777 rename(t1, t2);
778 assert(readText(t2) == "1");
779 write(t1, "2");
780 rename(t1, t2.byWchar);
781 assert(readText(t2) == "2");
785 /***************************************************
786 Delete file $(D name).
788 Params:
789 name = string or range of characters representing the file _name
791 Throws: $(D FileException) on error.
793 void remove(R)(R name)
794 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
795 !isConvertibleToString!R)
797 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
798 removeImpl(name, name.tempCString!FSChar());
799 else
800 removeImpl(null, name.tempCString!FSChar());
803 /// ditto
804 void remove(R)(auto ref R name)
805 if (isConvertibleToString!R)
807 remove!(StringTypeOf!R)(name);
810 @safe unittest
812 static assert(__traits(compiles, remove(TestAliasedString("foo"))));
815 private void removeImpl(const(char)[] name, const(FSChar)* namez) @trusted
817 version (Windows)
819 cenforce(DeleteFileW(namez), name, namez);
821 else version (Posix)
823 static import core.stdc.stdio;
825 if (!name)
827 import core.stdc.string : strlen;
828 auto len = strlen(namez);
829 name = namez[0 .. len];
831 cenforce(core.stdc.stdio.remove(namez) == 0,
832 "Failed to remove file " ~ name);
836 version (Windows) private WIN32_FILE_ATTRIBUTE_DATA getFileAttributesWin(R)(R name)
837 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
839 auto namez = name.tempCString!FSChar();
841 WIN32_FILE_ATTRIBUTE_DATA fad = void;
843 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
845 static void getFA(const(char)[] name, const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
847 import std.exception : enforce;
848 enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad),
849 new FileException(name.idup));
851 getFA(name, namez, fad);
853 else
855 static void getFA(const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
857 import core.stdc.wchar_ : wcslen;
858 import std.conv : to;
859 import std.exception : enforce;
861 enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad),
862 new FileException(namez[0 .. wcslen(namez)].to!string));
864 getFA(namez, fad);
866 return fad;
869 version (Windows) private ulong makeUlong(DWORD dwLow, DWORD dwHigh) @safe pure nothrow @nogc
871 ULARGE_INTEGER li;
872 li.LowPart = dwLow;
873 li.HighPart = dwHigh;
874 return li.QuadPart;
877 /***************************************************
878 Get size of file $(D name) in bytes.
880 Params:
881 name = string or range of characters representing the file _name
883 Throws: $(D FileException) on error (e.g., file not found).
885 ulong getSize(R)(R name)
886 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
887 !isConvertibleToString!R)
889 version (Windows)
891 with (getFileAttributesWin(name))
892 return makeUlong(nFileSizeLow, nFileSizeHigh);
894 else version (Posix)
896 auto namez = name.tempCString();
898 static trustedStat(const(FSChar)* namez, out stat_t buf) @trusted
900 return stat(namez, &buf);
902 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
903 alias names = name;
904 else
905 string names = null;
906 stat_t statbuf = void;
907 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
908 return statbuf.st_size;
912 /// ditto
913 ulong getSize(R)(auto ref R name)
914 if (isConvertibleToString!R)
916 return getSize!(StringTypeOf!R)(name);
919 @safe unittest
921 static assert(__traits(compiles, getSize(TestAliasedString("foo"))));
924 @safe unittest
926 // create a file of size 1
927 write(deleteme, "a");
928 scope(exit) { assert(exists(deleteme)); remove(deleteme); }
929 assert(getSize(deleteme) == 1);
930 // create a file of size 3
931 write(deleteme, "abc");
932 import std.utf : byChar;
933 assert(getSize(deleteme.byChar) == 3);
937 // Reads a time field from a stat_t with full precision.
938 version (Posix)
939 private SysTime statTimeToStdTime(char which)(ref stat_t statbuf)
941 auto unixTime = mixin(`statbuf.st_` ~ which ~ `time`);
942 long stdTime = unixTimeToStdTime(unixTime);
944 static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `tim`))))
945 stdTime += mixin(`statbuf.st_` ~ which ~ `tim.tv_nsec`) / 100;
946 else
947 static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `timensec`))))
948 stdTime += mixin(`statbuf.st_` ~ which ~ `timensec`) / 100;
949 else
950 static if (is(typeof(mixin(`statbuf.st_` ~ which ~ `time_nsec`))))
951 stdTime += mixin(`statbuf.st_` ~ which ~ `time_nsec`) / 100;
952 else
953 static if (is(typeof(mixin(`statbuf.__st_` ~ which ~ `timensec`))))
954 stdTime += mixin(`statbuf.__st_` ~ which ~ `timensec`) / 100;
956 return SysTime(stdTime);
960 Get the access and modified times of file or folder $(D name).
962 Params:
963 name = File/Folder _name to get times for.
964 accessTime = Time the file/folder was last accessed.
965 modificationTime = Time the file/folder was last modified.
967 Throws:
968 $(D FileException) on error.
970 void getTimes(R)(R name,
971 out SysTime accessTime,
972 out SysTime modificationTime)
973 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
974 !isConvertibleToString!R)
976 version (Windows)
978 import std.datetime.systime : FILETIMEToSysTime;
980 with (getFileAttributesWin(name))
982 accessTime = FILETIMEToSysTime(&ftLastAccessTime);
983 modificationTime = FILETIMEToSysTime(&ftLastWriteTime);
986 else version (Posix)
988 auto namez = name.tempCString();
990 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
992 return stat(namez, &buf);
994 stat_t statbuf = void;
996 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
997 alias names = name;
998 else
999 string names = null;
1000 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1002 accessTime = statTimeToStdTime!'a'(statbuf);
1003 modificationTime = statTimeToStdTime!'m'(statbuf);
1007 /// ditto
1008 void getTimes(R)(auto ref R name,
1009 out SysTime accessTime,
1010 out SysTime modificationTime)
1011 if (isConvertibleToString!R)
1013 return getTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
1016 @safe unittest
1018 SysTime atime, mtime;
1019 static assert(__traits(compiles, getTimes(TestAliasedString("foo"), atime, mtime)));
1022 @system unittest
1024 import std.stdio : writefln;
1026 auto currTime = Clock.currTime();
1028 write(deleteme, "a");
1029 scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1031 SysTime accessTime1 = void;
1032 SysTime modificationTime1 = void;
1034 getTimes(deleteme, accessTime1, modificationTime1);
1036 enum leeway = dur!"seconds"(5);
1039 auto diffa = accessTime1 - currTime;
1040 auto diffm = modificationTime1 - currTime;
1041 scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime1, modificationTime1, currTime, diffa, diffm);
1043 assert(abs(diffa) <= leeway);
1044 assert(abs(diffm) <= leeway);
1047 version (fullFileTests)
1049 import core.thread;
1050 enum sleepTime = dur!"seconds"(2);
1051 Thread.sleep(sleepTime);
1053 currTime = Clock.currTime();
1054 write(deleteme, "b");
1056 SysTime accessTime2 = void;
1057 SysTime modificationTime2 = void;
1059 getTimes(deleteme, accessTime2, modificationTime2);
1062 auto diffa = accessTime2 - currTime;
1063 auto diffm = modificationTime2 - currTime;
1064 scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime2, modificationTime2, currTime, diffa, diffm);
1066 //There is no guarantee that the access time will be updated.
1067 assert(abs(diffa) <= leeway + sleepTime);
1068 assert(abs(diffm) <= leeway);
1071 assert(accessTime1 <= accessTime2);
1072 assert(modificationTime1 <= modificationTime2);
1077 version (StdDdoc)
1080 $(BLUE This function is Windows-Only.)
1082 Get creation/access/modified times of file $(D name).
1084 This is the same as $(D getTimes) except that it also gives you the file
1085 creation time - which isn't possible on Posix systems.
1087 Params:
1088 name = File _name to get times for.
1089 fileCreationTime = Time the file was created.
1090 fileAccessTime = Time the file was last accessed.
1091 fileModificationTime = Time the file was last modified.
1093 Throws:
1094 $(D FileException) on error.
1096 void getTimesWin(R)(R name,
1097 out SysTime fileCreationTime,
1098 out SysTime fileAccessTime,
1099 out SysTime fileModificationTime)
1100 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1101 !isConvertibleToString!R);
1103 else version (Windows)
1105 void getTimesWin(R)(R name,
1106 out SysTime fileCreationTime,
1107 out SysTime fileAccessTime,
1108 out SysTime fileModificationTime)
1109 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1110 !isConvertibleToString!R)
1112 import std.datetime.systime : FILETIMEToSysTime;
1114 with (getFileAttributesWin(name))
1116 fileCreationTime = FILETIMEToSysTime(&ftCreationTime);
1117 fileAccessTime = FILETIMEToSysTime(&ftLastAccessTime);
1118 fileModificationTime = FILETIMEToSysTime(&ftLastWriteTime);
1122 void getTimesWin(R)(auto ref R name,
1123 out SysTime fileCreationTime,
1124 out SysTime fileAccessTime,
1125 out SysTime fileModificationTime)
1126 if (isConvertibleToString!R)
1128 getTimesWin!(StringTypeOf!R)(name, fileCreationTime, fileAccessTime, fileModificationTime);
1132 version (Windows) @system unittest
1134 import std.stdio : writefln;
1135 auto currTime = Clock.currTime();
1137 write(deleteme, "a");
1138 scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1140 SysTime creationTime1 = void;
1141 SysTime accessTime1 = void;
1142 SysTime modificationTime1 = void;
1144 getTimesWin(deleteme, creationTime1, accessTime1, modificationTime1);
1146 enum leeway = dur!"seconds"(5);
1149 auto diffc = creationTime1 - currTime;
1150 auto diffa = accessTime1 - currTime;
1151 auto diffm = modificationTime1 - currTime;
1152 scope(failure)
1154 writefln("[%s] [%s] [%s] [%s] [%s] [%s] [%s]",
1155 creationTime1, accessTime1, modificationTime1, currTime, diffc, diffa, diffm);
1158 // Deleting and recreating a file doesn't seem to always reset the "file creation time"
1159 //assert(abs(diffc) <= leeway);
1160 assert(abs(diffa) <= leeway);
1161 assert(abs(diffm) <= leeway);
1164 version (fullFileTests)
1166 import core.thread;
1167 Thread.sleep(dur!"seconds"(2));
1169 currTime = Clock.currTime();
1170 write(deleteme, "b");
1172 SysTime creationTime2 = void;
1173 SysTime accessTime2 = void;
1174 SysTime modificationTime2 = void;
1176 getTimesWin(deleteme, creationTime2, accessTime2, modificationTime2);
1179 auto diffa = accessTime2 - currTime;
1180 auto diffm = modificationTime2 - currTime;
1181 scope(failure)
1183 writefln("[%s] [%s] [%s] [%s] [%s]",
1184 accessTime2, modificationTime2, currTime, diffa, diffm);
1187 assert(abs(diffa) <= leeway);
1188 assert(abs(diffm) <= leeway);
1191 assert(creationTime1 == creationTime2);
1192 assert(accessTime1 <= accessTime2);
1193 assert(modificationTime1 <= modificationTime2);
1197 SysTime ctime, atime, mtime;
1198 static assert(__traits(compiles, getTimesWin(TestAliasedString("foo"), ctime, atime, mtime)));
1204 Set access/modified times of file or folder $(D name).
1206 Params:
1207 name = File/Folder _name to get times for.
1208 accessTime = Time the file/folder was last accessed.
1209 modificationTime = Time the file/folder was last modified.
1211 Throws:
1212 $(D FileException) on error.
1214 void setTimes(R)(R name,
1215 SysTime accessTime,
1216 SysTime modificationTime)
1217 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1218 !isConvertibleToString!R)
1220 version (Windows)
1222 import std.datetime.systime : SysTimeToFILETIME;
1224 auto namez = name.tempCString!FSChar();
1225 static auto trustedCreateFileW(const(FSChar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
1226 SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition,
1227 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted
1229 return CreateFileW(namez, dwDesiredAccess, dwShareMode,
1230 lpSecurityAttributes, dwCreationDisposition,
1231 dwFlagsAndAttributes, hTemplateFile);
1234 static auto trustedCloseHandle(HANDLE hObject) @trusted
1236 return CloseHandle(hObject);
1238 static auto trustedSetFileTime(HANDLE hFile, in FILETIME *lpCreationTime,
1239 in ref FILETIME lpLastAccessTime, in ref FILETIME lpLastWriteTime) @trusted
1241 return SetFileTime(hFile, lpCreationTime, &lpLastAccessTime, &lpLastWriteTime);
1244 const ta = SysTimeToFILETIME(accessTime);
1245 const tm = SysTimeToFILETIME(modificationTime);
1246 alias defaults =
1247 AliasSeq!(GENERIC_WRITE,
1249 null,
1250 OPEN_EXISTING,
1251 FILE_ATTRIBUTE_NORMAL |
1252 FILE_ATTRIBUTE_DIRECTORY |
1253 FILE_FLAG_BACKUP_SEMANTICS,
1254 HANDLE.init);
1255 auto h = trustedCreateFileW(namez, defaults);
1257 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1258 alias names = name;
1259 else
1260 string names = null;
1261 cenforce(h != INVALID_HANDLE_VALUE, names, namez);
1263 scope(exit)
1264 cenforce(trustedCloseHandle(h), names, namez);
1266 cenforce(trustedSetFileTime(h, null, ta, tm), names, namez);
1268 else version (Posix)
1270 auto namez = name.tempCString!FSChar();
1271 static if (is(typeof(&utimensat)))
1273 static auto trustedUtimensat(int fd, const(FSChar)* namez, const ref timespec[2] times, int flags) @trusted
1275 return utimensat(fd, namez, times, flags);
1277 timespec[2] t = void;
1279 t[0] = accessTime.toTimeSpec();
1280 t[1] = modificationTime.toTimeSpec();
1282 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1283 alias names = name;
1284 else
1285 string names = null;
1286 cenforce(trustedUtimensat(AT_FDCWD, namez, t, 0) == 0, names, namez);
1288 else
1290 static auto trustedUtimes(const(FSChar)* namez, const ref timeval[2] times) @trusted
1292 return utimes(namez, times);
1294 timeval[2] t = void;
1296 t[0] = accessTime.toTimeVal();
1297 t[1] = modificationTime.toTimeVal();
1299 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1300 alias names = name;
1301 else
1302 string names = null;
1303 cenforce(trustedUtimes(namez, t) == 0, names, namez);
1308 /// ditto
1309 void setTimes(R)(auto ref R name,
1310 SysTime accessTime,
1311 SysTime modificationTime)
1312 if (isConvertibleToString!R)
1314 setTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
1317 @safe unittest
1319 if (false) // Test instatiation
1320 setTimes(TestAliasedString("foo"), SysTime.init, SysTime.init);
1323 @system unittest
1325 import std.stdio : File;
1326 string newdir = deleteme ~ r".dir";
1327 string dir = newdir ~ r"/a/b/c";
1328 string file = dir ~ "/file";
1330 if (!exists(dir)) mkdirRecurse(dir);
1331 { auto f = File(file, "w"); }
1333 void testTimes(int hnsecValue)
1335 foreach (path; [file, dir]) // test file and dir
1337 SysTime atime = SysTime(DateTime(2010, 10, 4, 0, 0, 30), hnsecs(hnsecValue));
1338 SysTime mtime = SysTime(DateTime(2011, 10, 4, 0, 0, 30), hnsecs(hnsecValue));
1339 setTimes(path, atime, mtime);
1341 SysTime atime_res;
1342 SysTime mtime_res;
1343 getTimes(path, atime_res, mtime_res);
1344 assert(atime == atime_res);
1345 assert(mtime == mtime_res);
1349 testTimes(0);
1350 version (linux)
1351 testTimes(123_456_7);
1353 rmdirRecurse(newdir);
1357 Returns the time that the given file was last modified.
1359 Throws:
1360 $(D FileException) if the given file does not exist.
1362 SysTime timeLastModified(R)(R name)
1363 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1364 !isConvertibleToString!R)
1366 version (Windows)
1368 SysTime dummy;
1369 SysTime ftm;
1371 getTimesWin(name, dummy, dummy, ftm);
1373 return ftm;
1375 else version (Posix)
1377 auto namez = name.tempCString!FSChar();
1378 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1380 return stat(namez, &buf);
1382 stat_t statbuf = void;
1384 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1385 alias names = name;
1386 else
1387 string names = null;
1388 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1390 return statTimeToStdTime!'m'(statbuf);
1394 /// ditto
1395 SysTime timeLastModified(R)(auto ref R name)
1396 if (isConvertibleToString!R)
1398 return timeLastModified!(StringTypeOf!R)(name);
1401 @safe unittest
1403 static assert(__traits(compiles, timeLastModified(TestAliasedString("foo"))));
1407 Returns the time that the given file was last modified. If the
1408 file does not exist, returns $(D returnIfMissing).
1410 A frequent usage pattern occurs in build automation tools such as
1411 $(HTTP gnu.org/software/make, make) or $(HTTP
1412 en.wikipedia.org/wiki/Apache_Ant, ant). To check whether file $(D
1413 target) must be rebuilt from file $(D source) (i.e., $(D target) is
1414 older than $(D source) or does not exist), use the comparison
1415 below. The code throws a $(D FileException) if $(D source) does not
1416 exist (as it should). On the other hand, the $(D SysTime.min) default
1417 makes a non-existing $(D target) seem infinitely old so the test
1418 correctly prompts building it.
1420 Params:
1421 name = The _name of the file to get the modification time for.
1422 returnIfMissing = The time to return if the given file does not exist.
1424 Example:
1425 --------------------
1426 if (timeLastModified(source) >= timeLastModified(target, SysTime.min))
1428 // must (re)build
1430 else
1432 // target is up-to-date
1434 --------------------
1436 SysTime timeLastModified(R)(R name, SysTime returnIfMissing)
1437 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
1439 version (Windows)
1441 if (!exists(name))
1442 return returnIfMissing;
1444 SysTime dummy;
1445 SysTime ftm;
1447 getTimesWin(name, dummy, dummy, ftm);
1449 return ftm;
1451 else version (Posix)
1453 auto namez = name.tempCString!FSChar();
1454 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1456 return stat(namez, &buf);
1458 stat_t statbuf = void;
1460 return trustedStat(namez, statbuf) != 0 ?
1461 returnIfMissing :
1462 statTimeToStdTime!'m'(statbuf);
1466 @safe unittest
1468 //std.process.system("echo a > deleteme") == 0 || assert(false);
1469 if (exists(deleteme))
1470 remove(deleteme);
1472 write(deleteme, "a\n");
1474 scope(exit)
1476 assert(exists(deleteme));
1477 remove(deleteme);
1480 // assert(lastModified("deleteme") >
1481 // lastModified("this file does not exist", SysTime.min));
1482 //assert(lastModified("deleteme") > lastModified(__FILE__));
1486 // Tests sub-second precision of querying file times.
1487 // Should pass on most modern systems running on modern filesystems.
1488 // Exceptions:
1489 // - FreeBSD, where one would need to first set the
1490 // vfs.timestamp_precision sysctl to a value greater than zero.
1491 // - OS X, where the native filesystem (HFS+) stores filesystem
1492 // timestamps with 1-second precision.
1494 // Note: on linux systems, although in theory a change to a file date
1495 // can be tracked with precision of 4 msecs, this test waits 20 msecs
1496 // to prevent possible problems relative to the CI services the dlang uses,
1497 // as they may have the HZ setting that controls the software clock set to 100
1498 // (instead of the more common 250).
1499 // see https://man7.org/linux/man-pages/man7/time.7.html
1500 // https://stackoverflow.com/a/14393315,
1501 // https://issues.dlang.org/show_bug.cgi?id=21148
1502 version (FreeBSD) {} else
1503 version (DragonFlyBSD) {} else
1504 version (OSX) {} else
1505 @system unittest
1507 import core.thread;
1509 if (exists(deleteme))
1510 remove(deleteme);
1512 SysTime lastTime;
1513 foreach (n; 0 .. 3)
1515 write(deleteme, "a");
1516 auto time = timeLastModified(deleteme);
1517 remove(deleteme);
1518 assert(time != lastTime);
1519 lastTime = time;
1520 Thread.sleep(20.msecs);
1526 * Determine whether the given file (or directory) _exists.
1527 * Params:
1528 * name = string or range of characters representing the file _name
1529 * Returns:
1530 * true if the file _name specified as input _exists
1532 bool exists(R)(R name)
1533 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1534 !isConvertibleToString!R)
1536 return existsImpl(name.tempCString!FSChar());
1539 /// ditto
1540 bool exists(R)(auto ref R name)
1541 if (isConvertibleToString!R)
1543 return exists!(StringTypeOf!R)(name);
1546 private bool existsImpl(const(FSChar)* namez) @trusted nothrow @nogc
1548 version (Windows)
1550 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
1551 // fileio/base/getfileattributes.asp
1552 return GetFileAttributesW(namez) != 0xFFFFFFFF;
1554 else version (Posix)
1557 The reason why we use stat (and not access) here is
1558 the quirky behavior of access for SUID programs: if
1559 we used access, a file may not appear to "exist",
1560 despite that the program would be able to open it
1561 just fine. The behavior in question is described as
1562 follows in the access man page:
1564 > The check is done using the calling process's real
1565 > UID and GID, rather than the effective IDs as is
1566 > done when actually attempting an operation (e.g.,
1567 > open(2)) on the file. This allows set-user-ID
1568 > programs to easily determine the invoking user's
1569 > authority.
1571 While various operating systems provide eaccess or
1572 euidaccess functions, these are not part of POSIX -
1573 so it's safer to use stat instead.
1576 stat_t statbuf = void;
1577 return lstat(namez, &statbuf) == 0;
1579 else
1580 static assert(0);
1583 @safe unittest
1585 assert(exists("."));
1586 assert(!exists("this file does not exist"));
1587 write(deleteme, "a\n");
1588 scope(exit) { assert(exists(deleteme)); remove(deleteme); }
1589 assert(exists(deleteme));
1592 @safe unittest // Bugzilla 16573
1594 enum S : string { foo = "foo" }
1595 assert(__traits(compiles, S.foo.exists));
1599 Returns the attributes of the given file.
1601 Note that the file attributes on Windows and Posix systems are
1602 completely different. On Windows, they're what is returned by
1603 $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx,
1604 GetFileAttributes), whereas on Posix systems, they're the $(LUCKY
1605 st_mode) value which is part of the $(D stat struct) gotten by
1606 calling the $(HTTP en.wikipedia.org/wiki/Stat_%28Unix%29, $(D stat))
1607 function.
1609 On Posix systems, if the given file is a symbolic link, then
1610 attributes are the attributes of the file pointed to by the symbolic
1611 link.
1613 Params:
1614 name = The file to get the attributes of.
1616 Throws: $(D FileException) on error.
1618 uint getAttributes(R)(R name)
1619 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1620 !isConvertibleToString!R)
1622 version (Windows)
1624 auto namez = name.tempCString!FSChar();
1625 static auto trustedGetFileAttributesW(const(FSChar)* namez) @trusted
1627 return GetFileAttributesW(namez);
1629 immutable result = trustedGetFileAttributesW(namez);
1631 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1632 alias names = name;
1633 else
1634 string names = null;
1635 cenforce(result != INVALID_FILE_ATTRIBUTES, names, namez);
1637 return result;
1639 else version (Posix)
1641 auto namez = name.tempCString!FSChar();
1642 static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
1644 return stat(namez, &buf);
1646 stat_t statbuf = void;
1648 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1649 alias names = name;
1650 else
1651 string names = null;
1652 cenforce(trustedStat(namez, statbuf) == 0, names, namez);
1654 return statbuf.st_mode;
1658 /// ditto
1659 uint getAttributes(R)(auto ref R name)
1660 if (isConvertibleToString!R)
1662 return getAttributes!(StringTypeOf!R)(name);
1665 @safe unittest
1667 static assert(__traits(compiles, getAttributes(TestAliasedString(null))));
1671 If the given file is a symbolic link, then this returns the attributes of the
1672 symbolic link itself rather than file that it points to. If the given file
1673 is $(I not) a symbolic link, then this function returns the same result
1674 as getAttributes.
1676 On Windows, getLinkAttributes is identical to getAttributes. It exists on
1677 Windows so that you don't have to special-case code for Windows when dealing
1678 with symbolic links.
1680 Params:
1681 name = The file to get the symbolic link attributes of.
1683 Returns:
1684 the attributes
1686 Throws:
1687 $(D FileException) on error.
1689 uint getLinkAttributes(R)(R name)
1690 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1691 !isConvertibleToString!R)
1693 version (Windows)
1695 return getAttributes(name);
1697 else version (Posix)
1699 auto namez = name.tempCString!FSChar();
1700 static auto trustedLstat(const(FSChar)* namez, ref stat_t buf) @trusted
1702 return lstat(namez, &buf);
1704 stat_t lstatbuf = void;
1705 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1706 alias names = name;
1707 else
1708 string names = null;
1709 cenforce(trustedLstat(namez, lstatbuf) == 0, names, namez);
1710 return lstatbuf.st_mode;
1714 /// ditto
1715 uint getLinkAttributes(R)(auto ref R name)
1716 if (isConvertibleToString!R)
1718 return getLinkAttributes!(StringTypeOf!R)(name);
1721 @safe unittest
1723 static assert(__traits(compiles, getLinkAttributes(TestAliasedString(null))));
1727 Set the _attributes of the given file.
1729 Params:
1730 name = the file _name
1731 attributes = the _attributes to set the file to
1733 Throws:
1734 $(D FileException) if the given file does not exist.
1736 void setAttributes(R)(R name, uint attributes)
1737 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1738 !isConvertibleToString!R)
1740 version (Windows)
1742 auto namez = name.tempCString!FSChar();
1743 static auto trustedSetFileAttributesW(const(FSChar)* namez, uint dwFileAttributes) @trusted
1745 return SetFileAttributesW(namez, dwFileAttributes);
1747 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1748 alias names = name;
1749 else
1750 string names = null;
1751 cenforce(trustedSetFileAttributesW(namez, attributes), names, namez);
1753 else version (Posix)
1755 auto namez = name.tempCString!FSChar();
1756 static auto trustedChmod(const(FSChar)* namez, mode_t mode) @trusted
1758 return chmod(namez, mode);
1760 assert(attributes <= mode_t.max);
1761 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
1762 alias names = name;
1763 else
1764 string names = null;
1765 cenforce(!trustedChmod(namez, cast(mode_t) attributes), names, namez);
1769 /// ditto
1770 void setAttributes(R)(auto ref R name, uint attributes)
1771 if (isConvertibleToString!R)
1773 return setAttributes!(StringTypeOf!R)(name, attributes);
1776 @safe unittest
1778 static assert(__traits(compiles, setAttributes(TestAliasedString(null), 0)));
1782 Returns whether the given file is a directory.
1784 Params:
1785 name = The path to the file.
1787 Returns:
1788 true if name specifies a directory
1790 Throws:
1791 $(D FileException) if the given file does not exist.
1793 Example:
1794 --------------------
1795 assert(!"/etc/fonts/fonts.conf".isDir);
1796 assert("/usr/share/include".isDir);
1797 --------------------
1799 @property bool isDir(R)(R name)
1800 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1801 !isConvertibleToString!R)
1803 version (Windows)
1805 return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) != 0;
1807 else version (Posix)
1809 return (getAttributes(name) & S_IFMT) == S_IFDIR;
1813 /// ditto
1814 @property bool isDir(R)(auto ref R name)
1815 if (isConvertibleToString!R)
1817 return name.isDir!(StringTypeOf!R);
1820 @safe unittest
1822 static assert(__traits(compiles, TestAliasedString(null).isDir));
1825 @safe unittest
1827 version (Windows)
1829 if ("C:\\Program Files\\".exists)
1830 assert("C:\\Program Files\\".isDir);
1832 if ("C:\\Windows\\system.ini".exists)
1833 assert(!"C:\\Windows\\system.ini".isDir);
1835 else version (Posix)
1837 if (system_directory.exists)
1838 assert(system_directory.isDir);
1840 if (system_file.exists)
1841 assert(!system_file.isDir);
1845 @system unittest
1847 version (Windows)
1848 enum dir = "C:\\Program Files\\";
1849 else version (Posix)
1850 enum dir = system_directory;
1852 if (dir.exists)
1854 DirEntry de = DirEntry(dir);
1855 assert(de.isDir);
1856 assert(DirEntry(dir).isDir);
1861 Returns whether the given file _attributes are for a directory.
1863 Params:
1864 attributes = The file _attributes.
1866 Returns:
1867 true if attributes specifies a directory
1869 Example:
1870 --------------------
1871 assert(!attrIsDir(getAttributes("/etc/fonts/fonts.conf")));
1872 assert(!attrIsDir(getLinkAttributes("/etc/fonts/fonts.conf")));
1873 --------------------
1875 bool attrIsDir(uint attributes) @safe pure nothrow @nogc
1877 version (Windows)
1879 return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
1881 else version (Posix)
1883 return (attributes & S_IFMT) == S_IFDIR;
1887 @safe unittest
1889 version (Windows)
1891 if ("C:\\Program Files\\".exists)
1893 assert(attrIsDir(getAttributes("C:\\Program Files\\")));
1894 assert(attrIsDir(getLinkAttributes("C:\\Program Files\\")));
1897 if ("C:\\Windows\\system.ini".exists)
1899 assert(!attrIsDir(getAttributes("C:\\Windows\\system.ini")));
1900 assert(!attrIsDir(getLinkAttributes("C:\\Windows\\system.ini")));
1903 else version (Posix)
1905 if (system_directory.exists)
1907 assert(attrIsDir(getAttributes(system_directory)));
1908 assert(attrIsDir(getLinkAttributes(system_directory)));
1911 if (system_file.exists)
1913 assert(!attrIsDir(getAttributes(system_file)));
1914 assert(!attrIsDir(getLinkAttributes(system_file)));
1921 Returns whether the given file (or directory) is a file.
1923 On Windows, if a file is not a directory, then it's a file. So,
1924 either $(D isFile) or $(D isDir) will return true for any given file.
1926 On Posix systems, if $(D isFile) is $(D true), that indicates that the file
1927 is a regular file (e.g. not a block not device). So, on Posix systems, it's
1928 possible for both $(D isFile) and $(D isDir) to be $(D false) for a
1929 particular file (in which case, it's a special file). You can use
1930 $(D getAttributes) to get the attributes to figure out what type of special
1931 it is, or you can use $(D DirEntry) to get at its $(D statBuf), which is the
1932 result from $(D stat). In either case, see the man page for $(D stat) for
1933 more information.
1935 Params:
1936 name = The path to the file.
1938 Returns:
1939 true if name specifies a file
1941 Throws:
1942 $(D FileException) if the given file does not exist.
1944 Example:
1945 --------------------
1946 assert("/etc/fonts/fonts.conf".isFile);
1947 assert(!"/usr/share/include".isFile);
1948 --------------------
1950 @property bool isFile(R)(R name)
1951 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
1952 !isConvertibleToString!R)
1954 version (Windows)
1955 return !name.isDir;
1956 else version (Posix)
1957 return (getAttributes(name) & S_IFMT) == S_IFREG;
1960 /// ditto
1961 @property bool isFile(R)(auto ref R name)
1962 if (isConvertibleToString!R)
1964 return isFile!(StringTypeOf!R)(name);
1967 @system unittest // bugzilla 15658
1969 DirEntry e = DirEntry(".");
1970 static assert(is(typeof(isFile(e))));
1973 @safe unittest
1975 static assert(__traits(compiles, TestAliasedString(null).isFile));
1978 @safe unittest
1980 version (Windows)
1982 if ("C:\\Program Files\\".exists)
1983 assert(!"C:\\Program Files\\".isFile);
1985 if ("C:\\Windows\\system.ini".exists)
1986 assert("C:\\Windows\\system.ini".isFile);
1988 else version (Posix)
1990 if (system_directory.exists)
1991 assert(!system_directory.isFile);
1993 if (system_file.exists)
1994 assert(system_file.isFile);
2000 Returns whether the given file _attributes are for a file.
2002 On Windows, if a file is not a directory, it's a file. So, either
2003 $(D attrIsFile) or $(D attrIsDir) will return $(D true) for the
2004 _attributes of any given file.
2006 On Posix systems, if $(D attrIsFile) is $(D true), that indicates that the
2007 file is a regular file (e.g. not a block not device). So, on Posix systems,
2008 it's possible for both $(D attrIsFile) and $(D attrIsDir) to be $(D false)
2009 for a particular file (in which case, it's a special file). If a file is a
2010 special file, you can use the _attributes to check what type of special file
2011 it is (see the man page for $(D stat) for more information).
2013 Params:
2014 attributes = The file _attributes.
2016 Returns:
2017 true if the given file _attributes are for a file
2019 Example:
2020 --------------------
2021 assert(attrIsFile(getAttributes("/etc/fonts/fonts.conf")));
2022 assert(attrIsFile(getLinkAttributes("/etc/fonts/fonts.conf")));
2023 --------------------
2025 bool attrIsFile(uint attributes) @safe pure nothrow @nogc
2027 version (Windows)
2029 return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
2031 else version (Posix)
2033 return (attributes & S_IFMT) == S_IFREG;
2037 @safe unittest
2039 version (Windows)
2041 if ("C:\\Program Files\\".exists)
2043 assert(!attrIsFile(getAttributes("C:\\Program Files\\")));
2044 assert(!attrIsFile(getLinkAttributes("C:\\Program Files\\")));
2047 if ("C:\\Windows\\system.ini".exists)
2049 assert(attrIsFile(getAttributes("C:\\Windows\\system.ini")));
2050 assert(attrIsFile(getLinkAttributes("C:\\Windows\\system.ini")));
2053 else version (Posix)
2055 if (system_directory.exists)
2057 assert(!attrIsFile(getAttributes(system_directory)));
2058 assert(!attrIsFile(getLinkAttributes(system_directory)));
2061 if (system_file.exists)
2063 assert(attrIsFile(getAttributes(system_file)));
2064 assert(attrIsFile(getLinkAttributes(system_file)));
2071 Returns whether the given file is a symbolic link.
2073 On Windows, returns $(D true) when the file is either a symbolic link or a
2074 junction point.
2076 Params:
2077 name = The path to the file.
2079 Returns:
2080 true if name is a symbolic link
2082 Throws:
2083 $(D FileException) if the given file does not exist.
2085 @property bool isSymlink(R)(R name)
2086 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2087 !isConvertibleToString!R)
2089 version (Windows)
2090 return (getAttributes(name) & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2091 else version (Posix)
2092 return (getLinkAttributes(name) & S_IFMT) == S_IFLNK;
2095 /// ditto
2096 @property bool isSymlink(R)(auto ref R name)
2097 if (isConvertibleToString!R)
2099 return name.isSymlink!(StringTypeOf!R);
2102 @safe unittest
2104 static assert(__traits(compiles, TestAliasedString(null).isSymlink));
2107 @system unittest
2109 version (Windows)
2111 if ("C:\\Program Files\\".exists)
2112 assert(!"C:\\Program Files\\".isSymlink);
2114 if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
2115 assert("C:\\Documents and Settings\\".isSymlink);
2117 enum fakeSymFile = "C:\\Windows\\system.ini";
2118 if (fakeSymFile.exists)
2120 assert(!fakeSymFile.isSymlink);
2122 assert(!fakeSymFile.isSymlink);
2123 assert(!attrIsSymlink(getAttributes(fakeSymFile)));
2124 assert(!attrIsSymlink(getLinkAttributes(fakeSymFile)));
2126 assert(attrIsFile(getAttributes(fakeSymFile)));
2127 assert(attrIsFile(getLinkAttributes(fakeSymFile)));
2128 assert(!attrIsDir(getAttributes(fakeSymFile)));
2129 assert(!attrIsDir(getLinkAttributes(fakeSymFile)));
2131 assert(getAttributes(fakeSymFile) == getLinkAttributes(fakeSymFile));
2134 else version (Posix)
2136 if (system_directory.exists)
2138 assert(!system_directory.isSymlink);
2140 immutable symfile = deleteme ~ "_slink\0";
2141 scope(exit) if (symfile.exists) symfile.remove();
2143 core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
2145 assert(symfile.isSymlink);
2146 assert(!attrIsSymlink(getAttributes(symfile)));
2147 assert(attrIsSymlink(getLinkAttributes(symfile)));
2149 assert(attrIsDir(getAttributes(symfile)));
2150 assert(!attrIsDir(getLinkAttributes(symfile)));
2152 assert(!attrIsFile(getAttributes(symfile)));
2153 assert(!attrIsFile(getLinkAttributes(symfile)));
2156 if (system_file.exists)
2158 assert(!system_file.isSymlink);
2160 immutable symfile = deleteme ~ "_slink\0";
2161 scope(exit) if (symfile.exists) symfile.remove();
2163 core.sys.posix.unistd.symlink(system_file, symfile.ptr);
2165 assert(symfile.isSymlink);
2166 assert(!attrIsSymlink(getAttributes(symfile)));
2167 assert(attrIsSymlink(getLinkAttributes(symfile)));
2169 assert(!attrIsDir(getAttributes(symfile)));
2170 assert(!attrIsDir(getLinkAttributes(symfile)));
2172 assert(attrIsFile(getAttributes(symfile)));
2173 assert(!attrIsFile(getLinkAttributes(symfile)));
2177 static assert(__traits(compiles, () @safe { return "dummy".isSymlink; }));
2182 Returns whether the given file attributes are for a symbolic link.
2184 On Windows, return $(D true) when the file is either a symbolic link or a
2185 junction point.
2187 Params:
2188 attributes = The file attributes.
2190 Returns:
2191 true if attributes are for a symbolic link
2193 Example:
2194 --------------------
2195 core.sys.posix.unistd.symlink("/etc/fonts/fonts.conf", "/tmp/alink");
2197 assert(!getAttributes("/tmp/alink").isSymlink);
2198 assert(getLinkAttributes("/tmp/alink").isSymlink);
2199 --------------------
2201 bool attrIsSymlink(uint attributes) @safe pure nothrow @nogc
2203 version (Windows)
2204 return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
2205 else version (Posix)
2206 return (attributes & S_IFMT) == S_IFLNK;
2210 /****************************************************
2211 * Change directory to $(D pathname).
2212 * Throws: $(D FileException) on error.
2214 void chdir(R)(R pathname)
2215 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2216 !isConvertibleToString!R)
2218 // Place outside of @trusted block
2219 auto pathz = pathname.tempCString!FSChar();
2221 version (Windows)
2223 static auto trustedChdir(const(FSChar)* pathz) @trusted
2225 return SetCurrentDirectoryW(pathz);
2228 else version (Posix)
2230 static auto trustedChdir(const(FSChar)* pathz) @trusted
2232 return core.sys.posix.unistd.chdir(pathz) == 0;
2235 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2236 alias pathStr = pathname;
2237 else
2238 string pathStr = null;
2239 cenforce(trustedChdir(pathz), pathStr, pathz);
2242 /// ditto
2243 void chdir(R)(auto ref R pathname)
2244 if (isConvertibleToString!R)
2246 return chdir!(StringTypeOf!R)(pathname);
2249 @safe unittest
2251 static assert(__traits(compiles, chdir(TestAliasedString(null))));
2254 /****************************************************
2255 Make directory $(D pathname).
2257 Throws: $(D FileException) on Posix or $(D WindowsException) on Windows
2258 if an error occured.
2260 void mkdir(R)(R pathname)
2261 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2262 !isConvertibleToString!R)
2264 // Place outside of @trusted block
2265 const pathz = pathname.tempCString!FSChar();
2267 version (Windows)
2269 static auto trustedCreateDirectoryW(const(FSChar)* pathz) @trusted
2271 return CreateDirectoryW(pathz, null);
2273 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2274 alias pathStr = pathname;
2275 else
2276 string pathStr = null;
2277 wenforce(trustedCreateDirectoryW(pathz), pathStr, pathz);
2279 else version (Posix)
2281 import std.conv : octal;
2283 static auto trustedMkdir(const(FSChar)* pathz, mode_t mode) @trusted
2285 return core.sys.posix.sys.stat.mkdir(pathz, mode);
2287 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2288 alias pathStr = pathname;
2289 else
2290 string pathStr = null;
2291 cenforce(trustedMkdir(pathz, octal!777) == 0, pathStr, pathz);
2295 /// ditto
2296 void mkdir(R)(auto ref R pathname)
2297 if (isConvertibleToString!R)
2299 return mkdir!(StringTypeOf!R)(pathname);
2302 @safe unittest
2304 import std.file : mkdir;
2305 static assert(__traits(compiles, mkdir(TestAliasedString(null))));
2308 // Same as mkdir but ignores "already exists" errors.
2309 // Returns: "true" if the directory was created,
2310 // "false" if it already existed.
2311 private bool ensureDirExists()(in char[] pathname)
2313 import std.exception : enforce;
2314 const pathz = pathname.tempCString!FSChar();
2316 version (Windows)
2318 if (() @trusted { return CreateDirectoryW(pathz, null); }())
2319 return true;
2320 cenforce(GetLastError() == ERROR_ALREADY_EXISTS, pathname.idup);
2322 else version (Posix)
2324 import std.conv : octal;
2326 if (() @trusted { return core.sys.posix.sys.stat.mkdir(pathz, octal!777); }() == 0)
2327 return true;
2328 cenforce(errno == EEXIST || errno == EISDIR, pathname);
2330 enforce(pathname.isDir, new FileException(pathname.idup));
2331 return false;
2334 /****************************************************
2335 * Make directory and all parent directories as needed.
2337 * Does nothing if the directory specified by
2338 * $(D pathname) already exists.
2340 * Throws: $(D FileException) on error.
2343 void mkdirRecurse(in char[] pathname) @safe
2345 import std.path : dirName, baseName;
2347 const left = dirName(pathname);
2348 if (left.length != pathname.length && !exists(left))
2350 mkdirRecurse(left);
2352 if (!baseName(pathname).empty)
2354 ensureDirExists(pathname);
2358 @safe unittest
2360 import std.exception : assertThrown;
2362 import std.path : buildPath, buildNormalizedPath;
2364 immutable basepath = deleteme ~ "_dir";
2365 scope(exit) () @trusted { rmdirRecurse(basepath); }();
2367 auto path = buildPath(basepath, "a", "..", "b");
2368 mkdirRecurse(path);
2369 path = path.buildNormalizedPath;
2370 assert(path.isDir);
2372 path = buildPath(basepath, "c");
2373 write(path, "");
2374 assertThrown!FileException(mkdirRecurse(path));
2376 path = buildPath(basepath, "d");
2377 mkdirRecurse(path);
2378 mkdirRecurse(path); // should not throw
2381 version (Windows)
2383 assertThrown!FileException(mkdirRecurse(`1:\foobar`));
2386 // bug3570
2388 immutable basepath = deleteme ~ "_dir";
2389 version (Windows)
2391 immutable path = basepath ~ "\\fake\\here\\";
2393 else version (Posix)
2395 immutable path = basepath ~ `/fake/here/`;
2398 mkdirRecurse(path);
2399 assert(basepath.exists && basepath.isDir);
2400 scope(exit) () @trusted { rmdirRecurse(basepath); }();
2401 assert(path.exists && path.isDir);
2405 /****************************************************
2406 Remove directory $(D pathname).
2408 Params:
2409 pathname = Range or string specifying the directory name
2411 Throws: $(D FileException) on error.
2413 void rmdir(R)(R pathname)
2414 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
2415 !isConvertibleToString!R)
2417 // Place outside of @trusted block
2418 auto pathz = pathname.tempCString!FSChar();
2420 version (Windows)
2422 static auto trustedRmdir(const(FSChar)* pathz) @trusted
2424 return RemoveDirectoryW(pathz);
2427 else version (Posix)
2429 static auto trustedRmdir(const(FSChar)* pathz) @trusted
2431 return core.sys.posix.unistd.rmdir(pathz) == 0;
2434 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
2435 alias pathStr = pathname;
2436 else
2437 string pathStr = null;
2438 cenforce(trustedRmdir(pathz), pathStr, pathz);
2441 /// ditto
2442 void rmdir(R)(auto ref R pathname)
2443 if (isConvertibleToString!R)
2445 rmdir!(StringTypeOf!R)(pathname);
2448 @safe unittest
2450 static assert(__traits(compiles, rmdir(TestAliasedString(null))));
2454 $(BLUE This function is Posix-Only.)
2456 Creates a symbolic _link (_symlink).
2458 Params:
2459 original = The file that is being linked. This is the target path that's
2460 stored in the _symlink. A relative path is relative to the created
2461 _symlink.
2462 link = The _symlink to create. A relative path is relative to the
2463 current working directory.
2465 Throws:
2466 $(D FileException) on error (which includes if the _symlink already
2467 exists).
2469 version (StdDdoc) void symlink(RO, RL)(RO original, RL link)
2470 if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) ||
2471 isConvertibleToString!RO) &&
2472 (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) ||
2473 isConvertibleToString!RL));
2474 else version (Posix) void symlink(RO, RL)(RO original, RL link)
2475 if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) ||
2476 isConvertibleToString!RO) &&
2477 (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) ||
2478 isConvertibleToString!RL))
2480 static if (isConvertibleToString!RO || isConvertibleToString!RL)
2482 import std.meta : staticMap;
2483 alias Types = staticMap!(convertToString, RO, RL);
2484 symlink!Types(original, link);
2486 else
2488 import std.conv : text;
2489 auto oz = original.tempCString();
2490 auto lz = link.tempCString();
2491 alias posixSymlink = core.sys.posix.unistd.symlink;
2492 immutable int result = () @trusted { return posixSymlink(oz, lz); } ();
2493 cenforce(result == 0, text(link));
2497 version (Posix) @safe unittest
2499 if (system_directory.exists)
2501 immutable symfile = deleteme ~ "_slink\0";
2502 scope(exit) if (symfile.exists) symfile.remove();
2504 symlink(system_directory, symfile);
2506 assert(symfile.exists);
2507 assert(symfile.isSymlink);
2508 assert(!attrIsSymlink(getAttributes(symfile)));
2509 assert(attrIsSymlink(getLinkAttributes(symfile)));
2511 assert(attrIsDir(getAttributes(symfile)));
2512 assert(!attrIsDir(getLinkAttributes(symfile)));
2514 assert(!attrIsFile(getAttributes(symfile)));
2515 assert(!attrIsFile(getLinkAttributes(symfile)));
2518 if (system_file.exists)
2520 assert(!system_file.isSymlink);
2522 immutable symfile = deleteme ~ "_slink\0";
2523 scope(exit) if (symfile.exists) symfile.remove();
2525 symlink(system_file, symfile);
2527 assert(symfile.exists);
2528 assert(symfile.isSymlink);
2529 assert(!attrIsSymlink(getAttributes(symfile)));
2530 assert(attrIsSymlink(getLinkAttributes(symfile)));
2532 assert(!attrIsDir(getAttributes(symfile)));
2533 assert(!attrIsDir(getLinkAttributes(symfile)));
2535 assert(attrIsFile(getAttributes(symfile)));
2536 assert(!attrIsFile(getLinkAttributes(symfile)));
2540 version (Posix) @safe unittest
2542 static assert(__traits(compiles,
2543 symlink(TestAliasedString(null), TestAliasedString(null))));
2548 $(BLUE This function is Posix-Only.)
2550 Returns the path to the file pointed to by a symlink. Note that the
2551 path could be either relative or absolute depending on the symlink.
2552 If the path is relative, it's relative to the symlink, not the current
2553 working directory.
2555 Throws:
2556 $(D FileException) on error.
2558 version (StdDdoc) string readLink(R)(R link)
2559 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) ||
2560 isConvertibleToString!R);
2561 else version (Posix) string readLink(R)(R link)
2562 if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) ||
2563 isConvertibleToString!R)
2565 static if (isConvertibleToString!R)
2567 return readLink!(convertToString!R)(link);
2569 else
2571 import std.conv : to;
2572 import std.exception : assumeUnique;
2573 alias posixReadlink = core.sys.posix.unistd.readlink;
2574 enum bufferLen = 2048;
2575 enum maxCodeUnits = 6;
2576 char[bufferLen] buffer;
2577 const linkz = link.tempCString();
2578 auto size = () @trusted {
2579 return posixReadlink(linkz, buffer.ptr, buffer.length);
2580 } ();
2581 cenforce(size != -1, to!string(link));
2583 if (size <= bufferLen - maxCodeUnits)
2584 return to!string(buffer[0 .. size]);
2586 auto dynamicBuffer = new char[](bufferLen * 3 / 2);
2588 foreach (i; 0 .. 10)
2590 size = () @trusted {
2591 return posixReadlink(linkz, dynamicBuffer.ptr,
2592 dynamicBuffer.length);
2593 } ();
2594 cenforce(size != -1, to!string(link));
2596 if (size <= dynamicBuffer.length - maxCodeUnits)
2598 dynamicBuffer.length = size;
2599 return () @trusted {
2600 return assumeUnique(dynamicBuffer);
2601 } ();
2604 dynamicBuffer.length = dynamicBuffer.length * 3 / 2;
2607 throw new FileException(to!string(link), "Path is too long to read.");
2611 version (Posix) @safe unittest
2613 import std.exception : assertThrown;
2614 import std.string;
2616 foreach (file; [system_directory, system_file])
2618 if (file.exists)
2620 immutable symfile = deleteme ~ "_slink\0";
2621 scope(exit) if (symfile.exists) symfile.remove();
2623 symlink(file, symfile);
2624 assert(readLink(symfile) == file, format("Failed file: %s", file));
2628 assertThrown!FileException(readLink("/doesnotexist"));
2631 version (Posix) @safe unittest
2633 static assert(__traits(compiles, readLink(TestAliasedString("foo"))));
2636 version (Posix) @system unittest // input range of dchars
2638 mkdirRecurse(deleteme);
2639 scope(exit) if (deleteme.exists) rmdirRecurse(deleteme);
2640 write(deleteme ~ "/f", "");
2641 import std.range.interfaces : InputRange, inputRangeObject;
2642 import std.utf : byChar;
2643 immutable string link = deleteme ~ "/l";
2644 symlink("f", link);
2645 InputRange!dchar linkr = inputRangeObject(link);
2646 alias R = typeof(linkr);
2647 static assert(isInputRange!R);
2648 static assert(!isForwardRange!R);
2649 assert(readLink(linkr) == "f");
2653 /****************************************************
2654 * Get the current working directory.
2655 * Throws: $(D FileException) on error.
2657 version (Windows) string getcwd()
2659 import std.conv : to;
2660 /* GetCurrentDirectory's return value:
2661 1. function succeeds: the number of characters that are written to
2662 the buffer, not including the terminating null character.
2663 2. function fails: zero
2664 3. the buffer (lpBuffer) is not large enough: the required size of
2665 the buffer, in characters, including the null-terminating character.
2667 wchar[4096] buffW = void; //enough for most common case
2668 immutable n = cenforce(GetCurrentDirectoryW(to!DWORD(buffW.length), buffW.ptr),
2669 "getcwd");
2670 // we can do it because toUTFX always produces a fresh string
2671 if (n < buffW.length)
2673 return buffW[0 .. n].to!string;
2675 else //staticBuff isn't enough
2677 auto ptr = cast(wchar*) malloc(wchar.sizeof * n);
2678 scope(exit) free(ptr);
2679 immutable n2 = GetCurrentDirectoryW(n, ptr);
2680 cenforce(n2 && n2 < n, "getcwd");
2681 return ptr[0 .. n2].to!string;
2684 else version (Solaris) string getcwd()
2686 /* BUF_SIZE >= PATH_MAX */
2687 enum BUF_SIZE = 4096;
2688 /* The user should be able to specify any size buffer > 0 */
2689 auto p = cenforce(core.sys.posix.unistd.getcwd(null, BUF_SIZE),
2690 "cannot get cwd");
2691 scope(exit) core.stdc.stdlib.free(p);
2692 return p[0 .. core.stdc.string.strlen(p)].idup;
2694 else version (Posix) string getcwd()
2696 auto p = cenforce(core.sys.posix.unistd.getcwd(null, 0),
2697 "cannot get cwd");
2698 scope(exit) core.stdc.stdlib.free(p);
2699 return p[0 .. core.stdc.string.strlen(p)].idup;
2702 @system unittest
2704 auto s = getcwd();
2705 assert(s.length);
2708 version (OSX)
2709 private extern (C) int _NSGetExecutablePath(char* buf, uint* bufsize);
2710 else version (FreeBSD)
2711 private extern (C) int sysctl (const int* name, uint namelen, void* oldp,
2712 size_t* oldlenp, const void* newp, size_t newlen);
2713 else version (NetBSD)
2714 private extern (C) int sysctl (const int* name, uint namelen, void* oldp,
2715 size_t* oldlenp, const void* newp, size_t newlen);
2718 * Returns the full path of the current executable.
2720 * Throws:
2721 * $(REF1 Exception, object)
2723 @trusted string thisExePath ()
2725 version (OSX)
2727 import core.sys.posix.stdlib : realpath;
2728 import std.conv : to;
2729 import std.exception : errnoEnforce;
2731 uint size;
2733 _NSGetExecutablePath(null, &size); // get the length of the path
2734 auto buffer = new char[size];
2735 _NSGetExecutablePath(buffer.ptr, &size);
2737 auto absolutePath = realpath(buffer.ptr, null); // let the function allocate
2739 scope (exit)
2741 if (absolutePath)
2742 free(absolutePath);
2745 errnoEnforce(absolutePath);
2746 return to!(string)(absolutePath);
2748 else version (linux)
2750 return readLink("/proc/self/exe");
2752 else version (Windows)
2754 import std.conv : to;
2755 import std.exception : enforce;
2757 wchar[MAX_PATH] buf;
2758 wchar[] buffer = buf[];
2760 while (true)
2762 auto len = GetModuleFileNameW(null, buffer.ptr, cast(DWORD) buffer.length);
2763 enforce(len, sysErrorString(GetLastError()));
2764 if (len != buffer.length)
2765 return to!(string)(buffer[0 .. len]);
2766 buffer.length *= 2;
2769 else version (DragonFlyBSD)
2771 import core.sys.dragonflybsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME;
2772 import std.exception : errnoEnforce, assumeUnique;
2774 int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
2775 size_t len;
2777 auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
2778 errnoEnforce(result == 0);
2780 auto buffer = new char[len - 1];
2781 result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
2782 errnoEnforce(result == 0);
2784 return buffer.assumeUnique;
2786 else version (FreeBSD)
2788 import core.sys.freebsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME;
2789 import std.exception : errnoEnforce, assumeUnique;
2791 int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
2792 size_t len;
2794 auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
2795 errnoEnforce(result == 0);
2797 auto buffer = new char[len - 1];
2798 result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
2799 errnoEnforce(result == 0);
2801 return buffer.assumeUnique;
2803 else version (NetBSD)
2805 import core.sys.netbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_PATHNAME;
2806 import std.exception : errnoEnforce, assumeUnique;
2808 int[4] mib = [CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME];
2809 size_t len;
2811 auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
2812 errnoEnforce(result == 0);
2814 auto buffer = new char[len - 1];
2815 result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
2816 errnoEnforce(result == 0);
2818 return buffer.assumeUnique;
2820 else version (OpenBSD)
2822 import core.sys.openbsd.sys.sysctl : sysctl, CTL_KERN, KERN_PROC_ARGS, KERN_PROC_ARGV;
2823 import core.sys.posix.unistd : getpid;
2824 import std.conv : to;
2825 import std.exception : enforce, errnoEnforce;
2826 import std.process : searchPathFor;
2828 int[4] mib = [CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV];
2829 size_t len;
2831 auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0);
2832 errnoEnforce(result == 0);
2834 auto argv = new char*[len - 1];
2835 result = sysctl(mib.ptr, mib.length, argv.ptr, &len, null, 0);
2836 errnoEnforce(result == 0);
2838 auto argv0 = argv[0];
2839 if (*argv0 == '/' || *argv0 == '.')
2841 import core.sys.posix.stdlib : realpath;
2842 auto absolutePath = realpath(argv0, null);
2843 scope (exit)
2845 if (absolutePath)
2846 free(absolutePath);
2848 errnoEnforce(absolutePath);
2849 return to!(string)(absolutePath);
2851 else
2853 auto absolutePath = searchPathFor(to!string(argv0));
2854 errnoEnforce(absolutePath);
2855 return absolutePath;
2858 else version (Solaris)
2860 import core.sys.posix.unistd : getpid;
2861 import std.string : format;
2863 // Only Solaris 10 and later
2864 return readLink(format("/proc/%d/path/a.out", getpid()));
2866 else
2867 static assert(0, "thisExePath is not supported on this platform");
2870 @safe unittest
2872 import std.path : isAbsolute;
2873 auto path = thisExePath();
2875 assert(path.exists);
2876 assert(path.isAbsolute);
2877 assert(path.isFile);
2880 version (StdDdoc)
2883 Info on a file, similar to what you'd get from stat on a Posix system.
2885 struct DirEntry
2888 Constructs a $(D DirEntry) for the given file (or directory).
2890 Params:
2891 path = The file (or directory) to get a DirEntry for.
2893 Throws:
2894 $(D FileException) if the file does not exist.
2896 this(string path);
2898 version (Windows)
2900 private this(string path, in WIN32_FIND_DATAW *fd);
2902 else version (Posix)
2904 private this(string path, core.sys.posix.dirent.dirent* fd);
2908 Returns the path to the file represented by this $(D DirEntry).
2910 Example:
2911 --------------------
2912 auto de1 = DirEntry("/etc/fonts/fonts.conf");
2913 assert(de1.name == "/etc/fonts/fonts.conf");
2915 auto de2 = DirEntry("/usr/share/include");
2916 assert(de2.name == "/usr/share/include");
2917 --------------------
2919 @property string name() const;
2923 Returns whether the file represented by this $(D DirEntry) is a
2924 directory.
2926 Example:
2927 --------------------
2928 auto de1 = DirEntry("/etc/fonts/fonts.conf");
2929 assert(!de1.isDir);
2931 auto de2 = DirEntry("/usr/share/include");
2932 assert(de2.isDir);
2933 --------------------
2935 @property bool isDir();
2939 Returns whether the file represented by this $(D DirEntry) is a file.
2941 On Windows, if a file is not a directory, then it's a file. So,
2942 either $(D isFile) or $(D isDir) will return $(D true).
2944 On Posix systems, if $(D isFile) is $(D true), that indicates that
2945 the file is a regular file (e.g. not a block not device). So, on
2946 Posix systems, it's possible for both $(D isFile) and $(D isDir) to
2947 be $(D false) for a particular file (in which case, it's a special
2948 file). You can use $(D attributes) or $(D statBuf) to get more
2949 information about a special file (see the stat man page for more
2950 details).
2952 Example:
2953 --------------------
2954 auto de1 = DirEntry("/etc/fonts/fonts.conf");
2955 assert(de1.isFile);
2957 auto de2 = DirEntry("/usr/share/include");
2958 assert(!de2.isFile);
2959 --------------------
2961 @property bool isFile();
2964 Returns whether the file represented by this $(D DirEntry) is a
2965 symbolic link.
2967 On Windows, return $(D true) when the file is either a symbolic
2968 link or a junction point.
2970 @property bool isSymlink();
2973 Returns the size of the the file represented by this $(D DirEntry)
2974 in bytes.
2976 @property ulong size();
2979 $(BLUE This function is Windows-Only.)
2981 Returns the creation time of the file represented by this
2982 $(D DirEntry).
2984 @property SysTime timeCreated() const;
2987 Returns the time that the file represented by this $(D DirEntry) was
2988 last accessed.
2990 Note that many file systems do not update the access time for files
2991 (generally for performance reasons), so there's a good chance that
2992 $(D timeLastAccessed) will return the same value as
2993 $(D timeLastModified).
2995 @property SysTime timeLastAccessed();
2998 Returns the time that the file represented by this $(D DirEntry) was
2999 last modified.
3001 @property SysTime timeLastModified();
3004 Returns the _attributes of the file represented by this $(D DirEntry).
3006 Note that the file _attributes on Windows and Posix systems are
3007 completely different. On, Windows, they're what is returned by
3008 $(D GetFileAttributes)
3009 $(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, GetFileAttributes)
3010 Whereas, an Posix systems, they're the $(D st_mode) value which is
3011 part of the $(D stat) struct gotten by calling $(D stat).
3013 On Posix systems, if the file represented by this $(D DirEntry) is a
3014 symbolic link, then _attributes are the _attributes of the file
3015 pointed to by the symbolic link.
3017 @property uint attributes();
3020 On Posix systems, if the file represented by this $(D DirEntry) is a
3021 symbolic link, then $(D linkAttributes) are the attributes of the
3022 symbolic link itself. Otherwise, $(D linkAttributes) is identical to
3023 $(D attributes).
3025 On Windows, $(D linkAttributes) is identical to $(D attributes). It
3026 exists on Windows so that you don't have to special-case code for
3027 Windows when dealing with symbolic links.
3029 @property uint linkAttributes();
3031 version (Windows)
3032 alias stat_t = void*;
3035 $(BLUE This function is Posix-Only.)
3037 The $(D stat) struct gotten from calling $(D stat).
3039 @property stat_t statBuf();
3042 else version (Windows)
3044 struct DirEntry
3046 public:
3047 alias name this;
3049 this(string path)
3051 import std.datetime.systime : FILETIMEToSysTime;
3053 if (!path.exists())
3054 throw new FileException(path, "File does not exist");
3056 _name = path;
3058 with (getFileAttributesWin(path))
3060 _size = makeUlong(nFileSizeLow, nFileSizeHigh);
3061 _timeCreated = FILETIMEToSysTime(&ftCreationTime);
3062 _timeLastAccessed = FILETIMEToSysTime(&ftLastAccessTime);
3063 _timeLastModified = FILETIMEToSysTime(&ftLastWriteTime);
3064 _attributes = dwFileAttributes;
3068 private this(string path, in WIN32_FIND_DATAW *fd)
3070 import core.stdc.wchar_ : wcslen;
3071 import std.conv : to;
3072 import std.datetime.systime : FILETIMEToSysTime;
3073 import std.path : buildPath;
3075 size_t clength = wcslen(fd.cFileName.ptr);
3076 _name = buildPath(path, fd.cFileName[0 .. clength].to!string);
3077 _size = (cast(ulong) fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
3078 _timeCreated = FILETIMEToSysTime(&fd.ftCreationTime);
3079 _timeLastAccessed = FILETIMEToSysTime(&fd.ftLastAccessTime);
3080 _timeLastModified = FILETIMEToSysTime(&fd.ftLastWriteTime);
3081 _attributes = fd.dwFileAttributes;
3084 @property string name() const pure nothrow
3086 return _name;
3089 @property bool isDir() const pure nothrow
3091 return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
3094 @property bool isFile() const pure nothrow
3096 //Are there no options in Windows other than directory and file?
3097 //If there are, then this probably isn't the best way to determine
3098 //whether this DirEntry is a file or not.
3099 return !isDir;
3102 @property bool isSymlink() const pure nothrow
3104 return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
3107 @property ulong size() const pure nothrow
3109 return _size;
3112 @property SysTime timeCreated() const pure nothrow
3114 return cast(SysTime)_timeCreated;
3117 @property SysTime timeLastAccessed() const pure nothrow
3119 return cast(SysTime)_timeLastAccessed;
3122 @property SysTime timeLastModified() const pure nothrow
3124 return cast(SysTime)_timeLastModified;
3127 @property uint attributes() const pure nothrow
3129 return _attributes;
3132 @property uint linkAttributes() const pure nothrow
3134 return _attributes;
3137 private:
3138 string _name; /// The file or directory represented by this DirEntry.
3140 SysTime _timeCreated; /// The time when the file was created.
3141 SysTime _timeLastAccessed; /// The time when the file was last accessed.
3142 SysTime _timeLastModified; /// The time when the file was last modified.
3144 ulong _size; /// The size of the file in bytes.
3145 uint _attributes; /// The file attributes from WIN32_FIND_DATAW.
3148 else version (Posix)
3150 struct DirEntry
3152 public:
3153 alias name this;
3155 this(string path)
3157 if (!path.exists)
3158 throw new FileException(path, "File does not exist");
3160 _name = path;
3162 _didLStat = false;
3163 _didStat = false;
3164 _dTypeSet = false;
3167 private this(string path, core.sys.posix.dirent.dirent* fd)
3169 import std.path : buildPath;
3171 static if (is(typeof(fd.d_namlen)))
3172 immutable len = fd.d_namlen;
3173 else
3174 immutable len = (() @trusted => core.stdc.string.strlen(fd.d_name.ptr))();
3176 _name = buildPath(path, (() @trusted => fd.d_name.ptr[0 .. len])());
3178 _didLStat = false;
3179 _didStat = false;
3181 //fd_d_type doesn't work for all file systems,
3182 //in which case the result is DT_UNKOWN. But we
3183 //can determine the correct type from lstat, so
3184 //we'll only set the dtype here if we could
3185 //correctly determine it (not lstat in the case
3186 //of DT_UNKNOWN in case we don't ever actually
3187 //need the dtype, thus potentially avoiding the
3188 //cost of calling lstat).
3189 static if (__traits(compiles, fd.d_type != DT_UNKNOWN))
3191 if (fd.d_type != DT_UNKNOWN)
3193 _dType = fd.d_type;
3194 _dTypeSet = true;
3196 else
3197 _dTypeSet = false;
3199 else
3201 // e.g. Solaris does not have the d_type member
3202 _dTypeSet = false;
3206 @property string name() const pure nothrow
3208 return _name;
3211 @property bool isDir()
3213 _ensureStatOrLStatDone();
3215 return (_statBuf.st_mode & S_IFMT) == S_IFDIR;
3218 @property bool isFile()
3220 _ensureStatOrLStatDone();
3222 return (_statBuf.st_mode & S_IFMT) == S_IFREG;
3225 @property bool isSymlink()
3227 _ensureLStatDone();
3229 return (_lstatMode & S_IFMT) == S_IFLNK;
3232 @property ulong size()
3234 _ensureStatDone();
3235 return _statBuf.st_size;
3238 @property SysTime timeStatusChanged()
3240 _ensureStatDone();
3242 return statTimeToStdTime!'c'(_statBuf);
3245 @property SysTime timeLastAccessed()
3247 _ensureStatDone();
3249 return statTimeToStdTime!'a'(_statBuf);
3252 @property SysTime timeLastModified()
3254 _ensureStatDone();
3256 return statTimeToStdTime!'m'(_statBuf);
3259 @property uint attributes()
3261 _ensureStatDone();
3263 return _statBuf.st_mode;
3266 @property uint linkAttributes()
3268 _ensureLStatDone();
3270 return _lstatMode;
3273 @property stat_t statBuf()
3275 _ensureStatDone();
3277 return _statBuf;
3280 private:
3282 This is to support lazy evaluation, because doing stat's is
3283 expensive and not always needed.
3285 void _ensureStatDone() @safe
3287 import std.exception : enforce;
3289 static auto trustedStat(in char[] path, stat_t* buf) @trusted
3291 return stat(path.tempCString(), buf);
3293 if (_didStat)
3294 return;
3296 enforce(trustedStat(_name, &_statBuf) == 0,
3297 "Failed to stat file `" ~ _name ~ "'");
3299 _didStat = true;
3303 This is to support lazy evaluation, because doing stat's is
3304 expensive and not always needed.
3306 Try both stat and lstat for isFile and isDir
3307 to detect broken symlinks.
3309 void _ensureStatOrLStatDone()
3311 if (_didStat)
3312 return;
3314 if ( stat(_name.tempCString(), &_statBuf) != 0 )
3316 _ensureLStatDone();
3318 _statBuf = stat_t.init;
3319 _statBuf.st_mode = S_IFLNK;
3321 else
3323 _didStat = true;
3328 This is to support lazy evaluation, because doing stat's is
3329 expensive and not always needed.
3331 void _ensureLStatDone()
3333 import std.exception : enforce;
3335 if (_didLStat)
3336 return;
3338 stat_t statbuf = void;
3340 enforce(lstat(_name.tempCString(), &statbuf) == 0,
3341 "Failed to stat file `" ~ _name ~ "'");
3343 _lstatMode = statbuf.st_mode;
3345 _dTypeSet = true;
3346 _didLStat = true;
3349 string _name; /// The file or directory represented by this DirEntry.
3351 stat_t _statBuf = void; /// The result of stat().
3352 uint _lstatMode; /// The stat mode from lstat().
3353 ubyte _dType; /// The type of the file.
3355 bool _didLStat = false; /// Whether lstat() has been called for this DirEntry.
3356 bool _didStat = false; /// Whether stat() has been called for this DirEntry.
3357 bool _dTypeSet = false; /// Whether the dType of the file has been set.
3361 @system unittest
3363 version (Windows)
3365 if ("C:\\Program Files\\".exists)
3367 auto de = DirEntry("C:\\Program Files\\");
3368 assert(!de.isFile);
3369 assert(de.isDir);
3370 assert(!de.isSymlink);
3373 if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
3375 auto de = DirEntry("C:\\Documents and Settings\\");
3376 assert(de.isSymlink);
3379 if ("C:\\Windows\\system.ini".exists)
3381 auto de = DirEntry("C:\\Windows\\system.ini");
3382 assert(de.isFile);
3383 assert(!de.isDir);
3384 assert(!de.isSymlink);
3387 else version (Posix)
3389 import std.exception : assertThrown;
3391 if (system_directory.exists)
3394 auto de = DirEntry(system_directory);
3395 assert(!de.isFile);
3396 assert(de.isDir);
3397 assert(!de.isSymlink);
3400 immutable symfile = deleteme ~ "_slink\0";
3401 scope(exit) if (symfile.exists) symfile.remove();
3403 core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
3406 auto de = DirEntry(symfile);
3407 assert(!de.isFile);
3408 assert(de.isDir);
3409 assert(de.isSymlink);
3412 symfile.remove();
3413 core.sys.posix.unistd.symlink((deleteme ~ "_broken_symlink\0").ptr, symfile.ptr);
3416 //Issue 8298
3417 DirEntry de = DirEntry(symfile);
3419 assert(!de.isFile);
3420 assert(!de.isDir);
3421 assert(de.isSymlink);
3422 assertThrown(de.size);
3423 assertThrown(de.timeStatusChanged);
3424 assertThrown(de.timeLastAccessed);
3425 assertThrown(de.timeLastModified);
3426 assertThrown(de.attributes);
3427 assertThrown(de.statBuf);
3428 assert(symfile.exists);
3429 symfile.remove();
3433 if (system_file.exists)
3435 auto de = DirEntry(system_file);
3436 assert(de.isFile);
3437 assert(!de.isDir);
3438 assert(!de.isSymlink);
3443 alias PreserveAttributes = Flag!"preserveAttributes";
3445 version (StdDdoc)
3447 /// Defaults to $(D Yes.preserveAttributes) on Windows, and the opposite on all other platforms.
3448 PreserveAttributes preserveAttributesDefault;
3450 else version (Windows)
3452 enum preserveAttributesDefault = Yes.preserveAttributes;
3454 else
3456 enum preserveAttributesDefault = No.preserveAttributes;
3459 /***************************************************
3460 Copy file $(D from) _to file $(D to). File timestamps are preserved.
3461 File attributes are preserved, if $(D preserve) equals $(D Yes.preserveAttributes).
3462 On Windows only $(D Yes.preserveAttributes) (the default on Windows) is supported.
3463 If the target file exists, it is overwritten.
3465 Params:
3466 from = string or range of characters representing the existing file name
3467 to = string or range of characters representing the target file name
3468 preserve = whether to _preserve the file attributes
3470 Throws: $(D FileException) on error.
3472 void copy(RF, RT)(RF from, RT to, PreserveAttributes preserve = preserveAttributesDefault)
3473 if (isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) && !isConvertibleToString!RF &&
3474 isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) && !isConvertibleToString!RT)
3476 // Place outside of @trusted block
3477 auto fromz = from.tempCString!FSChar();
3478 auto toz = to.tempCString!FSChar();
3480 static if (isNarrowString!RF && is(Unqual!(ElementEncodingType!RF) == char))
3481 alias f = from;
3482 else
3483 enum string f = null;
3485 static if (isNarrowString!RT && is(Unqual!(ElementEncodingType!RT) == char))
3486 alias t = to;
3487 else
3488 enum string t = null;
3490 copyImpl(f, t, fromz, toz, preserve);
3493 /// ditto
3494 void copy(RF, RT)(auto ref RF from, auto ref RT to, PreserveAttributes preserve = preserveAttributesDefault)
3495 if (isConvertibleToString!RF || isConvertibleToString!RT)
3497 import std.meta : staticMap;
3498 alias Types = staticMap!(convertToString, RF, RT);
3499 copy!Types(from, to, preserve);
3502 @safe unittest // issue 15319
3504 assert(__traits(compiles, copy("from.txt", "to.txt")));
3507 private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, const(FSChar)* toz,
3508 PreserveAttributes preserve) @trusted
3510 version (Windows)
3512 assert(preserve == Yes.preserveAttributes);
3513 immutable result = CopyFileW(fromz, toz, false);
3514 if (!result)
3516 import core.stdc.wchar_ : wcslen;
3517 import std.conv : to;
3519 if (!t)
3520 t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
3522 throw new FileException(t);
3525 else version (Posix)
3527 static import core.stdc.stdio;
3528 import std.conv : to, octal;
3530 immutable fdr = core.sys.posix.fcntl.open(fromz, O_RDONLY);
3531 cenforce(fdr != -1, f, fromz);
3532 scope(exit) core.sys.posix.unistd.close(fdr);
3534 stat_t statbufr = void;
3535 cenforce(fstat(fdr, &statbufr) == 0, f, fromz);
3536 //cenforce(core.sys.posix.sys.stat.fstat(fdr, &statbufr) == 0, f, fromz);
3538 immutable fdw = core.sys.posix.fcntl.open(toz,
3539 O_CREAT | O_WRONLY, octal!666);
3540 cenforce(fdw != -1, t, toz);
3542 scope(failure) core.sys.posix.unistd.close(fdw);
3544 stat_t statbufw = void;
3545 cenforce(fstat(fdw, &statbufw) == 0, t, toz);
3546 if (statbufr.st_dev == statbufw.st_dev && statbufr.st_ino == statbufw.st_ino)
3547 throw new FileException(t, "Source and destination are the same file");
3550 scope(failure) core.stdc.stdio.remove(toz);
3552 scope(failure) core.sys.posix.unistd.close(fdw);
3553 cenforce(ftruncate(fdw, 0) == 0, t, toz);
3555 auto BUFSIZ = 4096u * 16;
3556 auto buf = core.stdc.stdlib.malloc(BUFSIZ);
3557 if (!buf)
3559 BUFSIZ = 4096;
3560 buf = core.stdc.stdlib.malloc(BUFSIZ);
3561 if (!buf)
3563 import core.exception : onOutOfMemoryError;
3564 onOutOfMemoryError();
3567 scope(exit) core.stdc.stdlib.free(buf);
3569 for (auto size = statbufr.st_size; size; )
3571 immutable toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size;
3572 cenforce(
3573 core.sys.posix.unistd.read(fdr, buf, toxfer) == toxfer
3574 && core.sys.posix.unistd.write(fdw, buf, toxfer) == toxfer,
3575 f, fromz);
3576 assert(size >= toxfer);
3577 size -= toxfer;
3579 if (preserve)
3580 cenforce(fchmod(fdw, to!mode_t(statbufr.st_mode)) == 0, f, fromz);
3583 cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz);
3585 utimbuf utim = void;
3586 utim.actime = cast(time_t) statbufr.st_atime;
3587 utim.modtime = cast(time_t) statbufr.st_mtime;
3589 cenforce(utime(toz, &utim) != -1, f, fromz);
3593 @safe unittest
3595 import std.algorithm, std.file; // issue 14817
3596 auto t1 = deleteme, t2 = deleteme~"2";
3597 scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
3598 write(t1, "11");
3599 copy(t1, t2);
3600 assert(readText(t2) == "11");
3601 write(t1, "2");
3602 copy(t1, t2);
3603 assert(readText(t2) == "2");
3605 import std.utf : byChar;
3606 copy(t1.byChar, t2.byChar);
3607 assert(readText(t2.byChar) == "2");
3610 @safe version (Posix) @safe unittest //issue 11434
3612 import std.conv : octal;
3613 auto t1 = deleteme, t2 = deleteme~"2";
3614 scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
3615 write(t1, "1");
3616 setAttributes(t1, octal!767);
3617 copy(t1, t2, Yes.preserveAttributes);
3618 assert(readText(t2) == "1");
3619 assert(getAttributes(t2) == octal!100767);
3622 @safe unittest // issue 15865
3624 import std.exception : assertThrown;
3625 auto t = deleteme;
3626 write(t, "a");
3627 scope(exit) t.remove();
3628 assertThrown!FileException(copy(t, t));
3629 assert(readText(t) == "a");
3633 Remove directory and all of its content and subdirectories,
3634 recursively.
3636 Throws:
3637 $(D FileException) if there is an error (including if the given
3638 file is not a directory).
3640 void rmdirRecurse(in char[] pathname)
3642 //No references to pathname will be kept after rmdirRecurse,
3643 //so the cast is safe
3644 rmdirRecurse(DirEntry(cast(string) pathname));
3648 Remove directory and all of its content and subdirectories,
3649 recursively.
3651 Throws:
3652 $(D FileException) if there is an error (including if the given
3653 file is not a directory).
3655 void rmdirRecurse(ref DirEntry de)
3657 if (!de.isDir)
3658 throw new FileException(de.name, "Not a directory");
3660 if (de.isSymlink)
3662 version (Windows)
3663 rmdir(de.name);
3664 else
3665 remove(de.name);
3667 else
3669 // all children, recursively depth-first
3670 foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false))
3672 attrIsDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name);
3675 // the dir itself
3676 rmdir(de.name);
3679 ///ditto
3680 //Note, without this overload, passing an RValue DirEntry still works, but
3681 //actually fully reconstructs a DirEntry inside the
3682 //"rmdirRecurse(in char[] pathname)" implementation. That is needlessly
3683 //expensive.
3684 //A DirEntry is a bit big (72B), so keeping the "by ref" signature is desirable.
3685 void rmdirRecurse(DirEntry de)
3687 rmdirRecurse(de);
3690 version (Windows) @system unittest
3692 import std.exception : enforce;
3693 auto d = deleteme ~ r".dir\a\b\c\d\e\f\g";
3694 mkdirRecurse(d);
3695 rmdirRecurse(deleteme ~ ".dir");
3696 enforce(!exists(deleteme ~ ".dir"));
3699 version (Posix) @system unittest
3701 import std.exception : enforce, collectException;
3702 import std.process : executeShell;
3703 collectException(rmdirRecurse(deleteme));
3704 auto d = deleteme~"/a/b/c/d/e/f/g";
3705 enforce(collectException(mkdir(d)));
3706 mkdirRecurse(d);
3707 core.sys.posix.unistd.symlink((deleteme~"/a/b/c\0").ptr,
3708 (deleteme~"/link\0").ptr);
3709 rmdirRecurse(deleteme~"/link");
3710 enforce(exists(d));
3711 rmdirRecurse(deleteme);
3712 enforce(!exists(deleteme));
3714 d = deleteme~"/a/b/c/d/e/f/g";
3715 mkdirRecurse(d);
3716 version (Android) string link_cmd = "ln -s ";
3717 else string link_cmd = "ln -sf ";
3718 executeShell(link_cmd~deleteme~"/a/b/c "~deleteme~"/link");
3719 rmdirRecurse(deleteme);
3720 enforce(!exists(deleteme));
3723 @system unittest
3725 void[] buf;
3727 buf = new void[10];
3728 (cast(byte[]) buf)[] = 3;
3729 string unit_file = deleteme ~ "-unittest_write.tmp";
3730 if (exists(unit_file)) remove(unit_file);
3731 write(unit_file, buf);
3732 void[] buf2 = read(unit_file);
3733 assert(buf == buf2);
3735 string unit2_file = deleteme ~ "-unittest_write2.tmp";
3736 copy(unit_file, unit2_file);
3737 buf2 = read(unit2_file);
3738 assert(buf == buf2);
3740 remove(unit_file);
3741 assert(!exists(unit_file));
3742 remove(unit2_file);
3743 assert(!exists(unit2_file));
3747 * Dictates directory spanning policy for $(D_PARAM dirEntries) (see below).
3749 enum SpanMode
3751 /** Only spans one directory. */
3752 shallow,
3753 /** Spans the directory in
3754 $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Post-order,
3755 _depth-first $(B post)-order), i.e. the content of any
3756 subdirectory is spanned before that subdirectory itself. Useful
3757 e.g. when recursively deleting files. */
3758 depth,
3759 /** Spans the directory in
3760 $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Pre-order, depth-first
3761 $(B pre)-order), i.e. the content of any subdirectory is spanned
3762 right after that subdirectory itself.
3764 Note that $(D SpanMode.breadth) will not result in all directory
3765 members occurring before any subdirectory members, i.e. it is not
3766 _true
3767 $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search,
3768 _breadth-first traversal).
3770 breadth,
3773 private struct DirIteratorImpl
3775 import std.array : Appender, appender;
3776 SpanMode _mode;
3777 // Whether we should follow symlinked directories while iterating.
3778 // It also indicates whether we should avoid functions which call
3779 // stat (since we should only need lstat in this case and it would
3780 // be more efficient to not call stat in addition to lstat).
3781 bool _followSymlink;
3782 DirEntry _cur;
3783 Appender!(DirHandle[]) _stack;
3784 Appender!(DirEntry[]) _stashed; //used in depth first mode
3785 //stack helpers
3786 void pushExtra(DirEntry de){ _stashed.put(de); }
3787 //ditto
3788 bool hasExtra(){ return !_stashed.data.empty; }
3789 //ditto
3790 DirEntry popExtra()
3792 DirEntry de;
3793 de = _stashed.data[$-1];
3794 _stashed.shrinkTo(_stashed.data.length - 1);
3795 return de;
3798 version (Windows)
3800 struct DirHandle
3802 string dirpath;
3803 HANDLE h;
3806 bool stepIn(string directory)
3808 import std.path : chainPath;
3810 auto search_pattern = chainPath(directory, "*.*");
3811 WIN32_FIND_DATAW findinfo;
3812 HANDLE h = FindFirstFileW(search_pattern.tempCString!FSChar(), &findinfo);
3813 cenforce(h != INVALID_HANDLE_VALUE, directory);
3814 _stack.put(DirHandle(directory, h));
3815 return toNext(false, &findinfo);
3818 bool next()
3820 if (_stack.data.empty)
3821 return false;
3822 WIN32_FIND_DATAW findinfo;
3823 return toNext(true, &findinfo);
3826 bool toNext(bool fetch, WIN32_FIND_DATAW* findinfo)
3828 import core.stdc.wchar_ : wcscmp;
3830 if (fetch)
3832 if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE)
3834 popDirStack();
3835 return false;
3838 while ( wcscmp(findinfo.cFileName.ptr, ".") == 0
3839 || wcscmp(findinfo.cFileName.ptr, "..") == 0)
3840 if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE)
3842 popDirStack();
3843 return false;
3845 _cur = DirEntry(_stack.data[$-1].dirpath, findinfo);
3846 return true;
3849 void popDirStack()
3851 assert(!_stack.data.empty);
3852 FindClose(_stack.data[$-1].h);
3853 _stack.shrinkTo(_stack.data.length-1);
3856 void releaseDirStack()
3858 foreach ( d; _stack.data)
3859 FindClose(d.h);
3862 bool mayStepIn()
3864 return _followSymlink ? _cur.isDir : _cur.isDir && !_cur.isSymlink;
3867 else version (Posix)
3869 struct DirHandle
3871 string dirpath;
3872 DIR* h;
3875 bool stepIn(string directory)
3877 auto h = directory.length ? opendir(directory.tempCString()) : opendir(".");
3878 cenforce(h, directory);
3879 _stack.put(DirHandle(directory, h));
3880 return next();
3883 bool next()
3885 if (_stack.data.empty)
3886 return false;
3887 for (dirent* fdata; (fdata = readdir(_stack.data[$-1].h)) != null; )
3889 // Skip "." and ".."
3890 if (core.stdc.string.strcmp(fdata.d_name.ptr, ".") &&
3891 core.stdc.string.strcmp(fdata.d_name.ptr, "..") )
3893 _cur = DirEntry(_stack.data[$-1].dirpath, fdata);
3894 return true;
3897 popDirStack();
3898 return false;
3901 void popDirStack()
3903 assert(!_stack.data.empty);
3904 closedir(_stack.data[$-1].h);
3905 _stack.shrinkTo(_stack.data.length-1);
3908 void releaseDirStack()
3910 foreach ( d; _stack.data)
3911 closedir(d.h);
3914 bool mayStepIn()
3916 return _followSymlink ? _cur.isDir : attrIsDir(_cur.linkAttributes);
3920 this(R)(R pathname, SpanMode mode, bool followSymlink)
3921 if (isInputRange!R && isSomeChar!(ElementEncodingType!R))
3923 _mode = mode;
3924 _followSymlink = followSymlink;
3925 _stack = appender(cast(DirHandle[])[]);
3926 if (_mode == SpanMode.depth)
3927 _stashed = appender(cast(DirEntry[])[]);
3929 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
3930 alias pathnameStr = pathname;
3931 else
3933 import std.array : array;
3934 string pathnameStr = pathname.array;
3936 if (stepIn(pathnameStr))
3938 if (_mode == SpanMode.depth)
3939 while (mayStepIn())
3941 auto thisDir = _cur;
3942 if (stepIn(_cur.name))
3944 pushExtra(thisDir);
3946 else
3947 break;
3951 @property bool empty(){ return _stashed.data.empty && _stack.data.empty; }
3952 @property DirEntry front(){ return _cur; }
3953 void popFront()
3955 switch (_mode)
3957 case SpanMode.depth:
3958 if (next())
3960 while (mayStepIn())
3962 auto thisDir = _cur;
3963 if (stepIn(_cur.name))
3965 pushExtra(thisDir);
3967 else
3968 break;
3971 else if (hasExtra())
3972 _cur = popExtra();
3973 break;
3974 case SpanMode.breadth:
3975 if (mayStepIn())
3977 if (!stepIn(_cur.name))
3978 while (!empty && !next()){}
3980 else
3981 while (!empty && !next()){}
3982 break;
3983 default:
3984 next();
3988 ~this()
3990 releaseDirStack();
3994 struct DirIterator
3996 private:
3997 RefCounted!(DirIteratorImpl, RefCountedAutoInitialize.no) impl;
3998 this(string pathname, SpanMode mode, bool followSymlink)
4000 impl = typeof(impl)(pathname, mode, followSymlink);
4002 public:
4003 @property bool empty(){ return impl.empty; }
4004 @property DirEntry front(){ return impl.front; }
4005 void popFront(){ impl.popFront(); }
4009 Returns an input range of $(D DirEntry) that lazily iterates a given directory,
4010 also provides two ways of foreach iteration. The iteration variable can be of
4011 type $(D string) if only the name is needed, or $(D DirEntry)
4012 if additional details are needed. The span _mode dictates how the
4013 directory is traversed. The name of each iterated directory entry
4014 contains the absolute _path.
4016 Params:
4017 path = The directory to iterate over.
4018 If empty, the current directory will be iterated.
4020 pattern = Optional string with wildcards, such as $(RED
4021 "*.d"). When present, it is used to filter the
4022 results by their file name. The supported wildcard
4023 strings are described under $(REF globMatch,
4024 std,_path).
4026 mode = Whether the directory's sub-directories should be
4027 iterated in depth-first port-order ($(LREF depth)),
4028 depth-first pre-order ($(LREF breadth)), or not at all
4029 ($(LREF shallow)).
4031 followSymlink = Whether symbolic links which point to directories
4032 should be treated as directories and their contents
4033 iterated over.
4035 Throws:
4036 $(D FileException) if the directory does not exist.
4038 Example:
4039 --------------------
4040 // Iterate a directory in depth
4041 foreach (string name; dirEntries("destroy/me", SpanMode.depth))
4043 remove(name);
4046 // Iterate the current directory in breadth
4047 foreach (string name; dirEntries("", SpanMode.breadth))
4049 writeln(name);
4052 // Iterate a directory and get detailed info about it
4053 foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth))
4055 writeln(e.name, "\t", e.size);
4058 // Iterate over all *.d files in current directory and all its subdirectories
4059 auto dFiles = dirEntries("", SpanMode.depth).filter!(f => f.name.endsWith(".d"));
4060 foreach (d; dFiles)
4061 writeln(d.name);
4063 // Hook it up with std.parallelism to compile them all in parallel:
4064 foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread
4066 string cmd = "dmd -c " ~ d.name;
4067 writeln(cmd);
4068 std.process.system(cmd);
4071 // Iterate over all D source files in current directory and all its
4072 // subdirectories
4073 auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
4074 foreach (d; dFiles)
4075 writeln(d.name);
4076 --------------------
4078 auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
4080 return DirIterator(path, mode, followSymlink);
4083 /// Duplicate functionality of D1's $(D std.file.listdir()):
4084 @safe unittest
4086 string[] listdir(string pathname)
4088 import std.algorithm;
4089 import std.array;
4090 import std.file;
4091 import std.path;
4093 return std.file.dirEntries(pathname, SpanMode.shallow)
4094 .filter!(a => a.isFile)
4095 .map!(a => std.path.baseName(a.name))
4096 .array;
4099 void main(string[] args)
4101 import std.stdio;
4103 string[] files = listdir(args[1]);
4104 writefln("%s", files);
4108 @system unittest
4110 import std.algorithm.comparison : equal;
4111 import std.algorithm.iteration : map;
4112 import std.algorithm.searching : startsWith;
4113 import std.array : array;
4114 import std.conv : to;
4115 import std.path : buildPath, absolutePath;
4116 import std.file : dirEntries;
4117 import std.process : thisProcessID;
4118 import std.range.primitives : walkLength;
4120 version (Android)
4121 string testdir = deleteme; // This has to be an absolute path when
4122 // called from a shared library on Android,
4123 // ie an apk
4124 else
4125 string testdir = "deleteme.dmd.unittest.std.file" ~ to!string(thisProcessID); // needs to be relative
4126 mkdirRecurse(buildPath(testdir, "somedir"));
4127 scope(exit) rmdirRecurse(testdir);
4128 write(buildPath(testdir, "somefile"), null);
4129 write(buildPath(testdir, "somedir", "somedeepfile"), null);
4131 // testing range interface
4132 size_t equalEntries(string relpath, SpanMode mode)
4134 import std.exception : enforce;
4135 auto len = enforce(walkLength(dirEntries(absolutePath(relpath), mode)));
4136 assert(walkLength(dirEntries(relpath, mode)) == len);
4137 assert(equal(
4138 map!(a => absolutePath(a.name))(dirEntries(relpath, mode)),
4139 map!(a => a.name)(dirEntries(absolutePath(relpath), mode))));
4140 return len;
4143 assert(equalEntries(testdir, SpanMode.shallow) == 2);
4144 assert(equalEntries(testdir, SpanMode.depth) == 3);
4145 assert(equalEntries(testdir, SpanMode.breadth) == 3);
4147 // testing opApply
4148 foreach (string name; dirEntries(testdir, SpanMode.breadth))
4150 //writeln(name);
4151 assert(name.startsWith(testdir));
4153 foreach (DirEntry e; dirEntries(absolutePath(testdir), SpanMode.breadth))
4155 //writeln(name);
4156 assert(e.isFile || e.isDir, e.name);
4159 //issue 7264
4160 foreach (string name; dirEntries(testdir, "*.d", SpanMode.breadth))
4164 foreach (entry; dirEntries(testdir, SpanMode.breadth))
4166 static assert(is(typeof(entry) == DirEntry));
4168 //issue 7138
4169 auto a = array(dirEntries(testdir, SpanMode.shallow));
4171 // issue 11392
4172 auto dFiles = dirEntries(testdir, SpanMode.shallow);
4173 foreach (d; dFiles){}
4175 // issue 15146
4176 dirEntries("", SpanMode.shallow).walkLength();
4179 /// Ditto
4180 auto dirEntries(string path, string pattern, SpanMode mode,
4181 bool followSymlink = true)
4183 import std.algorithm.iteration : filter;
4184 import std.path : globMatch, baseName;
4186 bool f(DirEntry de) { return globMatch(baseName(de.name), pattern); }
4187 return filter!f(DirIterator(path, mode, followSymlink));
4190 @system unittest
4192 import std.stdio : writefln;
4193 immutable dpath = deleteme ~ "_dir";
4194 immutable fpath = deleteme ~ "_file";
4195 immutable sdpath = deleteme ~ "_sdir";
4196 immutable sfpath = deleteme ~ "_sfile";
4197 scope(exit)
4199 if (dpath.exists) rmdirRecurse(dpath);
4200 if (fpath.exists) remove(fpath);
4201 if (sdpath.exists) remove(sdpath);
4202 if (sfpath.exists) remove(sfpath);
4205 mkdir(dpath);
4206 write(fpath, "hello world");
4207 version (Posix)
4209 core.sys.posix.unistd.symlink((dpath ~ '\0').ptr, (sdpath ~ '\0').ptr);
4210 core.sys.posix.unistd.symlink((fpath ~ '\0').ptr, (sfpath ~ '\0').ptr);
4213 static struct Flags { bool dir, file, link; }
4214 auto tests = [dpath : Flags(true), fpath : Flags(false, true)];
4215 version (Posix)
4217 tests[sdpath] = Flags(true, false, true);
4218 tests[sfpath] = Flags(false, true, true);
4221 auto past = Clock.currTime() - 2.seconds;
4222 auto future = past + 4.seconds;
4224 foreach (path, flags; tests)
4226 auto de = DirEntry(path);
4227 assert(de.name == path);
4228 assert(de.isDir == flags.dir);
4229 assert(de.isFile == flags.file);
4230 assert(de.isSymlink == flags.link);
4232 assert(de.isDir == path.isDir);
4233 assert(de.isFile == path.isFile);
4234 assert(de.isSymlink == path.isSymlink);
4235 assert(de.size == path.getSize());
4236 assert(de.attributes == getAttributes(path));
4237 assert(de.linkAttributes == getLinkAttributes(path));
4239 scope(failure) writefln("[%s] [%s] [%s] [%s]", past, de.timeLastAccessed, de.timeLastModified, future);
4240 assert(de.timeLastAccessed > past);
4241 assert(de.timeLastAccessed < future);
4242 assert(de.timeLastModified > past);
4243 assert(de.timeLastModified < future);
4245 assert(attrIsDir(de.attributes) == flags.dir);
4246 assert(attrIsDir(de.linkAttributes) == (flags.dir && !flags.link));
4247 assert(attrIsFile(de.attributes) == flags.file);
4248 assert(attrIsFile(de.linkAttributes) == (flags.file && !flags.link));
4249 assert(!attrIsSymlink(de.attributes));
4250 assert(attrIsSymlink(de.linkAttributes) == flags.link);
4252 version (Windows)
4254 assert(de.timeCreated > past);
4255 assert(de.timeCreated < future);
4257 else version (Posix)
4259 assert(de.timeStatusChanged > past);
4260 assert(de.timeStatusChanged < future);
4261 assert(de.attributes == de.statBuf.st_mode);
4268 * Reads a file line by line and parses the line into a single value or a
4269 * $(REF Tuple, std,typecons) of values depending on the length of `Types`.
4270 * The lines are parsed using the specified format string. The format string is
4271 * passed to $(REF formattedRead, std,_format), and therefore must conform to the
4272 * _format string specification outlined in $(MREF std, _format).
4274 * Params:
4275 * Types = the types that each of the elements in the line should be returned as
4276 * filename = the name of the file to read
4277 * format = the _format string to use when reading
4279 * Returns:
4280 * If only one type is passed, then an array of that type. Otherwise, an
4281 * array of $(REF Tuple, std,typecons)s.
4283 * Throws:
4284 * `Exception` if the format string is malformed. Also, throws `Exception`
4285 * if any of the lines in the file are not fully consumed by the call
4286 * to $(REF formattedRead, std,_format). Meaning that no empty lines or lines
4287 * with extra characters are allowed.
4289 Select!(Types.length == 1, Types[0][], Tuple!(Types)[])
4290 slurp(Types...)(string filename, in char[] format)
4292 import std.array : appender;
4293 import std.conv : text;
4294 import std.exception : enforce;
4295 import std.format : formattedRead;
4296 import std.stdio : File;
4298 auto app = appender!(typeof(return))();
4299 ElementType!(typeof(return)) toAdd;
4300 auto f = File(filename);
4301 scope(exit) f.close();
4302 foreach (line; f.byLine())
4304 formattedRead(line, format, &toAdd);
4305 enforce(line.empty,
4306 text("Trailing characters at the end of line: `", line,
4307 "'"));
4308 app.put(toAdd);
4310 return app.data;
4314 @system unittest
4316 import std.typecons : tuple;
4318 scope(exit)
4320 assert(exists(deleteme));
4321 remove(deleteme);
4324 write(deleteme, "12 12.25\n345 1.125"); // deleteme is the name of a temporary file
4326 // Load file; each line is an int followed by comma, whitespace and a
4327 // double.
4328 auto a = slurp!(int, double)(deleteme, "%s %s");
4329 assert(a.length == 2);
4330 assert(a[0] == tuple(12, 12.25));
4331 assert(a[1] == tuple(345, 1.125));
4336 Returns the path to a directory for temporary files.
4338 On Windows, this function returns the result of calling the Windows API function
4339 $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992.aspx, $(D GetTempPath)).
4341 On POSIX platforms, it searches through the following list of directories
4342 and returns the first one which is found to exist:
4343 $(OL
4344 $(LI The directory given by the $(D TMPDIR) environment variable.)
4345 $(LI The directory given by the $(D TEMP) environment variable.)
4346 $(LI The directory given by the $(D TMP) environment variable.)
4347 $(LI $(D /tmp))
4348 $(LI $(D /var/tmp))
4349 $(LI $(D /usr/tmp))
4352 On all platforms, $(D tempDir) returns $(D ".") on failure, representing
4353 the current working directory.
4355 The return value of the function is cached, so the procedures described
4356 above will only be performed the first time the function is called. All
4357 subsequent runs will return the same string, regardless of whether
4358 environment variables and directory structures have changed in the
4359 meantime.
4361 The POSIX $(D tempDir) algorithm is inspired by Python's
4362 $(LINK2 http://docs.python.org/library/tempfile.html#tempfile.tempdir, $(D tempfile.tempdir)).
4364 string tempDir() @trusted
4366 static string cache;
4367 if (cache is null)
4369 version (Windows)
4371 import std.conv : to;
4372 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992(v=vs.85).aspx
4373 wchar[MAX_PATH + 2] buf;
4374 DWORD len = GetTempPathW(buf.length, buf.ptr);
4375 if (len) cache = buf[0 .. len].to!string;
4377 else version (Posix)
4379 import std.process : environment;
4380 // This function looks through the list of alternative directories
4381 // and returns the first one which exists and is a directory.
4382 static string findExistingDir(T...)(lazy T alternatives)
4384 foreach (dir; alternatives)
4385 if (!dir.empty && exists(dir)) return dir;
4386 return null;
4389 cache = findExistingDir(environment.get("TMPDIR"),
4390 environment.get("TEMP"),
4391 environment.get("TMP"),
4392 "/tmp",
4393 "/var/tmp",
4394 "/usr/tmp");
4396 else static assert(false, "Unsupported platform");
4398 if (cache is null) cache = getcwd();
4400 return cache;