Eina: Windows fixes
authorcaro <caro>
Sat, 21 Jan 2012 08:21:32 +0000 (08:21 +0000)
committercaro <caro@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Sat, 21 Jan 2012 08:21:32 +0000 (08:21 +0000)
add eina_file_path_sanitize() which was missing in the windows port
add locks
synchronize a bit with the linux version

git-svn-id: http://svn.enlightenment.org/svn/e/trunk/eina@67422 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

src/lib/eina_file_win32.c

index 1cd8665..29d8175 100644 (file)
@@ -41,7 +41,7 @@ void *alloca (size_t);
 #include <windows.h>\r
 #undef WIN32_LEAN_AND_MEAN\r
 \r
-//#include <Evil.h>\r
+#include <Evil.h>\r
 \r
 #include "eina_config.h"\r
 #include "eina_private.h"\r
@@ -52,6 +52,8 @@ void *alloca (size_t);
 #include "eina_stringshare.h"\r
 #include "eina_hash.h"\r
 #include "eina_list.h"\r
+#include "eina_lock.h"\r
+#include "eina_log.h"\r
 \r
 /*============================================================================*\r
  *                                  Local                                     *\r
@@ -123,6 +125,8 @@ struct _Eina_File
    Eina_Hash *rmap;\r
    void *global_map;\r
 \r
+   Eina_Lock lock;\r
+\r
    ULONGLONG length;\r
    ULONGLONG mtime;\r
 \r
@@ -147,8 +151,7 @@ struct _Eina_File_Map
 };\r
 \r
 static Eina_Hash *_eina_file_cache = NULL;\r
-static Eina_List *_eina_file_cache_lru = NULL;\r
-static Eina_List *_eina_file_cache_delete = NULL;\r
+static Eina_Lock _eina_file_lock_cache;\r
 \r
 static int _eina_file_log_dom = -1;\r
 \r
@@ -486,6 +489,86 @@ _eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__)
      ^ eina_hash_int64(&key[1], sizeof (unsigned long int));\r
 }\r
 \r
+static char *\r
+_eina_file_win32_escape(const char *path, size_t *length)\r
+{\r
+   char *result = strdup(path ? path : "");\r
+   char *p = result;\r
+   char *q = result;\r
+   size_t len;\r
+\r
+   if (!result)\r
+     return NULL;\r
+\r
+   if (length) len = *length;\r
+   else len = strlen(result);\r
+\r
+   while ((p = strchr(p, '/')))\r
+     {\r
+        // remove double `/'\r
+        if (p[1] == '/')\r
+          {\r
+             memmove(p, p + 1, --len - (p - result));\r
+             result[len] = '\0';\r
+          }\r
+        else\r
+          if (p[1] == '.'\r
+              && p[2] == '.')\r
+            {\r
+               // remove `/../'\r
+               if (p[3] == '/')\r
+                 {\r
+                    char tmp;\r
+\r
+                    len -= p + 3 - q;\r
+                    memmove(q, p + 3, len - (q - result));\r
+                    result[len] = '\0';\r
+                    p = q;\r
+\r
+                    /* Update q correctly. */\r
+                    tmp = *p;\r
+                    *p = '\0';\r
+                    q = strrchr(result, '/');\r
+                    if (!q) q = result;\r
+                    *p = tmp;\r
+                 }\r
+               else\r
+                 // remove '/..$'\r
+                 if (p[3] == '\0')\r
+                   {\r
+                      len -= p + 2 - q;\r
+                      result[len] = '\0';\r
+                      q = p;\r
+                      ++p;\r
+                   }\r
+                 else\r
+                   {\r
+                      q = p;\r
+                      ++p;\r
+                   }\r
+            }\r
+          else\r
+            {\r
+               q = p;\r
+               ++p;\r
+            }\r
+     }\r
+\r
+   if (length)\r
+     *length = len;\r
+\r
+   return result;\r
+}\r
+\r
+\r
+/**\r
+ * @endcond\r
+ */\r
+\r
+/*============================================================================*\r
+ *                                 Global                                     *\r
+ *============================================================================*/\r
+\r
 Eina_Bool\r
 eina_file_init(void)\r
 {\r
@@ -497,7 +580,7 @@ eina_file_init(void)
         return EINA_FALSE;\r
      }\r
 \r
