/* EIO - EFL data type library * Copyright (C) 2010 Enlightenment Developers: * Cedric Bail * Vincent "caro" Torri * Stephen "okra" Houston * * 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 . */ #include "eio_private.h" #include "Eio.h" /*============================================================================* * Local * *============================================================================*/ /** * @cond LOCAL */ static void _eio_file_heavy(void *data, Ecore_Thread *thread) { Eio_File_Char_Ls *async = data; Eina_Iterator *ls; const char *file; Eina_List *pack = NULL; double start; ls = eina_file_ls(async->ls.directory); if (!ls) { eio_file_thread_error(&async->ls.common, thread); return; } eio_file_container_set(&async->ls.common, eina_iterator_container_get(ls)); start = ecore_time_get(); EINA_ITERATOR_FOREACH(ls, file) { Eina_Bool filter = EINA_TRUE; if (async->filter_cb) { filter = async->filter_cb((void*) async->ls.common.data, &async->ls.common, file); } if (filter) { Eio_File_Char *send_fc; send_fc = eio_char_malloc(); if (!send_fc) goto on_error; send_fc->filename = file; send_fc->associated = async->ls.common.worker.associated; async->ls.common.worker.associated = NULL; pack = eina_list_append(pack, send_fc); } else { on_error: eina_stringshare_del(file); if (async->ls.common.worker.associated) { eina_hash_free(async->ls.common.worker.associated); async->ls.common.worker.associated = NULL; } } pack = eio_pack_send(thread, pack, &start); if (ecore_thread_check(thread)) break; } if (pack) ecore_thread_feedback(thread, pack); async->ls.ls = ls; } void _eio_string_notify(void *data, Ecore_Thread *thread EINA_UNUSED, void *msg_data) { Eio_File_Char_Ls *async = data; Eina_List *pack = msg_data; Eio_File_Char *info; async->ls.common.length += eina_list_count(pack); // Check if it is an internal use if (async->ls.gather) { Eina_Array *gather; gather = eina_array_new(eina_list_count(pack)); EINA_LIST_FREE(pack, info) { if (!gather) eina_stringshare_del(info->filename); else eina_array_push(gather, info->filename); eio_char_free(info); } // transfer ownership to caller async->main_internal_cb((void*) async->ls.common.data, &async->ls.common, gather); return ; } EINA_LIST_FREE(pack, info) { async->ls.common.main.associated = info->associated; async->main_cb((void*) async->ls.common.data, &async->ls.common, info->filename); if (async->ls.common.main.associated) { eina_hash_free(async->ls.common.main.associated); async->ls.common.main.associated = NULL; } eina_stringshare_del(info->filename); eio_char_free(info); } } static void _eio_file_eina_ls_heavy(Ecore_Thread *thread, Eio_File_Direct_Ls *async, Eina_Iterator *ls) { const Eina_File_Direct_Info *info; Eina_List *pack = NULL; double start; if (!ls) { eio_file_thread_error(&async->ls.common, thread); return; } eio_file_container_set(&async->ls.common, eina_iterator_container_get(ls)); start = ecore_time_get(); EINA_ITERATOR_FOREACH(ls, 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) { Eio_File_Direct_Info *send_di; send_di = eio_direct_info_malloc(); if (!send_di) continue; memcpy(&send_di->info, info, sizeof (Eina_File_Direct_Info)); send_di->associated = async->ls.common.worker.associated; async->ls.common.worker.associated = NULL; pack = eina_list_append(pack, send_di); } else if (async->ls.common.worker.associated) { eina_hash_free(async->ls.common.worker.associated); async->ls.common.worker.associated = NULL; } pack = eio_pack_send(thread, pack, &start); if (ecore_thread_check(thread)) break; } if (pack) ecore_thread_feedback(thread, pack); async->ls.ls = ls; } static void _eio_file_direct_heavy(void *data, Ecore_Thread *thread) { Eio_File_Direct_Ls *async = data; Eina_Iterator *ls; ls = eina_file_direct_ls(async->ls.directory); _eio_file_eina_ls_heavy(thread, async, ls); } static void _eio_file_stat_heavy(void *data, Ecore_Thread *thread) { Eio_File_Direct_Ls *async = data; Eina_Iterator *ls; ls = eina_file_stat_ls(async->ls.directory); _eio_file_eina_ls_heavy(thread, async, ls); } void _eio_direct_notify(void *data, Ecore_Thread *thread EINA_UNUSED, void *msg_data) { Eio_File_Direct_Ls *async = data; Eina_List *pack = msg_data; Eio_File_Direct_Info *info; async->ls.common.length += eina_list_count(pack); // Check if it is an internal use if (async->ls.gather) { Eina_Array *gather; gather = eina_array_new(eina_list_count(pack)); EINA_LIST_FREE(pack, info) eina_array_push(gather, &info->info); // transfer ownership to caller async->main_internal_cb((void*) async->ls.common.data, &async->ls.common, gather); return ; } EINA_LIST_FREE(pack, info) { async->ls.common.main.associated = info->associated; async->main_cb((void*) async->ls.common.data, &async->ls.common, &info->info); if (async->ls.common.main.associated) { eina_hash_free(async->ls.common.main.associated); async->ls.common.main.associated = NULL; } eio_direct_info_free(info); } } #ifndef MAP_HUGETLB # define MAP_HUGETLB 0 #endif static void _eio_file_copy_heavy(void *data, Ecore_Thread *thread) { Eio_File_Progress *copy = data; eio_file_copy_do(thread, copy); } static void _eio_file_copy_notify(void *data, Ecore_Thread *thread EINA_UNUSED, void *msg_data) { Eio_File_Progress *copy = data; eio_progress_cb(msg_data, copy); } static void _eio_file_copy_free(Eio_File_Progress *copy) { eina_stringshare_del(copy->source); eina_stringshare_del(copy->dest); eio_file_free(©->common); } static void _eio_file_copy_end(void *data, Ecore_Thread *thread EINA_UNUSED) { Eio_File_Progress *copy = data; copy->common.done_cb((void*) copy->common.data, ©->common); _eio_file_copy_free(copy); } static void _eio_file_copy_error(void *data, Ecore_Thread *thread EINA_UNUSED) { Eio_File_Progress *copy = data; eio_file_error(©->common); _eio_file_copy_free(copy); } static void _eio_file_move_free(Eio_File_Move *move) { eina_stringshare_del(move->progress.source); eina_stringshare_del(move->progress.dest); eio_file_free(&move->progress.common); } static void _eio_file_move_copy_progress(void *data, Eio_File *handler EINA_UNUSED, const Eio_Progress *info) { Eio_File_Move *move = data; move->progress.progress_cb((void*) move->progress.common.data, &move->progress.common, info); } static void _eio_file_move_unlink_done(void *data, Eio_File *handler EINA_UNUSED) { Eio_File_Move *move = data; move->progress.common.done_cb((void*) move->progress.common.data, &move->progress.common); _eio_file_move_free(move); } static void _eio_file_move_unlink_error(void *data, Eio_File *handler EINA_UNUSED, int error) { Eio_File_Move *move = data; move->copy = NULL; move->progress.common.error = error; eio_file_error(&move->progress.common); _eio_file_move_free(move); } static void _eio_file_move_copy_done(void *data, Eio_File *handler EINA_UNUSED) { Eio_File_Move *move = data; Eio_File *rm; rm = eio_file_unlink(move->progress.source, _eio_file_move_unlink_done, _eio_file_move_unlink_error, move); if (rm) move->copy = rm; } static void _eio_file_move_copy_error(void *data, Eio_File *handler EINA_UNUSED, int error) { Eio_File_Move *move = data; move->progress.common.error = error; eio_file_error(&move->progress.common); _eio_file_move_free(move); } static void _eio_file_move_heavy(void *data, Ecore_Thread *thread) { Eio_File_Move *move = data; if (rename(move->progress.source, move->progress.dest) < 0) eio_file_thread_error(&move->progress.common, thread); else eio_progress_send(thread, &move->progress, 1, 1); } static void _eio_file_move_notify(void *data, Ecore_Thread *thread EINA_UNUSED, void *msg_data) { Eio_File_Move *move = data; eio_progress_cb(msg_data, &move->progress); } static void _eio_file_move_end(void *data, Ecore_Thread *thread EINA_UNUSED) { Eio_File_Move *move = data; move->progress.common.done_cb((void*) move->progress.common.data, &move->progress.common); _eio_file_move_free(move); } static void _eio_file_move_error(void *data, Ecore_Thread *thread EINA_UNUSED) { Eio_File_Move *move = data; if (move->copy) { eio_file_cancel(move->copy); return; } if (move->progress.common.error == EXDEV) { Eio_File *eio_cp; eio_cp = eio_file_copy(move->progress.source, move->progress.dest, move->progress.progress_cb ? _eio_file_move_copy_progress : NULL, _eio_file_move_copy_done, _eio_file_move_copy_error, move); if (eio_cp) { move->copy = eio_cp; move->progress.common.thread = ((Eio_File_Progress*)move->copy)->common.thread; return; } } eio_file_error(&move->progress.common); _eio_file_move_free(move); } /** * @endcond */ /*============================================================================* * Global * *============================================================================*/ /** * @cond LOCAL */ void eio_progress_cb(Eio_Progress *progress, Eio_File_Progress *op) { op->progress_cb((void *) op->common.data, &op->common, progress); eio_progress_free(progress); } static Eina_Bool _eio_file_copy_progress(void *data, unsigned long long done, unsigned long long total) { void **ctx = data; Ecore_Thread *thread = ctx[0]; Eio_File_Progress *copy = ctx[1]; eio_progress_send(thread, copy, done, total); return !ecore_thread_check(thread); } Eina_Bool eio_file_copy_do(Ecore_Thread *thread, Eio_File_Progress *copy) { void *ctx[2] = {thread, copy}; Eina_Bool ret = eina_file_copy(copy->source, copy->dest, (EINA_FILE_COPY_PERMISSION | EINA_FILE_COPY_XATTR), _eio_file_copy_progress, ctx); if (!ret) { eio_file_thread_error(©->common, thread); return EINA_FALSE; } return EINA_TRUE; } void eio_async_free(Eio_File_Ls *async) { eina_stringshare_del(async->directory); eio_file_free(&async->common); } void eio_async_end(void *data, Ecore_Thread *thread EINA_UNUSED) { Eio_File_Ls *async = data; async->common.done_cb((void*) async->common.data, &async->common); eio_file_container_set(&async->common, NULL); eina_iterator_free(async->ls); eio_async_free(async); } void eio_async_error(void *data, Ecore_Thread *thread EINA_UNUSED) { Eio_File_Ls *async = data; eio_file_error(&async->common); eio_async_free(async); } /** * @endcond */ /*============================================================================* * API * *============================================================================*/ static Eio_File * _eio_file_internal_ls(const char *dir, Eio_Filter_Cb filter_cb, Eio_Main_Cb main_cb, Eio_Array_Cb main_internal_cb, Eio_Done_Cb done_cb, Eio_Error_Cb error_cb, const void *data) { Eio_File_Char_Ls *async; EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(done_cb, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(error_cb, NULL); async = eio_common_alloc(sizeof(Eio_File_Char_Ls)); EINA_SAFETY_ON_NULL_RETURN_VAL(async, NULL); async->ls.directory = eina_stringshare_add(dir); async->filter_cb = filter_cb; if (main_internal_cb) { async->main_internal_cb = main_internal_cb; async->ls.gather = EINA_TRUE; } else { async->main_cb = main_cb; } if (!eio_long_file_set(&async->ls.common, done_cb, error_cb, data, _eio_file_heavy, _eio_string_notify, eio_async_end, eio_async_error)) return NULL; return &async->ls.common; } EAPI Eio_File * eio_file_ls(const char *dir, Eio_Filter_Cb filter_cb, Eio_Main_Cb main_cb, Eio_Done_Cb done_cb, Eio_Error_Cb error_cb, const void *data) { EINA_SAFETY_ON_NULL_RETURN_VAL(main_cb, NULL); return _eio_file_internal_ls(dir, filter_cb, main_cb, NULL, done_cb, error_cb, data); } Eio_File * _eio_file_ls(const char *dir, Eio_Array_Cb main_internal_cb, Eio_Done_Cb done_cb, Eio_Error_Cb error_cb, const void *data) { EINA_SAFETY_ON_NULL_RETURN_VAL(main_internal_cb, NULL); return _eio_file_internal_ls(dir, NULL, NULL, main_internal_cb, done_cb, error_cb, data); } static Eio_File * _eio_file_direct_internal_ls(const char *dir, Eio_Filter_Direct_Cb filter_cb, Eio_Main_Direct_Cb main_cb, Eio_Array_Cb main_internal_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(done_cb, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(error_cb, NULL); async = eio_common_alloc(sizeof(Eio_File_Direct_Ls)); EINA_SAFETY_ON_NULL_RETURN_VAL(async, NULL); async->ls.directory = eina_stringshare_add(dir); async->filter_cb = filter_cb; if (main_internal_cb) { async->main_internal_cb = main_internal_cb; async->ls.gather = EINA_TRUE; } else { async->main_cb = main_cb; } if (!eio_long_file_set(&async->ls.common, done_cb, error_cb, data, _eio_file_direct_heavy, _eio_direct_notify, eio_async_end, eio_async_error)) return NULL; return &async->ls.common; } EAPI Eio_File * eio_file_direct_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) { EINA_SAFETY_ON_NULL_RETURN_VAL(main_cb, NULL); return _eio_file_direct_internal_ls(dir, filter_cb, main_cb, NULL, done_cb, error_cb, data); } Eio_File * _eio_file_direct_ls(const char *dir, Eio_Array_Cb main_internal_cb, Eio_Done_Cb done_cb, Eio_Error_Cb error_cb, const void *data) { EINA_SAFETY_ON_NULL_RETURN_VAL(main_internal_cb, NULL); return _eio_file_direct_internal_ls(dir, NULL, NULL, main_internal_cb, done_cb, error_cb, data); } static Eio_File * _eio_file_stat_internal_ls(const char *dir, Eio_Filter_Direct_Cb filter_cb, Eio_Main_Direct_Cb main_cb, Eio_Array_Cb main_internal_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(done_cb, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(error_cb, NULL); async = eio_common_alloc(sizeof(Eio_File_Direct_Ls)); EINA_SAFETY_ON_NULL_RETURN_VAL(async, NULL); async->ls.directory = eina_stringshare_add(dir); async->filter_cb = filter_cb; if (main_internal_cb) { async->main_internal_cb = main_internal_cb; async->ls.gather = EINA_TRUE; } else { async->main_cb = main_cb; } if (!eio_long_file_set(&async->ls.common, done_cb, error_cb, data, _eio_file_stat_heavy, _eio_direct_notify, eio_async_end, eio_async_error)) return NULL; return &async->ls.common; } EAPI Eio_File * eio_file_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) { EINA_SAFETY_ON_NULL_RETURN_VAL(main_cb, NULL); return _eio_file_stat_internal_ls(dir, filter_cb, main_cb, NULL, done_cb, error_cb, data); } Eio_File * _eio_file_stat_ls(const char *dir, Eio_Array_Cb main_internal_cb, Eio_Done_Cb done_cb, Eio_Error_Cb error_cb, const void *data) { EINA_SAFETY_ON_NULL_RETURN_VAL(main_internal_cb, NULL); return _eio_file_stat_internal_ls(dir, NULL, NULL, main_internal_cb, done_cb, error_cb, data); } EAPI Eina_Bool eio_file_cancel(Eio_File *ls) { if (!ls) return EINA_FALSE; EINA_SAFETY_ON_NULL_RETURN_VAL(ls, EINA_FALSE); return ecore_thread_cancel(ls->thread); } EAPI Eina_Bool eio_file_check(Eio_File *ls) { EINA_SAFETY_ON_NULL_RETURN_VAL(ls, EINA_TRUE); return ecore_thread_check(ls->thread); } EAPI void * eio_file_container_get(Eio_File *ls) { EINA_SAFETY_ON_NULL_RETURN_VAL(ls, NULL); return ls->container; } EAPI Eina_Bool eio_file_associate_add(Eio_File *ls, const char *key, const void *data, Eina_Free_Cb free_cb) { EINA_SAFETY_ON_NULL_RETURN_VAL(ls, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); /* FIXME: Check if we are in the right worker thred */ if (!ls->worker.associated) ls->worker.associated = eina_hash_string_small_new(eio_associate_free); return eina_hash_add(ls->worker.associated, key, eio_associate_malloc(data, free_cb)); } EAPI Eina_Bool eio_file_associate_direct_add(Eio_File *ls, const char *key, const void *data, Eina_Free_Cb free_cb) { EINA_SAFETY_ON_NULL_RETURN_VAL(ls, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); /* FIXME: Check if we are in the right worker thred */ if (!ls->worker.associated) ls->worker.associated = eina_hash_string_small_new(eio_associate_free); return eina_hash_direct_add(ls->worker.associated, key, eio_associate_malloc(data, free_cb)); } EAPI void * eio_file_associate_find(Eio_File *ls, const char *key) { Eio_File_Associate *search; EINA_SAFETY_ON_NULL_RETURN_VAL(ls, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); if (!ls->main.associated) return NULL; search = eina_hash_find(ls->main.associated, key); if (!search) return NULL; return search->data; } EAPI Eio_File * eio_file_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_File_Progress *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 = eio_common_alloc(sizeof(Eio_File_Progress)); EINA_SAFETY_ON_NULL_RETURN_VAL(copy, NULL); copy->op = EIO_FILE_COPY; copy->progress_cb = progress_cb; copy->source = eina_stringshare_add(source); copy->dest = eina_stringshare_add(dest); if (!eio_long_file_set(©->common, done_cb, error_cb, data, _eio_file_copy_heavy, _eio_file_copy_notify, _eio_file_copy_end, _eio_file_copy_error)) return NULL; return ©->common; } EAPI Eio_File * eio_file_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_File_Move *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 = eio_common_alloc(sizeof(Eio_File_Move)); EINA_SAFETY_ON_NULL_RETURN_VAL(move, NULL); move->progress.op = EIO_FILE_MOVE; move->progress.progress_cb = progress_cb; move->progress.source = eina_stringshare_add(source); move->progress.dest = eina_stringshare_add(dest); move->copy = NULL; if (!eio_long_file_set(&move->progress.common, done_cb, error_cb, data, _eio_file_move_heavy, _eio_file_move_notify, _eio_file_move_end, _eio_file_move_error)) return NULL; return &move->progress.common; }