Install mono-dl.h header
[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/utils/mono-hash.h>
14 #include <mono/metadata/gc-internal.h>
15 #include <mono/metadata/profiler.h>
16 #include <mono/metadata/profiler-private.h>
17
18 #ifdef DISABLE_PORTABILITY
19 int __mono_io_portability_helpers = PORTABILITY_NONE;
20
21 void 
22 mono_portability_helpers_init (void)
23 {
24 }
25
26 gchar *
27 mono_portability_find_file (const gchar *pathname, gboolean last_exists)
28 {
29         g_assert_not_reached();
30         return NULL;
31 }
32
33 #else
34
35 #include <dirent.h>
36
37 int __mono_io_portability_helpers = PORTABILITY_UNKNOWN;
38
39 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists);
40
41 void mono_portability_helpers_init (void)
42 {
43         const gchar *env;
44
45         if (__mono_io_portability_helpers != PORTABILITY_UNKNOWN)
46                 return;
47         
48         __mono_io_portability_helpers = PORTABILITY_NONE;
49         
50         env = g_getenv ("MONO_IOMAP");
51         if (env != NULL) {
52                 /* parse the environment setting and set up some vars
53                  * here
54                  */
55                 gchar **options = g_strsplit (env, ":", 0);
56                 int i;
57                 
58                 if (options == NULL) {
59                         /* This shouldn't happen */
60                         return;
61                 }
62                 
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);
74                         }
75                 }
76         }
77 }
78
79 /* Returns newly allocated string, or NULL on failure */
80 static gchar *find_in_dir (DIR *current, const gchar *name)
81 {
82         struct dirent *entry;
83
84 #ifdef DEBUG
85         g_message ("%s: looking for [%s]\n", __func__, name);
86 #endif
87         
88         while((entry = readdir (current)) != NULL) {
89 #ifdef DEBUGX
90                 g_message ("%s: found [%s]\n", __func__, entry->d_name);
91 #endif
92                 
93                 if (!g_ascii_strcasecmp (name, entry->d_name)) {
94                         char *ret;
95                         
96 #ifdef DEBUG
97                         g_message ("%s: matched [%s] to [%s]\n", __func__,
98                                    entry->d_name, name);
99 #endif
100
101                         ret = g_strdup (entry->d_name);
102                         closedir (current);
103                         return ret;
104                 }
105         }
106         
107 #ifdef DEBUG
108         g_message ("%s: returning NULL\n", __func__);
109 #endif
110         
111         closedir (current);
112         
113         return(NULL);
114 }
115
116 static inline void append_report (GString **report, const gchar *format, ...)
117 {
118 #if GLIB_CHECK_VERSION(2,14,0)
119         va_list ap;
120         if (!*report)
121                 *report = g_string_new ("");
122
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
129 }
130
131 static inline void do_mono_profiler_iomap (GString **report, const char *pathname, const char *new_pathname)
132 {
133         char *rep = NULL;
134         GString *tmp = report ? *report : NULL;
135
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;
142         }
143
144         mono_profiler_iomap (rep, pathname, new_pathname);
145         g_free (rep);
146 }
147
148 gchar *mono_portability_find_file (const gchar *pathname, gboolean last_exists)
149 {
150         GString *report = NULL;
151         gchar *ret = mono_portability_find_file_internal (&report, pathname, last_exists);
152
153         if (report)
154                 g_string_free (report, TRUE);
155
156         return ret;
157 }
158
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)
161 {
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;
168
169         if (IS_PORTABILITY_NONE) {
170                 return(NULL);
171         }
172
173         if (do_report)
174                 append_report (report, " - Requested file path: '%s'\n", pathname);
175
176         new_pathname = g_strdup (pathname);
177         
178 #ifdef DEBUG
179         g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
180                    last_exists?"TRUE":"FALSE");
181 #endif
182         
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);
189         }
190         
191         /* First turn '\' into '/' and strip any drive letters */
192         g_strdelimit (new_pathname, "\\", '/');
193
194 #ifdef DEBUG
195         g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
196                    new_pathname);
197 #endif
198         
199         if (IS_PORTABILITY_DRIVE &&
200             g_ascii_isalpha (new_pathname[0]) &&
201             (new_pathname[1] == ':')) {
202                 int len = strlen (new_pathname);
203                 
204                 g_memmove (new_pathname, new_pathname+2, len - 2);
205                 new_pathname[len - 2] = '\0';
206
207                 if (do_report) {
208                         append_report (report, " - Stripped drive letter.\n");
209                         drive_stripped = TRUE;
210                 }
211 #ifdef DEBUG
212                 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
213                            __func__, new_pathname);
214 #endif
215         }
216
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
224         }
225
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);
233
234                 return(new_pathname);
235         }
236
237         /* OK, have to work harder.  Take each path component in turn
238          * and do a case-insensitive directory scan for it
239          */
240
241         if (!(IS_PORTABILITY_CASE)) {
242                 g_free (new_pathname);
243                 return(NULL);
244         }
245
246         components = g_strsplit (new_pathname, "/", 0);
247         if (components == NULL) {
248                 /* This shouldn't happen */
249                 g_free (new_pathname);
250                 return(NULL);
251         }
252         
253         while(components[num_components] != NULL) {
254                 num_components++;
255         }
256         g_free (new_pathname);
257         
258         if (num_components == 0){
259                 return NULL;
260         }
261         
262
263         new_components = (gchar **)g_new0 (gchar **, num_components + 1);
264
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);
277                         }
278                 
279                         new_components[component++] = g_strdup ("");
280                 } else {
281                         DIR *current;
282                         gchar *entry;
283                 
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);
293                         }
294                 
295                         entry = find_in_dir (current, components[0]);
296                         if (entry == NULL) {
297                                 g_strfreev (new_components);
298                                 g_strfreev (components);
299                                 return(NULL);
300                         }
301                 
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);
312                         }
313                 
314                         new_components[component++] = entry;
315                 }
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;
324                                 
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);
335                                 }
336                                 
337                                 entry = find_in_dir (current, components[0]);
338                                 if (entry == NULL) {
339                                         g_strfreev (new_components);
340                                         g_strfreev (components);
341                                         return(NULL);
342                                 }
343                                 
344                                 new_components[component++] = entry;
345                         }
346                 } else {
347                                 new_components[component++] = g_strdup (components[0]);
348                 }
349         }
350
351 #ifdef DEBUG
352         g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
353 #endif
354
355         g_assert (component == 1);
356         
357         for(; component < num_components; component++) {
358                 gchar *entry;
359                 gchar *path_so_far;
360                 
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);
371                         }
372                 }
373                 
374                 new_components[component] = entry;
375                 
376                 if (component < num_components -1) {
377                         path_so_far = g_strjoinv ("/", new_components);
378
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);
385                         }
386                 }
387         }
388         
389         g_strfreev (components);
390
391         new_pathname = g_strjoinv ("/", new_components);
392
393 #ifdef DEBUG
394         g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
395                    new_pathname);
396 #endif
397         
398         g_strfreev (new_components);
399
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);
405
406                 return(new_pathname);
407         }
408
409         g_free (new_pathname);
410         return(NULL);
411 }
412 #endif