-   _eina_file_cache = eina_hash_string_djb2_new(EINA_FREE_CB(_eina_file_real_close));\r
+   _eina_file_cache = eina_hash_string_djb2_new(NULL);\r
    if (!_eina_file_cache)\r
      {\r
         ERR("Could not create cache.");\r
@@ -506,21 +589,14 @@ eina_file_init(void)
         return EINA_FALSE;\r
      }\r
 \r
+   eina_lock_new(&_eina_file_lock_cache);\r
+\r
    return EINA_TRUE;\r
 }\r
 \r
 Eina_Bool\r
 eina_file_shutdown(void)\r
 {\r
-   Eina_File *f;\r
-   Eina_List *l;\r
-\r
-   EINA_LIST_FREE(_eina_file_cache_delete, f)\r
-     _eina_file_real_close(f);\r
-\r
-   EINA_LIST_FOREACH(_eina_file_cache_lru, l, f)\r
-     eina_hash_del(_eina_file_cache, f->filename, f);\r
-\r
    if (eina_hash_population(_eina_file_cache) > 0)\r
      {\r
         Eina_Iterator *it;\r
@@ -534,24 +610,57 @@ eina_file_shutdown(void)
 \r
    eina_hash_free(_eina_file_cache);\r
 \r
+   eina_lock_free(&_eina_file_lock_cache);\r
+\r
    eina_log_domain_unregister(_eina_file_log_dom);\r
    _eina_file_log_dom = -1;\r
    return EINA_TRUE;\r
 }\r
 \r
-\r
-/**\r
- * @endcond\r
- */\r
-\r
-/*============================================================================*\r
- *                                 Global                                     *\r
- *============================================================================*/\r
-\r
 /*============================================================================*\r
  *                                   API                                      *\r
  *============================================================================*/\r
 \r
+\r
+EAPI char *\r
+eina_file_path_sanitize(const char *path)\r
+{\r
+   char *result = NULL;\r
+   size_t len;\r
+\r
+   if (!path) return NULL;\r
+\r
+   len = strlen(path);\r
+   if (len < 3) return NULL;\r
+\r
+   if (!evil_path_is_absolute(path))\r
+     {\r
+        DWORD l;\r
+\r
+        l = GetCurrentDirectory(0, NULL);\r
+        if (l > 0)\r
+          {\r
+             char *cwd;\r
+             DWORD l2;\r
+\r
+             cwd = alloca(sizeof(char) * (l + 1));\r
+             l2 = GetCurrentDirectory(l + 1, cwd);\r
+             if (l2 == l)\r
+               {\r
+                  char *tmp;\r
+\r
+                  len += l + 2;\r
+                  tmp = alloca(sizeof (char) * len);\r
+                  snprintf(tmp, len, "%s/%s", cwd, path);\r
+                  tmp[len - 1] = '\0';\r
+                  result = tmp;\r
+               }\r
+          }\r
+     }\r
+\r
+   return _eina_file_win32_escape(result ? result : path, &len);\r
+}\r
+\r
 EAPI Eina_Bool\r
 eina_file_dir_list(const char *dir,\r
                    Eina_Bool recursive,\r
@@ -657,6 +766,8 @@ eina_file_ls(const char *dir)
    char               *new_dir;\r
    size_t              length;\r
 \r
+   EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);\r
+\r
    if (!dir || !*dir)\r
       return NULL;\r
 \r
@@ -707,6 +818,8 @@ eina_file_direct_ls(const char *dir)
    char                      *new_dir;\r
    size_t                     length;\r
 \r
+   EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);\r
+\r
    if (!dir || !*dir)\r
       return NULL;\r
 \r
@@ -764,10 +877,11 @@ eina_file_stat_ls(const char *dir)
 }\r
 \r
 EAPI Eina_File *\r
