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