Mercurial > projects > ldc
comparison lphobos/std/path.d @ 473:373489eeaf90
Applied downs' lphobos update
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Mon, 04 Aug 2008 19:28:49 +0200 |
parents | |
children | 88e23f8c2354 |
comparison
equal
deleted
inserted
replaced
472:15c804b6ce77 | 473:373489eeaf90 |
---|---|
1 | |
2 /** | |
3 * Macros: | |
4 * WIKI = Phobos/StdPath | |
5 * Copyright: | |
6 * Placed into public domain. | |
7 * www.digitalmars.com | |
8 * | |
9 * Grzegorz Adam Hankiewicz added some documentation. | |
10 * | |
11 * This module is used to parse file names. All the operations | |
12 * work only on strings; they don't perform any input/output | |
13 * operations. This means that if a path contains a directory name | |
14 * with a dot, functions like getExt() will work with it just as | |
15 * if it was a file. To differentiate these cases, | |
16 * use the std.file module first (i.e. std.file.isDir()). | |
17 */ | |
18 | |
19 /* NOTE: This file has been patched from the original DMD distribution to | |
20 work with the GDC compiler. | |
21 | |
22 Modified by David Friedman, March 2006 | |
23 */ | |
24 | |
25 module std.path; | |
26 | |
27 //debug=path; // uncomment to turn on debugging printf's | |
28 //private import std.stdio; | |
29 | |
30 import std.string; | |
31 | |
32 version(Unix) | |
33 { | |
34 import std.c.stdlib; | |
35 version(linux) import std.c.linux.linux; | |
36 else import std.c.unix.unix; | |
37 import std.outofmemory; | |
38 /*extern(C) struct passwd | |
39 { | |
40 char* pw_name, pw_passwd; uint pw_uid, pw_gid; char* pw_gecos, pw_dir, pw_shell; | |
41 }*/ | |
42 } | |
43 | |
44 version(Windows) | |
45 { | |
46 | |
47 /** String used to separate directory names in a path. Under | |
48 * Windows this is a backslash, under Linux a slash. */ | |
49 const char[1] sep = "\\"; | |
50 /** Alternate version of sep[] used in Windows (a slash). Under | |
51 * Linux this is empty. */ | |
52 const char[1] altsep = "/"; | |
53 /** Path separator string. A semi colon under Windows, a colon | |
54 * under Linux. */ | |
55 const char[1] pathsep = ";"; | |
56 /** String used to separate lines, \r\n under Windows and \n | |
57 * under Linux. */ | |
58 const char[2] linesep = "\r\n"; /// String used to separate lines. | |
59 const char[1] curdir = "."; /// String representing the current directory. | |
60 const char[2] pardir = ".."; /// String representing the parent directory. | |
61 } | |
62 else version(Unix) | |
63 { | |
64 /** String used to separate directory names in a path. Under | |
65 * Windows this is a backslash, under Linux a slash. */ | |
66 const char[1] sep = "/"; | |
67 /** Alternate version of sep[] used in Windows (a slash). Under | |
68 * Linux this is empty. */ | |
69 const char[0] altsep; | |
70 /** Path separator string. A semi colon under Windows, a colon | |
71 * under Linux. */ | |
72 const char[1] pathsep = ":"; | |
73 /** String used to separate lines, \r\n under Windows and \n | |
74 * under Linux. */ | |
75 const char[1] linesep = "\n"; | |
76 const char[1] curdir = "."; /// String representing the current directory. | |
77 const char[2] pardir = ".."; /// String representing the parent directory. | |
78 } | |
79 else | |
80 { | |
81 static assert(0); | |
82 } | |
83 | |
84 /***************************** | |
85 * Compare file names. | |
86 * Returns: | |
87 * <table border=1 cellpadding=4 cellspacing=0> | |
88 * <tr> <td> < 0 <td> filename1 < filename2 | |
89 * <tr> <td> = 0 <td> filename1 == filename2 | |
90 * <tr> <td> > 0 <td> filename1 > filename2 | |
91 * </table> | |
92 */ | |
93 | |
94 version (Windows) alias std.string.icmp fcmp; | |
95 | |
96 version (Unix) alias std.string.cmp fcmp; | |
97 | |
98 /************************** | |
99 * Extracts the extension from a filename or path. | |
100 * | |
101 * This function will search fullname from the end until the | |
102 * first dot, path separator or first character of fullname is | |
103 * reached. Under Windows, the drive letter separator (<i>colon</i>) | |
104 * also terminates the search. | |
105 * | |
106 * Returns: If a dot was found, characters to its right are | |
107 * returned. If a path separator was found, or fullname didn't | |
108 * contain any dots or path separators, returns null. | |
109 * | |
110 * Throws: Nothing. | |
111 * | |
112 * Examples: | |
113 * ----- | |
114 * version(Win32) | |
115 * { | |
116 * getExt(r"d:\path\foo.bat") // "bat" | |
117 * getExt(r"d:\path.two\bar") // null | |
118 * } | |
119 * version(linux) | |
120 * { | |
121 * getExt(r"/home/user.name/bar.") // "" | |
122 * getExt(r"d:\\path.two\\bar") // "two\\bar" | |
123 * getExt(r"/home/user/.resource") // "resource" | |
124 * } | |
125 * ----- | |
126 */ | |
127 | |
128 char[] getExt(char[] fullname) | |
129 { | |
130 size_t i; | |
131 | |
132 i = fullname.length; | |
133 while (i > 0) | |
134 { | |
135 if (fullname[i - 1] == '.') | |
136 return fullname[i .. fullname.length]; | |
137 i--; | |
138 version(Win32) | |
139 { | |
140 if (fullname[i] == ':' || fullname[i] == '\\') | |
141 break; | |
142 } | |
143 else version(Unix) | |
144 { | |
145 if (fullname[i] == '/') | |
146 break; | |
147 } | |
148 } | |
149 return null; | |
150 } | |
151 | |
152 unittest | |
153 { | |
154 debug(path) printf("path.getExt.unittest\n"); | |
155 int i; | |
156 char[] result; | |
157 | |
158 version (Win32) | |
159 result = getExt("d:\\path\\foo.bat"); | |
160 version (Unix) | |
161 result = getExt("/path/foo.bat"); | |
162 i = cmp(result, "bat"); | |
163 assert(i == 0); | |
164 | |
165 version (Win32) | |
166 result = getExt("d:\\path\\foo."); | |
167 version (Unix) | |
168 result = getExt("d/path/foo."); | |
169 i = cmp(result, ""); | |
170 assert(i == 0); | |
171 | |
172 version (Win32) | |
173 result = getExt("d:\\path\\foo"); | |
174 version (Unix) | |
175 result = getExt("d/path/foo"); | |
176 i = cmp(result, ""); | |
177 assert(i == 0); | |
178 | |
179 version (Win32) | |
180 result = getExt("d:\\path.bar\\foo"); | |
181 version (Unix) | |
182 result = getExt("/path.bar/foo"); | |
183 | |
184 i = cmp(result, ""); | |
185 assert(i == 0); | |
186 | |
187 result = getExt("foo"); | |
188 i = cmp(result, ""); | |
189 assert(i == 0); | |
190 } | |
191 | |
192 /************************** | |
193 * Returns the extensionless version of a filename or path. | |
194 * | |
195 * This function will search fullname from the end until the | |
196 * first dot, path separator or first character of fullname is | |
197 * reached. Under Windows, the drive letter separator (<i>colon</i>) | |
198 * also terminates the search. | |
199 * | |
200 * Returns: If a dot was found, characters to its left are | |
201 * returned. If a path separator was found, or fullname didn't | |
202 * contain any dots or path separators, returns null. | |
203 * | |
204 * Throws: Nothing. | |
205 * | |
206 * Examples: | |
207 * ----- | |
208 * version(Win32) | |
209 * { | |
210 * getName(r"d:\path\foo.bat") => "d:\path\foo" | |
211 * getName(r"d:\path.two\bar") => null | |
212 * } | |
213 * version(linux) | |
214 * { | |
215 * getName("/home/user.name/bar.") => "/home/user.name/bar" | |
216 * getName(r"d:\path.two\bar") => "d:\path" | |
217 * getName("/home/user/.resource") => "/home/user/" | |
218 * } | |
219 * ----- | |
220 */ | |
221 | |
222 char[] getName(char[] fullname) | |
223 { | |
224 size_t i; | |
225 | |
226 i = fullname.length; | |
227 while (i > 0) | |
228 { | |
229 if (fullname[i - 1] == '.') | |
230 return fullname[0 .. i - 1]; | |
231 i--; | |
232 version(Win32) | |
233 { | |
234 if (fullname[i] == ':' || fullname[i] == '\\') | |
235 break; | |
236 } | |
237 version(Unix) | |
238 { | |
239 if (fullname[i] == '/') | |
240 break; | |
241 } | |
242 } | |
243 return null; | |
244 } | |
245 | |
246 unittest | |
247 { | |
248 debug(path) printf("path.getName.unittest\n"); | |
249 int i; | |
250 char[] result; | |
251 | |
252 result = getName("foo.bar"); | |
253 i = cmp(result, "foo"); | |
254 assert(i == 0); | |
255 | |
256 result = getName("d:\\path.two\\bar"); | |
257 version (Win32) | |
258 i = cmp(result, null); | |
259 version (linux) | |
260 i = cmp(result, "d:\\path"); | |
261 assert(i == 0); | |
262 } | |
263 | |
264 /************************** | |
265 * Extracts the base name of a path. | |
266 * | |
267 * This function will search fullname from the end until the | |
268 * first path separator or first character of fullname is | |
269 * reached. Under Windows, the drive letter separator (<i>colon</i>) | |
270 * also terminates the search. | |
271 * | |
272 * Returns: If a path separator was found, all the characters to its | |
273 * right are returned. Otherwise, fullname is returned. | |
274 * | |
275 * Throws: Nothing. | |
276 * | |
277 * Examples: | |
278 * ----- | |
279 * version(Win32) | |
280 * { | |
281 * getBaseName(r"d:\path\foo.bat") => "foo.bat" | |
282 * } | |
283 * version(linux) | |
284 * { | |
285 * getBaseName("/home/user.name/bar.") => "bar." | |
286 * } | |
287 * ----- | |
288 */ | |
289 | |
290 char[] getBaseName(char[] fullname) | |
291 out (result) | |
292 { | |
293 assert(result.length <= fullname.length); | |
294 } | |
295 body | |
296 { | |
297 size_t i; | |
298 | |
299 for (i = fullname.length; i > 0; i--) | |
300 { | |
301 version(Win32) | |
302 { | |
303 if (fullname[i - 1] == ':' || fullname[i - 1] == '\\') | |
304 break; | |
305 } | |
306 else version(Unix) | |
307 { | |
308 if (fullname[i - 1] == '/') | |
309 break; | |
310 } | |
311 } | |
312 return fullname[i .. fullname.length]; | |
313 } | |
314 | |
315 unittest | |
316 { | |
317 debug(path) printf("path.getBaseName.unittest\n"); | |
318 int i; | |
319 char[] result; | |
320 | |
321 version (Windows) | |
322 result = getBaseName("d:\\path\\foo.bat"); | |
323 version (Unix) | |
324 result = getBaseName("/path/foo.bat"); | |
325 //printf("result = '%.*s'\n", result); | |
326 i = cmp(result, "foo.bat"); | |
327 assert(i == 0); | |
328 | |
329 version (Windows) | |
330 result = getBaseName("a\\b"); | |
331 version (Unix) | |
332 result = getBaseName("a/b"); | |
333 i = cmp(result, "b"); | |
334 assert(i == 0); | |
335 } | |
336 | |
337 | |
338 /************************** | |
339 * Extracts the directory part of a path. | |
340 * | |
341 * This function will search fullname from the end until the | |
342 * first path separator or first character of fullname is | |
343 * reached. Under Windows, the drive letter separator (<i>colon</i>) | |
344 * also terminates the search. | |
345 * | |
346 * Returns: If a path separator was found, all the characters to its | |
347 * left are returned. Otherwise, fullname is returned. | |
348 * | |
349 * Under Windows, the found path separator will be included in the | |
350 * returned string if it is preceeded by a colon. | |
351 * | |
352 * Throws: Nothing. | |
353 * | |
354 * Examples: | |
355 * ----- | |
356 * version(Win32) | |
357 * { | |
358 * getDirName(r"d:\path\foo.bat") => "d:\path" | |
359 * getDirName(getDirName(r"d:\path\foo.bat")) => "d:\" | |
360 * } | |
361 * version(linux) | |
362 * { | |
363 * getDirName("/home/user") => "/home" | |
364 * getDirName(getDirName("/home/user")) => "" | |
365 * } | |
366 * ----- | |
367 */ | |
368 | |
369 char[] getDirName(char[] fullname) | |
370 out (result) | |
371 { | |
372 assert(result.length <= fullname.length); | |
373 } | |
374 body | |
375 { | |
376 size_t i; | |
377 | |
378 for (i = fullname.length; i > 0; i--) | |
379 { | |
380 version(Win32) | |
381 { | |
382 if (fullname[i - 1] == ':') | |
383 break; | |
384 if (fullname[i - 1] == '\\') | |
385 { i--; | |
386 break; | |
387 } | |
388 } | |
389 else version(Unix) | |
390 { | |
391 if (fullname[i - 1] == '/') | |
392 { i--; | |
393 break; | |
394 } | |
395 } | |
396 } | |
397 return fullname[0 .. i]; | |
398 } | |
399 | |
400 | |
401 /******************************** | |
402 * Extracts the drive letter of a path. | |
403 * | |
404 * This function will search fullname for a colon from the beginning. | |
405 * | |
406 * Returns: If a colon is found, all the characters to its left | |
407 * plus the colon are returned. Otherwise, null is returned. | |
408 * | |
409 * Under Linux, this function always returns null immediately. | |
410 * | |
411 * Throws: Nothing. | |
412 * | |
413 * Examples: | |
414 * ----- | |
415 * getDrive(r"d:\path\foo.bat") => "d:" | |
416 * ----- | |
417 */ | |
418 | |
419 char[] getDrive(char[] fullname) | |
420 out (result) | |
421 { | |
422 assert(result.length <= fullname.length); | |
423 } | |
424 body | |
425 { | |
426 version(Win32) | |
427 { | |
428 size_t i; | |
429 | |
430 for (i = 0; i < fullname.length; i++) | |
431 { | |
432 if (fullname[i] == ':') | |
433 return fullname[0 .. i + 1]; | |
434 } | |
435 return null; | |
436 } | |
437 else version(Unix) | |
438 { | |
439 return null; | |
440 } | |
441 } | |
442 | |
443 /**************************** | |
444 * Appends a default extension to a filename. | |
445 * | |
446 * This function first searches filename for an extension and | |
447 * appends ext if there is none. ext should not have any leading | |
448 * dots, one will be inserted between filename and ext if filename | |
449 * doesn't already end with one. | |
450 * | |
451 * Returns: filename if it contains an extension, otherwise filename | |
452 * + ext. | |
453 * | |
454 * Throws: Nothing. | |
455 * | |
456 * Examples: | |
457 * ----- | |
458 * defaultExt("foo.txt", "raw") => "foo.txt" | |
459 * defaultExt("foo.", "raw") => "foo.raw" | |
460 * defaultExt("bar", "raw") => "bar.raw" | |
461 * ----- | |
462 */ | |
463 | |
464 char[] defaultExt(char[] filename, char[] ext) | |
465 { | |
466 char[] existing; | |
467 | |
468 existing = getExt(filename); | |
469 if (existing.length == 0) | |
470 { | |
471 // Check for filename ending in '.' | |
472 if (filename.length && filename[filename.length - 1] == '.') | |
473 filename ~= ext; | |
474 else | |
475 filename = filename ~ "." ~ ext; | |
476 } | |
477 return filename; | |
478 } | |
479 | |
480 | |
481 /**************************** | |
482 * Adds or replaces an extension to a filename. | |
483 * | |
484 * This function first searches filename for an extension and | |
485 * replaces it with ext if found. If there is no extension, ext | |
486 * will be appended. ext should not have any leading dots, one will | |
487 * be inserted between filename and ext if filename doesn't already | |
488 * end with one. | |
489 * | |
490 * Returns: filename + ext if filename is extensionless. Otherwise | |
491 * strips filename's extension off, appends ext and returns the | |
492 * result. | |
493 * | |
494 * Throws: Nothing. | |
495 * | |
496 * Examples: | |
497 * ----- | |
498 * addExt("foo.txt", "raw") => "foo.raw" | |
499 * addExt("foo.", "raw") => "foo.raw" | |
500 * addExt("bar", "raw") => "bar.raw" | |
501 * ----- | |
502 */ | |
503 | |
504 char[] addExt(char[] filename, char[] ext) | |
505 { | |
506 char[] existing; | |
507 | |
508 existing = getExt(filename); | |
509 if (existing.length == 0) | |
510 { | |
511 // Check for filename ending in '.' | |
512 if (filename.length && filename[filename.length - 1] == '.') | |
513 filename ~= ext; | |
514 else | |
515 filename = filename ~ "." ~ ext; | |
516 } | |
517 else | |
518 { | |
519 filename = filename[0 .. filename.length - existing.length] ~ ext; | |
520 } | |
521 return filename; | |
522 } | |
523 | |
524 | |
525 /************************************* | |
526 * Checks if path is absolute. | |
527 * | |
528 * Returns: non-zero if the path starts from the root directory (Linux) or | |
529 * drive letter and root directory (Windows), | |
530 * zero otherwise. | |
531 * | |
532 * Throws: Nothing. | |
533 * | |
534 * Examples: | |
535 * ----- | |
536 * version(Win32) | |
537 * { | |
538 * isabs(r"relative\path") => 0 | |
539 * isabs(r"\relative\path") => 0 | |
540 * isabs(r"d:\absolute") => 1 | |
541 * } | |
542 * version(linux) | |
543 * { | |
544 * isabs("/home/user") => 1 | |
545 * isabs("foo") => 0 | |
546 * } | |
547 * ----- | |
548 */ | |
549 | |
550 int isabs(char[] path) | |
551 { | |
552 char[] d = getDrive(path); | |
553 | |
554 version (Windows) | |
555 { | |
556 return d.length && d.length < path.length && path[d.length] == sep[0]; | |
557 } | |
558 else | |
559 return d.length < path.length && path[d.length] == sep[0]; | |
560 } | |
561 | |
562 unittest | |
563 { | |
564 debug(path) printf("path.isabs.unittest\n"); | |
565 | |
566 version (Windows) | |
567 { | |
568 assert(isabs(r"relative\path") == 0); | |
569 assert(isabs(r"\relative\path") == 0); | |
570 assert(isabs(r"d:\absolute") == 1); | |
571 } | |
572 version (linux) | |
573 { | |
574 assert(isabs("/home/user") == 1); | |
575 assert(isabs("foo") == 0); | |
576 } | |
577 } | |
578 | |
579 /************************************* | |
580 * Joins two path components. | |
581 * | |
582 * If p1 doesn't have a trailing path separator, one will be appended | |
583 * to it before concatting p2. | |
584 * | |
585 * Returns: p1 ~ p2. However, if p2 is an absolute path, only p2 | |
586 * will be returned. | |
587 * | |
588 * Throws: Nothing. | |
589 * | |
590 * Examples: | |
591 * ----- | |
592 * version(Win32) | |
593 * { | |
594 * join(r"c:\foo", "bar") => "c:\foo\bar" | |
595 * join("foo", r"d:\bar") => "d:\bar" | |
596 * } | |
597 * version(linux) | |
598 * { | |
599 * join("/foo/", "bar") => "/foo/bar" | |
600 * join("/foo", "/bar") => "/bar" | |
601 * } | |
602 * ----- | |
603 */ | |
604 | |
605 char[] join(char[] p1, char[] p2) | |
606 { | |
607 if (!p2.length) | |
608 return p1; | |
609 if (!p1.length) | |
610 return p2; | |
611 | |
612 char[] p; | |
613 char[] d1; | |
614 | |
615 version(Win32) | |
616 { | |
617 if (getDrive(p2)) | |
618 { | |
619 p = p2; | |
620 } | |
621 else | |
622 { | |
623 d1 = getDrive(p1); | |
624 if (p1.length == d1.length) | |
625 { | |
626 p = p1 ~ p2; | |
627 } | |
628 else if (p2[0] == '\\') | |
629 { | |
630 if (d1.length == 0) | |
631 p = p2; | |
632 else if (p1[p1.length - 1] == '\\') | |
633 p = p1 ~ p2[1 .. p2.length]; | |
634 else | |
635 p = p1 ~ p2; | |
636 } | |
637 else if (p1[p1.length - 1] == '\\') | |
638 { | |
639 p = p1 ~ p2; | |
640 } | |
641 else | |
642 { | |
643 p = p1 ~ sep ~ p2; | |
644 } | |
645 } | |
646 } | |
647 else version(Unix) | |
648 { | |
649 if (p2[0] == sep[0]) | |
650 { | |
651 p = p2; | |
652 } | |
653 else if (p1[p1.length - 1] == sep[0]) | |
654 { | |
655 p = p1 ~ p2; | |
656 } | |
657 else | |
658 { | |
659 p = p1 ~ sep ~ p2; | |
660 } | |
661 } | |
662 return p; | |
663 } | |
664 | |
665 unittest | |
666 { | |
667 debug(path) printf("path.join.unittest\n"); | |
668 | |
669 char[] p; | |
670 int i; | |
671 | |
672 p = join("foo", "bar"); | |
673 version (Win32) | |
674 i = cmp(p, "foo\\bar"); | |
675 version (Unix) | |
676 i = cmp(p, "foo/bar"); | |
677 assert(i == 0); | |
678 | |
679 version (Win32) | |
680 { p = join("foo\\", "bar"); | |
681 i = cmp(p, "foo\\bar"); | |
682 } | |
683 version (Unix) | |
684 { p = join("foo/", "bar"); | |
685 i = cmp(p, "foo/bar"); | |
686 } | |
687 assert(i == 0); | |
688 | |
689 version (Win32) | |
690 { p = join("foo", "\\bar"); | |
691 i = cmp(p, "\\bar"); | |
692 } | |
693 version (Unix) | |
694 { p = join("foo", "/bar"); | |
695 i = cmp(p, "/bar"); | |
696 } | |
697 assert(i == 0); | |
698 | |
699 version (Win32) | |
700 { p = join("foo\\", "\\bar"); | |
701 i = cmp(p, "\\bar"); | |
702 } | |
703 version (Unix) | |
704 { p = join("foo/", "/bar"); | |
705 i = cmp(p, "/bar"); | |
706 } | |
707 assert(i == 0); | |
708 | |
709 version(Win32) | |
710 { | |
711 p = join("d:", "bar"); | |
712 i = cmp(p, "d:bar"); | |
713 assert(i == 0); | |
714 | |
715 p = join("d:\\", "bar"); | |
716 i = cmp(p, "d:\\bar"); | |
717 assert(i == 0); | |
718 | |
719 p = join("d:\\", "\\bar"); | |
720 i = cmp(p, "d:\\bar"); | |
721 assert(i == 0); | |
722 | |
723 p = join("d:\\foo", "bar"); | |
724 i = cmp(p, "d:\\foo\\bar"); | |
725 assert(i == 0); | |
726 | |
727 p = join("d:", "\\bar"); | |
728 i = cmp(p, "d:\\bar"); | |
729 assert(i == 0); | |
730 | |
731 p = join("foo", "d:"); | |
732 i = cmp(p, "d:"); | |
733 assert(i == 0); | |
734 | |
735 p = join("foo", "d:\\"); | |
736 i = cmp(p, "d:\\"); | |
737 assert(i == 0); | |
738 | |
739 p = join("foo", "d:\\bar"); | |
740 i = cmp(p, "d:\\bar"); | |
741 assert(i == 0); | |
742 } | |
743 } | |
744 | |
745 | |
746 /********************************* | |
747 * Matches filename characters. | |
748 * | |
749 * Under Windows, the comparison is done ignoring case. Under Linux | |
750 * an exact match is performed. | |
751 * | |
752 * Returns: non zero if c1 matches c2, zero otherwise. | |
753 * | |
754 * Throws: Nothing. | |
755 * | |
756 * Examples: | |
757 * ----- | |
758 * version(Win32) | |
759 * { | |
760 * fncharmatch('a', 'b') => 0 | |
761 * fncharmatch('A', 'a') => 1 | |
762 * } | |
763 * version(linux) | |
764 * { | |
765 * fncharmatch('a', 'b') => 0 | |
766 * fncharmatch('A', 'a') => 0 | |
767 * } | |
768 * ----- | |
769 */ | |
770 | |
771 int fncharmatch(dchar c1, dchar c2) | |
772 { | |
773 version (Win32) | |
774 { | |
775 if (c1 != c2) | |
776 { | |
777 if ('A' <= c1 && c1 <= 'Z') | |
778 c1 += cast(char)'a' - 'A'; | |
779 if ('A' <= c2 && c2 <= 'Z') | |
780 c2 += cast(char)'a' - 'A'; | |
781 return c1 == c2; | |
782 } | |
783 return true; | |
784 } | |
785 else version (Unix) | |
786 { | |
787 return c1 == c2; | |
788 } | |
789 /* this is filesystem-dependent, figure out the filesystem? | |
790 else version (GNU) | |
791 { | |
792 // %% figure out filesystem? | |
793 return c1 == c2; | |
794 } | |
795 */ | |
796 } | |
797 | |
798 /************************************ | |
799 * Matches a pattern against a filename. | |
800 * | |
801 * Some characters of pattern have special a meaning (they are | |
802 * <i>meta-characters</i>) and <b>can't</b> be escaped. These are: | |
803 * <p><table> | |
804 * <tr><td><b>*</b></td> | |
805 * <td>Matches 0 or more instances of any character.</td></tr> | |
806 * <tr><td><b>?</b></td> | |
807 * <td>Matches exactly one instances of any character.</td></tr> | |
808 * <tr><td><b>[</b><i>chars</i><b>]</b></td> | |
809 * <td>Matches one instance of any character that appears | |
810 * between the brackets.</td></tr> | |
811 * <tr><td><b>[!</b><i>chars</i><b>]</b></td> | |
812 * <td>Matches one instance of any character that does not appear | |
813 * between the brackets after the exclamation mark.</td></tr> | |
814 * </table><p> | |
815 * Internally individual character comparisons are done calling | |
816 * fncharmatch(), so its rules apply here too. Note that path | |
817 * separators and dots don't stop a meta-character from matching | |
818 * further portions of the filename. | |
819 * | |
820 * Returns: non zero if pattern matches filename, zero otherwise. | |
821 * | |
822 * See_Also: fncharmatch(). | |
823 * | |
824 * Throws: Nothing. | |
825 * | |
826 * Examples: | |
827 * ----- | |
828 * version(Win32) | |
829 * { | |
830 * fnmatch("foo.bar", "*") => 1 | |
831 * fnmatch(r"foo/foo\bar", "f*b*r") => 1 | |
832 * fnmatch("foo.bar", "f?bar") => 0 | |
833 * fnmatch("Goo.bar", "[fg]???bar") => 1 | |
834 * fnmatch(r"d:\foo\bar", "d*foo?bar") => 1 | |
835 * } | |
836 * version(linux) | |
837 * { | |
838 * fnmatch("Go*.bar", "[fg]???bar") => 0 | |
839 * fnmatch("/foo*home/bar", "?foo*bar") => 1 | |
840 * fnmatch("foobar", "foo?bar") => 1 | |
841 * } | |
842 * ----- | |
843 */ | |
844 | |
845 int fnmatch(char[] filename, char[] pattern) | |
846 in | |
847 { | |
848 // Verify that pattern[] is valid | |
849 size_t i; | |
850 int inbracket = false; | |
851 | |
852 for (i = 0; i < pattern.length; i++) | |
853 { | |
854 switch (pattern[i]) | |
855 { | |
856 case '[': | |
857 assert(!inbracket); | |
858 inbracket = true; | |
859 break; | |
860 | |
861 case ']': | |
862 assert(inbracket); | |
863 inbracket = false; | |
864 break; | |
865 | |
866 default: | |
867 break; | |
868 } | |
869 } | |
870 } | |
871 body | |
872 { | |
873 size_t pi; | |
874 size_t ni; | |
875 char pc; | |
876 char nc; | |
877 size_t j; | |
878 int not; | |
879 int anymatch; | |
880 | |
881 ni = 0; | |
882 for (pi = 0; pi < pattern.length; pi++) | |
883 { | |
884 pc = pattern[pi]; | |
885 switch (pc) | |
886 { | |
887 case '*': | |
888 if (pi + 1 == pattern.length) | |
889 goto match; | |
890 for (j = ni; j < filename.length; j++) | |
891 { | |
892 if (fnmatch(filename[j .. filename.length], pattern[pi + 1 .. pattern.length])) | |
893 goto match; | |
894 } | |
895 goto nomatch; | |
896 | |
897 case '?': | |
898 if (ni == filename.length) | |
899 goto nomatch; | |
900 ni++; | |
901 break; | |
902 | |
903 case '[': | |
904 if (ni == filename.length) | |
905 goto nomatch; | |
906 nc = filename[ni]; | |
907 ni++; | |
908 not = 0; | |
909 pi++; | |
910 if (pattern[pi] == '!') | |
911 { not = 1; | |
912 pi++; | |
913 } | |
914 anymatch = 0; | |
915 while (1) | |
916 { | |
917 pc = pattern[pi]; | |
918 if (pc == ']') | |
919 break; | |
920 if (!anymatch && fncharmatch(nc, pc)) | |
921 anymatch = 1; | |
922 pi++; | |
923 } | |
924 if (!(anymatch ^ not)) | |
925 goto nomatch; | |
926 break; | |
927 | |
928 default: | |
929 if (ni == filename.length) | |
930 goto nomatch; | |
931 nc = filename[ni]; | |
932 if (!fncharmatch(pc, nc)) | |
933 goto nomatch; | |
934 ni++; | |
935 break; | |
936 } | |
937 } | |
938 if (ni < filename.length) | |
939 goto nomatch; | |
940 | |
941 match: | |
942 return true; | |
943 | |
944 nomatch: | |
945 return false; | |
946 } | |
947 | |
948 unittest | |
949 { | |
950 debug(path) printf("path.fnmatch.unittest\n"); | |
951 | |
952 version (Win32) | |
953 assert(fnmatch("foo", "Foo")); | |
954 version (Unix) | |
955 assert(!fnmatch("foo", "Foo")); | |
956 assert(fnmatch("foo", "*")); | |
957 assert(fnmatch("foo.bar", "*")); | |
958 assert(fnmatch("foo.bar", "*.*")); | |
959 assert(fnmatch("foo.bar", "foo*")); | |
960 assert(fnmatch("foo.bar", "f*bar")); | |
961 assert(fnmatch("foo.bar", "f*b*r")); | |
962 assert(fnmatch("foo.bar", "f???bar")); | |
963 assert(fnmatch("foo.bar", "[fg]???bar")); | |
964 assert(fnmatch("foo.bar", "[!gh]*bar")); | |
965 | |
966 assert(!fnmatch("foo", "bar")); | |
967 assert(!fnmatch("foo", "*.*")); | |
968 assert(!fnmatch("foo.bar", "f*baz")); | |
969 assert(!fnmatch("foo.bar", "f*b*x")); | |
970 assert(!fnmatch("foo.bar", "[gh]???bar")); | |
971 assert(!fnmatch("foo.bar", "[!fg]*bar")); | |
972 assert(!fnmatch("foo.bar", "[fg]???baz")); | |
973 } | |
974 | |
975 /** | |
976 * Performs tilde expansion in paths. | |
977 * | |
978 * There are two ways of using tilde expansion in a path. One | |
979 * involves using the tilde alone or followed by a path separator. In | |
980 * this case, the tilde will be expanded with the value of the | |
981 * environment variable <i>HOME</i>. The second way is putting | |
982 * a username after the tilde (i.e. <tt>~john/Mail</tt>). Here, | |
983 * the username will be searched for in the user database | |
984 * (i.e. <tt>/etc/passwd</tt> on Unix systems) and will expand to | |
985 * whatever path is stored there. The username is considered the | |
986 * string after the tilde ending at the first instance of a path | |
987 * separator. | |
988 * | |
989 * Note that using the <i>~user</i> syntax may give different | |
990 * values from just <i>~</i> if the environment variable doesn't | |
991 * match the value stored in the user database. | |
992 * | |
993 * When the environment variable version is used, the path won't | |
994 * be modified if the environment variable doesn't exist or it | |
995 * is empty. When the database version is used, the path won't be | |
996 * modified if the user doesn't exist in the database or there is | |
997 * not enough memory to perform the query. | |
998 * | |
999 * Returns: inputPath with the tilde expanded, or just inputPath | |
1000 * if it could not be expanded. | |
1001 * For Windows, expandTilde() merely returns its argument inputPath. | |
1002 * | |
1003 * Throws: std.outofmemory.OutOfMemoryException if there is not enough | |
1004 * memory to perform | |
1005 * the database lookup for the <i>~user</i> syntax. | |
1006 * | |
1007 * Examples: | |
1008 * ----- | |
1009 * import std.path; | |
1010 * | |
1011 * void process_file(char[] filename) | |
1012 * { | |
1013 * char[] path = expandTilde(filename); | |
1014 * ... | |
1015 * } | |
1016 * ----- | |
1017 * | |
1018 * ----- | |
1019 * import std.path; | |
1020 * | |
1021 * const char[] RESOURCE_DIR_TEMPLATE = "~/.applicationrc"; | |
1022 * char[] RESOURCE_DIR; // This gets expanded in main(). | |
1023 * | |
1024 * int main(char[][] args) | |
1025 * { | |
1026 * RESOURCE_DIR = expandTilde(RESOURCE_DIR_TEMPLATE); | |
1027 * ... | |
1028 * } | |
1029 * ----- | |
1030 * Version: Available since v0.143. | |
1031 * Authors: Grzegorz Adam Hankiewicz, Thomas Kühne. | |
1032 */ | |
1033 | |
1034 char[] expandTilde(char[] inputPath) | |
1035 { | |
1036 version(Unix) | |
1037 { | |
1038 static assert(sep.length == 1); | |
1039 | |
1040 // Return early if there is no tilde in path. | |
1041 if (inputPath.length < 1 || inputPath[0] != '~') | |
1042 return inputPath; | |
1043 | |
1044 if (inputPath.length == 1 || inputPath[1] == sep[0]) | |
1045 return expandFromEnvironment(inputPath); | |
1046 else | |
1047 return expandFromDatabase(inputPath); | |
1048 } | |
1049 else version(Windows) | |
1050 { | |
1051 // Put here real windows implementation. | |
1052 return inputPath; | |
1053 } | |
1054 else | |
1055 { | |
1056 static assert(0); // Guard. Implement on other platforms. | |
1057 } | |
1058 } | |
1059 | |
1060 | |
1061 unittest | |
1062 { | |
1063 debug(path) printf("path.expandTilde.unittest\n"); | |
1064 | |
1065 version (Unix) | |
1066 { | |
1067 // Retrieve the current home variable. | |
1068 char* c_home = getenv("HOME"); | |
1069 | |
1070 // Testing when there is no environment variable. | |
1071 unsetenv("HOME"); | |
1072 assert(expandTilde("~/") == "~/"); | |
1073 assert(expandTilde("~") == "~"); | |
1074 | |
1075 // Testing when an environment variable is set. | |
1076 int ret = setenv("HOME", "dmd/test\0", 1); | |
1077 assert(ret == 0); | |
1078 assert(expandTilde("~/") == "dmd/test/"); | |
1079 assert(expandTilde("~") == "dmd/test"); | |
1080 | |
1081 // The same, but with a variable ending in a slash. | |
1082 ret = setenv("HOME", "dmd/test/\0", 1); | |
1083 assert(ret == 0); | |
1084 assert(expandTilde("~/") == "dmd/test/"); | |
1085 assert(expandTilde("~") == "dmd/test"); | |
1086 | |
1087 // Recover original HOME variable before continuing. | |
1088 if (c_home) | |
1089 setenv("HOME", c_home, 1); | |
1090 else | |
1091 unsetenv("HOME"); | |
1092 | |
1093 // Test user expansion for root. Are there unices without /root? | |
1094 /* | |
1095 assert(expandTilde("~root") == "/root"); | |
1096 assert(expandTilde("~root/") == "/root/"); | |
1097 */ | |
1098 assert(expandTilde("~Idontexist/hey") == "~Idontexist/hey"); | |
1099 } | |
1100 } | |
1101 | |
1102 version (Unix) | |
1103 { | |
1104 | |
1105 /** | |
1106 * Replaces the tilde from path with the environment variable HOME. | |
1107 */ | |
1108 private char[] expandFromEnvironment(char[] path) | |
1109 { | |
1110 assert(path.length >= 1); | |
1111 assert(path[0] == '~'); | |
1112 | |
1113 // Get HOME and use that to replace the tilde. | |
1114 char* home = getenv("HOME"); | |
1115 if (home == null) | |
1116 return path; | |
1117 | |
1118 return combineCPathWithDPath(home, path, 1); | |
1119 } | |
1120 | |
1121 | |
1122 /** | |
1123 * Joins a path from a C string to the remainder of path. | |
1124 * | |
1125 * The last path separator from c_path is discarded. The result | |
1126 * is joined to path[char_pos .. length] if char_pos is smaller | |
1127 * than length, otherwise path is not appended to c_path. | |
1128 */ | |
1129 private char[] combineCPathWithDPath(char* c_path, char[] path, int char_pos) | |
1130 { | |
1131 assert(c_path != null); | |
1132 assert(path.length > 0); | |
1133 assert(char_pos >= 0); | |
1134 | |
1135 // Search end of C string | |
1136 size_t end = std.string.strlen(c_path); | |
1137 | |
1138 // Remove trailing path separator, if any | |
1139 if (end && c_path[end - 1] == sep[0]) | |
1140 end--; | |
1141 | |
1142 // Create our own copy, as lifetime of c_path is undocumented | |
1143 char[] cp = c_path[0 .. end].dup; | |
1144 | |
1145 // Do we append something from path? | |
1146 if (char_pos < path.length) | |
1147 cp ~= path[char_pos .. length]; | |
1148 | |
1149 return cp; | |
1150 } | |
1151 | |
1152 | |
1153 /** | |
1154 * Replaces the tilde from path with the path from the user database. | |
1155 */ | |
1156 private string expandFromDatabase(string path) | |
1157 { | |
1158 assert(path.length > 2 || (path.length == 2 && path[1] != sep[0])); | |
1159 assert(path[0] == '~'); | |
1160 | |
1161 // Extract username, searching for path separator. | |
1162 string username; | |
1163 ptrdiff_t last_char = find(path, sep[0]); | |
1164 | |
1165 if (last_char == -1) | |
1166 { | |
1167 username = path[1 .. length] ~ '\0'; | |
1168 last_char = username.length + 1; | |
1169 } | |
1170 else | |
1171 { | |
1172 username = path[1 .. last_char] ~ '\0'; | |
1173 } | |
1174 assert(last_char > 1); | |
1175 | |
1176 version (GNU_Unix_Have_getpwnam_r) | |
1177 { | |
1178 | |
1179 // Reserve C memory for the getpwnam_r() function. | |
1180 passwd result; | |
1181 int extra_memory_size = 5 * 1024; | |
1182 void* extra_memory; | |
1183 | |
1184 while (1) | |
1185 { | |
1186 extra_memory = std.c.stdlib.malloc(extra_memory_size); | |
1187 if (extra_memory == null) | |
1188 goto Lerror; | |
1189 | |
1190 // Obtain info from database. | |
1191 passwd *verify; | |
1192 std.c.stdlib.setErrno(0); | |
1193 if (getpwnam_r(username.ptr, &result, cast(char*) extra_memory, extra_memory_size, | |
1194 &verify) == 0) | |
1195 { | |
1196 // Failure if verify doesn't point at result. | |
1197 if (verify != &result) | |
1198 // username is not found, so return path[] | |
1199 goto Lnotfound; | |
1200 break; | |
1201 } | |
1202 | |
1203 switch (std.c.stdlib.getErrno()) { | |
1204 case 0: | |
1205 case ENOENT: | |
1206 case ESRCH: | |
1207 case EBADF: | |
1208 case EPERM: | |
1209 goto Lnotfound; | |
1210 case ERANGE: | |
1211 break; | |
1212 default: | |
1213 // not just out of memory: EMFILE, ENFILE too | |
1214 goto Lerror; | |
1215 } | |
1216 | |
1217 // extra_memory isn't large enough | |
1218 std.c.stdlib.free(extra_memory); | |
1219 extra_memory_size *= 2; | |
1220 } | |
1221 | |
1222 path = combineCPathWithDPath(result.pw_dir, path, last_char); | |
1223 | |
1224 Lnotfound: | |
1225 std.c.stdlib.free(extra_memory); | |
1226 return path; | |
1227 | |
1228 Lerror: | |
1229 // Errors are going to be caused by running out of memory | |
1230 if (extra_memory) | |
1231 std.c.stdlib.free(extra_memory); | |
1232 _d_OutOfMemory(); | |
1233 return null; | |
1234 | |
1235 } | |
1236 else | |
1237 { | |
1238 passwd * result; | |
1239 | |
1240 /* This does not guarantee another thread will not | |
1241 use getpwnam at the same time */ | |
1242 synchronized { | |
1243 result = getpwnam(username.ptr); | |
1244 } | |
1245 | |
1246 if (result) | |
1247 path = combineCPathWithDPath(result.pw_dir, path, last_char); | |
1248 return path; | |
1249 } | |
1250 } | |
1251 | |
1252 } |