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;
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_assert (num_components > 0);
193 g_free (new_pathname);
195 new_components = (gchar **)g_new0 (gchar **, num_components + 1);
197 if (num_components > 1) {
198 if (strcmp (components[0], "") == 0) {
199 /* first component blank, so start at / */
200 scanning = opendir ("/");
201 if (scanning == NULL) {
203 g_message ("%s: opendir 1 error: %s", __func__,
206 g_strfreev (new_components);
207 g_strfreev (components);
211 new_components[component++] = g_strdup ("");
216 current = opendir (".");
217 if (current == NULL) {
219 g_message ("%s: opendir 2 error: %s", __func__,
222 g_strfreev (new_components);
223 g_strfreev (components);
227 entry = find_in_dir (current, components[0]);
229 g_strfreev (new_components);
230 g_strfreev (components);
234 scanning = opendir (entry);
235 if (scanning == NULL) {
237 g_message ("%s: opendir 3 error: %s", __func__,
241 g_strfreev (new_components);
242 g_strfreev (components);
246 new_components[component++] = entry;
250 if (strcmp (components[0], "") == 0) {
251 /* First and only component blank */
252 new_components[component++] = g_strdup ("");
257 current = opendir (".");
258 if (current == NULL) {
260 g_message ("%s: opendir 4 error: %s",
264 g_strfreev (new_components);
265 g_strfreev (components);
269 entry = find_in_dir (current, components[0]);
271 g_strfreev (new_components);
272 g_strfreev (components);
276 new_components[component++] = entry;
279 new_components[component++] = g_strdup (components[0]);
284 g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
287 g_assert (component == 1);
289 for(; component < num_components; component++) {
294 component == num_components -1) {
295 entry = g_strdup (components[component]);
298 entry = find_in_dir (scanning, components[component]);
300 g_strfreev (new_components);
301 g_strfreev (components);
306 new_components[component] = entry;
308 if (component < num_components -1) {
309 path_so_far = g_strjoinv ("/", new_components);
311 scanning = opendir (path_so_far);
312 g_free (path_so_far);
313 if (scanning == NULL) {
314 g_strfreev (new_components);
315 g_strfreev (components);
321 g_strfreev (components);
323 new_pathname = g_strjoinv ("/", new_components);
326 g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
330 g_strfreev (new_components);
333 access (new_pathname, F_OK) == 0) ||
335 return(new_pathname);
338 g_free (new_pathname);
342 int _wapi_open (const char *pathname, int flags, mode_t mode)
345 gchar *located_filename;
347 if (flags & O_CREAT) {
348 located_filename = find_file (pathname, FALSE);
349 if (located_filename == NULL) {
350 fd = open (pathname, flags, mode);
352 fd = open (located_filename, flags, mode);
353 g_free (located_filename);
356 fd = open (pathname, flags, mode);
358 (errno == ENOENT || errno == ENOTDIR) &&
359 portability_helpers > 0) {
360 int saved_errno = errno;
361 located_filename = find_file (pathname, TRUE);
363 if (located_filename == NULL) {
368 fd = open (located_filename, flags, mode);
369 g_free (located_filename);
377 int _wapi_access (const char *pathname, int mode)
381 ret = access (pathname, mode);
383 (errno == ENOENT || errno == ENOTDIR) &&
384 portability_helpers > 0) {
385 int saved_errno = errno;
386 gchar *located_filename = find_file (pathname, TRUE);
388 if (located_filename == NULL) {
393 ret = access (located_filename, mode);
394 g_free (located_filename);
400 int _wapi_chmod (const char *pathname, mode_t mode)
404 ret = chmod (pathname, mode);
406 (errno == ENOENT || errno == ENOTDIR) &&
407 portability_helpers > 0) {
408 int saved_errno = errno;
409 gchar *located_filename = find_file (pathname, TRUE);
411 if (located_filename == NULL) {
416 ret = chmod (located_filename, mode);
417 g_free (located_filename);
423 int _wapi_utime (const char *filename, const struct utimbuf *buf)
427 ret = utime (filename, buf);
430 portability_helpers > 0) {
431 int saved_errno = errno;
432 gchar *located_filename = find_file (filename, TRUE);
434 if (located_filename == NULL) {
439 ret = utime (located_filename, buf);
440 g_free (located_filename);
446 int _wapi_unlink (const char *pathname)
450 ret = unlink (pathname);
452 (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) &&
453 portability_helpers > 0) {
454 int saved_errno = errno;
455 gchar *located_filename = find_file (pathname, TRUE);
457 if (located_filename == NULL) {
462 ret = unlink (located_filename);
463 g_free (located_filename);
469 int _wapi_rename (const char *oldpath, const char *newpath)
472 gchar *located_newpath = find_file (newpath, FALSE);
474 if (located_newpath == NULL) {
475 ret = rename (oldpath, newpath);
477 ret = rename (oldpath, located_newpath);
480 (errno == EISDIR || errno == ENAMETOOLONG ||
481 errno == ENOENT || errno == ENOTDIR || errno == EXDEV) &&
482 portability_helpers > 0) {
483 int saved_errno = errno;
484 gchar *located_oldpath = find_file (oldpath, TRUE);
486 if (located_oldpath == NULL) {
487 g_free (located_oldpath);
488 g_free (located_newpath);
494 ret = rename (located_oldpath, located_newpath);
495 g_free (located_oldpath);
497 g_free (located_newpath);
503 int _wapi_stat (const char *path, struct stat *buf)
507 ret = stat (path, buf);
509 (errno == ENOENT || errno == ENOTDIR) &&
510 portability_helpers > 0) {
511 int saved_errno = errno;
512 gchar *located_filename = find_file (path, TRUE);
514 if (located_filename == NULL) {
519 ret = stat (located_filename, buf);
520 g_free (located_filename);
526 int _wapi_lstat (const char *path, struct stat *buf)
530 ret = lstat (path, buf);
532 (errno == ENOENT || errno == ENOTDIR) &&
533 portability_helpers > 0) {
534 int saved_errno = errno;
535 gchar *located_filename = find_file (path, TRUE);
537 if (located_filename == NULL) {
542 ret = lstat (located_filename, buf);
543 g_free (located_filename);
549 int _wapi_mkdir (const char *pathname, mode_t mode)
552 gchar *located_filename = find_file (pathname, FALSE);
554 if (located_filename == NULL) {
555 ret = mkdir (pathname, mode);
557 ret = mkdir (located_filename, mode);
558 g_free (located_filename);
564 int _wapi_rmdir (const char *pathname)
568 ret = rmdir (pathname);
570 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
571 portability_helpers > 0) {
572 int saved_errno = errno;
573 gchar *located_filename = find_file (pathname, TRUE);
575 if (located_filename == NULL) {
580 ret = rmdir (located_filename);
581 g_free (located_filename);
587 int _wapi_chdir (const char *path)
593 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
594 portability_helpers > 0) {
595 int saved_errno = errno;
596 gchar *located_filename = find_file (path, TRUE);
598 if (located_filename == NULL) {
603 ret = chdir (located_filename);
604 g_free (located_filename);
610 gchar *_wapi_basename (const gchar *filename)
612 gchar *new_filename = g_strdup (filename), *ret;
614 mono_once (&options_once, options_init);
616 g_strdelimit (new_filename, "\\", '/');
618 if (portability_helpers & PORTABILITY_DRIVE &&
619 g_ascii_isalpha (new_filename[0]) &&
620 (new_filename[1] == ':')) {
621 int len = strlen (new_filename);
623 g_memmove (new_filename, new_filename + 2, len - 2);
624 new_filename[len - 2] = '\0';
627 ret = g_path_get_basename (new_filename);
628 g_free (new_filename);
633 gchar *_wapi_dirname (const gchar *filename)
635 gchar *new_filename = g_strdup (filename), *ret;
637 mono_once (&options_once, options_init);
639 g_strdelimit (new_filename, "\\", '/');
641 if (portability_helpers & PORTABILITY_DRIVE &&
642 g_ascii_isalpha (new_filename[0]) &&
643 (new_filename[1] == ':')) {
644 int len = strlen (new_filename);
646 g_memmove (new_filename, new_filename + 2, len - 2);
647 new_filename[len - 2] = '\0';
650 ret = g_path_get_dirname (new_filename);
651 g_free (new_filename);
656 GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
660 ret = g_dir_open (path, flags, error);
662 ((*error)->code == G_FILE_ERROR_NOENT ||
663 (*error)->code == G_FILE_ERROR_NOTDIR ||
664 (*error)->code == G_FILE_ERROR_NAMETOOLONG) &&
665 portability_helpers > 0) {
666 gchar *located_filename = find_file (path, TRUE);
669 if (located_filename == NULL) {
673 ret = g_dir_open (located_filename, flags, &tmp_error);
674 g_free (located_filename);
675 if (tmp_error != NULL) {
676 g_propagate_error (error, tmp_error);