summaryrefslogtreecommitdiff
path: root/Userland/Services/FileOperation
diff options
context:
space:
mode:
authorSam Atkins <atkinssj@gmail.com>2021-06-17 17:51:40 +0100
committerAndreas Kling <kling@serenityos.org>2021-07-22 12:48:44 +0200
commit0a62d517fdccfebe5bd61359f4ebdf3fee7269dc (patch)
tree13210dfe34d32ecc7360812826db119c02be42e3 /Userland/Services/FileOperation
parenta1b4ec15075ca49d3b6a8d1801b9c0ffe035cf24 (diff)
downloadserenity-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.cpp156
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;
}