diff --git a/legacy/eina/src/include/eina_file.h b/legacy/eina/src/include/eina_file.h index 432881ded7..a94ba83851 100644 --- a/legacy/eina/src/include/eina_file.h +++ b/legacy/eina/src/include/eina_file.h @@ -22,6 +22,7 @@ #include "eina_types.h" #include "eina_array.h" #include "eina_iterator.h" +#include /** * @addtogroup Eina_Tools_Group Tools @@ -55,6 +56,19 @@ EAPI Eina_Bool eina_file_dir_list(const char *dir, Eina_Bool recursive, Eina_Fil EAPI Eina_Array *eina_file_split(char *path) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); EAPI Eina_Iterator *eina_file_ls(const char *dir); + +typedef struct _Eina_File_Direct_Info Eina_File_Direct_Info; +struct _Eina_File_Direct_Info +{ + size_t path_length; /* size of the whole path */ + size_t name_length; /* size of the filename/basename component */ + size_t name_start; /* where the filename/basename component starts */ + char path[PATH_MAX]; + const struct dirent *dirent; +}; + +EAPI Eina_Iterator *eina_file_direct_ls(const char *dir); + /** * @} */ diff --git a/legacy/eina/src/lib/eina_file.c b/legacy/eina/src/lib/eina_file.c index 0b805ec414..bd2639d20d 100644 --- a/legacy/eina/src/lib/eina_file.c +++ b/legacy/eina/src/lib/eina_file.c @@ -92,8 +92,9 @@ _eina_file_ls_iterator_next(Eina_File_Iterator *it, void **data) dp = readdir(it->dirp); if (!dp) return EINA_FALSE; } - while (!strcmp(dp->d_name, ".") - || !strcmp(dp->d_name, "..")); + while ((dp->d_name[0] == '.') && + ((dp->d_name[1] == '\0') || + ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0')))); length = strlen(dp->d_name); name = alloca(length + 2 + it->length); @@ -121,6 +122,63 @@ _eina_file_ls_iterator_free(Eina_File_Iterator *it) free(it); } +typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator; +struct _Eina_File_Direct_Iterator +{ + Eina_Iterator iterator; + + DIR *dirp; + int length; + + Eina_File_Direct_Info info; + + char dir[1]; +}; + +static Eina_Bool +_eina_file_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data) +{ + struct dirent *dp; + size_t length; + + do + { + dp = readdir(it->dirp); + if (!dp) return EINA_FALSE; + + length = strlen(dp->d_name); + if (it->info.name_start + length + 1 >= PATH_MAX) + continue; + } + while ((dp->d_name[0] == '.') && + ((dp->d_name[1] == '\0') || + ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0')))); + + memcpy(it->info.path + it->info.name_start, dp->d_name, length); + it->info.name_length = length; + it->info.path_length = it->info.name_start + length; + it->info.path[it->info.path_length] = '\0'; + it->info.dirent = dp; + + *data = &it->info; + return EINA_TRUE; +} + +static char * +_eina_file_direct_ls_iterator_container(Eina_File_Direct_Iterator *it) +{ + return it->dir; +} + +static void +_eina_file_direct_ls_iterator_free(Eina_File_Direct_Iterator *it) +{ + closedir(it->dirp); + + EINA_MAGIC_SET(&it->iterator, 0); + free(it); +} + /*============================================================================* * Global * *============================================================================*/ @@ -322,12 +380,29 @@ eina_file_split(char *path) } /** - * Get an iterator to list the content of a directory. Give a chance to interrupt it - * and make it completly asynchrone. + * Get an iterator to list the content of a directory. + * + * Iterators are cheap to be created and allow interruption at any + * iteration. At each iteration, only the next directory entry is read + * from the filesystem with readdir(). + * + * The iterator will handle the user a stringshared value with the + * full path. One must call eina_stringshare_del() on it after usage + * to not leak! + * + * The eina_file_direct_ls() function will provide a possibly faster + * alternative if you need to filter the results somehow, like + * checking extension. + * * 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. + * @return Return an Eina_Iterator that will walk over the files and + * directory in the pointed directory. On failure it will + * return NULL. The iterator emits stringshared value with the + * full path and must be freed with eina_stringshare_del(). + * + * @see eina_file_direct_ls() */ EAPI Eina_Iterator * eina_file_ls(const char *dir) @@ -338,6 +413,7 @@ eina_file_ls(const char *dir) if (!dir) return NULL; length = strlen(dir); + if (length < 1) return NULL; it = malloc(sizeof (Eina_File_Iterator) + length); if (!it) return NULL; @@ -352,7 +428,10 @@ eina_file_ls(const char *dir) } memcpy(it->dir, dir, length + 1); - it->length = length; + if (dir[length - 1] != '/') + it->length = length; + else + it->length = length - 1; it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_ls_iterator_next); it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_ls_iterator_container); @@ -361,6 +440,73 @@ eina_file_ls(const char *dir) return &it->iterator; } +/** + * Get an iterator to list the content of a directory, with direct information. + * + * Iterators are cheap to be created and allow interruption at any + * iteration. At each iteration, only the next directory entry is read + * from the filesystem with readdir(). + * + * The iterator returns the direct pointer to couple of useful information in + * #Eina_File_Direct_Info and that pointer should not be modified anyhow! + * + * 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. The iterator emits #Eina_File_Direct_Info + * pointers that could be used but not modified. The lifetime + * of the returned pointer is until the next iteration and + * while the iterator is live, deleting the iterator + * invalidates the pointer. + * + * @see eina_file_ls() + */ +EAPI Eina_Iterator * +eina_file_direct_ls(const char *dir) +{ + Eina_File_Direct_Iterator *it; + size_t length; + + if (!dir) return NULL; + + length = strlen(dir); + if (length < 1) return NULL; + if (length + NAME_MAX + 2 >= PATH_MAX) return NULL; + + it = malloc(sizeof(Eina_File_Direct_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; + + 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; + } + + it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_direct_ls_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_direct_ls_iterator_container); + it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free); + + return &it->iterator; +} + /** * @} */