/* EIO - EFL data type library * Copyright (C) 2010 Enlightenment Developers: * Cedric Bail * * 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 . */ /** * @page tutorial_dir_copy eio_dir_copy() tutorial * * To use eio_dir_copy(), you basically need the source and * destination files (or directories), and set three callbacks: * * @li The notification callback, which allows you to know if a file or * a directory is copied, and the progress of the copy. * @li The end callback, which is called when the copy is finished. * @li The error callback, which is called if an error occured. You * can then retrieve the error type as an errno error. * * @warning It is the user's duty to provide the "right target". It * means that copying to '.' will copy the content directly inside '.' * and not in a subdirectory. * * Here is a simple example: * * @code * #include * #include * * static void * _test_notify_cb(void *data, Eio_File *handler, const Eio_Progress *info) * { * switch (info->op) * { * case EIO_FILE_COPY: * printf("[%s] %f%%\n", info->dest, info->percent); * break; * case EIO_DIR_COPY: * printf("global [%li/%li] %f%%\n", info->current, info->max, info->percent); * break; * } * } * * static void * _test_done_cb(void *data, Eio_File *handler) * { * printf("copy done\n"); * ecore_main_loop_quit(); * } * * static void * _test_error_cb(int error, Eio_File *handler, void *data) * { * fprintf(stderr, "error: [%s]\n", strerror(error)); * ecore_main_loop_quit(); * } * * int * main(int argc, char **argv) * { * Eio_File *cp; * * if (argc != 3) * { * fprintf(stderr, "eio_cp source_file destination_file\n"); * return -1; * } * * ecore_init(); * eio_init(); * * cp = eio_dir_copy(argv[1], argv[2], * _test_notify_cb, * _test_done_cb, * _test_error_cb, * NULL); * * ecore_main_loop_begin(); * * eio_shutdown(); * ecore_shutdown(); * * return 0; * } * @endcode */ /** * @page tutorial_dir_stat_ls eio_dir_stat_ls() tutorial * * @li The filter callback, which allow or not a file to be seen * by the main loop handler. This callback run in a separated thread. * @li The main callback, which receive in the main loop all the file * that are allowed by the filter. If you are updating a user interface * it make sense to delay the insertion a little, so you get a chance * to update the canvas for a bunch of file instead of one by one. * @li The end callback, which is called in the main loop when the * content of the directory has been correctly scanned and all the * file notified to the main loop. * @li The error callback, which is called if an error occured or * if the listing was cancelled during it's run. You can then retrieve * the error type as an errno error. * * Here is a simple example that implement a stupidly simple replacement for find: * * @code * #include * #include * * static Eina_Bool * _test_filter_cb(void *data, Eio_File *handler, const Eina_File_Direct_Info *info) * { * fprintf(stderr, "ACCEPTING: %s\n", info->path); * return EINA_TRUE; * } * * static void * _test_main_cb(void *data, Eio_File *handler, const Eina_File_Direct_Info *info) * { * fprintf(stderr, "PROCESS: %s\n", info->path); * } * * static void * _test_done_cb(void *data, Eio_File *handler) * { * printf("ls done\n"); * ecore_main_loop_quit(); * } * * static void * _test_error_cb(void *data, Eio_File *handler, int error) * { * fprintf(stderr, "error: [%s]\n", strerror(error)); * ecore_main_loop_quit(); * } * * int * main(int argc, char **argv) * { * Eio_File *cp; * * if (argc != 2) * { * fprintf(stderr, "eio_ls directory\n"); * return -1; * } * * ecore_init(); * eio_init(); * * cp = eio_dir_stat_ls(argv[1], * _test_filter_cb, * _test_main_cb, * _test_done_cb, * _test_error_cb, * NULL); * * ecore_main_loop_begin(); * * eio_shutdown(); * ecore_shutdown(); * * return 0; * } * * @endcode */ #include "eio_private.h" #include "Eio.h" /*============================================================================* * Local * *============================================================================*/ /** * @cond LOCAL */ static int eio_strcmp(const void *a, const void *b) { return strcmp(a, b); } static Eina_Bool _eio_dir_recursive_progress(Eio_Dir_Copy *copy, Eio_File *handler, const Eina_File_Direct_Info *info) { switch (info->type) { case EINA_FILE_UNKNOWN: eio_file_thread_error(©->progress.common, handler->thread); return EINA_FALSE; case EINA_FILE_LNK: copy->links = eina_list_append(copy->links, eina_stringshare_add(info->path)); break; case EINA_FILE_DIR: copy->dirs = eina_list_append(copy->dirs, eina_stringshare_add(info->path)); break; default: copy->files = eina_list_append(copy->files, eina_stringshare_add(info->path)); break; } return EINA_TRUE; } static Eina_Bool _eio_file_recursiv_ls(Ecore_Thread *thread, Eio_File *common, Eio_Filter_Direct_Cb filter_cb, void *data, const char *target) { Eina_File_Direct_Info *info; Eina_Iterator *it = NULL; Eina_List *l; Eina_List *dirs = NULL; const char *dir; it = eina_file_stat_ls(target); if (!it) { eio_file_thread_error(common, thread); return EINA_FALSE; } EINA_ITERATOR_FOREACH(it, info) { Eina_Bool filter = EINA_TRUE; struct stat buffer; switch (info->type) { case EINA_FILE_DIR: if (lstat(info->path, &buffer) != 0) goto on_error; if (S_ISLNK(buffer.st_mode)) info->type = EINA_FILE_LNK; default: break; } filter = filter_cb(data, common, info); if (filter && info->type == EINA_FILE_DIR) dirs = eina_list_append(dirs, eina_stringshare_add(info->path)); if (ecore_thread_check(thread)) goto on_error; } eina_iterator_free(it); it = NULL; EINA_LIST_FOREACH(dirs, l, dir) if (!_eio_file_recursiv_ls(thread, common, filter_cb, data, dir)) goto on_error; return EINA_TRUE; on_error: if (it) eina_iterator_free(it); EINA_LIST_FREE(dirs, dir) eina_stringshare_del(dir); return EINA_FALSE; } static Eina_Bool _eio_dir_recursiv_ls(Ecore_Thread *thread, Eio_Dir_Copy *copy, const char *target) { if (!_eio_file_recursiv_ls(thread, ©->progress.common, (Eio_Filter_Direct_Cb) _eio_dir_recursive_progress, copy, target)) return EINA_FALSE; return EINA_TRUE; } static Eina_Bool _eio_dir_init(Ecore_Thread *thread, long long *step, long long *count, int *length_source, int *length_dest, Eio_Dir_Copy *order, Eio_File_Progress *progress) { struct stat buffer; /* notify main thread of the amount of work todo */ *step = 0; *count = eina_list_count(order->files) + eina_list_count(order->dirs) * 2 + eina_list_count(order->links); eio_progress_send(thread, &order->progress, *step, *count); /* sort the content, so we create the directory in the right order */ order->dirs = eina_list_sort(order->dirs, -1, eio_strcmp); order->files = eina_list_sort(order->files, -1, eio_strcmp); order->links = eina_list_sort(order->links, -1, eio_strcmp); /* prepare stuff */ *length_source = eina_stringshare_strlen(order->progress.source); *length_dest = eina_stringshare_strlen(order->progress.dest); memcpy(progress, &order->progress, sizeof (Eio_File_Progress)); progress->source = NULL; progress->dest = NULL; /* create destination dir if not available */ if (stat(order->progress.dest, &buffer) != 0) { if (stat(order->progress.source, &buffer) != 0) { eio_file_thread_error(&order->progress.common, thread); return EINA_FALSE; } if (mkdir(order->progress.dest, buffer.st_mode) != 0) { eio_file_thread_error(&order->progress.common, thread); return EINA_FALSE; } } return EINA_TRUE; } static void _eio_dir_target(Eio_Dir_Copy *order, char *target, const char *dir, int length_source, int length_dest) { int length; length = eina_stringshare_strlen(dir); memcpy(target, order->progress.dest, length_dest); target[length_dest] = '/'; memcpy(target + length_dest + 1, dir + length_source, length - length_source + 1); } static Eina_Bool _eio_dir_mkdir(Ecore_Thread *thread, Eio_Dir_Copy *order, long long *step, long long count, int length_source, int length_dest) { const char *dir; Eina_List *l; char target[PATH_MAX]; /* create all directory */ EINA_LIST_FOREACH(order->dirs, l, dir) { /* build target dir path */ _eio_dir_target(order, target, dir, length_source, length_dest); /* create the directory (we will apply the mode later) */ if (mkdir(target, 0777) != 0) { eio_file_thread_error(&order->progress.common, thread); return EINA_FALSE; } /* inform main thread */ (*step)++; eio_progress_send(thread, &order->progress, *step, count); /* check for cancel request */ if (ecore_thread_check(thread)) return EINA_FALSE; } return EINA_TRUE; } static Eina_Bool _eio_dir_link(Ecore_Thread *thread, Eio_Dir_Copy *order, long long *step, long long count, int length_source, int length_dest) { const char *link; Eina_List *l; char oldpath[PATH_MAX]; char target[PATH_MAX]; char buffer[PATH_MAX]; char *newpath; /* Build once the base of the link target */ memcpy(buffer, order->progress.dest, length_dest); buffer[length_dest] = '/'; /* recreate all links */ EINA_LIST_FOREACH(order->links, l, link) { ssize_t length; /* build oldpath link */ _eio_dir_target(order, oldpath, link, length_source, length_dest); /* read link target */ length = readlink(link, target, PATH_MAX); if (length < 0) goto on_error; if (strncmp(target, order->progress.source, length_source) == 0) { /* The link is inside the zone to copy, so rename it */ memcpy(buffer + length_dest + 1, target + length_source, length - length_source + 1); newpath = target; } else { /* The link is outside the zone to copy */ newpath = target; } /* create the link */ if (symlink(newpath, oldpath) != 0) goto on_error; /* inform main thread */ (*step)++; eio_progress_send(thread, &order->progress, *step, count); /* check for cancel request */ if (ecore_thread_check(thread)) return EINA_FALSE; } return EINA_TRUE; on_error: eio_file_thread_error(&order->progress.common, thread); return EINA_FALSE; } static Eina_Bool _eio_dir_chmod(Ecore_Thread *thread, Eio_Dir_Copy *order, long long *step, long long count, int length_source, int length_dest, Eina_Bool rmdir_source) { const char *dir; char target[PATH_MAX]; struct stat buffer; while(order->dirs) { /* destroy in reverse order so that we don't prevent change of lower dir */ dir = eina_list_data_get(eina_list_last(order->dirs)); order->dirs = eina_list_remove_list(order->dirs, eina_list_last(order->dirs)); /* build target dir path */ _eio_dir_target(order, target, dir, length_source, length_dest); /* FIXME: in some case we already did a stat call, so would be nice to reuse previous result here */ /* stat the original dir for mode info */ if (stat(dir, &buffer) != 0) goto on_error; /* set the orginal mode to the newly created dir */ if (chmod(target, buffer.st_mode) != 0) goto on_error; /* if required destroy original directory */ if (rmdir_source) { if (rmdir(dir) != 0) goto on_error; } /* inform main thread */ (*step)++; eio_progress_send(thread, &order->progress, *step, count); /* check for cancel request */ if (ecore_thread_check(thread)) goto on_cancel; eina_stringshare_del(dir); } return EINA_TRUE; on_error: eio_file_thread_error(&order->progress.common, thread); on_cancel: if (dir) eina_stringshare_del(dir); return EINA_FALSE; } static void _eio_dir_copy_heavy(void *data, Ecore_Thread *thread) { Eio_Dir_Copy *copy = data; const char *file = NULL; const char *dir; const char *link; Eio_File_Progress file_copy; char target[PATH_MAX]; int length_source = 0; int length_dest = 0; long long count; long long step; /* list all the content that should be copied */ if (!_eio_dir_recursiv_ls(thread, copy, copy->progress.source)) return ; /* init all structure needed to copy the file */ if (!_eio_dir_init(thread, &step, &count, &length_source, &length_dest, copy, &file_copy)) goto on_error; /* suboperation is a file copy */ file_copy.op = EIO_FILE_COPY; /* create all directory */ if (!_eio_dir_mkdir(thread, copy, &step, count, length_source, length_dest)) goto on_error; /* copy all files */ EINA_LIST_FREE(copy->files, file) { /* build target file path */ _eio_dir_target(copy, target, file, length_source, length_dest); file_copy.source = file; file_copy.dest = eina_stringshare_add(target); /* copy the file */ if (!eio_file_copy_do(thread, &file_copy)) { copy->progress.common.error = file_copy.common.error; goto on_error; } /* notify main thread */ step++; eio_progress_send(thread, ©->progress, step, count); if (ecore_thread_check(thread)) goto on_error; eina_stringshare_del(file_copy.dest); eina_stringshare_del(file); } file_copy.dest = NULL; file = NULL; /* recreate link */ if (!_eio_dir_link(thread, copy, &step, count, length_source, length_dest)) goto on_error; /* set directory right back */ if (!_eio_dir_chmod(thread, copy, &step, count, length_source, length_dest, EINA_FALSE)) goto on_error; on_error: /* cleanup the mess */ if (file_copy.dest) eina_stringshare_del(file_copy.dest); if (file) eina_stringshare_del(file); EINA_LIST_FREE(copy->files, file) eina_stringshare_del(file); EINA_LIST_FREE(copy->dirs, dir) eina_stringshare_del(dir); EINA_LIST_FREE(copy->links, link) eina_stringshare_del(link); if (!ecore_thread_check(thread)) eio_progress_send(thread, ©->progress, count, count); return ; } static void _eio_dir_copy_notify(void *data, Ecore_Thread *thread __UNUSED__, void *msg_data) { Eio_Dir_Copy *copy = data; Eio_Progress *progress = msg_data; eio_progress_cb(progress, ©->progress); } static void _eio_dir_copy_free(Eio_Dir_Copy *copy) { eina_stringshare_del(copy->progress.source); eina_stringshare_del(copy->progress.dest); free(copy); } static void _eio_dir_copy_end(void *data, Ecore_Thread *thread __UNUSED__) { Eio_Dir_Copy *copy = data; copy->progress.common.done_cb((void*) copy->progress.common.data, ©->progress.common); _eio_dir_copy_free(copy); } static void _eio_dir_copy_error(void *data, Ecore_Thread *thread __UNUSED__) { Eio_Dir_Copy *copy = data; eio_file_error(©->progress.common); _eio_dir_copy_free(copy); } static void _eio_dir_move_heavy(void *data, Ecore_Thread *thread) { Eio_Dir_Copy *move = data; const char *file = NULL; const char *dir = NULL; Eio_File_Progress file_move; char target[PATH_MAX]; int length_source; int length_dest; long long count; long long step; /* just try a rename, maybe we are lucky... */ if (rename(move->progress.source, move->progress.dest) == 0) { /* we are really lucky */ eio_progress_send(thread, &move->progress, 1, 1); return ; } /* list all the content that should be moved */ if (!_eio_dir_recursiv_ls(thread, move, move->progress.source)) return ; /* init all structure needed to move the file */ if (!_eio_dir_init(thread, &step, &count, &length_source, &length_dest, move, &file_move)) goto on_error; /* sub operation is a file move */ file_move.op = EIO_FILE_MOVE; /* create all directory */ if (!_eio_dir_mkdir(thread, move, &step, count, length_source, length_dest)) goto on_error; /* move file around */ EINA_LIST_FREE(move->files, file) { /* build target file path */ _eio_dir_target(move, target, file, length_source, length_dest); file_move.source = file; file_move.dest = eina_stringshare_add(target); /* first try to rename */ if (rename(file_move.source, file_move.dest) < 0) { if (errno != EXDEV) { eio_file_thread_error(&move->progress.common, thread); goto on_error; } /* then try real copy */ if (!eio_file_copy_do(thread, &file_move)) { move->progress.common.error = file_move.common.error; goto on_error; } /* and unlink the original */ if (unlink(file) != 0) { eio_file_thread_error(&move->progress.common, thread); goto on_error; } } step++; eio_progress_send(thread, &move->progress, step, count); if (ecore_thread_check(thread)) goto on_error; eina_stringshare_del(file_move.dest); eina_stringshare_del(file); } file_move.dest = NULL; file = NULL; /* recreate link */ if (!_eio_dir_link(thread, move, &step, count, length_source, length_dest)) goto on_error; /* set directory right back */ if (!_eio_dir_chmod(thread, move, &step, count, length_source, length_dest, EINA_TRUE)) goto on_error; if (rmdir(move->progress.source) != 0) goto on_error; on_error: /* cleanup the mess */ if (file_move.dest) eina_stringshare_del(file_move.dest); if (file) eina_stringshare_del(file); EINA_LIST_FREE(move->files, file) eina_stringshare_del(file); EINA_LIST_FREE(move->dirs, dir) eina_stringshare_del(dir); if (!ecore_thread_check(thread)) eio_progress_send(thread, &move->progress, count, count); return; } static void _eio_dir_rmrf_heavy(void *data, Ecore_Thread *thread) { Eio_Dir_Copy *rmrf = data; const char *file = NULL; const char *dir = NULL; long long count; long long step; /* list all the content that should be moved */ if (!_eio_dir_recursiv_ls(thread, rmrf, rmrf->progress.source)) return ; /* init counter */ step = 0; count = eina_list_count(rmrf->files) + eina_list_count(rmrf->dirs) + 1; EINA_LIST_FREE(rmrf->files, file) { if (unlink(file) != 0) { eio_file_thread_error(&rmrf->progress.common, thread); goto on_error; } eina_stringshare_replace(&rmrf->progress.dest, file); step++; eio_progress_send(thread, &rmrf->progress, step, count); if (ecore_thread_check(thread)) goto on_error; eina_stringshare_del(file); } file = NULL; EINA_LIST_FREE(rmrf->dirs, dir) { if (rmdir(dir) != 0) { eio_file_thread_error(&rmrf->progress.common, thread); goto on_error; } eina_stringshare_replace(&rmrf->progress.dest, dir); step++; eio_progress_send(thread, &rmrf->progress, step, count); if (ecore_thread_check(thread)) goto on_error; eina_stringshare_del(dir); } dir = NULL; if (rmdir(rmrf->progress.source) != 0) goto on_error; step++; on_error: if (dir) eina_stringshare_del(dir); if (file) eina_stringshare_del(file); EINA_LIST_FREE(rmrf->dirs, dir) eina_stringshare_del(dir); EINA_LIST_FREE(rmrf->files, file) eina_stringshare_del(file); if (!ecore_thread_check(thread)) eio_progress_send(thread, &rmrf->progress, count, count); return; } static Eina_Bool _eio_dir_stat_find_forward(Eio_File_Direct_Ls *async, Eio_File *handler, const Eina_File_Direct_Info *info) { Eina_Bool filter = EINA_TRUE; if (async->filter_cb) { filter = async->filter_cb((void*) async->ls.common.data, &async->ls.common, info); } if (filter) { Eina_File_Direct_Info *send; send = eio_direct_info_malloc(); if (!send) return EINA_FALSE; memcpy(send, info, sizeof (Eina_File_Direct_Info)); ecore_thread_feedback(handler->thread, send); } return EINA_TRUE; } static void _eio_dir_stat_find_heavy(void *data, Ecore_Thread *thread) { Eio_File_Direct_Ls *async = data; async->ls.common.thread = thread; _eio_file_recursiv_ls(thread, &async->ls.common, (Eio_Filter_Direct_Cb) _eio_dir_stat_find_forward, async, async->ls.directory); } static void _eio_dir_stat_find_notify(void *data, Ecore_Thread *thread __UNUSED__, void *msg_data) { Eio_File_Direct_Ls *async = data; Eina_File_Direct_Info *info = msg_data; async->main_cb((void*) async->ls.common.data, &async->ls.common, info); eio_direct_info_free(info); } static void _eio_dir_stat_done(void *data, Ecore_Thread *thread __UNUSED__) { Eio_File_Ls *async = data; async->common.done_cb((void*) async->common.data, &async->common); eina_stringshare_del(async->directory); free(async); } static void _eio_dir_stat_error(void *data, Ecore_Thread *thread __UNUSED__) { Eio_File_Ls *async = data; eio_file_error(&async->common); eina_stringshare_del(async->directory); free(async); } /** * @endcond */ /*============================================================================* * Global * *============================================================================*/ /*============================================================================* * API * *============================================================================*/ /** * @addtogroup Eio_Group Eio Reference API * * @{ */ /** * @brief Copy a directory and it's content asynchronously * @param source Should be the name of the directory to copy the data from. * @param dest Should be the name of the directory to copy the data to. * @param progress_cb Callback called to know the progress of the copy. * @param done_cb Callback called when the copy is done. * @param error_cb Callback called when something goes wrong. * @param data Private data given to callback. * * This function will copy a directory and all it's content from source to dest. * It will try to use splice if possible, if not it will fallback to mmap/write. * It will try to preserve access right, but not user/group identity. */ EAPI Eio_File * eio_dir_copy(const char *source, const char *dest, Eio_Progress_Cb progress_cb, Eio_Done_Cb done_cb, Eio_Error_Cb error_cb, const void *data) { Eio_Dir_Copy *copy; EINA_SAFETY_ON_NULL_RETURN_VAL(source, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(dest, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(done_cb, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(error_cb, NULL); copy = malloc(sizeof(Eio_Dir_Copy)); EINA_SAFETY_ON_NULL_RETURN_VAL(copy, NULL); copy->progress.op = EIO_DIR_COPY; copy->progress.progress_cb = progress_cb; copy->progress.source = eina_stringshare_add(source); copy->progress.dest = eina_stringshare_add(dest); copy->files = NULL; copy->dirs = NULL; copy->links = NULL; if (!eio_long_file_set(©->progress.common, done_cb, error_cb, data, _eio_dir_copy_heavy, _eio_dir_copy_notify, _eio_dir_copy_end, _eio_dir_copy_error)) return NULL; return ©->progress.common; } /** * @brief Move a directory and it's content asynchronously * @param source Should be the name of the directory to copy the data from. * @param dest Should be the name of the directory to copy the data to. * @param progress_cb Callback called to know the progress of the copy. * @param done_cb Callback called when the copy is done. * @param error_cb Callback called when something goes wrong. * @param data Private data given to callback. * * This function will move a directory and all it's content from source to dest. * It will try first to rename the directory, if not it will try to use splice * if possible, if not it will fallback to mmap/write. * It will try to preserve access right, but not user/group identity. */ EAPI Eio_File * eio_dir_move(const char *source, const char *dest, Eio_Progress_Cb progress_cb, Eio_Done_Cb done_cb, Eio_Error_Cb error_cb, const void *data) { Eio_Dir_Copy *move; EINA_SAFETY_ON_NULL_RETURN_VAL(source, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(dest, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(done_cb, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(error_cb, NULL); move = malloc(sizeof(Eio_Dir_Copy)); EINA_SAFETY_ON_NULL_RETURN_VAL(move, NULL); move->progress.op = EIO_DIR_MOVE; move->progress.progress_cb = progress_cb; move->progress.source = eina_stringshare_add(source); move->progress.dest = eina_stringshare_add(dest); move->files = NULL; move->dirs = NULL; move->links = NULL; if (!eio_long_file_set(&move->progress.common, done_cb, error_cb, data, _eio_dir_move_heavy, _eio_dir_copy_notify, _eio_dir_copy_end, _eio_dir_copy_error)) return NULL; return &move->progress.common; } /** * @brief Remove a directory and it's content asynchronously * @param path Should be the name of the directory to destroy. * @param progress_cb Callback called to know the progress of the copy. * @param done_cb Callback called when the copy is done. * @param error_cb Callback called when something goes wrong. * @param data Private data given to callback. * * This function will move a directory and all it's content from source to dest. * It will try first to rename the directory, if not it will try to use splice * if possible, if not it will fallback to mmap/write. * It will try to preserve access right, but not user/group identity. */ EAPI Eio_File * eio_dir_unlink(const char *path, Eio_Progress_Cb progress_cb, Eio_Done_Cb done_cb, Eio_Error_Cb error_cb, const void *data) { Eio_Dir_Copy *rmrf; EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(done_cb, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(error_cb, NULL); rmrf = malloc(sizeof(Eio_Dir_Copy)); EINA_SAFETY_ON_NULL_RETURN_VAL(rmrf, NULL); rmrf->progress.op = EIO_UNLINK; rmrf->progress.progress_cb = progress_cb; rmrf->progress.source = eina_stringshare_add(path); rmrf->progress.dest = NULL; rmrf->files = NULL; rmrf->dirs = NULL; rmrf->links = NULL; if (!eio_long_file_set(&rmrf->progress.common, done_cb, error_cb, data, _eio_dir_rmrf_heavy, _eio_dir_copy_notify, _eio_dir_copy_end, _eio_dir_copy_error)) return NULL; return &rmrf->progress.common; } /** * @brief List the content of a directory and all it's sub-content asynchronously * @param dir The directory to list. * @param filter_cb Callback called from another thread. * @param main_cb Callback called from the main loop for each accepted file. * @param done_cb Callback called from the main loop when the content of the directory has been listed. * @param error_cb Callback called from the main loop when the directory could not be opened or listing content has been canceled. * @param data Data passed to callback and not modified at all by eio_dir_stat_find. * @return A reference to the IO operation. * * eio_dir_stat_find() run eina_file_stat_ls() recursivly in a separated thread using * ecore_thread_feedback_run. This prevent any lock in your apps. */ EAPI Eio_File * eio_dir_stat_ls(const char *dir, Eio_Filter_Direct_Cb filter_cb, Eio_Main_Direct_Cb main_cb, Eio_Done_Cb done_cb, Eio_Error_Cb error_cb, const void *data) { Eio_File_Direct_Ls *async; EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(main_cb, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(done_cb, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(error_cb, NULL); async = malloc(sizeof(Eio_File_Direct_Ls)); EINA_SAFETY_ON_NULL_RETURN_VAL(async, NULL); async->filter_cb = filter_cb; async->main_cb = main_cb; async->ls.directory = eina_stringshare_add(dir); if (!eio_long_file_set(&async->ls.common, done_cb, error_cb, data, _eio_dir_stat_find_heavy, _eio_dir_stat_find_notify, _eio_dir_stat_done, _eio_dir_stat_error)) return NULL; return &async->ls.common; } /** * @} */