8 #include <mono/utils/mono-io-portability.h>
9 #include <mono/metadata/profiler-private.h>
11 #ifndef DISABLE_PORTABILITY
15 int mono_io_portability_helpers = PORTABILITY_UNKNOWN;
17 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists);
19 void mono_portability_helpers_init (void)
23 if (mono_io_portability_helpers != PORTABILITY_UNKNOWN)
26 mono_io_portability_helpers = PORTABILITY_NONE;
28 env = g_getenv ("MONO_IOMAP");
30 /* parse the environment setting and set up some vars
33 gchar **options = g_strsplit (env, ":", 0);
36 if (options == NULL) {
37 /* This shouldn't happen */
41 for (i = 0; options[i] != NULL; i++) {
43 g_message ("%s: Setting option [%s]", __func__,
46 if (!strncasecmp (options[i], "drive", 5)) {
47 mono_io_portability_helpers |= PORTABILITY_DRIVE;
48 } else if (!strncasecmp (options[i], "case", 4)) {
49 mono_io_portability_helpers |= PORTABILITY_CASE;
50 } else if (!strncasecmp (options[i], "all", 3)) {
51 mono_io_portability_helpers |= (PORTABILITY_DRIVE | PORTABILITY_CASE);
57 /* Returns newly allocated string, or NULL on failure */
58 static gchar *find_in_dir (DIR *current, const gchar *name)
63 g_message ("%s: looking for [%s]\n", __func__, name);
66 while((entry = readdir (current)) != NULL) {
68 g_message ("%s: found [%s]\n", __func__, entry->d_name);
71 if (!g_ascii_strcasecmp (name, entry->d_name)) {
75 g_message ("%s: matched [%s] to [%s]\n", __func__,
79 ret = g_strdup (entry->d_name);
86 g_message ("%s: returning NULL\n", __func__);
94 static inline void append_report (GString **report, const gchar *format, ...)
96 #if defined (_EGLIB_MAJOR) || GLIB_CHECK_VERSION(2,14,0)
99 *report = g_string_new ("");
101 va_start (ap, format);
102 g_string_append_vprintf (*report, format, ap);
105 g_assert_not_reached ();
109 static inline void do_mono_profiler_iomap (GString **report, const char *pathname, const char *new_pathname)
112 GString *tmp = report ? *report : NULL;
116 rep = g_string_free (tmp, FALSE);
118 g_string_free (tmp, TRUE);
122 mono_profiler_iomap (rep, pathname, new_pathname);
126 gchar *mono_portability_find_file (const gchar *pathname, gboolean last_exists)
128 GString *report = NULL;
131 if (!pathname || !pathname [0])
133 ret = mono_portability_find_file_internal (&report, pathname, last_exists);
136 g_string_free (report, TRUE);
141 /* Returns newly-allocated string or NULL on failure */
142 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists)
144 gchar *new_pathname, **components, **new_components;
145 int num_components = 0, component = 0;
146 DIR *scanning = NULL;
148 gboolean drive_stripped = FALSE;
149 gboolean do_report = (mono_profiler_get_events () & MONO_PROFILE_IOMAP_EVENTS) != 0;
151 if (IS_PORTABILITY_NONE) {
156 append_report (report, " - Requested file path: '%s'\n", pathname);
158 new_pathname = g_strdup (pathname);
161 g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
162 last_exists?"TRUE":"FALSE");
166 access (new_pathname, F_OK) == 0) {
168 g_message ("%s: Found it without doing anything\n", __func__);
170 return(new_pathname);
173 /* First turn '\' into '/' and strip any drive letters */
174 g_strdelimit (new_pathname, "\\", '/');
177 g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
181 if (IS_PORTABILITY_DRIVE &&
182 g_ascii_isalpha (new_pathname[0]) &&
183 (new_pathname[1] == ':')) {
184 int len = strlen (new_pathname);
186 g_memmove (new_pathname, new_pathname+2, len - 2);
187 new_pathname[len - 2] = '\0';
190 append_report (report, " - Stripped drive letter.\n");
191 drive_stripped = TRUE;
194 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
195 __func__, new_pathname);
199 len = strlen (new_pathname);
200 if (len > 1 && new_pathname [len - 1] == '/') {
201 new_pathname [len - 1] = 0;
203 g_message ("%s: requested name had a trailing /, rewritten to '%s'\n",
204 __func__, new_pathname);
209 access (new_pathname, F_OK) == 0) {
211 g_message ("%s: Found it\n", __func__);
213 if (do_report && drive_stripped)
214 do_mono_profiler_iomap (report, pathname, new_pathname);
216 return(new_pathname);
219 /* OK, have to work harder. Take each path component in turn
220 * and do a case-insensitive directory scan for it
223 if (!(IS_PORTABILITY_CASE)) {
224 g_free (new_pathname);
228 components = g_strsplit (new_pathname, "/", 0);
229 if (components == NULL) {
230 /* This shouldn't happen */
231 g_free (new_pathname);
235 while(components[num_components] != NULL) {
238 g_free (new_pathname);
240 if (num_components == 0){
245 new_components = (gchar **)g_new0 (gchar **, num_components + 1);
247 if (num_components > 1) {
248 if (strcmp (components[0], "") == 0) {
249 /* first component blank, so start at / */
250 scanning = opendir ("/");
251 if (scanning == NULL) {
253 g_message ("%s: opendir 1 error: %s", __func__,
256 g_strfreev (new_components);
257 g_strfreev (components);
261 new_components[component++] = g_strdup ("");
266 current = opendir (".");
267 if (current == NULL) {
269 g_message ("%s: opendir 2 error: %s", __func__,
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 scanning = opendir (entry);
285 if (scanning == NULL) {
287 g_message ("%s: opendir 3 error: %s", __func__,
291 g_strfreev (new_components);
292 g_strfreev (components);
296 new_components[component++] = entry;
300 if (strcmp (components[0], "") == 0) {
301 /* First and only component blank */
302 new_components[component++] = g_strdup ("");
307 current = opendir (".");
308 if (current == NULL) {
310 g_message ("%s: opendir 4 error: %s",
314 g_strfreev (new_components);
315 g_strfreev (components);
319 entry = find_in_dir (current, components[0]);
321 g_strfreev (new_components);
322 g_strfreev (components);
326 new_components[component++] = entry;
329 new_components[component++] = g_strdup (components[0]);
334 g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
337 g_assert (component == 1);
339 for(; component < num_components; component++) {
344 component == num_components -1) {
345 entry = g_strdup (components[component]);
348 entry = find_in_dir (scanning, components[component]);
350 g_strfreev (new_components);
351 g_strfreev (components);
356 new_components[component] = entry;
358 if (component < num_components -1) {
359 path_so_far = g_strjoinv ("/", new_components);
361 scanning = opendir (path_so_far);
362 g_free (path_so_far);
363 if (scanning == NULL) {
364 g_strfreev (new_components);
365 g_strfreev (components);
371 g_strfreev (components);
373 new_pathname = g_strjoinv ("/", new_components);
376 g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
380 g_strfreev (new_components);
383 access (new_pathname, F_OK) == 0) ||
385 if (do_report && strcmp (pathname, new_pathname) != 0)
386 do_mono_profiler_iomap (report, pathname, new_pathname);
388 return(new_pathname);
391 g_free (new_pathname);