2010-04-06 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / utils / mono-io-portability.c
blob839b9b3d14ee1a0bf8444f92fc725944c0ce57a3
1 #include "config.h"
3 #include <string.h>
4 #ifdef HAVE_UNISTD_H
5 #include <unistd.h>
6 #endif
7 #include <errno.h>
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;
21 void
22 mono_portability_helpers_init (void)
26 gchar *
27 mono_portability_find_file (const gchar *pathname, gboolean last_exists)
29 g_assert_not_reached();
30 return NULL;
33 #else
35 #include <dirent.h>
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)
43 const gchar *env;
45 if (__mono_io_portability_helpers != PORTABILITY_UNKNOWN)
46 return;
48 __mono_io_portability_helpers = PORTABILITY_NONE;
50 env = g_getenv ("MONO_IOMAP");
51 if (env != NULL) {
52 /* parse the environment setting and set up some vars
53 * here
55 gchar **options = g_strsplit (env, ":", 0);
56 int i;
58 if (options == NULL) {
59 /* This shouldn't happen */
60 return;
63 for (i = 0; options[i] != NULL; i++) {
64 #ifdef DEBUG
65 g_message ("%s: Setting option [%s]", __func__,
66 options[i]);
67 #endif
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)
82 struct dirent *entry;
84 #ifdef DEBUG
85 g_message ("%s: looking for [%s]\n", __func__, name);
86 #endif
88 while((entry = readdir (current)) != NULL) {
89 #ifdef DEBUGX
90 g_message ("%s: found [%s]\n", __func__, entry->d_name);
91 #endif
93 if (!g_ascii_strcasecmp (name, entry->d_name)) {
94 char *ret;
96 #ifdef DEBUG
97 g_message ("%s: matched [%s] to [%s]\n", __func__,
98 entry->d_name, name);
99 #endif
101 ret = g_strdup (entry->d_name);
102 closedir (current);
103 return ret;
107 #ifdef DEBUG
108 g_message ("%s: returning NULL\n", __func__);
109 #endif
111 closedir (current);
113 return(NULL);
116 static inline void append_report (GString **report, const gchar *format, ...)
118 #if GLIB_CHECK_VERSION(2,14,0)
119 va_list ap;
120 if (!*report)
121 *report = g_string_new ("");
123 va_start (ap, format);
124 g_string_append_vprintf (*report, format, ap);
125 va_end (ap);
126 #else
127 g_assert_not_reached ();
128 #endif
131 static inline void do_mono_profiler_iomap (GString **report, const char *pathname, const char *new_pathname)
133 char *rep = NULL;
134 GString *tmp = report ? *report : NULL;
136 if (tmp) {
137 if (tmp->len > 0)
138 rep = g_string_free (tmp, FALSE);
139 else
140 g_string_free (tmp, TRUE);
141 *report = NULL;
144 mono_profiler_iomap (rep, pathname, new_pathname);
145 g_free (rep);
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);
153 if (report)
154 g_string_free (report, TRUE);
156 return ret;
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;
165 size_t len;
166 gboolean drive_stripped = FALSE;
167 gboolean do_report = (mono_profiler_get_events () & MONO_PROFILE_IOMAP_EVENTS) != 0;
169 if (IS_PORTABILITY_NONE) {
170 return(NULL);
173 if (do_report)
174 append_report (report, " - Requested file path: '%s'\n", pathname);
176 new_pathname = g_strdup (pathname);
178 #ifdef DEBUG
179 g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
180 last_exists?"TRUE":"FALSE");
181 #endif
183 if (last_exists &&
184 access (new_pathname, F_OK) == 0) {
185 #ifdef DEBUG
186 g_message ("%s: Found it without doing anything\n", __func__);
187 #endif
188 return(new_pathname);
191 /* First turn '\' into '/' and strip any drive letters */
192 g_strdelimit (new_pathname, "\\", '/');
194 #ifdef DEBUG
195 g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
196 new_pathname);
197 #endif
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';
207 if (do_report) {
208 append_report (report, " - Stripped drive letter.\n");
209 drive_stripped = TRUE;
211 #ifdef DEBUG
212 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
213 __func__, new_pathname);
214 #endif
217 len = strlen (new_pathname);
218 if (len > 1 && new_pathname [len - 1] == '/') {
219 new_pathname [len - 1] = 0;
220 #ifdef DEBUG
221 g_message ("%s: requested name had a trailing /, rewritten to '%s'\n",
222 __func__, new_pathname);
223 #endif
226 if (last_exists &&
227 access (new_pathname, F_OK) == 0) {
228 #ifdef DEBUG
229 g_message ("%s: Found it\n", __func__);
230 #endif
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);
243 return(NULL);
246 components = g_strsplit (new_pathname, "/", 0);
247 if (components == NULL) {
248 /* This shouldn't happen */
249 g_free (new_pathname);
250 return(NULL);
253 while(components[num_components] != NULL) {
254 num_components++;
256 g_free (new_pathname);
258 if (num_components == 0){
259 return NULL;
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) {
270 #ifdef DEBUG
271 g_message ("%s: opendir 1 error: %s", __func__,
272 g_strerror (errno));
273 #endif
274 g_strfreev (new_components);
275 g_strfreev (components);
276 return(NULL);
279 new_components[component++] = g_strdup ("");
280 } else {
281 DIR *current;
282 gchar *entry;
284 current = opendir (".");
285 if (current == NULL) {
286 #ifdef DEBUG
287 g_message ("%s: opendir 2 error: %s", __func__,
288 g_strerror (errno));
289 #endif
290 g_strfreev (new_components);
291 g_strfreev (components);
292 return(NULL);
295 entry = find_in_dir (current, components[0]);
296 if (entry == NULL) {
297 g_strfreev (new_components);
298 g_strfreev (components);
299 return(NULL);
302 scanning = opendir (entry);
303 if (scanning == NULL) {
304 #ifdef DEBUG
305 g_message ("%s: opendir 3 error: %s", __func__,
306 g_strerror (errno));
307 #endif
308 g_free (entry);
309 g_strfreev (new_components);
310 g_strfreev (components);
311 return(NULL);
314 new_components[component++] = entry;
316 } else {
317 if (last_exists) {
318 if (strcmp (components[0], "") == 0) {
319 /* First and only component blank */
320 new_components[component++] = g_strdup ("");
321 } else {
322 DIR *current;
323 gchar *entry;
325 current = opendir (".");
326 if (current == NULL) {
327 #ifdef DEBUG
328 g_message ("%s: opendir 4 error: %s",
329 __func__,
330 g_strerror (errno));
331 #endif
332 g_strfreev (new_components);
333 g_strfreev (components);
334 return(NULL);
337 entry = find_in_dir (current, components[0]);
338 if (entry == NULL) {
339 g_strfreev (new_components);
340 g_strfreev (components);
341 return(NULL);
344 new_components[component++] = entry;
346 } else {
347 new_components[component++] = g_strdup (components[0]);
351 #ifdef DEBUG
352 g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
353 #endif
355 g_assert (component == 1);
357 for(; component < num_components; component++) {
358 gchar *entry;
359 gchar *path_so_far;
361 if (!last_exists &&
362 component == num_components -1) {
363 entry = g_strdup (components[component]);
364 closedir (scanning);
365 } else {
366 entry = find_in_dir (scanning, components[component]);
367 if (entry == NULL) {
368 g_strfreev (new_components);
369 g_strfreev (components);
370 return(NULL);
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);
384 return(NULL);
389 g_strfreev (components);
391 new_pathname = g_strjoinv ("/", new_components);
393 #ifdef DEBUG
394 g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
395 new_pathname);
396 #endif
398 g_strfreev (new_components);
400 if ((last_exists &&
401 access (new_pathname, F_OK) == 0) ||
402 (!last_exists)) {
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);
410 return(NULL);
412 #endif