576 lines
14 KiB
C
576 lines
14 KiB
C
/* 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 "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);
|
|
}
|