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_IO_PORTABILITY_HELP");
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)) {
92 g_message ("%s: matched [%s] to [%s]\n", __func__,
97 return(g_strdup (entry->d_name));
102 g_message ("%s: returning NULL\n", __func__);
110 /* Returns newly-allocated string or NULL on failure */
111 static gchar *find_file (const gchar *pathname, gboolean last_exists)
113 gchar *new_pathname, **components, **new_components;
114 int num_components = 0, component = 0;
117 mono_once (&options_once, options_init);
119 if (portability_helpers == PORTABILITY_NONE) {
123 new_pathname = g_strdup (pathname);
126 g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
127 last_exists?"TRUE":"FALSE");
131 access (new_pathname, F_OK) == 0) {
133 g_message ("%s: Found it without doing anything\n", __func__);
135 return(new_pathname);
138 /* First turn '\' into '/' and strip any drive letters */
139 g_strdelimit (new_pathname, "\\", '/');
142 g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
146 if (portability_helpers & 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';
155 g_message ("%s: Stripped drive letter, now looking for [%s]\n",
156 __func__, new_pathname);
161 access (new_pathname, F_OK) == 0) {
163 g_message ("%s: Found it\n", __func__);
166 return(new_pathname);
169 /* OK, have to work harder. Take each path component in turn
170 * and do a case-insensitive directory scan for it
173 if (!(portability_helpers & PORTABILITY_CASE)) {
174 g_free (new_pathname);
178 components = g_strsplit (new_pathname, "/", 0);
179 if (components == NULL) {
180 /* This shouldn't happen */
181 g_free (new_pathname);
185 while(components[num_components] != NULL) {
188 g_assert (num_components > 0);
190 g_free (new_pathname);
192 new_components = (gchar **)g_new0 (gchar **, num_components + 1);
194 if (num_components > 1) {
195 if (strcmp (components[0], "") == 0) {
196 /* first component blank, so start at / */
197 scanning = opendir ("/");
198 if (scanning == NULL) {
200 g_message ("%s: opendir 1 error: %s", __func__,
203 g_strfreev (new_components);
204 g_strfreev (components);
208 new_components[component++] = g_strdup ("");
213 current = opendir (".");
214 if (current == NULL) {
216 g_message ("%s: opendir 2 error: %s", __func__,
219 g_strfreev (new_components);
220 g_strfreev (components);
224 entry = find_in_dir (current, components[0]);
226 g_strfreev (new_components);
227 g_strfreev (components);
231 scanning = opendir (entry);
232 if (scanning == NULL) {
234 g_message ("%s: opendir 3 error: %s", __func__,
238 g_strfreev (new_components);
239 g_strfreev (components);
243 new_components[component++] = entry;
247 if (strcmp (components[0], "") == 0) {
248 /* First and only component blank */
249 new_components[component++] = g_strdup ("");
254 current = opendir (".");
255 if (current == NULL) {
257 g_message ("%s: opendir 4 error: %s",
261 g_strfreev (new_components);
262 g_strfreev (components);
266 entry = find_in_dir (current, components[0]);
268 g_strfreev (new_components);
269 g_strfreev (components);
273 new_components[component++] = entry;
276 new_components[component++] = g_strdup (components[0]);
281 g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
284 g_assert (component == 1);
286 for(; component < num_components; component++) {
291 component == num_components -1) {
292 entry = g_strdup (components[component]);
295 entry = find_in_dir (scanning, components[component]);
297 g_strfreev (new_components);
298 g_strfreev (components);
303 new_components[component] = entry;
305 if (component < num_components -1) {
306 path_so_far = g_strjoinv ("/", new_components);
308 scanning = opendir (path_so_far);
309 g_free (path_so_far);
310 if (scanning == NULL) {
311 g_strfreev (new_components);
312 g_strfreev (components);
318 g_strfreev (components);
320 new_pathname = g_strjoinv ("/", new_components);
323 g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
327 g_strfreev (new_components);
330 access (new_pathname, F_OK) == 0) ||
332 return(new_pathname);
335 g_free (new_pathname);
339 int _wapi_open (const char *pathname, int flags, mode_t mode)
342 gchar *located_filename;
344 if (flags & O_CREAT) {
345 located_filename = find_file (pathname, FALSE);
346 if (located_filename == NULL) {
347 fd = open (pathname, flags, mode);
349 fd = open (located_filename, flags, mode);
350 g_free (located_filename);
353 fd = open (pathname, flags, mode);
355 (errno == ENOENT || errno == ENOTDIR) &&
356 portability_helpers > 0) {
357 int saved_errno = errno;
358 located_filename = find_file (pathname, TRUE);
360 if (located_filename == NULL) {
365 fd = open (located_filename, flags, mode);
366 g_free (located_filename);
374 int _wapi_access (const char *pathname, int mode)
378 ret = access (pathname, mode);
380 (errno == ENOENT || errno == ENOTDIR) &&
381 portability_helpers > 0) {
382 int saved_errno = errno;
383 gchar *located_filename = find_file (pathname, TRUE);
385 if (located_filename == NULL) {
390 ret = access (located_filename, mode);
391 g_free (located_filename);
397 int _wapi_chmod (const char *pathname, mode_t mode)
401 ret = chmod (pathname, mode);
403 (errno == ENOENT || errno == ENOTDIR) &&
404 portability_helpers > 0) {
405 int saved_errno = errno;
406 gchar *located_filename = find_file (pathname, TRUE);
408 if (located_filename == NULL) {
413 ret = chmod (located_filename, mode);
414 g_free (located_filename);
420 int _wapi_utime (const char *filename, const struct utimbuf *buf)
424 ret = utime (filename, buf);
427 portability_helpers > 0) {
428 int saved_errno = errno;
429 gchar *located_filename = find_file (filename, TRUE);
431 if (located_filename == NULL) {
436 ret = utime (located_filename, buf);
437 g_free (located_filename);
443 int _wapi_unlink (const char *pathname)
447 ret = unlink (pathname);
449 (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) &&
450 portability_helpers > 0) {
451 int saved_errno = errno;
452 gchar *located_filename = find_file (pathname, TRUE);
454 if (located_filename == NULL) {
459 ret = unlink (located_filename);
460 g_free (located_filename);
466 int _wapi_rename (const char *oldpath, const char *newpath)
469 gchar *located_newpath = find_file (newpath, FALSE);
471 if (located_newpath == NULL) {
472 ret = rename (oldpath, newpath);
474 ret = rename (oldpath, located_newpath);
477 (errno == EISDIR || errno == ENAMETOOLONG ||
478 errno == ENOENT || errno == ENOTDIR || errno == EXDEV) &&
479 portability_helpers > 0) {
480 int saved_errno = errno;
481 gchar *located_oldpath = find_file (oldpath, TRUE);
483 if (located_oldpath == NULL) {
484 g_free (located_oldpath);
485 g_free (located_newpath);
491 ret = rename (located_oldpath, located_newpath);
492 g_free (located_oldpath);
494 g_free (located_newpath);
500 int _wapi_stat (const char *path, struct stat *buf)
504 ret = stat (path, buf);
506 (errno == ENOENT || errno == ENOTDIR) &&
507 portability_helpers > 0) {
508 int saved_errno = errno;
509 gchar *located_filename = find_file (path, TRUE);
511 if (located_filename == NULL) {
516 ret = stat (located_filename, buf);
517 g_free (located_filename);
523 int _wapi_lstat (const char *path, struct stat *buf)
527 ret = lstat (path, buf);
529 (errno == ENOENT || errno == ENOTDIR) &&
530 portability_helpers > 0) {
531 int saved_errno = errno;
532 gchar *located_filename = find_file (path, TRUE);
534 if (located_filename == NULL) {
539 ret = lstat (located_filename, buf);
540 g_free (located_filename);
546 int _wapi_mkdir (const char *pathname, mode_t mode)
549 gchar *located_filename = find_file (pathname, FALSE);
551 if (located_filename == NULL) {
552 ret = mkdir (pathname, mode);
554 ret = mkdir (located_filename, mode);
555 g_free (located_filename);
561 int _wapi_rmdir (const char *pathname)
565 ret = rmdir (pathname);
567 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
568 portability_helpers > 0) {
569 int saved_errno = errno;
570 gchar *located_filename = find_file (pathname, TRUE);
572 if (located_filename == NULL) {
577 ret = rmdir (located_filename);
578 g_free (located_filename);
584 int _wapi_chdir (const char *path)
590 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
591 portability_helpers > 0) {
592 int saved_errno = errno;
593 gchar *located_filename = find_file (path, TRUE);
595 if (located_filename == NULL) {
600 ret = chdir (located_filename);
601 g_free (located_filename);
607 gchar *_wapi_basename (const gchar *filename)
609 gchar *new_filename = g_strdup (filename), *ret;
611 mono_once (&options_once, options_init);
613 g_strdelimit (new_filename, "\\", '/');
615 if (portability_helpers & PORTABILITY_DRIVE &&
616 g_ascii_isalpha (new_filename[0]) &&
617 (new_filename[1] == ':')) {
618 int len = strlen (new_filename);
620 g_memmove (new_filename, new_filename + 2, len - 2);
621 new_filename[len - 2] = '\0';
624 ret = g_path_get_basename (new_filename);
625 g_free (new_filename);
630 gchar *_wapi_dirname (const gchar *filename)
632 gchar *new_filename = g_strdup (filename), *ret;
634 mono_once (&options_once, options_init);
636 g_strdelimit (new_filename, "\\", '/');
638 if (portability_helpers & PORTABILITY_DRIVE &&
639 g_ascii_isalpha (new_filename[0]) &&
640 (new_filename[1] == ':')) {
641 int len = strlen (new_filename);
643 g_memmove (new_filename, new_filename + 2, len - 2);
644 new_filename[len - 2] = '\0';
647 ret = g_path_get_dirname (new_filename);
648 g_free (new_filename);
653 GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
657 ret = g_dir_open (path, flags, error);
659 ((*error)->code == G_FILE_ERROR_NOENT ||
660 (*error)->code == G_FILE_ERROR_NOTDIR ||
661 (*error)->code == G_FILE_ERROR_NAMETOOLONG) &&
662 portability_helpers > 0) {
663 gchar *located_filename = find_file (path, TRUE);
666 if (located_filename == NULL) {
670 ret = g_dir_open (located_filename, flags, &tmp_error);
671 g_free (located_filename);
672 if (tmp_error != NULL) {
673 g_propagate_error (error, tmp_error);