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