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(const ref Strings path
, const char* name
, bool cwd)
670 return searchPath(path
[], name
.toDString
, cwd).ptr
;
673 extern (D
) static const(char)[] searchPath(const char*[] path
, const char[] name
, bool cwd)
677 return exists(name
) ? name
: null;
686 auto n
= combine(p
.toDString
, name
);
689 //combine might return name
690 if (n
.ptr
!= name
.ptr
)
692 mem
.xfree(cast(void*)n
.ptr
);
698 extern (D
) static const(char)[] searchPath(const char* path
, const char[] name
, bool cwd)
702 return exists(name
) ? name
: null;
711 const(char)[] result
;
713 int sink(const(char)* p
) nothrow
715 auto n
= combine(p
.toDString
, name
);
716 mem
.xfree(cast(void*)p
);
720 return 1; // done with splitPath() call
725 splitPath(&sink
, path
);
731 /************************************
732 * Determine if path contains reserved character.
736 * index of the first reserved character in path if found, size_t.max otherwise
738 extern (D
) static size_t
findReservedChar(const char[] name
) pure @nogc @safe
742 // According to https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
743 // the following characters are not allowed in path: < > : " | ? *
744 foreach (idx
; 0 .. name
.length
)
747 if (c
== '<' || c
== '>' || c
== ':' || c
== '"' || c
== '|' || c
== '?' || c
== '*')
761 assert(findReservedChar(r
"") == size_t
.max
);
762 assert(findReservedChar(r
" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.-_=+()") == size_t
.max
);
766 assert(findReservedChar(` < `) == 1);
767 assert(findReservedChar(` >`) == 1);
768 assert(findReservedChar(`: `) == 0);
769 assert(findReservedChar(`"`) == 0);
770 assert(findReservedChar(`|`) == 0);
771 assert(findReservedChar(`?`) == 0);
772 assert(findReservedChar(`*`) == 0);
776 assert(findReservedChar(`<>:"|?*`) == size_t
.max
);
780 /************************************
781 * Determine if path has a reference to parent directory.
785 * true if path contains '..' reference to parent directory
787 extern (D
) static bool refersToParentDir(const char[] name
) pure @nogc @safe
790 foreach (i
; 0 .. name
.length
)
792 if (isDirSeparator(name
[i
]))
794 if (name
[s
..i
] == "..")
799 if (name
[s
..$] == "..")
806 assert(!refersToParentDir(r
""));
807 assert(!refersToParentDir(r
"foo"));
808 assert(!refersToParentDir(r
"foo.."));
809 assert(!refersToParentDir(r
"foo..boo"));
810 assert(!refersToParentDir(r
"foo/..boo"));
811 assert(!refersToParentDir(r
"foo../boo"));
812 assert(refersToParentDir(r
".."));
813 assert(refersToParentDir(r
"../"));
814 assert(refersToParentDir(r
"foo/.."));
815 assert(refersToParentDir(r
"foo/../"));
816 assert(refersToParentDir(r
"foo/../../boo"));
820 // Backslash as directory separator
821 assert(!refersToParentDir(r
"foo\..boo"));
822 assert(!refersToParentDir(r
"foo..\boo"));
823 assert(refersToParentDir(r
"..\"));
824 assert(refersToParentDir(r
"foo\.."));
825 assert(refersToParentDir(r
"foo\..\"));
826 assert(refersToParentDir(r
"foo\..\..\boo"));
832 Check if the file the `path` points to exists
835 0 if it does not exists
836 1 if it exists and is not a directory
837 2 if it exists and is a directory
839 extern (C
++) static int exists(const(char)* name
)
841 return exists(name
.toDString
);
845 extern (D
) static int exists(const char[] name
)
852 if (name
.toCStringThen
!((v
) => stat(v
.ptr
, &st
)) < 0)
854 if (S_ISDIR(st
.st_mode
))
858 else version (Windows
)
860 return name
.extendedPathThen
!((wname
)
862 const dw = GetFileAttributesW(&wname
[0]);
865 else if (dw & FILE_ATTRIBUTE_DIRECTORY
)
878 Ensure that the provided path exists
880 Accepts a path to either a file or a directory.
881 In the former case, the basepath (path to the containing directory)
882 will be checked for existence, and created if it does not exists.
883 In the later case, the directory pointed to will be checked for existence
884 and created if needed.
887 path = a path to a file or a directory
890 `true` if the directory exists or was successfully created
892 extern (D
) static bool ensurePathExists(const char[] path
)
894 //printf("FileName::ensurePathExists(%s)\n", path ? path : "");
900 // We were provided with a file name
901 // We need to call ourselves recursively to ensure parent dir exist
902 const char[] p
= FileName
.path(path
);
907 // Note: Windows filename comparison should be case-insensitive,
908 // however p is a subslice of path so we don't need it
909 if (path
.length
== p
.length ||
910 (path
.length
> 2 && path
[1] == ':' && path
[2 .. $] == p
))
912 mem
.xfree(cast(void*)p
.ptr
);
916 const r
= ensurePathExists(p
);
917 mem
.xfree(cast(void*)p
);
924 const r
= _mkdir(path
);
928 const r
= path
.toCStringThen
!((pathCS
) => mkdir(pathCS
.ptr
, (7 << 6) |
(7 << 3) |
7));
934 // Don't error out if another instance of dmd just created
938 import core
.sys
.windows
.winerror
: ERROR_ALREADY_EXISTS
;
939 if (GetLastError() == ERROR_ALREADY_EXISTS
)
952 extern (C
++) static bool ensurePathExists(const(char)* path
)
954 return ensurePathExists(path
.toDString
);
957 /******************************************
958 * Return canonical version of name.
959 * This code is high risk.
961 extern (C
++) static const(char)* canonicalName(const(char)* name
)
963 return canonicalName(name
.toDString
).ptr
;
967 extern (D
) static const(char)[] canonicalName(const char[] name
)
971 import core
.stdc
.limits
; // PATH_MAX
972 import core
.sys
.posix
.unistd
; // _PC_PATH_MAX
974 // Older versions of druntime don't have PATH_MAX defined.
975 // i.e: dmd __VERSION__ < 2085, gdc __VERSION__ < 2076.
976 static if (!__traits(compiles
, PATH_MAX
))
978 version (DragonFlyBSD
)
979 enum PATH_MAX
= 1024;
980 else version (FreeBSD
)
981 enum PATH_MAX
= 1024;
983 enum PATH_MAX
= 4096;
984 else version (NetBSD
)
985 enum PATH_MAX
= 1024;
986 else version (OpenBSD
)
987 enum PATH_MAX
= 1024;
989 enum PATH_MAX
= 1024;
990 else version (Solaris
)
991 enum PATH_MAX
= 1024;
994 // Have realpath(), passing a NULL destination pointer may return an
995 // internally malloc'd buffer, however it is implementation defined
996 // as to what happens, so cannot rely on it.
997 static if (__traits(compiles
, PATH_MAX
))
999 // Have compile time limit on filesystem path, use it with realpath.
1000 char[PATH_MAX
] buf
= void;
1001 auto path
= name
.toCStringThen
!((n
) => realpath(n
.ptr
, buf
.ptr
));
1003 return xarraydup(path
.toDString
);
1005 else static if (__traits(compiles
, canonicalize_file_name
))
1007 // Have canonicalize_file_name, which malloc's memory.
1008 // We need a dmd.root.rmem allocation though.
1009 auto path
= name
.toCStringThen
!((n
) => canonicalize_file_name(n
.ptr
));
1010 scope(exit
) .free(path
);
1012 return xarraydup(path
.toDString
);
1014 else static if (__traits(compiles
, _PC_PATH_MAX
))
1016 // Panic! Query the OS for the buffer limit.
1017 auto path_max
= pathconf("/", _PC_PATH_MAX
);
1020 char *buf
= cast(char*)mem
.xmalloc(path_max
);
1021 scope(exit
) mem
.xfree(buf
);
1022 auto path
= name
.toCStringThen
!((n
) => realpath(n
.ptr
, buf
));
1024 return xarraydup(path
.toDString
);
1027 // Give up trying to support this platform, just duplicate the filename
1028 // unless there is nothing to copy from.
1031 return xarraydup(name
);
1033 else version (Windows
)
1035 // Convert to wstring first since otherwise the Win32 APIs have a character limit
1036 return name
.toWStringzThen
!((wname
)
1038 /* Apparently, there is no good way to do this on Windows.
1039 * GetFullPathName isn't it, but use it anyway.
1041 // First find out how long the buffer has to be, incl. terminating null.
1042 const capacity
= GetFullPathNameW(&wname
[0], 0, null, null);
1043 if (!capacity
) return null;
1044 auto buffer
= cast(wchar*) mem
.xmalloc_noscan(capacity
* wchar.sizeof
);
1045 scope(exit
) mem
.xfree(buffer
);
1047 // Actually get the full path name. If the buffer is large enough,
1048 // the returned length does NOT include the terminating null...
1049 const length
= GetFullPathNameW(&wname
[0], capacity
, buffer
, null /*filePart*/);
1050 assert(length
== capacity
- 1);
1052 return toNarrowStringz(buffer
[0 .. length
]);
1063 string filename
= "foo.bar";
1064 const path
= canonicalName(filename
);
1065 scope(exit
) free(path
.ptr
);
1066 assert(path
.length
>= filename
.length
);
1067 assert(path
[$ - filename
.length
.. $] == filename
);
1070 /********************************
1071 * Free memory allocated by FileName routines
1073 extern (C
++) static void free(const(char)* str) pure
1077 assert(str[0] != cast(char)0xAB);
1078 memset(cast(void*)str, 0xAB, strlen(str) + 1); // stomp
1080 mem
.xfree(cast(void*)str);
1083 extern (C
++) const(char)* toChars() const pure nothrow @nogc @trusted
1085 // Since we can return an empty slice (but '\0' terminated),
1086 // we don't do bounds check (as `&str[0]` does)
1090 const(char)[] toString() const pure nothrow @nogc @trusted
1095 bool opCast(T
)() const pure nothrow @nogc @safe
1098 return str.ptr
!is null;
1104 /****************************************************************
1105 * The code before used the POSIX function `mkdir` on Windows. That
1106 * function is now deprecated and fails with long paths, so instead
1107 * we use the newer `CreateDirectoryW`.
1109 * `CreateDirectoryW` is the unicode version of the generic macro
1110 * `CreateDirectory`. `CreateDirectoryA` has a file path
1111 * limitation of 248 characters, `mkdir` fails with less and might
1112 * fail due to the number of consecutive `..`s in the
1113 * path. `CreateDirectoryW` also normally has a 248 character
1114 * limit, unless the path is absolute and starts with `\\?\`. Note
1115 * that this is different from starting with the almost identical
1119 * path = The path to create.
1122 * 0 on success, 1 on failure.
1125 * https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
1127 private int _mkdir(const char[] path
) nothrow
1129 const createRet
= path
.extendedPathThen
!(
1130 p
=> CreateDirectoryW(&p
[0], null /*securityAttributes*/));
1131 // different conventions for CreateDirectory and mkdir
1132 return createRet
== 0 ?
1 : 0;
1135 /**********************************
1136 * Converts a UTF-16 string to a (null-terminated) narrow string.
1138 * If `buffer` is specified and the result fits, a slice of that buffer,
1139 * otherwise a new buffer which can be released via `mem.xfree()`.
1140 * Nulls are propagated, i.e., if `wide` is null, the returned slice is
1143 char[] toNarrowStringz(const(wchar)[] wide
, char[] buffer
= null) nothrow
1145 import dmd
.common
.file
: CodePage
;
1150 const requiredLength
= WideCharToMultiByte(CodePage
, 0, wide
.ptr
, cast(int) wide
.length
, buffer
.ptr
, cast(int) buffer
.length
, null, null);
1151 if (requiredLength
< buffer
.length
)
1153 buffer
[requiredLength
] = 0;
1154 return buffer
[0 .. requiredLength
];
1157 char* newBuffer
= cast(char*) mem
.xmalloc_noscan(requiredLength
+ 1);
1158 const length
= WideCharToMultiByte(CodePage
, 0, wide
.ptr
, cast(int) wide
.length
, newBuffer
, requiredLength
, null, null);
1159 assert(length
== requiredLength
);
1160 newBuffer
[length
] = 0;
1161 return newBuffer
[0 .. length
];
1164 /**********************************
1165 * Converts a slice of UTF-8 characters to an array of wchar that's null
1166 * terminated so it can be passed to Win32 APIs then calls the supplied
1170 * str = The string to convert.
1173 * The result of calling F on the UTF16 version of str.
1175 private auto toWStringzThen(alias F
)(const char[] str) nothrow
1177 import dmd
.common
.smallbuffer
: SmallBuffer
, toWStringz
;
1179 if (!str.length
) return F(""w
.ptr
);
1181 wchar[1024] support
= void;
1182 auto buf
= SmallBuffer
!wchar(support
.length
, support
);
1183 wchar[] wide
= toWStringz(str, buf
);
1184 scope(exit
) wide
.ptr
!= buf
.ptr
&& mem
.xfree(wide
.ptr
);