Merge pull request #2802 from BrzVlad/feature-evacuation-opt2
[mono.git] / mono / io-layer / io-portability.c
index 0cb30805a42d347392d46d55185d3d496dcc0cfa..282cbe93a29b8e6f60923db0fafba329529ac924 100644 (file)
@@ -6,6 +6,7 @@
  *     Dick Porter (dick@ximian.com)
  *
  * Copyright (c) 2006 Novell, Inc.
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 
 #include <config.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/types.h>
-#include <dirent.h>
+#include <sys/time.h>
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+#endif
 #include <utime.h>
 #include <sys/stat.h>
 
-#include <mono/io-layer/mono-mutex.h>
 #include <mono/io-layer/error.h>
 #include <mono/io-layer/wapi_glob.h>
 #include <mono/io-layer/io-portability.h>
+#include <mono/utils/mono-io-portability.h>
 
 #undef DEBUG
 
-enum {
-       PORTABILITY_NONE        = 0x00,
-       PORTABILITY_UNKNOWN     = 0x01,
-       PORTABILITY_DRIVE       = 0x02,
-       PORTABILITY_CASE        = 0x04,
-};
-
-static mono_once_t options_once = MONO_ONCE_INIT;
-static int portability_helpers = PORTABILITY_UNKNOWN;
-
-static void options_init (void)
-{
-       const gchar *env;
-       
-       portability_helpers = PORTABILITY_NONE;
-       
-       env = g_getenv ("MONO_IOMAP");
-       if (env != NULL) {
-               /* parse the environment setting and set up some vars
-                * here
-                */
-               gchar **options = g_strsplit (env, ":", 0);
-               int i;
-               
-               if (options == NULL) {
-                       /* This shouldn't happen */
-                       return;
-               }
-               
-               for (i = 0; options[i] != NULL; i++) {
-#ifdef DEBUG
-                       g_message ("%s: Setting option [%s]", __func__,
-                                  options[i]);
-#endif
-                       if (!strncasecmp (options[i], "drive", 5)) {
-                               portability_helpers |= PORTABILITY_DRIVE;
-                       } else if (!strncasecmp (options[i], "case", 4)) {
-                               portability_helpers |= PORTABILITY_CASE;
-                       } else if (!strncasecmp (options[i], "all", 3)) {
-                               portability_helpers |= (PORTABILITY_DRIVE |
-                                                       PORTABILITY_CASE);
-                       }
-               }
-       }
-}
-
-/* Returns newly allocated string, or NULL on failure */
-static gchar *find_in_dir (DIR *current, const gchar *name)
-{
-       struct dirent *entry;
-
-#ifdef DEBUG
-       g_message ("%s: looking for [%s]\n", __func__, name);
-#endif
-       
-       while((entry = readdir (current)) != NULL) {
-#ifdef DEBUGX
-               g_message ("%s: found [%s]\n", __func__, entry->d_name);
-#endif
-               
-               if (!g_ascii_strcasecmp (name, entry->d_name)) {
-                       char *ret;
-                       
-#ifdef DEBUG
-                       g_message ("%s: matched [%s] to [%s]\n", __func__,
-                                  entry->d_name, name);
-#endif
-
-                       ret = g_strdup (entry->d_name);
-                       closedir (current);
-                       return ret;
-               }
-       }
-       
-#ifdef DEBUG
-       g_message ("%s: returning NULL\n", __func__);
-#endif
-       
-       closedir (current);
-       
-       return(NULL);
-}
-
-/* Returns newly-allocated string or NULL on failure */
-static gchar *find_file (const gchar *pathname, gboolean last_exists)
-{
-       gchar *new_pathname, **components, **new_components;
-       int num_components = 0, component = 0;
-       DIR *scanning = NULL;
-       
-       mono_once (&options_once, options_init);
-
-       if (portability_helpers == PORTABILITY_NONE) {
-               return(NULL);
-       }
-
-       new_pathname = g_strdup (pathname);
-       
-#ifdef DEBUG
-       g_message ("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
-                  last_exists?"TRUE":"FALSE");
-#endif
-       
-       if (last_exists &&
-           access (new_pathname, F_OK) == 0) {
-#ifdef DEBUG
-               g_message ("%s: Found it without doing anything\n", __func__);
-#endif
-               return(new_pathname);
-       }
-       
-       /* First turn '\' into '/' and strip any drive letters */
-       g_strdelimit (new_pathname, "\\", '/');
-
-#ifdef DEBUG
-       g_message ("%s: Fixed slashes, now have [%s]\n", __func__,
-                  new_pathname);
-#endif
-       
-       if (portability_helpers & PORTABILITY_DRIVE &&
-           g_ascii_isalpha (new_pathname[0]) &&
-           (new_pathname[1] == ':')) {
-               int len = strlen (new_pathname);
-               
-               g_memmove (new_pathname, new_pathname+2, len - 2);
-               new_pathname[len - 2] = '\0';
-               
-#ifdef DEBUG
-               g_message ("%s: Stripped drive letter, now looking for [%s]\n",
-                          __func__, new_pathname);
-#endif
-       }
-       
-       if (last_exists &&
-           access (new_pathname, F_OK) == 0) {
-#ifdef DEBUG
-               g_message ("%s: Found it\n", __func__);
-#endif
-               
-               return(new_pathname);
-       }
-
-       /* OK, have to work harder.  Take each path component in turn
-        * and do a case-insensitive directory scan for it
-        */
-
-       if (!(portability_helpers & PORTABILITY_CASE)) {
-               g_free (new_pathname);
-               return(NULL);
-       }
-
-       components = g_strsplit (new_pathname, "/", 0);
-       if (components == NULL) {
-               /* This shouldn't happen */
-               g_free (new_pathname);
-               return(NULL);
-       }
-       
-       while(components[num_components] != NULL) {
-               num_components++;
-       }
-       g_free (new_pathname);
-       
-       if (num_components == 0){
-               return NULL;
-       }
-       
-
-       new_components = (gchar **)g_new0 (gchar **, num_components + 1);
-
-       if (num_components > 1) {
-               if (strcmp (components[0], "") == 0) {
-                       /* first component blank, so start at / */
-                       scanning = opendir ("/");
-                       if (scanning == NULL) {
-#ifdef DEBUG
-                               g_message ("%s: opendir 1 error: %s", __func__,
-                                          g_strerror (errno));
-#endif
-                               g_strfreev (new_components);
-                               g_strfreev (components);
-                               return(NULL);
-                       }
-               
-                       new_components[component++] = g_strdup ("");
-               } else {
-                       DIR *current;
-                       gchar *entry;
-               
-                       current = opendir (".");
-                       if (current == NULL) {
-#ifdef DEBUG
-                               g_message ("%s: opendir 2 error: %s", __func__,
-                                          g_strerror (errno));
-#endif
-                               g_strfreev (new_components);
-                               g_strfreev (components);
-                               return(NULL);
-                       }
-               
-                       entry = find_in_dir (current, components[0]);
-                       if (entry == NULL) {
-                               g_strfreev (new_components);
-                               g_strfreev (components);
-                               return(NULL);
-                       }
-               
-                       scanning = opendir (entry);
-                       if (scanning == NULL) {
-#ifdef DEBUG
-                               g_message ("%s: opendir 3 error: %s", __func__,
-                                          g_strerror (errno));
-#endif
-                               g_free (entry);
-                               g_strfreev (new_components);
-                               g_strfreev (components);
-                               return(NULL);
-                       }
-               
-                       new_components[component++] = entry;
-               }
-       } else {
-               if (last_exists) {
-                       if (strcmp (components[0], "") == 0) {
-                               /* First and only component blank */
-                               new_components[component++] = g_strdup ("");
-                       } else {
-                               DIR *current;
-                               gchar *entry;
-                               
-                               current = opendir (".");
-                               if (current == NULL) {
-#ifdef DEBUG
-                                       g_message ("%s: opendir 4 error: %s",
-                                                  __func__,
-                                                  g_strerror (errno));
-#endif
-                                       g_strfreev (new_components);
-                                       g_strfreev (components);
-                                       return(NULL);
-                               }
-                               
-                               entry = find_in_dir (current, components[0]);
-                               if (entry == NULL) {
-                                       g_strfreev (new_components);
-                                       g_strfreev (components);
-                                       return(NULL);
-                               }
-                               
-                               new_components[component++] = entry;
-                       }
-               } else {
-                               new_components[component++] = g_strdup (components[0]);
-               }
-       }
-
-#ifdef DEBUG
-       g_message ("%s: Got first entry: [%s]\n", __func__, new_components[0]);
-#endif
-
-       g_assert (component == 1);
-       
-       for(; component < num_components; component++) {
-               gchar *entry;
-               gchar *path_so_far;
-               
-               if (!last_exists &&
-                   component == num_components -1) {
-                       entry = g_strdup (components[component]);
-                       closedir (scanning);
-               } else {
-                       entry = find_in_dir (scanning, components[component]);
-                       if (entry == NULL) {
-                               g_strfreev (new_components);
-                               g_strfreev (components);
-                               return(NULL);
-                       }
-               }
-               
-               new_components[component] = entry;
-               
-               if (component < num_components -1) {
-                       path_so_far = g_strjoinv ("/", new_components);
-
-                       scanning = opendir (path_so_far);
-                       g_free (path_so_far);
-                       if (scanning == NULL) {
-                               g_strfreev (new_components);
-                               g_strfreev (components);
-                               return(NULL);
-                       }
-               }
-       }
-       
-       g_strfreev (components);
-
-       new_pathname = g_strjoinv ("/", new_components);
-
-#ifdef DEBUG
-       g_message ("%s: pathname [%s] became [%s]\n", __func__, pathname,
-                  new_pathname);
-#endif
-       
-       g_strfreev (new_components);
-
-       if ((last_exists &&
-            access (new_pathname, F_OK) == 0) ||
-           (!last_exists)) {
-               return(new_pathname);
-       }
-       
-       g_free (new_pathname);
-       return(NULL);
-}
-
 int _wapi_open (const char *pathname, int flags, mode_t mode)
 {
        int fd;
        gchar *located_filename;
        
        if (flags & O_CREAT) {
-               located_filename = find_file (pathname, FALSE);
+               located_filename = mono_portability_find_file (pathname, FALSE);
                if (located_filename == NULL) {
                        fd = open (pathname, flags, mode);
                } else {
@@ -361,9 +50,9 @@ int _wapi_open (const char *pathname, int flags, mode_t mode)
                fd = open (pathname, flags, mode);
                if (fd == -1 &&
                    (errno == ENOENT || errno == ENOTDIR) &&
-                   portability_helpers > 0) {
+                   IS_PORTABILITY_SET) {
                        int saved_errno = errno;
-                       located_filename = find_file (pathname, TRUE);
+                       located_filename = mono_portability_find_file (pathname, TRUE);
                        
                        if (located_filename == NULL) {
                                errno = saved_errno;
@@ -386,9 +75,9 @@ int _wapi_access (const char *pathname, int mode)
        ret = access (pathname, mode);
        if (ret == -1 &&
            (errno == ENOENT || errno == ENOTDIR) &&
-           portability_helpers > 0) {
+           IS_PORTABILITY_SET) {
                int saved_errno = errno;
-               gchar *located_filename = find_file (pathname, TRUE);
+               gchar *located_filename = mono_portability_find_file (pathname, TRUE);
                
                if (located_filename == NULL) {
                        errno = saved_errno;
@@ -409,9 +98,9 @@ int _wapi_chmod (const char *pathname, mode_t mode)
        ret = chmod (pathname, mode);
        if (ret == -1 &&
            (errno == ENOENT || errno == ENOTDIR) &&
-           portability_helpers > 0) {
+           IS_PORTABILITY_SET) {
                int saved_errno = errno;
-               gchar *located_filename = find_file (pathname, TRUE);
+               gchar *located_filename = mono_portability_find_file (pathname, TRUE);
                
                if (located_filename == NULL) {
                        errno = saved_errno;
@@ -432,9 +121,9 @@ int _wapi_utime (const char *filename, const struct utimbuf *buf)
        ret = utime (filename, buf);
        if (ret == -1 &&
            errno == ENOENT &&
-           portability_helpers > 0) {
+           IS_PORTABILITY_SET) {
                int saved_errno = errno;
-               gchar *located_filename = find_file (filename, TRUE);
+               gchar *located_filename = mono_portability_find_file (filename, TRUE);
                
                if (located_filename == NULL) {
                        errno = saved_errno;
@@ -455,9 +144,9 @@ int _wapi_unlink (const char *pathname)
        ret = unlink (pathname);
        if (ret == -1 &&
            (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) &&
-           portability_helpers > 0) {
+           IS_PORTABILITY_SET) {
                int saved_errno = errno;
-               gchar *located_filename = find_file (pathname, TRUE);
+               gchar *located_filename = mono_portability_find_file (pathname, TRUE);
                
                if (located_filename == NULL) {
                        errno = saved_errno;
@@ -474,7 +163,7 @@ int _wapi_unlink (const char *pathname)
 int _wapi_rename (const char *oldpath, const char *newpath)
 {
        int ret;
-       gchar *located_newpath = find_file (newpath, FALSE);
+       gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
        
        if (located_newpath == NULL) {
                ret = rename (oldpath, newpath);
@@ -484,9 +173,9 @@ int _wapi_rename (const char *oldpath, const char *newpath)
                if (ret == -1 &&
                    (errno == EISDIR || errno == ENAMETOOLONG ||
                     errno == ENOENT || errno == ENOTDIR || errno == EXDEV) &&
-                   portability_helpers > 0) {
+                   IS_PORTABILITY_SET) {
                        int saved_errno = errno;
-                       gchar *located_oldpath = find_file (oldpath, TRUE);
+                       gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
                        
                        if (located_oldpath == NULL) {
                                g_free (located_oldpath);
@@ -512,9 +201,9 @@ int _wapi_stat (const char *path, struct stat *buf)
        ret = stat (path, buf);
        if (ret == -1 &&
            (errno == ENOENT || errno == ENOTDIR) &&
-           portability_helpers > 0) {
+           IS_PORTABILITY_SET) {
                int saved_errno = errno;
-               gchar *located_filename = find_file (path, TRUE);
+               gchar *located_filename = mono_portability_find_file (path, TRUE);
                
                if (located_filename == NULL) {
                        errno = saved_errno;
@@ -535,9 +224,9 @@ int _wapi_lstat (const char *path, struct stat *buf)
        ret = lstat (path, buf);
        if (ret == -1 &&
            (errno == ENOENT || errno == ENOTDIR) &&
-           portability_helpers > 0) {
+           IS_PORTABILITY_SET) {
                int saved_errno = errno;
-               gchar *located_filename = find_file (path, TRUE);
+               gchar *located_filename = mono_portability_find_file (path, TRUE);
                
                if (located_filename == NULL) {
                        errno = saved_errno;
@@ -554,7 +243,7 @@ int _wapi_lstat (const char *path, struct stat *buf)
 int _wapi_mkdir (const char *pathname, mode_t mode)
 {
        int ret;
-       gchar *located_filename = find_file (pathname, FALSE);
+       gchar *located_filename = mono_portability_find_file (pathname, FALSE);
        
        if (located_filename == NULL) {
                ret = mkdir (pathname, mode);
@@ -573,9 +262,9 @@ int _wapi_rmdir (const char *pathname)
        ret = rmdir (pathname);
        if (ret == -1 &&
            (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
-           portability_helpers > 0) {
+           IS_PORTABILITY_SET) {
                int saved_errno = errno;
-               gchar *located_filename = find_file (pathname, TRUE);
+               gchar *located_filename = mono_portability_find_file (pathname, TRUE);
                
                if (located_filename == NULL) {
                        errno = saved_errno;
@@ -596,9 +285,9 @@ int _wapi_chdir (const char *path)
        ret = chdir (path);
        if (ret == -1 &&
            (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
-           portability_helpers > 0) {
+           IS_PORTABILITY_SET) {
                int saved_errno = errno;
-               gchar *located_filename = find_file (path, TRUE);
+               gchar *located_filename = mono_portability_find_file (path, TRUE);
                
                if (located_filename == NULL) {
                        errno = saved_errno;
@@ -616,11 +305,11 @@ gchar *_wapi_basename (const gchar *filename)
 {
        gchar *new_filename = g_strdup (filename), *ret;
 
-       mono_once (&options_once, options_init);
-       
-       g_strdelimit (new_filename, "\\", '/');
+       if (IS_PORTABILITY_SET) {
+               g_strdelimit (new_filename, "\\", '/');
+       }
 
-       if (portability_helpers & PORTABILITY_DRIVE &&
+       if (IS_PORTABILITY_DRIVE &&
            g_ascii_isalpha (new_filename[0]) &&
            (new_filename[1] == ':')) {
                int len = strlen (new_filename);
@@ -639,11 +328,11 @@ gchar *_wapi_dirname (const gchar *filename)
 {
        gchar *new_filename = g_strdup (filename), *ret;
 
-       mono_once (&options_once, options_init);
-       
-       g_strdelimit (new_filename, "\\", '/');
+       if (IS_PORTABILITY_SET) {
+               g_strdelimit (new_filename, "\\", '/');
+       }
 
-       if (portability_helpers & PORTABILITY_DRIVE &&
+       if (IS_PORTABILITY_DRIVE &&
            g_ascii_isalpha (new_filename[0]) &&
            (new_filename[1] == ':')) {
                int len = strlen (new_filename);
@@ -667,8 +356,8 @@ GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
            ((*error)->code == G_FILE_ERROR_NOENT ||
             (*error)->code == G_FILE_ERROR_NOTDIR ||
             (*error)->code == G_FILE_ERROR_NAMETOOLONG) &&
-           portability_helpers > 0) {
-               gchar *located_filename = find_file (path, TRUE);
+           IS_PORTABILITY_SET) {
+               gchar *located_filename = mono_portability_find_file (path, TRUE);
                GError *tmp_error = NULL;
                
                if (located_filename == NULL) {
@@ -839,7 +528,7 @@ gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern,
                return -1;
        }
 
-       if (portability_helpers & PORTABILITY_CASE) {
+       if (IS_PORTABILITY_CASE) {
                flags = WAPI_GLOB_IGNORECASE;
        }
        
@@ -852,11 +541,16 @@ gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern,
                 * TODO: should this be a MONO_IOMAP option?
                 */
                gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
+               gint result2;
                
                g_dir_rewind (dir);
-               result = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf);
+               result2 = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf);
 
                g_free (pattern2);
+
+               if (result != 0) {
+                       result = result2;
+               }
        }
        
        g_dir_close (dir);