-eina_file_open(const char *filename, Eina_Bool shared)\r
+eina_file_open(const char *path, Eina_Bool shared)\r
 {\r
    Eina_File *file;\r
    Eina_File *n;\r
+   char *filename;\r
    HANDLE handle;\r
    HANDLE fm;\r
    WIN32_FILE_ATTRIBUTE_DATA fad;\r
@@ -775,8 +889,10 @@ eina_file_open(const char *filename, Eina_Bool shared)
    ULARGE_INTEGER mtime;\r
    Eina_Bool create = EINA_FALSE;\r
 \r
-   /* FIXME: always open absolute path (need to fix filename according to current\r
-      directory) */\r
+   EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);\r
+\r
+   filename = eina_file_path_sanitize(path);\r
+   if (!filename) return NULL;\r
 \r
    /* FIXME: how to emulate shm_open ? Just OpenFileMapping ? */\r
 #if 0\r
@@ -805,33 +921,29 @@ eina_file_open(const char *filename, Eina_Bool shared)
    mtime.u.LowPart = fad.ftLastWriteTime.dwLowDateTime;\r
    mtime.u.HighPart = fad.ftLastWriteTime.dwHighDateTime;\r
 \r
+   eina_lock_take(&_eina_file_lock_cache);\r
+\r
    file = eina_hash_find(_eina_file_cache, filename);\r
    if (file &&\r
-       (file->mtime != mtime.QuadPart || file->length != length.QuadPart))\r
+       (file->mtime == mtime.QuadPart && file->length == length.QuadPart))\r
      {\r
-        create = EINA_TRUE;\r
-\r
-        if (file->refcount == 0)\r
-          {\r
-             _eina_file_cache_lru = eina_list_prepend(_eina_file_cache_lru, file);\r
-             eina_hash_del(_eina_file_cache, file->filename, file);\r
-\r
-             file = NULL;\r
-          }\r
-        else if (!file->delete_me)\r
-          {\r
-             file->delete_me = EINA_TRUE;\r
-             _eina_file_cache_delete = eina_list_prepend(_eina_file_cache_delete, file);\r
-          }\r
+        file->delete_me = EINA_TRUE;\r
+        eina_hash_del(_eina_file_cache, file->filename, file);\r
+        _eina_file_real_close(file);\r
+        file = NULL;\r
      }\r
 \r
-   if (!file || create)\r
+   if (!file)\r
      {\r
-        n = calloc(1, sizeof (Eina_File));\r
+        n = malloc(sizeof (Eina_File) + strlen(filename) + 1);\r
         if (!n)\r
-          goto close_fm;\r
+          {\r
+             eina_lock_release(&_eina_file_lock_cache);\r
+             goto close_fm;\r
+          }\r
 \r
-        n->filename = eina_stringshare_add(filename);\r
+        n->filename = (char*) (n + 1);\r
+        strcpy((char*) n->filename, filename);\r
         n->map = eina_hash_new(EINA_KEY_LENGTH(_eina_file_map_key_length),\r
                                EINA_KEY_CMP(_eina_file_map_key_cmp),\r
                                EINA_KEY_HASH(_eina_file_map_key_hash),\r
@@ -839,6 +951,7 @@ eina_file_open(const char *filename, Eina_Bool shared)
                                3);\r
         n->rmap = eina_hash_pointer_new(NULL);\r
         n->global_map = MAP_FAILED;\r
+        n->global_refcount = 0;\r
         n->length = length.QuadPart;\r
         n->mtime = mtime.QuadPart;\r
         n->refcount = 0;\r
@@ -846,8 +959,8 @@ eina_file_open(const char *filename, Eina_Bool shared)
         n->fm = fm;\r
         n->shared = shared;\r
         n->delete_me = EINA_FALSE;\r
-\r
-        eina_hash_set(_eina_file_cache, filename, n);\r
+        eina_lock_new(&n->lock);\r
+        eina_hash_direct_add(_eina_file_cache, n->filename, n);\r
      }\r
    else\r
      {\r
@@ -855,12 +968,14 @@ eina_file_open(const char *filename, Eina_Bool shared)
         CloseHandle(handle);\r
 \r
         n = file;\r
-\r
-        if (n->refcount == 0)\r
-          _eina_file_cache_lru = eina_list_remove(_eina_file_cache_lru, n);\r
      }\r
-\r
+   eina_lock_take(&n->lock);\r
    n->refcount++;\r
+   eina_lock_release(&n->lock);\r
+\r
+   eina_lock_release(&_eina_file_lock_cache);\r
+\r
+   free(filename);\r
 \r
    return n;\r
 \r
