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