#include "dirlistjob.h" #include #include "fileinfo_p.h" #include "gioptrs.h" #include namespace Fm { DirListJob::DirListJob(const FilePath& path, Flags _flags, const std::shared_ptr& cutFilesHashSet): dir_path{path}, flags{_flags}, cutFilesHashSet_{cutFilesHashSet} { } void DirListJob::exec() { GErrorPtr err; GFileInfoPtr dir_inf; GFilePtr dir_gfile = dir_path.gfile(); // FIXME: these are hacks for search:/// URI implemented by libfm which contains some bugs bool isFileSearch = dir_path.hasUriScheme("search"); if(isFileSearch) { // NOTE: The GFile instance changes its URI during file enumeration (bad design). // So we create a copy here to avoid channging the gfile stored in dir_path. // FIXME: later we should refactor file search and remove this dirty hack. dir_gfile = GFilePtr{g_file_dup(dir_gfile.get())}; } _retry: err.reset(); dir_inf = GFileInfoPtr{ g_file_query_info(dir_gfile.get(), gfile_info_query_attribs, G_FILE_QUERY_INFO_NONE, cancellable().get(), &err), false }; if(!dir_inf) { ErrorAction act = emitError(err, ErrorSeverity::MODERATE); if(act == ErrorAction::RETRY) { err.reset(); goto _retry; } return; } if(g_file_info_get_file_type(dir_inf.get()) != G_FILE_TYPE_DIRECTORY) { auto path_str = dir_path.toString(); err = GErrorPtr{ G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, tr("The specified directory '%1' is not valid").arg(path_str.get()) }; emitError(err, ErrorSeverity::CRITICAL); return; } else { std::lock_guard lock{mutex_}; dir_fi = std::make_shared(dir_inf, dir_path.parent()); } FileInfoList foundFiles; /* check if FS is R/O and set attr. into inf */ // FIXME: _fm_file_info_job_update_fs_readonly(gf, inf, nullptr, nullptr); err.reset(); GFileEnumeratorPtr enu = GFileEnumeratorPtr{ g_file_enumerate_children(dir_gfile.get(), gfile_info_query_attribs, G_FILE_QUERY_INFO_NONE, cancellable().get(), &err), false }; if(enu) { // qDebug() << "START LISTING:" << dir_path.toString().get(); while(!isCancelled()) { err.reset(); GFileInfoPtr inf{g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false}; if(inf) { #if 0 FmPath* dir, *sub; GFile* child; if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY)) { /* FIXME: handle symlinks */ if(g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) { g_object_unref(inf); continue; } } #endif // virtual folders may return children not within them // For example: the search:/// URI implemented by libfm might return files from different folders during enumeration. // So here we call g_file_enumerator_get_container() to get the real parent path rather than simply using dir_path. // This is not the behaviour of gio, but the extensions by libfm might do this. // FIXME: after we port these vfs implementation from libfm, we can redesign this. FilePath realParentPath = FilePath{g_file_enumerator_get_container(enu.get()), true}; if(isFileSearch) { // this is a file sarch job (search:/// URI) // FIXME: redesign file search and remove this dirty hack // the libfm implementation of search:/// URI returns a customized GFile implementation that does not behave normally. // let's get its actual URI and re-create a normal gio GFile instance from it. realParentPath = FilePath::fromUri(realParentPath.uri().get()); } #if 0 if(g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY) /* for dir: check if its FS is R/O and set attr. into inf */ { _fm_file_info_job_update_fs_readonly(child, inf, nullptr, nullptr); } fi = fm_file_info_new_from_g_file_data(child, inf, sub); #endif auto fileInfo = std::make_shared(inf, realParentPath); if(emit_files_found) { // Q_EMIT filesFound(); } if(cutFilesHashSet_ && cutFilesHashSet_->count(fileInfo->path().hash()) > 0) { fileInfo->bindCutFiles(cutFilesHashSet_); } foundFiles.push_back(std::move(fileInfo)); } else { if(err) { ErrorAction act = emitError(err, ErrorSeverity::MILD); /* ErrorAction::RETRY is not supported. */ if(act == ErrorAction::ABORT) { cancel(); } } /* otherwise it's EOL */ break; } } err.reset(); g_file_enumerator_close(enu.get(), cancellable().get(), &err); } else { emitError(err, ErrorSeverity::CRITICAL); } // qDebug() << "END LISTING:" << dir_path.toString().get(); if(!foundFiles.empty()) { std::lock_guard lock{mutex_}; files_.swap(foundFiles); } } #if 0 //FIXME: incremental.. static gboolean emit_found_files(gpointer user_data) { /* this callback is called from the main thread */ FmDirListJob* job = FM_DIR_LIST_JOB(user_data); /* g_print("emit_found_files: %d\n", g_slist_length(job->files_to_add)); */ if(g_source_is_destroyed(g_main_current_source())) { return FALSE; } g_signal_emit(job, signals[FILES_FOUND], 0, job->files_to_add); g_slist_free_full(job->files_to_add, (GDestroyNotify)fm_file_info_unref); job->files_to_add = nullptr; job->delay_add_files_handler = 0; return FALSE; } static gpointer queue_add_file(FmJob* fmjob, gpointer user_data) { FmDirListJob* job = FM_DIR_LIST_JOB(fmjob); FmFileInfo* file = FM_FILE_INFO(user_data); /* this callback is called from the main thread */ /* g_print("queue_add_file: %s\n", fm_file_info_get_disp_name(file)); */ job->files_to_add = g_slist_prepend(job->files_to_add, fm_file_info_ref(file)); if(job->delay_add_files_handler == 0) job->delay_add_files_handler = g_timeout_add_seconds_full(G_PRIORITY_LOW, 1, emit_found_files, g_object_ref(job), g_object_unref); return nullptr; } void fm_dir_list_job_add_found_file(FmDirListJob* job, FmFileInfo* file) { fm_file_info_list_push_tail(job->files, file); if(G_UNLIKELY(job->emit_files_found)) { fm_job_call_main_thread(FM_JOB(job), queue_add_file, file); } } #endif } // namespace Fm