@@ -875,42 +990,48 @@ eina_file_open(const char *filename, Eina_Bool shared)
 EAPI void\r
 eina_file_close(Eina_File *file)\r
 {\r
+   EINA_SAFETY_ON_NULL_RETURN(file);\r
+\r
+   eina_lock_take(&file->lock);\r
    file->refcount--;\r
+   eina_lock_release(&file->lock);\r
 \r
    if (file->refcount != 0) return ;\r
+   eina_lock_take(&_eina_file_lock_cache);\r
 \r
-   if (file->delete_me)\r
-     {\r
-        _eina_file_cache_delete = eina_list_remove(_eina_file_cache_delete, file);\r
-        _eina_file_real_close(file);\r
-     }\r
-   else\r
-     {\r
-        _eina_file_cache_lru = eina_list_prepend(_eina_file_cache_lru, file);\r
-     }\r
+   eina_hash_del(_eina_file_cache, file->filename, file);\r
+   _eina_file_real_close(file);\r
+   \r
+   eina_lock_release(&_eina_file_lock_cache);\r
 }\r
 \r
 EAPI size_t\r
 eina_file_size_get(Eina_File *file)\r
 {\r
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);\r
    return file->length;\r
 }\r
 \r
 EAPI time_t\r
 eina_file_mtime_get(Eina_File *file)\r
 {\r
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);\r
   return file->mtime;\r
 }\r
 \r
 EAPI const char *\r
 eina_file_filename_get(Eina_File *file)\r
 {\r
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);\r
    return file->filename;\r
 }\r
 \r
 EAPI void *\r
 eina_file_map_all(Eina_File *file, Eina_File_Populate rule __UNUSED__)\r
 {\r
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);\r
+\r
+   eina_lock_take(&file->lock);\r
    if (file->global_map == MAP_FAILED)\r
      {\r
         void  *data;\r
@@ -929,6 +1050,7 @@ eina_file_map_all(Eina_File *file, Eina_File_Populate rule __UNUSED__)
         return file->global_map;\r
      }\r
 \r
+   eina_lock_release(&file->lock);\r
    return NULL;\r
 }\r
 \r
@@ -939,6 +1061,8 @@ eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
    Eina_File_Map *map;\r
    unsigned long int key[2];\r
 \r
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);\r
+\r
    if (offset > file->length)\r
      return NULL;\r
    if (offset + length > file->length)\r
@@ -950,6 +1074,8 @@ eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
    key[0] = offset;\r
    key[1] = length;\r
 \r
+   eina_lock_take(&file->lock);\r
+\r
    map = eina_hash_find(file->map, &key);\r
    if (!map)\r
      {\r
@@ -983,21 +1109,25 @@ eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
 \r
    map->refcount++;\r
 \r
+   eina_lock_release(&file->lock);\r
+\r
    return map->map;\r
 }\r
 \r
 EAPI void\r
 eina_file_map_free(Eina_File *file, void *map)\r
 {\r
+   EINA_SAFETY_ON_NULL_RETURN(file);\r
+\r
+   eina_lock_take(&file->lock);\r
+\r
    if (file->global_map == map)\r
      {\r
         file->global_refcount--;\r
 \r
-        if (file->global_refcount > 0) return ;\r
+        if (file->global_refcount > 0) goto on_exit;\r
 \r
-        /* FIXME: are we sure that file->global_map != MAP_FAILED ? */\r
-        if (file->global_map != MAP_FAILED)\r
-          UnmapViewOfFile(file->global_map);\r
+        UnmapViewOfFile(file->global_map);\r
         file->global_map = MAP_FAILED;\r
      }\r
    else\r
@@ -1010,7 +1140,7 @@ eina_file_map_free(Eina_File *file, void *map)
 \r
         em->refcount--;\r
 \r
-        if (em->refcount > 0) return ;\r
+        if (em->refcount > 0) goto on_exit;\r
 \r
         key[0] = em->offset;\r
         key[1] = em->length;\r
@@ -1018,4 +1148,7 @@ eina_file_map_free(Eina_File *file, void *map)
         eina_hash_del(file->rmap, &map, em);\r
         eina_hash_del(file->map, &key, em);\r
      }\r
+\r
+ on_exit:\r
+   eina_lock_release(&file->lock);\r
 }\r