-/* EINA - EFL data type library\r
- * Copyright (C) 2010 Vincent Torri\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library;\r
- * if not, see <http://www.gnu.org/licenses/>.\r
- */\r
-\r
-#ifdef HAVE_CONFIG_H\r
-# include "config.h"\r
-#endif\r
-\r
-#ifdef HAVE_ALLOCA_H\r
-# include <alloca.h>\r
-#elif defined __GNUC__\r
-# define alloca __builtin_alloca\r
-#elif defined _AIX\r
-# define alloca __alloca\r
-#elif defined _MSC_VER\r
-# include <malloc.h>\r
-# define alloca _alloca\r
-#else\r
-# include <stddef.h>\r
-# ifdef __cplusplus\r
-extern "C"\r
-# endif\r
-void *alloca (size_t);\r
-#endif\r
-\r
-#define WIN32_LEAN_AND_MEAN\r
-#include <windows.h>\r
-#undef WIN32_LEAN_AND_MEAN\r
-\r
-#include <Evil.h>\r
-\r
-#include "eina_config.h"\r
-#include "eina_private.h"\r
-\r
-/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */\r
-#include "eina_safety_checks.h"\r
-#include "eina_file.h"\r
-#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
- *============================================================================*/\r
-\r
-/**\r
- * @cond LOCAL\r
- */\r
-\r
-#ifndef EINA_LOG_COLOR_DEFAULT\r
-#define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN\r
-#endif\r
-\r
-#ifdef ERR\r
-#undef ERR\r
-#endif\r
-#define ERR(...) EINA_LOG_DOM_ERR(_eina_file_log_dom, __VA_ARGS__)\r
-\r
-#ifdef WRN\r
-#undef WRN\r
-#endif\r
-#define WRN(...) EINA_LOG_DOM_WARN(_eina_file_log_dom, __VA_ARGS__)\r
-\r
-#ifdef DBG\r
-#undef DBG\r
-#endif\r
-#define DBG(...) EINA_LOG_DOM_DBG(_eina_file_log_dom, __VA_ARGS__)\r
-\r
-#ifdef MAP_FAILED\r
-# undef MAP_FAILED\r
-#endif\r
-#define MAP_FAILED ((void *)-1)\r
-\r
-typedef struct _Eina_File_Iterator Eina_File_Iterator;\r
-typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;\r
-typedef struct _Eina_File_Map Eina_File_Map;\r
-\r
-struct _Eina_File_Iterator\r
-{\r
- Eina_Iterator iterator;\r
-\r
- WIN32_FIND_DATA data;\r
- HANDLE handle;\r
- size_t length;\r
- Eina_Bool is_last : 1;\r
-\r
- char dir[1];\r
-};\r
-\r
-struct _Eina_File_Direct_Iterator\r
-{\r
- Eina_Iterator iterator;\r
-\r
- WIN32_FIND_DATA data;\r
- HANDLE handle;\r
- size_t length;\r
- Eina_Bool is_last : 1;\r
-\r
- Eina_File_Direct_Info info;\r
-\r
- char dir[1];\r
-};\r
-\r
-struct _Eina_File\r
-{\r
- const char *filename;\r
-\r
- Eina_Hash *map;\r
- Eina_Hash *rmap;\r
- void *global_map;\r
-\r
- Eina_Lock lock;\r
-\r
- ULONGLONG length;\r
- ULONGLONG mtime;\r
-\r
- int refcount;\r
- int global_refcount;\r
-\r
- HANDLE handle;\r
- HANDLE fm;\r
-\r
- Eina_Bool shared : 1;\r
- Eina_Bool delete_me : 1;\r
-};\r
-\r
-struct _Eina_File_Map\r
-{\r
- void *map;\r
-\r
- unsigned long int offset;\r
- unsigned long int length;\r
-\r
- int refcount;\r
-};\r
-\r
-static Eina_Hash *_eina_file_cache = NULL;\r
-static Eina_Lock _eina_file_lock_cache;\r
-\r
-static int _eina_file_log_dom = -1;\r
-\r
-static void\r
-_eina_file_win32_backslash_change(char *dir)\r
-{\r
- char *tmp;\r
-\r
- tmp = dir;\r
- while (*tmp)\r
- {\r
- if (*tmp == '/') *tmp = '\\';\r
- tmp++;\r
- }\r
-}\r
-\r
-static Eina_Bool\r
-_eina_file_win32_is_dir(const char *dir)\r
-{\r
-#ifdef UNICODE\r
- wchar_t *wdir = NULL;\r
-#endif\r
- DWORD attr;\r
-\r
- /* check if it's a directory */\r
-#ifdef UNICODE\r
- wdir = evil_char_to_wchar(dir);\r
- if (!wdir)\r
- return EINA_FALSE;\r
-\r
- attr = GetFileAttributes(wdir);\r
- free(wdir);\r
-#else\r
- attr = GetFileAttributes(dir);\r
-#endif\r
-\r
- if (attr == 0xFFFFFFFF)\r
- return EINA_FALSE;\r
-\r
- if (!(attr & FILE_ATTRIBUTE_DIRECTORY))\r
- return EINA_FALSE;\r
-\r
- return EINA_TRUE;\r
-}\r
-\r
-static char *\r
-_eina_file_win32_dir_new(const char *dir)\r
-{\r
- char *new_dir;\r
- size_t length;\r
-\r
- length = strlen(dir);\r
-\r
- new_dir = (char *)malloc(sizeof(char) * length + 5);\r
- if (!new_dir)\r
- return NULL;\r
-\r
- memcpy(new_dir, dir, length);\r
- memcpy(new_dir + length, "\\*.*", 5);\r
- _eina_file_win32_backslash_change(new_dir);\r
-\r
- return new_dir;\r
-}\r
-\r
-static HANDLE\r
-_eina_file_win32_first_file(const char *dir, WIN32_FIND_DATA *fd)\r
-{\r
- HANDLE h;\r
-#ifdef UNICODE\r
- wchar_t *wdir = NULL;\r
-\r
- wdir = evil_char_to_wchar(dir);\r
- if (!wdir)\r
- return NULL;\r
-\r
- h = FindFirstFile(wdir, fd);\r
- free(wdir);\r
-#else\r
- h = FindFirstFile(dir, fd);\r
-#endif\r
-\r
- if (!h)\r
- return NULL;\r
-\r
- while ((fd->cFileName[0] == '.') &&\r
- ((fd->cFileName[1] == '\0') ||\r
- ((fd->cFileName[1] == '.') && (fd->cFileName[2] == '\0'))))\r
- {\r
- if (!FindNextFile(h, fd))\r
- return NULL;\r
- }\r
-\r
- return h;\r
-}\r
-\r
-static Eina_Bool\r
-_eina_file_win32_ls_iterator_next(Eina_File_Iterator *it, void **data)\r
-{\r
-#ifdef UNICODE\r
- wchar_t *old_name;\r
-#else\r
- char *old_name;\r
-#endif\r
- char *name;\r
- char *cname;\r
- size_t length;\r
- Eina_Bool is_last;\r
- Eina_Bool res = EINA_TRUE;\r
-\r
- if (it->handle == INVALID_HANDLE_VALUE)\r
- return EINA_FALSE;\r
-\r
- is_last = it->is_last;\r
-#ifdef UNICODE\r
- old_name = _wcsdup(it->data.cFileName);\r
-#else\r
- old_name = _strdup(it->data.cFileName);\r
-#endif\r
- if (!old_name)\r
- return EINA_FALSE;\r
-\r
- do {\r
- if (!FindNextFile(it->handle, &it->data))\r
- {\r
- if (GetLastError() == ERROR_NO_MORE_FILES)\r
- it->is_last = EINA_TRUE;\r
- else\r
- res = EINA_FALSE;\r
- }\r
- } while ((it->data.cFileName[0] == '.') &&\r
- ((it->data.cFileName[1] == '\0') ||\r
- ((it->data.cFileName[1] == '.') && (it->data.cFileName[2] == '\0')))); /* FIXME: what about UNICODE ? */\r
-\r
-#ifdef UNICODE\r
- cname = evil_wchar_to_char(old_name);\r
- if (!cname)\r
- return EINA_FALSE;\r
-#else\r
- cname = old_name;\r
-#endif\r
-\r
- length = strlen(cname);\r
- name = alloca(length + 2 + it->length);\r
-\r
- memcpy(name, it->dir, it->length);\r
- memcpy(name + it->length, "\\", 1);\r
- memcpy(name + it->length + 1, cname, length + 1);\r
-\r
- *data = (char *)eina_stringshare_add(name);\r
-\r
-#ifdef UNICODE\r
- free(cname);\r
-#endif\r
- free(old_name);\r
-\r
- if (is_last)\r
- res = EINA_FALSE;\r
-\r
- return res;\r
-}\r
-\r
-static HANDLE\r
-_eina_file_win32_ls_iterator_container(Eina_File_Iterator *it)\r
-{\r
- return it->handle;\r
-}\r
-\r
-static void\r
-_eina_file_win32_ls_iterator_free(Eina_File_Iterator *it)\r
-{\r
- if (it->handle != INVALID_HANDLE_VALUE)\r
- FindClose(it->handle);\r
-\r
- EINA_MAGIC_SET(&it->iterator, 0);\r
- free(it);\r
-}\r
-\r
-static Eina_Bool\r
-_eina_file_win32_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)\r
-{\r
-#ifdef UNICODE\r
- wchar_t *old_name;\r
-#else\r
- char *old_name;\r
-#endif\r
- char *cname;\r
- size_t length;\r
- DWORD attr;\r
- Eina_Bool is_last;\r
- Eina_Bool res = EINA_TRUE;\r
-\r
- if (it->handle == INVALID_HANDLE_VALUE)\r
- return EINA_FALSE;\r
-\r
- attr = it->data.dwFileAttributes;\r
- is_last = it->is_last;\r
-#ifdef UNICODE\r
- old_name = _wcsdup(it->data.cFileName);\r
-#else\r
- old_name = _strdup(it->data.cFileName);\r
-#endif\r
- if (!old_name)\r
- return EINA_FALSE;\r
-\r
- do {\r
- if (!FindNextFile(it->handle, &it->data))\r
- {\r
- if (GetLastError() == ERROR_NO_MORE_FILES)\r
- it->is_last = EINA_TRUE;\r
- else\r
- res = EINA_FALSE;\r
- }\r
-\r
-#ifdef UNICODE\r
- length = wcslen(old_name);\r
-#else\r
- length = strlen(old_name);\r
-#endif\r
- if (it->info.name_start + length + 1 >= PATH_MAX)\r
- {\r
- free(old_name);\r
-#ifdef UNICODE\r
- old_name = _wcsdup(it->data.cFileName);\r
-#else\r
- old_name = _strdup(it->data.cFileName);\r
-#endif\r
- continue;\r
- }\r
-\r
- } while ((it->data.cFileName[0] == '.') &&\r
- ((it->data.cFileName[1] == '\0') ||\r
- ((it->data.cFileName[1] == '.') && (it->data.cFileName[2] == '\0')))); /* FIXME: what about UNICODE ? */\r
-\r
-#ifdef UNICODE\r
- cname = evil_wchar_to_char(old_name);\r
- if (!cname)\r
- return EINA_FALSE;\r
-#else\r
- cname = old_name;\r
-#endif\r
-\r
- memcpy(it->info.path + it->info.name_start, cname, length);\r
- it->info.name_length = length;\r
- it->info.path_length = it->info.name_start + length;\r
- it->info.path[it->info.path_length] = '\0';\r
-\r
- if (attr & FILE_ATTRIBUTE_DIRECTORY)\r
- it->info.type = EINA_FILE_DIR;\r
- else if (attr & FILE_ATTRIBUTE_REPARSE_POINT)\r
- it->info.type = EINA_FILE_LNK;\r
- else if (attr & (FILE_ATTRIBUTE_ARCHIVE |\r
- FILE_ATTRIBUTE_COMPRESSED |\r
- FILE_ATTRIBUTE_COMPRESSED |\r
- FILE_ATTRIBUTE_HIDDEN |\r
- FILE_ATTRIBUTE_NORMAL |\r
- FILE_ATTRIBUTE_SPARSE_FILE |\r
- FILE_ATTRIBUTE_TEMPORARY))\r
- it->info.type = EINA_FILE_REG;\r
- else\r
- it->info.type = EINA_FILE_UNKNOWN;\r
-\r
- *data = &it->info;\r
-\r
-#ifdef UNICODE\r
- free(cname);\r
-#endif\r
-\r
- free(old_name);\r
-\r
- if (is_last)\r
- res = EINA_FALSE;\r
-\r
- return res;\r
-}\r
-\r
-static HANDLE\r
-_eina_file_win32_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)\r
-{\r
- return it->handle;\r
-}\r
-\r
-static void\r
-_eina_file_win32_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)\r
-{\r
- if (it->handle != INVALID_HANDLE_VALUE)\r
- FindClose(it->handle);\r
-\r
- EINA_MAGIC_SET(&it->iterator, 0);\r
- free(it);\r
-}\r
-\r
-static void\r
-_eina_file_real_close(Eina_File *file)\r
-{\r
- eina_hash_free(file->rmap);\r
- eina_hash_free(file->map);\r
-\r
- if (file->global_map != MAP_FAILED)\r
- UnmapViewOfFile(file->global_map);\r
-\r
- CloseHandle(file->fm);\r
- CloseHandle(file->handle);\r
-\r
- eina_stringshare_del(file->filename);\r
-\r
- free(file);\r
-}\r
-\r
-static void\r
-_eina_file_map_close(Eina_File_Map *map)\r
-{\r
- if (map->map != MAP_FAILED)\r
- UnmapViewOfFile(map->map);\r
- free(map);\r
-}\r
-\r
-static unsigned int\r
-_eina_file_map_key_length(const void *key __UNUSED__)\r
-{\r
- return sizeof (unsigned long int) * 2;\r
-}\r
-\r
-static int\r
-_eina_file_map_key_cmp(const unsigned long int *key1, int key1_length __UNUSED__,\r
- const unsigned long int *key2, int key2_length __UNUSED__)\r
-{\r
- if (key1[0] - key2[0] == 0) return key1[1] - key2[1];\r
- return key1[0] - key2[0];\r
-}\r
-\r
-static int\r
-_eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__)\r
-{\r
- return eina_hash_int64(&key[0], sizeof (unsigned long int))\r
- ^ 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
- _eina_file_log_dom = eina_log_domain_register("eina_file",\r
- EINA_LOG_COLOR_DEFAULT);\r
- if (_eina_file_log_dom < 0)\r
- {\r
- EINA_LOG_ERR("Could not register log domain: eina_file");\r
- return EINA_FALSE;\r
- }\r
-\r
- _eina_file_cache = eina_hash_string_djb2_new(NULL);\r
- if (!_eina_file_cache)\r
- {\r
- ERR("Could not create cache.");\r
- eina_log_domain_unregister(_eina_file_log_dom);\r
- _eina_file_log_dom = -1;\r
- 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
- if (eina_hash_population(_eina_file_cache) > 0)\r
- {\r
- Eina_Iterator *it;\r
- const char *key;\r
-\r
- it = eina_hash_iterator_key_new(_eina_file_cache);\r
- EINA_ITERATOR_FOREACH(it, key)\r
- ERR("File [%s] still open !", key);\r
- eina_iterator_free(it);\r
- }\r
-\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
- * 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
- Eina_File_Dir_List_Cb cb,\r
- void *data)\r
-{\r
- WIN32_FIND_DATA file;\r
- HANDLE h;\r
- char *new_dir;\r
-\r
- EINA_SAFETY_ON_NULL_RETURN_VAL(cb, EINA_FALSE);\r
- EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE);\r
- EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE);\r
-\r
- if (!_eina_file_win32_is_dir(dir))\r
- return EINA_FALSE;\r
-\r
- new_dir = _eina_file_win32_dir_new(dir);\r
- if (!new_dir)\r
- return EINA_FALSE;\r
-\r
- h = _eina_file_win32_first_file(new_dir, &file);\r
-\r
- if (h == INVALID_HANDLE_VALUE)\r
- return EINA_FALSE;\r
-\r
- do\r
- {\r
- char *filename;\r
-\r
-# ifdef UNICODE\r
- filename = evil_wchar_to_char(file.cFileName);\r
-# else\r
- filename = file.cFileName;\r
-# endif /* ! UNICODE */\r
- if (!strcmp(filename, ".") || !strcmp(filename, ".."))\r
- continue;\r
-\r
- cb(filename, dir, data);\r
-\r
- if (recursive == EINA_TRUE)\r
- {\r
- char *path;\r
-\r
- path = alloca(strlen(dir) + strlen(filename) + 2);\r
- strcpy(path, dir);\r
- strcat(path, "/");\r
- strcat(path, filename);\r
-\r
- if (!(file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))\r
- continue;\r
-\r
- eina_file_dir_list(path, recursive, cb, data);\r
- }\r
-\r
-# ifdef UNICODE\r
- free(filename);\r
-# endif /* UNICODE */\r
-\r
- } while (FindNextFile(h, &file));\r
- FindClose(h);\r
-\r
- return EINA_TRUE;\r
-}\r
-\r
-EAPI Eina_Array *\r
-eina_file_split(char *path)\r
-{\r
- Eina_Array *ea;\r
- char *current;\r
- size_t length;\r
-\r
- EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);\r
-\r
- ea = eina_array_new(16);\r
-\r
- if (!ea)\r
- return NULL;\r
-\r
- for (current = strchr(path, '\\');\r
- current;\r
- path = current + 1, current = strchr(path, '\\'))\r
- {\r
- length = current - path;\r
-\r
- if (length <= 0)\r
- continue;\r
-\r
- eina_array_push(ea, path);\r
- *current = '\0';\r
- }\r
-\r
- if (*path != '\0')\r
- eina_array_push(ea, path);\r
-\r
- return ea;\r
-}\r
-\r
-EAPI Eina_Iterator *\r
-eina_file_ls(const char *dir)\r
-{\r
- Eina_File_Iterator *it;\r
- 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
- if (!_eina_file_win32_is_dir(dir))\r
- return NULL;\r
-\r
- length = strlen(dir);\r
-\r
- it = calloc(1, sizeof (Eina_File_Iterator) + length);\r
- if (!it)\r
- return NULL;\r
-\r
- EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);\r
-\r
- new_dir = _eina_file_win32_dir_new(dir);\r
- if (!new_dir)\r
- goto free_it;\r
-\r
- it->handle = _eina_file_win32_first_file(new_dir, &it->data);\r
- free(new_dir);\r
- if (it->handle == INVALID_HANDLE_VALUE)\r
- goto free_it;\r
-\r
- memcpy(it->dir, dir, length + 1);\r
- if (dir[length - 1] != '\\')\r
- it->length = length;\r
- else\r
- it->length = length - 1;\r
- _eina_file_win32_backslash_change(it->dir);\r
-\r
- it->iterator.version = EINA_ITERATOR_VERSION;\r
- it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_win32_ls_iterator_next);\r
- it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_win32_ls_iterator_container);\r
- it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_win32_ls_iterator_free);\r
-\r
- return &it->iterator;\r
-\r
- free_it:\r
- free(it);\r
-\r
- return NULL;\r
-}\r
-\r
-EAPI Eina_Iterator *\r
-eina_file_direct_ls(const char *dir)\r
-{\r
- Eina_File_Direct_Iterator *it;\r
- 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
- length = strlen(dir);\r
-\r
- if (length + 12 + 2 >= MAX_PATH)\r
- return NULL;\r
-\r
- it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);\r
- if (!it)\r
- return NULL;\r
-\r
- EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);\r
-\r
- new_dir = _eina_file_win32_dir_new(dir);\r
- if (!new_dir)\r
- goto free_it;\r
-\r
- it->handle = _eina_file_win32_first_file(new_dir, &it->data);\r
- free(new_dir);\r
- if (it->handle == INVALID_HANDLE_VALUE)\r
- goto free_it;\r
-\r
- memcpy(it->dir, dir, length + 1);\r
- it->length = length;\r
- _eina_file_win32_backslash_change(it->dir);\r
-\r
- memcpy(it->info.path, dir, length);\r
- if (dir[length - 1] == '\\')\r
- it->info.name_start = length;\r
- else\r
- {\r
- it->info.path[length] = '\\';\r
- it->info.name_start = length + 1;\r
- }\r
- _eina_file_win32_backslash_change(it->info.path);\r
-\r
- it->iterator.version = EINA_ITERATOR_VERSION;\r
- it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_win32_direct_ls_iterator_next);\r
- it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_win32_direct_ls_iterator_container);\r
- it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_win32_direct_ls_iterator_free);\r
-\r
- return &it->iterator;\r
-\r
- free_it:\r
- free(it);\r
-\r
- return NULL;\r
-}\r
-\r
-EAPI Eina_Iterator *\r
-eina_file_stat_ls(const char *dir)\r
-{\r
- return eina_file_direct_ls(dir);\r
-}\r
-\r
-EAPI Eina_File *\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
- ULARGE_INTEGER length;\r
- ULARGE_INTEGER mtime;\r
-\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
- if (shared)\r
- handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,\r
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,\r
- NULL);\r
- else\r
-#endif\r
- handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,\r
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,\r
- NULL);\r
-\r
- if (handle == INVALID_HANDLE_VALUE)\r
- return NULL;\r
-\r
- fm = CreateFileMapping(handle, NULL, PAGE_READONLY, 0, 0, NULL);\r
- if (!fm)\r
- goto close_handle;\r
-\r
- if (!GetFileAttributesEx(filename, GetFileExInfoStandard, &fad))\r
- goto close_fm;\r
-\r
- length.u.LowPart = fad.nFileSizeLow;\r
- length.u.HighPart = fad.nFileSizeHigh;\r
- 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
- {\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)\r
- {\r
- n = malloc(sizeof (Eina_File) + strlen(filename) + 1);\r
- if (!n)\r
- {\r
- eina_lock_release(&_eina_file_lock_cache);\r
- goto close_fm;\r
- }\r
-\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
- EINA_FREE_CB(_eina_file_map_close),\r
- 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
- n->handle = handle;\r
- n->fm = fm;\r
- n->shared = shared;\r
- n->delete_me = EINA_FALSE;\r
- eina_lock_new(&n->lock);\r
- eina_hash_direct_add(_eina_file_cache, n->filename, n);\r
- }\r
- else\r
- {\r
- CloseHandle(fm);\r
- CloseHandle(handle);\r
-\r
- n = file;\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
- close_fm:\r
- CloseHandle(fm);\r
- close_handle:\r
- CloseHandle(handle);\r
-\r
- return NULL;\r
-}\r
-\r
-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
- 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
-\r
- data = MapViewOfFile(file->fm, FILE_MAP_READ,\r
- 0, 0, file->length);\r
- if (!data)\r
- file->global_map = MAP_FAILED;\r
- else\r
- file->global_map = data;\r
- }\r
-\r
- if (file->global_map != MAP_FAILED)\r
- {\r
- file->global_refcount++;\r
- return file->global_map;\r
- }\r
-\r
- eina_lock_release(&file->lock);\r
- return NULL;\r
-}\r
-\r
-EAPI void *\r
-eina_file_map_new(Eina_File *file, Eina_File_Populate rule,\r
- unsigned long int offset, unsigned long int length)\r
-{\r
- 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
- return NULL;\r
-\r
- if (offset == 0 && length == file->length)\r
- return eina_file_map_all(file, rule);\r
-\r
- 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
- void *data;\r
-\r
- map = malloc(sizeof (Eina_File_Map));\r
- if (!map) return NULL;\r
-\r
- data = MapViewOfFile(file->fm, FILE_MAP_READ,\r
- offset & 0xffff0000,\r
- offset & 0x0000ffff,\r
- length);\r
- if (!data)\r
- map->map = MAP_FAILED;\r
- else\r
- map->map = data;\r
-\r
- map->offset = offset;\r
- map->length = length;\r
- map->refcount = 0;\r
-\r
- if (map->map == MAP_FAILED)\r
- {\r
- free(map);\r
- return NULL;\r
- }\r
-\r
- eina_hash_add(file->map, &key, map);\r
- eina_hash_direct_add(file->rmap, map->map, map);\r
- }\r
-\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) goto on_exit;\r
-\r
- UnmapViewOfFile(file->global_map);\r
- file->global_map = MAP_FAILED;\r
- }\r
- else\r
- {\r
- Eina_File_Map *em;\r
- unsigned long int key[2];\r
-\r
- em = eina_hash_find(file->rmap, &map);\r
- if (!em) return ;\r
-\r
- em->refcount--;\r
-\r
- if (em->refcount > 0) goto on_exit;\r
-\r
- key[0] = em->offset;\r
- key[1] = em->length;\r
-\r
- 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
+/* EINA - EFL data type library
+ * Copyright (C) 2010 Vincent Torri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+
+#include <Evil.h>
+
+#include "eina_config.h"
+#include "eina_private.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_file.h"
+#include "eina_stringshare.h"
+#include "eina_hash.h"
+#include "eina_list.h"
+#include "eina_lock.h"
+#include "eina_log.h"
+
+/*============================================================================*
+ * Local *
+ *============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+#ifndef EINA_LOG_COLOR_DEFAULT
+#define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
+#endif
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_file_log_dom, __VA_ARGS__)
+
+#ifdef WRN
+#undef WRN
+#endif
+#define WRN(...) EINA_LOG_DOM_WARN(_eina_file_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_file_log_dom, __VA_ARGS__)
+
+#ifdef MAP_FAILED
+# undef MAP_FAILED
+#endif
+#define MAP_FAILED ((void *)-1)
+
+typedef struct _Eina_File_Iterator Eina_File_Iterator;
+typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;
+typedef struct _Eina_File_Map Eina_File_Map;
+
+struct _Eina_File_Iterator
+{
+ Eina_Iterator iterator;
+
+ WIN32_FIND_DATA data;
+ HANDLE handle;
+ size_t length;
+ Eina_Bool is_last : 1;
+
+ char dir[1];
+};
+
+struct _Eina_File_Direct_Iterator
+{
+ Eina_Iterator iterator;
+
+ WIN32_FIND_DATA data;
+ HANDLE handle;
+ size_t length;
+ Eina_Bool is_last : 1;
+
+ Eina_File_Direct_Info info;
+
+ char dir[1];
+};
+
+struct _Eina_File
+{
+ const char *filename;
+
+ Eina_Hash *map;
+ Eina_Hash *rmap;
+ void *global_map;
+
+ Eina_Lock lock;
+
+ ULONGLONG length;
+ ULONGLONG mtime;
+
+ int refcount;
+ int global_refcount;
+
+ HANDLE handle;
+ HANDLE fm;
+
+ Eina_Bool shared : 1;
+ Eina_Bool delete_me : 1;
+};
+
+struct _Eina_File_Map
+{
+ void *map;
+
+ unsigned long int offset;
+ unsigned long int length;
+
+ int refcount;
+};
+
+static Eina_Hash *_eina_file_cache = NULL;
+static Eina_Lock _eina_file_lock_cache;
+
+static int _eina_file_log_dom = -1;
+
+static void
+_eina_file_win32_backslash_change(char *dir)
+{
+ char *tmp;
+
+ tmp = dir;
+ while (*tmp)
+ {
+ if (*tmp == '/') *tmp = '\\';
+ tmp++;
+ }
+}
+
+static Eina_Bool
+_eina_file_win32_is_dir(const char *dir)
+{
+#ifdef UNICODE
+ wchar_t *wdir = NULL;
+#endif
+ DWORD attr;
+
+ /* check if it's a directory */
+#ifdef UNICODE
+ wdir = evil_char_to_wchar(dir);
+ if (!wdir)
+ return EINA_FALSE;
+
+ attr = GetFileAttributes(wdir);
+ free(wdir);
+#else
+ attr = GetFileAttributes(dir);
+#endif
+
+ if (attr == 0xFFFFFFFF)
+ return EINA_FALSE;
+
+ if (!(attr & FILE_ATTRIBUTE_DIRECTORY))
+ return EINA_FALSE;
+
+ return EINA_TRUE;
+}
+
+static char *
+_eina_file_win32_dir_new(const char *dir)
+{
+ char *new_dir;
+ size_t length;
+
+ length = strlen(dir);
+
+ new_dir = (char *)malloc(sizeof(char) * length + 5);
+ if (!new_dir)
+ return NULL;
+
+ memcpy(new_dir, dir, length);
+ memcpy(new_dir + length, "\\*.*", 5);
+ _eina_file_win32_backslash_change(new_dir);
+
+ return new_dir;
+}
+
+static HANDLE
+_eina_file_win32_first_file(const char *dir, WIN32_FIND_DATA *fd)
+{
+ HANDLE h;
+#ifdef UNICODE
+ wchar_t *wdir = NULL;
+
+ wdir = evil_char_to_wchar(dir);
+ if (!wdir)
+ return NULL;
+
+ h = FindFirstFile(wdir, fd);
+ free(wdir);
+#else
+ h = FindFirstFile(dir, fd);
+#endif
+
+ if (!h)
+ return NULL;
+
+ while ((fd->cFileName[0] == '.') &&
+ ((fd->cFileName[1] == '\0') ||
+ ((fd->cFileName[1] == '.') && (fd->cFileName[2] == '\0'))))
+ {
+ if (!FindNextFile(h, fd))
+ return NULL;
+ }
+
+ return h;
+}
+
+static Eina_Bool
+_eina_file_win32_ls_iterator_next(Eina_File_Iterator *it, void **data)
+{
+#ifdef UNICODE
+ wchar_t *old_name;
+#else
+ char *old_name;
+#endif
+ char *name;
+ char *cname;
+ size_t length;
+ Eina_Bool is_last;
+ Eina_Bool res = EINA_TRUE;
+
+ if (it->handle == INVALID_HANDLE_VALUE)
+ return EINA_FALSE;
+
+ is_last = it->is_last;
+#ifdef UNICODE
+ old_name = _wcsdup(it->data.cFileName);
+#else
+ old_name = _strdup(it->data.cFileName);
+#endif
+ if (!old_name)
+ return EINA_FALSE;
+
+ do {
+ if (!FindNextFile(it->handle, &it->data))
+ {
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ it->is_last = EINA_TRUE;
+ else
+ res = EINA_FALSE;
+ }
+ } while ((it->data.cFileName[0] == '.') &&
+ ((it->data.cFileName[1] == '\0') ||
+ ((it->data.cFileName[1] == '.') && (it->data.cFileName[2] == '\0')))); /* FIXME: what about UNICODE ? */
+
+#ifdef UNICODE
+ cname = evil_wchar_to_char(old_name);
+ if (!cname)
+ return EINA_FALSE;
+#else
+ cname = old_name;
+#endif
+
+ length = strlen(cname);
+ name = alloca(length + 2 + it->length);
+
+ memcpy(name, it->dir, it->length);
+ memcpy(name + it->length, "\\", 1);
+ memcpy(name + it->length + 1, cname, length + 1);
+
+ *data = (char *)eina_stringshare_add(name);
+
+#ifdef UNICODE
+ free(cname);
+#endif
+ free(old_name);
+
+ if (is_last)
+ res = EINA_FALSE;
+
+ return res;
+}
+
+static HANDLE
+_eina_file_win32_ls_iterator_container(Eina_File_Iterator *it)
+{
+ return it->handle;
+}
+
+static void
+_eina_file_win32_ls_iterator_free(Eina_File_Iterator *it)
+{
+ if (it->handle != INVALID_HANDLE_VALUE)
+ FindClose(it->handle);
+
+ EINA_MAGIC_SET(&it->iterator, 0);
+ free(it);
+}
+
+static Eina_Bool
+_eina_file_win32_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
+{
+#ifdef UNICODE
+ wchar_t *old_name;
+#else
+ char *old_name;
+#endif
+ char *cname;
+ size_t length;
+ DWORD attr;
+ Eina_Bool is_last;
+ Eina_Bool res = EINA_TRUE;
+
+ if (it->handle == INVALID_HANDLE_VALUE)
+ return EINA_FALSE;
+
+ attr = it->data.dwFileAttributes;
+ is_last = it->is_last;
+#ifdef UNICODE
+ old_name = _wcsdup(it->data.cFileName);
+#else
+ old_name = _strdup(it->data.cFileName);
+#endif
+ if (!old_name)
+ return EINA_FALSE;
+
+ do {
+ if (!FindNextFile(it->handle, &it->data))
+ {
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ it->is_last = EINA_TRUE;
+ else
+ res = EINA_FALSE;
+ }
+
+#ifdef UNICODE
+ length = wcslen(old_name);
+#else
+ length = strlen(old_name);
+#endif
+ if (it->info.name_start + length + 1 >= PATH_MAX)
+ {
+ free(old_name);
+#ifdef UNICODE
+ old_name = _wcsdup(it->data.cFileName);
+#else
+ old_name = _strdup(it->data.cFileName);
+#endif
+ continue;
+ }
+
+ } while ((it->data.cFileName[0] == '.') &&
+ ((it->data.cFileName[1] == '\0') ||
+ ((it->data.cFileName[1] == '.') && (it->data.cFileName[2] == '\0')))); /* FIXME: what about UNICODE ? */
+
+#ifdef UNICODE
+ cname = evil_wchar_to_char(old_name);
+ if (!cname)
+ return EINA_FALSE;
+#else
+ cname = old_name;
+#endif
+
+ memcpy(it->info.path + it->info.name_start, cname, length);
+ it->info.name_length = length;
+ it->info.path_length = it->info.name_start + length;
+ it->info.path[it->info.path_length] = '\0';
+
+ if (attr & FILE_ATTRIBUTE_DIRECTORY)
+ it->info.type = EINA_FILE_DIR;
+ else if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
+ it->info.type = EINA_FILE_LNK;
+ else if (attr & (FILE_ATTRIBUTE_ARCHIVE |
+ FILE_ATTRIBUTE_COMPRESSED |
+ FILE_ATTRIBUTE_COMPRESSED |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_NORMAL |
+ FILE_ATTRIBUTE_SPARSE_FILE |
+ FILE_ATTRIBUTE_TEMPORARY))
+ it->info.type = EINA_FILE_REG;
+ else
+ it->info.type = EINA_FILE_UNKNOWN;
+
+ *data = &it->info;
+
+#ifdef UNICODE
+ free(cname);
+#endif
+
+ free(old_name);
+
+ if (is_last)
+ res = EINA_FALSE;
+
+ return res;
+}
+
+static HANDLE
+_eina_file_win32_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)
+{
+ return it->handle;
+}
+
+static void
+_eina_file_win32_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)
+{
+ if (it->handle != INVALID_HANDLE_VALUE)
+ FindClose(it->handle);
+
+ EINA_MAGIC_SET(&it->iterator, 0);
+ free(it);
+}
+
+static void
+_eina_file_real_close(Eina_File *file)
+{
+ eina_hash_free(file->rmap);
+ eina_hash_free(file->map);
+
+ if (file->global_map != MAP_FAILED)
+ UnmapViewOfFile(file->global_map);
+
+ CloseHandle(file->fm);
+ CloseHandle(file->handle);
+
+ eina_stringshare_del(file->filename);
+
+ free(file);
+}
+
+static void
+_eina_file_map_close(Eina_File_Map *map)
+{
+ if (map->map != MAP_FAILED)
+ UnmapViewOfFile(map->map);
+ free(map);
+}
+
+static unsigned int
+_eina_file_map_key_length(const void *key __UNUSED__)
+{
+ return sizeof (unsigned long int) * 2;
+}
+
+static int
+_eina_file_map_key_cmp(const unsigned long int *key1, int key1_length __UNUSED__,
+ const unsigned long int *key2, int key2_length __UNUSED__)
+{
+ if (key1[0] - key2[0] == 0) return key1[1] - key2[1];
+ return key1[0] - key2[0];
+}
+
+static int
+_eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__)
+{
+ return eina_hash_int64(&key[0], sizeof (unsigned long int))
+ ^ eina_hash_int64(&key[1], sizeof (unsigned long int));
+}
+
+static char *
+_eina_file_win32_escape(const char *path, size_t *length)
+{
+ char *result = strdup(path ? path : "");
+ char *p = result;
+ char *q = result;
+ size_t len;
+
+ if (!result)
+ return NULL;
+
+ if (length) len = *length;
+ else len = strlen(result);
+
+ while ((p = strchr(p, '/')))
+ {
+ // remove double `/'
+ if (p[1] == '/')
+ {
+ memmove(p, p + 1, --len - (p - result));
+ result[len] = '\0';
+ }
+ else
+ if (p[1] == '.'
+ && p[2] == '.')
+ {
+ // remove `/../'
+ if (p[3] == '/')
+ {
+ char tmp;
+
+ len -= p + 3 - q;
+ memmove(q, p + 3, len - (q - result));
+ result[len] = '\0';
+ p = q;
+
+ /* Update q correctly. */
+ tmp = *p;
+ *p = '\0';
+ q = strrchr(result, '/');
+ if (!q) q = result;
+ *p = tmp;
+ }
+ else
+ // remove '/..$'
+ if (p[3] == '\0')
+ {
+ len -= p + 2 - q;
+ result[len] = '\0';
+ q = p;
+ ++p;
+ }
+ else
+ {
+ q = p;
+ ++p;
+ }
+ }
+ else
+ {
+ q = p;
+ ++p;
+ }
+ }
+
+ if (length)
+ *length = len;
+
+ return result;
+}
+
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+ * Global *
+ *============================================================================*/
+
+Eina_Bool
+eina_file_init(void)
+{
+ _eina_file_log_dom = eina_log_domain_register("eina_file",
+ EINA_LOG_COLOR_DEFAULT);
+ if (_eina_file_log_dom < 0)
+ {
+ EINA_LOG_ERR("Could not register log domain: eina_file");
+ return EINA_FALSE;
+ }
+
+ _eina_file_cache = eina_hash_string_djb2_new(NULL);
+ if (!_eina_file_cache)
+ {
+ ERR("Could not create cache.");
+ eina_log_domain_unregister(_eina_file_log_dom);
+ _eina_file_log_dom = -1;
+ return EINA_FALSE;
+ }
+
+ eina_lock_new(&_eina_file_lock_cache);
+
+ return EINA_TRUE;
+}
+
+Eina_Bool
+eina_file_shutdown(void)
+{
+ if (eina_hash_population(_eina_file_cache) > 0)
+ {
+ Eina_Iterator *it;
+ const char *key;
+
+ it = eina_hash_iterator_key_new(_eina_file_cache);
+ EINA_ITERATOR_FOREACH(it, key)
+ ERR("File [%s] still open !", key);
+ eina_iterator_free(it);
+ }
+
+ eina_hash_free(_eina_file_cache);
+
+ eina_lock_free(&_eina_file_lock_cache);
+
+ eina_log_domain_unregister(_eina_file_log_dom);
+ _eina_file_log_dom = -1;
+ return EINA_TRUE;
+}
+
+/*============================================================================*
+ * API *
+ *============================================================================*/
+
+
+EAPI char *
+eina_file_path_sanitize(const char *path)
+{
+ char *result = NULL;
+ size_t len;
+
+ if (!path) return NULL;
+
+ len = strlen(path);
+ if (len < 3) return NULL;
+
+ if (!evil_path_is_absolute(path))
+ {
+ DWORD l;
+
+ l = GetCurrentDirectory(0, NULL);
+ if (l > 0)
+ {
+ char *cwd;
+ DWORD l2;
+
+ cwd = alloca(sizeof(char) * (l + 1));
+ l2 = GetCurrentDirectory(l + 1, cwd);
+ if (l2 == l)
+ {
+ char *tmp;
+
+ len += l + 2;
+ tmp = alloca(sizeof (char) * len);
+ snprintf(tmp, len, "%s/%s", cwd, path);
+ tmp[len - 1] = '\0';
+ result = tmp;
+ }
+ }
+ }
+
+ return _eina_file_win32_escape(result ? result : path, &len);
+}
+
+EAPI Eina_Bool
+eina_file_dir_list(const char *dir,
+ Eina_Bool recursive,
+ Eina_File_Dir_List_Cb cb,
+ void *data)
+{
+ WIN32_FIND_DATA file;
+ HANDLE h;
+ char *new_dir;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(cb, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE);
+
+ if (!_eina_file_win32_is_dir(dir))
+ return EINA_FALSE;
+
+ new_dir = _eina_file_win32_dir_new(dir);
+ if (!new_dir)
+ return EINA_FALSE;
+
+ h = _eina_file_win32_first_file(new_dir, &file);
+
+ if (h == INVALID_HANDLE_VALUE)
+ return EINA_FALSE;
+
+ do
+ {
+ char *filename;
+
+# ifdef UNICODE
+ filename = evil_wchar_to_char(file.cFileName);
+# else
+ filename = file.cFileName;
+# endif /* ! UNICODE */
+ if (!strcmp(filename, ".") || !strcmp(filename, ".."))
+ continue;
+
+ cb(filename, dir, data);
+
+ if (recursive == EINA_TRUE)
+ {
+ char *path;
+
+ path = alloca(strlen(dir) + strlen(filename) + 2);
+ strcpy(path, dir);
+ strcat(path, "/");
+ strcat(path, filename);
+
+ if (!(file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ continue;
+
+ eina_file_dir_list(path, recursive, cb, data);
+ }
+
+# ifdef UNICODE
+ free(filename);
+# endif /* UNICODE */
+
+ } while (FindNextFile(h, &file));
+ FindClose(h);
+
+ return EINA_TRUE;
+}
+
+EAPI Eina_Array *
+eina_file_split(char *path)
+{
+ Eina_Array *ea;
+ char *current;
+ size_t length;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
+
+ ea = eina_array_new(16);
+
+ if (!ea)
+ return NULL;
+
+ for (current = strchr(path, '\\');
+ current;
+ path = current + 1, current = strchr(path, '\\'))
+ {
+ length = current - path;
+
+ if (length <= 0)
+ continue;
+
+ eina_array_push(ea, path);
+ *current = '\0';
+ }
+
+ if (*path != '\0')
+ eina_array_push(ea, path);
+
+ return ea;
+}
+
+EAPI Eina_Iterator *
+eina_file_ls(const char *dir)
+{
+ Eina_File_Iterator *it;
+ char *new_dir;
+ size_t length;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
+
+ if (!dir || !*dir)
+ return NULL;
+
+ if (!_eina_file_win32_is_dir(dir))
+ return NULL;
+
+ length = strlen(dir);
+
+ it = calloc(1, sizeof (Eina_File_Iterator) + length);
+ if (!it)
+ return NULL;
+
+ EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+ new_dir = _eina_file_win32_dir_new(dir);
+ if (!new_dir)
+ goto free_it;
+
+ it->handle = _eina_file_win32_first_file(new_dir, &it->data);
+ free(new_dir);
+ if (it->handle == INVALID_HANDLE_VALUE)
+ goto free_it;
+
+ memcpy(it->dir, dir, length + 1);
+ if (dir[length - 1] != '\\')
+ it->length = length;
+ else
+ it->length = length - 1;
+ _eina_file_win32_backslash_change(it->dir);
+
+ it->iterator.version = EINA_ITERATOR_VERSION;
+ it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_win32_ls_iterator_next);
+ it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_win32_ls_iterator_container);
+ it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_win32_ls_iterator_free);
+
+ return &it->iterator;
+
+ free_it:
+ free(it);
+
+ return NULL;
+}
+
+EAPI Eina_Iterator *
+eina_file_direct_ls(const char *dir)
+{
+ Eina_File_Direct_Iterator *it;
+ char *new_dir;
+ size_t length;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
+
+ if (!dir || !*dir)
+ return NULL;
+
+ length = strlen(dir);
+
+ if (length + 12 + 2 >= MAX_PATH)
+ return NULL;
+
+ it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
+ if (!it)
+ return NULL;
+
+ EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+ new_dir = _eina_file_win32_dir_new(dir);
+ if (!new_dir)
+ goto free_it;
+
+ it->handle = _eina_file_win32_first_file(new_dir, &it->data);
+ free(new_dir);
+ if (it->handle == INVALID_HANDLE_VALUE)
+ goto free_it;
+
+ memcpy(it->dir, dir, length + 1);
+ it->length = length;
+ _eina_file_win32_backslash_change(it->dir);
+
+ memcpy(it->info.path, dir, length);
+ if (dir[length - 1] == '\\')
+ it->info.name_start = length;
+ else
+ {
+ it->info.path[length] = '\\';
+ it->info.name_start = length + 1;
+ }
+ _eina_file_win32_backslash_change(it->info.path);
+
+ it->iterator.version = EINA_ITERATOR_VERSION;
+ it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_win32_direct_ls_iterator_next);
+ it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_win32_direct_ls_iterator_container);
+ it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_win32_direct_ls_iterator_free);
+
+ return &it->iterator;
+
+ free_it:
+ free(it);
+
+ return NULL;
+}
+
+EAPI Eina_Iterator *
+eina_file_stat_ls(const char *dir)
+{
+ return eina_file_direct_ls(dir);
+}
+
+EAPI Eina_File *
+eina_file_open(const char *path, Eina_Bool shared)
+{
+ Eina_File *file;
+ Eina_File *n;
+ char *filename;
+ HANDLE handle;
+ HANDLE fm;
+ WIN32_FILE_ATTRIBUTE_DATA fad;
+ ULARGE_INTEGER length;
+ ULARGE_INTEGER mtime;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
+
+ filename = eina_file_path_sanitize(path);
+ if (!filename) return NULL;
+
+ /* FIXME: how to emulate shm_open ? Just OpenFileMapping ? */
+#if 0
+ if (shared)
+ handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,
+ NULL);
+ else
+#endif
+ handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,
+ NULL);
+
+ if (handle == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ fm = CreateFileMapping(handle, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (!fm)
+ goto close_handle;
+
+ if (!GetFileAttributesEx(filename, GetFileExInfoStandard, &fad))
+ goto close_fm;
+
+ length.u.LowPart = fad.nFileSizeLow;
+ length.u.HighPart = fad.nFileSizeHigh;
+ mtime.u.LowPart = fad.ftLastWriteTime.dwLowDateTime;
+ mtime.u.HighPart = fad.ftLastWriteTime.dwHighDateTime;
+
+ eina_lock_take(&_eina_file_lock_cache);
+
+ file = eina_hash_find(_eina_file_cache, filename);
+ if (file &&
+ (file->mtime == mtime.QuadPart && file->length == length.QuadPart))
+ {
+ file->delete_me = EINA_TRUE;
+ eina_hash_del(_eina_file_cache, file->filename, file);
+ _eina_file_real_close(file);
+ file = NULL;
+ }
+
+ if (!file)
+ {
+ n = malloc(sizeof (Eina_File) + strlen(filename) + 1);
+ if (!n)
+ {
+ eina_lock_release(&_eina_file_lock_cache);
+ goto close_fm;
+ }
+
+ n->filename = (char*) (n + 1);
+ strcpy((char*) n->filename, filename);
+ n->map = eina_hash_new(EINA_KEY_LENGTH(_eina_file_map_key_length),
+ EINA_KEY_CMP(_eina_file_map_key_cmp),
+ EINA_KEY_HASH(_eina_file_map_key_hash),
+ EINA_FREE_CB(_eina_file_map_close),
+ 3);
+ n->rmap = eina_hash_pointer_new(NULL);
+ n->global_map = MAP_FAILED;
+ n->global_refcount = 0;
+ n->length = length.QuadPart;
+ n->mtime = mtime.QuadPart;
+ n->refcount = 0;
+ n->handle = handle;
+ n->fm = fm;
+ n->shared = shared;
+ n->delete_me = EINA_FALSE;
+ eina_lock_new(&n->lock);
+ eina_hash_direct_add(_eina_file_cache, n->filename, n);
+ }
+ else
+ {
+ CloseHandle(fm);
+ CloseHandle(handle);
+
+ n = file;
+ }
+ eina_lock_take(&n->lock);
+ n->refcount++;
+ eina_lock_release(&n->lock);
+
+ eina_lock_release(&_eina_file_lock_cache);
+
+ free(filename);
+
+ return n;
+
+ close_fm:
+ CloseHandle(fm);
+ close_handle:
+ CloseHandle(handle);
+
+ return NULL;
+}
+
+EAPI void
+eina_file_close(Eina_File *file)
+{
+ EINA_SAFETY_ON_NULL_RETURN(file);
+
+ eina_lock_take(&file->lock);
+ file->refcount--;
+ eina_lock_release(&file->lock);
+
+ if (file->refcount != 0) return ;
+ eina_lock_take(&_eina_file_lock_cache);
+
+ eina_hash_del(_eina_file_cache, file->filename, file);
+ _eina_file_real_close(file);
+
+ eina_lock_release(&_eina_file_lock_cache);
+}
+
+EAPI size_t
+eina_file_size_get(Eina_File *file)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
+ return file->length;
+}
+
+EAPI time_t
+eina_file_mtime_get(Eina_File *file)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
+ return file->mtime;
+}
+
+EAPI const char *
+eina_file_filename_get(Eina_File *file)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+ return file->filename;
+}
+
+EAPI void *
+eina_file_map_all(Eina_File *file, Eina_File_Populate rule __UNUSED__)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
+ eina_lock_take(&file->lock);
+ if (file->global_map == MAP_FAILED)
+ {
+ void *data;
+
+ data = MapViewOfFile(file->fm, FILE_MAP_READ,
+ 0, 0, file->length);
+ if (!data)
+ file->global_map = MAP_FAILED;
+ else
+ file->global_map = data;
+ }
+
+ if (file->global_map != MAP_FAILED)
+ {
+ file->global_refcount++;
+ return file->global_map;
+ }
+
+ eina_lock_release(&file->lock);
+ return NULL;
+}
+
+EAPI void *
+eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
+ unsigned long int offset, unsigned long int length)
+{
+ Eina_File_Map *map;
+ unsigned long int key[2];
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
+ if (offset > file->length)
+ return NULL;
+ if (offset + length > file->length)
+ return NULL;
+
+ if (offset == 0 && length == file->length)
+ return eina_file_map_all(file, rule);
+
+ key[0] = offset;
+ key[1] = length;
+
+ eina_lock_take(&file->lock);
+
+ map = eina_hash_find(file->map, &key);
+ if (!map)
+ {
+ void *data;
+
+ map = malloc(sizeof (Eina_File_Map));
+ if (!map) return NULL;
+
+ data = MapViewOfFile(file->fm, FILE_MAP_READ,
+ offset & 0xffff0000,
+ offset & 0x0000ffff,
+ length);
+ if (!data)
+ map->map = MAP_FAILED;
+ else
+ map->map = data;
+
+ map->offset = offset;
+ map->length = length;
+ map->refcount = 0;
+
+ if (map->map == MAP_FAILED)
+ {
+ free(map);
+ return NULL;
+ }
+
+ eina_hash_add(file->map, &key, map);
+ eina_hash_direct_add(file->rmap, map->map, map);
+ }
+
+ map->refcount++;
+
+ eina_lock_release(&file->lock);
+
+ return map->map;
+}
+
+EAPI void
+eina_file_map_free(Eina_File *file, void *map)
+{
+ EINA_SAFETY_ON_NULL_RETURN(file);
+
+ eina_lock_take(&file->lock);
+
+ if (file->global_map == map)
+ {
+ file->global_refcount--;
+
+ if (file->global_refcount > 0) goto on_exit;
+
+ UnmapViewOfFile(file->global_map);
+ file->global_map = MAP_FAILED;
+ }
+ else
+ {
+ Eina_File_Map *em;
+ unsigned long int key[2];
+
+ em = eina_hash_find(file->rmap, &map);
+ if (!em) return ;
+
+ em->refcount--;
+
+ if (em->refcount > 0) goto on_exit;
+
+ key[0] = em->offset;
+ key[1] = em->length;
+
+ eina_hash_del(file->rmap, &map, em);
+ eina_hash_del(file->map, &key, em);
+ }
+
+ on_exit:
+ eina_lock_release(&file->lock);
+}