efl/legacy/eina/src/lib/eina_file.c

367 lines
8.6 KiB
C
Raw Normal View History

/* EINA - EFL data type library
* Copyright (C) 2007-2008 Jorge Luis Zapata Muga, 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
#ifndef _WIN32
# define _GNU_SOURCE
#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
#include <string.h>
#ifndef _WIN32
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <dirent.h>
#else
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# undef WIN32_LEAN_AND_MEAN
# include <Evil.h>
#endif /* _WIN2 */
#ifndef _WIN32
# define PATH_DELIM '/'
#else
# define PATH_DELIM '\\'
#endif
#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"
typedef struct _Eina_File_Iterator Eina_File_Iterator;
struct _Eina_File_Iterator
{
Eina_Iterator iterator;
DIR *dirp;
int length;
char dir[1];
};
static Eina_Bool
_eina_file_ls_iterator_next(Eina_File_Iterator *it, void **data)
{
struct dirent *dp;
char *name;
size_t length;
do
{
dp = readdir(it->dirp);
if (!dp) return EINA_FALSE;
}
while (!strcmp(dp->d_name, ".")
|| !strcmp(dp->d_name, ".."));
length = strlen(dp->d_name);
name = alloca(length + 2 + it->length);
memcpy(name, it->dir, it->length);
memcpy(name + it->length, "/", 1);
memcpy(name + it->length + 1, dp->d_name, length + 1);
*data = (char*) eina_stringshare_add(name);
return EINA_TRUE;
}
static char *
_eina_file_ls_iterator_container(Eina_File_Iterator *it)
{
return it->dir;
}
static void
_eina_file_ls_iterator_free(Eina_File_Iterator *it)
{
closedir(it->dirp);
EINA_MAGIC_SET(&it->iterator, 0);
free(it);
}
2008-08-08 22:47:15 -07:00
/*============================================================================*
* Global *
*============================================================================*/
/*============================================================================*
* API *
*============================================================================*/
2008-08-08 22:47:15 -07:00
/**
* @addtogroup Eina_File_Group File
*
* @brief Functions to traverse directories and split paths.
*
* @li eina_file_dir_list() list the content of a directory,
* recusrsively or not, and can call a callback function for eachfound
* file.
* @li eina_file_split() split a path into all the subdirectories that
* compose it, according to the separator of the file system.
*
* @{
*/
/**
* @brief List all files on the directory calling the function for every file found.
*
* @param dir The directory name.
* @param recursive Iterate recursively in the directory.
* @param cb The callback to be called.
* @param data The data to pass to the callback.
2010-02-11 09:38:24 -08:00
* @return #EINA_TRUE on success, #EINA_FALSE otherwise.
*
* This function lists all the files in @p dir. To list also all the
* sub directoris recursively, @p recursive must be set to #EINA_TRUE,
* otherwise it must be set to #EINA_FALSE. For each found file, @p cb
* is called and @p data is passed to it.
*
* If @p cb or @p dir are @c NULL, or if @p dir is a string of size 0,
* or if @p dir can not be opened, this function returns #EINA_FALSE
* immediatly. otherwise, it returns #EINA_TRUE.
*/
EAPI Eina_Bool
eina_file_dir_list(const char *dir, Eina_Bool recursive, Eina_File_Dir_List_Cb cb, void *data)
{
#ifndef _WIN32
struct dirent *de;
DIR *d;
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);
d = opendir(dir);
if (!d) return EINA_FALSE;
while ((de = readdir(d)))
{
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
continue;
cb(de->d_name, dir, data);
/* d_type is only available on linux and bsd (_BSD_SOURCE) */
if (recursive == EINA_TRUE) {
char *path;
path = alloca(strlen(dir) + strlen(de->d_name) + 2);
strcpy(path, dir);
strcat(path, "/");
strcat(path, de->d_name);
#ifndef sun
if (de->d_type == DT_UNKNOWN) {
#endif
struct stat st;
if (stat(path, &st))
continue ;
if (!S_ISDIR(st.st_mode))
continue ;
#ifndef sun
} else if (de->d_type != DT_DIR) {
continue ;
}
#endif
eina_file_dir_list(path, recursive, cb, data);
}
}
closedir(d);
#else
WIN32_FIND_DATA file;
HANDLE hSearch;
char *new_dir;
TCHAR *tdir;
size_t length_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);
length_dir = strlen(dir);
new_dir = (char *)alloca(length_dir + 5);
if (!new_dir) return EINA_FALSE;
memcpy(new_dir, dir, length_dir);
memcpy(new_dir + length_dir, "/*.*", 5);
#ifdef UNICODE
tdir = evil_char_to_wchar(new_dir);
#else
tdir = new_dir;
#endif /* ! UNICODE */
hSearch = FindFirstFile(tdir, &file);
#ifdef UNICODE
free(tdir);
#endif /* UNICODE */
if (hSearch == 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(hSearch, &file));
FindClose(hSearch);
#endif /* _WIN32 */
return EINA_TRUE;
}
/**
* @brief Split a path according to the delimiter of the filesystem.
*
* @param path The path to split.
* @return An array of the parts of the path to split.
*
* This function splits @p path according to the delimiter of the used
* filesystem. If @p path is @c NULL or if the array can not be
* created, @c NULL is returned, otherwise, an array with the
* different parts of @p path is returned.
*/
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, PATH_DELIM);
current != NULL;
path = current + 1, current = strchr(path, PATH_DELIM))
{
length = current - path;
if (length <= 0) continue ;
eina_array_push(ea, path);
*current = '\0';
}
if (*path != '\0')
eina_array_push(ea, path);
return ea;
}
/**
* Get an iterator to list the content of a directory. Give a chance to interrupt it
* and make it completly asynchrone.
* The iterator will walk over '.' and '..' without returning them.
* @param dir The name of the directory to list
* @return Return an Eina_Iterator that will walk over the files and directory in the pointed
* directory. On failure it will return NULL.
*/
EAPI Eina_Iterator *
eina_file_ls(const char *dir)
{
Eina_File_Iterator *it;
size_t length;
if (!dir) return NULL;
length = strlen(dir);
it = malloc(sizeof (Eina_File_Iterator) + length);
if (!it) return NULL;
EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
it->dirp = opendir(dir);
if (!it->dirp)
{
free(it);
return NULL;
}
memcpy(it->dir, dir, length + 1);
it->length = length;
it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_ls_iterator_next);
it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_ls_iterator_container);
it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_ls_iterator_free);
return &it->iterator;
}
/**
* @}
*/