Update Haiku support (#15674)
[mono-project.git] / mono / utils / mono-io-portability.c
blob9231320780f3c0e85fde5a9a0f46a51b89b9139b
1 /**
2 * \file
3 */
5 #include "config.h"
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>
16 #ifndef DISABLE_PORTABILITY
18 #include <dirent.h>
20 int mono_io_portability_helpers = PORTABILITY_UNKNOWN;
22 static inline gchar *mono_portability_find_file_internal (const gchar *pathname, gboolean last_exists);
24 void mono_portability_helpers_init (void)
26 gchar *env;
28 if (mono_io_portability_helpers != PORTABILITY_UNKNOWN)
29 return;
31 mono_io_portability_helpers = PORTABILITY_NONE;
33 env = g_getenv ("MONO_IOMAP");
34 if (env != NULL) {
35 /* parse the environment setting and set up some vars
36 * here
38 gchar **options = g_strsplit (env, ":", 0);
39 int i;
41 if (options == NULL) {
42 /* This shouldn't happen */
43 return;
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);
59 g_free (env);
63 /* Returns newly allocated string, or NULL on failure */
64 static gchar *find_in_dir (DIR *current, const gchar *name)
66 struct dirent *entry;
68 #ifdef DEBUG
69 g_message ("%s: looking for [%s]\n", __func__, name);
70 #endif
72 while((entry = readdir (current)) != NULL) {
73 #ifdef DEBUGX
74 g_message ("%s: found [%s]\n", __func__, entry->d_name);
75 #endif
77 if (!g_ascii_strcasecmp (name, entry->d_name)) {
78 char *ret;
80 #ifdef DEBUG
81 g_message ("%s: matched [%s] to [%s]\n", __func__,
82 entry->d_name, name);
83 #endif
85 ret = g_strdup (entry->d_name);
86 closedir (current);
87 return ret;
91 #ifdef DEBUG
92 g_message ("%s: returning NULL\n", __func__);
93 #endif
95 closedir (current);
97 return(NULL);
100 gchar *mono_portability_find_file (const gchar *pathname, gboolean last_exists)
102 gchar *ret;
104 if (!pathname || !pathname [0])
105 return NULL;
106 ret = mono_portability_find_file_internal (pathname, last_exists);
108 return ret;
111 /* Returns newly-allocated string or NULL on failure */
112 static inline gchar *mono_portability_find_file_internal (const gchar *pathname, gboolean last_exists)
114 gchar *new_pathname, **components, **new_components;
115 int num_components = 0, component = 0;
116 DIR *scanning = NULL;
117 size_t len;
119 if (IS_PORTABILITY_NONE) {
120 return(NULL);
123 new_pathname = g_strdup (pathname);
125 #ifdef DEBUG
126 g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
127 last_exists?"TRUE":"FALSE");
128 #endif
130 if (last_exists &&
131 access (new_pathname, F_OK) == 0) {
132 #ifdef DEBUG
133 g_message ("%s: Found it without doing anything\n", __func__);
134 #endif
135 return(new_pathname);
138 /* First turn '\' into '/' and strip any drive letters */
139 g_strdelimit (new_pathname, '\\', '/');
141 #ifdef DEBUG
142 g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
143 new_pathname);
144 #endif
146 if (IS_PORTABILITY_DRIVE &&
147 g_ascii_isalpha (new_pathname[0]) &&
148 (new_pathname[1] == ':')) {
149 int len = strlen (new_pathname);
151 g_memmove (new_pathname, new_pathname+2, len - 2);
152 new_pathname[len - 2] = '\0';
153 #ifdef DEBUG
154 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
155 __func__, new_pathname);
156 #endif
159 len = strlen (new_pathname);
160 if (len > 1 && new_pathname [len - 1] == '/') {
161 new_pathname [len - 1] = 0;
162 #ifdef DEBUG
163 g_message ("%s: requested name had a trailing /, rewritten to '%s'\n",
164 __func__, new_pathname);
165 #endif
168 if (last_exists &&
169 access (new_pathname, F_OK) == 0) {
170 #ifdef DEBUG
171 g_message ("%s: Found it\n", __func__);
172 #endif
174 return(new_pathname);
177 /* OK, have to work harder. Take each path component in turn
178 * and do a case-insensitive directory scan for it
181 if (!(IS_PORTABILITY_CASE)) {
182 g_free (new_pathname);
183 return(NULL);
186 components = g_strsplit (new_pathname, "/", 0);
187 if (components == NULL) {
188 /* This shouldn't happen */
189 g_free (new_pathname);
190 return(NULL);
193 while(components[num_components] != NULL) {
194 num_components++;
196 g_free (new_pathname);
198 if (num_components == 0){
199 return NULL;
203 new_components = (gchar **)g_new0 (gchar **, num_components + 1);
205 if (num_components > 1) {
206 if (strcmp (components[0], "") == 0) {
207 /* first component blank, so start at / */
208 scanning = opendir ("/");
209 if (scanning == NULL) {
210 #ifdef DEBUG
211 g_message ("%s: opendir 1 error: %s", __func__,
212 g_strerror (errno));
213 #endif
214 g_strfreev (new_components);
215 g_strfreev (components);
216 return(NULL);
219 new_components[component++] = g_strdup ("");
220 } else {
221 DIR *current;
222 gchar *entry;
224 current = opendir (".");
225 if (current == NULL) {
226 #ifdef DEBUG
227 g_message ("%s: opendir 2 error: %s", __func__,
228 g_strerror (errno));
229 #endif
230 g_strfreev (new_components);
231 g_strfreev (components);
232 return(NULL);
235 entry = find_in_dir (current, components[0]);
236 if (entry == NULL) {
237 g_strfreev (new_components);
238 g_strfreev (components);
239 return(NULL);
242 scanning = opendir (entry);
243 if (scanning == NULL) {
244 #ifdef DEBUG
245 g_message ("%s: opendir 3 error: %s", __func__,
246 g_strerror (errno));
247 #endif
248 g_free (entry);
249 g_strfreev (new_components);
250 g_strfreev (components);
251 return(NULL);
254 new_components[component++] = entry;
256 } else {
257 if (last_exists) {
258 if (strcmp (components[0], "") == 0) {
259 /* First and only component blank */
260 new_components[component++] = g_strdup ("");
261 } else {
262 DIR *current;
263 gchar *entry;
265 current = opendir (".");
266 if (current == NULL) {
267 #ifdef DEBUG
268 g_message ("%s: opendir 4 error: %s",
269 __func__,
270 g_strerror (errno));
271 #endif
272 g_strfreev (new_components);
273 g_strfreev (components);
274 return(NULL);
277 entry = find_in_dir (current, components[0]);
278 if (entry == NULL) {
279 g_strfreev (new_components);
280 g_strfreev (components);
281 return(NULL);
284 new_components[component++] = entry;
286 } else {
287 new_components[component++] = g_strdup (components[0]);
291 #ifdef DEBUG
292 g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
293 #endif
295 g_assert (component == 1);
297 for(; component < num_components; component++) {
298 gchar *entry;
299 gchar *path_so_far;
301 if (!last_exists &&
302 component == num_components -1) {
303 entry = g_strdup (components[component]);
304 closedir (scanning);
305 } else {
306 entry = find_in_dir (scanning, components[component]);
307 if (entry == NULL) {
308 g_strfreev (new_components);
309 g_strfreev (components);
310 return(NULL);
314 new_components[component] = entry;
316 if (component < num_components -1) {
317 path_so_far = g_strjoinv ("/", new_components);
319 scanning = opendir (path_so_far);
320 g_free (path_so_far);
321 if (scanning == NULL) {
322 g_strfreev (new_components);
323 g_strfreev (components);
324 return(NULL);
329 g_strfreev (components);
331 new_pathname = g_strjoinv ("/", new_components);
333 #ifdef DEBUG
334 g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
335 new_pathname);
336 #endif
338 g_strfreev (new_components);
340 if ((last_exists &&
341 access (new_pathname, F_OK) == 0) ||
342 (!last_exists)) {
343 return(new_pathname);
346 g_free (new_pathname);
347 return(NULL);
350 #else /* DISABLE_PORTABILITY */
352 MONO_EMPTY_SOURCE_FILE (mono_io_portability);
354 #endif /* DISABLE_PORTABILITY */