Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package hexyl for openSUSE:Factory checked 
in at 2026-02-16 13:10:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/hexyl (Old)
 and      /work/SRC/openSUSE:Factory/.hexyl.new.1977 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "hexyl"

Mon Feb 16 13:10:55 2026 rev:9 rq:1333171 version:0.17.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/hexyl/hexyl.changes      2025-01-27 
20:55:04.330183793 +0100
+++ /work/SRC/openSUSE:Factory/.hexyl.new.1977/hexyl.changes    2026-02-16 
13:17:24.146223752 +0100
@@ -1,0 +2,16 @@
+Sat Feb 14 15:14:11 UTC 2026 - Martin Hauke <[email protected]>
+
+- Update to version 0.17.0
+  * Enable custom colors with environment variables.
+  * Add new braille character table and color schemes.
+  * Add shell completion.
+  * Add option to output result in C include file style.
+  * Fix some clippy warnings.
+  * Fix issue #238.
+  * Check if terminal_width is less than offset and return 1 (#244)
+  * Add colors to --help.
+  * feat: handle standard input "-".
+- Package bash-completion.
+- Package manpage.
+
+-------------------------------------------------------------------

Old:
----
  cargo_config
  hexyl-0.16.0.tar.gz

New:
----
  hexyl-0.17.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ hexyl.spec ++++++
--- /var/tmp/diff_new_pack.j56lRW/_old  2026-02-16 13:17:25.154266791 +0100
+++ /var/tmp/diff_new_pack.j56lRW/_new  2026-02-16 13:17:25.154266791 +0100
@@ -1,8 +1,8 @@
 #
 # spec file for package hexyl
 #
-# Copyright (c) 2025 SUSE LLC
-# Copyright (c) 2022-2024, Martin Hauke <[email protected]>
+# Copyright (c) 2026 SUSE LLC and contributors
+# Copyright (c) 2022-2026, Martin Hauke <[email protected]>
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,19 +18,17 @@
 
 
 Name:           hexyl
-Version:        0.16.0
+Version:        0.17.0
 Release:        0
 Summary:        A command-line hex viewer
-License:        Apache-2.0
+License:        Apache-2.0 OR MIT
 Group:          Development/Tools/Other
 #Git-Clone:     https://github.com/sharkdp/hexyl.git
 URL:            https://github.com/sharkdp/hexyl
 Source:         
https://github.com/sharkdp/hexyl/archive/refs/tags/v%{version}.tar.gz#/%{name}-%{version}.tar.gz
 Source1:        vendor.tar.xz
-Source2:        cargo_config
-BuildRequires:  cargo
 BuildRequires:  cargo-packaging
-BuildRequires:  rust
+BuildRequires:  pandoc
 
 %description
 hexyl is a simple hex viewer for the terminal. It uses a colored output
@@ -40,19 +38,24 @@
 
 %prep
 %autosetup -p 1 -a 1
-install -D -m 0644 %{SOURCE2} .cargo/config
 
 %build
 %{cargo_build}
+pandoc --standalone --to man ./doc/hexyl.1.md -o hexyl.1
 
 %check
 %{cargo_test}
 
 %install
 cargo install --no-track --root=%{buildroot}%{_prefix} --path .
+install -Dm 0644 %{name}.1 %{buildroot}%{_mandir}/man1/%{name}.1
+%{buildroot}%{_bindir}/%{name} --completion bash > %{name}.bash
+install -Dm 0644 %{name}.bash 
%{buildroot}%{_datadir}/bash-completion/completions/%{name}
 
 %files
-%license LICENSE-APACHE
+%license LICENSE-APACHE LICENSE-MIT
 %doc CHANGELOG.md README.md
 %{_bindir}/hexyl
+%{_datadir}/bash-completion/completions/%{name}
+%{_mandir}/man1/%{name}.1%{?ext_man}
 

++++++ hexyl-0.16.0.tar.gz -> hexyl-0.17.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/.github/workflows/CICD.yml 
new/hexyl-0.17.0/.github/workflows/CICD.yml
--- old/hexyl-0.16.0/.github/workflows/CICD.yml 2024-12-27 14:43:18.000000000 
+0100
+++ new/hexyl-0.17.0/.github/workflows/CICD.yml 2026-02-14 13:53:54.000000000 
+0100
@@ -36,7 +36,7 @@
 
   ensure_cargo_fmt:
     name: Ensure 'cargo fmt' has been run
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-24.04
     steps:
     - uses: dtolnay/rust-toolchain@stable
       with:
@@ -46,7 +46,7 @@
 
   min_version:
     name: Minimum supported rust version
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-24.04
     needs: crate_metadata
     steps:
     - name: Checkout source code
@@ -70,18 +70,19 @@
       fail-fast: false
       matrix:
         job:
-          - { target: aarch64-unknown-linux-gnu   , os: ubuntu-20.04, 
use-cross: true }
-          - { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, 
use-cross: true }
-          - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, 
use-cross: true }
-          - { target: i686-pc-windows-msvc        , os: windows-2019           
       }
-          - { target: i686-unknown-linux-gnu      , os: ubuntu-20.04, 
use-cross: true }
-          - { target: i686-unknown-linux-musl     , os: ubuntu-20.04, 
use-cross: true }
-          - { target: x86_64-apple-darwin         , os: macos-13               
       }
+          - { target: aarch64-unknown-linux-gnu   , os: ubuntu-24.04, 
use-cross: true }
+          - { target: arm-unknown-linux-gnueabihf , os: ubuntu-24.04, 
use-cross: true }
+          - { target: arm-unknown-linux-musleabihf, os: ubuntu-24.04, 
use-cross: true }
+          - { target: i686-pc-windows-msvc        , os: windows-2025           
       }
+          - { target: i686-unknown-linux-gnu      , os: ubuntu-24.04, 
use-cross: true }
+          - { target: i686-unknown-linux-musl     , os: ubuntu-24.04, 
use-cross: true }
+          - { target: x86_64-apple-darwin         , os: macos-15-intel         
       }
           - { target: aarch64-apple-darwin        , os: macos-15               
       }
-          - { target: x86_64-pc-windows-gnu       , os: windows-2019           
       }
-          - { target: x86_64-pc-windows-msvc      , os: windows-2019           
       }
-          - { target: x86_64-unknown-linux-gnu    , os: ubuntu-20.04, 
use-cross: true }
-          - { target: x86_64-unknown-linux-musl   , os: ubuntu-20.04, 
use-cross: true }
+          # Was causing CI failures unrelated to app logic
+          # - { target: x86_64-pc-windows-gnu       , os: windows-2019         
         }
+          - { target: x86_64-pc-windows-msvc      , os: windows-2025           
       }
+          - { target: x86_64-unknown-linux-gnu    , os: ubuntu-24.04, 
use-cross: true }
+          - { target: x86_64-unknown-linux-musl   , os: ubuntu-24.04, 
use-cross: true }
     env:
       BUILD_CMD: cargo
     steps:
@@ -155,7 +156,7 @@
       run: |
         # test only library unit tests and binary for arm-type targets
         unset CARGO_TEST_OPTIONS
-        unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | 
aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${{ 
needs.crate_metadata.outputs.name }}" ;; esac;
+        case ${{ matrix.job.target }} in arm-* | aarch64-*) 
CARGO_TEST_OPTIONS="--lib --bin ${{ needs.crate_metadata.outputs.name }}" ;; 
esac;
         echo "CARGO_TEST_OPTIONS=${CARGO_TEST_OPTIONS}" >> $GITHUB_OUTPUT
 
     - name: Run tests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/CHANGELOG.md 
new/hexyl-0.17.0/CHANGELOG.md
--- old/hexyl-0.16.0/CHANGELOG.md       2024-12-27 14:43:18.000000000 +0100
+++ new/hexyl-0.17.0/CHANGELOG.md       2026-02-14 13:53:54.000000000 +0100
@@ -1,9 +1,17 @@
-# unreleased 
+# v0.17.0
 
 ## Features
 
+- Add option to output result in C include file style, see #242 (@wpcwzy)
+- Add `--color-scheme` option, see #247 (@aticu)
+- Add `braille` character table, see #247 (@aticu)
+- Add command line argument to generate shell completion, see #155 (@friedz)
+- Add colors to `--help`/`-h`, see #253 (@starsep)
+
 ## Bugfixes
 
