bump rx to official 2.1 release.
[mono-project.git] / mono / utils / mono-io-portability.c
blob979bb6037fd21ba9be040c408eca9cba4a265fe2
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/metadata/gc-internal.h>
14 #include <mono/metadata/profiler.h>
15 #include <mono/metadata/profiler-private.h>
17 #ifndef DISABLE_PORTABILITY
19 #include <dirent.h>
21 int __mono_io_portability_helpers = PORTABILITY_UNKNOWN;
23 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists);
25 void mono_portability_helpers_init (void)
27 const gchar *env;
29 if (__mono_io_portability_helpers != PORTABILITY_UNKNOWN)
30 return;
32 __mono_io_portability_helpers = PORTABILITY_NONE;
34 env = g_getenv ("MONO_IOMAP");
35 if (env != NULL) {
36 /* parse the environment setting and set up some vars
37 * here
39 gchar **options = g_strsplit (env, ":", 0);
40 int i;
42 if (options == NULL) {
43 /* This shouldn't happen */
44 return;
47 for (i = 0; options[i] != NULL; i++) {
48 #ifdef DEBUG
49 g_message ("%s: Setting option [%s]", __func__,
50 options[i]);
51 #endif
52 if (!strncasecmp (options[i], "drive", 5)) {
53 __mono_io_portability_helpers |= PORTABILITY_DRIVE;
54 } else if (!strncasecmp (options[i], "case", 4)) {
55 __mono_io_portability_helpers |= PORTABILITY_CASE;
56 } else if (!strncasecmp (options[i], "all", 3)) {
57 __mono_io_portability_helpers |= (PORTABILITY_DRIVE | PORTABILITY_CASE);
63 /* Returns newly allocated string, or NULL on failure */
64 static gchar *find_in_dir (DIR *current, const gchar *name)
66 struct dirent *entry;
68 #ifdef DEBUG
69 g_message ("%s: looking for [%s]\n", __func__, name);
70 #endif
72 while((entry = readdir (current)) != NULL) {
73 #ifdef DEBUGX
74 g_message ("%s: found [%s]\n", __func__, entry->d_name);
75 #endif
77 if (!g_ascii_strcasecmp (name, entry->d_name)) {
78 char *ret;
80 #ifdef DEBUG
81 g_message ("%s: matched [%s] to [%s]\n", __func__,
82 entry->d_name, name);
83 #endif
85 ret = g_strdup (entry->d_name);
86 closedir (current);
87 return ret;
91 #ifdef DEBUG
92 g_message ("%s: returning NULL\n", __func__);
93 #endif
95 closedir (current);
97 return(NULL);
100 static inline void append_report (GString **report, const gchar *format, ...)
102 #if defined (_EGLIB_MAJOR) || GLIB_CHECK_VERSION(2,14,0)
103 va_list ap;
104 if (!*report)
105 *report = g_string_new ("");
107 va_start (ap, format);
108 g_string_append_vprintf (*report, format, ap);
109 va_end (ap);
110 #else
111 g_assert_not_reached ();
112 #endif
115 static inline void do_mono_profiler_iomap (GString **report, const char *pathname, const char *new_pathname)
117 char *rep = NULL;
118 GString *tmp = report ? *report : NULL;
120 if (tmp) {
121 if (tmp->len > 0)
122 rep = g_string_free (tmp, FALSE);
123 else
124 g_string_free (tmp, TRUE);
125 *report = NULL;
128 mono_profiler_iomap (rep, pathname, new_pathname);
129 g_free (rep);
132 gchar *mono_portability_find_file (const gchar *pathname, gboolean last_exists)
134 GString *report = NULL;
135 gchar *ret;
137 if (!pathname || !pathname [0])
138 return NULL;
139 ret = mono_portability_find_file_internal (&report, pathname, last_exists);
141 if (report)
142 g_string_free (report, TRUE);
144 return ret;
147 /* Returns newly-allocated string or NULL on failure */
148 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists)
150 gchar *new_pathname, **components, **new_components;
151 int num_components = 0, component = 0;
152 DIR *scanning = NULL;
153 size_t len;
154 gboolean drive_stripped = FALSE;
155 gboolean do_report = (mono_profiler_get_events () & MONO_PROFILE_IOMAP_EVENTS) != 0;
157 if (IS_PORTABILITY_NONE) {
158 return(NULL);
161 if (do_report)
162 append_report (report, " - Requested file path: '%s'\n", pathname);
164 new_pathname = g_strdup (pathname);
166 #ifdef DEBUG
167 g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
168 last_exists?"TRUE":"FALSE");
169 #endif
171 if (last_exists &&
172 access (new_pathname, F_OK) == 0) {
173 #ifdef DEBUG
174 g_message ("%s: Found it without doing anything\n", __func__);
175 #endif
176 return(new_pathname);
179 /* First turn '\' into '/' and strip any drive letters */
180 g_strdelimit (new_pathname, "\\", '/');
182 #ifdef DEBUG
183 g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
184 new_pathname);
185 #endif
187 if (IS_PORTABILITY_DRIVE &&
188 g_ascii_isalpha (new_pathname[0]) &&
189 (new_pathname[1] == ':')) {
190 int len = strlen (new_pathname);
192 g_memmove (new_pathname, new_pathname+2, len - 2);
193 new_pathname[len - 2] = '\0';
195 if (do_report) {
196 append_report (report, " - Stripped drive letter.\n");
197 drive_stripped = TRUE;
199 #ifdef DEBUG
200 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
201 __func__, new_pathname);
202 #endif
205 len = strlen (new_pathname);
206 if (len > 1 && new_pathname [len - 1] == '/') {
207 new_pathname [len - 1] = 0;
208 #ifdef DEBUG
209 g_message ("%s: requested name had a trailing /, rewritten to '%s'\n",
210 __func__, new_pathname);
211 #endif
214 if (last_exists &&
215 access (new_pathname, F_OK) == 0) {
216 #ifdef DEBUG
217 g_message ("%s: Found it\n", __func__);
218 #endif
219 if (do_report && drive_stripped)
220 do_mono_profiler_iomap (report, pathname, new_pathname);
222 return(new_pathname);
225 /* OK, have to work harder. Take each path component in turn
226 * and do a case-insensitive directory scan for it
229 if (!(IS_PORTABILITY_CASE)) {
230 g_free (new_pathname);
231 return(NULL);
234 components = g_strsplit (new_pathname, "/", 0);
235 if (components == NULL) {
236 /* This shouldn't happen */
237 g_free (new_pathname);
238 return(NULL);
241 while(components[num_components] != NULL) {
242 num_components++;
244 g_free (new_pathname);
246 if (num_components == 0){
247 return NULL;
251 new_components = (gchar **)g_new0 (gchar **, num_components + 1);
253 if (num_components > 1) {
254 if (strcmp (components[0], "") == 0) {
255 /* first component blank, so start at / */
256 scanning = opendir ("/");
257 if (scanning == NULL) {
258 #ifdef DEBUG
259 g_message ("%s: opendir 1 error: %s", __func__,
260 g_strerror (errno));
261 #endif
262 g_strfreev (new_components);
263 g_strfreev (components);
264 return(NULL);
267 new_components[component++] = g_strdup ("");
268 } else {
269 DIR *current;
270 gchar *entry;
272 current = opendir (".");
273 if (current == NULL) {
274 #ifdef DEBUG
275 g_message ("%s: opendir 2 error: %s", __func__,
276 g_strerror (errno));
277 #endif
278 g_strfreev (new_components);
279 g_strfreev (components);
280 return(NULL);
283 entry = find_in_dir (current, components[0]);
284 if (entry == NULL) {
285 g_strfreev (new_components);
286 g_strfreev (components);
287 return(NULL);
290 scanning = opendir (entry);
291 if (scanning == NULL) {
292 #ifdef DEBUG
293 g_message ("%s: opendir 3 error: %s", __func__,
294 g_strerror (errno));
295 #endif
296 g_free (entry);
297 g_strfreev (new_components);
298 g_strfreev (components);
299 return(NULL);
302 new_components[component++] = entry;
304 } else {
305 if (last_exists) {
306 if (strcmp (components[0], "") == 0) {
307 /* First and only component blank */
308 new_components[component++] = g_strdup ("");
309 } else {
310 DIR *current;
311 gchar *entry;
313 current = opendir (".");
314 if (current == NULL) {
315 #ifdef DEBUG
316 g_message ("%s: opendir 4 error: %s",
317 __func__,
318 g_strerror (errno));
319 #endif
320 g_strfreev (new_components);
321 g_strfreev (components);
322 return(NULL);
325 entry = find_in_dir (current, components[0]);
326 if (entry == NULL) {
327 g_strfreev (new_components);
328 g_strfreev (components);
329 return(NULL);
332 new_components[component++] = entry;
334 } else {
335 new_components[component++] = g_strdup (components[0]);
339 #ifdef DEBUG
340 g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
341 #endif
343 g_assert (component == 1);
345 for(; component < num_components; component++) {
346 gchar *entry;
347 gchar *path_so_far;
349 if (!last_exists &&
350 component == num_components -1) {
351 entry = g_strdup (components[component]);
352 closedir (scanning);
353 } else {
354 entry = find_in_dir (scanning, components[component]);
355 if (entry == NULL) {
356 g_strfreev (new_components);
357 g_strfreev (components);
358 return(NULL);
362 new_components[component] = entry;
364 if (component < num_components -1) {
365 path_so_far = g_strjoinv ("/", new_components);
367 scanning = opendir (path_so_far);
368 g_free (path_so_far);
369 if (scanning == NULL) {
370 g_strfreev (new_components);
371 g_strfreev (components);
372 return(NULL);
377 g_strfreev (components);
379 new_pathname = g_strjoinv ("/", new_components);
381 #ifdef DEBUG
382 g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
383 new_pathname);
384 #endif
386 g_strfreev (new_components);
388 if ((last_exists &&
389 access (new_pathname, F_OK) == 0) ||
390 (!last_exists)) {
391 if (do_report && strcmp (pathname, new_pathname) != 0)
392 do_mono_profiler_iomap (report, pathname, new_pathname);
394 return(new_pathname);
397 g_free (new_pathname);
398 return(NULL);
400 #endif