diff options
author | Sam Atkins <atkinssj@gmail.com> | 2021-06-17 17:51:40 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-07-22 12:48:44 +0200 |
commit | 0a62d517fdccfebe5bd61359f4ebdf3fee7269dc (patch) | |
tree | 13210dfe34d32ecc7360812826db119c02be42e3 /Userland/Services/FileOperation | |
parent | a1b4ec15075ca49d3b6a8d1801b9c0ffe035cf24 (diff) | |
download | serenity-0a62d517fdccfebe5bd61359f4ebdf3fee7269dc.zip |
FileManager+FileOperation: Implement (and use) 'Move' command
`FileOperation Move ...` is now used for cut-and-paste in the
FileManager.
Diffstat (limited to 'Userland/Services/FileOperation')
-rw-r--r-- | Userland/Services/FileOperation/main.cpp | 156 |
1 files changed, 133 insertions, 23 deletions
diff --git a/Userland/Services/FileOperation/main.cpp b/Userland/Services/FileOperation/main.cpp index 49b22a7a81..1156db5ca5 100644 --- a/Userland/Services/FileOperation/main.cpp +++ b/Userland/Services/FileOperation/main.cpp @@ -12,11 +12,14 @@ #include <LibCore/File.h> #include <sched.h> #include <sys/stat.h> +#include <unistd.h> struct WorkItem { enum class Type { CreateDirectory, + DeleteDirectory, CopyFile, + MoveFile, }; Type type; String source; @@ -25,6 +28,7 @@ struct WorkItem { }; static int perform_copy(Vector<String> const& sources, String const& destination); +static int perform_move(Vector<String> const& sources, String const& destination); static int execute_work_items(Vector<WorkItem> const& items); static void report_error(String message); static void report_warning(String message); @@ -43,6 +47,8 @@ int main(int argc, char** argv) if (operation == "Copy") return perform_copy(sources, destination); + if (operation == "Move") + return perform_move(sources, destination); report_warning(String::formatted("Unknown operation '{}'", operation)); return 0; @@ -58,7 +64,7 @@ static void report_warning(String message) outln("WARN {}", message); } -static bool collect_work_items(String const& source, String const& destination, Vector<WorkItem>& items) +static bool collect_copy_work_items(String const& source, String const& destination, Vector<WorkItem>& items) { struct stat st = {}; if (stat(source.characters(), &st) < 0) { @@ -89,7 +95,7 @@ static bool collect_work_items(String const& source, String const& destination, Core::DirIterator dt(source, Core::DirIterator::SkipParentAndBaseDir); while (dt.has_next()) { auto name = dt.next_path(); - if (!collect_work_items( + if (!collect_copy_work_items( String::formatted("{}/{}", source, name), String::formatted("{}/{}", destination, LexicalPath::basename(source)), items)) { @@ -105,7 +111,68 @@ int perform_copy(Vector<String> const& sources, String const& destination) Vector<WorkItem> items; for (auto& source : sources) { - if (!collect_work_items(source, destination, items)) + if (!collect_copy_work_items(source, destination, items)) + return 1; + } + + return execute_work_items(items); +} + +static bool collect_move_work_items(String const& source, String const& destination, Vector<WorkItem>& items) +{ + struct stat st = {}; + if (stat(source.characters(), &st) < 0) { + auto original_errno = errno; + report_error(String::formatted("stat: {}", strerror(original_errno))); + return false; + } + + if (!S_ISDIR(st.st_mode)) { + // It's a file. + items.append(WorkItem { + .type = WorkItem::Type::MoveFile, + .source = source, + .destination = String::formatted("{}/{}", destination, LexicalPath(source).basename()), + .size = st.st_size, + }); + return true; + } + + // It's a directory. + items.append(WorkItem { + .type = WorkItem::Type::CreateDirectory, + .source = {}, + .destination = String::formatted("{}/{}", destination, LexicalPath(source).basename()), + .size = 0, + }); + + Core::DirIterator dt(source, Core::DirIterator::SkipParentAndBaseDir); + while (dt.has_next()) { + auto name = dt.next_path(); + if (!collect_move_work_items( + String::formatted("{}/{}", source, name), + String::formatted("{}/{}", destination, LexicalPath(source).basename()), + items)) { + return false; + } + } + + items.append(WorkItem { + .type = WorkItem::Type::DeleteDirectory, + .source = source, + .destination = {}, + .size = 0, + }); + + return true; +} + +int perform_move(Vector<String> const& sources, String const& destination) +{ + Vector<WorkItem> items; + + for (auto& source : sources) { + if (!collect_move_work_items(source, destination, items)) return 1; } @@ -127,28 +194,16 @@ int execute_work_items(Vector<WorkItem> const& items) outln("PROGRESS {} {} {} {} {} {} {}", i, items.size(), executed_work_bytes, total_work_bytes, item_done, item.size, item.source); }; - switch (item.type) { - - case WorkItem::Type::CreateDirectory: { - outln("MKDIR {}", item.destination); - if (mkdir(item.destination.characters(), 0755) < 0 && errno != EEXIST) { - auto original_errno = errno; - report_error(String::formatted("mkdir: {}", strerror(original_errno))); - return 1; - } - break; - } - - case WorkItem::Type::CopyFile: { - auto source_file_or_error = Core::File::open(item.source, Core::OpenMode::ReadOnly); + auto copy_file = [&](String const& source, String const& destination) { + auto source_file_or_error = Core::File::open(source, Core::OpenMode::ReadOnly); if (source_file_or_error.is_error()) { - report_warning(String::formatted("Failed to open {} for reading: {}", item.source, source_file_or_error.error())); - return 1; + report_warning(String::formatted("Failed to open {} for reading: {}", source, source_file_or_error.error())); + return false; } - auto destination_file_or_error = Core::File::open(item.destination, (Core::OpenMode)(Core::OpenMode::WriteOnly | Core::OpenMode::Truncate)); + auto destination_file_or_error = Core::File::open(destination, (Core::OpenMode)(Core::OpenMode::WriteOnly | Core::OpenMode::Truncate)); if (destination_file_or_error.is_error()) { - report_warning(String::formatted("Failed to open {} for write: {}", item.destination, destination_file_or_error.error())); - return 1; + report_warning(String::formatted("Failed to open {} for write: {}", destination, destination_file_or_error.error())); + return false; } auto& source_file = *source_file_or_error.value(); auto& destination_file = *destination_file_or_error.value(); @@ -160,7 +215,7 @@ int execute_work_items(Vector<WorkItem> const& items) break; if (!destination_file.write(buffer)) { report_warning(String::formatted("Failed to write to destination file: {}", destination_file.error_string())); - return 1; + return false; } item_done += buffer.size(); executed_work_bytes += buffer.size(); @@ -171,6 +226,61 @@ int execute_work_items(Vector<WorkItem> const& items) sched_yield(); } print_progress(); + return true; + }; + + switch (item.type) { + + case WorkItem::Type::CreateDirectory: { + outln("MKDIR {}", item.destination); + if (mkdir(item.destination.characters(), 0755) < 0 && errno != EEXIST) { + auto original_errno = errno; + report_error(String::formatted("mkdir: {}", strerror(original_errno))); + return 1; + } + break; + } + + case WorkItem::Type::DeleteDirectory: { + if (rmdir(item.source.characters()) < 0) { + auto original_errno = errno; + report_error(String::formatted("rmdir: {}", strerror(original_errno))); + return 1; + } + break; + } + + case WorkItem::Type::CopyFile: { + if (!copy_file(item.source, item.destination)) + return 1; + + break; + } + + case WorkItem::Type::MoveFile: { + if (rename(item.source.characters(), item.destination.characters()) == 0) { + item_done += item.size; + executed_work_bytes += item.size; + print_progress(); + continue; + } + + auto original_errno = errno; + if (original_errno != EXDEV) { + report_warning(String::formatted("Failed to move {}: {}", item.source, strerror(original_errno))); + return 1; + } + + // EXDEV means we have to copy the file data and then remove the original + if (!copy_file(item.source, item.destination)) + return 1; + + if (unlink(item.source.characters()) < 0) { + auto original_errno = errno; + report_error(String::formatted("unlink: {}", strerror(original_errno))); + return 1; + } + break; } |