2023-11-15 18:13:36 +03:00
/*
* Copyright ( c ) 2023 , the SerenityOS developers .
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2023-12-10 10:25:24 +03:00
# include <AK/Checked.h>
2023-11-15 18:13:36 +03:00
# include <LibCore/ArgsParser.h>
# include <LibCore/File.h>
# include <LibCore/System.h>
# include <LibFileSystem/FileSystem.h>
# include <LibMain/Main.h>
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 { } ;
}
2023-12-10 10:25:24 +03:00
static ErrorOr < void > process_file ( Core : : File & file , size_t block_size , size_t file_size , size_t file_size_rounded )
2023-11-15 18:13:36 +03:00
{
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 ;
2023-12-10 10:25:24 +03:00
size_t block_size_in_kib = 256 ;
2023-11-15 18:13:36 +03:00
Core : : ArgsParser args_parser ;
args_parser . set_general_help ( " Print file to stdout, while progressively deleting read segments. " ) ;
2023-12-10 10:25:24 +03:00
args_parser . add_option ( block_size_in_kib , " Base Block size in KiB, defaults to 256 KiB " , " block-size " , ' b ' , " base block size " ) ;
2023-11-15 18:13:36 +03:00
args_parser . add_positional_argument ( path , " File path " , " path " , Core : : ArgsParser : : Required : : Yes ) ;
args_parser . parse ( arguments ) ;
2023-12-10 10:25:24 +03:00
if ( block_size_in_kib < 1 )
return Error : : from_string_literal ( " Invalid block size " ) ;
if ( Checked < size_t > : : multiplication_would_overflow ( block_size_in_kib , KiB ) )
return Error : : from_string_literal ( " Overflow in block size " ) ;
size_t block_size = block_size_in_kib * KiB ;
2023-11-15 18:13:36 +03:00
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 ) ) ;
2023-12-10 10:25:24 +03:00
TRY ( process_file ( * file , block_size , file_size , file_size_rounded ) ) ;
2023-11-15 18:13:36 +03:00
TRY ( FileSystem : : remove ( path , FileSystem : : RecursionMode : : Disallowed ) ) ;
return 0 ;
}