mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 09:24:57 +00:00
Utilities: Add a drain
utility
This commit is contained in:
parent
2de582afc4
commit
84b981ade7
2 changed files with 103 additions and 0 deletions
102
Userland/Utilities/drain.cpp
Normal file
102
Userland/Utilities/drain.cpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <LibMain/Main.h>
|
||||
|
||||
constexpr size_t block_size = 256 * KiB;
|
||||
|
||||
static ErrorOr<void> seek_and_read(size_t offset, ByteBuffer& buffer, Core::File& file)
|
||||
{
|
||||
TRY(file.seek(offset, SeekMode::SetPosition));
|
||||
TRY(file.read_until_filled(buffer));
|
||||
return {};
|
||||
}
|
||||
|
||||
static ErrorOr<void> seek_and_write(size_t offset, ByteBuffer& buffer, Core::File& file)
|
||||
{
|
||||
TRY(file.seek(offset, SeekMode::SetPosition));
|
||||
TRY(file.write_until_depleted(buffer));
|
||||
return {};
|
||||
}
|
||||
|
||||
static ErrorOr<void> process_file(Core::File& file, size_t file_size, size_t file_size_rounded)
|
||||
{
|
||||
size_t head = 0;
|
||||
size_t tail = file_size_rounded - block_size;
|
||||
size_t const last_block_size = file_size - tail;
|
||||
|
||||
auto head_buffer = TRY(ByteBuffer::create_uninitialized(block_size));
|
||||
auto tail_buffer = TRY(ByteBuffer::create_uninitialized(last_block_size));
|
||||
|
||||
auto stdout = TRY(Core::File::standard_output());
|
||||
|
||||
// Overwrite the current block (after saving its contents to a temporary buffer) with the last block until we've processed half of the blocks in the file.
|
||||
while (head <= tail) {
|
||||
TRY(seek_and_read(head, head_buffer, file));
|
||||
TRY(seek_and_read(tail, tail_buffer, file));
|
||||
|
||||
TRY(seek_and_write(head, tail_buffer, file));
|
||||
TRY(file.truncate(tail));
|
||||
|
||||
TRY(stdout->write_until_depleted(head_buffer));
|
||||
|
||||
if (tail_buffer.size() != block_size)
|
||||
TRY(tail_buffer.try_resize(block_size));
|
||||
|
||||
head += block_size;
|
||||
tail -= block_size;
|
||||
};
|
||||
|
||||
size_t remaining_size = file_size - head;
|
||||
|
||||
// Note that we iterate downwards from the end of the file, as the above algorithm left all of the remaining blocks in reverse order.
|
||||
while (remaining_size) {
|
||||
size_t to_write = remaining_size >= block_size ? block_size : last_block_size;
|
||||
|
||||
tail_buffer.trim(to_write, true);
|
||||
TRY(seek_and_read(tail, tail_buffer, file));
|
||||
|
||||
TRY(file.truncate(tail));
|
||||
|
||||
TRY(stdout->write_until_depleted(tail_buffer));
|
||||
|
||||
tail -= to_write;
|
||||
remaining_size -= to_write;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
TRY(Core::System::pledge("stdio cpath rpath wpath"));
|
||||
StringView path;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.set_general_help("Print file to stdout, while progressively deleting read segments.");
|
||||
args_parser.add_positional_argument(path, "File path", "path", Core::ArgsParser::Required::Yes);
|
||||
args_parser.parse(arguments);
|
||||
|
||||
if (!FileSystem::exists(path))
|
||||
return Error::from_errno(ENOENT);
|
||||
|
||||
auto file = TRY(Core::File::open(path, Core::File::OpenMode::ReadWrite));
|
||||
size_t file_size = TRY(file->seek(0, SeekMode::FromEndPosition));
|
||||
if (file_size < block_size * 2)
|
||||
return Error::from_string_literal("Input file too small");
|
||||
|
||||
size_t file_size_rounded = TRY(file->seek(ceil_div(file_size, block_size) * block_size, SeekMode::SetPosition));
|
||||
|
||||
TRY(process_file(*file, file_size, file_size_rounded));
|
||||
|
||||
TRY(FileSystem::remove(path, FileSystem::RecursionMode::Disallowed));
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue