1 /**
2  * Encapsulate path and file names.
3  *
4  * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
5  * Authors:   Walter Bright, http://www.digitalmars.com
6  * License:   $(LINK2 http://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
10  */
11 
12 module dmd.root.filename;
13 
14 import core.stdc.ctype;
15 import core.stdc.errno;
16 import core.stdc.string;
17 import dmd.root.array;
18 import dmd.root.file;
19 import dmd.root.outbuffer;
20 import dmd.root.port;
21 import dmd.root.rmem;
22 import dmd.root.rootobject;
23 import dmd.root.string;
24 
25 version (Posix)
26 {
27     import core.sys.posix.stdlib;
28     import core.sys.posix.sys.stat;
29     import core.sys.posix.unistd : getcwd;
30 }
31 
32 version (Windows)
33 {
34     import core.sys.windows.winbase;
35     import core.sys.windows.windef;
36     import core.sys.windows.winnls;
37 
38     extern (Windows) DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*) nothrow @nogc;
39     extern (Windows) void SetLastError(DWORD) nothrow @nogc;
40     extern (C) char* getcwd(char* buffer, size_t maxlen) nothrow;
41 
42     // assume filenames encoded in system default Windows ANSI code page
43     private enum CodePage = CP_ACP;
44 }
45 
46 version (CRuntime_Glibc)
47 {
48     extern (C) char* canonicalize_file_name(const char*) nothrow;
49 }
50 
51 alias Strings = Array!(const(char)*);
52 
53 
54 // Check whether character is a directory separator
55 private bool isDirSeparator(char c) pure nothrow @nogc
56 {
57     version (Windows)
58     {
59         return c == '\\' || c == '/';
60     }
61     else version (Posix)
62     {
63         return c == '/';
64     }
65     else
66     {
67         assert(0);
68     }
69 }
70 
71 /***********************************************************
72  * Encapsulate path and file names.
73  */
74 struct FileName
75 {
76 nothrow:
77     private const(char)[] str;
78 
79     ///
80     extern (D) this(const(char)[] str) pure
81     {
82         this.str = str.xarraydup;
83     }
84 
85     /// Compare two name according to the platform's rules (case sensitive or not)
86     extern (C++) static bool equals(const(char)* name1, const(char)* name2) pure @nogc
87     {
88         return equals(name1.toDString, name2.toDString);
89     }
90 
91     /// Ditto
92     extern (D) static bool equals(const(char)[] name1, const(char)[] name2) pure @nogc
93     {
94         if (name1.length != name2.length)
95             return false;
96 
97         version (Windows)
98         {
99             return name1.ptr == name2.ptr ||
100                    Port.memicmp(name1.ptr, name2.ptr, name1.length) == 0;
101         }
102         else
103         {
104             return name1 == name2;
105         }
106     }
107 
108     /************************************
109      * Determine if path is absolute.
110      * Params:
111      *  name = path
112      * Returns:
113      *  true if absolute path name.
114      */
115     extern (C++) static bool absolute(const(char)* name) pure @nogc
116     {
117         return absolute(name.toDString);
118     }
119 
120     /// Ditto
121     extern (D) static bool absolute(const(char)[] name) pure @nogc
122     {
123         if (!name.length)
124             return false;
125 
126         version (Windows)
127         {
128             return isDirSeparator(name[0])
129                 || (name.length >= 2 && name[1] == ':');
130         }
131         else version (Posix)
132         {
133             return isDirSeparator(name[0]);
134         }
135         else
136         {
137             assert(0);
138         }
139     }
140 
141     unittest
142     {
143         assert(absolute("/"[]) == true);
144         assert(absolute(""[]) == false);
145 
146         version (Windows)
147         {
148             assert(absolute(r"\"[]) == true);
149             assert(absolute(r"\\"[]) == true);
150             assert(absolute(r"c:"[]) == true);
151         }
152     }
153 
154     /**
155     Return the given name as an absolute path
156 
157     Params:
158         name = path
159         base = the absolute base to prefix name with if it is relative
160 
161     Returns: name as an absolute path relative to base
162     */
163     extern (C++) static const(char)* toAbsolute(const(char)* name, const(char)* base = null)
164     {
165         const name_ = name.toDString();
166         const base_ = base ? base.toDString() : getcwd(null, 0).toDString();
167         return absolute(name_) ? name : combine(base_, name_).ptr;
168     }
169 
170     /********************************
171      * Determine file name extension as slice of input.
172      * Params:
173      *  str = file name
174      * Returns:
175      *  filename extension (read-only).
176      *  Points past '.' of extension.
177      *  If there isn't one, return null.
178      */
179     extern (C++) static const(char)* ext(const(char)* str) pure @nogc
180     {
181         return ext(str.toDString).ptr;
182     }
183 
184     /// Ditto
185     extern (D) static const(char)[] ext(const(char)[] str) nothrow pure @safe @nogc
186     {
187         foreach_reverse (idx, char e; str)
188         {
189             switch (e)
190             {
191             case '.':
192                 return str[idx + 1 .. $];
193             version (Posix)
194             {
195             case '/':
196                 return null;
197             }
198             version (Windows)
199             {
200             case '\\':
201             case ':':
202             case '/':
203                 return null;
204             }
205             default:
206                 continue;
207             }
208         }
209         return null;
210     }
211 
212     unittest
213     {
214         assert(ext("/foo/bar/dmd.conf"[]) == "conf");
215         assert(ext("object.o"[]) == "o");
216         assert(ext("/foo/bar/dmd"[]) == null);
217         assert(ext(".objdir.o/object"[]) == null);
218         assert(ext([]) == null);
219     }
220 
221     extern (C++) const(char)* ext() const pure @nogc
222     {
223         return ext(str).ptr;
224     }
225 
226     /********************************
227      * Return file name without extension.
228      *
229      * TODO:
230      * Once slice are used everywhere and `\0` is not assumed,
231      * this can be turned into a simple slicing.
232      *
233      * Params:
234      *  str = file name
235      *
236      * Returns:
237      *  mem.xmalloc'd filename with extension removed.
238      */
239     extern (C++) static const(char)* removeExt(const(char)* str)
240     {
241         return removeExt(str.toDString).ptr;
242     }
243 
244     /// Ditto
245     extern (D) static const(char)[] removeExt(const(char)[] str)
246     {
247         auto e = ext(str);
248         if (e.length)
249         {
250             const len = (str.length - e.length) - 1; // -1 for the dot
251             char* n = cast(char*)mem.xmalloc(len + 1);
252             memcpy(n, str.ptr, len);
253             n[len] = 0;
254             return n[0 .. len];
255         }
256         return mem.xstrdup(str.ptr)[0 .. str.length];
257     }
258 
259     unittest
260     {
261         assert(removeExt("/foo/bar/object.d"[]) == "/foo/bar/object");
262         assert(removeExt("/foo/bar/frontend.di"[]) == "/foo/bar/frontend");
263     }
264 
265     /********************************
266      * Return filename name excluding path (read-only).
267      */
268     extern (C++) static const(char)* name(const(char)* str) pure @nogc
269     {
270         return name(str.toDString).ptr;
271     }
272 
273     /// Ditto
274     extern (D) static const(char)[] name(const(char)[] str) pure @nogc
275     {
276         foreach_reverse (idx, char e; str)
277         {
278             switch (e)
279             {
280                 version (Posix)
281                 {
282                 case '/':
283                     return str[idx + 1 .. $];
284                 }
285                 version (Windows)
286                 {
287                 case '/':
288                 case '\\':
289                     return str[idx + 1 .. $];
290                 case ':':
291                     /* The ':' is a drive letter only if it is the second
292                      * character or the last character,
293                      * otherwise it is an ADS (Alternate Data Stream) separator.
294                      * Consider ADS separators as part of the file name.
295                      */
296                     if (idx == 1 || idx == str.length - 1)
297                         return str[idx + 1 .. $];
298                     break;
299                 }
300             default:
301                 break;
302             }
303         }
304         return str;
305     }
306 
307     extern (C++) const(char)* name() const pure @nogc
308     {
309         return name(str).ptr;
310     }
311 
312     unittest
313     {
314         assert(name("/foo/bar/object.d"[]) == "object.d");
315         assert(name("/foo/bar/frontend.di"[]) == "frontend.di");
316     }
317 
318     /**************************************
319      * Return path portion of str.
320      * returned string is newly allocated
321      * Path does not include trailing path separator.
322      */
323     extern (C++) static const(char)* path(const(char)* str)
324     {
325         return path(str.toDString).ptr;
326     }
327 
328     /// Ditto
329     extern (D) static const(char)[] path(const(char)[] str)
330     {
331         const n = name(str);
332         bool hasTrailingSlash;
333         if (n.length < str.length)
334         {
335             if (isDirSeparator(str[$ - n.length - 1]))
336                 hasTrailingSlash = true;
337         }
338         const pathlen = str.length - n.length - (hasTrailingSlash ? 1 : 0);
339         char* path = cast(char*)mem.xmalloc(pathlen + 1);
340         memcpy(path, str.ptr, pathlen);
341         path[pathlen] = 0;
342         return path[0 .. pathlen];
343     }
344 
345     unittest
346     {
347         assert(path("/foo/bar"[]) == "/foo");
348         assert(path("foo"[]) == "");
349     }
350 
351     /**************************************
352      * Replace filename portion of path.
353      */
354     extern (D) static const(char)[] replaceName(const(char)[] path, const(char)[] name)
355     {
356         if (absolute(name))
357             return name;
358         auto n = FileName.name(path);
359         if (n == path)
360             return name;
361         return combine(path[0 .. $ - n.length], name);
362     }
363 
364     /**
365        Combine a `path` and a file `name`
366 
367        Params:
368          path = Path to append to
369          name = Name to append to path
370 
371        Returns:
372          The `\0` terminated string which is the combination of `path` and `name`
373          and a valid path.
374     */
375     extern (C++) static const(char)* combine(const(char)* path, const(char)* name)
376     {
377         if (!path)
378             return name;
379         return combine(path.toDString, name.toDString).ptr;
380     }
381 
382     /// Ditto
383     extern(D) static const(char)[] combine(const(char)[] path, const(char)[] name)
384     {
385         return !path.length ? name : buildPath(path, name);
386     }
387 
388     unittest
389     {
390         version (Windows)
391             assert(combine("foo"[], "bar"[]) == "foo\\bar");
392         else
393             assert(combine("foo"[], "bar"[]) == "foo/bar");
394         assert(combine("foo/"[], "bar"[]) == "foo/bar");
395     }
396 
397     static const(char)[] buildPath(const(char)[][] fragments...)
398     {
399         size_t size;
400         foreach (f; fragments)
401             size += f.length ? f.length + 1 : 0;
402         if (size == 0)
403             size = 1;
404 
405         char* p = cast(char*) mem.xmalloc_noscan(size);
406         size_t length;
407         foreach (f; fragments)
408         {
409             if (!f.length)
410                 continue;
411 
412             p[length .. length + f.length] = f;
413             length += f.length;
414 
415             const last = p[length - 1];
416             version (Posix)
417             {
418                 if (!isDirSeparator(last))
419                     p[length++] = '/';
420             }
421             else version (Windows)
422             {
423                 if (!isDirSeparator(last) && last != ':')
424                     p[length++] = '\\';
425             }
426             else
427                 assert(0);
428         }
429 
430         // overwrite last slash with null terminator
431         p[length ? --length : 0] = 0;
432 
433         return p[0 .. length];
434     }
435 
436     unittest
437     {
438         assert(buildPath() == "");
439         assert(buildPath("foo") == "foo");
440         assert(buildPath("foo", null) == "foo");
441         assert(buildPath(null, "foo") == "foo");
442         version (Windows)
443             assert(buildPath("C:", r"a\", "bb/", "ccc", "d") == r"C:a\bb/ccc\d");
444         else
445             assert(buildPath("a/", "bb", "ccc") == "a/bb/ccc");
446     }
447 
448     // Split a path into an Array of paths
449     extern (C++) static Strings* splitPath(const(char)* path)
450     {
451         auto array = new Strings();
452         int sink(const(char)* p) nothrow
453         {
454             array.push(p);
455             return 0;
456         }
457         splitPath(&sink, path);
458         return array;
459     }
460 
461     /****
462      * Split path (such as that returned by `getenv("PATH")`) into pieces, each piece is mem.xmalloc'd
463      * Handle double quotes and ~.
464      * Pass the pieces to sink()
465      * Params:
466      *  sink = send the path pieces here, end when sink() returns !=0
467      *  path = the path to split up.
468      */
469     static void splitPath(int delegate(const(char)*) nothrow sink, const(char)* path)
470     {
471         if (!path)
472             return;
473 
474         auto p = path;
475         OutBuffer buf;
476         char c;
477         do
478         {
479             const(char)* home;
480             bool instring = false;
481             while (isspace(*p)) // skip leading whitespace
482                 ++p;
483             buf.reserve(8); // guess size of piece
484             for (;; ++p)
485             {
486                 c = *p;
487                 switch (c)
488                 {
489                     case '"':
490                         instring ^= false; // toggle inside/outside of string
491                         continue;
492 
493                     version (OSX)
494                     {
495                     case ',':
496                     }
497                     version (Windows)
498                     {
499                     case ';':
500                     }
501                     version (Posix)
502                     {
503                     case ':':
504                     }
505                         p++;    // ; cannot appear as part of a
506                         break;  // path, quotes won't protect it
507 
508                     case 0x1A:  // ^Z means end of file
509                     case 0:
510                         break;
511 
512                     case '\r':
513                         continue;  // ignore carriage returns
514 
515                     version (Posix)
516                     {
517                     case '~':
518                         if (!home)
519                             home = getenv("HOME");
520                         // Expand ~ only if it is prefixing the rest of the path.
521                         if (!buf.length && p[1] == '/' && home)
522                             buf.writestring(home);
523                         else
524                             buf.writeByte('~');
525                         continue;
526                     }
527 
528                     version (none)
529                     {
530                     case ' ':
531                     case '\t':         // tabs in filenames?
532                         if (!instring) // if not in string
533                             break;     // treat as end of path
534                     }
535                     default:
536                         buf.writeByte(c);
537                         continue;
538                 }
539                 break;
540             }
541             if (buf.length) // if path is not empty
542             {
543                 if (sink(buf.extractChars()))
544                     break;
545             }
546         } while (c);
547     }
548 
549     /**
550      * Add the extension `ext` to `name`, regardless of the content of `name`
551      *
552      * Params:
553      *   name = Path to append the extension to
554      *   ext  = Extension to add (should not include '.')
555      *
556      * Returns:
557      *   A newly allocated string (free with `FileName.free`)
558      */
559     extern(D) static char[] addExt(const(char)[] name, const(char)[] ext) pure
560     {
561         const len = name.length + ext.length + 2;
562         auto s = cast(char*)mem.xmalloc(len);
563         s[0 .. name.length] = name[];
564         s[name.length] = '.';
565         s[name.length + 1 .. len - 1] = ext[];
566         s[len - 1] = '\0';
567         return s[0 .. len - 1];
568     }
569 
570 
571     /***************************
572      * Free returned value with FileName::free()
573      */
574     extern (C++) static const(char)* defaultExt(const(char)* name, const(char)* ext)
575     {
576         return defaultExt(name.toDString, ext.toDString).ptr;
577     }
578 
579     /// Ditto
580     extern (D) static const(char)[] defaultExt(const(char)[] name, const(char)[] ext)
581     {
582         auto e = FileName.ext(name);
583         if (e.length) // it already has an extension
584             return name.xarraydup;
585         return addExt(name, ext);
586     }
587 
588     unittest
589     {
590         assert(defaultExt("/foo/object.d"[], "d") == "/foo/object.d");
591         assert(defaultExt("/foo/object"[], "d") == "/foo/object.d");
592         assert(defaultExt("/foo/bar.d"[], "o") == "/foo/bar.d");
593     }
594 
595     /***************************
596      * Free returned value with FileName::free()
597      */
598     extern (C++) static const(char)* forceExt(const(char)* name, const(char)* ext)
599     {
600         return forceExt(name.toDString, ext.toDString).ptr;
601     }
602 
603     /// Ditto
604     extern (D) static const(char)[] forceExt(const(char)[] name, const(char)[] ext)
605     {
606         if (auto e = FileName.ext(name))
607             return addExt(name[0 .. $ - e.length - 1], ext);
608         return defaultExt(name, ext); // doesn't have one
609     }
610 
611     unittest
612     {
613         assert(forceExt("/foo/object.d"[], "d") == "/foo/object.d");
614         assert(forceExt("/foo/object"[], "d") == "/foo/object.d");
615         assert(forceExt("/foo/bar.d"[], "o") == "/foo/bar.o");
616     }
617 
618     /// Returns:
619     ///   `true` if `name`'s extension is `ext`
620     extern (C++) static bool equalsExt(const(char)* name, const(char)* ext) pure @nogc
621     {
622         return equalsExt(name.toDString, ext.toDString);
623     }
624 
625     /// Ditto
626     extern (D) static bool equalsExt(const(char)[] name, const(char)[] ext) pure @nogc
627     {
628         auto e = FileName.ext(name);
629         if (!e.length && !ext.length)
630             return true;
631         if (!e.length || !ext.length)
632             return false;
633         return FileName.equals(e, ext);
634     }
635 
636     unittest
637     {
638         assert(!equalsExt("foo.bar"[], "d"));
639         assert(equalsExt("foo.bar"[], "bar"));
640         assert(equalsExt("object.d"[], "d"));
641         assert(!equalsExt("object"[], "d"));
642     }
643 
644     /******************************
645      * Return !=0 if extensions match.
646      */
647     extern (C++) bool equalsExt(const(char)* ext) const pure @nogc
648     {
649         return equalsExt(str, ext.toDString());
650     }
651 
652     /*************************************
653      * Search paths for file.
654      * Params:
655      *  path = array of path strings
656      *  name = file to look for
657      *  cwd = true means search current directory before searching path
658      * Returns:
659      *  if found, filename combined with path, otherwise null
660      */
661     extern (C++) static const(char)* searchPath(Strings* path, const(char)* name, bool cwd)
662     {
663         return searchPath(path, name.toDString, cwd).ptr;
664     }
665 
666     extern (D) static const(char)[] searchPath(Strings* path, const(char)[] name, bool cwd)
667     {
668         if (absolute(name))
669         {
670             return exists(name) ? name : null;
671         }
672         if (cwd)
673         {
674             if (exists(name))
675                 return name;
676         }
677         if (path)
678         {
679             foreach (p; *path)
680             {
681                 auto n = combine(p.toDString, name);
682                 if (exists(n))
683                     return n;
684                 //combine might return name
685                 if (n.ptr != name.ptr)
686                 {
687                     mem.xfree(cast(void*)n.ptr);
688                 }
689             }
690         }
691         return null;
692     }
693 
694     extern (D) static const(char)[] searchPath(const(char)* path, const(char)[] name, bool cwd)
695     {
696         if (absolute(name))
697         {
698             return exists(name) ? name : null;
699         }
700         if (cwd)
701         {
702             if (exists(name))
703                 return name;
704         }
705         if (path && *path)
706         {
707             const(char)[] result;
708 
709             int sink(const(char)* p) nothrow
710             {
711                 auto n = combine(p.toDString, name);
712                 mem.xfree(cast(void*)p);
713                 if (exists(n))
714                 {
715                     result = n;
716                     return 1;   // done with splitPath() call
717                 }
718                 return 0;
719             }
720 
721             splitPath(&sink, path);
722             return result;
723         }
724         return null;
725     }
726 
727     /************************************
728      * Determine if path is safe.
729      * Params:
730      *  name = path
731      * Returns:
732      *  true if path is safe.
733      */
734     private extern (D) static bool safePath(const(char)* name) pure @nogc
735     {
736         // Don't allow absolute path
737         if (absolute(name))
738         {
739             return false;
740         }
741         // Don't allow parent directory
742         if (name[0] == '.' && name[1] == '.' && (!name[2] || isDirSeparator(name[2])))
743         {
744             return false;
745         }
746 
747         version (Windows)
748         {
749             // According to https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
750             // the following characters are not allowed in path: < > : " | ? *
751             // Additionally we do not allow reference to parent directory ("..") in the path.
752             for (const(char)* p = name; *p; p++)
753             {
754                 char c = *p;
755                 if (c == '<' || c == '>' || c == ':' || c == '"' || c == '|' || c == '?' || c == '*' ||
756                    (isDirSeparator(c) && p[1] == '.' && p[2] == '.' && (!p[3] || isDirSeparator(p[3]))))
757                 {
758                     return false;
759                 }
760             }
761             return true;
762         }
763         else version (Posix)
764         {
765             // Do not allow reference to parent directory ("..") in the path.
766             for (const(char)* p = name; *p; p++)
767             {
768                 char c = *p;
769                 if (isDirSeparator(c) && p[1] == '.' && p[2] == '.' && (!p[3] || isDirSeparator(p[3])))
770                 {
771                     return false;
772                 }
773             }
774             return true;
775         }
776         else
777         {
778             assert(0);
779         }
780     }
781     unittest
782     {
783         assert(safePath(r""));
784         assert(safePath(r"foo.bar"));
785         assert(safePath(r"foo.bar"));
786         assert(safePath(r"foo.bar.boo"));
787         assert(safePath(r"foo/bar.boo"));
788         assert(safePath(r"foo/bar/boo"));
789         assert(safePath(r"foo/bar//boo"));       // repeated directory separator
790         assert(safePath(r"foo.."));
791         assert(safePath(r"foo..boo"));
792         assert(safePath(r"foo/..boo"));
793         assert(safePath(r"foo../boo"));
794         assert(!safePath(r"/"));
795         assert(!safePath(r".."));
796         assert(!safePath(r"../"));
797         assert(!safePath(r"foo/.."));
798         assert(!safePath(r"foo/../"));
799         assert(!safePath(r"foo/../../boo"));
800 
801         version (Windows)
802         {
803             assert(!safePath(r"\foo"));           // absolute path
804             assert(!safePath(r"\\foo"));          // UNC path
805             assert(!safePath(r"c:foo"));          // drive letter + relative path
806             assert(!safePath(r"c:\foo"));         // drive letter + absolute path
807 
808             // Backslash as directory separator
809             assert(safePath(r"foo\bar.boo"));
810             assert(safePath(r"foo\bar\boo"));
811             assert(safePath(r"foo\bar\\boo"));   // repeated directory separator
812             assert(safePath(r"foo\..boo"));
813             assert(safePath(r"foo..\boo"));
814             assert(!safePath(r"\"));
815             assert(!safePath(r"..\"));
816             assert(!safePath(r"foo\.."));
817             assert(!safePath(r"foo\..\"));
818             assert(!safePath(r"foo\..\..\boo"));
819         }
820     }
821 
822     /*************************************
823      * Search Path for file in a safe manner.
824      *
825      * Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
826      * ('Path Traversal') attacks.
827      *      http://cwe.mitre.org/data/definitions/22.html
828      * More info:
829      *      https://www.securecoding.cert.org/confluence/display/c/FIO02-C.+Canonicalize+path+names+originating+from+tainted+sources
830      * Returns:
831      *      NULL    file not found
832      *      !=NULL  mem.xmalloc'd file name
833      */
834     extern (C++) static const(char)* safeSearchPath(Strings* path, const(char)* name)
835     {
836         if (!safePath(name))
837         {
838             return null;
839         }
840 
841         version (Windows)
842         {
843             return FileName.searchPath(path, name, false);
844         }
845         else version (Posix)
846         {
847             if (path)
848             {
849                 /* Each path is converted to a cannonical name and then a check is done to see
850                  * that the searched name is really a child one of the the paths searched.
851                  */
852                 for (size_t i = 0; i < path.dim; i++)
853                 {
854                     const(char)* cname = null;
855                     const(char)* cpath = canonicalName((*path)[i]);
856                     //printf("FileName::safeSearchPath(): name=%s; path=%s; cpath=%s\n",
857                     //      name, (char *)path.data[i], cpath);
858                     if (cpath is null)
859                         goto cont;
860                     cname = canonicalName(combine(cpath, name));
861                     //printf("FileName::safeSearchPath(): cname=%s\n", cname);
862                     if (cname is null)
863                         goto cont;
864                     //printf("FileName::safeSearchPath(): exists=%i "
865                     //      "strncmp(cpath, cname, %i)=%i\n", exists(cname),
866                     //      strlen(cpath), strncmp(cpath, cname, strlen(cpath)));
867                     // exists and name is *really* a "child" of path
868                     if (exists(cname) && strncmp(cpath, cname, strlen(cpath)) == 0)
869                     {
870                         mem.xfree(cast(void*)cpath);
871                         const(char)* p = mem.xstrdup(cname);
872                         mem.xfree(cast(void*)cname);
873                         return p;
874                     }
875                 cont:
876                     if (cpath)
877                         mem.xfree(cast(void*)cpath);
878                     if (cname)
879                         mem.xfree(cast(void*)cname);
880                 }
881             }
882             return null;
883         }
884         else
885         {
886             assert(0);
887         }
888     }
889 
890     /**
891        Check if the file the `path` points to exists
892 
893        Returns:
894          0 if it does not exists
895          1 if it exists and is not a directory
896          2 if it exists and is a directory
897      */
898     extern (C++) static int exists(const(char)* name)
899     {
900         return exists(name.toDString);
901     }
902 
903     /// Ditto
904     extern (D) static int exists(const(char)[] name)
905     {
906         if (!name.length)
907             return 0;
908         version (Posix)
909         {
910             stat_t st;
911             if (name.toCStringThen!((v) => stat(v.ptr, &st)) < 0)
912                 return 0;
913             if (S_ISDIR(st.st_mode))
914                 return 2;
915             return 1;
916         }
917         else version (Windows)
918         {
919             return name.toWStringzThen!((wname)
920             {
921                 const dw = GetFileAttributesW(&wname[0]);
922                 if (dw == -1)
923                     return 0;
924                 else if (dw & FILE_ATTRIBUTE_DIRECTORY)
925                     return 2;
926                 else
927                     return 1;
928             });
929         }
930         else
931         {
932             assert(0);
933         }
934     }
935 
936     /**
937        Ensure that the provided path exists
938 
939        Accepts a path to either a file or a directory.
940        In the former case, the basepath (path to the containing directory)
941        will be checked for existence, and created if it does not exists.
942        In the later case, the directory pointed to will be checked for existence
943        and created if needed.
944 
945        Params:
946          path = a path to a file or a directory
947 
948        Returns:
949          `true` if the directory exists or was successfully created
950      */
951     extern (D) static bool ensurePathExists(const(char)[] path)
952     {
953         //printf("FileName::ensurePathExists(%s)\n", path ? path : "");
954         if (!path.length)
955             return true;
956         if (exists(path))
957             return true;
958 
959         // We were provided with a file name
960         // We need to call ourselves recursively to ensure parent dir exist
961         const char[] p = FileName.path(path);
962         if (p.length)
963         {
964             version (Windows)
965             {
966                 // Note: Windows filename comparison should be case-insensitive,
967                 // however p is a subslice of path so we don't need it
968                 if (path.length == p.length ||
969                     (path.length > 2 && path[1] == ':' && path[2 .. $] == p))
970                 {
971                     mem.xfree(cast(void*)p.ptr);
972                     return true;
973                 }
974             }
975             const r = ensurePathExists(p);
976             mem.xfree(cast(void*)p);
977 
978             if (!r)
979                 return r;
980         }
981 
982         version (Windows)
983             const r = _mkdir(path);
984         version (Posix)
985         {
986             errno = 0;
987             const r = path.toCStringThen!((pathCS) => mkdir(pathCS.ptr, (7 << 6) | (7 << 3) | 7));
988         }
989 
990         if (r == 0)
991             return true;
992 
993         // Don't error out if another instance of dmd just created
994         // this directory
995         version (Windows)
996         {
997             import core.sys.windows.winerror : ERROR_ALREADY_EXISTS;
998             if (GetLastError() == ERROR_ALREADY_EXISTS)
999                 return true;
1000         }
1001         version (Posix)
1002         {
1003             if (errno == EEXIST)
1004                 return true;
1005         }
1006 
1007         return false;
1008     }
1009 
1010     ///ditto
1011     extern (C++) static bool ensurePathExists(const(char)* path)
1012     {
1013         return ensurePathExists(path.toDString);
1014     }
1015 
1016     /******************************************
1017      * Return canonical version of name.
1018      * This code is high risk.
1019      */
1020     extern (C++) static const(char)* canonicalName(const(char)* name)
1021     {
1022         return canonicalName(name.toDString).ptr;
1023     }
1024 
1025     /// Ditto
1026     extern (D) static const(char)[] canonicalName(const(char)[] name)
1027     {
1028         version (Posix)
1029         {
1030             import core.stdc.limits;      // PATH_MAX
1031             import core.sys.posix.unistd; // _PC_PATH_MAX
1032 
1033             // Older versions of druntime don't have PATH_MAX defined.
1034             // i.e: dmd __VERSION__ < 2085, gdc __VERSION__ < 2076.
1035             static if (!__traits(compiles, PATH_MAX))
1036             {
1037                 version (DragonFlyBSD)
1038                     enum PATH_MAX = 1024;
1039                 else version (FreeBSD)
1040                     enum PATH_MAX = 1024;
1041                 else version (linux)
1042                     enum PATH_MAX = 4096;
1043                 else version (NetBSD)
1044                     enum PATH_MAX = 1024;
1045                 else version (OpenBSD)
1046                     enum PATH_MAX = 1024;
1047                 else version (OSX)
1048                     enum PATH_MAX = 1024;
1049                 else version (Solaris)
1050                     enum PATH_MAX = 1024;
1051             }
1052 
1053             // Have realpath(), passing a NULL destination pointer may return an
1054             // internally malloc'd buffer, however it is implementation defined
1055             // as to what happens, so cannot rely on it.
1056             static if (__traits(compiles, PATH_MAX))
1057             {
1058                 // Have compile time limit on filesystem path, use it with realpath.
1059                 char[PATH_MAX] buf = void;
1060                 auto path = name.toCStringThen!((n) => realpath(n.ptr, buf.ptr));
1061                 if (path !is null)
1062                     return xarraydup(path.toDString);
1063             }
1064             else static if (__traits(compiles, canonicalize_file_name))
1065             {
1066                 // Have canonicalize_file_name, which malloc's memory.
1067                 // We need a dmd.root.rmem allocation though.
1068                 auto path = name.toCStringThen!((n) => canonicalize_file_name(n.ptr));
1069                 scope(exit) .free(path.ptr);
1070                 if (path !is null)
1071                     return xarraydup(path.toDString);
1072             }
1073             else static if (__traits(compiles, _PC_PATH_MAX))
1074             {
1075                 // Panic! Query the OS for the buffer limit.
1076                 auto path_max = pathconf("/", _PC_PATH_MAX);
1077                 if (path_max > 0)
1078                 {
1079                     char *buf = cast(char*)mem.xmalloc(path_max);
1080                     scope(exit) mem.xfree(buf);
1081                     auto path = name.toCStringThen!((n) => realpath(n.ptr, buf));
1082                     if (path !is null)
1083                         return xarraydup(path.toDString);
1084                 }
1085             }
1086             // Give up trying to support this platform, just duplicate the filename
1087             // unless there is nothing to copy from.
1088             if (!name.length)
1089                 return null;
1090             return xarraydup(name);
1091         }
1092         else version (Windows)
1093         {
1094             // Convert to wstring first since otherwise the Win32 APIs have a character limit
1095             return name.toWStringzThen!((wname)
1096             {
1097                 /* Apparently, there is no good way to do this on Windows.
1098                  * GetFullPathName isn't it, but use it anyway.
1099                  */
1100                 // First find out how long the buffer has to be, incl. terminating null.
1101                 const capacity = GetFullPathNameW(&wname[0], 0, null, null);
1102                 if (!capacity) return null;
1103                 auto buffer = cast(wchar*) mem.xmalloc_noscan(capacity * wchar.sizeof);
1104                 scope(exit) mem.xfree(buffer);
1105 
1106                 // Actually get the full path name. If the buffer is large enough,
1107                 // the returned length does NOT include the terminating null...
1108                 const length = GetFullPathNameW(&wname[0], capacity, buffer, null /*filePart*/);
1109                 assert(length == capacity - 1);
1110 
1111                 return toNarrowStringz(buffer[0 .. length]);
1112             });
1113         }
1114         else
1115         {
1116             assert(0);
1117         }
1118     }
1119 
1120     unittest
1121     {
1122         string filename = "foo.bar";
1123         const path = canonicalName(filename);
1124         scope(exit) free(path.ptr);
1125         assert(path.length >= filename.length);
1126         assert(path[$ - filename.length .. $] == filename);
1127     }
1128 
1129     /********************************
1130      * Free memory allocated by FileName routines
1131      */
1132     extern (C++) static void free(const(char)* str) pure
1133     {
1134         if (str)
1135         {
1136             assert(str[0] != cast(char)0xAB);
1137             memset(cast(void*)str, 0xAB, strlen(str) + 1); // stomp
1138         }
1139         mem.xfree(cast(void*)str);
1140     }
1141 
1142     extern (C++) const(char)* toChars() const pure nothrow @nogc @trusted
1143     {
1144         // Since we can return an empty slice (but '\0' terminated),
1145         // we don't do bounds check (as `&str[0]` does)
1146         return str.ptr;
1147     }
1148 
1149     const(char)[] toString() const pure nothrow @nogc @trusted
1150     {
1151         return str;
1152     }
1153 
1154     bool opCast(T)() const pure nothrow @nogc @safe
1155     if (is(T == bool))
1156     {
1157         return str.ptr !is null;
1158     }
1159 }
1160 
1161 version(Windows)
1162 {
1163     /****************************************************************
1164      * The code before used the POSIX function `mkdir` on Windows. That
1165      * function is now deprecated and fails with long paths, so instead
1166      * we use the newer `CreateDirectoryW`.
1167      *
1168      * `CreateDirectoryW` is the unicode version of the generic macro
1169      * `CreateDirectory`.  `CreateDirectoryA` has a file path
1170      *  limitation of 248 characters, `mkdir` fails with less and might
1171      *  fail due to the number of consecutive `..`s in the
1172      *  path. `CreateDirectoryW` also normally has a 248 character
1173      * limit, unless the path is absolute and starts with `\\?\`. Note
1174      * that this is different from starting with the almost identical
1175      * `\\?`.
1176      *
1177      * Params:
1178      *  path = The path to create.
1179      *
1180      * Returns:
1181      *  0 on success, 1 on failure.
1182      *
1183      * References:
1184      *  https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
1185      */
1186     private int _mkdir(const(char)[] path) nothrow
1187     {
1188         const createRet = path.extendedPathThen!(
1189             p => CreateDirectoryW(&p[0], null /*securityAttributes*/));
1190         // different conventions for CreateDirectory and mkdir
1191         return createRet == 0 ? 1 : 0;
1192     }
1193 
1194     /**************************************
1195      * Converts a path to one suitable to be passed to Win32 API
1196      * functions that can deal with paths longer than 248
1197      * characters then calls the supplied function on it.
1198      *
1199      * Params:
1200      *  path = The Path to call F on.
1201      *
1202      * Returns:
1203      *  The result of calling F on path.
1204      *
1205      * References:
1206      *  https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
1207      */
1208     package auto extendedPathThen(alias F)(const(char)[] path)
1209     {
1210         if (!path.length)
1211             return F((wchar[]).init);
1212         return path.toWStringzThen!((wpath)
1213         {
1214             // GetFullPathNameW expects a sized buffer to store the result in. Since we don't
1215             // know how large it has to be, we pass in null and get the needed buffer length
1216             // as the return code.
1217             const pathLength = GetFullPathNameW(&wpath[0],
1218                                                 0 /*length8*/,
1219                                                 null /*output buffer*/,
1220                                                 null /*filePartBuffer*/);
1221             if (pathLength == 0)
1222             {
1223                 return F((wchar[]).init);
1224             }
1225 
1226             // wpath is the UTF16 version of path, but to be able to use
1227             // extended paths, we need to prefix with `\\?\` and the absolute
1228             // path.
1229             static immutable prefix = `\\?\`w;
1230 
1231             // prefix only needed for long names and non-UNC names
1232             const needsPrefix = pathLength >= MAX_PATH && (wpath[0] != '\\' || wpath[1] != '\\');
1233             const prefixLength = needsPrefix ? prefix.length : 0;
1234 
1235             // +1 for the null terminator
1236             const bufferLength = pathLength + prefixLength + 1;
1237 
1238             wchar[1024] absBuf = void;
1239             wchar[] absPath = bufferLength > absBuf.length
1240                 ? new wchar[bufferLength] : absBuf[0 .. bufferLength];
1241 
1242             absPath[0 .. prefixLength] = prefix[0 .. prefixLength];
1243 
1244             const absPathRet = GetFullPathNameW(&wpath[0],
1245                 cast(uint)(absPath.length - prefixLength - 1),
1246                 &absPath[prefixLength],
1247                 null /*filePartBuffer*/);
1248 
1249             if (absPathRet == 0 || absPathRet > absPath.length - prefixLength)
1250             {
1251                 return F((wchar[]).init);
1252             }
1253 
1254             absPath[$ - 1] = '\0';
1255             // Strip null terminator from the slice
1256             return F(absPath[0 .. $ - 1]);
1257         });
1258     }
1259 
1260     /**********************************
1261      * Converts a UTF-16 string to a (null-terminated) narrow string.
1262      * Returns:
1263      *  If `buffer` is specified and the result fits, a slice of that buffer,
1264      *  otherwise a new buffer which can be released via `mem.xfree()`.
1265      *  Nulls are propagated, i.e., if `wide` is null, the returned slice is
1266      *  null too.
1267      */
1268     char[] toNarrowStringz(const(wchar)[] wide, char[] buffer = null) nothrow
1269     {
1270         if (wide is null)
1271             return null;
1272 
1273         const requiredLength = WideCharToMultiByte(CodePage, 0, wide.ptr, cast(int) wide.length, buffer.ptr, cast(int) buffer.length, null, null);
1274         if (requiredLength < buffer.length)
1275         {
1276             buffer[requiredLength] = 0;
1277             return buffer[0 .. requiredLength];
1278         }
1279 
1280         char* newBuffer = cast(char*) mem.xmalloc_noscan(requiredLength + 1);
1281         const length = WideCharToMultiByte(CodePage, 0, wide.ptr, cast(int) wide.length, newBuffer, requiredLength, null, null);
1282         assert(length == requiredLength);
1283         newBuffer[length] = 0;
1284         return newBuffer[0 .. length];
1285     }
1286 
1287     /**********************************
1288      * Converts a narrow string to a (null-terminated) UTF-16 string.
1289      * Returns:
1290      *  If `buffer` is specified and the result fits, a slice of that buffer,
1291      *  otherwise a new buffer which can be released via `mem.xfree()`.
1292      *  Nulls are propagated, i.e., if `narrow` is null, the returned slice is
1293      *  null too.
1294      */
1295     wchar[] toWStringz(const(char)[] narrow, wchar[] buffer = null) nothrow
1296     {
1297         if (narrow is null)
1298             return null;
1299 
1300         const requiredLength = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
1301         if (requiredLength < buffer.length)
1302         {
1303             buffer[requiredLength] = 0;
1304             return buffer[0 .. requiredLength];
1305         }
1306 
1307         wchar* newBuffer = cast(wchar*) mem.xmalloc_noscan((requiredLength + 1) * wchar.sizeof);
1308         const length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, newBuffer, requiredLength);
1309         assert(length == requiredLength);
1310         newBuffer[length] = 0;
1311         return newBuffer[0 .. length];
1312     }
1313 
1314     /**********************************
1315      * Converts a slice of UTF-8 characters to an array of wchar that's null
1316      * terminated so it can be passed to Win32 APIs then calls the supplied
1317      * function on it.
1318      *
1319      * Params:
1320      *  str = The string to convert.
1321      *
1322      * Returns:
1323      *  The result of calling F on the UTF16 version of str.
1324      */
1325     private auto toWStringzThen(alias F)(const(char)[] str) nothrow
1326     {
1327         if (!str.length) return F(""w.ptr);
1328 
1329         wchar[1024] buf = void;
1330         wchar[] wide = toWStringz(str, buf);
1331         scope(exit) wide.ptr != buf.ptr && mem.xfree(wide.ptr);
1332 
1333         return F(wide);
1334     }
1335 }