+- Fix memory allocation bug when terminal width is less than 10, see #244 
(@selfup)
+
 
 # v0.16.0
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/Cargo.lock new/hexyl-0.17.0/Cargo.lock
--- old/hexyl-0.16.0/Cargo.lock 2024-12-27 14:43:18.000000000 +0100
+++ new/hexyl-0.17.0/Cargo.lock 2026-02-14 13:53:54.000000000 +0100
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "aho-corasick"
@@ -68,13 +68,12 @@
 
 [[package]]
 name = "assert_cmd"
-version = "2.0.16"
+version = "2.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d"
+checksum = "9c5bcfa8749ac45dd12cb11055aeeb6b27a3895560d60d71e3c23bf979e60514"
 dependencies = [
  "anstyle",
  "bstr",
- "doc-comment",
  "libc",
  "predicates",
  "predicates-core",
@@ -129,6 +128,15 @@
 ]
 
 [[package]]
+name = "clap_complete"
+version = "4.5.55"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a"
+dependencies = [
+ "clap",
+]
+
+[[package]]
 name = "clap_derive"
 version = "4.5.18"
 source = "registry+https://github.com/rust-lang/crates.io-index";
@@ -185,12 +193,6 @@
 checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
 
 [[package]]
-name = "doc-comment"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
-
-[[package]]
 name = "errno"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index";
@@ -217,11 +219,12 @@
 
 [[package]]
 name = "hexyl"
-version = "0.16.0"
+version = "0.17.0"
 dependencies = [
  "anyhow",
  "assert_cmd",
  "clap",
+ "clap_complete",
  "const_format",
  "libc",
  "owo-colors",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/Cargo.toml new/hexyl-0.17.0/Cargo.toml
--- old/hexyl-0.16.0/Cargo.toml 2024-12-27 14:43:18.000000000 +0100
+++ new/hexyl-0.17.0/Cargo.toml 2026-02-14 13:53:54.000000000 +0100
@@ -1,15 +1,16 @@
 [package]
 authors = ["David Peter <[email protected]>"]
 categories = ["command-line-utilities"]
+keywords = ["hex", "viewer"]
 description = "A command-line hex viewer"
 homepage = "https://github.com/sharkdp/hexyl";
 license = "MIT/Apache-2.0"
 name = "hexyl"
 readme = "README.md"
 repository = "https://github.com/sharkdp/hexyl";
-version = "0.16.0"
+version = "0.17.0"
 edition = "2021"
-rust-version = "1.74"
+rust-version = "1.88"
 
 [dependencies]
 anyhow = "1.0"
@@ -19,13 +20,14 @@
 supports-color = "3"
 thiserror = "1.0"
 terminal_size = "0.4"
+clap_complete = "4"
 
 [dependencies.clap]
 version = "4"
 features = ["derive", "wrap_help"]
 
 [dev-dependencies]
-assert_cmd = "2.0"
+assert_cmd = "2.1"
 predicates = "3.0"
 pretty_assertions = "1.4.0"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/README.md new/hexyl-0.17.0/README.md
--- old/hexyl-0.16.0/README.md  2024-12-27 14:43:18.000000000 +0100
+++ new/hexyl-0.17.0/README.md  2026-02-14 13:53:54.000000000 +0100
@@ -7,6 +7,18 @@
 `hexyl` is a hex viewer for the terminal. It uses a colored output to 
distinguish different categories
 of bytes (NULL bytes, printable ASCII characters, ASCII whitespace characters, 
other ASCII characters and non-ASCII).
 
+### Sponsors
+
+A special *thank you* goes to our biggest <a 
href="doc/sponsors.md">sponsor</a>:<br>
+
+<a href="https://www.warp.dev/hexyl";>
+  <img src="doc/sponsors/warp-logo.png" width="200" alt="Warp">
+  <br>
+  <strong>Warp, the intelligent terminal</strong>
+  <br>
+  <sub>Available on MacOS, Linux, Windows</sub>
+</a>
+
 ## Preview
 
 ![](https://i.imgur.com/MWO9uSL.png)
@@ -181,6 +193,22 @@
 x env use hexyl
 ```
 
+## Configuration
+
+`hexyl` colors can be configured via environment variables. The variables used 
are as follows:
+
+ * `HEXYL_COLOR_ASCII_PRINTABLE`: Any non-whitespace printable ASCII character
+ * `HEXYL_COLOR_ASCII_WHITESPACE`: Whitespace such as space or newline (only 
visible in middle panel with byte values)
+ * `HEXYL_COLOR_ASCII_OTHER`: Any other ASCII character (< `0x80`) besides null
+ * `HEXYL_COLOR_NULL`: The null byte (`0x00`)
+ * `HEXYL_COLOR_NONASCII`: Any non-ASCII byte (> `0x7F`)
+ * `HEXYL_COLOR_OFFSET`: The lefthand file offset
+
+The colors can be any of the 8 standard terminal colors: `black`, `blue`, 
`cyan`, `green`, `magenta`, `red`,
+`yellow` and `white`. The "bright" variants are also supported (e.g., `bright 
blue`). Additionally, you can use
+the RGB hex format, `#abcdef`. For example, `HEXYL_COLOR_ASCII_PRINTABLE=blue 
HEXYL_COLOR_ASCII_WHITESPACE="bright green"
+HEXYL_COLOR_ASCII_OTHER="#ff7f99"`.
+
 ## License
 
 Licensed under either of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/doc/hexyl.1.md 
new/hexyl-0.17.0/doc/hexyl.1.md
--- old/hexyl-0.16.0/doc/hexyl.1.md     2024-12-27 14:43:18.000000000 +0100
+++ new/hexyl-0.17.0/doc/hexyl.1.md     2026-02-14 13:53:54.000000000 +0100
@@ -106,6 +106,22 @@
 **-V**, **\--version**
 :   Prints version information.
 
+# ENVIRONMENT VARIABLES
+
+**hexyl** colors can be configured via environment variables. The variables 
used are as follows:
+
+:   - **HEXYL_COLOR_ASCII_PRINTABLE**: Any non-whitespace printable ASCII 
character
+    - **HEXYL_COLOR_ASCII_WHITESPACE**: Whitespace such as space or newline 
(only visible in middle panel with byte values)
+    - **HEXYL_COLOR_ASCII_OTHER**: Any other ASCII character (< **0x80**) 
besides null
+    - **HEXYL_COLOR_NULL**: The null byte (**0x00**)
+    - **HEXYL_COLOR_NONASCII**: Any non-ASCII byte (> **0x7F**)
+    - **HEXYL_COLOR_OFFSET**: The lefthand file offset
+
+The colors can be any of the 8 standard terminal colors: **black**, **blue**, 
**cyan**, **green**, **magenta**, **red**,
+**yellow** and **white**. The "bright" variants are also supported (e.g., 
**bright blue**). Additionally, you can use
+the RGB hex format, **#abcdef**. For example, 
**HEXYL_COLOR_ASCII_PRINTABLE=blue HEXYL_COLOR_ASCII_WHITESPACE="bright green"
+HEXYL_COLOR_ASCII_OTHER="#ff7f99"**.
+
 # NOTES
 
 Source repository:
Binary files old/hexyl-0.16.0/doc/sponsors/tuple-logo.png and 
new/hexyl-0.17.0/doc/sponsors/tuple-logo.png differ
Binary files old/hexyl-0.16.0/doc/sponsors/warp-logo.png and 
new/hexyl-0.17.0/doc/sponsors/warp-logo.png differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/doc/sponsors.md 
new/hexyl-0.17.0/doc/sponsors.md
--- old/hexyl-0.16.0/doc/sponsors.md    1970-01-01 01:00:00.000000000 +0100
+++ new/hexyl-0.17.0/doc/sponsors.md    2026-02-14 13:53:54.000000000 +0100
@@ -0,0 +1,14 @@
+## Sponsors
+
+`hexyl` development is sponsored by many individuals and companies. Thank you 
very much!
+
+Please note, that being sponsored does not affect the individuality of the 
`hexyl`
+project or affect the maintainers' actions in any way.
+We remain impartial and continue to assess pull requests solely on merit - the
+features added, bugs solved, and effect on the overall complexity of the code.
+No issue will have a different priority based on sponsorship status of the
+reporter.
+
+Contributions from anybody are most welcomed.
+
+If you want to see our biggest sponsors, check the top of 
[`README.md`](../README.md#sponsors).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/examples/simple.rs 
new/hexyl-0.17.0/examples/simple.rs
--- old/hexyl-0.16.0/examples/simple.rs 2024-12-27 14:43:18.000000000 +0100
+++ new/hexyl-0.17.0/examples/simple.rs 2026-02-14 13:53:54.000000000 +0100
@@ -3,7 +3,7 @@
 use hexyl::{BorderStyle, PrinterBuilder};
 
 fn main() {
-    let input = vec![
+    let input = [
         0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 
0x0d, 0x49, 0x48, 0x44,
         0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x44, 0x08, 0x02, 
0x00, 0x00, 0x00,
     ];
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/src/colors.rs 
new/hexyl-0.17.0/src/colors.rs
--- old/hexyl-0.16.0/src/colors.rs      2024-12-27 14:43:18.000000000 +0100
+++ new/hexyl-0.17.0/src/colors.rs      2026-02-14 13:53:54.000000000 +0100
@@ -1,12 +1,114 @@
-use owo_colors::{colors, Color};
+use owo_colors::{colors, AnsiColors, Color, DynColors, OwoColorize};
+use std::str::FromStr;
+use std::sync::LazyLock;
 
-pub const COLOR_NULL: &[u8] = colors::BrightBlack::ANSI_FG.as_bytes();
-pub const COLOR_OFFSET: &[u8] = colors::BrightBlack::ANSI_FG.as_bytes();
-pub const COLOR_ASCII_PRINTABLE: &[u8] = colors::Cyan::ANSI_FG.as_bytes();
-pub const COLOR_ASCII_WHITESPACE: &[u8] = colors::Green::ANSI_FG.as_bytes();
-pub const COLOR_ASCII_OTHER: &[u8] = colors::Green::ANSI_FG.as_bytes();
-pub const COLOR_NONASCII: &[u8] = colors::Yellow::ANSI_FG.as_bytes();
-pub const COLOR_RESET: &[u8] = colors::Default::ANSI_FG.as_bytes();
+pub static COLOR_NULL: LazyLock<String> =
+    LazyLock::new(|| init_color("NULL", AnsiColors::BrightBlack));
+pub static COLOR_OFFSET: LazyLock<String> =
+    LazyLock::new(|| init_color("OFFSET", AnsiColors::BrightBlack));
+pub static COLOR_ASCII_PRINTABLE: LazyLock<String> =
+    LazyLock::new(|| init_color("ASCII_PRINTABLE", AnsiColors::Cyan));
+pub static COLOR_ASCII_WHITESPACE: LazyLock<String> =
+    LazyLock::new(|| init_color("ASCII_WHITESPACE", AnsiColors::Green));
+pub static COLOR_ASCII_OTHER: LazyLock<String> =
+    LazyLock::new(|| init_color("ASCII_OTHER", AnsiColors::Green));
+pub static COLOR_NONASCII: LazyLock<String> =
+    LazyLock::new(|| init_color("NONASCII", AnsiColors::Yellow));
+pub const COLOR_RESET: &str = colors::Default::ANSI_FG;
+
+fn init_color(name: &str, default_ansi: AnsiColors) -> String {
+    let default = DynColors::Ansi(default_ansi);
+    let env_var = format!("HEXYL_COLOR_{name}");
+    let color = match std::env::var(env_var).as_deref() {
+        Ok(color) => match DynColors::from_str(color) {
+            Ok(color) => color,
+            _ => default,
+        },
+        _ => default,
+    };
+    // owo_colors' API isn't designed to get the terminal codes directly for
+    // dynamic colors, so we use this hack to get them from the LHS of some 
text.
+    format!("{}", "|".color(color))
+        .split_once("|")
+        .unwrap()
+        .0
+        .to_owned()
+}
+
+pub const COLOR_NULL_RGB: &[u8] = &rgb_bytes(100, 100, 100);
+
+pub const COLOR_DEL: &[u8] = &rgb_bytes(64, 128, 0);
+
+pub const COLOR_GRADIENT_NONASCII: [[u8; 19]; 128] =
+    generate_color_gradient(&[(255, 0, 0, 0.0), (255, 255, 0, 0.66), (255, 
255, 255, 1.0)]);
+
+pub const COLOR_GRADIENT_ASCII_NONPRINTABLE: [[u8; 19]; 31] =
+    generate_color_gradient(&[(255, 0, 255, 0.0), (128, 0, 255, 1.0)]);
+
+pub const COLOR_GRADIENT_ASCII_PRINTABLE: [[u8; 19]; 95] =
+    generate_color_gradient(&[(0, 128, 255, 0.0), (0, 255, 128, 1.0)]);
+
+const fn as_dec(byte: u8) -> [u8; 3] {
+    [
+        b'0' + (byte / 100),
+        b'0' + ((byte % 100) / 10),
+        b'0' + (byte % 10),
+    ]
+}
+
+const fn rgb_bytes(r: u8, g: u8, b: u8) -> [u8; 19] {
+    let mut buf = *b"\x1b[38;2;rrr;ggg;bbbm";
+
+    // r 7
+    buf[7] = as_dec(r)[0];
+    buf[8] = as_dec(r)[1];
+    buf[9] = as_dec(r)[2];
+
+    // g 11
+    buf[11] = as_dec(g)[0];
+    buf[12] = as_dec(g)[1];
+    buf[13] = as_dec(g)[2];
+
+    // b 15
+    buf[15] = as_dec(b)[0];
+    buf[16] = as_dec(b)[1];
+    buf[17] = as_dec(b)[2];
+
+    buf
+}
+
+const fn generate_color_gradient<const N: usize>(stops: &[(u8, u8, u8, f64)]) 
-> [[u8; 19]; N] {
+    let mut out = [rgb_bytes(0, 0, 0); N];
+
+    assert!(stops.len() >= 2, "need at least two stops for the gradient");
+
+    let mut byte = 0;
+    while byte < N {
+        let relative_byte = byte as f64 / N as f64;
+
+        let mut i = 1;
+        while i < stops.len() && stops[i].3 < relative_byte {
+            i += 1;
+        }
+        if i >= stops.len() {
+            i = stops.len() - 1;
+        }
+        let prev_stop = stops[i - 1];
+        let stop = stops[i];
+        let diff = stop.3 - prev_stop.3;
+        let t = (relative_byte - prev_stop.3) / diff;
+
+        let r = (prev_stop.0 as f64 + (t * (stop.0 as f64 - prev_stop.0 as 
f64))) as u8;
+        let g = (prev_stop.1 as f64 + (t * (stop.1 as f64 - prev_stop.1 as 
f64))) as u8;
+        let b = (prev_stop.2 as f64 + (t * (stop.2 as f64 - prev_stop.2 as 
f64))) as u8;
+
+        out[byte] = rgb_bytes(r, g, b);
+
+        byte += 1;
+    }
+
+    out
+}
 
 #[rustfmt::skip]
 pub const CP437: [char; 256] = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/src/input.rs 
new/hexyl-0.17.0/src/input.rs
--- old/hexyl-0.16.0/src/input.rs       2024-12-27 14:43:18.000000000 +0100
+++ new/hexyl-0.17.0/src/input.rs       2026-02-14 13:53:54.000000000 +0100
@@ -6,7 +6,7 @@
     Stdin(io::StdinLock<'a>),
 }
 
-impl<'a> Read for Input<'a> {
+impl Read for Input<'_> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         match *self {
             Input::File(ref mut file) => file.read(buf),
@@ -15,13 +15,13 @@
     }
 }
 
-impl<'a> Seek for Input<'a> {
+impl Seek for Input<'_> {
     fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
         fn try_skip<R>(reader: R, pos: SeekFrom, err_desc: &'static str) -> 
io::Result<u64>
         where
             R: Read,
         {
-            let cant_seek_abs_err = || 
Err(io::Error::new(io::ErrorKind::Other, err_desc));
+            let cant_seek_abs_err = || Err(io::Error::other(err_desc));
 
             let offset = match pos {
                 SeekFrom::Current(o) => u64::try_from(o).or_else(|_e| 
cant_seek_abs_err())?,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/src/lib.rs new/hexyl-0.17.0/src/lib.rs
--- old/hexyl-0.16.0/src/lib.rs 2024-12-27 14:43:18.000000000 +0100
+++ new/hexyl-0.17.0/src/lib.rs 2026-02-14 13:53:54.000000000 +0100
@@ -24,6 +24,13 @@
     NonAscii,
 }
 
+pub enum IncludeMode {
+    File(String), // filename
+    Stdin,
+    Slice,
+    Off,
+}
+
 #[derive(Copy, Clone, Debug, Default, ValueEnum)]
 #[non_exhaustive]
 pub enum CharacterTable {
@@ -43,6 +50,24 @@
     /// Uses code page 437 (for non-ASCII bytes).
     #[value(name = "codepage-437")]
     CP437,
+
+    /// Uses braille characters for non-printable bytes.
+    Braille,
+}
+
+#[derive(Copy, Clone, Debug, Default, ValueEnum)]
+#[non_exhaustive]
+pub enum ColorScheme {
+    /// Show the default colors: bright black for NULL bytes, green for ASCII
+    /// space characters and non-printable ASCII, cyan for printable ASCII 
characters,
+    /// and yellow for non-ASCII bytes.
+    #[default]
+    Default,
+
+    /// Show bright black for NULL bytes, cyan for printable ASCII characters, 
a gradient
+    /// from pink to violet for non-printable ASCII characters and a 
heatmap-like gradient
+    /// from red to yellow to white for non-ASCII bytes.
+    Gradient,
 }
 
 #[derive(Copy, Clone, Debug, Default, ValueEnum)]
@@ -81,14 +106,29 @@
         }
     }
 
-    fn color(self) -> &'static [u8] {
+    fn color(self, color_scheme: ColorScheme) -> &'static [u8] {
         use crate::ByteCategory::*;
-        match self.category() {
-            Null => COLOR_NULL,
-            AsciiPrintable => COLOR_ASCII_PRINTABLE,
-            AsciiWhitespace => COLOR_ASCII_WHITESPACE,
-            AsciiOther => COLOR_ASCII_OTHER,
-            NonAscii => COLOR_NONASCII,
+        match color_scheme {
+            ColorScheme::Default => match self.category() {
+                Null => COLOR_NULL.as_bytes(),
+                AsciiPrintable => COLOR_ASCII_PRINTABLE.as_bytes(),
+                AsciiWhitespace => COLOR_ASCII_WHITESPACE.as_bytes(),
+                AsciiOther => COLOR_ASCII_OTHER.as_bytes(),
+                NonAscii => COLOR_NONASCII.as_bytes(),
+            },
+            ColorScheme::Gradient => match self.category() {
+                Null => COLOR_NULL_RGB,
+                AsciiWhitespace if self.0 == b' ' => 
&COLOR_GRADIENT_ASCII_PRINTABLE[0],
+                AsciiPrintable => &COLOR_GRADIENT_ASCII_PRINTABLE[(self.0 - b' 
') as usize],
+                AsciiWhitespace | AsciiOther => {
+                    if self.0 == 0x7f {
+                        COLOR_DEL
+                    } else {
+                        &COLOR_GRADIENT_ASCII_NONPRINTABLE[self.0 as usize - 1]
+                    }
+                }
+                NonAscii => &COLOR_GRADIENT_NONASCII[(self.0 - 128) as usize],
+            },
         }
     }
 
@@ -113,6 +153,37 @@
             },
             CharacterTable::CP1047 => CP1047[self.0 as usize],
             CharacterTable::CP437 => CP437[self.0 as usize],
+            CharacterTable::Braille => match self.category() {
+                // null is important enough to get its own symbol
+                Null => '⋄',
+                AsciiPrintable => self.0 as char,
+                AsciiWhitespace if self.0 == b' ' => ' ',
+                // `\t`, `\n` and `\r` are important enough to get their own 
symbols
+                AsciiWhitespace if self.0 == b'\t' => '→',
+                AsciiWhitespace if self.0 == b'\n' => '↵',
+                AsciiWhitespace if self.0 == b'\r' => '←',
+                AsciiWhitespace | AsciiOther | NonAscii => {
+                    /// Adjust the bits from the original number to a new 
number.
+                    ///
+                    /// Bit positions in braille are adjusted as follows:
+                    ///
+                    /// ```text
+                    /// 0 3 => 0 1
+                    /// 1 4 => 2 3
+                    /// 2 5 => 4 5
+                    /// 6 7 => 6 7
+                    /// ```
+                    fn to_braille_bits(byte: u8) -> u8 {
+                        let mut out = 0;
+                        for (from, to) in [0, 3, 1, 4, 2, 5, 6, 
7].into_iter().enumerate() {
+                            out |= (byte >> from & 1) << to;
+                        }
+                        out
+                    }
+
+                    char::from_u32(0x2800 + to_braille_bits(self.0) as 
u32).unwrap()
+                }
+            },
         }
     }
 }
@@ -203,6 +274,8 @@
     base: Base,
     endianness: Endianness,
     character_table: CharacterTable,
+    include_mode: IncludeMode,
+    color_scheme: ColorScheme,
 }
 
 impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
@@ -219,6 +292,8 @@
             base: Base::Hexadecimal,
             endianness: Endianness::Big,
             character_table: CharacterTable::Default,
+            include_mode: IncludeMode::Off,
+            color_scheme: ColorScheme::Default,
         }
     }
 
@@ -272,20 +347,57 @@
         self
     }
 
+    pub fn include_mode(mut self, include: IncludeMode) -> Self {
+        self.include_mode = include;
+        self
+    }
+
+    pub fn color_scheme(mut self, color_scheme: ColorScheme) -> Self {
+        self.color_scheme = color_scheme;
+        self
+    }
+
     pub fn build(self) -> Printer<'a, Writer> {
-        Printer::new(
-            self.writer,
-            self.show_color,
-            self.show_char_panel,
-            self.show_position_panel,
-            self.border_style,
-            self.use_squeeze,
-            self.panels,
-            self.group_size,
-            self.base,
-            self.endianness,
-            self.character_table,
-        )
+        Printer {
+            idx: 0,
+            line_buf: vec![0x0; 8 * self.panels as usize],
+            writer: self.writer,
+            show_char_panel: self.show_char_panel,
+            show_position_panel: self.show_position_panel,
+            show_color: self.show_color,
+            curr_color: None,
+            color_scheme: self.color_scheme,
+            border_style: self.border_style,
+            byte_hex_panel: (0u8..=u8::MAX)
+                .map(|i| match self.base {
+                    Base::Binary => format!("{i:08b}"),
+                    Base::Octal => format!("{i:03o}"),
+                    Base::Decimal => format!("{i:03}"),
+                    Base::Hexadecimal => format!("{i:02x}"),
+                })
+                .collect(),
+            byte_char_panel: (0u8..=u8::MAX)
+                .map(|i| format!("{}", Byte(i).as_char(self.character_table)))
+                .collect(),
+            byte_hex_panel_g: (0u8..=u8::MAX).map(|i| 
format!("{i:02x}")).collect(),
+            squeezer: if self.use_squeeze {
+                Squeezer::Ignore
+            } else {
+                Squeezer::Disabled
+            },
+            display_offset: 0,
+            panels: self.panels,
+            squeeze_byte: 0x00,
+            group_size: self.group_size,
+            base_digits: match self.base {
+                Base::Binary => 8,
+                Base::Octal => 3,
+                Base::Decimal => 3,
+                Base::Hexadecimal => 2,
+            },
+            endianness: self.endianness,
+            include_mode: self.include_mode,
+        }
     }
 }
 
@@ -298,6 +410,7 @@
     show_position_panel: bool,
     show_color: bool,
     curr_color: Option<&'static [u8]>,
+    color_scheme: ColorScheme,
     border_style: BorderStyle,
     byte_hex_panel: Vec<String>,
     byte_char_panel: Vec<String>,
@@ -314,62 +427,11 @@
     base_digits: u8,
     /// Whether to show groups in little or big endian format.
     endianness: Endianness,
+    /// Whether to output in C include file style.
+    include_mode: IncludeMode,
 }
 
 impl<'a, Writer: Write> Printer<'a, Writer> {
-    fn new(
-        writer: &'a mut Writer,
-        show_color: bool,
-        show_char_panel: bool,
-        show_position_panel: bool,
-        border_style: BorderStyle,
-        use_squeeze: bool,
-        panels: u64,
-        group_size: u8,
-        base: Base,
-        endianness: Endianness,
-        character_table: CharacterTable,
-    ) -> Printer<'a, Writer> {
-        Printer {
-            idx: 0,
-            line_buf: vec![0x0; 8 * panels as usize],
-            writer,
-            show_char_panel,
-            show_position_panel,
-            show_color,
-            curr_color: None,
-            border_style,
-            byte_hex_panel: (0u8..=u8::MAX)
-                .map(|i| match base {
-                    Base::Binary => format!("{i:08b}"),
-                    Base::Octal => format!("{i:03o}"),
-                    Base::Decimal => format!("{i:03}"),
-                    Base::Hexadecimal => format!("{i:02x}"),
-                })
-                .collect(),
-            byte_char_panel: (0u8..=u8::MAX)
-                .map(|i| format!("{}", Byte(i).as_char(character_table)))
-                .collect(),
-            byte_hex_panel_g: (0u8..=u8::MAX).map(|i| 
format!("{i:02x}")).collect(),
-            squeezer: if use_squeeze {
-                Squeezer::Ignore
-            } else {
-                Squeezer::Disabled
-            },
-            display_offset: 0,
-            panels,
-            squeeze_byte: 0x00,
-            group_size,
-            base_digits: match base {
-                Base::Binary => 8,
-                Base::Octal => 3,
-                Base::Decimal => 3,
-                Base::Hexadecimal => 2,
-            },
-            endianness,
-        }
-    }
-
     pub fn display_offset(&mut self, display_offset: u64) -> &mut Self {
         self.display_offset = display_offset;
         self
@@ -440,14 +502,14 @@
                 .as_bytes(),
         )?;
         if self.show_color {
-            self.writer.write_all(COLOR_OFFSET)?;
+            self.writer.write_all(COLOR_OFFSET.as_bytes())?;
         }
         if self.show_position_panel {
             match self.squeezer {
                 Squeezer::Print => {
-                    self.writer.write_all(&[b'*'])?;
+                    self.writer.write_all(b"*")?;
                     if self.show_color {
-                        self.writer.write_all(COLOR_RESET)?;
+                        self.writer.write_all(COLOR_RESET.as_bytes())?;
                     }
                     self.writer.write_all(b"       ")?;
                 }
@@ -462,7 +524,7 @@
                             .write_all(self.byte_hex_panel_g[byte as 
usize].as_bytes())?;
                     }
                     if self.show_color {
-                        self.writer.write_all(COLOR_RESET)?;
+                        self.writer.write_all(COLOR_RESET.as_bytes())?;
                     }
                 }
             }
@@ -481,9 +543,10 @@
             Squeezer::Print | Squeezer::Delete => self.writer.write_all(b" ")?,
             Squeezer::Ignore | Squeezer::Disabled => {
                 if let Some(&b) = self.line_buf.get(i as usize) {
-                    if self.show_color && self.curr_color != 
Some(Byte(b).color()) {
-                        self.writer.write_all(Byte(b).color())?;
-                        self.curr_color = Some(Byte(b).color());
+                    if self.show_color && self.curr_color != 
Some(Byte(b).color(self.color_scheme))
+                    {
+                        
self.writer.write_all(Byte(b).color(self.color_scheme))?;
+                        self.curr_color = 
Some(Byte(b).color(self.color_scheme));
                     }
                     self.writer
                         .write_all(self.byte_char_panel[b as 
usize].as_bytes())?;
@@ -494,7 +557,7 @@
         }
         if i == 8 * self.panels - 1 {
             if self.show_color {
-                self.writer.write_all(COLOR_RESET)?;
+                self.writer.write_all(COLOR_RESET.as_bytes())?;
                 self.curr_color = None;
             }
             self.writer.write_all(
@@ -505,7 +568,7 @@
             )?;
         } else if i % 8 == 7 {
             if self.show_color {
-                self.writer.write_all(COLOR_RESET)?;
+                self.writer.write_all(COLOR_RESET.as_bytes())?;
                 self.curr_color = None;
             }
             self.writer.write_all(
@@ -531,14 +594,14 @@
             Squeezer::Print => {
                 if !self.show_position_panel && i == 0 {
                     if self.show_color {
-                        self.writer.write_all(COLOR_OFFSET)?;
+                        self.writer.write_all(COLOR_OFFSET.as_bytes())?;
                     }
                     self.writer
                         .write_all(self.byte_char_panel[b'*' as 
usize].as_bytes())?;
                     if self.show_color {
-                        self.writer.write_all(COLOR_RESET)?;
+                        self.writer.write_all(COLOR_RESET.as_bytes())?;
                     }
-                } else if i % (self.group_size as usize) == 0 {
+                } else if i.is_multiple_of(self.group_size as usize) {
                     self.writer.write_all(b" ")?;
                 }
                 for _ in 0..self.base_digits {
@@ -547,12 +610,12 @@
             }
             Squeezer::Delete => self.writer.write_all(b"   ")?,
             Squeezer::Ignore | Squeezer::Disabled => {
-                if i % (self.group_size as usize) == 0 {
+                if i.is_multiple_of(self.group_size as usize) {
                     self.writer.write_all(b" ")?;
                 }
-                if self.show_color && self.curr_color != Some(Byte(b).color()) 
{
-                    self.writer.write_all(Byte(b).color())?;
-                    self.curr_color = Some(Byte(b).color());
+                if self.show_color && self.curr_color != 
Some(Byte(b).color(self.color_scheme)) {
+                    self.writer.write_all(Byte(b).color(self.color_scheme))?;
+                    self.curr_color = Some(Byte(b).color(self.color_scheme));
                 }
                 self.writer
                     .write_all(self.byte_hex_panel[b as usize].as_bytes())?;
@@ -562,7 +625,7 @@
         if i % 8 == 7 {
             if self.show_color {
                 self.curr_color = None;
-                self.writer.write_all(COLOR_RESET)?;
+                self.writer.write_all(COLOR_RESET.as_bytes())?;
             }
             self.writer.write_all(b" ")?;
             // byte is last in last panel
@@ -585,7 +648,7 @@
         Ok(())
     }
 
-    fn reorder_buffer_to_little_endian(&self, buf: &mut Vec<u8>) {
+    fn reorder_buffer_to_little_endian(&self, buf: &mut [u8]) {
         let n = buf.len();
         let group_sz = self.group_size as usize;
 
@@ -617,6 +680,36 @@
 
         let mut buf = BufReader::new(reader);
 
+        // special handler for include mode
+        match &self.include_mode {
+            // Input from a file
+            // Output like `unsigned char <filename>[] = { ... }; unsigned int 
<filename>_len = ...;`
+            IncludeMode::File(filename) => {
+                // convert non-alphanumeric characters to '_'
+                let var_name = filename
+                    .chars()
+                    .map(|c| if c.is_alphanumeric() { c } else { '_' })
+                    .collect::<String>();
+
+                writeln!(self.writer, "unsigned char {}[] = {{", var_name)?;
+
+                let total_bytes = self.print_bytes_in_include_style(&mut buf)?;
+
+                writeln!(self.writer, "}};")?;
+                writeln!(
+                    self.writer,
+                    "unsigned int {}_len = {};",
+                    var_name, total_bytes
+                )?;
+                return Ok(());
+            }
+            IncludeMode::Stdin | IncludeMode::Slice => {
+                self.print_bytes_in_include_style(&mut buf)?;
+                return Ok(());
+            }
+            IncludeMode::Off => {}
+        }
+
         let leftover = loop {
             // read a maximum of 8 * self.panels bytes from the reader
             if let Ok(n) = buf.read(&mut self.line_buf) {
@@ -733,8 +826,8 @@
             writeln!(self.writer)?;
         } else if let Some(n) = leftover {
             // last line is incomplete
-            self.print_position_panel()?;
             self.squeezer = Squeezer::Ignore;
+            self.print_position_panel()?;
             self.print_bytes()?;
             self.squeezer = Squeezer::Print;
             for i in n..8 * self.panels as usize {
@@ -757,6 +850,44 @@
 
         Ok(())
     }
+
+    /// Print the bytes in C include file style
+    /// Return the number of bytes read  
+    fn print_bytes_in_include_style<Reader: Read>(
+        &mut self,
+        buf: &mut BufReader<Reader>,
+    ) -> Result<usize, io::Error> {
+        let mut buffer = [0; 1024];
+        let mut total_bytes = 0;
+        let mut is_first_chunk = true;
+        let mut line_counter = 0;
+        loop {
+            match buf.read(&mut buffer) {
+                Ok(0) => break, // EOF
+                Ok(bytes_read) => {
+                    total_bytes += bytes_read;
+
+                    for &byte in &buffer[..bytes_read] {
+                        if line_counter % 12 == 0 {
+                            if !is_first_chunk || line_counter > 0 {
+                                writeln!(self.writer, ",")?;
+                            }
+                            // indentation of first line
+                            write!(self.writer, "  ")?;
+                            is_first_chunk = false;
+                        } else {
+                            write!(self.writer, ", ")?;
+                        }
+                        write!(self.writer, "0x{:02x}", byte)?;
+                        line_counter += 1;
+                    }
+                }
+                Err(e) => return Err(e),
+            }
+        }
+        writeln!(self.writer)?;
+        Ok(total_bytes)
+    }
 }
 
 #[cfg(test)]
@@ -768,19 +899,20 @@
 
     fn assert_print_all_output<Reader: Read>(input: Reader, expected_string: 
String) {
         let mut output = vec![];
-        let mut printer = Printer::new(
-            &mut output,
-            false,
-            true,
-            true,
-            BorderStyle::Unicode,
-            true,
-            2,
-            1,
-            Base::Hexadecimal,
-            Endianness::Big,
-            CharacterTable::Default,
-        );
+        let mut printer = PrinterBuilder::new(&mut output)
+            .show_color(false)
+            .show_char_panel(true)
+            .show_position_panel(true)
+            .with_border_style(BorderStyle::Unicode)
+            .enable_squeezing(true)
+            .num_panels(2)
+            .group_size(1)
+            .with_base(Base::Hexadecimal)
+            .endianness(Endianness::Big)
+            .character_table(CharacterTable::Default)
+            .include_mode(IncludeMode::Off)
+            .color_scheme(ColorScheme::Default)
+            .build();
 
         printer.print_all(input).unwrap();
 
@@ -824,19 +956,20 @@
         .to_owned();
 
         let mut output = vec![];
-        let mut printer: Printer<Vec<u8>> = Printer::new(
-            &mut output,
-            false,
-            true,
-            true,
-            BorderStyle::Unicode,
-            true,
-            2,
-            1,
-            Base::Hexadecimal,
-            Endianness::Big,
-            CharacterTable::Default,
-        );
+        let mut printer: Printer<Vec<u8>> = PrinterBuilder::new(&mut output)
+            .show_color(false)
+            .show_char_panel(true)
+            .show_position_panel(true)
+            .with_border_style(BorderStyle::Unicode)
+            .enable_squeezing(true)
+            .num_panels(2)
+            .group_size(1)
+            .with_base(Base::Hexadecimal)
+            .endianness(Endianness::Big)
+            .character_table(CharacterTable::Default)
+            .include_mode(IncludeMode::Off)
+            .color_scheme(ColorScheme::Default)
+            .build();
         printer.display_offset(0xdeadbeef);
 
         printer.print_all(input).unwrap();
@@ -859,19 +992,20 @@
         .to_owned();
 
         let mut output = vec![];
-        let mut printer: Printer<Vec<u8>> = Printer::new(
-            &mut output,
-            false,
-            true,
-            true,
-            BorderStyle::Unicode,
-            true,
-            4,
-            1,
-            Base::Hexadecimal,
-            Endianness::Big,
-            CharacterTable::Default,
-        );
+        let mut printer: Printer<Vec<u8>> = PrinterBuilder::new(&mut output)
+            .show_color(false)
+            .show_char_panel(true)
+            .show_position_panel(true)
+            .with_border_style(BorderStyle::Unicode)
+            .enable_squeezing(true)
+            .num_panels(4)
+            .group_size(1)
+            .with_base(Base::Hexadecimal)
+            .endianness(Endianness::Big)
+            .character_table(CharacterTable::Default)
+            .include_mode(IncludeMode::Off)
+            .color_scheme(ColorScheme::Default)
+            .build();
 
         printer.print_all(input).unwrap();
 
@@ -920,19 +1054,96 @@
         .to_owned();
 
         let mut output = vec![];
-        let mut printer: Printer<Vec<u8>> = Printer::new(
-            &mut output,
-            false,
-            true,
-            true,
-            BorderStyle::Unicode,
-            true,
-            3,
-            1,
-            Base::Hexadecimal,
-            Endianness::Big,
-            CharacterTable::Default,
-        );
+        let mut printer: Printer<Vec<u8>> = PrinterBuilder::new(&mut output)
+            .show_color(false)
+            .show_char_panel(true)
+            .show_position_panel(true)
+            .with_border_style(BorderStyle::Unicode)
+            .enable_squeezing(true)
+            .num_panels(3)
+            .group_size(1)
+            .with_base(Base::Hexadecimal)
+            .endianness(Endianness::Big)
+            .character_table(CharacterTable::Default)
+            .include_mode(IncludeMode::Off)
+            .color_scheme(ColorScheme::Default)
+            .build();
+
+        printer.print_all(input).unwrap();
+
+        let actual_string: &str = str::from_utf8(&output).unwrap();
+        assert_eq!(actual_string, expected_string)
+    }
+
+    // issue#238
+    #[test]
+    fn display_offset_in_last_line() {
+        let input = io::Cursor::new(b"AAAAAAAAAAAAAAAACCCC");
+        let expected_string = "\
+┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
+│00000000│ 41 41 41 41 41 41 41 41 ┊ 41 41 41 41 41 41 41 41 
│AAAAAAAA┊AAAAAAAA│
+│00000010│ 43 43 43 43             ┊                         │CCCC    ┊        
│
+└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
+"
+        .to_owned();
+        assert_print_all_output(input, expected_string);
+    }
+
+    #[test]
+    fn include_mode_from_file() {
+        let input = io::Cursor::new(b"spamspamspamspamspam");
+        let expected_string = "unsigned char test_txt[] = {
+  0x73, 0x70, 0x61, 0x6d, 0x73, 0x70, 0x61, 0x6d, 0x73, 0x70, 0x61, 0x6d,
+  0x73, 0x70, 0x61, 0x6d, 0x73, 0x70, 0x61, 0x6d
+};
+unsigned int test_txt_len = 20;
+"
+        .to_owned();
+        let mut output = vec![];
+        let mut printer: Printer<Vec<u8>> = PrinterBuilder::new(&mut output)
+            .show_color(false)
+            .show_char_panel(true)
+            .show_position_panel(true)
+            .with_border_style(BorderStyle::Unicode)
+            .enable_squeezing(true)
+            .num_panels(2)
+            .group_size(1)
+            .with_base(Base::Hexadecimal)
+            .endianness(Endianness::Big)
+            .character_table(CharacterTable::Default)
+            .include_mode(IncludeMode::File("test.txt".to_owned()))
+            .color_scheme(ColorScheme::Default)
+            .build();
+
+        printer.print_all(input).unwrap();
+
+        let actual_string: &str = str::from_utf8(&output).unwrap();
+        assert_eq!(actual_string, expected_string)
+    }
+
+    #[test]
+    fn include_mode_from_stdin() {
+        let input = io::Cursor::new(b"spamspamspamspamspam");
+        let expected_string =
+            "  0x73, 0x70, 0x61, 0x6d, 0x73, 0x70, 0x61, 0x6d, 0x73, 0x70, 
0x61, 0x6d,
+  0x73, 0x70, 0x61, 0x6d, 0x73, 0x70, 0x61, 0x6d
+"
+            .to_owned();
+        let mut output = vec![];
+        let mut printer: Printer<Vec<u8>> = PrinterBuilder::new(&mut output)
+            .show_color(false)
+            .show_char_panel(true)
+            .show_position_panel(true)
+            .with_border_style(BorderStyle::Unicode)
+            .enable_squeezing(true)
+            .num_panels(2)
+            .group_size(1)
+            .with_base(Base::Hexadecimal)
+            .endianness(Endianness::Big)
+            .character_table(CharacterTable::Default)
+            .include_mode(IncludeMode::Stdin)
+            .color_scheme(ColorScheme::Default)
+            .build();
 
         printer.print_all(input).unwrap();
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/src/main.rs new/hexyl-0.17.0/src/main.rs
--- old/hexyl-0.16.0/src/main.rs        2024-12-27 14:43:18.000000000 +0100
+++ new/hexyl-0.17.0/src/main.rs        2026-02-14 13:53:54.000000000 +0100
@@ -3,8 +3,11 @@
 use std::num::{NonZeroI64, NonZeroU64};
 use std::path::PathBuf;
 
+use clap::builder::styling::{AnsiColor, Effects};
 use clap::builder::ArgPredicate;
-use clap::{ArgAction, Parser, ValueEnum};
+use clap::builder::Styles;
+use clap::{ArgAction, CommandFactory, Parser, ValueEnum};
+use clap_complete::aot::{generate, Shell};
 
 use anyhow::{anyhow, bail, Context, Result};
 
@@ -14,7 +17,9 @@
 
 use terminal_size::terminal_size;
 
-use hexyl::{Base, BorderStyle, CharacterTable, Endianness, Input, 
PrinterBuilder};
+use hexyl::{
+    Base, BorderStyle, CharacterTable, ColorScheme, Endianness, IncludeMode, 
Input, PrinterBuilder,
+};
 
 use hexyl::{
     COLOR_ASCII_OTHER, COLOR_ASCII_PRINTABLE, COLOR_ASCII_WHITESPACE, 
COLOR_NONASCII, COLOR_NULL,
@@ -51,8 +56,14 @@
                                         the right.
 Cannot be used with other width-setting options.";
 
+const STYLES: Styles = Styles::styled()
+    .header(AnsiColor::Green.on_default().effects(Effects::BOLD))
+    .usage(AnsiColor::Green.on_default().effects(Effects::BOLD))
+    .literal(AnsiColor::Cyan.on_default().effects(Effects::BOLD))
+    .placeholder(AnsiColor::Cyan.on_default());
+
 #[derive(Debug, Parser)]
-#[command(version, about, max_term_width(90))]
+#[command(version, about, max_term_width(90), styles = STYLES)]
 struct Opt {
     /// The file to display. If no FILE argument is given, read from STDIN.
     #[arg(value_name("FILE"))]
@@ -129,6 +140,10 @@
     #[arg(long, value_enum, default_value_t, value_name("FORMAT"))]
     character_table: CharacterTable,
 
+    /// Defines the color scheme for the characters.
+    #[arg(long, value_enum, default_value_t, value_name("FORMAT"))]
+    color_scheme: ColorScheme,
+
     /// Whether to display the position panel on the left.
     #[arg(short('P'), long)]
     no_position: bool,
@@ -188,6 +203,20 @@
     /// Print a table showing how different types of bytes are colored.
     #[arg(long)]
     print_color_table: bool,
+
+    /// Output in C include file style (similar to xxd -i).
+    #[arg(
+        short('i'),
+        long("include"),
+        help = "Output in C include file style",
+        conflicts_with("little_endian_format"),
+        conflicts_with("endianness")
+    )]
+    include_mode: bool,
+
+    /// Show shell completion for a certain shell
+    #[arg(long, value_name("SHELL"))]
+    completion: Option<Shell>,
 }
 
 #[derive(Clone, Debug, Default, ValueEnum)]
@@ -244,16 +273,27 @@
         return print_color_table().map_err(|e| anyhow!(e));
     }
 
+    if let Some(sh) = opt.completion {
+        let mut cmd = Opt::command();
+        let name = cmd.get_name().to_string();
+        generate(sh, &mut cmd, name, &mut io::stdout());
+        return Ok(());
+    }
+
     let stdin = io::stdin();
 
-    let mut reader = match opt.file {
+    let mut reader = match &opt.file {
         Some(filename) => {
-            if filename.is_dir() {
-                bail!("'{}' is a directory.", filename.to_string_lossy());
-            }
-            let file = File::open(&filename)?;
+            if filename.as_os_str() == "-" {
+                Input::Stdin(stdin.lock())
+            } else {
+                if filename.is_dir() {
+                    bail!("'{}' is a directory.", filename.to_string_lossy());
+                }
+                let file = File::open(filename)?;
 
-            Input::File(file)
+                Input::File(file)
+            }
         }
         None => Input::Stdin(stdin.lock()),
     };
@@ -362,7 +402,7 @@
         } else {
             ((8 / group_size) * (base_digits * group_size + 1)) + 2
         };
-        if (terminal_width - offset) / col_width < 1 {
+        if (terminal_width.saturating_sub(offset)) / col_width < 1 {
             1
         } else {
             (terminal_width - offset) / col_width
@@ -429,8 +469,35 @@
 
     let character_table = opt.character_table;
 
+    let color_scheme = opt.color_scheme;
+
     let mut stdout = BufWriter::new(io::stdout().lock());
 
+    let include_mode = match opt.include_mode {
+        // include mode on
+        true => {
+            if let Some(include_file) = opt.file {
+                // input from a file
+                if include_file.as_os_str() == "-" {
+                    IncludeMode::File("stdin".to_string())
+                } else {
+                    IncludeMode::File(
+                        include_file
+                            .file_name()
+                            .and_then(|n| n.to_str())
+                            .unwrap_or("file")
+                            .to_string(),
+                    )
+                }
+            } else {
+                // input from stdin
+                IncludeMode::Stdin
+            }
+        }
+        // include mode off
+        false => IncludeMode::Off,
+    };
+
     let mut printer = PrinterBuilder::new(&mut stdout)
         .show_color(show_color)
         .show_char_panel(show_char_panel)
@@ -442,6 +509,8 @@
         .with_base(base)
         .endianness(endianness)
         .character_table(character_table)
+        .include_mode(include_mode)
+        .color_scheme(color_scheme)
         .build();
     printer.display_offset(skip_offset + display_offset);
     printer.print_all(&mut reader).map_err(|e| anyhow!(e))?;
@@ -493,32 +562,32 @@
     writeln!(stdout, "hexyl color reference:\n")?;
 
     // NULL bytes
-    stdout.write_all(COLOR_NULL)?;
+    stdout.write_all(COLOR_NULL.as_bytes())?;
     writeln!(stdout, "⋄ NULL bytes (0x00)")?;
-    stdout.write_all(COLOR_RESET)?;
+    stdout.write_all(COLOR_RESET.as_bytes())?;
 
     // ASCII printable
-    stdout.write_all(COLOR_ASCII_PRINTABLE)?;
+    stdout.write_all(COLOR_ASCII_PRINTABLE.as_bytes())?;
     writeln!(stdout, "a ASCII printable characters (0x20 - 0x7E)")?;
-    stdout.write_all(COLOR_RESET)?;
+    stdout.write_all(COLOR_RESET.as_bytes())?;
 
     // ASCII whitespace
-    stdout.write_all(COLOR_ASCII_WHITESPACE)?;
+    stdout.write_all(COLOR_ASCII_WHITESPACE.as_bytes())?;
     writeln!(stdout, "_ ASCII whitespace (0x09 - 0x0D, 0x20)")?;
-    stdout.write_all(COLOR_RESET)?;
+    stdout.write_all(COLOR_RESET.as_bytes())?;
 
     // ASCII other
-    stdout.write_all(COLOR_ASCII_OTHER)?;
+    stdout.write_all(COLOR_ASCII_OTHER.as_bytes())?;
     writeln!(
         stdout,
         "• ASCII control characters (except NULL and whitespace)"
     )?;
-    stdout.write_all(COLOR_RESET)?;
+    stdout.write_all(COLOR_RESET.as_bytes())?;
 
     // Non-ASCII
-    stdout.write_all(COLOR_NONASCII)?;
+    stdout.write_all(COLOR_NONASCII.as_bytes())?;
     writeln!(stdout, "× Non-ASCII bytes (0x80 - 0xFF)")?;
-    stdout.write_all(COLOR_RESET)?;
+    stdout.write_all(COLOR_RESET.as_bytes())?;
 
     Ok(())
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hexyl-0.16.0/tests/integration_tests.rs 
new/hexyl-0.17.0/tests/integration_tests.rs
--- old/hexyl-0.16.0/tests/integration_tests.rs 2024-12-27 14:43:18.000000000 
+0100
+++ new/hexyl-0.17.0/tests/integration_tests.rs 2026-02-14 13:53:54.000000000 
+0100
@@ -1,7 +1,7 @@
 use assert_cmd::Command;
 
 fn hexyl() -> Command {
-    let mut cmd = Command::cargo_bin("hexyl").unwrap();
+    let mut cmd = Command::new(assert_cmd::cargo_bin!("hexyl"));
     cmd.current_dir("tests/examples");
     cmd
 }
@@ -785,3 +785,180 @@
             );
     }
 }
+
+mod colors {
+    use super::hexyl;
+    use owo_colors::{colors, Color};
+    use std::collections::HashMap;
+
+    // This is a helper for testing color in output. Writing tests to expect
+    // raw color codes is ugly and hard to look at. Loading expected output
+    // from files works fine, but you end up with a lot of files for all the
+    // tests, and you have to cross-reference the file with the test that uses
+    // it. The files also suffer from the same problem of being hard to
+    // visually inspect. Just catting the file to see the colorized output
+    // loses the nuance of where exactly the color codes appear (before or
+    // after spaces, for example), or whether there are redundant codes.
+    //
+    // So this ColorMap solves the problem neatly by having two inputs:
+    // - the easy to read expected output in plain format without any colors
+    // - a mapping with identical structure except some characters replaced
+    //   with single character color codes.
+    // This makes it easy to reference the output and expected colors side by
+    // side, and provides fairly precise control over exactly where color codes
+    // are expected (the only caveat being you can't have two color codes back
+    // to back). ColorMap combines these into the actual expected output.
+    //
+    // The color mapping needs to be identical to the expected output, except
+    // it has some chars replaced by color code stand ins. These are replaced
+    // with the actual color codes by the colorize method. The '.' character
+    // is also ignored (it doesn't need to match the input). This makes the
+    // color map more readable and avoids input characters from conflicting
+    // with color chars.
+    struct ColorMap {
+        text_map: &'static str,
+        char_to_color: HashMap<char, &'static str>,
+    }
+
+    impl ColorMap {
+        fn from(text_map: &'static str) -> Self {
+            ColorMap {
+                text_map,
+                char_to_color: HashMap::new(),
+            }
+        }
+
+        fn with<C: Color>(&mut self, c: char) -> &mut Self {
+            self.char_to_color.insert(c, C::ANSI_FG);
+            self
+        }
+
+        fn colorize(&self, input: &str) -> String {
+            let mut output = String::new();
+            let mut input_chars = input.chars();
+            for c in self.text_map.chars() {
+                let next_input = input_chars.next().expect("input and color 
map don't match");
+                if let Some(color) = self.char_to_color.get(&c) {
+                    output.push_str(color);
+                } else if c != '.' {
+                    // ignore '.' in the mapping for readability
+                    assert_eq!(c, next_input, "input and color map don't 
match");
+                }
+                output.push(next_input);
+            }
+            output
+        }
+    }
+
+    #[test]
+    fn hex_colors() {
+        let input = b"He\x11\0 \xff\0\xdd";
+        let expected_text = "\
+            
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
+            │00000000│ 48 65 11 00 20 ff 00 dd ┊                         │He•⋄ 
×⋄×┊        │\n\
+            
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n";
+        let expected = ColorMap::from(
+            "\
+            
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
+            │r.......d y. .. b. c. g. m. c. m.d┊                        
d│y.bcgmcmd        d\n\
+            
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
+        )
+        .with::<colors::Red>('r')
+        .with::<colors::Default>('d')
+        .with::<colors::Yellow>('y')
+        .with::<colors::Blue>('b')
+        .with::<colors::Green>('g')
+        .with::<colors::BrightMagenta>('m')
+        .with::<colors::CustomColor<0xab, 0xcd, 0xef>>('c')
+        .colorize(expected_text);
+
+        hexyl()
+            .write_stdin(input)
+            .arg("--color=always")
+            .env("HEXYL_COLOR_OFFSET", "red")
+            .env("HEXYL_COLOR_ASCII_PRINTABLE", "yellow")
+            .env("HEXYL_COLOR_ASCII_WHITESPACE", "green")
+            .env("HEXYL_COLOR_ASCII_OTHER", "blue")
+            .env("HEXYL_COLOR_NONASCII", "bright magenta")
+            .env("HEXYL_COLOR_NULL", "#abcdef")
+            .assert()
+            .success()
+            .stdout(expected);
+    }
+
+    #[test]
+    fn binary_colors() {
+        let input = b"He\x11\0 \xff\0\xdd";
+        let expected_text = "\
+            
┌────────┬─────────────────────────────────────────────────────────────────────────┬────────┐\n\
+            │00000000│ 01001000 01100101 00010001 00000000 00100000 11111111 
00000000 11011101 │He•⋄ ×⋄×│\n\
+            
└────────┴─────────────────────────────────────────────────────────────────────────┴────────┘\n";
+        let expected = ColorMap::from(
+            "\
+            
┌────────┬─────────────────────────────────────────────────────────────────────────┬────────┐\n\
+            │r.......d y....... ........ b....... c....... g....... m....... 
c....... m.......d│y.bcgmcmd\n\
+            
└────────┴─────────────────────────────────────────────────────────────────────────┴────────┘\n"
+        )
+        .with::<colors::Red>('r')
+        .with::<colors::Default>('d')
+        .with::<colors::Yellow>('y')
+        .with::<colors::Blue>('b')
+        .with::<colors::Green>('g')
+        .with::<colors::BrightMagenta>('m')
+        .with::<colors::CustomColor<0xab, 0xcd, 0xef>>('c')
+        .colorize(expected_text);
+
+        hexyl()
+            .write_stdin(input)
+            .arg("--color=always")
+            .arg("--panels=1")
+            .arg("--base=binary")
+            .env("HEXYL_COLOR_OFFSET", "red")
+            .env("HEXYL_COLOR_ASCII_PRINTABLE", "yellow")
+            .env("HEXYL_COLOR_ASCII_WHITESPACE", "green")
+            .env("HEXYL_COLOR_ASCII_OTHER", "blue")
+            .env("HEXYL_COLOR_NONASCII", "bright magenta")
+            .env("HEXYL_COLOR_NULL", "#abcdef")
+            .assert()
+            .success()
+            .stdout(expected);
+    }
+
+    #[test]
+    fn groupsize_colors() {
+        let input = b"He\x11\0 \xff\0\xdd";
+        let expected_text = "\
+            ┌────────┬─────────────────────┬────────┐\n\
+            │00000000│ 4865 1100 20ff 00dd │He•⋄ ×⋄×│\n\
+            └────────┴─────────────────────┴────────┘\n";
+        let expected = ColorMap::from(
+            "\
+            ┌────────┬─────────────────────┬────────┐\n\
+            │r.......d y... b.c. g.m. c.m.d│y.bcgmcmd\n\
+            └────────┴─────────────────────┴────────┘\n",
+        )
+        .with::<colors::Red>('r')
+        .with::<colors::Default>('d')
+        .with::<colors::Yellow>('y')
+        .with::<colors::Blue>('b')
+        .with::<colors::Green>('g')
+        .with::<colors::BrightMagenta>('m')
+        .with::<colors::CustomColor<0xab, 0xcd, 0xef>>('c')
+        .colorize(expected_text);
+
+        hexyl()
+            .write_stdin(input)
+            .arg("--color=always")
+            .arg("--panels=1")
+            .arg("--groupsize=2")
+            .env("HEXYL_COLOR_OFFSET", "red")
+            .env("HEXYL_COLOR_ASCII_PRINTABLE", "yellow")
+            .env("HEXYL_COLOR_ASCII_WHITESPACE", "green")
+            .env("HEXYL_COLOR_ASCII_OTHER", "blue")
+            .env("HEXYL_COLOR_NONASCII", "bright magenta")
+            .env("HEXYL_COLOR_NULL", "#abcdef")
+            .assert()
+            .success()
+            .stdout(expected);
+    }
+}

++++++ vendor.tar.xz ++++++
/work/SRC/openSUSE:Factory/hexyl/vendor.tar.xz 
/work/SRC/openSUSE:Factory/.hexyl.new.1977/vendor.tar.xz differ: char 15, line 1

Reply via email to