/* 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 . */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_ALLOCA_H # include #elif defined __GNUC__ # define alloca __builtin_alloca #elif defined _AIX # define alloca __alloca #elif defined _MSC_VER # include # define alloca _alloca #else # include # ifdef __cplusplus extern "C" # endif void *alloca (size_t); #endif #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN #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" /*============================================================================* * Local * *============================================================================*/ /** * @cond LOCAL */ typedef struct _Eina_File_Iterator Eina_File_Iterator; typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator; 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]; }; 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) { char *old_name; 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; old_name = strdup(it->data.cFileName); 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')))); #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) { char *old_name; 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; old_name = strdup(it->data.cFileName); 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; } length = strlen(old_name); if (it->info.name_start + length + 1 >= PATH_MAX) { free(old_name); old_name = strdup(it->data.cFileName); continue; } } while ((it->data.cFileName[0] == '.') && ((it->data.cFileName[1] == '\0') || ((it->data.cFileName[1] == '.') && (it->data.cFileName[2] == '\0')))); #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); } /** * @endcond */ /*============================================================================* * Global * *============================================================================*/ /*============================================================================* * API * *============================================================================*/ 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; 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; 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); }