mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 12:38:12 +00:00
Utilities: Add strings
This commit is contained in:
parent
e3112a3d2e
commit
8677dbfc7f
3 changed files with 180 additions and 1 deletions
39
Base/usr/share/man/man1/strings.md
Normal file
39
Base/usr/share/man/man1/strings.md
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
## Name
|
||||||
|
|
||||||
|
strings - find printable strings in files
|
||||||
|
|
||||||
|
## Synopsis
|
||||||
|
|
||||||
|
```**sh
|
||||||
|
$ strings [-n NUMBER] [-p] [-t FORMAT] [PATHS...]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
`strings` looks for printable strings in each file specified in `PATHS` and writes them to standard output. If `PATHS` is not specified, input is read from standard input.
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
* `-n NUMBER`: Specify the minimum string length (4 is default).
|
||||||
|
* `-p`: Write the pathname for each file specified in `PATHS` to standard output.
|
||||||
|
* `-t FORMAT`: Write each string preceded by its byte offset from the start of the file in the specified `FORMAT`, where `FORMAT` matches one of the following: `d` (decimal), `o` (octal), or `x` (hexidecimal).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Display the printable strings in /bin/strings with a minimum length of 8 characters:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ strings -n 8 /bin/strings
|
||||||
|
```
|
||||||
|
|
||||||
|
Display the printable strings in a binary file, preceded by their byte offset in hexadecimal format:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ strings -t x ~/Videos/test.webm
|
||||||
|
```
|
||||||
|
|
||||||
|
Display the printable strings in all .txt files in the current directory, preceded by their pathname:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ strings -p *.txt
|
||||||
|
```
|
|
@ -8,7 +8,7 @@ list(APPEND REQUIRED_TARGETS
|
||||||
)
|
)
|
||||||
list(APPEND RECOMMENDED_TARGETS
|
list(APPEND RECOMMENDED_TARGETS
|
||||||
adjtime aplay abench asctl bt checksum chres cksum copy fortune gunzip gzip init install keymap lsirq lsof lspci man mknod mktemp
|
adjtime aplay abench asctl bt checksum chres cksum copy fortune gunzip gzip init install keymap lsirq lsof lspci man mknod mktemp
|
||||||
nc netstat notify ntpquery open passwd pls printf pro shot tar tt unzip wallpaper zip
|
nc netstat notify ntpquery open passwd pls printf pro shot strings tar tt unzip wallpaper zip
|
||||||
)
|
)
|
||||||
|
|
||||||
# FIXME: Support specifying component dependencies for utilities (e.g. WebSocket for telws)
|
# FIXME: Support specifying component dependencies for utilities (e.g. WebSocket for telws)
|
||||||
|
|
140
Userland/Utilities/strings.cpp
Normal file
140
Userland/Utilities/strings.cpp
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/CharacterTypes.h>
|
||||||
|
#include <AK/Forward.h>
|
||||||
|
#include <LibCore/ArgsParser.h>
|
||||||
|
#include <LibCore/Stream.h>
|
||||||
|
#include <LibCore/System.h>
|
||||||
|
#include <LibMain/Main.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
enum class StringOffsetFormat {
|
||||||
|
None = 0,
|
||||||
|
Decimal,
|
||||||
|
Octal,
|
||||||
|
Hexadecimal
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: This is similar to how the cat utility works in the sense of aggregating
|
||||||
|
// data in 32K buffer.
|
||||||
|
static constexpr size_t buffer_read_size = 32768;
|
||||||
|
|
||||||
|
static bool should_print_characters(Vector<u8> const& characters)
|
||||||
|
{
|
||||||
|
for (u8 ch : characters) {
|
||||||
|
if (is_ascii_printable(ch) && !is_ascii_space(ch))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_characters(Vector<u8> const& characters, StringOffsetFormat string_offset_format, size_t string_offset_position)
|
||||||
|
{
|
||||||
|
switch (string_offset_format) {
|
||||||
|
case StringOffsetFormat::Decimal:
|
||||||
|
out("{:>7d} ", string_offset_position);
|
||||||
|
break;
|
||||||
|
case StringOffsetFormat::Octal:
|
||||||
|
out("{:>7o} ", string_offset_position);
|
||||||
|
break;
|
||||||
|
case StringOffsetFormat::Hexadecimal:
|
||||||
|
out("{:>7x} ", string_offset_position);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
outln("{:s}", characters.span());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_characters_in_span(Vector<u8>& characters, ReadonlyBytes span)
|
||||||
|
{
|
||||||
|
int processed_characters = 0;
|
||||||
|
for (u8 ch : span) {
|
||||||
|
++processed_characters;
|
||||||
|
if (is_ascii_printable(ch) || ch == '\t')
|
||||||
|
characters.append(ch);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return processed_characters;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ErrorOr<void> process_strings_in_file(StringView path, bool show_paths, StringOffsetFormat string_offset_format, size_t minimum_string_length)
|
||||||
|
{
|
||||||
|
Array<u8, buffer_read_size> buffer;
|
||||||
|
Vector<u8> output_characters;
|
||||||
|
auto file = TRY(Core::Stream::File::open_file_or_standard_stream(path, Core::Stream::OpenMode::Read));
|
||||||
|
size_t processed_characters = 0;
|
||||||
|
size_t string_offset_position = 0;
|
||||||
|
bool did_show_path = false;
|
||||||
|
while (!file->is_eof()) {
|
||||||
|
auto buffer_span = TRY(file->read(buffer));
|
||||||
|
while (!buffer_span.is_empty()) {
|
||||||
|
string_offset_position += processed_characters;
|
||||||
|
processed_characters = process_characters_in_span(output_characters, buffer_span);
|
||||||
|
if (show_paths && !did_show_path) {
|
||||||
|
outln("path {}:", path);
|
||||||
|
did_show_path = true;
|
||||||
|
}
|
||||||
|
if (output_characters.size() >= minimum_string_length && should_print_characters(output_characters)) {
|
||||||
|
print_characters(output_characters, string_offset_format, string_offset_position);
|
||||||
|
}
|
||||||
|
buffer_span = buffer_span.slice(processed_characters);
|
||||||
|
output_characters.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
{
|
||||||
|
TRY(Core::System::pledge("stdio rpath"));
|
||||||
|
|
||||||
|
Vector<StringView> paths;
|
||||||
|
size_t minimum_string_length = 4;
|
||||||
|
bool show_paths = false;
|
||||||
|
|
||||||
|
StringOffsetFormat string_offset_format { StringOffsetFormat::None };
|
||||||
|
|
||||||
|
Core::ArgsParser args_parser;
|
||||||
|
args_parser.add_option(minimum_string_length, "Specify the minimum string length.", nullptr, 'n', "number");
|
||||||
|
args_parser.add_option(show_paths, "Display the path for each matched file.", nullptr, 'p');
|
||||||
|
args_parser.add_option({ Core::ArgsParser::OptionArgumentMode::Required,
|
||||||
|
"Write offset relative to start of each file in (d)ec, (o)ct, or he(x) format.",
|
||||||
|
nullptr,
|
||||||
|
't',
|
||||||
|
"format",
|
||||||
|
[&string_offset_format](char const* s) {
|
||||||
|
StringView value = { s, strlen(s) };
|
||||||
|
if (value == "d") {
|
||||||
|
string_offset_format = StringOffsetFormat::Decimal;
|
||||||
|
} else if (value == "o") {
|
||||||
|
string_offset_format = StringOffsetFormat::Octal;
|
||||||
|
} else if (value == "x") {
|
||||||
|
string_offset_format = StringOffsetFormat::Hexadecimal;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} });
|
||||||
|
args_parser.set_general_help("Write the sequences of printable characters in files or pipes to stdout.");
|
||||||
|
args_parser.add_positional_argument(paths, "File path", "path", Core::ArgsParser::Required::No);
|
||||||
|
args_parser.parse(arguments);
|
||||||
|
|
||||||
|
if (minimum_string_length < 1) {
|
||||||
|
warnln("Invalid minimum string length {}", minimum_string_length);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paths.is_empty())
|
||||||
|
paths.append("-"sv);
|
||||||
|
|
||||||
|
for (auto const& path : paths)
|
||||||
|
TRY(process_strings_in_file(path, show_paths, string_offset_format, minimum_string_length));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue