1 // Written in the D programming language.
4 Utilities for manipulating files and scanning directories. Functions
5 in this module handle files as a unit, e.g., read or write one _file
6 at a time. For opening files and manipulating them via handles refer
7 to module $(MREF std, stdio).
9 $(SCRIPT inhibitQuickIndex = 1;)
11 $(TR $(TH Category) $(TH Functions))
12 $(TR $(TD General) $(TD
20 $(TR $(TD Directories) $(TD
39 $(TR $(TD Symlinks) $(TD
43 $(TR $(TD Attributes) $(TD
48 $(LREF getLinkAttributes)
52 $(TR $(TD Timestamp) $(TD
56 $(LREF timeLastModified)
61 $(LREF PreserveAttributes)
67 Copyright: Copyright Digital Mars 2007 - 2011.
68 See_Also: The $(HTTP ddili.org/ders/d.en/files.html, official tutorial) for an
69 introduction to working with files in D, module
70 $(MREF std, stdio) for opening files and manipulating them via handles,
71 and module $(MREF std, path) for manipulating path strings.
73 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
74 Authors: $(HTTP digitalmars.com, Walter Bright),
75 $(HTTP erdani.org, Andrei Alexandrescu),
77 Source: $(PHOBOSSRC std/_file.d)
81 import core
.stdc
.errno
, core
.stdc
.stdlib
, core
.stdc
.string
;
82 import core
.time
: abs
, dur
, hnsecs
, seconds
;
84 import std
.datetime
.date
: DateTime
;
85 import std
.datetime
.systime
: Clock
, SysTime
, unixTimeToStdTime
;
86 import std
.internal
.cstring
;
88 import std
.range
.primitives
;
94 import core
.sys
.windows
.windows
, std
.windows
.syserror
;
98 import core
.sys
.posix
.dirent
, core
.sys
.posix
.fcntl
, core
.sys
.posix
.sys
.stat
,
99 core
.sys
.posix
.sys
.time
, core
.sys
.posix
.unistd
, core
.sys
.posix
.utime
;
102 static assert(false, "Module " ~ .stringof
~ " not implemented for this OS.");
104 // Character type used for operating system filesystem APIs
107 private alias FSChar
= wchar;
111 private alias FSChar
= char;
116 // Purposefully not documented. Use at your own risk
117 @property string
deleteme() @safe
119 import std
.conv
: to
;
120 import std
.path
: buildPath
;
121 import std
.process
: thisProcessID
;
123 static _deleteme
= "deleteme.dmd.unittest.pid";
124 static _first
= true;
128 _deleteme
= buildPath(tempDir(), _deleteme
) ~ to
!string(thisProcessID
);
135 version (unittest) private struct TestAliasedString
137 string
get() @safe @nogc pure nothrow { return _s
; }
145 package enum system_directory
= "/system/etc";
146 package enum system_file
= "/system/etc/hosts";
150 package enum system_directory
= "/usr/include";
151 package enum system_file
= "/usr/include/assert.h";
156 Exception thrown for file I/O errors.
158 class FileException
: Exception
160 import std
.conv
: text
, to
;
165 immutable uint errno
;
167 private this(in char[] name
, in char[] msg
, string file
, size_t line
, uint errno
) @safe pure
170 super(name
.idup
, file
, line
);
172 super(text(name
, ": ", msg
), file
, line
);
178 Constructor which takes an error message.
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).
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
,
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__
)
226 throw new FileException(name
, .GetLastError(), file
, line
);
230 throw new FileException(name
, .errno
, file
, line
);
236 private T
cenforce(T
)(T condition
, const(char)[] name
, const(FSChar
)* namez
,
237 string file
= __FILE__
, size_t line
= __LINE__
)
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
);
254 private T
cenforce(T
)(T condition
, const(char)[] name
, const(FSChar
)* namez
,
255 string file
= __FILE__
, size_t line
= __LINE__
)
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
);
274 cenforce(false, null, null,
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)
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
);
305 return readImpl(null, name
.tempCString
!FSChar(), upTo
);
311 import std
.utf
: byChar
;
314 assert(exists(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);
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
);
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 {
345 minInitialAlloc
= 1024 * 4,
346 maxInitialAlloc
= size_t
.max
/ 2,
347 sizeIncrement
= 1024 * 16,
348 maxSlackMemoryAllowed
= 1024;
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
)
362 void[] result
= uninitializedArray
!(ubyte[])(initialAlloc
);
363 scope(failure
) GC
.free(result
.ptr
);
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;
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
]
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
406 DWORD sizeLow
= GetFileSize(hFile
, &sizeHigh
);
407 const bool result
= sizeLow
!= INVALID_FILE_SIZE
;
409 fileSize
= makeUlong(sizeLow
, sizeHigh
);
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
)
423 totalNumRead
+= chunkSize
;
429 AliasSeq
!(GENERIC_READ
,
430 FILE_SHARE_READ | FILE_SHARE_WRITE
, (SECURITY_ATTRIBUTES
*).init
,
431 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN
,
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
);
444 () @trusted { GC
.free(buf
.ptr
); } ();
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);
462 scope(exit
) if (exists(deleteme
)) remove(deleteme
);
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.
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
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
));
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");
508 S
readText(S
= string
, R
)(auto ref R name
)
509 if (isConvertibleToString
!R
)
511 return readText
!(S
, StringTypeOf
!R
)(name
);
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.
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);
539 writeImpl(null, name
.tempCString
!FSChar(), buffer
, false);
547 assert(exists(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
);
557 void write(R
)(auto ref R name
, const void[] buffer
)
558 if (isConvertibleToString
!R
)
560 write
!(StringTypeOf
!R
)(name
, buffer
);
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.
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);
586 writeImpl(null, name
.tempCString
!FSChar(), buffer
, true);
594 assert(exists(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 ];
602 assert(cast(int[]) read(deleteme
) == a
~ b
);
606 void append(R
)(auto ref R name
, const void[] buffer
)
607 if (isConvertibleToString
!R
)
609 append
!(StringTypeOf
!R
)(name
, buffer
);
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
;
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;
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
)
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
657 AliasSeq
!(GENERIC_WRITE
, 0, null, OPEN_ALWAYS
,
658 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN
,
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
,
669 AliasSeq
!(GENERIC_WRITE
, 0, null, CREATE_ALWAYS
,
670 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN
,
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;
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
)
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.
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))
711 enum string f
= null;
713 static if (isNarrowString
!RT
&& is(Unqual
!(ElementEncodingType
!RT
) == char))
716 enum string t
= null;
718 renameImpl(f
, t
, fromz
, toz
);
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
);
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
743 import std
.exception
: enforce
;
745 const result
= MoveFileExW(fromz
, toz
, MOVEFILE_REPLACE_EXISTING
);
748 import core
.stdc
.wchar_
: wcslen
;
749 import std
.conv
: to
, text
;
752 f
= to
!(typeof(f
))(fromz
[0 .. wcslen(fromz
)]);
755 t
= to
!(typeof(t
))(toz
[0 .. wcslen(toz
)]);
759 text("Attempting to rename file ", f
, " to ", t
)));
764 static import core
.stdc
.stdio
;
766 cenforce(core
.stdc
.stdio
.rename(fromz
, toz
) == 0, t
, toz
);
772 import std
.utf
: byWchar
;
774 auto t1
= deleteme
, t2
= deleteme
~"2";
775 scope(exit
) foreach (t
; [t1
, t2
]) if (t
.exists
) t
.remove();
778 assert(readText(t2
) == "1");
780 rename(t1
, t2
.byWchar
);
781 assert(readText(t2
) == "2");
785 /***************************************************
786 Delete file $(D name).
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());
800 removeImpl(null, name
.tempCString
!FSChar());
804 void remove(R
)(auto ref R name
)
805 if (isConvertibleToString
!R
)
807 remove
!(StringTypeOf
!R
)(name
);
812 static assert(__traits(compiles
, remove(TestAliasedString("foo"))));
815 private void removeImpl(const(char)[] name
, const(FSChar
)* namez
) @trusted
819 cenforce(DeleteFileW(namez
), name
, namez
);
823 static import core
.stdc
.stdio
;
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
);
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
));
869 version (Windows
) private ulong makeUlong(DWORD dwLow
, DWORD dwHigh
) @safe pure nothrow @nogc
873 li
.HighPart
= dwHigh
;
877 /***************************************************
878 Get size of file $(D name) in bytes.
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
)
891 with (getFileAttributesWin(name
))
892 return makeUlong(nFileSizeLow
, nFileSizeHigh
);
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))
906 stat_t statbuf
= void;
907 cenforce(trustedStat(namez
, statbuf
) == 0, names
, namez
);
908 return statbuf
.st_size
;
913 ulong getSize(R
)(auto ref R name
)
914 if (isConvertibleToString
!R
)
916 return getSize
!(StringTypeOf
!R
)(name
);
921 static assert(__traits(compiles
, getSize(TestAliasedString("foo"))));
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.
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;
947 static if (is(typeof(mixin(`statbuf.st_` ~ which
~ `timensec`))))
948 stdTime
+= mixin(`statbuf.st_` ~ which
~ `timensec`) / 100;
950 static if (is(typeof(mixin(`statbuf.st_` ~ which
~ `time_nsec`))))
951 stdTime
+= mixin(`statbuf.st_` ~ which
~ `time_nsec`) / 100;
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).
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.
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
)
978 import std
.datetime
.systime
: FILETIMEToSysTime
;
980 with (getFileAttributesWin(name
))
982 accessTime
= FILETIMEToSysTime(&ftLastAccessTime
);
983 modificationTime
= FILETIMEToSysTime(&ftLastWriteTime
);
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))
1000 cenforce(trustedStat(namez
, statbuf
) == 0, names
, namez
);
1002 accessTime
= statTimeToStdTime
!'a'(statbuf
);
1003 modificationTime
= statTimeToStdTime
!'m'(statbuf
);
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
);
1018 SysTime atime
, mtime
;
1019 static assert(__traits(compiles
, getTimes(TestAliasedString("foo"), atime
, mtime
)));
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
)
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
);
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.
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.
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
;
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
)
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
;
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).
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.
1212 $(D FileException) on error.
1214 void setTimes(R
)(R name
,
1216 SysTime modificationTime
)
1217 if (isInputRange
!R
&& !isInfinite
!R
&& isSomeChar
!(ElementEncodingType
!R
) &&
1218 !isConvertibleToString
!R
)
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
);
1247 AliasSeq
!(GENERIC_WRITE
,
1251 FILE_ATTRIBUTE_NORMAL |
1252 FILE_ATTRIBUTE_DIRECTORY |
1253 FILE_FLAG_BACKUP_SEMANTICS
,
1255 auto h
= trustedCreateFileW(namez
, defaults
);
1257 static if (isNarrowString
!R
&& is(Unqual
!(ElementEncodingType
!R
) == char))
1260 string names
= null;
1261 cenforce(h
!= INVALID_HANDLE_VALUE
, names
, namez
);
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))
1285 string names
= null;
1286 cenforce(trustedUtimensat(AT_FDCWD
, namez
, t
, 0) == 0, names
, namez
);
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))
1302 string names
= null;
1303 cenforce(trustedUtimes(namez
, t
) == 0, names
, namez
);
1309 void setTimes(R
)(auto ref R name
,
1311 SysTime modificationTime
)
1312 if (isConvertibleToString
!R
)
1314 setTimes
!(StringTypeOf
!R
)(name
, accessTime
, modificationTime
);
1319 if (false) // Test instatiation
1320 setTimes(TestAliasedString("foo"), SysTime
.init
, SysTime
.init
);
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
);
1343 getTimes(path
, atime_res
, mtime_res
);
1344 assert(atime
== atime_res
);
1345 assert(mtime
== mtime_res
);
1351 testTimes(123_456_7);
1353 rmdirRecurse(newdir
);
1357 Returns the time that the given file was last modified.
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
)
1371 getTimesWin(name
, dummy
, dummy
, 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))
1387 string names
= null;
1388 cenforce(trustedStat(namez
, statbuf
) == 0, names
, namez
);
1390 return statTimeToStdTime
!'m'(statbuf
);
1395 SysTime
timeLastModified(R
)(auto ref R name
)
1396 if (isConvertibleToString
!R
)
1398 return timeLastModified
!(StringTypeOf
!R
)(name
);
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.
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.
1425 --------------------
1426 if (timeLastModified(source) >= timeLastModified(target, SysTime.min))
1432 // target is up-to-date
1434 --------------------
1436 SysTime
timeLastModified(R
)(R name
, SysTime returnIfMissing
)
1437 if (isInputRange
!R
&& !isInfinite
!R
&& isSomeChar
!(ElementEncodingType
!R
))
1442 return returnIfMissing
;
1447 getTimesWin(name
, dummy
, dummy
, 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 ?
1462 statTimeToStdTime
!'m'(statbuf
);
1468 //std.process.system("echo a > deleteme") == 0 || assert(false);
1469 if (exists(deleteme
))
1472 write(deleteme
, "a\n");
1476 assert(exists(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.
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
1509 if (exists(deleteme
))
1515 write(deleteme
, "a");
1516 auto time
= timeLastModified(deleteme
);
1518 assert(time
!= lastTime
);
1520 Thread
.sleep(20.msecs
);
1526 * Determine whether the given file (or directory) _exists.
1528 * name = string or range of characters representing the file _name
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());
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
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
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;
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))
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
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
)
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))
1634 string names
= null;
1635 cenforce(result
!= INVALID_FILE_ATTRIBUTES
, names
, namez
);
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))
1651 string names
= null;
1652 cenforce(trustedStat(namez
, statbuf
) == 0, names
, namez
);
1654 return statbuf
.st_mode
;
1659 uint getAttributes(R
)(auto ref R name
)
1660 if (isConvertibleToString
!R
)
1662 return getAttributes
!(StringTypeOf
!R
)(name
);
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
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.
1681 name = The file to get the symbolic link attributes of.
1687 $(D FileException) on error.
1689 uint getLinkAttributes(R
)(R name
)
1690 if (isInputRange
!R
&& !isInfinite
!R
&& isSomeChar
!(ElementEncodingType
!R
) &&
1691 !isConvertibleToString
!R
)
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))
1708 string names
= null;
1709 cenforce(trustedLstat(namez
, lstatbuf
) == 0, names
, namez
);
1710 return lstatbuf
.st_mode
;
1715 uint getLinkAttributes(R
)(auto ref R name
)
1716 if (isConvertibleToString
!R
)
1718 return getLinkAttributes
!(StringTypeOf
!R
)(name
);
1723 static assert(__traits(compiles
, getLinkAttributes(TestAliasedString(null))));
1727 Set the _attributes of the given file.
1730 name = the file _name
1731 attributes = the _attributes to set the file to
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
)
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))
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))
1764 string names
= null;
1765 cenforce(!trustedChmod(namez
, cast(mode_t
) attributes
), names
, namez
);
1770 void setAttributes(R
)(auto ref R name
, uint attributes
)
1771 if (isConvertibleToString
!R
)
1773 return setAttributes
!(StringTypeOf
!R
)(name
, attributes
);
1778 static assert(__traits(compiles
, setAttributes(TestAliasedString(null), 0)));
1782 Returns whether the given file is a directory.
1785 name = The path to the file.
1788 true if name specifies a directory
1791 $(D FileException) if the given file does not exist.
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
)
1805 return (getAttributes(name
) & FILE_ATTRIBUTE_DIRECTORY
) != 0;
1807 else version (Posix
)
1809 return (getAttributes(name
) & S_IFMT
) == S_IFDIR
;
1814 @property bool isDir(R
)(auto ref R name
)
1815 if (isConvertibleToString
!R
)
1817 return name
.isDir
!(StringTypeOf
!R
);
1822 static assert(__traits(compiles
, TestAliasedString(null).isDir
));
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
);
1848 enum dir
= "C:\\Program Files\\";
1849 else version (Posix
)
1850 enum dir
= system_directory
;
1854 DirEntry
de = DirEntry(dir
);
1856 assert(DirEntry(dir
).isDir
);
1861 Returns whether the given file _attributes are for a directory.
1864 attributes = The file _attributes.
1867 true if attributes specifies a directory
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
1879 return (attributes
& FILE_ATTRIBUTE_DIRECTORY
) != 0;
1881 else version (Posix
)
1883 return (attributes
& S_IFMT
) == S_IFDIR
;
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
1936 name = The path to the file.
1939 true if name specifies a file
1942 $(D FileException) if the given file does not exist.
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
)
1956 else version (Posix
)
1957 return (getAttributes(name
) & S_IFMT
) == S_IFREG
;
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
))));
1975 static assert(__traits(compiles
, TestAliasedString(null).isFile
));
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).
2014 attributes = The file _attributes.
2017 true if the given file _attributes are for a file
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
2029 return (attributes
& FILE_ATTRIBUTE_DIRECTORY
) == 0;
2031 else version (Posix
)
2033 return (attributes
& S_IFMT
) == S_IFREG
;
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
2077 name = The path to the file.
2080 true if name is a symbolic link
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
)
2090 return (getAttributes(name
) & FILE_ATTRIBUTE_REPARSE_POINT
) != 0;
2091 else version (Posix
)
2092 return (getLinkAttributes(name
) & S_IFMT
) == S_IFLNK
;
2096 @property bool isSymlink(R
)(auto ref R name
)
2097 if (isConvertibleToString
!R
)
2099 return name
.isSymlink
!(StringTypeOf
!R
);
2104 static assert(__traits(compiles
, TestAliasedString(null).isSymlink
));
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
2188 attributes = The file attributes.
2191 true if attributes are for a symbolic link
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
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();
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
;
2238 string pathStr
= null;
2239 cenforce(trustedChdir(pathz
), pathStr
, pathz
);
2243 void chdir(R
)(auto ref R pathname
)
2244 if (isConvertibleToString
!R
)
2246 return chdir
!(StringTypeOf
!R
)(pathname
);
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();
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
;
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
;
2290 string pathStr
= null;
2291 cenforce(trustedMkdir(pathz
, octal
!777) == 0, pathStr
, pathz
);
2296 void mkdir(R
)(auto ref R pathname
)
2297 if (isConvertibleToString
!R
)
2299 return mkdir
!(StringTypeOf
!R
)(pathname
);
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();
2318 if (() @trusted { return CreateDirectoryW(pathz
, null); }())
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)
2328 cenforce(errno
== EEXIST || errno
== EISDIR
, pathname
);
2330 enforce(pathname
.isDir
, new FileException(pathname
.idup
));
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
))
2352 if (!baseName(pathname
).empty
)
2354 ensureDirExists(pathname
);
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");
2369 path
= path
.buildNormalizedPath
;
2372 path
= buildPath(basepath
, "c");
2374 assertThrown
!FileException(mkdirRecurse(path
));
2376 path
= buildPath(basepath
, "d");
2378 mkdirRecurse(path
); // should not throw
2383 assertThrown
!FileException(mkdirRecurse(`1:\foobar`));
2388 immutable basepath
= deleteme
~ "_dir";
2391 immutable path
= basepath
~ "\\fake\\here\\";
2393 else version (Posix
)
2395 immutable path
= basepath
~ `/fake/here/`;
2399 assert(basepath
.exists
&& basepath
.isDir
);
2400 scope(exit
) () @trusted { rmdirRecurse(basepath
); }();
2401 assert(path
.exists
&& path
.isDir
);
2405 /****************************************************
2406 Remove directory $(D pathname).
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();
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
;
2437 string pathStr
= null;
2438 cenforce(trustedRmdir(pathz
), pathStr
, pathz
);
2442 void rmdir(R
)(auto ref R pathname
)
2443 if (isConvertibleToString
!R
)
2445 rmdir
!(StringTypeOf
!R
)(pathname
);
2450 static assert(__traits(compiles
, rmdir(TestAliasedString(null))));
2454 $(BLUE This function is Posix-Only.)
2456 Creates a symbolic _link (_symlink).
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
2462 link = The _symlink to create. A relative path is relative to the
2463 current working directory.
2466 $(D FileException) on error (which includes if the _symlink already
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
);
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
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
);
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
);
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
);
2594 cenforce(size
!= -1, to
!string(link
));
2596 if (size
<= dynamicBuffer
.length
- maxCodeUnits
)
2598 dynamicBuffer
.length
= size
;
2599 return () @trusted {
2600 return assumeUnique(dynamicBuffer
);
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
;
2616 foreach (file
; [system_directory
, system_file
])
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";
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
),
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
),
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),
2698 scope(exit
) core
.stdc
.stdlib
.free(p
);
2699 return p
[0 .. core
.stdc
.string
.strlen(p
)].idup
;
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.
2721 * $(REF1 Exception, object)
2723 @trusted string
thisExePath ()
2727 import core
.sys
.posix
.stdlib
: realpath
;
2728 import std
.conv
: to
;
2729 import std
.exception
: errnoEnforce
;
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
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
[];
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
]);
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];
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];
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
];
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
];
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);
2848 errnoEnforce(absolutePath
);
2849 return to
!(string
)(absolutePath
);
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()));
2867 static assert(0, "thisExePath is not supported on this platform");
2872 import std
.path
: isAbsolute
;
2873 auto path
= thisExePath();
2875 assert(path
.exists
);
2876 assert(path
.isAbsolute
);
2877 assert(path
.isFile
);
2883 Info on a file, similar to what you'd get from stat on a Posix system.
2888 Constructs a $(D DirEntry) for the given file (or directory).
2891 path = The file (or directory) to get a DirEntry for.
2894 $(D FileException) if the file does not exist.
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).
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
2927 --------------------
2928 auto de1 = DirEntry("/etc/fonts/fonts.conf");
2931 auto de2 = DirEntry("/usr/share/include");
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
2953 --------------------
2954 auto de1 = DirEntry("/etc/fonts/fonts.conf");
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
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)
2976 @property ulong size();
2979 $(BLUE This function is Windows-Only.)
2981 Returns the creation time of the file represented by this
2984 @property SysTime
timeCreated() const;
2987 Returns the time that the file represented by this $(D DirEntry) was
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
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
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();
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
)
3051 import std
.datetime
.systime
: FILETIMEToSysTime
;
3054 throw new FileException(path
, "File does not exist");
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
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.
3102 @property bool isSymlink() const pure nothrow
3104 return (attributes
& FILE_ATTRIBUTE_REPARSE_POINT
) != 0;
3107 @property ulong size() const pure nothrow
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
3132 @property uint linkAttributes() const pure nothrow
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
)
3158 throw new FileException(path
, "File does not exist");
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
;
3174 immutable len
= (() @trusted => core
.stdc
.string
.strlen(fd
.d_name
.ptr
))();
3176 _name
= buildPath(path
, (() @trusted => fd
.d_name
.ptr
[0 .. len
])());
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
)
3201 // e.g. Solaris does not have the d_type member
3206 @property string
name() const pure nothrow
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()
3229 return (_lstatMode
& S_IFMT
) == S_IFLNK
;
3232 @property ulong size()
3235 return _statBuf
.st_size
;
3238 @property SysTime
timeStatusChanged()
3242 return statTimeToStdTime
!'c'(_statBuf
);
3245 @property SysTime
timeLastAccessed()
3249 return statTimeToStdTime
!'a'(_statBuf
);
3252 @property SysTime
timeLastModified()
3256 return statTimeToStdTime
!'m'(_statBuf
);
3259 @property uint attributes()
3263 return _statBuf
.st_mode
;
3266 @property uint linkAttributes()
3273 @property stat_t
statBuf()
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
);
3296 enforce(trustedStat(_name
, &_statBuf
) == 0,
3297 "Failed to stat file `" ~ _name
~ "'");
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()
3314 if ( stat(_name
.tempCString(), &_statBuf
) != 0 )
3318 _statBuf
= stat_t
.init
;
3319 _statBuf
.st_mode
= S_IFLNK
;
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
;
3338 stat_t statbuf
= void;
3340 enforce(lstat(_name
.tempCString(), &statbuf
) == 0,
3341 "Failed to stat file `" ~ _name
~ "'");
3343 _lstatMode
= statbuf
.st_mode
;
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.
3365 if ("C:\\Program Files\\".exists
)
3367 auto de = DirEntry("C:\\Program Files\\");
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");
3384 assert(!de.isSymlink
);
3387 else version (Posix
)
3389 import std
.exception
: assertThrown
;
3391 if (system_directory
.exists
)
3394 auto de = DirEntry(system_directory
);
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
);
3409 assert(de.isSymlink
);
3413 core
.sys
.posix
.unistd
.symlink((deleteme
~ "_broken_symlink\0").ptr
, symfile
.ptr
);
3417 DirEntry
de = DirEntry(symfile
);
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
);
3433 if (system_file
.exists
)
3435 auto de = DirEntry(system_file
);
3438 assert(!de.isSymlink
);
3443 alias PreserveAttributes
= Flag
!"preserveAttributes";
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
;
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.
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))
3483 enum string f
= null;
3485 static if (isNarrowString
!RT
&& is(Unqual
!(ElementEncodingType
!RT
) == char))
3488 enum string t
= null;
3490 copyImpl(f
, t
, fromz
, toz
, preserve
);
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
3512 assert(preserve
== Yes
.preserveAttributes
);
3513 immutable result
= CopyFileW(fromz
, toz
, false);
3516 import core
.stdc
.wchar_
: wcslen
;
3517 import std
.conv
: to
;
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
);
3560 buf
= core
.stdc
.stdlib
.malloc(BUFSIZ
);
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
;
3573 core
.sys
.posix
.unistd
.read(fdr
, buf
, toxfer
) == toxfer
3574 && core
.sys
.posix
.unistd
.write(fdw
, buf
, toxfer
) == toxfer
,
3576 assert(size
>= toxfer
);
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
);
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();
3600 assert(readText(t2
) == "11");
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();
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
;
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,
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,
3652 $(D FileException) if there is an error (including if the given
3653 file is not a directory).
3655 void rmdirRecurse(ref DirEntry
de)
3658 throw new FileException(de.name
, "Not a directory");
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
);
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
3684 //A DirEntry is a bit big (72B), so keeping the "by ref" signature is desirable.
3685 void rmdirRecurse(DirEntry
de)
3690 version (Windows
) @system unittest
3692 import std
.exception
: enforce
;
3693 auto d
= deleteme
~ r
".dir\a\b\c\d\e\f\g";
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
)));
3707 core
.sys
.posix
.unistd
.symlink((deleteme
~"/a/b/c\0").ptr
,
3708 (deleteme
~"/link\0").ptr
);
3709 rmdirRecurse(deleteme
~"/link");
3711 rmdirRecurse(deleteme
);
3712 enforce(!exists(deleteme
));
3714 d
= deleteme
~"/a/b/c/d/e/f/g";
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
));
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
);
3741 assert(!exists(unit_file
));
3743 assert(!exists(unit2_file
));
3747 * Dictates directory spanning policy for $(D_PARAM dirEntries) (see below).
3751 /** Only spans one directory. */
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. */
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
3767 $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search,
3768 _breadth-first traversal).
3773 private struct DirIteratorImpl
3775 import std
.array
: Appender
, appender
;
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
;
3783 Appender
!(DirHandle
[]) _stack
;
3784 Appender
!(DirEntry
[]) _stashed
; //used in depth first mode
3786 void pushExtra(DirEntry
de){ _stashed
.put(de); }
3788 bool hasExtra(){ return !_stashed
.data
.empty
; }
3793 de = _stashed
.data
[$-1];
3794 _stashed
.shrinkTo(_stashed
.data
.length
- 1);
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
);
3820 if (_stack
.data
.empty
)
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
;
3832 if (FindNextFileW(_stack
.data
[$-1].h
, findinfo
) == FALSE
)
3838 while ( wcscmp(findinfo
.cFileName
.ptr
, ".") == 0
3839 ||
wcscmp(findinfo
.cFileName
.ptr
, "..") == 0)
3840 if (FindNextFileW(_stack
.data
[$-1].h
, findinfo
) == FALSE
)
3845 _cur
= DirEntry(_stack
.data
[$-1].dirpath
, findinfo
);
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
)
3864 return _followSymlink ? _cur
.isDir
: _cur
.isDir
&& !_cur
.isSymlink
;
3867 else version (Posix
)
3875 bool stepIn(string directory
)
3877 auto h
= directory
.length ?
opendir(directory
.tempCString()) : opendir(".");
3878 cenforce(h
, directory
);
3879 _stack
.put(DirHandle(directory
, h
));
3885 if (_stack
.data
.empty
)
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
);
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
)
3916 return _followSymlink ? _cur
.isDir
: attrIsDir(_cur
.linkAttributes
);
3920 this(R
)(R pathname
, SpanMode mode
, bool followSymlink
)
3921 if (isInputRange
!R
&& isSomeChar
!(ElementEncodingType
!R
))
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
;
3933 import std
.array
: array
;
3934 string pathnameStr
= pathname
.array
;
3936 if (stepIn(pathnameStr
))
3938 if (_mode
== SpanMode
.depth
)
3941 auto thisDir
= _cur
;
3942 if (stepIn(_cur
.name
))
3951 @property bool empty(){ return _stashed
.data
.empty
&& _stack
.data
.empty
; }
3952 @property DirEntry
front(){ return _cur
; }
3957 case SpanMode
.depth
:
3962 auto thisDir
= _cur
;
3963 if (stepIn(_cur
.name
))
3971 else if (hasExtra())
3974 case SpanMode
.breadth
:
3977 if (!stepIn(_cur
.name
))
3978 while (!empty
&& !next()){}
3981 while (!empty
&& !next()){}
3997 RefCounted
!(DirIteratorImpl
, RefCountedAutoInitialize
.no
) impl
;
3998 this(string pathname
, SpanMode mode
, bool followSymlink
)
4000 impl
= typeof(impl
)(pathname
, mode
, followSymlink
);
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.
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,
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
4031 followSymlink = Whether symbolic links which point to directories
4032 should be treated as directories and their contents
4036 $(D FileException) if the directory does not exist.
4039 --------------------
4040 // Iterate a directory in depth
4041 foreach (string name; dirEntries("destroy/me", SpanMode.depth))
4046 // Iterate the current directory in breadth
4047 foreach (string name; dirEntries("", SpanMode.breadth))
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"));
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;
4068 std.process.system(cmd);
4071 // Iterate over all D source files in current directory and all its
4073 auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
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()):
4086 string
[] listdir(string pathname
)
4088 import std
.algorithm
;
4093 return std
.file
.dirEntries(pathname
, SpanMode
.shallow
)
4094 .filter
!(a
=> a
.isFile
)
4095 .map
!(a
=> std
.path
.baseName(a
.name
))
4099 void main(string
[] args
)
4103 string
[] files
= listdir(args
[1]);
4104 writefln("%s", files
);
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
;
4121 string testdir
= deleteme
; // This has to be an absolute path when
4122 // called from a shared library on Android,
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
);
4138 map
!(a
=> absolutePath(a
.name
))(dirEntries(relpath
, mode
)),
4139 map
!(a
=> a
.name
)(dirEntries(absolutePath(relpath
), mode
))));
4143 assert(equalEntries(testdir
, SpanMode
.shallow
) == 2);
4144 assert(equalEntries(testdir
, SpanMode
.depth
) == 3);
4145 assert(equalEntries(testdir
, SpanMode
.breadth
) == 3);
4148 foreach (string name
; dirEntries(testdir
, SpanMode
.breadth
))
4151 assert(name
.startsWith(testdir
));
4153 foreach (DirEntry e
; dirEntries(absolutePath(testdir
), SpanMode
.breadth
))
4156 assert(e
.isFile || e
.isDir
, e
.name
);
4160 foreach (string name
; dirEntries(testdir
, "*.d", SpanMode
.breadth
))
4164 foreach (entry
; dirEntries(testdir
, SpanMode
.breadth
))
4166 static assert(is(typeof(entry
) == DirEntry
));
4169 auto a
= array(dirEntries(testdir
, SpanMode
.shallow
));
4172 auto dFiles
= dirEntries(testdir
, SpanMode
.shallow
);
4173 foreach (d
; dFiles
){}
4176 dirEntries("", SpanMode
.shallow
).walkLength();
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
));
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";
4199 if (dpath
.exists
) rmdirRecurse(dpath
);
4200 if (fpath
.exists
) remove(fpath
);
4201 if (sdpath
.exists
) remove(sdpath
);
4202 if (sfpath
.exists
) remove(sfpath
);
4206 write(fpath
, "hello world");
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)];
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
);
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).
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
4280 * If only one type is passed, then an array of that type. Otherwise, an
4281 * array of $(REF Tuple, std,typecons)s.
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
);
4306 text("Trailing characters at the end of line: `", line
,
4316 import std
.typecons
: tuple
;
4320 assert(exists(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
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:
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.)
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
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
;
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
;
4389 cache
= findExistingDir(environment
.get("TMPDIR"),
4390 environment
.get("TEMP"),
4391 environment
.get("TMP"),
4396 else static assert(false, "Unsupported platform");
4398 if (cache
is null) cache
= getcwd();