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