12 #include <mono/utils/mono-io-portability.h>
13 #include <mono/metadata/profiler-private.h>
14 #include <mono/utils/mono-compiler.h>
16 #ifndef DISABLE_PORTABILITY
20 int mono_io_portability_helpers
= PORTABILITY_UNKNOWN
;
22 static inline gchar
*mono_portability_find_file_internal (const gchar
*pathname
, gboolean last_exists
);
24 void mono_portability_helpers_init (void)
28 if (mono_io_portability_helpers
!= PORTABILITY_UNKNOWN
)
31 mono_io_portability_helpers
= PORTABILITY_NONE
;
33 env
= g_getenv ("MONO_IOMAP");
35 /* parse the environment setting and set up some vars
38 gchar
**options
= g_strsplit (env
, ":", 0);
41 if (options
== NULL
) {
42 /* This shouldn't happen */
46 for (i
= 0; options
[i
] != NULL
; i
++) {
48 g_message ("%s: Setting option [%s]", __func__
,
51 if (!strncasecmp (options
[i
], "drive", 5)) {
52 mono_io_portability_helpers
|= PORTABILITY_DRIVE
;
53 } else if (!strncasecmp (options
[i
], "case", 4)) {
54 mono_io_portability_helpers
|= PORTABILITY_CASE
;
55 } else if (!strncasecmp (options
[i
], "all", 3)) {
56 mono_io_portability_helpers
|= (PORTABILITY_DRIVE
| PORTABILITY_CASE
);
63 /* Returns newly allocated string, or NULL on failure */
64 static gchar
*find_in_dir (DIR *current
, const gchar
*name
)
69 g_message ("%s: looking for [%s]\n", __func__
, name
);
72 while((entry
= readdir (current
)) != NULL
) {
74 g_message ("%s: found [%s]\n", __func__
, entry
->d_name
);
77 if (!g_ascii_strcasecmp (name
, entry
->d_name
)) {
81 g_message ("%s: matched [%s] to [%s]\n", __func__
,
85 ret
= g_strdup (entry
->d_name
);
92 g_message ("%s: returning NULL\n", __func__
);
100 gchar
*mono_portability_find_file (const gchar
*pathname
, gboolean last_exists
)
104 if (!pathname
|| !pathname
[0])
106 ret
= mono_portability_find_file_internal (pathname
, last_exists
);
111 /* Returns newly-allocated string or NULL on failure */
112 static inline gchar
*mono_portability_find_file_internal (const gchar
*pathname
, gboolean last_exists
)
114 gchar
*new_pathname
, **components
, **new_components
;
115 int num_components
= 0, component
= 0;
116 DIR *scanning
= NULL
;
119 if (IS_PORTABILITY_NONE
) {
123 new_pathname
= g_strdup (pathname
);
126 g_message ("%s: Finding [%s] last_exists: %s\n", __func__
, pathname
,
127 last_exists
?"TRUE":"FALSE");
131 access (new_pathname
, F_OK
) == 0) {
133 g_message ("%s: Found it without doing anything\n", __func__
);
135 return(new_pathname
);
138 /* First turn '\' into '/' and strip any drive letters */
139 g_strdelimit (new_pathname
, '\\', '/');
142 g_message ("%s: Fixed slashes, now have [%s]\n", __func__
,
146 if (IS_PORTABILITY_DRIVE
&&
147 g_ascii_isalpha (new_pathname
[0]) &&
148 (new_pathname
[1] == ':')) {
149 int len
= strlen (new_pathname
);
151 g_memmove (new_pathname
, new_pathname
+2, len
- 2);
152 new_pathname
[len
- 2] = '\0';
154 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
155 __func__
, new_pathname
);
159 len
= strlen (new_pathname
);
160 if (len
> 1 && new_pathname
[len
- 1] == '/') {
161 new_pathname
[len
- 1] = 0;
163 g_message ("%s: requested name had a trailing /, rewritten to '%s'\n",
164 __func__
, new_pathname
);
169 access (new_pathname
, F_OK
) == 0) {
171 g_message ("%s: Found it\n", __func__
);
174 return(new_pathname
);
177 /* OK, have to work harder. Take each path component in turn
178 * and do a case-insensitive directory scan for it
181 if (!(IS_PORTABILITY_CASE
)) {
182 g_free (new_pathname
);
186 components
= g_strsplit (new_pathname
, "/", 0);
187 if (components
== NULL
) {
188 /* This shouldn't happen */
189 g_free (new_pathname
);
193 while(components
[num_components
] != NULL
) {
196 g_free (new_pathname
);
198 if (num_components
== 0){
203 new_components
= (gchar
**)g_new0 (gchar
**, num_components
+ 1);
205 if (num_components
> 1) {
206 if (strcmp (components
[0], "") == 0) {
207 /* first component blank, so start at / */
208 scanning
= opendir ("/");
209 if (scanning
== NULL
) {
211 g_message ("%s: opendir 1 error: %s", __func__
,
214 g_strfreev (new_components
);
215 g_strfreev (components
);
219 new_components
[component
++] = g_strdup ("");
224 current
= opendir (".");
225 if (current
== NULL
) {
227 g_message ("%s: opendir 2 error: %s", __func__
,
230 g_strfreev (new_components
);
231 g_strfreev (components
);
235 entry
= find_in_dir (current
, components
[0]);
237 g_strfreev (new_components
);
238 g_strfreev (components
);
242 scanning
= opendir (entry
);
243 if (scanning
== NULL
) {
245 g_message ("%s: opendir 3 error: %s", __func__
,
249 g_strfreev (new_components
);
250 g_strfreev (components
);
254 new_components
[component
++] = entry
;
258 if (strcmp (components
[0], "") == 0) {
259 /* First and only component blank */
260 new_components
[component
++] = g_strdup ("");
265 current
= opendir (".");
266 if (current
== NULL
) {
268 g_message ("%s: opendir 4 error: %s",
272 g_strfreev (new_components
);
273 g_strfreev (components
);
277 entry
= find_in_dir (current
, components
[0]);
279 g_strfreev (new_components
);
280 g_strfreev (components
);
284 new_components
[component
++] = entry
;
287 new_components
[component
++] = g_strdup (components
[0]);
292 g_message ("%s: Got first entry: [%s]\n", __func__
, new_components
[0]);
295 g_assert (component
== 1);
297 for(; component
< num_components
; component
++) {
302 component
== num_components
-1) {
303 entry
= g_strdup (components
[component
]);
306 entry
= find_in_dir (scanning
, components
[component
]);
308 g_strfreev (new_components
);
309 g_strfreev (components
);
314 new_components
[component
] = entry
;
316 if (component
< num_components
-1) {
317 path_so_far
= g_strjoinv ("/", new_components
);
319 scanning
= opendir (path_so_far
);
320 g_free (path_so_far
);
321 if (scanning
== NULL
) {
322 g_strfreev (new_components
);
323 g_strfreev (components
);
329 g_strfreev (components
);
331 new_pathname
= g_strjoinv ("/", new_components
);
334 g_message ("%s: pathname [%s] became [%s]\n", __func__
, pathname
,
338 g_strfreev (new_components
);
341 access (new_pathname
, F_OK
) == 0) ||
343 return(new_pathname
);
346 g_free (new_pathname
);
350 #else /* DISABLE_PORTABILITY */
352 MONO_EMPTY_SOURCE_FILE (mono_io_portability
);
354 #endif /* DISABLE_PORTABILITY */