1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 07:38:10 +00:00
serenity/Userland/Utilities/uniq.cpp
Tim Schumacher d5871f5717 AK: Rename Stream::{read,write} to Stream::{read_some,write_some}
Similar to POSIX read, the basic read and write functions of AK::Stream
do not have a lower limit of how much data they read or write (apart
from "none at all").

Rename the functions to "read some [data]" and "write some [data]" (with
"data" being omitted, since everything here is reading and writing data)
to make them sufficiently distinct from the functions that ensure to
use the entire buffer (which should be the go-to function for most
usages).

No functional changes, just a lot of new FIXMEs.
2023-03-13 15:16:20 +00:00

115 lines
4.3 KiB
C++

/*
* Copyright (c) 2020, Matthew L. Curry <matthew.curry@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/CharacterTypes.h>
#include <AK/RefPtr.h>
#include <AK/StringView.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibCore/System.h>
#include <unistd.h>
static ErrorOr<void> write_line_content(StringView line, size_t count, bool duplicates_only, bool print_count, Core::File& outfile)
{
if (duplicates_only && count <= 1)
return {};
// FIXME: This should write the entire span.
if (print_count)
TRY(outfile.write_some(DeprecatedString::formatted("{} {}\n", count, line).bytes()));
else
TRY(outfile.write_some(DeprecatedString::formatted("{}\n", line).bytes()));
return {};
}
static StringView skip(StringView line, unsigned char_skip_count, unsigned field_skip_count)
{
line = line.trim("\n"sv);
if (field_skip_count) {
bool in_field = false;
int field_index = 0;
unsigned current_field = 0;
for (size_t i = 0; i < line.length(); i++) {
char c = line[i];
if (is_ascii_space(c)) {
in_field = false;
field_index = i;
if (++current_field > field_skip_count)
break;
} else if (!in_field) {
in_field = true;
}
}
line = line.substring_view(field_index);
}
char_skip_count = min(char_skip_count, line.length());
return line.substring_view(char_skip_count);
}
ErrorOr<int> serenity_main(Main::Arguments arguments)
{
TRY(Core::System::pledge("stdio rpath wpath cpath"));
StringView inpath;
StringView outpath;
bool duplicates_only = false;
bool unique_only = false;
bool ignore_case = false;
bool print_count = false;
unsigned skip_chars = 0;
unsigned skip_fields = 0;
Core::ArgsParser args_parser;
args_parser.add_option(duplicates_only, "Only print duplicated lines", "repeated", 'd');
args_parser.add_option(unique_only, "Only print unique lines (default)", "unique", 'u');
args_parser.add_option(ignore_case, "Ignore case when comparing lines", "ignore-case", 'i');
args_parser.add_option(print_count, "Prefix each line by its number of occurrences", "count", 'c');
args_parser.add_option(skip_chars, "Skip N chars", "skip-chars", 's', "N");
args_parser.add_option(skip_fields, "Skip N fields", "skip-fields", 'f', "N");
args_parser.add_positional_argument(inpath, "Input file", "input", Core::ArgsParser::Required::No);
args_parser.add_positional_argument(outpath, "Output file", "output", Core::ArgsParser::Required::No);
args_parser.parse(arguments);
if (!unique_only && !duplicates_only) {
unique_only = true;
} else if (unique_only && duplicates_only) {
// Printing duplicated and unique lines shouldn't print anything
return 0;
}
auto infile = TRY(Core::BufferedFile::create(TRY(Core::File::open_file_or_standard_stream(inpath, Core::File::OpenMode::Read))));
auto outfile = TRY(Core::File::open_file_or_standard_stream(outpath, Core::File::OpenMode::Write));
size_t count = 0;
ByteBuffer previous_buf = TRY(ByteBuffer::create_uninitialized(1024));
ByteBuffer current_buf = TRY(ByteBuffer::create_uninitialized(1024));
StringView previous = TRY(infile->read_line(previous_buf));
StringView previous_to_compare = skip(previous, skip_chars, skip_fields);
while (TRY(infile->can_read_line())) {
// FIXME: The buffer does not automatically resize,
// and this will return EMSGSIZE if the read line
// is more than 1024 bytes.
StringView current = TRY(infile->read_line(current_buf));
StringView current_to_compare = skip(current, skip_chars, skip_fields);
bool lines_equal = ignore_case ? current_to_compare.equals_ignoring_ascii_case(previous_to_compare) : current_to_compare == previous_to_compare;
if (!lines_equal) {
TRY(write_line_content(previous, count, duplicates_only, print_count, *outfile));
count = 1;
} else {
count++;
}
swap(current_to_compare, previous_to_compare);
swap(current_buf, previous_buf);
swap(current, previous);
}
TRY(write_line_content(previous, count, duplicates_only, print_count, *outfile));
return 0;
}