Added Symbolicate tool.
[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 #ifndef DISABLE_PORTABILITY
18
19 #include <dirent.h>
20
21 int __mono_io_portability_helpers = PORTABILITY_UNKNOWN;
22
23 static inline gchar *mono_portability_find_file_internal (GString **report, const gchar *pathname, gboolean last_exists);
24
25 void mono_portability_helpers_init (void)
26 {
27         const gchar *env;
28
29         if (__mono_io_portability_helpers != PORTABILITY_UNKNOWN)
30                 return;
31         
32         __mono_io_portability_helpers = PORTABILITY_NONE;
33         
34         env = g_getenv ("MONO_IOMAP");
35         if (env != NULL) {
36                 /* parse the environment setting and set up some vars
37                  * here
38                  */
39                 gchar **options = g_strsplit (env, ":", 0);
40                 int i;
41                 
42                 if (options == NULL) {
43                         /* This shouldn't happen */
44                         return;
45                 }
46                 
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);
58                         }
59                 }
60         }
61 }
62
63 /* Returns newly allocated string, or NULL on failure */
64 static gchar *find_in_dir (DIR *current, const gchar *name)
65 {
66         struct dirent *entry;
67
68 #ifdef DEBUG
69         g_message ("%s: looking for [%s]\n", __func__, name);
70 #endif
71         
72         while((entry = readdir (current)) != NULL) {
73 #ifdef DEBUGX
74                 g_message ("%s: found [%s]\n", __func__, entry->d_name);
75 #endif
76                 
77                 if (!g_ascii_strcasecmp (name, entry->d_name)) {
78                         char *ret;
79                         
80 #ifdef DEBUG
81                         g_message ("%s: matched [%s] to [%s]\n", __func__,
82                                    entry->d_name, name);
83 #endif
84
85                         ret = g_strdup (entry->d_name);
86                         closedir (current);
87                         return ret;
88                 }
89         }
90         
91 #ifdef DEBUG
92         g_message ("%s: returning NULL\n", __func__);
93 #endif
94         
95         closedir (current);
96         
97         return(NULL);
98 }
99
100 static inline void append_report (GString **report, const gchar *format, ...)
101 {
102 #if defined (_EGLIB_MAJOR) || GLIB_CHECK_VERSION(2,14,0)
103         va_list ap;
104         if (!*report)
105                 *report = g_string_new ("");
106
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
113 }
114
115 static inline void do_mono_profiler_iomap (GString **report, const char *pathname, const char *new_pathname)
116 {
117         char *rep = NULL;
118         GString *tmp = report ? *report : NULL;
119
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;
126         }
127
128         mono_profiler_iomap (rep, pathname, new_pathname);
129         g_free (rep);
130 }
131
132 gchar *mono_portability_find_file (const gchar *pathname, gboolean last_exists)
133 {
134         GString *report = NULL;
135         gchar *ret;
136         
137         if (!pathname || !pathname [0])
138                 return NULL;
139         ret = mono_portability_find_file_internal (&report, pathname, last_exists);
140
141         if (report)
142                 g_string_free (report, TRUE);
143
144         return ret;
145 }
146
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)
149 {
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;
156
157         if (IS_PORTABILITY_NONE) {
158                 return(NULL);
159         }
160
161         if (do_report)
162                 append_report (report, " - Requested file path: '%s'\n", pathname);
163
164         new_pathname = g_strdup (pathname);
165         
166 #ifdef DEBUG
167         g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
168                    last_exists?"TRUE":"FALSE");
169 #endif
170         
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);
177         }
178         
179         /* First turn '\' into '/' and strip any drive letters */
180         g_strdelimit (new_pathname, "\\", '/');
181
182 #ifdef DEBUG
183         g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
184                    new_pathname);
185 #endif
186         
187         if (IS_PORTABILITY_DRIVE &&
188             g_ascii_isalpha (new_pathname[0]) &&
189             (new_pathname[1] == ':')) {
190                 int len = strlen (new_pathname);
191                 
192                 g_memmove (new_pathname, new_pathname+2, len - 2);
193                 new_pathname[len - 2] = '\0';
194
195                 if (do_report) {
196                         append_report (report, " - Stripped drive letter.\n");
197                         drive_stripped = TRUE;
198                 }
199 #ifdef DEBUG
200                 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
201                            __func__, new_pathname);
202 #endif
203         }
204
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
212         }
213
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);
221
222                 return(new_pathname);
223         }
224
225         /* OK, have to work harder.  Take each path component in turn
226          * and do a case-insensitive directory scan for it
227          */
228
229         if (!(IS_PORTABILITY_CASE)) {
230                 g_free (new_pathname);
231                 return(NULL);
232         }
233
234         components = g_strsplit (new_pathname, "/", 0);
235         if (components == NULL) {
236                 /* This shouldn't happen */
237                 g_free (new_pathname);
238                 return(NULL);
239         }
240         
241         while(components[num_components] != NULL) {
242                 num_components++;
243         }
244         g_free (new_pathname);
245         
246         if (num_components == 0){
247                 return NULL;
248         }
249         
250
251         new_components = (gchar **)g_new0 (gchar **, num_components + 1);
252
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);
265                         }
266                 
267                         new_components[component++] = g_strdup ("");
268                 } else {
269                         DIR *current;
270                         gchar *entry;
271                 
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);
281                         }
282                 
283                         entry = find_in_dir (current, components[0]);
284                         if (entry == NULL) {
285                                 g_strfreev (new_components);
286                                 g_strfreev (components);
287                                 return(NULL);
288                         }
289                 
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);
300                         }
301                 
302                         new_components[component++] = entry;
303                 }
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;
312                                 
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);
323                                 }
324                                 
325                                 entry = find_in_dir (current, components[0]);
326                                 if (entry == NULL) {
327                                         g_strfreev (new_components);
328                                         g_strfreev (components);
329                                         return(NULL);
330                                 }
331                                 
332                                 new_components[component++] = entry;
333                         }
334                 } else {
335                                 new_components[component++] = g_strdup (components[0]);
336                 }
337         }
338
339 #ifdef DEBUG
340         g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
341 #endif
342
343         g_assert (component == 1);
344         
345         for(; component < num_components; component++) {
346                 gchar *entry;
347                 gchar *path_so_far;
348                 
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);
359                         }
360                 }
361                 
362                 new_components[component] = entry;
363                 
364                 if (component < num_components -1) {
365                         path_so_far = g_strjoinv ("/", new_components);
366
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);
373                         }
374                 }
375         }
376         
377         g_strfreev (components);
378
379         new_pathname = g_strjoinv ("/", new_components);
380
381 #ifdef DEBUG
382         g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
383                    new_pathname);
384 #endif
385         
386         g_strfreev (new_components);
387
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);
393
394                 return(new_pathname);
395         }
396
397         g_free (new_pathname);
398         return(NULL);
399 }
400 #endif