2 * Encapsulate path and file names.
4 * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
5 * Authors: Walter Bright, https://www.digitalmars.com
6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/filename.d, root/_filename.d)
8 * Documentation: https://dlang.org/phobos/dmd_root_filename.html
9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/filename.d
12 module dmd
.root
.filename
;
14 import core
.stdc
.ctype
;
15 import core
.stdc
.errno
;
16 import core
.stdc
.string
;
18 import dmd
.common
.file
;
19 import dmd
.common
.outbuffer
;
21 import dmd
.root
.array
;
25 import dmd
.root
.string
;
29 import core
.sys
.posix
.stdlib
;
30 import core
.sys
.posix
.sys
.stat
;
31 import core
.sys
.posix
.unistd
: getcwd
;
36 import core
.sys
.windows
.winbase
;
37 import core
.sys
.windows
.windef
;
38 import core
.sys
.windows
.winnls
;
40 import dmd
.common
.smallbuffer
: extendedPathThen
;
42 extern (Windows
) DWORD
GetFullPathNameW(LPCWSTR
, DWORD
, LPWSTR
, LPWSTR
*) nothrow @nogc;
43 extern (Windows
) void SetLastError(DWORD
) nothrow @nogc;
44 extern (C
) char* getcwd(char* buffer
, size_t maxlen
) nothrow;
47 version (CRuntime_Glibc
)
49 extern (C
) char* canonicalize_file_name(const char*) nothrow;
52 alias Strings
= Array
!(const(char)*);
55 // Check whether character is a directory separator
56 bool isDirSeparator(char c
) pure nothrow @nogc @safe
60 return c
== '\\' || c
== '/';
72 /***********************************************************
73 * Encapsulate path and file names.
78 private const(char)[] str;
81 extern (D
) this(const(char)[] str) pure
83 this.str = str.xarraydup
;
87 extern (C
++) static FileName
create(const(char)* name
) pure
89 return FileName(name
.toDString
);
92 /// Compare two name according to the platform's rules (case sensitive or not)
93 extern (C
++) static bool equals(const(char)* name1
, const(char)* name2
) pure @nogc
95 return equals(name1
.toDString
, name2
.toDString
);
99 extern (D
) static bool equals(const(char)[] name1
, const(char)[] name2
) pure @nogc
101 if (name1
.length
!= name2
.length
)
106 return name1
.ptr
== name2
.ptr ||
107 Port
.memicmp(name1
.ptr
, name2
.ptr
, name1
.length
) == 0;
111 return name1
== name2
;
115 /************************************
116 * Determine if path is absolute.
120 * true if absolute path name.
122 extern (C
++) static bool absolute(const(char)* name
) pure @nogc
124 return absolute(name
.toDString
);
128 extern (D
) static bool absolute(const(char)[] name
) pure @nogc @safe
135 return isDirSeparator(name
[0])
136 ||
(name
.length
>= 2 && name
[1] == ':');
140 return isDirSeparator(name
[0]);
150 assert(absolute("/"[]) == true);
151 assert(absolute(""[]) == false);
155 assert(absolute(r
"\"[]) == true);
156 assert(absolute(r
"\\"[]) == true);
157 assert(absolute(r
"c:"[]) == true);
162 Return the given name as an absolute path
166 base = the absolute base to prefix name with if it is relative
168 Returns: name as an absolute path relative to base
170 extern (C
++) static const(char)* toAbsolute(const(char)* name
, const(char)* base
= null)
172 const name_
= name
.toDString();
173 const base_
= base ? base
.toDString() : getcwd(null, 0).toDString();
174 return absolute(name_
) ? name
: combine(base_
, name_
).ptr
;
177 /********************************
178 * Determine file name extension as slice of input.
182 * filename extension (read-only).
183 * Points past '.' of extension.
184 * If there isn't one, return null.
186 extern (C
++) static const(char)* ext(const(char)* str) pure @nogc
188 return ext(str.toDString
).ptr
;
192 extern (D
) static const(char)[] ext(const(char)[] str) nothrow pure @safe @nogc
194 foreach_reverse (idx
, char e
; str)
199 return str[idx
+ 1 .. $];
221 assert(ext("/foo/bar/dmd.conf"[]) == "conf");
222 assert(ext("object.o"[]) == "o");
223 assert(ext("/foo/bar/dmd"[]) == null);
224 assert(ext(".objdir.o/object"[]) == null);
225 assert(ext([]) == null);
228 extern (C
++) const(char)* ext() const pure @nogc
233 /********************************
234 * Return file name without extension.
237 * Once slice are used everywhere and `\0` is not assumed,
238 * this can be turned into a simple slicing.
244 * mem.xmalloc'd filename with extension removed.
246 extern (C
++) static const(char)* removeExt(const(char)* str)
248 return removeExt(str.toDString
).ptr
;
252 extern (D
) static const(char)[] removeExt(const(char)[] str)
257 const len
= (str.length
- e
.length
) - 1; // -1 for the dot
258 char* n
= cast(char*)mem
.xmalloc(len
+ 1);
259 memcpy(n
, str.ptr
, len
);
263 return mem
.xstrdup(str.ptr
)[0 .. str.length
];
268 assert(removeExt("/foo/bar/object.d"[]) == "/foo/bar/object");
269 assert(removeExt("/foo/bar/frontend.di"[]) == "/foo/bar/frontend");
272 /********************************
273 * Return filename name excluding path (read-only).
275 extern (C
++) static const(char)* name(const(char)* str) pure @nogc
277 return name(str.toDString
).ptr
;
281 extern (D
) static const(char)[] name(const(char)[] str) pure @nogc @safe
283 foreach_reverse (idx
, char e
; str)
290 return str[idx
+ 1 .. $];
296 return str[idx
+ 1 .. $];
298 /* The ':' is a drive letter only if it is the second
299 * character or the last character,
300 * otherwise it is an ADS (Alternate Data Stream) separator.
301 * Consider ADS separators as part of the file name.
303 if (idx
== 1 || idx
== str.length
- 1)
304 return str[idx
+ 1 .. $];
314 extern (C
++) const(char)* name() const pure @nogc
316 return name(str).ptr
;
321 assert(name("/foo/bar/object.d"[]) == "object.d");
322 assert(name("/foo/bar/frontend.di"[]) == "frontend.di");
325 /**************************************
326 * Return path portion of str.
327 * returned string is newly allocated
328 * Path does not include trailing path separator.
330 extern (C
++) static const(char)* path(const(char)* str)
332 return path(str.toDString
).ptr
;
336 extern (D
) static const(char)[] path(const(char)[] str)
339 bool hasTrailingSlash
;
340 if (n
.length
< str.length
)
342 if (isDirSeparator(str[$ - n
.length
- 1]))
343 hasTrailingSlash
= true;
345 const pathlen
= str.length
- n
.length
- (hasTrailingSlash ?
1 : 0);
346 char* path
= cast(char*)mem
.xmalloc(pathlen
+ 1);
347 memcpy(path
, str.ptr
, pathlen
);
349 return path
[0 .. pathlen
];
354 assert(path("/foo/bar"[]) == "/foo");
355 assert(path("foo"[]) == "");
358 /**************************************
359 * Replace filename portion of path.
361 extern (D
) static const(char)[] replaceName(const(char)[] path
, const(char)[] name
)
365 auto n
= FileName
.name(path
);
368 return combine(path
[0 .. $ - n
.length
], name
);
372 Combine a `path` and a file `name`
375 path = Path to append to
376 name = Name to append to path
379 The `\0` terminated string which is the combination of `path` and `name`
382 extern (C
++) static const(char)* combine(const(char)* path
, const(char)* name
)
386 return combine(path
.toDString
, name
.toDString
).ptr
;
390 extern(D
) static const(char)[] combine(const(char)[] path
, const(char)[] name
)
392 return !path
.length ? name
: buildPath(path
, name
);
398 assert(combine("foo"[], "bar"[]) == "foo\\bar");
400 assert(combine("foo"[], "bar"[]) == "foo/bar");
401 assert(combine("foo/"[], "bar"[]) == "foo/bar");
404 static const(char)[] buildPath(const(char)[][] fragments
...)
407 foreach (f
; fragments
)
408 size
+= f
.length ? f
.length
+ 1 : 0;
412 char* p
= cast(char*) mem
.xmalloc_noscan(size
);
414 foreach (f
; fragments
)
419 p
[length
.. length
+ f
.length
] = f
;
422 const last
= p
[length
- 1];
425 if (!isDirSeparator(last
))
428 else version (Windows
)
430 if (!isDirSeparator(last
) && last
!= ':')
437 // overwrite last slash with null terminator
438 p
[length ?
--length
: 0] = 0;
440 return p
[0 .. length
];
445 assert(buildPath() == "");
446 assert(buildPath("foo") == "foo");
447 assert(buildPath("foo", null) == "foo");
448 assert(buildPath(null, "foo") == "foo");
450 assert(buildPath("C:", r
"a\", "bb/", "ccc", "d") == r
"C:a\bb/ccc\d");
452 assert(buildPath("a/", "bb", "ccc") == "a/bb/ccc");
455 // Split a path into an Array of paths
456 extern (C
++) static Strings
* splitPath(const(char)* path
)
458 auto array
= new Strings();
459 int sink(const(char)* p
) nothrow
464 splitPath(&sink
, path
);
469 * Split path (such as that returned by `getenv("PATH")`) into pieces, each piece is mem.xmalloc'd
470 * Handle double quotes and ~.
471 * Pass the pieces to sink()
473 * sink = send the path pieces here, end when sink() returns !=0
474 * path = the path to split up.
476 static void splitPath(int delegate(const(char)*) nothrow sink
, const(char)* path
)
487 bool instring
= false;
488 while (isspace(*p
)) // skip leading whitespace
490 buf
.reserve(8); // guess size of piece
497 instring ^
= false; // toggle inside/outside of string
512 p
++; // ; cannot appear as part of a
513 break; // path, quotes won't protect it
515 case 0x1A: // ^Z means end of file
520 continue; // ignore carriage returns
526 home
= getenv("HOME");
527 // Expand ~ only if it is prefixing the rest of the path.
528 if (!buf
.length
&& p
[1] == '/' && home
)
529 buf
.writestring(home
);
538 case '\t': // tabs in filenames?
539 if (!instring
) // if not in string
540 break; // treat as end of path
548 if (buf
.length
) // if path is not empty
550 if (sink(buf
.extractChars()))
557 * Add the extension `ext` to `name`, regardless of the content of `name`
560 * name = Path to append the extension to
561 * ext = Extension to add (should not include '.')
564 * A newly allocated string (free with `FileName.free`)
566 extern(D
) static char[] addExt(const(char)[] name
, const(char)[] ext
) pure
568 const len
= name
.length
+ ext
.length
+ 2;
569 auto s
= cast(char*)mem
.xmalloc(len
);
570 s
[0 .. name
.length
] = name
[];
571 s
[name
.length
] = '.';
572 s
[name
.length
+ 1 .. len
- 1] = ext
[];
574 return s
[0 .. len
- 1];
578 /***************************
579 * Free returned value with FileName::free()
581 extern (C
++) static const(char)* defaultExt(const(char)* name
, const(char)* ext
)
583 return defaultExt(name
.toDString
, ext
.toDString
).ptr
;
587 extern (D
) static const(char)[] defaultExt(const(char)[] name
, const(char)[] ext
)
589 auto e
= FileName
.ext(name
);
590 if (e
.length
) // it already has an extension
591 return name
.xarraydup
;
592 return addExt(name
, ext
);
597 assert(defaultExt("/foo/object.d"[], "d") == "/foo/object.d");
598 assert(defaultExt("/foo/object"[], "d") == "/foo/object.d");
599 assert(defaultExt("/foo/bar.d"[], "o") == "/foo/bar.d");
602 /***************************
603 * Free returned value with FileName::free()
605 extern (C
++) static const(char)* forceExt(const(char)* name
, const(char)* ext
)
607 return forceExt(name
.toDString
, ext
.toDString
).ptr
;
611 extern (D
) static const(char)[] forceExt(const(char)[] name
, const(char)[] ext
)
613 if (auto e
= FileName
.ext(name
))
614 return addExt(name
[0 .. $ - e
.length
- 1], ext
);
615 return defaultExt(name
, ext
); // doesn't have one
620 assert(forceExt("/foo/object.d"[], "d") == "/foo/object.d");
621 assert(forceExt("/foo/object"[], "d") == "/foo/object.d");
622 assert(forceExt("/foo/bar.d"[], "o") == "/foo/bar.o");
626 /// `true` if `name`'s extension is `ext`
627 extern (C
++) static bool equalsExt(const(char)* name
, const(char)* ext
) pure @nogc
629 return equalsExt(name
.toDString
, ext
.toDString
);
633 extern (D
) static bool equalsExt(const(char)[] name
, const(char)[] ext
) pure @nogc
635 auto e
= FileName
.ext(name
);
636 if (!e
.length
&& !ext
.length
)
638 if (!e
.length ||
!ext
.length
)
640 return FileName
.equals(e
, ext
);
645 assert(!equalsExt("foo.bar"[], "d"));
646 assert(equalsExt("foo.bar"[], "bar"));
647 assert(equalsExt("object.d"[], "d"));
648 assert(!equalsExt("object"[], "d"));
651 /******************************
652 * Return !=0 if extensions match.
654 extern (C
++) bool equalsExt(const(char)* ext
) const pure @nogc
656 return equalsExt(str, ext
.toDString());
659 /*************************************
660 * Search paths for file.
662 * path = array of path strings
663 * name = file to look for
664 * cwd = true means search current directory before searching path
666 * if found, filename combined with path, otherwise null
668 extern (C
++) static const(char)* searchPath(Strings
* path
, const(char)* name
, bool cwd)
670 return searchPath(path
, name
.toDString
, cwd).ptr
;
673 extern (D
) static const(char)[] searchPath(Strings
* path
, const(char)[] name
, bool cwd)
677 return exists(name
) ? name
: null;
688 auto n
= combine(p
.toDString
, name
);
691 //combine might return name
692 if (n
.ptr
!= name
.ptr
)
694 mem
.xfree(cast(void*)n
.ptr
);
701 extern (D
) static const(char)[] searchPath(const(char)* path
, const(char)[] name
, bool cwd)
705 return exists(name
) ? name
: null;
714 const(char)[] result
;
716 int sink(const(char)* p
) nothrow
718 auto n
= combine(p
.toDString
, name
);
719 mem
.xfree(cast(void*)p
);
723 return 1; // done with splitPath() call
728 splitPath(&sink
, path
);
734 /************************************
735 * Determine if path contains reserved character.
739 * index of the first reserved character in path if found, size_t.max otherwise
741 extern (D
) static size_t
findReservedChar(const(char)[] name
) pure @nogc @safe
745 // According to https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
746 // the following characters are not allowed in path: < > : " | ? *
747 foreach (idx
; 0 .. name
.length
)
750 if (c
== '<' || c
== '>' || c
== ':' || c
== '"' || c
== '|' || c
== '?' || c
== '*')
764 assert(findReservedChar(r
"") == size_t
.max
);
765 assert(findReservedChar(r
" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.-_=+()") == size_t
.max
);
769 assert(findReservedChar(` < `) == 1);
770 assert(findReservedChar(` >`) == 1);
771 assert(findReservedChar(`: `) == 0);
772 assert(findReservedChar(`"`) == 0);
773 assert(findReservedChar(`|`) == 0);
774 assert(findReservedChar(`?`) == 0);
775 assert(findReservedChar(`*`) == 0);
779 assert(findReservedChar(`<>:"|?*`) == size_t
.max
);
783 /************************************
784 * Determine if path has a reference to parent directory.
788 * true if path contains '..' reference to parent directory
790 extern (D
) static bool refersToParentDir(const(char)[] name
) pure @nogc @safe
793 foreach (i
; 0 .. name
.length
)
795 if (isDirSeparator(name
[i
]))
797 if (name
[s
..i
] == "..")
802 if (name
[s
..$] == "..")
809 assert(!refersToParentDir(r
""));
810 assert(!refersToParentDir(r
"foo"));
811 assert(!refersToParentDir(r
"foo.."));
812 assert(!refersToParentDir(r
"foo..boo"));
813 assert(!refersToParentDir(r
"foo/..boo"));
814 assert(!refersToParentDir(r
"foo../boo"));
815 assert(refersToParentDir(r
".."));
816 assert(refersToParentDir(r
"../"));
817 assert(refersToParentDir(r
"foo/.."));
818 assert(refersToParentDir(r
"foo/../"));
819 assert(refersToParentDir(r
"foo/../../boo"));
823 // Backslash as directory separator
824 assert(!refersToParentDir(r
"foo\..boo"));
825 assert(!refersToParentDir(r
"foo..\boo"));
826 assert(refersToParentDir(r
"..\"));
827 assert(refersToParentDir(r
"foo\.."));
828 assert(refersToParentDir(r
"foo\..\"));
829 assert(refersToParentDir(r
"foo\..\..\boo"));
835 Check if the file the `path` points to exists
838 0 if it does not exists
839 1 if it exists and is not a directory
840 2 if it exists and is a directory
842 extern (C
++) static int exists(const(char)* name
)
844 return exists(name
.toDString
);
848 extern (D
) static int exists(const(char)[] name
)
855 if (name
.toCStringThen
!((v
) => stat(v
.ptr
, &st
)) < 0)
857 if (S_ISDIR(st
.st_mode
))
861 else version (Windows
)
863 return name
.extendedPathThen
!((wname
)
865 const dw = GetFileAttributesW(&wname
[0]);
868 else if (dw & FILE_ATTRIBUTE_DIRECTORY
)
881 Ensure that the provided path exists
883 Accepts a path to either a file or a directory.
884 In the former case, the basepath (path to the containing directory)
885 will be checked for existence, and created if it does not exists.
886 In the later case, the directory pointed to will be checked for existence
887 and created if needed.
890 path = a path to a file or a directory
893 `true` if the directory exists or was successfully created
895 extern (D
) static bool ensurePathExists(const(char)[] path
)
897 //printf("FileName::ensurePathExists(%s)\n", path ? path : "");
903 // We were provided with a file name
904 // We need to call ourselves recursively to ensure parent dir exist
905 const char[] p
= FileName
.path(path
);
910 // Note: Windows filename comparison should be case-insensitive,
911 // however p is a subslice of path so we don't need it
912 if (path
.length
== p
.length ||
913 (path
.length
> 2 && path
[1] == ':' && path
[2 .. $] == p
))
915 mem
.xfree(cast(void*)p
.ptr
);
919 const r
= ensurePathExists(p
);
920 mem
.xfree(cast(void*)p
);
927 const r
= _mkdir(path
);
931 const r
= path
.toCStringThen
!((pathCS
) => mkdir(pathCS
.ptr
, (7 << 6) |
(7 << 3) |
7));
937 // Don't error out if another instance of dmd just created
941 import core
.sys
.windows
.winerror
: ERROR_ALREADY_EXISTS
;
942 if (GetLastError() == ERROR_ALREADY_EXISTS
)
955 extern (C
++) static bool ensurePathExists(const(char)* path
)
957 return ensurePathExists(path
.toDString
);
960 /******************************************
961 * Return canonical version of name.
962 * This code is high risk.
964 extern (C
++) static const(char)* canonicalName(const(char)* name
)
966 return canonicalName(name
.toDString
).ptr
;
970 extern (D
) static const(char)[] canonicalName(const(char)[] name
)
974 import core
.stdc
.limits
; // PATH_MAX
975 import core
.sys
.posix
.unistd
; // _PC_PATH_MAX
977 // Older versions of druntime don't have PATH_MAX defined.
978 // i.e: dmd __VERSION__ < 2085, gdc __VERSION__ < 2076.
979 static if (!__traits(compiles
, PATH_MAX
))
981 version (DragonFlyBSD
)
982 enum PATH_MAX
= 1024;
983 else version (FreeBSD
)
984 enum PATH_MAX
= 1024;
986 enum PATH_MAX
= 4096;
987 else version (NetBSD
)
988 enum PATH_MAX
= 1024;
989 else version (OpenBSD
)
990 enum PATH_MAX
= 1024;
992 enum PATH_MAX
= 1024;
993 else version (Solaris
)
994 enum PATH_MAX
= 1024;
997 // Have realpath(), passing a NULL destination pointer may return an
998 // internally malloc'd buffer, however it is implementation defined
999 // as to what happens, so cannot rely on it.
1000 static if (__traits(compiles
, PATH_MAX
))
1002 // Have compile time limit on filesystem path, use it with realpath.
1003 char[PATH_MAX
] buf
= void;
1004 auto path
= name
.toCStringThen
!((n
) => realpath(n
.ptr
, buf
.ptr
));
1006 return xarraydup(path
.toDString
);
1008 else static if (__traits(compiles
, canonicalize_file_name
))
1010 // Have canonicalize_file_name, which malloc's memory.
1011 // We need a dmd.root.rmem allocation though.
1012 auto path
= name
.toCStringThen
!((n
) => canonicalize_file_name(n
.ptr
));
1013 scope(exit
) .free(path
);
1015 return xarraydup(path
.toDString
);
1017 else static if (__traits(compiles
, _PC_PATH_MAX
))
1019 // Panic! Query the OS for the buffer limit.
1020 auto path_max
= pathconf("/", _PC_PATH_MAX
);
1023 char *buf
= cast(char*)mem
.xmalloc(path_max
);
1024 scope(exit
) mem
.xfree(buf
);
1025 auto path
= name
.toCStringThen
!((n
) => realpath(n
.ptr
, buf
));
1027 return xarraydup(path
.toDString
);
1030 // Give up trying to support this platform, just duplicate the filename
1031 // unless there is nothing to copy from.
1034 return xarraydup(name
);
1036 else version (Windows
)
1038 // Convert to wstring first since otherwise the Win32 APIs have a character limit
1039 return name
.toWStringzThen
!((wname
)
1041 /* Apparently, there is no good way to do this on Windows.
1042 * GetFullPathName isn't it, but use it anyway.
1044 // First find out how long the buffer has to be, incl. terminating null.
1045 const capacity
= GetFullPathNameW(&wname
[0], 0, null, null);
1046 if (!capacity
) return null;
1047 auto buffer
= cast(wchar*) mem
.xmalloc_noscan(capacity
* wchar.sizeof
);
1048 scope(exit
) mem
.xfree(buffer
);
1050 // Actually get the full path name. If the buffer is large enough,
1051 // the returned length does NOT include the terminating null...
1052 const length
= GetFullPathNameW(&wname
[0], capacity
, buffer
, null /*filePart*/);
1053 assert(length
== capacity
- 1);
1055 return toNarrowStringz(buffer
[0 .. length
]);
1066 string filename
= "foo.bar";
1067 const path
= canonicalName(filename
);
1068 scope(exit
) free(path
.ptr
);
1069 assert(path
.length
>= filename
.length
);
1070 assert(path
[$ - filename
.length
.. $] == filename
);
1073 /********************************
1074 * Free memory allocated by FileName routines
1076 extern (C
++) static void free(const(char)* str) pure
1080 assert(str[0] != cast(char)0xAB);
1081 memset(cast(void*)str, 0xAB, strlen(str) + 1); // stomp
1083 mem
.xfree(cast(void*)str);
1086 extern (C
++) const(char)* toChars() const pure nothrow @nogc @trusted
1088 // Since we can return an empty slice (but '\0' terminated),
1089 // we don't do bounds check (as `&str[0]` does)
1093 const(char)[] toString() const pure nothrow @nogc @trusted
1098 bool opCast(T
)() const pure nothrow @nogc @safe
1101 return str.ptr
!is null;
1107 /****************************************************************
1108 * The code before used the POSIX function `mkdir` on Windows. That
1109 * function is now deprecated and fails with long paths, so instead
1110 * we use the newer `CreateDirectoryW`.
1112 * `CreateDirectoryW` is the unicode version of the generic macro
1113 * `CreateDirectory`. `CreateDirectoryA` has a file path
1114 * limitation of 248 characters, `mkdir` fails with less and might
1115 * fail due to the number of consecutive `..`s in the
1116 * path. `CreateDirectoryW` also normally has a 248 character
1117 * limit, unless the path is absolute and starts with `\\?\`. Note
1118 * that this is different from starting with the almost identical
1122 * path = The path to create.
1125 * 0 on success, 1 on failure.
1128 * https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
1130 private int _mkdir(const(char)[] path
) nothrow
1132 const createRet
= path
.extendedPathThen
!(
1133 p
=> CreateDirectoryW(&p
[0], null /*securityAttributes*/));
1134 // different conventions for CreateDirectory and mkdir
1135 return createRet
== 0 ?
1 : 0;
1138 /**********************************
1139 * Converts a UTF-16 string to a (null-terminated) narrow string.
1141 * If `buffer` is specified and the result fits, a slice of that buffer,
1142 * otherwise a new buffer which can be released via `mem.xfree()`.
1143 * Nulls are propagated, i.e., if `wide` is null, the returned slice is
1146 char[] toNarrowStringz(const(wchar)[] wide
, char[] buffer
= null) nothrow
1148 import dmd
.common
.file
: CodePage
;
1153 const requiredLength
= WideCharToMultiByte(CodePage
, 0, wide
.ptr
, cast(int) wide
.length
, buffer
.ptr
, cast(int) buffer
.length
, null, null);
1154 if (requiredLength
< buffer
.length
)
1156 buffer
[requiredLength
] = 0;
1157 return buffer
[0 .. requiredLength
];
1160 char* newBuffer
= cast(char*) mem
.xmalloc_noscan(requiredLength
+ 1);
1161 const length
= WideCharToMultiByte(CodePage
, 0, wide
.ptr
, cast(int) wide
.length
, newBuffer
, requiredLength
, null, null);
1162 assert(length
== requiredLength
);
1163 newBuffer
[length
] = 0;
1164 return newBuffer
[0 .. length
];
1167 /**********************************
1168 * Converts a slice of UTF-8 characters to an array of wchar that's null
1169 * terminated so it can be passed to Win32 APIs then calls the supplied
1173 * str = The string to convert.
1176 * The result of calling F on the UTF16 version of str.
1178 private auto toWStringzThen(alias F
)(const(char)[] str) nothrow
1180 import dmd
.common
.smallbuffer
: SmallBuffer
, toWStringz
;
1182 if (!str.length
) return F(""w
.ptr
);
1184 wchar[1024] support
= void;
1185 auto buf
= SmallBuffer
!wchar(support
.length
, support
);
1186 wchar[] wide
= toWStringz(str, buf
);
1187 scope(exit
) wide
.ptr
!= buf
.ptr
&& mem
.xfree(wide
.ptr
);