From 967314023c0af9e900a58dfb1ce0b1362a29c445 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 22 Jun 2021 15:45:55 +0100 Subject: [PATCH] FileOperation: Implement 'Delete' operation --- Userland/Services/FileOperation/main.cpp | 71 +++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/Userland/Services/FileOperation/main.cpp b/Userland/Services/FileOperation/main.cpp index 22b7a36b8c2..829ad6803d7 100644 --- a/Userland/Services/FileOperation/main.cpp +++ b/Userland/Services/FileOperation/main.cpp @@ -20,6 +20,7 @@ struct WorkItem { DeleteDirectory, CopyFile, MoveFile, + DeleteFile, }; Type type; String source; @@ -29,6 +30,7 @@ struct WorkItem { static int perform_copy(Vector const& sources, String const& destination); static int perform_move(Vector const& sources, String const& destination); +static int perform_delete(Vector const& sources); static int execute_work_items(Vector const& items); static void report_error(String message); static void report_warning(String message); @@ -39,10 +41,13 @@ int main(int argc, char** argv) Vector paths; Core::ArgsParser args_parser; - args_parser.add_positional_argument(operation, "Operation: either 'Copy' or 'Move'", "operation", Core::ArgsParser::Required::Yes); + args_parser.add_positional_argument(operation, "Operation: either 'Copy', 'Move' or 'Delete'", "operation", Core::ArgsParser::Required::Yes); args_parser.add_positional_argument(paths, "Source paths, followed by a destination if applicable", "paths", Core::ArgsParser::Required::Yes); args_parser.parse(argc, argv); + if (operation == "Delete") + return perform_delete(paths); + String destination = paths.take_last(); if (paths.is_empty()) { report_warning("At least one source and destination are required"); @@ -183,6 +188,56 @@ int perform_move(Vector const& sources, String const& destination) return execute_work_items(items); } +static bool collect_delete_work_items(String const& source, Vector& 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::DeleteFile, + .source = source, + .destination = {}, + .size = st.st_size, + }); + return true; + } + + // It's a directory. + Core::DirIterator dt(source, Core::DirIterator::SkipParentAndBaseDir); + while (dt.has_next()) { + auto name = dt.next_path(); + if (!collect_delete_work_items(String::formatted("{}/{}", source, name), items)) + return false; + } + + items.append(WorkItem { + .type = WorkItem::Type::DeleteDirectory, + .source = source, + .destination = {}, + .size = 0, + }); + + return true; +} + +int perform_delete(Vector const& sources) +{ + Vector items; + + for (auto& source : sources) { + if (!collect_delete_work_items(source, items)) + return 1; + } + + return execute_work_items(items); +} + int execute_work_items(Vector const& items) { off_t total_work_bytes = 0; @@ -288,6 +343,20 @@ int execute_work_items(Vector const& items) break; } + case WorkItem::Type::DeleteFile: { + if (unlink(item.source.characters()) < 0) { + auto original_errno = errno; + report_error(String::formatted("unlink: {}", strerror(original_errno))); + return 1; + } + + item_done += item.size; + executed_work_bytes += item.size; + print_progress(); + + break; + } + default: VERIFY_NOT_REACHED(); }