1760c2415Smrg 
2760c2415Smrg /* Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
3760c2415Smrg  * http://www.digitalmars.com
4760c2415Smrg  * Distributed under the Boost Software License, Version 1.0.
5760c2415Smrg  * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
6760c2415Smrg  * https://github.com/D-Programming-Language/dmd/blob/master/src/root/filename.c
7760c2415Smrg  */
8760c2415Smrg 
9760c2415Smrg #include "dsystem.h"
10760c2415Smrg #include "filename.h"
11760c2415Smrg 
12760c2415Smrg #include "outbuffer.h"
13760c2415Smrg #include "array.h"
14760c2415Smrg #include "file.h"
15760c2415Smrg #include "rmem.h"
16760c2415Smrg 
17760c2415Smrg #if _WIN32
18760c2415Smrg #include <windows.h>
19760c2415Smrg #endif
20760c2415Smrg 
21760c2415Smrg #if POSIX
22760c2415Smrg #include <utime.h>
23760c2415Smrg #endif
24760c2415Smrg 
25760c2415Smrg /****************************** FileName ********************************/
26760c2415Smrg 
FileName(const char * str)27760c2415Smrg FileName::FileName(const char *str)
28760c2415Smrg     : str(mem.xstrdup(str))
29760c2415Smrg {
30760c2415Smrg }
31760c2415Smrg 
combine(const char * path,const char * name)32760c2415Smrg const char *FileName::combine(const char *path, const char *name)
33760c2415Smrg {   char *f;
34760c2415Smrg     size_t pathlen;
35760c2415Smrg     size_t namelen;
36760c2415Smrg 
37760c2415Smrg     if (!path || !*path)
38760c2415Smrg         return name;
39760c2415Smrg     pathlen = strlen(path);
40760c2415Smrg     namelen = strlen(name);
41760c2415Smrg     f = (char *)mem.xmalloc(pathlen + 1 + namelen + 1);
42760c2415Smrg     memcpy(f, path, pathlen);
43760c2415Smrg #if POSIX
44760c2415Smrg     if (path[pathlen - 1] != '/')
45760c2415Smrg     {   f[pathlen] = '/';
46760c2415Smrg         pathlen++;
47760c2415Smrg     }
48760c2415Smrg #elif _WIN32
49760c2415Smrg     if (path[pathlen - 1] != '\\' &&
50760c2415Smrg         path[pathlen - 1] != '/'  &&
51760c2415Smrg         path[pathlen - 1] != ':')
52760c2415Smrg     {   f[pathlen] = '\\';
53760c2415Smrg         pathlen++;
54760c2415Smrg     }
55760c2415Smrg #else
56760c2415Smrg     assert(0);
57760c2415Smrg #endif
58760c2415Smrg     memcpy(f + pathlen, name, namelen + 1);
59760c2415Smrg     return f;
60760c2415Smrg }
61760c2415Smrg 
62760c2415Smrg // Split a path into an Array of paths
splitPath(const char * path)63760c2415Smrg Strings *FileName::splitPath(const char *path)
64760c2415Smrg {
65760c2415Smrg     char c = 0;                         // unnecessary initializer is for VC /W4
66760c2415Smrg     const char *p;
67760c2415Smrg     OutBuffer buf;
68760c2415Smrg     Strings *array;
69760c2415Smrg 
70760c2415Smrg     array = new Strings();
71760c2415Smrg     if (path)
72760c2415Smrg     {
73760c2415Smrg         p = path;
74760c2415Smrg         do
75760c2415Smrg         {   char instring = 0;
76760c2415Smrg 
77760c2415Smrg             while (isspace((utf8_t)*p))         // skip leading whitespace
78760c2415Smrg                 p++;
79760c2415Smrg             buf.reserve(strlen(p) + 1); // guess size of path
80760c2415Smrg             for (; ; p++)
81760c2415Smrg             {
82760c2415Smrg                 c = *p;
83760c2415Smrg                 switch (c)
84760c2415Smrg                 {
85760c2415Smrg                     case '"':
86760c2415Smrg                         instring ^= 1;  // toggle inside/outside of string
87760c2415Smrg                         continue;
88760c2415Smrg 
89760c2415Smrg #if MACINTOSH
90760c2415Smrg                     case ',':
91760c2415Smrg #endif
92760c2415Smrg #if _WIN32
93760c2415Smrg                     case ';':
94760c2415Smrg #endif
95760c2415Smrg #if POSIX
96760c2415Smrg                     case ':':
97760c2415Smrg #endif
98760c2415Smrg                         p++;
99760c2415Smrg                         break;          // note that ; cannot appear as part
100760c2415Smrg                                         // of a path, quotes won't protect it
101760c2415Smrg 
102760c2415Smrg                     case 0x1A:          // ^Z means end of file
103760c2415Smrg                     case 0:
104760c2415Smrg                         break;
105760c2415Smrg 
106760c2415Smrg                     case '\r':
107760c2415Smrg                         continue;       // ignore carriage returns
108760c2415Smrg 
109760c2415Smrg #if POSIX
110760c2415Smrg                     case '~':
111760c2415Smrg                     {
112760c2415Smrg                         char *home = getenv("HOME");
113*0bfacb9bSmrg                         // Expand ~ only if it is prefixing the rest of the path.
114*0bfacb9bSmrg                         if (!buf.offset && p[1] == '/' && home)
115760c2415Smrg                             buf.writestring(home);
116760c2415Smrg                         else
117760c2415Smrg                             buf.writestring("~");
118760c2415Smrg                         continue;
119760c2415Smrg                     }
120760c2415Smrg #endif
121760c2415Smrg 
122760c2415Smrg                     default:
123760c2415Smrg                         buf.writeByte(c);
124760c2415Smrg                         continue;
125760c2415Smrg                 }
126760c2415Smrg                 break;
127760c2415Smrg             }
128760c2415Smrg             if (buf.offset)             // if path is not empty
129760c2415Smrg             {
130760c2415Smrg                 array->push(buf.extractString());
131760c2415Smrg             }
132760c2415Smrg         } while (c);
133760c2415Smrg     }
134760c2415Smrg     return array;
135760c2415Smrg }
136760c2415Smrg 
compare(RootObject * obj)137760c2415Smrg int FileName::compare(RootObject *obj)
138760c2415Smrg {
139760c2415Smrg     return compare(str, ((FileName *)obj)->str);
140760c2415Smrg }
141760c2415Smrg 
compare(const char * name1,const char * name2)142760c2415Smrg int FileName::compare(const char *name1, const char *name2)
143760c2415Smrg {
144760c2415Smrg #if _WIN32
145760c2415Smrg     return stricmp(name1, name2);
146760c2415Smrg #else
147760c2415Smrg     return strcmp(name1, name2);
148760c2415Smrg #endif
149760c2415Smrg }
150760c2415Smrg 
equals(RootObject * obj)151760c2415Smrg bool FileName::equals(RootObject *obj)
152760c2415Smrg {
153760c2415Smrg     return compare(obj) == 0;
154760c2415Smrg }
155760c2415Smrg 
equals(const char * name1,const char * name2)156760c2415Smrg bool FileName::equals(const char *name1, const char *name2)
157760c2415Smrg {
158760c2415Smrg     return compare(name1, name2) == 0;
159760c2415Smrg }
160760c2415Smrg 
161760c2415Smrg /************************************
162760c2415Smrg  * Return !=0 if absolute path name.
163760c2415Smrg  */
164760c2415Smrg 
absolute(const char * name)165760c2415Smrg bool FileName::absolute(const char *name)
166760c2415Smrg {
167760c2415Smrg #if _WIN32
168760c2415Smrg     return (*name == '\\') ||
169760c2415Smrg            (*name == '/')  ||
170760c2415Smrg            (*name && name[1] == ':');
171760c2415Smrg #elif POSIX
172760c2415Smrg     return (*name == '/');
173760c2415Smrg #else
174760c2415Smrg     assert(0);
175760c2415Smrg #endif
176760c2415Smrg }
177760c2415Smrg 
178760c2415Smrg /********************************
179760c2415Smrg  * Return filename extension (read-only).
180760c2415Smrg  * Points past '.' of extension.
181760c2415Smrg  * If there isn't one, return NULL.
182760c2415Smrg  */
183760c2415Smrg 
ext(const char * str)184760c2415Smrg const char *FileName::ext(const char *str)
185760c2415Smrg {
186760c2415Smrg     size_t len = strlen(str);
187760c2415Smrg 
188760c2415Smrg     const char *e = str + len;
189760c2415Smrg     for (;;)
190760c2415Smrg     {
191760c2415Smrg         switch (*e)
192760c2415Smrg         {   case '.':
193760c2415Smrg                 return e + 1;
194760c2415Smrg #if POSIX
195760c2415Smrg             case '/':
196760c2415Smrg                 break;
197760c2415Smrg #endif
198760c2415Smrg #if _WIN32
199760c2415Smrg             case '\\':
200760c2415Smrg             case ':':
201760c2415Smrg             case '/':
202760c2415Smrg                 break;
203760c2415Smrg #endif
204760c2415Smrg             default:
205760c2415Smrg                 if (e == str)
206760c2415Smrg                     break;
207760c2415Smrg                 e--;
208760c2415Smrg                 continue;
209760c2415Smrg         }
210760c2415Smrg         return NULL;
211760c2415Smrg     }
212760c2415Smrg }
213760c2415Smrg 
ext()214760c2415Smrg const char *FileName::ext()
215760c2415Smrg {
216760c2415Smrg     return ext(str);
217760c2415Smrg }
218760c2415Smrg 
219760c2415Smrg /********************************
220760c2415Smrg  * Return mem.xmalloc'd filename with extension removed.
221760c2415Smrg  */
222760c2415Smrg 
removeExt(const char * str)223760c2415Smrg const char *FileName::removeExt(const char *str)
224760c2415Smrg {
225760c2415Smrg     const char *e = ext(str);
226760c2415Smrg     if (e)
227760c2415Smrg     {   size_t len = (e - str) - 1;
228760c2415Smrg         char *n = (char *)mem.xmalloc(len + 1);
229760c2415Smrg         memcpy(n, str, len);
230760c2415Smrg         n[len] = 0;
231760c2415Smrg         return n;
232760c2415Smrg     }
233760c2415Smrg     return mem.xstrdup(str);
234760c2415Smrg }
235760c2415Smrg 
236760c2415Smrg /********************************
237760c2415Smrg  * Return filename name excluding path (read-only).
238760c2415Smrg  */
239760c2415Smrg 
name(const char * str)240760c2415Smrg const char *FileName::name(const char *str)
241760c2415Smrg {
242760c2415Smrg     size_t len = strlen(str);
243760c2415Smrg 
244760c2415Smrg     const char *e = str + len;
245760c2415Smrg     for (;;)
246760c2415Smrg     {
247760c2415Smrg         switch (*e)
248760c2415Smrg         {
249760c2415Smrg #if POSIX
250760c2415Smrg             case '/':
251760c2415Smrg                return e + 1;
252760c2415Smrg #endif
253760c2415Smrg #if _WIN32
254760c2415Smrg             case '/':
255760c2415Smrg             case '\\':
256760c2415Smrg                 return e + 1;
257760c2415Smrg             case ':':
258760c2415Smrg                 /* The ':' is a drive letter only if it is the second
259760c2415Smrg                  * character or the last character,
260760c2415Smrg                  * otherwise it is an ADS (Alternate Data Stream) separator.
261760c2415Smrg                  * Consider ADS separators as part of the file name.
262760c2415Smrg                  */
263760c2415Smrg                 if (e == str + 1 || e == str + len - 1)
264760c2415Smrg                     return e + 1;
265760c2415Smrg #endif
266760c2415Smrg                 /* falls through */
267760c2415Smrg             default:
268760c2415Smrg                 if (e == str)
269760c2415Smrg                     break;
270760c2415Smrg                 e--;
271760c2415Smrg                 continue;
272760c2415Smrg         }
273760c2415Smrg         return e;
274760c2415Smrg     }
275760c2415Smrg }
276760c2415Smrg 
name()277760c2415Smrg const char *FileName::name()
278760c2415Smrg {
279760c2415Smrg     return name(str);
280760c2415Smrg }
281760c2415Smrg 
282760c2415Smrg /**************************************
283760c2415Smrg  * Return path portion of str.
284760c2415Smrg  * Path will does not include trailing path separator.
285760c2415Smrg  */
286760c2415Smrg 
path(const char * str)287760c2415Smrg const char *FileName::path(const char *str)
288760c2415Smrg {
289760c2415Smrg     const char *n = name(str);
290760c2415Smrg     size_t pathlen;
291760c2415Smrg 
292760c2415Smrg     if (n > str)
293760c2415Smrg     {
294760c2415Smrg #if POSIX
295760c2415Smrg         if (n[-1] == '/')
296760c2415Smrg             n--;
297760c2415Smrg #elif _WIN32
298760c2415Smrg         if (n[-1] == '\\' || n[-1] == '/')
299760c2415Smrg             n--;
300760c2415Smrg #else
301760c2415Smrg         assert(0);
302760c2415Smrg #endif
303760c2415Smrg     }
304760c2415Smrg     pathlen = n - str;
305760c2415Smrg     char *path = (char *)mem.xmalloc(pathlen + 1);
306760c2415Smrg     memcpy(path, str, pathlen);
307760c2415Smrg     path[pathlen] = 0;
308760c2415Smrg     return path;
309760c2415Smrg }
310760c2415Smrg 
311760c2415Smrg /**************************************
312760c2415Smrg  * Replace filename portion of path.
313760c2415Smrg  */
314760c2415Smrg 
replaceName(const char * path,const char * name)315760c2415Smrg const char *FileName::replaceName(const char *path, const char *name)
316760c2415Smrg {
317760c2415Smrg     size_t pathlen;
318760c2415Smrg     size_t namelen;
319760c2415Smrg 
320760c2415Smrg     if (absolute(name))
321760c2415Smrg         return name;
322760c2415Smrg 
323760c2415Smrg     const char *n = FileName::name(path);
324760c2415Smrg     if (n == path)
325760c2415Smrg         return name;
326760c2415Smrg     pathlen = n - path;
327760c2415Smrg     namelen = strlen(name);
328760c2415Smrg     char *f = (char *)mem.xmalloc(pathlen + 1 + namelen + 1);
329760c2415Smrg     memcpy(f, path, pathlen);
330760c2415Smrg #if POSIX
331760c2415Smrg     if (path[pathlen - 1] != '/')
332760c2415Smrg     {   f[pathlen] = '/';
333760c2415Smrg         pathlen++;
334760c2415Smrg     }
335760c2415Smrg #elif _WIN32
336760c2415Smrg     if (path[pathlen - 1] != '\\' &&
337760c2415Smrg         path[pathlen - 1] != '/' &&
338760c2415Smrg         path[pathlen - 1] != ':')
339760c2415Smrg     {   f[pathlen] = '\\';
340760c2415Smrg         pathlen++;
341760c2415Smrg     }
342760c2415Smrg #else
343760c2415Smrg     assert(0);
344760c2415Smrg #endif
345760c2415Smrg     memcpy(f + pathlen, name, namelen + 1);
346760c2415Smrg     return f;
347760c2415Smrg }
348760c2415Smrg 
349760c2415Smrg /***************************
350760c2415Smrg  * Free returned value with FileName::free()
351760c2415Smrg  */
352760c2415Smrg 
defaultExt(const char * name,const char * ext)353760c2415Smrg const char *FileName::defaultExt(const char *name, const char *ext)
354760c2415Smrg {
355760c2415Smrg     const char *e = FileName::ext(name);
356760c2415Smrg     if (e)                              // if already has an extension
357760c2415Smrg         return mem.xstrdup(name);
358760c2415Smrg 
359760c2415Smrg     size_t len = strlen(name);
360760c2415Smrg     size_t extlen = strlen(ext);
361760c2415Smrg     char *s = (char *)mem.xmalloc(len + 1 + extlen + 1);
362760c2415Smrg     memcpy(s,name,len);
363760c2415Smrg     s[len] = '.';
364760c2415Smrg     memcpy(s + len + 1, ext, extlen + 1);
365760c2415Smrg     return s;
366760c2415Smrg }
367760c2415Smrg 
368760c2415Smrg /***************************
369760c2415Smrg  * Free returned value with FileName::free()
370760c2415Smrg  */
371760c2415Smrg 
forceExt(const char * name,const char * ext)372760c2415Smrg const char *FileName::forceExt(const char *name, const char *ext)
373760c2415Smrg {
374760c2415Smrg     const char *e = FileName::ext(name);
375760c2415Smrg     if (e)                              // if already has an extension
376760c2415Smrg     {
377760c2415Smrg         size_t len = e - name;
378760c2415Smrg         size_t extlen = strlen(ext);
379760c2415Smrg 
380760c2415Smrg         char *s = (char *)mem.xmalloc(len + extlen + 1);
381760c2415Smrg         memcpy(s,name,len);
382760c2415Smrg         memcpy(s + len, ext, extlen + 1);
383760c2415Smrg         return s;
384760c2415Smrg     }
385760c2415Smrg     else
386760c2415Smrg         return defaultExt(name, ext);   // doesn't have one
387760c2415Smrg }
388760c2415Smrg 
389760c2415Smrg /******************************
390760c2415Smrg  * Return !=0 if extensions match.
391760c2415Smrg  */
392760c2415Smrg 
equalsExt(const char * ext)393760c2415Smrg bool FileName::equalsExt(const char *ext)
394760c2415Smrg {
395760c2415Smrg     return equalsExt(str, ext);
396760c2415Smrg }
397760c2415Smrg 
equalsExt(const char * name,const char * ext)398760c2415Smrg bool FileName::equalsExt(const char *name, const char *ext)
399760c2415Smrg {
400760c2415Smrg     const char *e = FileName::ext(name);
401760c2415Smrg     if (!e && !ext)
402760c2415Smrg         return true;
403760c2415Smrg     if (!e || !ext)
404760c2415Smrg         return false;
405760c2415Smrg     return FileName::compare(e, ext) == 0;
406760c2415Smrg }
407760c2415Smrg 
408760c2415Smrg /*************************************
409760c2415Smrg  * Search Path for file.
410760c2415Smrg  * Input:
411760c2415Smrg  *      cwd     if true, search current directory before searching path
412760c2415Smrg  */
413760c2415Smrg 
searchPath(Strings * path,const char * name,bool cwd)414760c2415Smrg const char *FileName::searchPath(Strings *path, const char *name, bool cwd)
415760c2415Smrg {
416760c2415Smrg     if (absolute(name))
417760c2415Smrg     {
418760c2415Smrg         return exists(name) ? name : NULL;
419760c2415Smrg     }
420760c2415Smrg     if (cwd)
421760c2415Smrg     {
422760c2415Smrg         if (exists(name))
423760c2415Smrg             return name;
424760c2415Smrg     }
425760c2415Smrg     if (path)
426760c2415Smrg     {
427760c2415Smrg 
428760c2415Smrg         for (size_t i = 0; i < path->dim; i++)
429760c2415Smrg         {
430760c2415Smrg             const char *p = (*path)[i];
431760c2415Smrg             const char *n = combine(p, name);
432760c2415Smrg 
433760c2415Smrg             if (exists(n))
434760c2415Smrg                 return n;
435760c2415Smrg         }
436760c2415Smrg     }
437760c2415Smrg     return NULL;
438760c2415Smrg }
439760c2415Smrg 
440760c2415Smrg 
441760c2415Smrg /*************************************
442760c2415Smrg  * Search Path for file in a safe manner.
443760c2415Smrg  *
444760c2415Smrg  * Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
445760c2415Smrg  * ('Path Traversal') attacks.
446760c2415Smrg  *      http://cwe.mitre.org/data/definitions/22.html
447760c2415Smrg  * More info:
448760c2415Smrg  *      https://www.securecoding.cert.org/confluence/display/c/FIO02-C.+Canonicalize+path+names+originating+from+tainted+sources
449760c2415Smrg  * Returns:
450760c2415Smrg  *      NULL    file not found
451760c2415Smrg  *      !=NULL  mem.xmalloc'd file name
452760c2415Smrg  */
453760c2415Smrg 
safeSearchPath(Strings * path,const char * name)454760c2415Smrg const char *FileName::safeSearchPath(Strings *path, const char *name)
455760c2415Smrg {
456760c2415Smrg #if _WIN32
457760c2415Smrg     // don't allow leading / because it might be an absolute
458760c2415Smrg     // path or UNC path or something we'd prefer to just not deal with
459760c2415Smrg     if (*name == '/')
460760c2415Smrg     {
461760c2415Smrg         return NULL;
462760c2415Smrg     }
463760c2415Smrg     /* Disallow % \ : and .. in name characters
464760c2415Smrg      * We allow / for compatibility with subdirectories which is allowed
465760c2415Smrg      * on dmd/posix. With the leading / blocked above and the rest of these
466760c2415Smrg      * conservative restrictions, we should be OK.
467760c2415Smrg      */
468760c2415Smrg     for (const char *p = name; *p; p++)
469760c2415Smrg     {
470760c2415Smrg         char c = *p;
471760c2415Smrg         if (c == '\\' || c == ':' || c == '%' || (c == '.' && p[1] == '.'))
472760c2415Smrg         {
473760c2415Smrg             return NULL;
474760c2415Smrg         }
475760c2415Smrg     }
476760c2415Smrg 
477760c2415Smrg     return FileName::searchPath(path, name, false);
478760c2415Smrg #elif POSIX
479760c2415Smrg     /* Even with realpath(), we must check for // and disallow it
480760c2415Smrg      */
481760c2415Smrg     for (const char *p = name; *p; p++)
482760c2415Smrg     {
483760c2415Smrg         char c = *p;
484760c2415Smrg         if (c == '/' && p[1] == '/')
485760c2415Smrg         {
486760c2415Smrg             return NULL;
487760c2415Smrg         }
488760c2415Smrg     }
489760c2415Smrg 
490760c2415Smrg     if (path)
491760c2415Smrg     {
492760c2415Smrg         /* Each path is converted to a cannonical name and then a check is done to see
493760c2415Smrg          * that the searched name is really a child one of the the paths searched.
494760c2415Smrg          */
495760c2415Smrg         for (size_t i = 0; i < path->dim; i++)
496760c2415Smrg         {
497760c2415Smrg             const char *cname = NULL;
498760c2415Smrg             const char *cpath = canonicalName((*path)[i]);
499760c2415Smrg             //printf("FileName::safeSearchPath(): name=%s; path=%s; cpath=%s\n",
500760c2415Smrg             //      name, (char *)path->data[i], cpath);
501760c2415Smrg             if (cpath == NULL)
502760c2415Smrg                 goto cont;
503760c2415Smrg             cname = canonicalName(combine(cpath, name));
504760c2415Smrg             //printf("FileName::safeSearchPath(): cname=%s\n", cname);
505760c2415Smrg             if (cname == NULL)
506760c2415Smrg                 goto cont;
507760c2415Smrg             //printf("FileName::safeSearchPath(): exists=%i "
508760c2415Smrg             //      "strncmp(cpath, cname, %i)=%i\n", exists(cname),
509760c2415Smrg             //      strlen(cpath), strncmp(cpath, cname, strlen(cpath)));
510760c2415Smrg             // exists and name is *really* a "child" of path
511760c2415Smrg             if (exists(cname) && strncmp(cpath, cname, strlen(cpath)) == 0)
512760c2415Smrg             {
513760c2415Smrg                 ::free(const_cast<char *>(cpath));
514760c2415Smrg                 const char *p = mem.xstrdup(cname);
515760c2415Smrg                 ::free(const_cast<char *>(cname));
516760c2415Smrg                 return p;
517760c2415Smrg             }
518760c2415Smrg cont:
519760c2415Smrg             if (cpath)
520760c2415Smrg                 ::free(const_cast<char *>(cpath));
521760c2415Smrg             if (cname)
522760c2415Smrg                 ::free(const_cast<char *>(cname));
523760c2415Smrg         }
524760c2415Smrg     }
525760c2415Smrg     return NULL;
526760c2415Smrg #else
527760c2415Smrg     assert(0);
528760c2415Smrg #endif
529760c2415Smrg }
530760c2415Smrg 
531760c2415Smrg 
exists(const char * name)532760c2415Smrg int FileName::exists(const char *name)
533760c2415Smrg {
534760c2415Smrg #if POSIX
535760c2415Smrg     struct stat st;
536760c2415Smrg 
537760c2415Smrg     if (stat(name, &st) < 0)
538760c2415Smrg         return 0;
539760c2415Smrg     if (S_ISDIR(st.st_mode))
540760c2415Smrg         return 2;
541760c2415Smrg     return 1;
542760c2415Smrg #elif _WIN32
543760c2415Smrg     DWORD dw;
544760c2415Smrg     int result;
545760c2415Smrg 
546760c2415Smrg     dw = GetFileAttributesA(name);
547760c2415Smrg     if (dw == INVALID_FILE_ATTRIBUTES)
548760c2415Smrg         result = 0;
549760c2415Smrg     else if (dw & FILE_ATTRIBUTE_DIRECTORY)
550760c2415Smrg         result = 2;
551760c2415Smrg     else
552760c2415Smrg         result = 1;
553760c2415Smrg     return result;
554760c2415Smrg #else
555760c2415Smrg     assert(0);
556760c2415Smrg #endif
557760c2415Smrg }
558760c2415Smrg 
ensurePathExists(const char * path)559760c2415Smrg bool FileName::ensurePathExists(const char *path)
560760c2415Smrg {
561760c2415Smrg     //printf("FileName::ensurePathExists(%s)\n", path ? path : "");
562760c2415Smrg     if (path && *path)
563760c2415Smrg     {
564760c2415Smrg         if (!exists(path))
565760c2415Smrg         {
566760c2415Smrg             const char *p = FileName::path(path);
567760c2415Smrg             if (*p)
568760c2415Smrg             {
569760c2415Smrg #if _WIN32
570760c2415Smrg                 size_t len = strlen(path);
571760c2415Smrg                 if ((len > 2 && p[-1] == ':' && strcmp(path + 2, p) == 0) ||
572760c2415Smrg                     len == strlen(p))
573760c2415Smrg                 {   mem.xfree(const_cast<char *>(p));
574760c2415Smrg                     return 0;
575760c2415Smrg                 }
576760c2415Smrg #endif
577760c2415Smrg                 bool r = ensurePathExists(p);
578760c2415Smrg                 mem.xfree(const_cast<char *>(p));
579760c2415Smrg                 if (r)
580760c2415Smrg                     return r;
581760c2415Smrg             }
582760c2415Smrg #if _WIN32
583760c2415Smrg             char sep = '\\';
584760c2415Smrg #elif POSIX
585760c2415Smrg             char sep = '/';
586760c2415Smrg #endif
587760c2415Smrg             if (path[strlen(path) - 1] != sep)
588760c2415Smrg             {
589760c2415Smrg                 //printf("mkdir(%s)\n", path);
590760c2415Smrg #if _WIN32
591760c2415Smrg                 int r = _mkdir(path);
592760c2415Smrg #endif
593760c2415Smrg #if POSIX
594760c2415Smrg                 int r = mkdir(path, (7 << 6) | (7 << 3) | 7);
595760c2415Smrg #endif
596760c2415Smrg                 if (r)
597760c2415Smrg                 {
598760c2415Smrg                     /* Don't error out if another instance of dmd just created
599760c2415Smrg                      * this directory
600760c2415Smrg                      */
601760c2415Smrg                     if (errno != EEXIST)
602760c2415Smrg                         return true;
603760c2415Smrg                 }
604760c2415Smrg             }
605760c2415Smrg         }
606760c2415Smrg     }
607760c2415Smrg     return false;
608760c2415Smrg }
609760c2415Smrg 
610760c2415Smrg /******************************************
611760c2415Smrg  * Return canonical version of name in a malloc'd buffer.
612760c2415Smrg  * This code is high risk.
613760c2415Smrg  */
canonicalName(const char * name)614760c2415Smrg const char *FileName::canonicalName(const char *name)
615760c2415Smrg {
616760c2415Smrg #if POSIX
617760c2415Smrg     // NULL destination buffer is allowed and preferred
618760c2415Smrg     return realpath(name, NULL);
619760c2415Smrg #elif _WIN32
620760c2415Smrg     /* Apparently, there is no good way to do this on Windows.
621760c2415Smrg      * GetFullPathName isn't it, but use it anyway.
622760c2415Smrg      */
623760c2415Smrg     DWORD result = GetFullPathNameA(name, 0, NULL, NULL);
624760c2415Smrg     if (result)
625760c2415Smrg     {
626760c2415Smrg         char *buf = (char *)mem.xmalloc(result);
627760c2415Smrg         result = GetFullPathNameA(name, result, buf, NULL);
628760c2415Smrg         if (result == 0)
629760c2415Smrg         {
630760c2415Smrg             ::free(buf);
631760c2415Smrg             return NULL;
632760c2415Smrg         }
633760c2415Smrg         return buf;
634760c2415Smrg     }
635760c2415Smrg     return NULL;
636760c2415Smrg #else
637760c2415Smrg     assert(0);
638760c2415Smrg     return NULL;
639760c2415Smrg #endif
640760c2415Smrg }
641760c2415Smrg 
642760c2415Smrg /********************************
643760c2415Smrg  * Free memory allocated by FileName routines
644760c2415Smrg  */
free(const char * str)645760c2415Smrg void FileName::free(const char *str)
646760c2415Smrg {
647760c2415Smrg     if (str)
648760c2415Smrg     {   assert(str[0] != (char)0xAB);
649760c2415Smrg         memset(const_cast<char *>(str), 0xAB, strlen(str) + 1);     // stomp
650760c2415Smrg     }
651760c2415Smrg     mem.xfree(const_cast<char *>(str));
652760c2415Smrg }
653760c2415Smrg 
toChars()654760c2415Smrg const char *FileName::toChars() const
655760c2415Smrg {
656760c2415Smrg     return str;
657760c2415Smrg }
658