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