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/metadata/gc-internal.h>
14 #include <mono/metadata/profiler.h>
15 #include <mono/metadata/profiler-private.h>
17 #ifdef DISABLE_PORTABILITY
18 int __mono_io_portability_helpers = PORTABILITY_NONE;
21 mono_portability_helpers_init (void)
26 mono_portability_find_file (const gchar *pathname, gboolean last_exists)
28 g_assert_not_reached();
36 int __mono_io_portability_helpers = PORTABILITY_UNKNOWN;
38 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists);
40 void mono_portability_helpers_init (void)
44 if (__mono_io_portability_helpers != PORTABILITY_UNKNOWN)
47 __mono_io_portability_helpers = PORTABILITY_NONE;
49 env = g_getenv ("MONO_IOMAP");
51 /* parse the environment setting and set up some vars
54 gchar **options = g_strsplit (env, ":", 0);
57 if (options == NULL) {
58 /* This shouldn't happen */
62 for (i = 0; options[i] != NULL; i++) {
64 g_message ("%s: Setting option [%s]", __func__,
67 if (!strncasecmp (options[i], "drive", 5)) {
68 __mono_io_portability_helpers |= PORTABILITY_DRIVE;
69 } else if (!strncasecmp (options[i], "case", 4)) {
70 __mono_io_portability_helpers |= PORTABILITY_CASE;
71 } else if (!strncasecmp (options[i], "all", 3)) {
72 __mono_io_portability_helpers |= (PORTABILITY_DRIVE | PORTABILITY_CASE);
78 /* Returns newly allocated string, or NULL on failure */
79 static gchar *find_in_dir (DIR *current, const gchar *name)
84 g_message ("%s: looking for [%s]\n", __func__, name);
87 while((entry = readdir (current)) != NULL) {
89 g_message ("%s: found [%s]\n", __func__, entry->d_name);
92 if (!g_ascii_strcasecmp (name, entry->d_name)) {
96 g_message ("%s: matched [%s] to [%s]\n", __func__,
100 ret = g_strdup (entry->d_name);
107 g_message ("%s: returning NULL\n", __func__);
115 static inline void append_report (GString **report, const gchar *format, ...)
117 #if GLIB_CHECK_VERSION(2,14,0)
120 *report = g_string_new ("");
122 va_start (ap, format);
123 g_string_append_vprintf (*report, format, ap);
126 g_assert_not_reached ();
130 static inline void do_mono_profiler_iomap (GString **report, const char *pathname, const char *new_pathname)
133 GString *tmp = report ? *report : NULL;
137 rep = g_string_free (tmp, FALSE);
139 g_string_free (tmp, TRUE);
143 mono_profiler_iomap (rep, pathname, new_pathname);
147 gchar *mono_portability_find_file (const gchar *pathname, gboolean last_exists)
149 GString *report = NULL;
150 gchar *ret = mono_portability_find_file_internal (&report, pathname, last_exists);
153 g_string_free (report, TRUE);
158 /* Returns newly-allocated string or NULL on failure */
159 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists)
161 gchar *new_pathname, **components, **new_components;
162 int num_components = 0, component = 0;
163 DIR *scanning = NULL;
165 gboolean drive_stripped = FALSE;
166 gboolean do_report = (mono_profiler_get_events () & MONO_PROFILE_IOMAP_EVENTS) != 0;
168 if (IS_PORTABILITY_NONE) {
173 append_report (report, " - Requested file path: '%s'\n", pathname);
175 new_pathname = g_strdup (pathname);
178 g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
179 last_exists?"TRUE":"FALSE");
183 access (new_pathname, F_OK) == 0) {
185 g_message ("%s: Found it without doing anything\n", __func__);
187 return(new_pathname);
190 /* First turn '\' into '/' and strip any drive letters */
191 g_strdelimit (new_pathname, "\\", '/');
194 g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
198 if (IS_PORTABILITY_DRIVE &&
199 g_ascii_isalpha (new_pathname[0]) &&
200 (new_pathname[1] == ':')) {
201 int len = strlen (new_pathname);
203 g_memmove (new_pathname, new_pathname+2, len - 2);
204 new_pathname[len - 2] = '\0';
207 append_report (report, " - Stripped drive letter.\n");
208 drive_stripped = TRUE;
211 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
212 __func__, new_pathname);
216 len = strlen (new_pathname);
217 if (len > 1 && new_pathname [len - 1] == '/') {
218 new_pathname [len - 1] = 0;
220 g_message ("%s: requested name had a trailing /, rewritten to '%s'\n",
221 __func__, new_pathname);
226 access (new_pathname, F_OK) == 0) {
228 g_message ("%s: Found it\n", __func__);
230 if (do_report && drive_stripped)
231 do_mono_profiler_iomap (report, pathname, new_pathname);
233 return(new_pathname);
236 /* OK, have to work harder. Take each path component in turn
237 * and do a case-insensitive directory scan for it
240 if (!(IS_PORTABILITY_CASE)) {
241 g_free (new_pathname);
245 components = g_strsplit (new_pathname, "/", 0);
246 if (components == NULL) {
247 /* This shouldn't happen */
248 g_free (new_pathname);
252 while(components[num_components] != NULL) {
255 g_free (new_pathname);
257 if (num_components == 0){
262 new_components = (gchar **)g_new0 (gchar **, num_components + 1);
264 if (num_components > 1) {
265 if (strcmp (components[0], "") == 0) {
266 /* first component blank, so start at / */
267 scanning = opendir ("/");
268 if (scanning == NULL) {
270 g_message ("%s: opendir 1 error: %s", __func__,
273 g_strfreev (new_components);
274 g_strfreev (components);
278 new_components[component++] = g_strdup ("");
283 current = opendir (".");
284 if (current == NULL) {
286 g_message ("%s: opendir 2 error: %s", __func__,
289 g_strfreev (new_components);
290 g_strfreev (components);
294 entry = find_in_dir (current, components[0]);
296 g_strfreev (new_components);
297 g_strfreev (components);
301 scanning = opendir (entry);
302 if (scanning == NULL) {
304 g_message ("%s: opendir 3 error: %s", __func__,
308 g_strfreev (new_components);
309 g_strfreev (components);
313 new_components[component++] = entry;
317 if (strcmp (components[0], "") == 0) {
318 /* First and only component blank */
319 new_components[component++] = g_strdup ("");
324 current = opendir (".");
325 if (current == NULL) {
327 g_message ("%s: opendir 4 error: %s",
331 g_strfreev (new_components);
332 g_strfreev (components);
336 entry = find_in_dir (current, components[0]);
338 g_strfreev (new_components);
339 g_strfreev (components);
343 new_components[component++] = entry;
346 new_components[component++] = g_strdup (components[0]);
351 g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
354 g_assert (component == 1);
356 for(; component < num_components; component++) {
361 component == num_components -1) {
362 entry = g_strdup (components[component]);
365 entry = find_in_dir (scanning, components[component]);
367 g_strfreev (new_components);
368 g_strfreev (components);
373 new_components[component] = entry;
375 if (component < num_components -1) {
376 path_so_far = g_strjoinv ("/", new_components);
378 scanning = opendir (path_so_far);
379 g_free (path_so_far);
380 if (scanning == NULL) {
381 g_strfreev (new_components);
382 g_strfreev (components);
388 g_strfreev (components);
390 new_pathname = g_strjoinv ("/", new_components);
393 g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
397 g_strfreev (new_components);
400 access (new_pathname, F_OK) == 0) ||
402 if (do_report && strcmp (pathname, new_pathname) != 0)
403 do_mono_profiler_iomap (report, pathname, new_pathname);
405 return(new_pathname);
408 g_free (new_pathname);