Mercurial > projects > ddmd
annotate dmd/FileName.d @ 5:63623152e82a
Fixed memory corruption bug which was introduced when attempting to restore GC functionality
author | dkoroskin <> |
---|---|
date | Tue, 08 Dec 2009 21:39:17 +0300 |
parents | d706d958e4e8 |
children | 2cc604139636 |
rev | line source |
---|---|
0 | 1 module dmd.FileName; |
2 | |
3 import dmd.String; | |
4 import dmd.Array; | |
5 import dmd.OutBuffer; | |
6 import dmd.File; | |
7 | |
4 | 8 import core.memory; |
2 | 9 |
5
63623152e82a
Fixed memory corruption bug which was introduced when attempting to restore GC functionality
dkoroskin <>
parents:
4
diff
changeset
|
10 import core.stdc.stdlib : malloc, alloca; |
0 | 11 import core.stdc.string : memcpy, strlen; |
12 import core.stdc.ctype : isspace; | |
13 | |
14 import std.contracts : assumeUnique; | |
15 import std.string : cmp, icmp; | |
16 import std.file : mkdirRecurse; | |
17 | |
18 import core.sys.windows.windows; | |
19 | |
20 class FileName : String | |
21 { | |
22 this(string str) | |
23 { | |
24 super(str); | |
25 } | |
26 | |
27 this(string path, string name) | |
28 { | |
29 super(combine(path, name)); | |
30 } | |
31 | |
32 hash_t hashCode() | |
33 { | |
34 version (_WIN32) { | |
35 // We need a different hashCode because it must be case-insensitive | |
36 size_t len = str.length; | |
37 hash_t hash = 0; | |
38 ubyte* s = cast(ubyte*)str.ptr; | |
39 | |
40 for (;;) | |
41 { | |
42 switch (len) | |
43 { | |
44 case 0: | |
45 return hash; | |
46 | |
47 case 1: | |
48 hash *= 37; | |
49 hash += *(cast(ubyte*)s) | 0x20; | |
50 return hash; | |
51 | |
52 case 2: | |
53 hash *= 37; | |
54 hash += *(cast(ushort*)s) | 0x2020; | |
55 return hash; | |
56 | |
57 case 3: | |
58 hash *= 37; | |
59 hash += (*cast(ushort*)s) << 8 + | |
60 ((cast(ubyte*)s)[2]) | 0x202020; | |
61 break; | |
62 | |
63 default: | |
64 hash *= 37; | |
65 hash += (*cast(int*)s) | 0x20202020; | |
66 s += 4; | |
67 len -= 4; | |
68 break; | |
69 } | |
70 } | |
71 } else { | |
72 // darwin HFS is case insensitive, though... | |
73 return super.hashCode(); | |
74 } | |
75 } | |
76 | |
77 bool opEquals(Object obj) | |
78 { | |
79 return opCmp(obj) == 0; | |
80 } | |
81 | |
82 static int equals(string name1, string name2) | |
83 { | |
84 return compare(name1, name2) == 0; | |
85 } | |
86 | |
87 int opCmp(Object obj) | |
88 { | |
89 return compare(str, (cast(FileName)obj).str); | |
90 } | |
91 | |
92 static int compare(string name1, string name2) | |
93 { | |
94 version (_WIN32) { | |
95 return icmp(name1, name2); | |
96 } else { | |
97 return cmp(name1, name2); | |
98 } | |
99 } | |
100 | |
101 static bool absolute(string name) | |
102 { | |
103 version (_WIN32) { | |
104 return (*name == '\\') || | |
105 (*name == '/') || | |
106 (*name && name[1] == ':'); | |
107 } else version (POSIX) { | |
108 return (*name == '/'); | |
109 } else { | |
110 static assert(false); | |
111 } | |
112 } | |
113 | |
114 /******************************** | |
115 * Return filename extension (read-only). | |
116 * Points past '.' of extension. | |
117 * If there isn't one, return NULL. | |
118 */ | |
119 static string ext(string str) | |
120 { | |
121 foreach_reverse (i, c; str) | |
122 { | |
123 switch (c) | |
124 { | |
125 case '.': | |
126 return str[i+1..$]; | |
127 version (POSIX) { | |
128 case '/': | |
129 return null; | |
130 } | |
131 version (_WIN32) { | |
132 case '\\': | |
133 case ':': | |
134 case '/': | |
135 return null; | |
136 | |
137 } default: | |
138 break; | |
139 } | |
140 } | |
141 | |
142 return null; | |
143 } | |
144 | |
145 string ext() | |
146 { | |
147 return ext(str); | |
148 } | |
149 | |
150 /******************************** | |
151 * Return filename with extension removed. | |
152 */ | |
153 | |
154 static string removeExt(string str) | |
155 { | |
156 string e = ext(str); | |
157 if (e !is null) | |
158 { | |
159 size_t len = (e.ptr - str.ptr - 1); | |
160 return str[0..len]; | |
161 } | |
162 | |
163 return str; | |
164 } | |
165 | |
166 /******************************** | |
167 * Return filename name excluding path (read-only). | |
168 */ | |
169 | |
170 static string name(string str) | |
171 { | |
172 foreach_reverse(i, c; str) | |
173 { | |
174 switch (c) | |
175 { | |
176 version (POSIX) { | |
177 case '/': | |
178 return str[i+1..$]; | |
179 } | |
180 version (_WIN32) { | |
181 case '/': | |
182 case '\\': | |
183 return str[i+1..$]; | |
184 case ':': | |
185 /* The ':' is a drive letter only if it is the second | |
186 * character or the last character, | |
187 * otherwise it is an ADS (Alternate Data Stream) separator. | |
188 * Consider ADS separators as part of the file name. | |
189 */ | |
190 if (i == 1 || i == str.length - 1) | |
191 return str[i+1..$]; | |
192 } | |
193 default: | |
194 break; | |
195 } | |
196 } | |
197 | |
198 return str; | |
199 } | |
200 | |
201 string name() | |
202 { | |
203 return name(str); | |
204 } | |
205 | |
206 /************************************** | |
207 * Return path portion of str. | |
208 * Path will does not include trailing path separator. | |
209 */ | |
210 | |
211 static string path(string str) | |
212 { | |
213 auto n = name(str).ptr; | |
214 | |
215 if (n > str.ptr) | |
216 { | |
217 auto p = n - 1; | |
218 version (POSIX) { | |
219 if (*p == '/') | |
220 n--; | |
221 } else version (_WIN32) { | |
222 if (*p == '\\' || *p == '/') | |
223 n--; | |
224 } else { | |
225 static assert(false); | |
226 } | |
227 } | |
228 | |
229 size_t pathlen = n - str.ptr; | |
230 return str[0..pathlen]; | |
231 } | |
232 | |
233 /************************************** | |
234 * Replace filename portion of path. | |
235 */ | |
236 | |
237 static string replaceName(string path, string name) | |
238 { | |
239 if (absolute(name)) | |
240 return name; | |
241 | |
242 string n = FileName.name(path); | |
243 if (n is path) | |
244 return name; | |
245 | |
246 size_t pathlen = n.ptr - path.ptr; | |
247 size_t namelen = name.length; | |
248 | |
2 | 249 char* f = cast(char*)GC.malloc(pathlen + 1 + namelen + 1); |
0 | 250 memcpy(f, path.ptr, pathlen); |
251 version (POSIX) { | |
252 if (path[pathlen - 1] != '/') | |
253 { | |
254 f[pathlen] = '/'; | |
255 pathlen++; | |
256 } | |
257 } else version (_WIN32) { | |
258 if (path[pathlen - 1] != '\\' && | |
259 path[pathlen - 1] != '/' && | |
260 path[pathlen - 1] != ':') | |
261 { | |
262 f[pathlen] = '\\'; | |
263 pathlen++; | |
264 } | |
265 } else { | |
266 static assert(false); | |
267 } | |
268 memcpy(f + pathlen, name.ptr, namelen + 1); | |
269 | |
270 return assumeUnique(f[0..pathlen+namelen]); | |
271 } | |
272 | |
273 static string combine(string path, string name) | |
274 { | |
275 size_t pathlen; | |
276 size_t namelen; | |
277 | |
278 if (path.length == 0) | |
279 return name; | |
280 | |
281 pathlen = path.length; | |
282 namelen = name.length; | |
283 | |
2 | 284 char* f = cast(char*)GC.malloc(pathlen + 1 + namelen + 1); |
0 | 285 |
286 memcpy(f, path.ptr, pathlen); | |
287 | |
288 version (POSIX) { | |
289 if (path[pathlen - 1] != '/') | |
290 { | |
291 f[pathlen] = '/'; | |
292 pathlen++; | |
293 } | |
294 } else version (_WIN32) { | |
295 if (path[pathlen - 1] != '\\' && | |
296 path[pathlen - 1] != '/' && | |
297 path[pathlen - 1] != ':') | |
298 { | |
299 f[pathlen] = '\\'; | |
300 pathlen++; | |
301 } | |
302 } else { | |
303 static assert(0); | |
304 } | |
305 memcpy(f + pathlen, name.ptr, namelen + 1); | |
306 | |
307 return assumeUnique(f[0..pathlen+namelen]); | |
308 } | |
309 | |
310 static string[] splitPath(const(char)[] spath) | |
311 { | |
312 char c = 0; // unnecessary initializer is for VC /W4 | |
313 | |
314 scope OutBuffer buf = new OutBuffer(); | |
315 string[] array; | |
316 | |
317 if (spath !is null) | |
318 { | |
319 const(char)* p = spath.ptr; | |
320 int len = spath.length; | |
321 do | |
322 { | |
323 char instring = 0; | |
324 | |
325 while (len > 0 && isspace(*p)) { // skip leading whitespace | |
326 p++; | |
327 --len; | |
328 } | |
329 | |
330 buf.reserve(len + 1); // guess size of path | |
331 for (; len; p++, len--) | |
332 { | |
333 c = *p; | |
334 switch (c) | |
335 { | |
336 case '"': | |
337 instring ^= 1; // toggle inside/outside of string | |
338 continue; | |
339 | |
340 version (MACINTOSH) { | |
341 case ',': | |
342 } | |
343 version (_WIN32) { | |
344 case ';': | |
345 } | |
346 version (POSIX) { | |
347 case ':': | |
348 } | |
349 p++; | |
350 break; // note that ; cannot appear as part | |
351 // of a path, quotes won't protect it | |
352 | |
353 case 0x1A: // ^Z means end of file | |
354 //case 0: | |
355 break; | |
356 | |
357 case '\r': | |
358 continue; // ignore carriage returns | |
359 | |
360 version (POSIX) { | |
361 case '~': | |
362 buf.writestring(getenv("HOME")); | |
363 continue; | |
364 } | |
365 | |
366 version (disabled) { | |
367 case ' ': | |
368 case '\t': // tabs in filenames? | |
369 if (!instring) // if not in string | |
370 break; // treat as end of path | |
371 } | |
372 default: | |
373 buf.writeByte(c); | |
374 continue; | |
375 } | |
376 break; | |
377 } | |
378 if (buf.offset) // if path is not empty | |
379 { | |
380 //buf.writeByte(0); // to asciiz | |
381 array ~= buf.extractString(); | |
382 } | |
383 } while (len > 0); | |
384 } | |
385 | |
386 return array; | |
387 } | |
388 | |
389 static FileName defaultExt(string name, string ext) | |
390 { | |
391 string e = FileName.ext(name); | |
392 if (e !is null) { | |
393 // if already has an extension | |
394 return new FileName(name); | |
395 } | |
396 | |
397 size_t len = name.length; | |
398 size_t extlen = ext.length; | |
2 | 399 char* s = cast(char*)GC.malloc(len + 1 + extlen + 1); |
0 | 400 memcpy(s, name.ptr, len); |
401 s[len] = '.'; | |
402 memcpy(s + len + 1, ext.ptr, extlen + 1); | |
403 | |
404 return new FileName(assumeUnique(s[0..len+1+extlen])); | |
405 } | |
406 | |
407 static FileName forceExt(string name, string ext) | |
408 { | |
409 string e = FileName.ext(name); | |
410 if (e !is null) // if already has an extension | |
411 { | |
412 size_t len = e.ptr - name.ptr; | |
413 size_t extlen = ext.length; | |
414 | |
5
63623152e82a
Fixed memory corruption bug which was introduced when attempting to restore GC functionality
dkoroskin <>
parents:
4
diff
changeset
|
415 char* s = cast(char*)malloc(len + extlen + 1); /// ! |
0 | 416 memcpy(s, name.ptr, len); |
417 memcpy(s + len, ext.ptr, extlen + 1); | |
418 return new FileName(assumeUnique(s[0..len+extlen])); | |
419 } | |
420 | |
421 return defaultExt(name, ext); // doesn't have one | |
422 } | |
423 | |
424 /****************************** | |
425 * Return true if extensions match. | |
426 */ | |
427 | |
428 bool equalsExt(string ext) | |
429 { | |
430 string e = FileName.ext(); | |
431 if (e.length == 0 && ext.length == 0) | |
432 return true; | |
433 | |
434 if (e.length == 0 || ext.length == 0) | |
435 return false; | |
436 | |
437 version (POSIX) { | |
438 return cmp(e,ext) == 0; | |
439 } else version (_WIN32) { | |
440 return icmp(e,ext) == 0; | |
441 } else { | |
442 static assert(0); | |
443 } | |
444 } | |
445 | |
446 /************************************* | |
447 * Copy file from this to to. | |
448 */ | |
449 | |
450 void CopyTo(FileName to) | |
451 { | |
452 scope File file = new File(this); | |
453 | |
454 version (_WIN32) { | |
2 | 455 file.touchtime = GC.malloc(WIN32_FIND_DATA.sizeof); // keep same file time |
0 | 456 } else version (POSIX) { |
2 | 457 file.touchtime = GC.malloc(stat.sizeof); // keep same file time |
0 | 458 } else { |
459 static assert(0); | |
460 } | |
461 file.readv(); | |
462 file.name = to; | |
463 file.writev(); | |
464 } | |
465 | |
466 /************************************* | |
467 * Search Path for file. | |
468 * Input: | |
469 * cwd if true, search current directory before searching path | |
470 */ | |
471 | |
472 static string searchPath(Array path, string name, bool cwd) | |
473 { | |
474 if (absolute(name)) { | |
475 return exists(name) ? name : null; | |
476 } | |
477 | |
478 if (cwd) { | |
479 if (exists(name)) { | |
480 return name; | |
481 } | |
482 } | |
483 | |
484 if (path !is null) { | |
485 foreach (i; 0..path.dim) | |
486 { | |
487 String p = cast(String)path.data[i]; | |
488 string n = combine(p.str, name); | |
489 | |
490 if (exists(n)) | |
491 return n; | |
492 } | |
493 } | |
494 | |
495 return null; | |
496 } | |
497 | |
498 static int exists(string name) | |
499 { | |
500 version (POSIX) { | |
501 stat st; | |
502 | |
503 if (stat(name, &st) < 0) | |
504 return 0; | |
505 if (S_ISDIR(st.st_mode)) | |
506 return 2; | |
507 return 1; | |
508 } else version (_WIN32) { | |
509 HANDLE h = CreateFileA(toStringz(name), GENERIC_READ, FILE_SHARE_READ, null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, HANDLE.init); /// | |
510 if (h == INVALID_HANDLE_VALUE) { | |
511 return 0; | |
512 } | |
513 | |
514 CloseHandle(h); | |
515 | |
516 DWORD dw = GetFileAttributesA(name.ptr); /// CARE! | |
517 if (dw == -1L) { | |
518 assert(false); | |
519 return 0; | |
520 } else if (dw & FILE_ATTRIBUTE_DIRECTORY) { | |
521 return 2; | |
522 } else { | |
523 return 1; | |
524 } | |
525 } else { | |
526 static assert(0); | |
527 } | |
528 } | |
529 | |
530 static void ensurePathExists(string path) | |
531 { | |
532 try { | |
533 mkdirRecurse(path); | |
534 } catch { | |
535 } | |
536 } | |
537 } |