1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module iv
.vfs
.posixci
/*is aliced*/;
22 static if (VFS_NORMAL_OS
) {
24 import core
.sys
.posix
.dirent
;
28 // `name` will be modified
29 package bool findFileCI (const(char)[] path
, char[] name
, bool asDir
) nothrow @nogc {
30 import core
.sys
.posix
.dirent
;
31 import core
.stdc
.stdlib
: alloca
;
32 if (path
.length
== 0) path
= ".";
33 while (path
.length
&& path
[$-1] == '/') path
= path
[0..$-1];
34 if (path
.length
== 0) path
= "/";
35 if (path
.length
+name
.length
> 4094) return false;
36 char* tmpbuf
= cast(char*)alloca(path
.length
+name
.length
+2);
37 tmpbuf
[0..path
.length
] = path
[];
38 // check if we have perfect match
40 import core
.sys
.posix
.sys
.stat
;
41 tmpbuf
[path
.length
] = '/';
42 tmpbuf
[path
.length
+1..path
.length
+1+name
.length
] = name
[];
43 tmpbuf
[path
.length
+1+name
.length
] = '\0';
45 auto res
= stat(tmpbuf
, &st
);
48 if ((asDir
&& (st
.st_mode
&(S_IFDIR|S_IFLNK
)) != 0) ||
(!asDir
&& (st
.st_mode
&(S_IFREG|S_IFLNK
)) != 0)) {
53 // vanilla dmd has bug in dirent definition: it's not aligned
54 /*static if (dirent.d_name.offsetof == 19)*/ {
55 tmpbuf
[path
.length
] = '\0';
56 auto dir
= opendir(tmpbuf
);
57 if (dir
is null) return false;
58 scope(exit
) closedir(dir
);
60 auto de = readdir(dir
);
61 if (de is null) break;
62 //{ import core.stdc.stdio : printf; printf("[%s]\n", de.d_name.ptr); }
65 while (pos
< de.d_name
.length
&& de.d_name
.ptr
[pos
]) ++pos
;
66 dename
= de.d_name
[0..pos
];
67 if (dename
!= "." && dename
!= ".." && koi8StrCaseEqu(name
, dename
)) {
68 // i found her... maybe
69 import core
.sys
.posix
.sys
.stat
;
70 tmpbuf
[path
.length
] = '/';
71 tmpbuf
[path
.length
+1..path
.length
+1+dename
.length
] = dename
[];
72 tmpbuf
[path
.length
+1+dename
.length
] = '\0';
74 auto res
= stat(tmpbuf
, &st
);
77 if ((asDir
&& (st
.st_mode
&(S_IFDIR|S_IFLNK
)) != 0) ||
(!asDir
&& (st
.st_mode
&(S_IFREG|S_IFLNK
)) != 0)) {
89 // `path` will be modified; returns new path (slice of `path`) or `null` (and `path` is not modified)
90 package char[] dirNormalize (char[] path
) nothrow @nogc {
93 if (path
.length
&& path
[0] == '/') tmpbuf
.ptr
[tpos
++] = '/';
94 while (ppos
< path
.length
) {
95 if (path
.ptr
[ppos
] == '/') {
96 while (ppos
< path
.length
&& path
.ptr
[ppos
] == '/') ++ppos
;
100 while (pend
< path
.length
&& path
.ptr
[pend
] != '/') ++pend
;
102 if (pend
< path
.length
) ++pend
;
103 const(char)[] nm
= path
[ppos
..pend
];
104 if (nm
== "../" || nm
== "..") {
105 if (tpos
== 0) return null;
106 --tpos
; // skip slash
107 while (tpos
> 0 && tmpbuf
.ptr
[tpos
-1] != '/') --tpos
;
108 //if (tpos == 0) return null;
109 } else if (nm
!= "./" && nm
!= ".") {
110 if (tpos
+nm
.length
>= tmpbuf
.length
) return null;
111 tmpbuf
[tpos
..tpos
+nm
.length
] = nm
[];
116 if (tpos
== 0) return null;
117 if (tpos
> 1 && tmpbuf
.ptr
[tpos
-1] == '/') --tpos
;
118 path
[0..tpos
] = tmpbuf
[0..tpos
];
119 return path
[0..tpos
];
123 // `asDir`: should last element be threated as directory?
124 // `path` will be modified; returns new path (slice of `path`) or `null`
125 package char[] findPathCI (char[] path
, bool asDir
=false) nothrow @nogc {
126 path
= dirNormalize(path
);
127 if (path
is null) return null;
128 if (path
.length
== 0) return null;
130 while (ppos
< path
.length
) {
132 if (ppos
== 0 && path
[0] == '/') {
137 while (pend
< path
.length
&& path
.ptr
[pend
] != '/') ++pend
;
139 if (!findFileCI(path
[0..ppos
], path
[ppos
..pend
], (pend
< path
.length ?
true : asDir
))) return null;
152 //char[] path = "//foo/bar////./z/./../../..".dup;
153 char[] path = "//foo/bar////./z/./././..".dup;
154 writeln(findPathCI(path));
157 char[] name = "PosixCI.D".dup;
158 writeln(findPathCI(name));
161 char[] name = "SaMpLES".dup;
162 writeln(findPathCI(name, true));
165 char[] name = "SaMpLES".dup;
166 writeln(findPathCI(name, true));
169 char[] name = "./SaMpLES/sAMPle00.d".dup;
170 writeln(findPathCI(name));
173 char[] name = "./SaMpLES/../sAMPle00.d/../PosixCI.d".dup;
174 writeln(findPathCI(name));
177 char[] name = "posixci.d".dup;
178 writeln(findPathCI(name));