8 #include <mono/utils/mono-io-portability.h>
9 #include <mono/metadata/metadata.h>
10 #include <mono/metadata/class.h>
11 #include <mono/metadata/class-internals.h>
12 #include <mono/metadata/object.h>
13 #include <mono/utils/mono-hash.h>
14 #include <mono/metadata/gc-internal.h>
15 #include <mono/metadata/profiler.h>
16 #include <mono/metadata/profiler-private.h>
18 #ifdef DISABLE_PORTABILITY
19 int __mono_io_portability_helpers
= PORTABILITY_NONE
;
22 mono_portability_helpers_init (void)
27 mono_portability_find_file (const gchar
*pathname
, gboolean last_exists
)
29 g_assert_not_reached();
37 int __mono_io_portability_helpers
= PORTABILITY_UNKNOWN
;
39 static inline gchar
*mono_portability_find_file_internal (GString
**report
, const gchar
*pathname
, gboolean last_exists
);
41 void mono_portability_helpers_init (void)
45 if (__mono_io_portability_helpers
!= PORTABILITY_UNKNOWN
)
48 __mono_io_portability_helpers
= PORTABILITY_NONE
;
50 env
= g_getenv ("MONO_IOMAP");
52 /* parse the environment setting and set up some vars
55 gchar
**options
= g_strsplit (env
, ":", 0);
58 if (options
== NULL
) {
59 /* This shouldn't happen */
63 for (i
= 0; options
[i
] != NULL
; i
++) {
65 g_message ("%s: Setting option [%s]", __func__
,
68 if (!strncasecmp (options
[i
], "drive", 5)) {
69 __mono_io_portability_helpers
|= PORTABILITY_DRIVE
;
70 } else if (!strncasecmp (options
[i
], "case", 4)) {
71 __mono_io_portability_helpers
|= PORTABILITY_CASE
;
72 } else if (!strncasecmp (options
[i
], "all", 3)) {
73 __mono_io_portability_helpers
|= (PORTABILITY_DRIVE
| PORTABILITY_CASE
);
79 /* Returns newly allocated string, or NULL on failure */
80 static gchar
*find_in_dir (DIR *current
, const gchar
*name
)
85 g_message ("%s: looking for [%s]\n", __func__
, name
);
88 while((entry
= readdir (current
)) != NULL
) {
90 g_message ("%s: found [%s]\n", __func__
, entry
->d_name
);
93 if (!g_ascii_strcasecmp (name
, entry
->d_name
)) {
97 g_message ("%s: matched [%s] to [%s]\n", __func__
,
101 ret
= g_strdup (entry
->d_name
);
108 g_message ("%s: returning NULL\n", __func__
);
116 static inline void append_report (GString
**report
, const gchar
*format
, ...)
118 #if GLIB_CHECK_VERSION(2,14,0)
121 *report
= g_string_new ("");
123 va_start (ap
, format
);
124 g_string_append_vprintf (*report
, format
, ap
);
127 g_assert_not_reached ();
131 static inline void do_mono_profiler_iomap (GString
**report
, const char *pathname
, const char *new_pathname
)
134 GString
*tmp
= report
? *report
: NULL
;
138 rep
= g_string_free (tmp
, FALSE
);
140 g_string_free (tmp
, TRUE
);
144 mono_profiler_iomap (rep
, pathname
, new_pathname
);
148 gchar
*mono_portability_find_file (const gchar
*pathname
, gboolean last_exists
)
150 GString
*report
= NULL
;
151 gchar
*ret
= mono_portability_find_file_internal (&report
, pathname
, last_exists
);
154 g_string_free (report
, TRUE
);
159 /* Returns newly-allocated string or NULL on failure */
160 static inline gchar
*mono_portability_find_file_internal (GString
**report
, const gchar
*pathname
, gboolean last_exists
)
162 gchar
*new_pathname
, **components
, **new_components
;
163 int num_components
= 0, component
= 0;
164 DIR *scanning
= NULL
;
166 gboolean drive_stripped
= FALSE
;
167 gboolean do_report
= (mono_profiler_get_events () & MONO_PROFILE_IOMAP_EVENTS
) != 0;
169 if (IS_PORTABILITY_NONE
) {
174 append_report (report
, " - Requested file path: '%s'\n", pathname
);
176 new_pathname
= g_strdup (pathname
);
179 g_message ("%s: Finding [%s] last_exists: %s\n", __func__
, pathname
,
180 last_exists
?"TRUE":"FALSE");
184 access (new_pathname
, F_OK
) == 0) {
186 g_message ("%s: Found it without doing anything\n", __func__
);
188 return(new_pathname
);
191 /* First turn '\' into '/' and strip any drive letters */
192 g_strdelimit (new_pathname
, "\\", '/');
195 g_message ("%s: Fixed slashes, now have [%s]\n", __func__
,
199 if (IS_PORTABILITY_DRIVE
&&
200 g_ascii_isalpha (new_pathname
[0]) &&
201 (new_pathname
[1] == ':')) {
202 int len
= strlen (new_pathname
);
204 g_memmove (new_pathname
, new_pathname
+2, len
- 2);
205 new_pathname
[len
- 2] = '\0';
208 append_report (report
, " - Stripped drive letter.\n");
209 drive_stripped
= TRUE
;
212 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
213 __func__
, new_pathname
);
217 len
= strlen (new_pathname
);
218 if (len
> 1 && new_pathname
[len
- 1] == '/') {
219 new_pathname
[len
- 1] = 0;
221 g_message ("%s: requested name had a trailing /, rewritten to '%s'\n",
222 __func__
, new_pathname
);
227 access (new_pathname
, F_OK
) == 0) {
229 g_message ("%s: Found it\n", __func__
);
231 if (do_report
&& drive_stripped
)
232 do_mono_profiler_iomap (report
, pathname
, new_pathname
);
234 return(new_pathname
);
237 /* OK, have to work harder. Take each path component in turn
238 * and do a case-insensitive directory scan for it
241 if (!(IS_PORTABILITY_CASE
)) {
242 g_free (new_pathname
);
246 components
= g_strsplit (new_pathname
, "/", 0);
247 if (components
== NULL
) {
248 /* This shouldn't happen */
249 g_free (new_pathname
);
253 while(components
[num_components
] != NULL
) {
256 g_free (new_pathname
);
258 if (num_components
== 0){
263 new_components
= (gchar
**)g_new0 (gchar
**, num_components
+ 1);
265 if (num_components
> 1) {
266 if (strcmp (components
[0], "") == 0) {
267 /* first component blank, so start at / */
268 scanning
= opendir ("/");
269 if (scanning
== NULL
) {
271 g_message ("%s: opendir 1 error: %s", __func__
,
274 g_strfreev (new_components
);
275 g_strfreev (components
);
279 new_components
[component
++] = g_strdup ("");
284 current
= opendir (".");
285 if (current
== NULL
) {
287 g_message ("%s: opendir 2 error: %s", __func__
,
290 g_strfreev (new_components
);
291 g_strfreev (components
);
295 entry
= find_in_dir (current
, components
[0]);
297 g_strfreev (new_components
);
298 g_strfreev (components
);
302 scanning
= opendir (entry
);
303 if (scanning
== NULL
) {
305 g_message ("%s: opendir 3 error: %s", __func__
,
309 g_strfreev (new_components
);
310 g_strfreev (components
);
314 new_components
[component
++] = entry
;
318 if (strcmp (components
[0], "") == 0) {
319 /* First and only component blank */
320 new_components
[component
++] = g_strdup ("");
325 current
= opendir (".");
326 if (current
== NULL
) {
328 g_message ("%s: opendir 4 error: %s",
332 g_strfreev (new_components
);
333 g_strfreev (components
);
337 entry
= find_in_dir (current
, components
[0]);
339 g_strfreev (new_components
);
340 g_strfreev (components
);
344 new_components
[component
++] = entry
;
347 new_components
[component
++] = g_strdup (components
[0]);
352 g_message ("%s: Got first entry: [%s]\n", __func__
, new_components
[0]);
355 g_assert (component
== 1);
357 for(; component
< num_components
; component
++) {
362 component
== num_components
-1) {
363 entry
= g_strdup (components
[component
]);
366 entry
= find_in_dir (scanning
, components
[component
]);
368 g_strfreev (new_components
);
369 g_strfreev (components
);
374 new_components
[component
] = entry
;
376 if (component
< num_components
-1) {
377 path_so_far
= g_strjoinv ("/", new_components
);
379 scanning
= opendir (path_so_far
);
380 g_free (path_so_far
);
381 if (scanning
== NULL
) {
382 g_strfreev (new_components
);
383 g_strfreev (components
);
389 g_strfreev (components
);
391 new_pathname
= g_strjoinv ("/", new_components
);
394 g_message ("%s: pathname [%s] became [%s]\n", __func__
, pathname
,
398 g_strfreev (new_components
);
401 access (new_pathname
, F_OK
) == 0) ||
403 if (do_report
&& strcmp (pathname
, new_pathname
) != 0)
404 do_mono_profiler_iomap (report
, pathname
, new_pathname
);
406 return(new_pathname
);
409 g_free (new_pathname
);