8 #include <mono/utils/mono-io-portability.h>
9 #include <mono/metadata/profiler-private.h>
10 #include <mono/utils/mono-compiler.h>
12 #ifndef DISABLE_PORTABILITY
16 int mono_io_portability_helpers = PORTABILITY_UNKNOWN;
18 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists);
20 void mono_portability_helpers_init (void)
24 if (mono_io_portability_helpers != PORTABILITY_UNKNOWN)
27 mono_io_portability_helpers = PORTABILITY_NONE;
29 env = g_getenv ("MONO_IOMAP");
31 /* parse the environment setting and set up some vars
34 gchar **options = g_strsplit (env, ":", 0);
37 if (options == NULL) {
38 /* This shouldn't happen */
42 for (i = 0; options[i] != NULL; i++) {
44 g_message ("%s: Setting option [%s]", __func__,
47 if (!strncasecmp (options[i], "drive", 5)) {
48 mono_io_portability_helpers |= PORTABILITY_DRIVE;
49 } else if (!strncasecmp (options[i], "case", 4)) {
50 mono_io_portability_helpers |= PORTABILITY_CASE;
51 } else if (!strncasecmp (options[i], "all", 3)) {
52 mono_io_portability_helpers |= (PORTABILITY_DRIVE | PORTABILITY_CASE);
59 /* Returns newly allocated string, or NULL on failure */
60 static gchar *find_in_dir (DIR *current, const gchar *name)
65 g_message ("%s: looking for [%s]\n", __func__, name);
68 while((entry = readdir (current)) != NULL) {
70 g_message ("%s: found [%s]\n", __func__, entry->d_name);
73 if (!g_ascii_strcasecmp (name, entry->d_name)) {
77 g_message ("%s: matched [%s] to [%s]\n", __func__,
81 ret = g_strdup (entry->d_name);
88 g_message ("%s: returning NULL\n", __func__);
96 static inline void append_report (GString **report, const gchar *format, ...)
100 *report = g_string_new ("");
102 va_start (ap, format);
103 g_string_append_vprintf (*report, format, ap);
107 static inline void do_mono_profiler_iomap (GString **report, const char *pathname, const char *new_pathname)
110 GString *tmp = report ? *report : NULL;
114 rep = g_string_free (tmp, FALSE);
116 g_string_free (tmp, TRUE);
120 mono_profiler_iomap (rep, pathname, new_pathname);
124 gchar *mono_portability_find_file (const gchar *pathname, gboolean last_exists)
126 GString *report = NULL;
129 if (!pathname || !pathname [0])
131 ret = mono_portability_find_file_internal (&report, pathname, last_exists);
134 g_string_free (report, TRUE);
139 /* Returns newly-allocated string or NULL on failure */
140 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists)
142 gchar *new_pathname, **components, **new_components;
143 int num_components = 0, component = 0;
144 DIR *scanning = NULL;
146 gboolean drive_stripped = FALSE;
147 gboolean do_report = (mono_profiler_get_events () & MONO_PROFILE_IOMAP_EVENTS) != 0;
149 if (IS_PORTABILITY_NONE) {
154 append_report (report, " - Requested file path: '%s'\n", pathname);
156 new_pathname = g_strdup (pathname);
159 g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
160 last_exists?"TRUE":"FALSE");
164 access (new_pathname, F_OK) == 0) {
166 g_message ("%s: Found it without doing anything\n", __func__);
168 return(new_pathname);
171 /* First turn '\' into '/' and strip any drive letters */
172 g_strdelimit (new_pathname, "\\", '/');
175 g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
179 if (IS_PORTABILITY_DRIVE &&
180 g_ascii_isalpha (new_pathname[0]) &&
181 (new_pathname[1] == ':')) {
182 int len = strlen (new_pathname);
184 g_memmove (new_pathname, new_pathname+2, len - 2);
185 new_pathname[len - 2] = '\0';
188 append_report (report, " - Stripped drive letter.\n");
189 drive_stripped = TRUE;
192 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
193 __func__, new_pathname);
197 len = strlen (new_pathname);
198 if (len > 1 && new_pathname [len - 1] == '/') {
199 new_pathname [len - 1] = 0;
201 g_message ("%s: requested name had a trailing /, rewritten to '%s'\n",
202 __func__, new_pathname);
207 access (new_pathname, F_OK) == 0) {
209 g_message ("%s: Found it\n", __func__);
211 if (do_report && drive_stripped)
212 do_mono_profiler_iomap (report, pathname, new_pathname);
214 return(new_pathname);
217 /* OK, have to work harder. Take each path component in turn
218 * and do a case-insensitive directory scan for it
221 if (!(IS_PORTABILITY_CASE)) {
222 g_free (new_pathname);
226 components = g_strsplit (new_pathname, "/", 0);
227 if (components == NULL) {
228 /* This shouldn't happen */
229 g_free (new_pathname);
233 while(components[num_components] != NULL) {
236 g_free (new_pathname);
238 if (num_components == 0){
243 new_components = (gchar **)g_new0 (gchar **, num_components + 1);
245 if (num_components > 1) {
246 if (strcmp (components[0], "") == 0) {
247 /* first component blank, so start at / */
248 scanning = opendir ("/");
249 if (scanning == NULL) {
251 g_message ("%s: opendir 1 error: %s", __func__,
254 g_strfreev (new_components);
255 g_strfreev (components);
259 new_components[component++] = g_strdup ("");
264 current = opendir (".");
265 if (current == NULL) {
267 g_message ("%s: opendir 2 error: %s", __func__,
270 g_strfreev (new_components);
271 g_strfreev (components);
275 entry = find_in_dir (current, components[0]);
277 g_strfreev (new_components);
278 g_strfreev (components);
282 scanning = opendir (entry);
283 if (scanning == NULL) {
285 g_message ("%s: opendir 3 error: %s", __func__,
289 g_strfreev (new_components);
290 g_strfreev (components);
294 new_components[component++] = entry;
298 if (strcmp (components[0], "") == 0) {
299 /* First and only component blank */
300 new_components[component++] = g_strdup ("");
305 current = opendir (".");
306 if (current == NULL) {
308 g_message ("%s: opendir 4 error: %s",
312 g_strfreev (new_components);
313 g_strfreev (components);
317 entry = find_in_dir (current, components[0]);
319 g_strfreev (new_components);
320 g_strfreev (components);
324 new_components[component++] = entry;
327 new_components[component++] = g_strdup (components[0]);
332 g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
335 g_assert (component == 1);
337 for(; component < num_components; component++) {
342 component == num_components -1) {
343 entry = g_strdup (components[component]);
346 entry = find_in_dir (scanning, components[component]);
348 g_strfreev (new_components);
349 g_strfreev (components);
354 new_components[component] = entry;
356 if (component < num_components -1) {
357 path_so_far = g_strjoinv ("/", new_components);
359 scanning = opendir (path_so_far);
360 g_free (path_so_far);
361 if (scanning == NULL) {
362 g_strfreev (new_components);
363 g_strfreev (components);
369 g_strfreev (components);
371 new_pathname = g_strjoinv ("/", new_components);
374 g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
378 g_strfreev (new_components);
381 access (new_pathname, F_OK) == 0) ||
383 if (do_report && strcmp (pathname, new_pathname) != 0)
384 do_mono_profiler_iomap (report, pathname, new_pathname);
386 return(new_pathname);
389 g_free (new_pathname);
393 #else /* DISABLE_PORTABILITY */
395 MONO_EMPTY_SOURCE_FILE (mono_io_portability);
397 #endif /* DISABLE_PORTABILITY */