mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 06:57:45 +00:00
Utilities: cut: Implement field-based cutting
This commit is contained in:
parent
96ea4f71bd
commit
ba29798039
1 changed files with 88 additions and 41 deletions
|
@ -120,45 +120,12 @@ static bool expand_list(String& list, Vector<Range>& ranges)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cut_file(const String& file, const Vector<Range>& ranges, void (*process_line)(char*, size_t, const Vector<Range>&))
|
|
||||||
{
|
|
||||||
FILE* fp = stdin;
|
|
||||||
if (!file.is_null()) {
|
|
||||||
fp = fopen(file.characters(), "r");
|
|
||||||
if (!fp) {
|
|
||||||
warnln("cut: Could not open file '{}'", file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char* line = nullptr;
|
|
||||||
ssize_t line_length = 0;
|
|
||||||
size_t line_capacity = 0;
|
|
||||||
while ((line_length = getline(&line, &line_capacity, fp)) != -1) {
|
|
||||||
if (line_length < 0) {
|
|
||||||
warnln("cut: Failed to read line from file '{}'", file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
line[line_length - 1] = '\0';
|
|
||||||
line_length--;
|
|
||||||
|
|
||||||
process_line(line, line_length, ranges);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line)
|
|
||||||
free(line);
|
|
||||||
|
|
||||||
if (!file.is_null())
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void process_line_bytes(char* line, size_t length, const Vector<Range>& ranges)
|
static void process_line_bytes(char* line, size_t length, const Vector<Range>& ranges)
|
||||||
{
|
{
|
||||||
for (auto& i : ranges) {
|
for (auto& i : ranges) {
|
||||||
if (i.m_from >= length)
|
if (i.m_from >= length)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
auto to = min(i.m_to, length);
|
auto to = min(i.m_to, length);
|
||||||
auto sub_string = String(line).substring(i.m_from - 1, to - i.m_from + 1);
|
auto sub_string = String(line).substring(i.m_from - 1, to - i.m_from + 1);
|
||||||
out("{}", sub_string);
|
out("{}", sub_string);
|
||||||
|
@ -166,35 +133,81 @@ static void process_line_bytes(char* line, size_t length, const Vector<Range>& r
|
||||||
outln();
|
outln();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void process_line_fields(char* line, size_t length, const Vector<Range>& ranges, char delimiter)
|
||||||
|
{
|
||||||
|
auto string_split = String(line, length).split(delimiter);
|
||||||
|
Vector<String> output_fields;
|
||||||
|
|
||||||
|
for (auto& range : ranges) {
|
||||||
|
for (size_t i = range.m_from - 1; i < min(range.m_to, string_split.size()); i++) {
|
||||||
|
output_fields.append(string_split[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outln("{}", String::join(delimiter, output_fields));
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
String byte_list = "";
|
String byte_list = "";
|
||||||
|
String fields_list = "";
|
||||||
|
String delimiter = "\t";
|
||||||
|
|
||||||
Vector<String> files;
|
Vector<String> files;
|
||||||
|
|
||||||
Core::ArgsParser args_parser;
|
Core::ArgsParser args_parser;
|
||||||
args_parser.add_positional_argument(files, "file(s) to cut", "file", Core::ArgsParser::Required::No);
|
args_parser.add_positional_argument(files, "file(s) to cut", "file", Core::ArgsParser::Required::No);
|
||||||
args_parser.add_option(byte_list, "select only these bytes", "bytes", 'b', "list");
|
args_parser.add_option(byte_list, "select only these bytes", "bytes", 'b', "list");
|
||||||
|
args_parser.add_option(fields_list, "select only these fields", "fields", 'f', "list");
|
||||||
|
args_parser.add_option(delimiter, "set a custom delimiter", "delimiter", 'd', "delimiter");
|
||||||
args_parser.parse(argc, argv);
|
args_parser.parse(argc, argv);
|
||||||
|
|
||||||
if (byte_list == "") {
|
bool selected_bytes = (byte_list != "");
|
||||||
warnln("cut: you must specify a list of bytes");
|
bool selected_fields = (fields_list != "");
|
||||||
|
|
||||||
|
int selected_options_count = (selected_bytes ? 1 : 0) + (selected_fields ? 1 : 0);
|
||||||
|
|
||||||
|
if (selected_options_count == 0) {
|
||||||
|
warnln("cut: you must specify a list of bytes, or fields");
|
||||||
args_parser.print_usage(stderr, argv[0]);
|
args_parser.print_usage(stderr, argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Range> byte_vector;
|
if (selected_options_count > 1) {
|
||||||
auto expansion_successful = expand_list(byte_list, byte_vector);
|
warnln("cut: you must specify only one of bytes, or fields");
|
||||||
|
args_parser.print_usage(stderr, argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delimiter.length() != 1) {
|
||||||
|
warnln("cut: the delimiter must be a single character");
|
||||||
|
args_parser.print_usage(stderr, argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String ranges_list;
|
||||||
|
Vector<Range> ranges_vector;
|
||||||
|
|
||||||
|
if (selected_bytes) {
|
||||||
|
ranges_list = byte_list;
|
||||||
|
} else if (selected_fields) {
|
||||||
|
ranges_list = fields_list;
|
||||||
|
} else {
|
||||||
|
// This should never happen, since we already checked the options count above.
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto expansion_successful = expand_list(ranges_list, ranges_vector);
|
||||||
|
|
||||||
if (!expansion_successful) {
|
if (!expansion_successful) {
|
||||||
args_parser.print_usage(stderr, argv[0]);
|
args_parser.print_usage(stderr, argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
quick_sort(byte_vector, [](auto& a, auto& b) { return a.m_from < b.m_from; });
|
quick_sort(ranges_vector, [](auto& a, auto& b) { return a.m_from < b.m_from; });
|
||||||
|
|
||||||
Vector<Range> disjoint_ranges;
|
Vector<Range> disjoint_ranges;
|
||||||
for (auto& range : byte_vector) {
|
for (auto& range : ranges_vector) {
|
||||||
if (disjoint_ranges.is_empty()) {
|
if (disjoint_ranges.is_empty()) {
|
||||||
disjoint_ranges.append(range);
|
disjoint_ranges.append(range);
|
||||||
continue;
|
continue;
|
||||||
|
@ -214,8 +227,42 @@ int main(int argc, char** argv)
|
||||||
files.append(String());
|
files.append(String());
|
||||||
|
|
||||||
/* Process each file */
|
/* Process each file */
|
||||||
for (auto& file : files)
|
for (auto& file : files) {
|
||||||
cut_file(file, disjoint_ranges, process_line_bytes);
|
FILE* fp = stdin;
|
||||||
|
if (!file.is_null()) {
|
||||||
|
fp = fopen(file.characters(), "r");
|
||||||
|
if (!fp) {
|
||||||
|
warnln("cut: Could not open file '{}'", file);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* line = nullptr;
|
||||||
|
ssize_t line_length = 0;
|
||||||
|
size_t line_capacity = 0;
|
||||||
|
while ((line_length = getline(&line, &line_capacity, fp)) != -1) {
|
||||||
|
if (line_length < 0) {
|
||||||
|
warnln("cut: Failed to read line from file '{}'", file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line[line_length - 1] = '\0';
|
||||||
|
line_length--;
|
||||||
|
|
||||||
|
if (selected_bytes) {
|
||||||
|
process_line_bytes(line, line_length, disjoint_ranges);
|
||||||
|
} else if (selected_fields) {
|
||||||
|
process_line_fields(line, line_length, disjoint_ranges, delimiter[0]);
|
||||||
|
} else {
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line)
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
if (!file.is_null())
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue