Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow printing binary content by treating it the same as text #2976

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Syntax highlighting for JavaScript files that start with `#!/usr/bin/env bun` #2913 (@sharunkumar)
- `bat --strip-ansi={never,always,auto}` to remove ANSI escape sequences from bat's input, see #2999 (@eth-p)
- Add or remove individual style components without replacing all styles #2929 (@eth-p)
- Add option `--binary=as-text` for printing binary content, see issue #2974 and PR #2976 (@einfachIrgendwer0815)

## Bugfixes

Expand Down
7 changes: 7 additions & 0 deletions doc/long-help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ Options:
* unicode (␇, ␊, ␀, ..)
* caret (^G, ^J, ^@, ..)

--binary <behavior>
How to treat binary content. (default: no-printing)

Possible values:
* no-printing: do not print any binary content
* as-text: treat binary content as normal text

-p, --plain...
Only show plain style, no decorations. This is an alias for '--style=plain'. When '-p' is
used twice ('-pp'), it also disables automatic paging (alias for '--style=plain
Expand Down
2 changes: 2 additions & 0 deletions doc/short-help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Options:
Show non-printable characters (space, tab, newline, ..).
--nonprintable-notation <notation>
Set notation for non-printable characters.
--binary <behavior>
How to treat binary content. (default: no-printing)
-p, --plain...
Show plain style (alias for '--style=plain').
-l, --language <language>
Expand Down
6 changes: 6 additions & 0 deletions src/bin/bat/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::{
config::{get_args_from_config_file, get_args_from_env_opts_var, get_args_from_env_vars},
};
use bat::style::StyleComponentList;
use bat::BinaryBehavior;
use bat::StripAnsiMode;
use clap::ArgMatches;

Expand Down Expand Up @@ -193,6 +194,11 @@ impl App {
Some("caret") => NonprintableNotation::Caret,
_ => unreachable!("other values for --nonprintable-notation are not allowed"),
},
binary: match self.matches.get_one::<String>("binary").map(|s| s.as_str()) {
Some("as-text") => BinaryBehavior::AsText,
Some("no-printing") => BinaryBehavior::NoPrinting,
_ => unreachable!("other values for --binary are not allowed"),
},
wrapping_mode: if self.interactive_output || maybe_term_width.is_some() {
if !self.matches.get_flag("chop-long-lines") {
match self.matches.get_one::<String>("wrap").map(|s| s.as_str()) {
Expand Down
16 changes: 16 additions & 0 deletions src/bin/bat/clap_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,22 @@ pub fn build_app(interactive_output: bool) -> Command {
* caret (^G, ^J, ^@, ..)",
),
)
.arg(
Arg::new("binary")
.long("binary")
.action(ArgAction::Set)
.default_value("no-printing")
.value_parser(["no-printing", "as-text"])
.value_name("behavior")
.hide_default_value(true)
.help("How to treat binary content. (default: no-printing)")
.long_help(
"How to treat binary content. (default: no-printing)\n\n\
Possible values:\n \
* no-printing: do not print any binary content\n \
* as-text: treat binary content as normal text",
),
)
.arg(
Arg::new("plain")
.overrides_with("plain")
Expand Down
5 changes: 4 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::line_range::{HighlightedLineRanges, LineRanges};
use crate::nonprintable_notation::NonprintableNotation;
use crate::nonprintable_notation::{BinaryBehavior, NonprintableNotation};
#[cfg(feature = "paging")]
use crate::paging::PagingMode;
use crate::style::StyleComponents;
Expand Down Expand Up @@ -44,6 +44,9 @@ pub struct Config<'a> {
/// The configured notation for non-printable characters
pub nonprintable_notation: NonprintableNotation,

/// How to treat binary content
pub binary: BinaryBehavior,

/// The character width of the terminal
pub term_width: usize,

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ mod terminal;
mod vscreen;
pub(crate) mod wrapping;

pub use nonprintable_notation::NonprintableNotation;
pub use nonprintable_notation::{BinaryBehavior, NonprintableNotation};
pub use preprocessor::StripAnsiMode;
pub use pretty_printer::{Input, PrettyPrinter, Syntax};
pub use syntax_mapping::{MappingTarget, SyntaxMapping};
Expand Down
12 changes: 12 additions & 0 deletions src/nonprintable_notation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,15 @@ pub enum NonprintableNotation {
#[default]
Unicode,
}

/// How to treat binary content
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum BinaryBehavior {
/// Do not print any binary content
#[default]
NoPrinting,

/// Treat binary content as normal text
AsText,
}
22 changes: 17 additions & 5 deletions src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use crate::style::StyleComponent;
use crate::terminal::{as_terminal_escaped, to_ansi_color};
use crate::vscreen::{AnsiStyle, EscapeSequence, EscapeSequenceIterator};
use crate::wrapping::WrappingMode;
use crate::BinaryBehavior;
use crate::StripAnsiMode;

const ANSI_UNDERLINE_ENABLE: EscapeSequence = EscapeSequence::CSI {
Expand Down Expand Up @@ -268,7 +269,8 @@ impl<'a> InteractivePrinter<'a> {
.content_type
.map_or(false, |c| c.is_binary() && !config.show_nonprintable);

let needs_to_match_syntax = !is_printing_binary
let needs_to_match_syntax = (!is_printing_binary
|| matches!(config.binary, BinaryBehavior::AsText))
&& (config.colored_output || config.strip_ansi == StripAnsiMode::Auto);

let (is_plain_text, highlighter_from_set) = if needs_to_match_syntax {
Expand Down Expand Up @@ -458,7 +460,10 @@ impl<'a> Printer for InteractivePrinter<'a> {
}

if !self.config.style_components.header() {
if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable {
if Some(ContentType::BINARY) == self.content_type
&& !self.config.show_nonprintable
&& !matches!(self.config.binary, BinaryBehavior::AsText)
{
writeln!(
handle,
"{}: Binary content from {} will not be printed to the terminal \
Expand Down Expand Up @@ -539,7 +544,10 @@ impl<'a> Printer for InteractivePrinter<'a> {
})?;

if self.config.style_components.grid() {
if self.content_type.map_or(false, |c| c.is_text()) || self.config.show_nonprintable {
if self.content_type.map_or(false, |c| c.is_text())
|| self.config.show_nonprintable
|| matches!(self.config.binary, BinaryBehavior::AsText)
{
self.print_horizontal_line(handle, '┼')?;
} else {
self.print_horizontal_line(handle, '┴')?;
Expand All @@ -551,7 +559,9 @@ impl<'a> Printer for InteractivePrinter<'a> {

fn print_footer(&mut self, handle: &mut OutputHandle, _input: &OpenedInput) -> Result<()> {
if self.config.style_components.grid()
&& (self.content_type.map_or(false, |c| c.is_text()) || self.config.show_nonprintable)
&& (self.content_type.map_or(false, |c| c.is_text())
|| self.config.show_nonprintable
|| matches!(self.config.binary, BinaryBehavior::AsText))
{
self.print_horizontal_line(handle, '┴')
} else {
Expand Down Expand Up @@ -599,7 +609,9 @@ impl<'a> Printer for InteractivePrinter<'a> {
.into()
} else {
let mut line = match self.content_type {
Some(ContentType::BINARY) | None => {
Some(ContentType::BINARY) | None
if !matches!(self.config.binary, BinaryBehavior::AsText) =>
{
return Ok(());
}
Some(ContentType::UTF_16LE) => UTF_16LE.decode_with_bom_removal(line_buffer).0,
Expand Down
10 changes: 10 additions & 0 deletions tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1938,6 +1938,16 @@ fn show_all_with_unicode() {
.stderr("");
}

#[test]
fn binary_as_text() {
bat()
.arg("--binary=as-text")
.arg("control_characters.txt")
.assert()
.stdout("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F")
.stderr("");
}

#[test]
fn no_paging_arg() {
bat()
Expand Down
Loading