2 * io-portability.c: Optional filename mangling to try to cope with
3 * badly-written non-portable windows apps
6 * Dick Porter (dick@ximian.com)
8 * Copyright (c) 2006 Novell, Inc.
20 #include <sys/types.h>
25 #include <mono/io-layer/mono-mutex.h>
26 #include <mono/io-layer/io-portability.h>
31 PORTABILITY_NONE = 0x00,
32 PORTABILITY_UNKNOWN = 0x01,
33 PORTABILITY_DRIVE = 0x02,
34 PORTABILITY_CASE = 0x04,
37 static mono_once_t options_once = MONO_ONCE_INIT;
38 static int portability_helpers = PORTABILITY_UNKNOWN;
40 static void options_init (void)
44 portability_helpers = PORTABILITY_NONE;
46 env = g_getenv ("MONO_IOMAP");
48 /* parse the environment setting and set up some vars
51 gchar **options = g_strsplit (env, ":", 0);
54 if (options == NULL) {
55 /* This shouldn't happen */
59 for (i = 0; options[i] != NULL; i++) {
61 g_message ("%s: Setting option [%s]", __func__,
64 if (!strncasecmp (options[i], "drive", 5)) {
65 portability_helpers |= PORTABILITY_DRIVE;
66 } else if (!strncasecmp (options[i], "case", 4)) {
67 portability_helpers |= PORTABILITY_CASE;
68 } else if (!strncasecmp (options[i], "all", 3)) {
69 portability_helpers |= (PORTABILITY_DRIVE |
76 /* Returns newly allocated string, or NULL on failure */
77 static gchar *find_in_dir (DIR *current, const gchar *name)
82 g_message ("%s: looking for [%s]\n", __func__, name);
85 while((entry = readdir (current)) != NULL) {
87 g_message ("%s: found [%s]\n", __func__, entry->d_name);
90 if (!g_ascii_strcasecmp (name, entry->d_name)) {
94 g_message ("%s: matched [%s] to [%s]\n", __func__,
98 ret = g_strdup (entry->d_name);
105 g_message ("%s: returning NULL\n", __func__);
113 /* Returns newly-allocated string or NULL on failure */
114 static gchar *find_file (const gchar *pathname, gboolean last_exists)
116 gchar *new_pathname, **components, **new_components;
117 int num_components = 0, component = 0;
118 DIR *scanning = NULL;
120 mono_once (&options_once, options_init);
122 if (portability_helpers == PORTABILITY_NONE) {
126 new_pathname = g_strdup (pathname);
129 g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
130 last_exists?"TRUE":"FALSE");
134 access (new_pathname, F_OK) == 0) {
136 g_message ("%s: Found it without doing anything\n", __func__);
138 return(new_pathname);
141 /* First turn '\' into '/' and strip any drive letters */
142 g_strdelimit (new_pathname, "\\", '/');
145 g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
149 if (portability_helpers & PORTABILITY_DRIVE &&
150 g_ascii_isalpha (new_pathname[0]) &&
151 (new_pathname[1] == ':')) {
152 int len = strlen (new_pathname);
154 g_memmove (new_pathname, new_pathname+2, len - 2);
155 new_pathname[len - 2] = '\0';
158 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
159 __func__, new_pathname);
164 access (new_pathname, F_OK) == 0) {
166 g_message ("%s: Found it\n", __func__);
169 return(new_pathname);
172 /* OK, have to work harder. Take each path component in turn
173 * and do a case-insensitive directory scan for it
176 if (!(portability_helpers & PORTABILITY_CASE)) {
177 g_free (new_pathname);
181 components = g_strsplit (new_pathname, "/", 0);
182 if (components == NULL) {
183 /* This shouldn't happen */
184 g_free (new_pathname);
188 while(components[num_components] != NULL) {
191 g_free (new_pathname);
193 if (num_components == 0){
198 new_components = (gchar **)g_new0 (gchar **, num_components + 1);
200 if (num_components > 1) {
201 if (strcmp (components[0], "") == 0) {
202 /* first component blank, so start at / */
203 scanning = opendir ("/");
204 if (scanning == NULL) {
206 g_message ("%s: opendir 1 error: %s", __func__,
209 g_strfreev (new_components);
210 g_strfreev (components);
214 new_components[component++] = g_strdup ("");
219 current = opendir (".");
220 if (current == NULL) {
222 g_message ("%s: opendir 2 error: %s", __func__,
225 g_strfreev (new_components);
226 g_strfreev (components);
230 entry = find_in_dir (current, components[0]);
232 g_strfreev (new_components);
233 g_strfreev (components);
237 scanning = opendir (entry);
238 if (scanning == NULL) {
240 g_message ("%s: opendir 3 error: %s", __func__,
244 g_strfreev (new_components);
245 g_strfreev (components);
249 new_components[component++] = entry;
253 if (strcmp (components[0], "") == 0) {
254 /* First and only component blank */
255 new_components[component++] = g_strdup ("");
260 current = opendir (".");
261 if (current == NULL) {
263 g_message ("%s: opendir 4 error: %s",
267 g_strfreev (new_components);
268 g_strfreev (components);
272 entry = find_in_dir (current, components[0]);
274 g_strfreev (new_components);
275 g_strfreev (components);
279 new_components[component++] = entry;
282 new_components[component++] = g_strdup (components[0]);
287 g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
290 g_assert (component == 1);
292 for(; component < num_components; component++) {
297 component == num_components -1) {
298 entry = g_strdup (components[component]);
301 entry = find_in_dir (scanning, components[component]);
303 g_strfreev (new_components);
304 g_strfreev (components);
309 new_components[component] = entry;
311 if (component < num_components -1) {
312 path_so_far = g_strjoinv ("/", new_components);
314 scanning = opendir (path_so_far);
315 g_free (path_so_far);
316 if (scanning == NULL) {
317 g_strfreev (new_components);
318 g_strfreev (components);
324 g_strfreev (components);
326 new_pathname = g_strjoinv ("/", new_components);
329 g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
333 g_strfreev (new_components);
336 access (new_pathname, F_OK) == 0) ||
338 return(new_pathname);
341 g_free (new_pathname);
345 int _wapi_open (const char *pathname, int flags, mode_t mode)
348 gchar *located_filename;
350 if (flags & O_CREAT) {
351 located_filename = find_file (pathname, FALSE);
352 if (located_filename == NULL) {
353 fd = open (pathname, flags, mode);
355 fd = open (located_filename, flags, mode);
356 g_free (located_filename);
359 fd = open (pathname, flags, mode);
361 (errno == ENOENT || errno == ENOTDIR) &&
362 portability_helpers > 0) {
363 int saved_errno = errno;
364 located_filename = find_file (pathname, TRUE);
366 if (located_filename == NULL) {
371 fd = open (located_filename, flags, mode);
372 g_free (located_filename);
380 int _wapi_access (const char *pathname, int mode)
384 ret = access (pathname, mode);
386 (errno == ENOENT || errno == ENOTDIR) &&
387 portability_helpers > 0) {
388 int saved_errno = errno;
389 gchar *located_filename = find_file (pathname, TRUE);
391 if (located_filename == NULL) {
396 ret = access (located_filename, mode);
397 g_free (located_filename);
403 int _wapi_chmod (const char *pathname, mode_t mode)
407 ret = chmod (pathname, mode);
409 (errno == ENOENT || errno == ENOTDIR) &&
410 portability_helpers > 0) {
411 int saved_errno = errno;
412 gchar *located_filename = find_file (pathname, TRUE);
414 if (located_filename == NULL) {
419 ret = chmod (located_filename, mode);
420 g_free (located_filename);
426 int _wapi_utime (const char *filename, const struct utimbuf *buf)
430 ret = utime (filename, buf);
433 portability_helpers > 0) {
434 int saved_errno = errno;
435 gchar *located_filename = find_file (filename, TRUE);
437 if (located_filename == NULL) {
442 ret = utime (located_filename, buf);
443 g_free (located_filename);
449 int _wapi_unlink (const char *pathname)
453 ret = unlink (pathname);
455 (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) &&
456 portability_helpers > 0) {
457 int saved_errno = errno;
458 gchar *located_filename = find_file (pathname, TRUE);
460 if (located_filename == NULL) {
465 ret = unlink (located_filename);
466 g_free (located_filename);
472 int _wapi_rename (const char *oldpath, const char *newpath)
475 gchar *located_newpath = find_file (newpath, FALSE);
477 if (located_newpath == NULL) {
478 ret = rename (oldpath, newpath);
480 ret = rename (oldpath, located_newpath);
483 (errno == EISDIR || errno == ENAMETOOLONG ||
484 errno == ENOENT || errno == ENOTDIR || errno == EXDEV) &&
485 portability_helpers > 0) {
486 int saved_errno = errno;
487 gchar *located_oldpath = find_file (oldpath, TRUE);
489 if (located_oldpath == NULL) {
490 g_free (located_oldpath);
491 g_free (located_newpath);
497 ret = rename (located_oldpath, located_newpath);
498 g_free (located_oldpath);
500 g_free (located_newpath);
506 int _wapi_stat (const char *path, struct stat *buf)
510 ret = stat (path, buf);
512 (errno == ENOENT || errno == ENOTDIR) &&
513 portability_helpers > 0) {
514 int saved_errno = errno;
515 gchar *located_filename = find_file (path, TRUE);
517 if (located_filename == NULL) {
522 ret = stat (located_filename, buf);
523 g_free (located_filename);
529 int _wapi_lstat (const char *path, struct stat *buf)
533 ret = lstat (path, buf);
535 (errno == ENOENT || errno == ENOTDIR) &&
536 portability_helpers > 0) {
537 int saved_errno = errno;
538 gchar *located_filename = find_file (path, TRUE);
540 if (located_filename == NULL) {
545 ret = lstat (located_filename, buf);
546 g_free (located_filename);
552 int _wapi_mkdir (const char *pathname, mode_t mode)
555 gchar *located_filename = find_file (pathname, FALSE);
557 if (located_filename == NULL) {
558 ret = mkdir (pathname, mode);
560 ret = mkdir (located_filename, mode);
561 g_free (located_filename);
567 int _wapi_rmdir (const char *pathname)
571 ret = rmdir (pathname);
573 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
574 portability_helpers > 0) {
575 int saved_errno = errno;
576 gchar *located_filename = find_file (pathname, TRUE);
578 if (located_filename == NULL) {
583 ret = rmdir (located_filename);
584 g_free (located_filename);
590 int _wapi_chdir (const char *path)
596 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
597 portability_helpers > 0) {
598 int saved_errno = errno;
599 gchar *located_filename = find_file (path, TRUE);
601 if (located_filename == NULL) {
606 ret = chdir (located_filename);
607 g_free (located_filename);
613 gchar *_wapi_basename (const gchar *filename)
615 gchar *new_filename = g_strdup (filename), *ret;
617 mono_once (&options_once, options_init);
619 g_strdelimit (new_filename, "\\", '/');
621 if (portability_helpers & PORTABILITY_DRIVE &&
622 g_ascii_isalpha (new_filename[0]) &&
623 (new_filename[1] == ':')) {
624 int len = strlen (new_filename);
626 g_memmove (new_filename, new_filename + 2, len - 2);
627 new_filename[len - 2] = '\0';
630 ret = g_path_get_basename (new_filename);
631 g_free (new_filename);
636 gchar *_wapi_dirname (const gchar *filename)
638 gchar *new_filename = g_strdup (filename), *ret;
640 mono_once (&options_once, options_init);
642 g_strdelimit (new_filename, "\\", '/');
644 if (portability_helpers & PORTABILITY_DRIVE &&
645 g_ascii_isalpha (new_filename[0]) &&
646 (new_filename[1] == ':')) {
647 int len = strlen (new_filename);
649 g_memmove (new_filename, new_filename + 2, len - 2);
650 new_filename[len - 2] = '\0';
653 ret = g_path_get_dirname (new_filename);
654 g_free (new_filename);
659 GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
663 ret = g_dir_open (path, flags, error);
665 ((*error)->code == G_FILE_ERROR_NOENT ||
666 (*error)->code == G_FILE_ERROR_NOTDIR ||
667 (*error)->code == G_FILE_ERROR_NAMETOOLONG) &&
668 portability_helpers > 0) {
669 gchar *located_filename = find_file (path, TRUE);
672 if (located_filename == NULL) {
676 ret = g_dir_open (located_filename, flags, &tmp_error);
677 g_free (located_filename);
678 if (tmp_error != NULL) {
679 g_propagate_error (error, tmp_error);