Fixed.
[mono.git] / mono / utils / mono-io-portability.c
1 #include "config.h"
2
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>
16
17 #ifdef DISABLE_PORTABILITY
18 int __mono_io_portability_helpers = PORTABILITY_NONE;
19
20 void 
21 mono_portability_helpers_init (void)
22 {
23 }
24
25 gchar *
26 mono_portability_find_file (const gchar *pathname, gboolean last_exists)
27 {
28         g_assert_not_reached();
29         return NULL;
30 }
31
32 #else
33
34 #include <dirent.h>
35
36 int __mono_io_portability_helpers = PORTABILITY_UNKNOWN;
37
38 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists);
39
40 void mono_portability_helpers_init (void)
41 {
42         const gchar *env;
43
44         if (__mono_io_portability_helpers != PORTABILITY_UNKNOWN)
45                 return;
46         
47         __mono_io_portability_helpers = PORTABILITY_NONE;
48         
49         env = g_getenv ("MONO_IOMAP");
50         if (env != NULL) {
51                 /* parse the environment setting and set up some vars
52                  * here
53                  */
54                 gchar **options = g_strsplit (env, ":", 0);
55                 int i;
56                 
57                 if (options == NULL) {
58                         /* This shouldn't happen */
59                         return;
60                 }
61                 
62                 for (i = 0; options[i] != NULL; i++) {
63 #ifdef DEBUG
64                         g_message ("%s: Setting option [%s]", __func__,
65                                    options[i]);
66 #endif
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);
73                         }
74                 }
75         }
76 }
77
78 /* Returns newly allocated string, or NULL on failure */
79 static gchar *find_in_dir (DIR *current, const gchar *name)
80 {
81         struct dirent *entry;
82
83 #ifdef DEBUG
84         g_message ("%s: looking for [%s]\n", __func__, name);
85 #endif
86         
87         while((entry = readdir (current)) != NULL) {
88 #ifdef DEBUGX
89                 g_message ("%s: found [%s]\n", __func__, entry->d_name);
90 #endif
91                 
92                 if (!g_ascii_strcasecmp (name, entry->d_name)) {
93                         char *ret;
94                         
95 #ifdef DEBUG
96                         g_message ("%s: matched [%s] to [%s]\n", __func__,
97                                    entry->d_name, name);
98 #endif
99
100                         ret = g_strdup (entry->d_name);
101                         closedir (current);
102                         return ret;
103                 }
104         }
105         
106 #ifdef DEBUG
107         g_message ("%s: returning NULL\n", __func__);
108 #endif
109         
110         closedir (current);
111         
112         return(NULL);
113 }
114
115 static inline void append_report (GString **report, const gchar *format, ...)
116 {
117 #if GLIB_CHECK_VERSION(2,14,0)
118         va_list ap;
119         if (!*report)
120                 *report = g_string_new ("");
121
122         va_start (ap, format);
123         g_string_append_vprintf (*report, format, ap);
124         va_end (ap);
125 #else
126         g_assert_not_reached ();
127 #endif
128 }
129
130 static inline void do_mono_profiler_iomap (GString **report, const char *pathname, const char *new_pathname)
131 {
132         char *rep = NULL;
133         GString *tmp = report ? *report : NULL;
134
135         if (tmp) {
136                 if (tmp->len > 0)
137                         rep = g_string_free (tmp, FALSE);
138                 else
139                         g_string_free (tmp, TRUE);
140                 *report = NULL;
141         }
142
143         mono_profiler_iomap (rep, pathname, new_pathname);
144         g_free (rep);
145 }
146
147 gchar *mono_portability_find_file (const gchar *pathname, gboolean last_exists)
148 {
149         GString *report = NULL;
150         gchar *ret = mono_portability_find_file_internal (&report, pathname, last_exists);
151
152         if (report)
153                 g_string_free (report, TRUE);
154
155         return ret;
156 }
157
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)
160 {
161         gchar *new_pathname, **components, **new_components;
162         int num_components = 0, component = 0;
163         DIR *scanning = NULL;
164         size_t len;
165         gboolean drive_stripped = FALSE;
166         gboolean do_report = (mono_profiler_get_events () & MONO_PROFILE_IOMAP_EVENTS) != 0;
167
168         if (IS_PORTABILITY_NONE) {
169                 return(NULL);
170         }
171
172         if (do_report)
173                 append_report (report, " - Requested file path: '%s'\n", pathname);
174
175         new_pathname = g_strdup (pathname);
176         
177 #ifdef DEBUG
178         g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
179                    last_exists?"TRUE":"FALSE");
180 #endif
181         
182         if (last_exists &&
183             access (new_pathname, F_OK) == 0) {
184 #ifdef DEBUG
185                 g_message ("%s: Found it without doing anything\n", __func__);
186 #endif
187                 return(new_pathname);
188         }
189         
190         /* First turn '\' into '/' and strip any drive letters */
191         g_strdelimit (new_pathname, "\\", '/');
192
193 #ifdef DEBUG
194         g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
195                    new_pathname);
196 #endif
197         
198         if (IS_PORTABILITY_DRIVE &&
199             g_ascii_isalpha (new_pathname[0]) &&
200             (new_pathname[1] == ':')) {
201                 int len = strlen (new_pathname);
202                 
203                 g_memmove (new_pathname, new_pathname+2, len - 2);
204                 new_pathname[len - 2] = '\0';
205
206                 if (do_report) {
207                         append_report (report, " - Stripped drive letter.\n");
208                         drive_stripped = TRUE;
209                 }
210 #ifdef DEBUG
211                 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
212                            __func__, new_pathname);
213 #endif
214         }
215
216         len = strlen (new_pathname);
217         if (len > 1 && new_pathname [len - 1] == '/') {
218                 new_pathname [len - 1] = 0;
219 #ifdef DEBUG
220                 g_message ("%s: requested name had a trailing /, rewritten to '%s'\n",
221                            __func__, new_pathname);
222 #endif
223         }
224
225         if (last_exists &&
226             access (new_pathname, F_OK) == 0) {
227 #ifdef DEBUG
228                 g_message ("%s: Found it\n", __func__);
229 #endif
230                 if (do_report && drive_stripped)
231                         do_mono_profiler_iomap (report, pathname, new_pathname);
232
233                 return(new_pathname);
234         }
235
236         /* OK, have to work harder.  Take each path component in turn
237          * and do a case-insensitive directory scan for it
238          */
239
240         if (!(IS_PORTABILITY_CASE)) {
241                 g_free (new_pathname);
242                 return(NULL);
243         }
244
245         components = g_strsplit (new_pathname, "/", 0);
246         if (components == NULL) {
247                 /* This shouldn't happen */
248                 g_free (new_pathname);
249                 return(NULL);
250         }
251         
252         while(components[num_components] != NULL) {
253                 num_components++;
254         }
255         g_free (new_pathname);
256         
257         if (num_components == 0){
258                 return NULL;
259         }
260         
261
262         new_components = (gchar **)g_new0 (gchar **, num_components + 1);
263
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) {
269 #ifdef DEBUG
270                                 g_message ("%s: opendir 1 error: %s", __func__,
271                                            g_strerror (errno));
272 #endif
273                                 g_strfreev (new_components);
274                                 g_strfreev (components);
275                                 return(NULL);
276                         }
277                 
278                         new_components[component++] = g_strdup ("");
279                 } else {
280                         DIR *current;
281                         gchar *entry;
282                 
283                         current = opendir (".");
284                         if (current == NULL) {
285 #ifdef DEBUG
286                                 g_message ("%s: opendir 2 error: %s", __func__,
287                                            g_strerror (errno));
288 #endif
289                                 g_strfreev (new_components);
290                                 g_strfreev (components);
291                                 return(NULL);
292                         }
293                 
294                         entry = find_in_dir (current, components[0]);
295                         if (entry == NULL) {
296                                 g_strfreev (new_components);
297                                 g_strfreev (components);
298                                 return(NULL);
299                         }
300                 
301                         scanning = opendir (entry);
302                         if (scanning == NULL) {
303 #ifdef DEBUG
304                                 g_message ("%s: opendir 3 error: %s", __func__,
305                                            g_strerror (errno));
306 #endif
307                                 g_free (entry);
308                                 g_strfreev (new_components);
309                                 g_strfreev (components);
310                                 return(NULL);
311                         }
312                 
313                         new_components[component++] = entry;
314                 }
315         } else {
316                 if (last_exists) {
317                         if (strcmp (components[0], "") == 0) {
318                                 /* First and only component blank */
319                                 new_components[component++] = g_strdup ("");
320                         } else {
321                                 DIR *current;
322                                 gchar *entry;
323                                 
324                                 current = opendir (".");
325                                 if (current == NULL) {
326 #ifdef DEBUG
327                                         g_message ("%s: opendir 4 error: %s",
328                                                    __func__,
329                                                    g_strerror (errno));
330 #endif
331                                         g_strfreev (new_components);
332                                         g_strfreev (components);
333                                         return(NULL);
334                                 }
335                                 
336                                 entry = find_in_dir (current, components[0]);
337                                 if (entry == NULL) {
338                                         g_strfreev (new_components);
339                                         g_strfreev (components);
340                                         return(NULL);
341                                 }
342                                 
343                                 new_components[component++] = entry;
344                         }
345                 } else {
346                                 new_components[component++] = g_strdup (components[0]);
347                 }
348         }
349
350 #ifdef DEBUG
351         g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
352 #endif
353
354         g_assert (component == 1);
355         
356         for(; component < num_components; component++) {
357                 gchar *entry;
358                 gchar *path_so_far;
359                 
360                 if (!last_exists &&
361                     component == num_components -1) {
362                         entry = g_strdup (components[component]);
363                         closedir (scanning);
364                 } else {
365                         entry = find_in_dir (scanning, components[component]);
366                         if (entry == NULL) {
367                                 g_strfreev (new_components);
368                                 g_strfreev (components);
369                                 return(NULL);
370                         }
371                 }
372                 
373                 new_components[component] = entry;
374                 
375                 if (component < num_components -1) {
376                         path_so_far = g_strjoinv ("/", new_components);
377
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);
383                                 return(NULL);
384                         }
385                 }
386         }
387         
388         g_strfreev (components);
389
390         new_pathname = g_strjoinv ("/", new_components);
391
392 #ifdef DEBUG
393         g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
394                    new_pathname);
395 #endif
396         
397         g_strfreev (new_components);
398
399         if ((last_exists &&
400              access (new_pathname, F_OK) == 0) ||
401             (!last_exists)) {
402                 if (do_report && strcmp (pathname, new_pathname) != 0)
403                         do_mono_profiler_iomap (report, pathname, new_pathname);
404
405                 return(new_pathname);
406         }
407
408         g_free (new_pathname);
409         return(NULL);
410 }
411 #endif