1 /* EINA - EFL data type library
2 * Copyright (C) 2010 Vincent Torri
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
25 #elif defined __GNUC__
26 # define alloca __builtin_alloca
28 # define alloca __alloca
29 #elif defined _MSC_VER
31 # define alloca _alloca
37 void *alloca (size_t);
40 #define WIN32_LEAN_AND_MEAN
42 #undef WIN32_LEAN_AND_MEAN
46 #include "eina_config.h"
47 #include "eina_private.h"
49 /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
50 #include "eina_safety_checks.h"
51 #include "eina_file.h"
52 #include "eina_stringshare.h"
53 #include "eina_hash.h"
54 #include "eina_list.h"
55 #include "eina_lock.h"
58 /*============================================================================*
60 *============================================================================*/
66 #ifndef EINA_LOG_COLOR_DEFAULT
67 #define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
73 #define ERR(...) EINA_LOG_DOM_ERR(_eina_file_log_dom, __VA_ARGS__)
78 #define WRN(...) EINA_LOG_DOM_WARN(_eina_file_log_dom, __VA_ARGS__)
83 #define DBG(...) EINA_LOG_DOM_DBG(_eina_file_log_dom, __VA_ARGS__)
88 #define MAP_FAILED ((void *)-1)
90 typedef struct _Eina_File_Iterator Eina_File_Iterator;
91 typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;
92 typedef struct _Eina_File_Map Eina_File_Map;
94 struct _Eina_File_Iterator
96 Eina_Iterator iterator;
101 Eina_Bool is_last : 1;
106 struct _Eina_File_Direct_Iterator
108 Eina_Iterator iterator;
110 WIN32_FIND_DATA data;
113 Eina_Bool is_last : 1;
115 Eina_File_Direct_Info info;
122 const char *filename;
139 Eina_Bool shared : 1;
140 Eina_Bool delete_me : 1;
143 struct _Eina_File_Map
147 unsigned long int offset;
148 unsigned long int length;
153 static Eina_Hash *_eina_file_cache = NULL;
154 static Eina_Lock _eina_file_lock_cache;
156 static int _eina_file_log_dom = -1;
159 _eina_file_win32_backslash_change(char *dir)
166 if (*tmp == '/') *tmp = '\\';
172 _eina_file_win32_is_dir(const char *dir)
175 wchar_t *wdir = NULL;
179 /* check if it's a directory */
181 wdir = evil_char_to_wchar(dir);
185 attr = GetFileAttributes(wdir);
188 attr = GetFileAttributes(dir);
191 if (attr == 0xFFFFFFFF)
194 if (!(attr & FILE_ATTRIBUTE_DIRECTORY))
201 _eina_file_win32_dir_new(const char *dir)
206 length = strlen(dir);
208 new_dir = (char *)malloc(sizeof(char) * length + 5);
212 memcpy(new_dir, dir, length);
213 memcpy(new_dir + length, "\\*.*", 5);
214 _eina_file_win32_backslash_change(new_dir);
220 _eina_file_win32_first_file(const char *dir, WIN32_FIND_DATA *fd)
224 wchar_t *wdir = NULL;
226 wdir = evil_char_to_wchar(dir);
230 h = FindFirstFile(wdir, fd);
233 h = FindFirstFile(dir, fd);
239 while ((fd->cFileName[0] == '.') &&
240 ((fd->cFileName[1] == '\0') ||
241 ((fd->cFileName[1] == '.') && (fd->cFileName[2] == '\0'))))
243 if (!FindNextFile(h, fd))
251 _eina_file_win32_ls_iterator_next(Eina_File_Iterator *it, void **data)
262 Eina_Bool res = EINA_TRUE;
264 if (it->handle == INVALID_HANDLE_VALUE)
267 is_last = it->is_last;
269 old_name = _wcsdup(it->data.cFileName);
271 old_name = _strdup(it->data.cFileName);
277 if (!FindNextFile(it->handle, &it->data))
279 if (GetLastError() == ERROR_NO_MORE_FILES)
280 it->is_last = EINA_TRUE;
284 } while ((it->data.cFileName[0] == '.') &&
285 ((it->data.cFileName[1] == '\0') ||
286 ((it->data.cFileName[1] == '.') && (it->data.cFileName[2] == '\0')))); /* FIXME: what about UNICODE ? */
289 cname = evil_wchar_to_char(old_name);
296 length = strlen(cname);
297 name = alloca(length + 2 + it->length);
299 memcpy(name, it->dir, it->length);
300 memcpy(name + it->length, "\\", 1);
301 memcpy(name + it->length + 1, cname, length + 1);
303 *data = (char *)eina_stringshare_add(name);
317 _eina_file_win32_ls_iterator_container(Eina_File_Iterator *it)
323 _eina_file_win32_ls_iterator_free(Eina_File_Iterator *it)
325 if (it->handle != INVALID_HANDLE_VALUE)
326 FindClose(it->handle);
328 EINA_MAGIC_SET(&it->iterator, 0);
333 _eina_file_win32_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
344 Eina_Bool res = EINA_TRUE;
346 if (it->handle == INVALID_HANDLE_VALUE)
349 attr = it->data.dwFileAttributes;
350 is_last = it->is_last;
352 old_name = _wcsdup(it->data.cFileName);
354 old_name = _strdup(it->data.cFileName);
360 if (!FindNextFile(it->handle, &it->data))
362 if (GetLastError() == ERROR_NO_MORE_FILES)
363 it->is_last = EINA_TRUE;
369 length = wcslen(old_name);
371 length = strlen(old_name);
373 if (it->info.name_start + length + 1 >= PATH_MAX)
377 old_name = _wcsdup(it->data.cFileName);
379 old_name = _strdup(it->data.cFileName);
384 } while ((it->data.cFileName[0] == '.') &&
385 ((it->data.cFileName[1] == '\0') ||
386 ((it->data.cFileName[1] == '.') && (it->data.cFileName[2] == '\0')))); /* FIXME: what about UNICODE ? */
389 cname = evil_wchar_to_char(old_name);
396 memcpy(it->info.path + it->info.name_start, cname, length);
397 it->info.name_length = length;
398 it->info.path_length = it->info.name_start + length;
399 it->info.path[it->info.path_length] = '\0';
401 if (attr & FILE_ATTRIBUTE_DIRECTORY)
402 it->info.type = EINA_FILE_DIR;
403 else if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
404 it->info.type = EINA_FILE_LNK;
405 else if (attr & (FILE_ATTRIBUTE_ARCHIVE |
406 FILE_ATTRIBUTE_COMPRESSED |
407 FILE_ATTRIBUTE_COMPRESSED |
408 FILE_ATTRIBUTE_HIDDEN |
409 FILE_ATTRIBUTE_NORMAL |
410 FILE_ATTRIBUTE_SPARSE_FILE |
411 FILE_ATTRIBUTE_TEMPORARY))
412 it->info.type = EINA_FILE_REG;
414 it->info.type = EINA_FILE_UNKNOWN;
431 _eina_file_win32_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)
437 _eina_file_win32_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)
439 if (it->handle != INVALID_HANDLE_VALUE)
440 FindClose(it->handle);
442 EINA_MAGIC_SET(&it->iterator, 0);
447 _eina_file_real_close(Eina_File *file)
449 eina_hash_free(file->rmap);
450 eina_hash_free(file->map);
452 if (file->global_map != MAP_FAILED)
453 UnmapViewOfFile(file->global_map);
455 CloseHandle(file->fm);
456 CloseHandle(file->handle);
462 _eina_file_map_close(Eina_File_Map *map)
464 if (map->map != MAP_FAILED)
465 UnmapViewOfFile(map->map);
470 _eina_file_map_key_length(const void *key __UNUSED__)
472 return sizeof (unsigned long int) * 2;
476 _eina_file_map_key_cmp(const unsigned long int *key1, int key1_length __UNUSED__,
477 const unsigned long int *key2, int key2_length __UNUSED__)
479 if (key1[0] - key2[0] == 0) return key1[1] - key2[1];
480 return key1[0] - key2[0];
484 _eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__)
486 return eina_hash_int64(&key[0], sizeof (unsigned long int))
487 ^ eina_hash_int64(&key[1], sizeof (unsigned long int));
491 _eina_file_win32_escape(const char *path, size_t *length)
493 char *result = strdup(path ? path : "");
501 if (length) len = *length;
502 else len = strlen(result);
504 while ((p = strchr(p, '/')))
509 memmove(p, p + 1, --len - (p - result));
522 memmove(q, p + 3, len - (q - result));
526 /* Update q correctly. */
529 q = strrchr(result, '/');
566 /*============================================================================*
568 *============================================================================*/
573 _eina_file_log_dom = eina_log_domain_register("eina_file",
574 EINA_LOG_COLOR_DEFAULT);
575 if (_eina_file_log_dom < 0)
577 EINA_LOG_ERR("Could not register log domain: eina_file");
581 _eina_file_cache = eina_hash_string_djb2_new(NULL);
582 if (!_eina_file_cache)
584 ERR("Could not create cache.");
585 eina_log_domain_unregister(_eina_file_log_dom);
586 _eina_file_log_dom = -1;
590 eina_lock_new(&_eina_file_lock_cache);
596 eina_file_shutdown(void)
598 if (eina_hash_population(_eina_file_cache) > 0)
603 it = eina_hash_iterator_key_new(_eina_file_cache);
604 EINA_ITERATOR_FOREACH(it, key)
605 ERR("File [%s] still open !", key);
606 eina_iterator_free(it);
609 eina_hash_free(_eina_file_cache);
611 eina_lock_free(&_eina_file_lock_cache);
613 eina_log_domain_unregister(_eina_file_log_dom);
614 _eina_file_log_dom = -1;
618 /*============================================================================*
620 *============================================================================*/
624 eina_file_path_sanitize(const char *path)
629 if (!path) return NULL;
632 if (len < 3) return NULL;
634 if (!evil_path_is_absolute(path))
638 l = GetCurrentDirectory(0, NULL);
644 cwd = alloca(sizeof(char) * (l + 1));
645 l2 = GetCurrentDirectory(l + 1, cwd);
651 tmp = alloca(sizeof (char) * len);
652 snprintf(tmp, len, "%s/%s", cwd, path);
659 return _eina_file_win32_escape(result ? result : path, &len);
663 eina_file_dir_list(const char *dir,
665 Eina_File_Dir_List_Cb cb,
668 WIN32_FIND_DATA file;
672 EINA_SAFETY_ON_NULL_RETURN_VAL(cb, EINA_FALSE);
673 EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE);
674 EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE);
676 if (!_eina_file_win32_is_dir(dir))
679 new_dir = _eina_file_win32_dir_new(dir);
683 h = _eina_file_win32_first_file(new_dir, &file);
685 if (h == INVALID_HANDLE_VALUE)
693 filename = evil_wchar_to_char(file.cFileName);
695 filename = file.cFileName;
696 # endif /* ! UNICODE */
697 if (!strcmp(filename, ".") || !strcmp(filename, ".."))
700 cb(filename, dir, data);
702 if (recursive == EINA_TRUE)
706 path = alloca(strlen(dir) + strlen(filename) + 2);
709 strcat(path, filename);
711 if (!(file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
714 eina_file_dir_list(path, recursive, cb, data);
719 # endif /* UNICODE */
721 } while (FindNextFile(h, &file));
728 eina_file_split(char *path)
734 EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
736 ea = eina_array_new(16);
741 for (current = strchr(path, '\\');
743 path = current + 1, current = strchr(path, '\\'))
745 length = current - path;
750 eina_array_push(ea, path);
755 eina_array_push(ea, path);
761 eina_file_ls(const char *dir)
763 Eina_File_Iterator *it;
767 EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
772 if (!_eina_file_win32_is_dir(dir))
775 length = strlen(dir);
777 it = calloc(1, sizeof (Eina_File_Iterator) + length);
781 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
783 new_dir = _eina_file_win32_dir_new(dir);
787 it->handle = _eina_file_win32_first_file(new_dir, &it->data);
789 if (it->handle == INVALID_HANDLE_VALUE)
792 memcpy(it->dir, dir, length + 1);
793 if (dir[length - 1] != '\\')
796 it->length = length - 1;
797 _eina_file_win32_backslash_change(it->dir);
799 it->iterator.version = EINA_ITERATOR_VERSION;
800 it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_win32_ls_iterator_next);
801 it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_win32_ls_iterator_container);
802 it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_win32_ls_iterator_free);
804 return &it->iterator;
813 eina_file_direct_ls(const char *dir)
815 Eina_File_Direct_Iterator *it;
819 EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
824 length = strlen(dir);
826 if (length + 12 + 2 >= MAX_PATH)
829 it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
833 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
835 new_dir = _eina_file_win32_dir_new(dir);
839 it->handle = _eina_file_win32_first_file(new_dir, &it->data);
841 if (it->handle == INVALID_HANDLE_VALUE)
844 memcpy(it->dir, dir, length + 1);
846 _eina_file_win32_backslash_change(it->dir);
848 memcpy(it->info.path, dir, length);
849 if (dir[length - 1] == '\\')
850 it->info.name_start = length;
853 it->info.path[length] = '\\';
854 it->info.name_start = length + 1;
856 _eina_file_win32_backslash_change(it->info.path);
858 it->iterator.version = EINA_ITERATOR_VERSION;
859 it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_win32_direct_ls_iterator_next);
860 it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_win32_direct_ls_iterator_container);
861 it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_win32_direct_ls_iterator_free);
863 return &it->iterator;
872 eina_file_stat_ls(const char *dir)
874 return eina_file_direct_ls(dir);
878 eina_file_open(const char *path, Eina_Bool shared)
885 WIN32_FILE_ATTRIBUTE_DATA fad;
886 ULARGE_INTEGER length;
887 ULARGE_INTEGER mtime;
889 EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
891 filename = eina_file_path_sanitize(path);
892 if (!filename) return NULL;
894 /* FIXME: how to emulate shm_open ? Just OpenFileMapping ? */
897 handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
898 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,
902 handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
903 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,
906 if (handle == INVALID_HANDLE_VALUE)
909 fm = CreateFileMapping(handle, NULL, PAGE_READONLY, 0, 0, NULL);
913 if (!GetFileAttributesEx(filename, GetFileExInfoStandard, &fad))
916 length.u.LowPart = fad.nFileSizeLow;
917 length.u.HighPart = fad.nFileSizeHigh;
918 mtime.u.LowPart = fad.ftLastWriteTime.dwLowDateTime;
919 mtime.u.HighPart = fad.ftLastWriteTime.dwHighDateTime;
921 eina_lock_take(&_eina_file_lock_cache);
923 file = eina_hash_find(_eina_file_cache, filename);
925 (file->mtime != mtime.QuadPart || file->length != length.QuadPart))
927 file->delete_me = EINA_TRUE;
928 eina_hash_del(_eina_file_cache, file->filename, file);
929 _eina_file_real_close(file);
935 n = malloc(sizeof (Eina_File) + strlen(filename) + 1);
938 eina_lock_release(&_eina_file_lock_cache);
942 n->filename = (char*) (n + 1);
943 strcpy((char*) n->filename, filename);
944 n->map = eina_hash_new(EINA_KEY_LENGTH(_eina_file_map_key_length),
945 EINA_KEY_CMP(_eina_file_map_key_cmp),
946 EINA_KEY_HASH(_eina_file_map_key_hash),
947 EINA_FREE_CB(_eina_file_map_close),
949 n->rmap = eina_hash_pointer_new(NULL);
950 n->global_map = MAP_FAILED;
951 n->global_refcount = 0;
952 n->length = length.QuadPart;
953 n->mtime = mtime.QuadPart;
958 n->delete_me = EINA_FALSE;
959 eina_lock_new(&n->lock);
960 eina_hash_direct_add(_eina_file_cache, n->filename, n);
969 eina_lock_take(&n->lock);
971 eina_lock_release(&n->lock);
973 eina_lock_release(&_eina_file_lock_cache);
988 eina_file_close(Eina_File *file)
990 EINA_SAFETY_ON_NULL_RETURN(file);
992 eina_lock_take(&file->lock);
994 eina_lock_release(&file->lock);
996 if (file->refcount != 0) return ;
997 eina_lock_take(&_eina_file_lock_cache);
999 eina_hash_del(_eina_file_cache, file->filename, file);
1000 _eina_file_real_close(file);
1002 eina_lock_release(&_eina_file_lock_cache);
1006 eina_file_size_get(Eina_File *file)
1008 EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1009 return file->length;
1013 eina_file_mtime_get(Eina_File *file)
1015 EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1020 eina_file_filename_get(Eina_File *file)
1022 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1023 return file->filename;
1027 eina_file_map_all(Eina_File *file, Eina_File_Populate rule __UNUSED__)
1029 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1031 eina_lock_take(&file->lock);
1032 if (file->global_map == MAP_FAILED)
1036 data = MapViewOfFile(file->fm, FILE_MAP_READ,
1037 0, 0, file->length);
1039 file->global_map = MAP_FAILED;
1041 file->global_map = data;
1044 if (file->global_map != MAP_FAILED)
1046 file->global_refcount++;
1047 return file->global_map;
1050 eina_lock_release(&file->lock);
1055 eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
1056 unsigned long int offset, unsigned long int length)
1059 unsigned long int key[2];
1061 EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1063 if (offset > file->length)
1065 if (offset + length > file->length)
1068 if (offset == 0 && length == file->length)
1069 return eina_file_map_all(file, rule);
1074 eina_lock_take(&file->lock);
1076 map = eina_hash_find(file->map, &key);
1081 map = malloc(sizeof (Eina_File_Map));
1084 eina_lock_release(&file->lock);
1088 data = MapViewOfFile(file->fm, FILE_MAP_READ,
1089 offset & 0xffff0000,
1090 offset & 0x0000ffff,
1093 map->map = MAP_FAILED;
1097 map->offset = offset;
1098 map->length = length;
1101 if (map->map == MAP_FAILED)
1104 eina_lock_release(&file->lock);
1108 eina_hash_add(file->map, &key, map);
1109 eina_hash_direct_add(file->rmap, map->map, map);
1114 eina_lock_release(&file->lock);
1120 eina_file_map_free(Eina_File *file, void *map)
1122 EINA_SAFETY_ON_NULL_RETURN(file);
1124 eina_lock_take(&file->lock);
1126 if (file->global_map == map)
1128 file->global_refcount--;
1130 if (file->global_refcount > 0) goto on_exit;
1132 UnmapViewOfFile(file->global_map);
1133 file->global_map = MAP_FAILED;
1138 unsigned long int key[2];
1140 em = eina_hash_find(file->rmap, &map);
1141 if (!em) goto on_exit;
1145 if (em->refcount > 0) goto on_exit;
1147 key[0] = em->offset;
1148 key[1] = em->length;
1150 eina_hash_del(file->rmap, &map, em);
1151 eina_hash_del(file->map, &key, em);
1155 eina_lock_release(&file->lock);