hexyl-0.16.0/.cargo_vcs_info.json0000644000000001360000000000100122510ustar { "git": { "sha1": "abc20a380c8c2d9d76c1976222725d3211cef809" }, "path_in_vcs": "" }hexyl-0.16.0/.github/workflows/CICD.yml000064400000000000000000000320511046102023000156640ustar 00000000000000name: CICD env: CICD_INTERMEDIATES_DIR: "_cicd-intermediates" MSRV_FEATURES: "" on: workflow_dispatch: pull_request: push: branches: - master tags: - '*' jobs: crate_metadata: name: Extract crate metadata runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Extract crate information id: crate_metadata run: | cargo metadata --no-deps --format-version 1 | jq -r '"name=" + .packages[0].name' | tee -a $GITHUB_OUTPUT cargo metadata --no-deps --format-version 1 | jq -r '"version=" + .packages[0].version' | tee -a $GITHUB_OUTPUT cargo metadata --no-deps --format-version 1 | jq -r '"maintainer=" + .packages[0].authors[0]' | tee -a $GITHUB_OUTPUT cargo metadata --no-deps --format-version 1 | jq -r '"homepage=" + .packages[0].homepage' | tee -a $GITHUB_OUTPUT cargo metadata --no-deps --format-version 1 | jq -r '"msrv=" + .packages[0].rust_version' | tee -a $GITHUB_OUTPUT outputs: name: ${{ steps.crate_metadata.outputs.name }} version: ${{ steps.crate_metadata.outputs.version }} maintainer: ${{ steps.crate_metadata.outputs.maintainer }} homepage: ${{ steps.crate_metadata.outputs.homepage }} msrv: ${{ steps.crate_metadata.outputs.msrv }} ensure_cargo_fmt: name: Ensure 'cargo fmt' has been run runs-on: ubuntu-20.04 steps: - uses: dtolnay/rust-toolchain@stable with: components: rustfmt - uses: actions/checkout@v3 - run: cargo fmt -- --check min_version: name: Minimum supported rust version runs-on: ubuntu-20.04 needs: crate_metadata steps: - name: Checkout source code uses: actions/checkout@v3 - name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }}) uses: dtolnay/rust-toolchain@master with: toolchain: ${{ needs.crate_metadata.outputs.msrv }} components: clippy - name: Run clippy (on minimum supported rust version to prevent warnings we can't fix) run: cargo clippy --locked --all-targets ${{ env.MSRV_FEATURES }} - name: Run tests run: cargo test --locked ${{ env.MSRV_FEATURES }} build: name: ${{ matrix.job.target }} (${{ matrix.job.os }}) runs-on: ${{ matrix.job.os }} needs: crate_metadata strategy: 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-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 } env: BUILD_CMD: cargo steps: - name: Checkout source code uses: actions/checkout@v3 - name: Install prerequisites shell: bash run: | case ${{ matrix.job.target }} in arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; esac - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.job.target }} - name: Install cross if: matrix.job.use-cross uses: taiki-e/install-action@v2 with: tool: cross - name: Overwrite build command env variable if: matrix.job.use-cross shell: bash run: echo "BUILD_CMD=cross" >> $GITHUB_ENV - name: Show version information (Rust, cargo, GCC) shell: bash run: | gcc --version || true rustup -V rustup toolchain list rustup default cargo -V rustc -V - name: Build shell: bash run: $BUILD_CMD build --locked --release --target=${{ matrix.job.target }} - name: Run example if: ${{ !matrix.job.use-cross }} shell: bash run: $BUILD_CMD run --release --target=${{ matrix.job.target }} --example=simple - name: Set binary name & path id: bin shell: bash run: | # Figure out suffix of binary EXE_suffix="" case ${{ matrix.job.target }} in *-pc-windows-*) EXE_suffix=".exe" ;; esac; # Setup paths BIN_NAME="${{ needs.crate_metadata.outputs.name }}${EXE_suffix}" BIN_PATH="target/${{ matrix.job.target }}/release/${BIN_NAME}" # Let subsequent steps know where to find the binary echo "BIN_PATH=${BIN_PATH}" >> $GITHUB_OUTPUT echo "BIN_NAME=${BIN_NAME}" >> $GITHUB_OUTPUT - name: Set testing options id: test-options shell: bash 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; echo "CARGO_TEST_OPTIONS=${CARGO_TEST_OPTIONS}" >> $GITHUB_OUTPUT - name: Run tests shell: bash run: $BUILD_CMD test --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}} - name: Setup Pandoc uses: r-lib/actions/setup-pandoc@v2 - name: Generate man page run: pandoc -s -f markdown -t man -o "doc/${{ needs.crate_metadata.outputs.name }}.1" "doc/${{ needs.crate_metadata.outputs.name }}.1.md" - name: Create tarball id: package shell: bash run: | PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac; PKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-v${{ needs.crate_metadata.outputs.version }}-${{ matrix.job.target }} PKG_NAME=${PKG_BASENAME}${PKG_suffix} echo "PKG_NAME=${PKG_NAME}" >> $GITHUB_OUTPUT PKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/package" ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/" mkdir -p "${ARCHIVE_DIR}" # Binary cp "${{ steps.bin.outputs.BIN_PATH }}" "$ARCHIVE_DIR" # README, LICENSE and CHANGELOG files cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR" # Man page cp "doc/${{ needs.crate_metadata.outputs.name }}.1" "$ARCHIVE_DIR" # base compressed package pushd "${PKG_STAGING}/" >/dev/null case ${{ matrix.job.target }} in *-pc-windows-*) 7z -y a "${PKG_NAME}" "${PKG_BASENAME}"/* | tail -2 ;; *) tar czf "${PKG_NAME}" "${PKG_BASENAME}"/* ;; esac; popd >/dev/null # Let subsequent steps know where to find the compressed package echo "PKG_PATH=${PKG_STAGING}/${PKG_NAME}" >> $GITHUB_OUTPUT - name: Create Debian package id: debian-package shell: bash if: startsWith(matrix.job.os, 'ubuntu') run: | COPYRIGHT_YEARS="2018 - "$(date "+%Y") DPKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/debian-package" DPKG_DIR="${DPKG_STAGING}/dpkg" mkdir -p "${DPKG_DIR}" DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }} DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }}-musl case ${{ matrix.job.target }} in *-musl*) DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-musl ; DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }} ;; esac; DPKG_VERSION=${{ needs.crate_metadata.outputs.version }} unset DPKG_ARCH case ${{ matrix.job.target }} in aarch64-*-linux-*) DPKG_ARCH=arm64 ;; arm-*-linux-*hf) DPKG_ARCH=armhf ;; i686-*-linux-*) DPKG_ARCH=i686 ;; x86_64-*-linux-*) DPKG_ARCH=amd64 ;; *) DPKG_ARCH=notset ;; esac; DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb" echo "DPKG_NAME=${DPKG_NAME}" >> $GITHUB_OUTPUT # Binary install -Dm755 "${{ steps.bin.outputs.BIN_PATH }}" "${DPKG_DIR}/usr/bin/${{ steps.bin.outputs.BIN_NAME }}" # Man page install -Dm644 'doc/${{ needs.crate_metadata.outputs.name }}.1' "${DPKG_DIR}/usr/share/man/man1/${{ needs.crate_metadata.outputs.name }}.1" gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ needs.crate_metadata.outputs.name }}.1" # README and LICENSE install -Dm644 "README.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/README.md" install -Dm644 "LICENSE-MIT" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-MIT" install -Dm644 "LICENSE-APACHE" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-APACHE" install -Dm644 "CHANGELOG.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/changelog" gzip -n --best "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/changelog" cat > "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/copyright" < "${DPKG_DIR}/DEBIAN/control" <> $GITHUB_OUTPUT # build dpkg fakeroot dpkg-deb --build "${DPKG_DIR}" "${DPKG_PATH}" - name: "Artifact upload: tarball" uses: actions/upload-artifact@main with: name: ${{ steps.package.outputs.PKG_NAME }} path: ${{ steps.package.outputs.PKG_PATH }} - name: "Artifact upload: Debian package" uses: actions/upload-artifact@main if: steps.debian-package.outputs.DPKG_NAME with: name: ${{ steps.debian-package.outputs.DPKG_NAME }} path: ${{ steps.debian-package.outputs.DPKG_PATH }} - name: Check for release id: is-release shell: bash run: | unset IS_RELEASE ; if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true' ; fi echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT - name: Publish archives and packages uses: softprops/action-gh-release@v1 if: steps.is-release.outputs.IS_RELEASE with: files: | ${{ steps.package.outputs.PKG_PATH }} ${{ steps.debian-package.outputs.DPKG_PATH }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} hexyl-0.16.0/.gitignore000064400000000000000000000000561046102023000130320ustar 00000000000000/target **/*.rs.bk # Generated files hexyl.1 hexyl-0.16.0/CHANGELOG.md000064400000000000000000000166571046102023000126710ustar 00000000000000# unreleased ## Features ## Bugfixes # v0.16.0 ## Features * New `--print-color-table` option, see #229 (@sahinfalcon) ## Bugfixes - Throw an error when try to view a directory, see #234 (@Integral-Tech) # v0.15.0 ## Features - Add codepage 1047 for EBCDIC, see #226 (@v1gnesh) ## Other - Rewrite CLI using the derive API, see #225 (@sorairolake) # v0.14.0 ## Features * New `--character-table` option, with the ability to use [codepage 437](https://www.azabani.com/2020/11/15/xd.html), see #194 and #195 (@sharifhsn) * New `--character-table=ascii` option for a ASCII-only character table, see #212 and #36 (@sharkdp) ## Bugfixes * Show output when doing `hexyl /dev/zero`, see #211 (@sharifhsn) * Respect NO_COLOR environment variable, see #210 (@sharkdp) # v0.13.1 ## Bugfixes - Correctly handle discontinuous input (stdin), see #196 and #197 (@sharifhsn) # v0.13.0 ## Features - Support both little and big Endian dumps using `--endianness={little,big}`, see #189 and #104 (@RinHizakura) ## Changes - **Breaking**: Changed the meaning of the short flag `-C` to be consistent with `hexdump -C`. Previously, this would *hide* the character panel, but now `-C` *shows* the character panel, in case it has been previously (e.g. in an `alias`) disabled with `--no-characters`, see #187 (@sharkdp) ## `hexyl` as a library - New `endianness` method for `PrinterBuilder` # v0.12.0 ## Features - Only show one panel by default if the terminal width is not wide enough for two panels, see #182 (@sharkdp) - Respect the `NO_COLOR` environment variable, see #179 (@sharifhsn) ## Bugfixes - Do not fail with an error if `--panels=auto` is used and the output is piped, see #184 (@sharkdp) ## Changes - Breaking: For `xxd`-compatibility reasons, `--group_bytes` has been renamed to `--group-size` (with an `--groupsize` alias), see #121 (@sharkdp) ## `hexyl` as a library - Breaking: `num_group_bytes` has been renamed to `group_size`. # v0.11.0 ## Features - Significantly improved performance, see #173 and #176 (@sharifhsn) - Added variable panels through the `--panels` and `--terminal-width` flags, see [#13](https://github.com/sharkdp/hexyl/issues/13) and [#164](https://github.com/sharkdp/hexyl/pull/164) (@sharifhsn) - Added new `--group-bytes`/`-g` option, see #104 and #170 (@RinHizakura) - Added new `--base B` option (where `B` can be `binary`, `octal`, `decimal` or `hexadecimal`), see #147 and #178 (@sharifhsn) - Show actual zero bytes as `⋄` in the character panel (previously: `0`), in order not to confuse them with ASCII `0` bytes if colors are deactivated. Closes #166 (@sharkdp) ## `hexyl` as a library - Breaking change: `Printer::new` is deprecated as a part of the public API. Alternatively, you can now construct a `Printer` using the `PrinterBuilder` builder API, see [#168](https://github.com/sharkdp/hexyl/pull/168). (@sharifhsn) ## Other - More tests for the squeezing feature, see #177 (@mkatychev) ## Thank you Special thanks go to @sharifhsn, not just for the new features, bugfixes and performance improvements. But also for many internal improvements of the code base and other maintenance tasks. # v0.10.0 ## Features - Added new `--plain`, `--no-characters`, and `--no-position` flags, see #154 (@mkatychev) - Allow hex numbers and units for `--block-size` argument, see #111 and #144 (@merkrafter) ## Other - Added a man page, see #151 (@sorairolake) - Mention ability to specify length in hex, see #143 (@merkrafter) - `--length` and `--bytes` are now marked as conflicting command-line options, see #152 (@sorairolake) # v0.9.0 ## Changes - Breaking change (binary): setting the `-o/--display-offset` flag no longer overrides the value set by `--skip` [#115](https://github.com/sharkdp/hexyl/issues/115). The first displayed address is now the sum of the two values - this matches the behaviour of `xxd`. ## Features - Allow relative and negative byte offsets (e.g. `hexyl --skip=-1block`), see #99 (@ErichDonGubler) - Added `-l` as another alias for '-n/--length' (`xxd` compatibility), see #121 and #135 (@TheDoctor314) ## Bugfixes - Argument `--length` silently takes precedence over `--bytes`, see #105 - Print warning on empty content, see #107 and #108 - Disallow block sizes of zero, see #110 - Fix newline appearing in `--version` output, see #131 and #133 (@scimas) ## Other - Better diagnostic messages, see #98 (@ErichDonGubler) ## Packaging - `hexyl` is now available on snapstore, see #116 (@purveshpatel511) # v0.8.0 ## Features - A new `--skip ` / `-s ` option can be used to skip the first `N` bytes of the input, see #16, #88 (@Tarnadas, @MaxJohansen, @ErichDonGubler) - The `--length`/`--bytes`/`--skip`/`--display-offset` options can now take units for their value argument, for example: ``` bash hexyl /dev/random --length=1KiB hexyl $(which hexyl) --skip=1MiB --length=10KiB ``` Both decimal SI prefixes (kB, MB, …) as well as binary IEC prefixes (KiB, MiB, …) are supported. In addition, there is a new `--block-size ` option that can be used to control the size of the `block` unit: ``` bash hexyl /dev/random --block-size=4kB --length=2block ``` See: #44 (@ErichDonGubler and @aswild) ## Other - Various improvements throughout the code base by @ErichDonGubler ## Packaging - `hexyl` is now available on Void Linux, see #91 (@notramo) # v0.7.0 ## Bugfixes - hexyl can now be closed with `Ctrl-C` when reading input from STDIN, see #84 ## Changes - Breaking change (library): [`Printer::print_all`](https://docs.rs/hexyl/latest/hexyl/struct.Printer.html#method.print_all) does not take a second argument anymore. - Added an example on how to use `hexyl` as a library: https://github.com/sharkdp/hexyl/blob/v0.7.0/examples/simple.rs # v0.6.0 ## Features - `hexyl` can now be used as a library, see #67 (@tommilligan) - Added a new `-o`/`--display-offset` option to add a certain offset to the reported file positions, see #57 (@tommilligan) ## Bugfixes - Remove additional space on short input, see #69 (@nalshihabi) ## Other - Performance improvements, see #73 and #66 # v0.5.1 ## Bugfixes - A bug in the squeezing logic caused a wrong hexdump, see #62 (@awidegreen) - Some colors are printed even if they're disabled, see #64 (@awidegreen) - Fixed build failure on OpenBSD 6.5, see #61 # v0.5.0 ## Features - Added support for squeezing where reoccurring lines are squashed together and visualized with an asterisk. A new `-v`/`--no-squeezing` option can be used to disable the feature. For details, see #59 (@awidegreen) - Added a new `--border` option with support for various styles (Unicode, ASCII, None), see #54 (@dmke) - The `--length`/`-n` argument can be passed as a hexadecimal number (`hexyl -n 0xff /dev/urandom`), see #45 (@Qyriad) - Added `--bytes`/`-c` as an alias for `--length`/`-n`, see #48 (@selfup) ## Changes - Print header immediately before the first line, see #51 (@mziter) # v0.4.0 ## Features - Added a new `--color=always/auto/never` option which can be used to control `hexyl`s color output, see #30 (@bennetthardwick) - Use 16 colors instead of 256, see #38 ## Changes - Various speed improvements, see #33 (@kballard) ## Bugfixes - Proper Ctrl-C handling, see #35 - Proper handling of broken pipes (`hexyl … | head`) # v0.3.1 - Various (huge) performance improvements, see #23 and #24 (@kballard) - Replaced 24-bit truecolor ANSI codes by 8-bit codes to support more terminal emulators, fixes #9 # v0.3.0 Windows support # v0.2.0 Initial release hexyl-0.16.0/CONTRIBUTING.md000064400000000000000000000017311046102023000132740ustar 00000000000000# Contributing Thank you for considering to contribute to `hexyl`! ## Add an entry to the changelog If your contribution changes the behavior of `hexyl` (as opposed to a typo-fix in the documentation), please update the [`CHANGELOG.md`](CHANGELOG.md) file and describe your changes. This makes the release process much easier and therefore helps to get your changes into a new `hexyl` release faster. The top of the `CHANGELOG` contains a *"unreleased"* section with a few subsections (Features, Bugfixes, …). Please add your entry to the subsection that best describes your change Entries follow this format: ``` - Short description of what has been changed, see #123 (@user) ``` Here, `#123` is the number of the original issue and/or your pull request. Please replace `@user` by your GitHub username. ## Adding a new feature Please consider opening a [ticket](https://github.com/sharkdp/hexyl/issues/new) first in order to give us a chance to discuss the feature first. hexyl-0.16.0/Cargo.lock0000644000000362550000000000100102370ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" [[package]] name = "anstyle-parse" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", "windows-sys 0.59.0", ] [[package]] name = "anyhow" version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" [[package]] name = "assert_cmd" version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" dependencies = [ "anstyle", "bstr", "doc-comment", "libc", "predicates", "predicates-core", "predicates-tree", "wait-timeout", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bstr" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", "regex-automata", "serde", ] [[package]] name = "clap" version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap_builder" version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", "terminal_size", ] [[package]] name = "clap_derive" version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "const_format" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50c655d81ff1114fb0dcdea9225ea9f0cc712a6f8d189378e82bdf62a473a64b" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "difflib" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "float-cmp" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ "num-traits", ] [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hexyl" version = "0.16.0" dependencies = [ "anyhow", "assert_cmd", "clap", "const_format", "libc", "owo-colors", "predicates", "pretty_assertions", "supports-color", "terminal_size", "thiserror", ] [[package]] name = "is_ci" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "libc" version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "owo-colors" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" [[package]] name = "predicates" version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" dependencies = [ "anstyle", "difflib", "float-cmp", "normalize-line-endings", "predicates-core", "regex", ] [[package]] name = "predicates-core" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" [[package]] name = "predicates-tree" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" dependencies = [ "predicates-core", "termtree", ] [[package]] name = "pretty_assertions" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", ] [[package]] name = "proc-macro2" version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "serde" version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "supports-color" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77" dependencies = [ "is_ci", ] [[package]] name = "syn" version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "terminal_size" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" dependencies = [ "rustix", "windows-sys 0.59.0", ] [[package]] name = "termtree" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "wait-timeout" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ "libc", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" hexyl-0.16.0/Cargo.toml0000644000000032670000000000100102570ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.74" name = "hexyl" version = "0.16.0" authors = ["David Peter "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "A command-line hex viewer" homepage = "https://github.com/sharkdp/hexyl" readme = "README.md" categories = ["command-line-utilities"] license = "MIT/Apache-2.0" repository = "https://github.com/sharkdp/hexyl" [profile.release] lto = true codegen-units = 1 [lib] name = "hexyl" path = "src/lib.rs" [[bin]] name = "hexyl" path = "src/main.rs" [[example]] name = "simple" path = "examples/simple.rs" [[test]] name = "integration_tests" path = "tests/integration_tests.rs" [dependencies.anyhow] version = "1.0" [dependencies.clap] version = "4" features = [ "derive", "wrap_help", ] [dependencies.const_format] version = "0.2" [dependencies.libc] version = "0.2" [dependencies.owo-colors] version = "4" [dependencies.supports-color] version = "3" [dependencies.terminal_size] version = "0.4" [dependencies.thiserror] version = "1.0" [dev-dependencies.assert_cmd] version = "2.0" [dev-dependencies.predicates] version = "3.0" [dev-dependencies.pretty_assertions] version = "1.4.0" hexyl-0.16.0/Cargo.toml.orig000064400000000000000000000012761046102023000137360ustar 00000000000000[package] authors = ["David Peter "] categories = ["command-line-utilities"] 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" edition = "2021" rust-version = "1.74" [dependencies] anyhow = "1.0" const_format = "0.2" libc = "0.2" owo-colors = "4" supports-color = "3" thiserror = "1.0" terminal_size = "0.4" [dependencies.clap] version = "4" features = ["derive", "wrap_help"] [dev-dependencies] assert_cmd = "2.0" predicates = "3.0" pretty_assertions = "1.4.0" [profile.release] lto = true codegen-units = 1 hexyl-0.16.0/LICENSE-APACHE000064400000000000000000000261351046102023000127740ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. hexyl-0.16.0/LICENSE-MIT000064400000000000000000000017771046102023000125110ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hexyl-0.16.0/README.md000064400000000000000000000100551046102023000123210ustar 00000000000000![](doc/logo.svg) [![CICD](https://github.com/sharkdp/hexyl/actions/workflows/CICD.yml/badge.svg)](https://github.com/sharkdp/hexyl/actions/workflows/CICD.yml) [![](https://img.shields.io/crates/l/hexyl.svg?colorB=22ba4c)](https://crates.io/crates/hexyl) ![](https://img.shields.io/crates/v/hexyl.svg?colorB=00aa88) `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). ## Preview ![](https://i.imgur.com/MWO9uSL.png) ![](https://i.imgur.com/Dp7Wncz.png) ![](https://i.imgur.com/ln3TniI.png) ![](https://i.imgur.com/f8nm8g6.png) ## Installation ### On Ubuntu *... and other Debian-based Linux distributions.* If you run Ubuntu 19.10 (Eoan Ermine) or newer, you can install the [officially maintained package](https://packages.ubuntu.com/search?keywords=hexyl): ```bash sudo apt install hexyl ``` If you use an older version of Ubuntu, you can download the latest `.deb` package from the release page and install it via: ``` bash sudo dpkg -i hexyl_0.15.0_amd64.deb # adapt version number and architecture ``` ### On Debian If you run Debian Buster or newer, you can install the [officially maintained Debian package](https://packages.debian.org/search?searchon=names&keywords=hexyl): ```bash sudo apt-get install hexyl ``` If you run an older version of Debian, see above for instructions on how to manually install `hexyl`. ### On Fedora If you run Fedora 35 or newer, you can install the [officially maintained Fedora package](https://packages.fedoraproject.org/pkgs/rust-hexyl/hexyl): ```bash sudo dnf install hexyl ``` ### On Arch Linux You can install `hexyl` from [the official package repository](https://archlinux.org/packages/extra/x86_64/hexyl/): ``` pacman -S hexyl ``` ### On Void Linux ``` xbps-install hexyl ``` ### On Gentoo Linux Available in [dm9pZCAq overlay](https://github.com/gentoo-mirror/dm9pZCAq) ``` sudo eselect repository enable dm9pZCAq sudo emerge --sync dm9pZCAq sudo emerge sys-apps/hexyl::dm9pZCAq ``` ### On macOS Via [Homebrew](https://brew.sh): ``` brew install hexyl ``` ...or via [MacPorts](https://www.macports.org): ``` sudo port install hexyl ``` ### On FreeBSD ``` pkg install hexyl ``` ### On NetBSD ``` pkgin install hexyl ``` ### On OpenBSD ``` doas pkg_add hexyl ``` ### on Termux ``` pkg install hexyl ``` or ``` apt install hexyl ``` ### Via Nix ``` nix-env -i hexyl ``` ### Via Guix ``` guix package -i hexyl ``` Or add the `hexyl` package in the list of packages to be installed in your system configuration (e.g., `/etc/config.scm`). ### On other distributions Check out the [release page](https://github.com/sharkdp/hexyl/releases) for binary builds. ### On Windows Check out the [release page](https://github.com/sharkdp/hexyl/releases) for binary builds. Alternatively, install from source via `cargo`, `snap` or `scoop` (see below). Make sure that you use a terminal that supports ANSI escape sequences (like ConHost v2 since Windows 10 1703 or Windows Terminal since Windows 10 1903). ### Via cargo If you have Rust 1.56 or higher, you can install `hexyl` from source via `cargo`: ``` cargo install hexyl ``` Alternatively, you can install `hexyl` directly from the repository by using: ``` git clone https://github.com/sharkdp/hexyl cargo install --path ./hexyl ``` Note: To convert the man page, you will need [Pandoc](https://pandoc.org/). You can convert from Markdown by using (in the project root): ``` pandoc -s -f markdown -t man -o ./doc/hexyl.1 ./doc/hexyl.1.md ``` ### Via snap package ``` sudo snap install hexyl ``` [Get it from the Snap Store](https://snapcraft.io/hexyl) ### Via [Scoop](https://scoop.sh) ``` scoop install hexyl ``` ### Via [X-CMD](https://x-cmd.com) ``` x env use hexyl ``` ## License Licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) at your option. hexyl-0.16.0/doc/hexyl.1.md000064400000000000000000000065171046102023000134310ustar 00000000000000% HEXYL(1) hexyl 0.12.0 | General Commands Manual % % 2022-12-05 # NAME hexyl - a command-line hex viewer # SYNOPSIS **hexyl** [_OPTIONS_] [_FILE_] # DESCRIPTION **hexyl** is a simple 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). # POSITIONAL ARGUMENTS _FILE_ : The file to display. If no _FILE_ argument is given, read from STDIN. # OPTIONS **-n**, **\--length** _N_ : Only read _N_ bytes from the input. The _N_ argument can also include a unit with a decimal prefix (kB, MB, ..) or binary prefix (kiB, MiB, ..), or can be specified using a hex number. Examples: : Read the first 64 bytes: : $ **hexyl \--length=64** Read the first 4 kibibytes: : $ **hexyl \--length=4KiB** Read the first 255 bytes (specified using a hex number): : $ **hexyl \--length=0xff** **-c**, **\--bytes** _N_ : An alias for **-n**/**\--length**. **-l** _N_ : Yet another alias for **-n**/**\--length**. **-s**, **\--skip** _N_ : Skip the first _N_ bytes of the input. The _N_ argument can also include a unit (see **\--length** for details). A negative value is valid and will seek from the end of the file. **\--block-size** _SIZE_ : Sets the size of the block unit to _SIZE_ (default is 512). Examples: : Sets the block size to 1024 bytes: : $ **hexyl \--block-size=1024 \--length=5block** Sets the block size to 4 kilobytes: : $ **hexyl \--block-size=4kB \--length=2block** **-v**, **\--no-squeezing** : Displays all input data. Otherwise any number of groups of output lines which would be identical to the preceding group of lines, are replaced with a line comprised of a single asterisk. **\--color** _WHEN_ : When to use colors. The auto-mode only displays colors if the output goes to an interactive terminal. Possible values: : - **always** (default) - **auto** - **never** **\--border** _STYLE_ : Whether to draw a border with Unicode characters, ASCII characters, or none at all. Possible values: : - **unicode** (default) - **ascii** - **none** **-o**, **\--display-offset** _N_ : Add _N_ bytes to the displayed file position. The _N_ argument can also include a unit (see **\--length** for details). A negative value is valid and calculates an offset relative to the end of the file. **-h**, **\--help** : Prints help information. **-V**, **\--version** : Prints version information. # NOTES Source repository: : # EXAMPLES Print a given file: : $ **hexyl small.png** Print and view a given file in the terminal pager: : $ **hexyl big.png | less -r** Print the first 256 bytes of a given special file: : $ **hexyl -n 256 /dev/urandom** # AUTHORS **hexyl** was written by David Peter . # REPORTING BUGS Bugs can be reported on GitHub at: : # COPYRIGHT **hexyl** is dual-licensed under: : - Apache License 2.0 () - MIT License () # SEE ALSO **hexdump**(1), **xxd**(1) hexyl-0.16.0/doc/logo.svg000064400000000000000000000267651046102023000133070ustar 00000000000000 image/svg+xml hexyl-0.16.0/examples/simple.rs000064400000000000000000000012701046102023000145160ustar 00000000000000use std::io; use hexyl::{BorderStyle, PrinterBuilder}; fn main() { let input = vec![ 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, ]; let stdout = io::stdout(); let mut handle = stdout.lock(); let mut printer = PrinterBuilder::new(&mut handle) .show_color(true) .show_char_panel(true) .show_position_panel(true) .with_border_style(BorderStyle::Unicode) .enable_squeezing(false) .num_panels(2) .group_size(1) .build(); printer.print_all(&input[..]).unwrap(); } hexyl-0.16.0/src/colors.rs000064400000000000000000000114301046102023000134760ustar 00000000000000use owo_colors::{colors, Color}; 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(); #[rustfmt::skip] pub const CP437: [char; 256] = [ // Copyright (c) 2016, Delan Azabani // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // modified to use the ⋄ character instead of ␀ // use https://en.wikipedia.org/w/index.php?title=Code_page_437&oldid=978947122 // not ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/PC/CP437.TXT // because we want the graphic versions of 01h–1Fh + 7Fh '⋄','☺','☻','♥','♦','♣','♠','•','◘','○','◙','♂','♀','♪','♫','☼', '►','◄','↕','‼','¶','§','▬','↨','↑','↓','→','←','∟','↔','▲','▼', ' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/', '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?', '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', 'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_', '`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', 'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~','⌂', 'Ç','ü','é','â','ä','à','å','ç','ê','ë','è','ï','î','ì','Ä','Å', 'É','æ','Æ','ô','ö','ò','û','ù','ÿ','Ö','Ü','¢','£','¥','₧','ƒ', 'á','í','ó','ú','ñ','Ñ','ª','º','¿','⌐','¬','½','¼','¡','«','»', '░','▒','▓','│','┤','╡','╢','╖','╕','╣','║','╗','╝','╜','╛','┐', '└','┴','┬','├','─','┼','╞','╟','╚','╔','╩','╦','╠','═','╬','╧', '╨','╤','╥','╙','╘','╒','╓','╫','╪','┘','┌','█','▄','▌','▐','▀', 'α','ß','Γ','π','Σ','σ','µ','τ','Φ','Θ','Ω','δ','∞','φ','ε','∩', '≡','±','≥','≤','⌠','⌡','÷','≈','°','∙','·','√','ⁿ','²','■','ff', ]; #[rustfmt::skip] pub const CP1047: [char; 256] = [ // // Copyright (c) 2016,2024 IBM Corporation and other Contributors. // // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // which accompanies this distribution, and is available at // http://www.eclipse.org/legal/epl-v10.html // // Contributors: // Mark Taylor - Initial Contribution // // ref1 https://github.com/ibm-messaging/mq-smf-csv/blob/master/src/smfConv.c // ref2 https://web.archive.org/web/20150607033635/http://www-01.ibm.com/software/globalization/cp/cp01047.html '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', '.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.', ' ','.','.','.','.','.','.','.','.','.','$','.','<','(','+','|', '&','.','.','.','.','.','.','.','.','.','!','$','*',')',';','.', '-','/','.','.','.','.','.','.','.','.','.',',','%','_','>','?', '.','.','.','.','.','.','.','.','.','.',':','#','@','\'','=','.', '.','a','b','c','d','e','f','g','h','i','.','{','.','(','+','.', '.','j','k','l','m','n','o','p','q','r','.','}','.',')','.','.', '.','~','s','t','u','v','w','x','y','z','.','.','.','.','.','.', '.','.','.','.','.','.','.','.','.','.','[',']','.','.','.','-', '{','A','B','C','D','E','F','G','H','I','.','.','.','.','.','.', '}','J','K','L','M','N','O','P','Q','R','.','.','.','.','.','.', '.','.','S','T','U','V','W','X','Y','Z','.','.','.','.','.','.', '0','1','2','3','4','5','6','7','8','9','.','.','.','.','.','.' ]; hexyl-0.16.0/src/input.rs000064400000000000000000000036231046102023000133410ustar 00000000000000use std::fs; use std::io::{self, copy, sink, Read, Seek, SeekFrom}; pub enum Input<'a> { File(fs::File), Stdin(io::StdinLock<'a>), } impl<'a> Read for Input<'a> { fn read(&mut self, buf: &mut [u8]) -> io::Result { match *self { Input::File(ref mut file) => file.read(buf), Input::Stdin(ref mut stdin) => stdin.read(buf), } } } impl<'a> Seek for Input<'a> { fn seek(&mut self, pos: SeekFrom) -> io::Result { fn try_skip(reader: R, pos: SeekFrom, err_desc: &'static str) -> io::Result where R: Read, { let cant_seek_abs_err = || Err(io::Error::new(io::ErrorKind::Other, err_desc)); let offset = match pos { SeekFrom::Current(o) => u64::try_from(o).or_else(|_e| cant_seek_abs_err())?, SeekFrom::Start(_) | SeekFrom::End(_) => cant_seek_abs_err()?, }; copy(&mut reader.take(offset), &mut sink()) } match *self { Input::File(ref mut file) => { let seek_res = file.seek(pos); if let Err(Some(libc::ESPIPE)) = seek_res.as_ref().map_err(|err| err.raw_os_error()) { try_skip( file, pos, "Pipes only support seeking forward with a relative offset", ) } else { seek_res } } Input::Stdin(ref mut stdin) => try_skip( stdin, pos, "STDIN only supports seeking forward with a relative offset", ), } } } impl<'a> Input<'a> { pub fn into_inner(self) -> Box { match self { Input::File(file) => Box::new(file), Input::Stdin(stdin) => Box::new(stdin), } } } hexyl-0.16.0/src/lib.rs000064400000000000000000001016431046102023000127510ustar 00000000000000pub(crate) mod colors; pub(crate) mod input; pub use colors::*; pub use input::*; use std::io::{self, BufReader, Read, Write}; use clap::ValueEnum; pub enum Base { Binary, Octal, Decimal, Hexadecimal, } #[derive(Copy, Clone)] pub enum ByteCategory { Null, AsciiPrintable, AsciiWhitespace, AsciiOther, NonAscii, } #[derive(Copy, Clone, Debug, Default, ValueEnum)] #[non_exhaustive] pub enum CharacterTable { /// Show printable ASCII characters as-is, '⋄' for NULL bytes, ' ' for /// space, '_' for other ASCII whitespace, '•' for other ASCII characters, /// and '×' for non-ASCII bytes. #[default] Default, /// Show printable ASCII as-is, ' ' for space, '.' for everything else. Ascii, /// Show printable EBCDIC as-is, ' ' for space, '.' for everything else. #[value(name = "codepage-1047")] CP1047, /// Uses code page 437 (for non-ASCII bytes). #[value(name = "codepage-437")] CP437, } #[derive(Copy, Clone, Debug, Default, ValueEnum)] pub enum Endianness { /// Print out groups in little-endian format. Little, /// Print out groups in big-endian format. #[default] Big, } #[derive(PartialEq)] enum Squeezer { Print, Delete, Ignore, Disabled, } #[derive(Copy, Clone)] struct Byte(u8); impl Byte { fn category(self) -> ByteCategory { if self.0 == 0x00 { ByteCategory::Null } else if self.0.is_ascii_graphic() { ByteCategory::AsciiPrintable } else if self.0.is_ascii_whitespace() { ByteCategory::AsciiWhitespace } else if self.0.is_ascii() { ByteCategory::AsciiOther } else { ByteCategory::NonAscii } } fn color(self) -> &'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, } } fn as_char(self, character_table: CharacterTable) -> char { use crate::ByteCategory::*; match character_table { CharacterTable::Default => match self.category() { Null => '⋄', AsciiPrintable => self.0 as char, AsciiWhitespace if self.0 == 0x20 => ' ', AsciiWhitespace => '_', AsciiOther => '•', NonAscii => '×', }, CharacterTable::Ascii => match self.category() { Null => '.', AsciiPrintable => self.0 as char, AsciiWhitespace if self.0 == 0x20 => ' ', AsciiWhitespace => '.', AsciiOther => '.', NonAscii => '.', }, CharacterTable::CP1047 => CP1047[self.0 as usize], CharacterTable::CP437 => CP437[self.0 as usize], } } } struct BorderElements { left_corner: char, horizontal_line: char, column_separator: char, right_corner: char, } #[derive(Clone, Copy, Debug, Default, ValueEnum)] pub enum BorderStyle { /// Draw a border with Unicode characters. #[default] Unicode, /// Draw a border with ASCII characters. Ascii, /// Do not draw a border at all. None, } impl BorderStyle { fn header_elems(&self) -> Option { match self { BorderStyle::Unicode => Some(BorderElements { left_corner: '┌', horizontal_line: '─', column_separator: '┬', right_corner: '┐', }), BorderStyle::Ascii => Some(BorderElements { left_corner: '+', horizontal_line: '-', column_separator: '+', right_corner: '+', }), BorderStyle::None => None, } } fn footer_elems(&self) -> Option { match self { BorderStyle::Unicode => Some(BorderElements { left_corner: '└', horizontal_line: '─', column_separator: '┴', right_corner: '┘', }), BorderStyle::Ascii => Some(BorderElements { left_corner: '+', horizontal_line: '-', column_separator: '+', right_corner: '+', }), BorderStyle::None => None, } } fn outer_sep(&self) -> char { match self { BorderStyle::Unicode => '│', BorderStyle::Ascii => '|', BorderStyle::None => ' ', } } fn inner_sep(&self) -> char { match self { BorderStyle::Unicode => '┊', BorderStyle::Ascii => '|', BorderStyle::None => ' ', } } } pub struct PrinterBuilder<'a, Writer: Write> { 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, } impl<'a, Writer: Write> PrinterBuilder<'a, Writer> { pub fn new(writer: &'a mut Writer) -> Self { PrinterBuilder { writer, show_color: true, show_char_panel: true, show_position_panel: true, border_style: BorderStyle::Unicode, use_squeeze: true, panels: 2, group_size: 1, base: Base::Hexadecimal, endianness: Endianness::Big, character_table: CharacterTable::Default, } } pub fn show_color(mut self, show_color: bool) -> Self { self.show_color = show_color; self } pub fn show_char_panel(mut self, show_char_panel: bool) -> Self { self.show_char_panel = show_char_panel; self } pub fn show_position_panel(mut self, show_position_panel: bool) -> Self { self.show_position_panel = show_position_panel; self } pub fn with_border_style(mut self, border_style: BorderStyle) -> Self { self.border_style = border_style; self } pub fn enable_squeezing(mut self, enable: bool) -> Self { self.use_squeeze = enable; self } pub fn num_panels(mut self, num: u64) -> Self { self.panels = num; self } pub fn group_size(mut self, num: u8) -> Self { self.group_size = num; self } pub fn with_base(mut self, base: Base) -> Self { self.base = base; self } pub fn endianness(mut self, endianness: Endianness) -> Self { self.endianness = endianness; self } pub fn character_table(mut self, character_table: CharacterTable) -> Self { self.character_table = character_table; 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, ) } } pub struct Printer<'a, Writer: Write> { idx: u64, /// the buffer containing all the bytes in a line for character printing line_buf: Vec, writer: &'a mut Writer, show_char_panel: bool, show_position_panel: bool, show_color: bool, curr_color: Option<&'static [u8]>, border_style: BorderStyle, byte_hex_panel: Vec, byte_char_panel: Vec, // same as previous but in Fixed(242) gray color, for position panel byte_hex_panel_g: Vec, squeezer: Squeezer, display_offset: u64, /// The number of panels to draw. panels: u64, squeeze_byte: usize, /// The number of octets per group. group_size: u8, /// The number of digits used to write the base. base_digits: u8, /// Whether to show groups in little or big endian format. endianness: Endianness, } 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 } fn panel_sz(&self) -> usize { // add one to include the trailing space of a group let group_sz = self.base_digits as usize * self.group_size as usize + 1; let group_per_panel = 8 / self.group_size as usize; // add one to include the leading space 1 + group_sz * group_per_panel } fn write_border(&mut self, border_elements: BorderElements) -> io::Result<()> { let h = border_elements.horizontal_line; let c = border_elements.column_separator; let l = border_elements.left_corner; let r = border_elements.right_corner; let h8 = h.to_string().repeat(8); let h_repeat = h.to_string().repeat(self.panel_sz()); if self.show_position_panel { write!(self.writer, "{l}{h8}{c}")?; } else { write!(self.writer, "{l}")?; } for _ in 0..self.panels - 1 { write!(self.writer, "{h_repeat}{c}")?; } if self.show_char_panel { write!(self.writer, "{h_repeat}{c}")?; } else { write!(self.writer, "{h_repeat}")?; } if self.show_char_panel { for _ in 0..self.panels - 1 { write!(self.writer, "{h8}{c}")?; } writeln!(self.writer, "{h8}{r}")?; } else { writeln!(self.writer, "{r}")?; } Ok(()) } pub fn print_header(&mut self) -> io::Result<()> { if let Some(e) = self.border_style.header_elems() { self.write_border(e)? } Ok(()) } pub fn print_footer(&mut self) -> io::Result<()> { if let Some(e) = self.border_style.footer_elems() { self.write_border(e)? } Ok(()) } fn print_position_panel(&mut self) -> io::Result<()> { self.writer.write_all( self.border_style .outer_sep() .encode_utf8(&mut [0; 4]) .as_bytes(), )?; if self.show_color { self.writer.write_all(COLOR_OFFSET)?; } if self.show_position_panel { match self.squeezer { Squeezer::Print => { self.writer.write_all(&[b'*'])?; if self.show_color { self.writer.write_all(COLOR_RESET)?; } self.writer.write_all(b" ")?; } Squeezer::Ignore | Squeezer::Disabled | Squeezer::Delete => { let byte_index: [u8; 8] = (self.idx + self.display_offset).to_be_bytes(); let mut i = 0; while byte_index[i] == 0x0 && i < 4 { i += 1; } for &byte in byte_index.iter().skip(i) { self.writer .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( self.border_style .outer_sep() .encode_utf8(&mut [0; 4]) .as_bytes(), )?; } Ok(()) } fn print_char(&mut self, i: u64) -> io::Result<()> { match self.squeezer { 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()); } self.writer .write_all(self.byte_char_panel[b as usize].as_bytes())?; } else { self.squeezer = Squeezer::Print; } } } if i == 8 * self.panels - 1 { if self.show_color { self.writer.write_all(COLOR_RESET)?; self.curr_color = None; } self.writer.write_all( self.border_style .outer_sep() .encode_utf8(&mut [0; 4]) .as_bytes(), )?; } else if i % 8 == 7 { if self.show_color { self.writer.write_all(COLOR_RESET)?; self.curr_color = None; } self.writer.write_all( self.border_style .inner_sep() .encode_utf8(&mut [0; 4]) .as_bytes(), )?; } Ok(()) } pub fn print_char_panel(&mut self) -> io::Result<()> { for i in 0..self.line_buf.len() { self.print_char(i as u64)?; } Ok(()) } fn print_byte(&mut self, i: usize, b: u8) -> io::Result<()> { match self.squeezer { Squeezer::Print => { if !self.show_position_panel && i == 0 { if self.show_color { self.writer.write_all(COLOR_OFFSET)?; } self.writer .write_all(self.byte_char_panel[b'*' as usize].as_bytes())?; if self.show_color { self.writer.write_all(COLOR_RESET)?; } } else if i % (self.group_size as usize) == 0 { self.writer.write_all(b" ")?; } for _ in 0..self.base_digits { self.writer.write_all(b" ")?; } } Squeezer::Delete => self.writer.write_all(b" ")?, Squeezer::Ignore | Squeezer::Disabled => { if i % (self.group_size as usize) == 0 { 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()); } self.writer .write_all(self.byte_hex_panel[b as usize].as_bytes())?; } } // byte is last in panel if i % 8 == 7 { if self.show_color { self.curr_color = None; self.writer.write_all(COLOR_RESET)?; } self.writer.write_all(b" ")?; // byte is last in last panel if i as u64 % (8 * self.panels) == 8 * self.panels - 1 { self.writer.write_all( self.border_style .outer_sep() .encode_utf8(&mut [0; 4]) .as_bytes(), )?; } else { self.writer.write_all( self.border_style .inner_sep() .encode_utf8(&mut [0; 4]) .as_bytes(), )?; } } Ok(()) } fn reorder_buffer_to_little_endian(&self, buf: &mut Vec) { let n = buf.len(); let group_sz = self.group_size as usize; for idx in (0..n).step_by(group_sz) { let remaining = n - idx; let total = remaining.min(group_sz); buf[idx..idx + total].reverse(); } } pub fn print_bytes(&mut self) -> io::Result<()> { let mut buf = self.line_buf.clone(); if matches!(self.endianness, Endianness::Little) { self.reorder_buffer_to_little_endian(&mut buf); }; for (i, &b) in buf.iter().enumerate() { self.print_byte(i, b)?; } Ok(()) } /// Loop through the given `Reader`, printing until the `Reader` buffer /// is exhausted. pub fn print_all(&mut self, reader: Reader) -> io::Result<()> { let mut is_empty = true; let mut buf = BufReader::new(reader); let leftover = loop { // read a maximum of 8 * self.panels bytes from the reader if let Ok(n) = buf.read(&mut self.line_buf) { if n > 0 && n < 8 * self.panels as usize { // if less are read, that indicates end of file after if is_empty { self.print_header()?; is_empty = false; } let mut leftover = n; // loop until input is ceased if let Some(s) = loop { if let Ok(n) = buf.read(&mut self.line_buf[leftover..]) { leftover += n; // there is no more input being read if n == 0 { self.line_buf.resize(leftover, 0); break Some(leftover); } // amount read has exceeded line buffer if leftover >= 8 * self.panels as usize { break None; } } } { break Some(s); }; } else if n == 0 { // if no bytes are read, that indicates end of file if self.squeezer == Squeezer::Delete { // empty the last line when ending is squeezed self.line_buf.clear(); break Some(0); } break None; } } if is_empty { self.print_header()?; } // squeeze is active, check if the line is the same // skip print if still squeezed, otherwise print and deactivate squeeze if matches!(self.squeezer, Squeezer::Print | Squeezer::Delete) { if self .line_buf .chunks_exact(std::mem::size_of::()) .all(|w| usize::from_ne_bytes(w.try_into().unwrap()) == self.squeeze_byte) { if self.squeezer == Squeezer::Delete { self.idx += 8 * self.panels; continue; } } else { self.squeezer = Squeezer::Ignore; } } // print the line self.print_position_panel()?; self.print_bytes()?; if self.show_char_panel { self.print_char_panel()?; } self.writer.write_all(b"\n")?; if is_empty { self.writer.flush()?; is_empty = false; } // increment index to next line self.idx += 8 * self.panels; // change from print to delete if squeeze is still active if self.squeezer == Squeezer::Print { self.squeezer = Squeezer::Delete; } // repeat the first byte in the line until it's a usize // compare that usize with each usize chunk in the line // if they are all the same, change squeezer to print let repeat_byte = (self.line_buf[0] as usize) * (usize::MAX / 255); if !matches!(self.squeezer, Squeezer::Disabled | Squeezer::Delete) && self .line_buf .chunks_exact(std::mem::size_of::()) .all(|w| usize::from_ne_bytes(w.try_into().unwrap()) == repeat_byte) { self.squeezer = Squeezer::Print; self.squeeze_byte = repeat_byte; }; }; // special ending if is_empty { self.base_digits = 2; self.print_header()?; if self.show_position_panel { write!(self.writer, "{0:9}", "│")?; } write!( self.writer, "{0:2}{1:2$}{0}{0:>3$}", "│", "No content", self.panel_sz() - 1, self.panel_sz() + 1, )?; if self.show_char_panel { write!(self.writer, "{0:>9}{0:>9}", "│")?; } writeln!(self.writer)?; } else if let Some(n) = leftover { // last line is incomplete self.print_position_panel()?; self.squeezer = Squeezer::Ignore; self.print_bytes()?; self.squeezer = Squeezer::Print; for i in n..8 * self.panels as usize { self.print_byte(i, 0)?; } if self.show_char_panel { self.squeezer = Squeezer::Ignore; self.print_char_panel()?; self.squeezer = Squeezer::Print; for i in n..8 * self.panels as usize { self.print_char(i as u64)?; } } self.writer.write_all(b"\n")?; } self.print_footer()?; self.writer.flush()?; Ok(()) } } #[cfg(test)] mod tests { use std::io; use std::str; use super::*; fn assert_print_all_output(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, ); printer.print_all(input).unwrap(); let actual_string: &str = str::from_utf8(&output).unwrap(); assert_eq!(actual_string, expected_string,) } #[test] fn empty_file_passes() { let input = io::empty(); let expected_string = "\ ┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐ │ │ No content │ │ │ │ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘ " .to_owned(); assert_print_all_output(input, expected_string); } #[test] fn short_input_passes() { let input = io::Cursor::new(b"spam"); let expected_string = "\ ┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐ │00000000│ 73 70 61 6d ┊ │spam ┊ │ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘ " .to_owned(); assert_print_all_output(input, expected_string); } #[test] fn display_offset() { let input = io::Cursor::new(b"spamspamspamspamspam"); let expected_string = "\ ┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐ │deadbeef│ 73 70 61 6d 73 70 61 6d ┊ 73 70 61 6d 73 70 61 6d │spamspam┊spamspam│ │deadbeff│ 73 70 61 6d ┊ │spam ┊ │ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘ " .to_owned(); let mut output = vec![]; let mut printer: Printer> = Printer::new( &mut output, false, true, true, BorderStyle::Unicode, true, 2, 1, Base::Hexadecimal, Endianness::Big, CharacterTable::Default, ); printer.display_offset(0xdeadbeef); printer.print_all(input).unwrap(); let actual_string: &str = str::from_utf8(&output).unwrap(); assert_eq!(actual_string, expected_string) } #[test] fn multiple_panels() { let input = io::Cursor::new(b"supercalifragilisticexpialidocioussupercalifragilisticexpialidocioussupercalifragilisticexpialidocious"); let expected_string = "\ ┌────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬────────┬────────┬────────┬────────┐ │00000000│ 73 75 70 65 72 63 61 6c ┊ 69 66 72 61 67 69 6c 69 ┊ 73 74 69 63 65 78 70 69 ┊ 61 6c 69 64 6f 63 69 6f │supercal┊ifragili┊sticexpi┊alidocio│ │00000020│ 75 73 73 75 70 65 72 63 ┊ 61 6c 69 66 72 61 67 69 ┊ 6c 69 73 74 69 63 65 78 ┊ 70 69 61 6c 69 64 6f 63 │ussuperc┊alifragi┊listicex┊pialidoc│ │00000040│ 69 6f 75 73 73 75 70 65 ┊ 72 63 61 6c 69 66 72 61 ┊ 67 69 6c 69 73 74 69 63 ┊ 65 78 70 69 61 6c 69 64 │ioussupe┊rcalifra┊gilistic┊expialid│ │00000060│ 6f 63 69 6f 75 73 ┊ ┊ ┊ │ocious ┊ ┊ ┊ │ └────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴────────┴────────┴────────┴────────┘ " .to_owned(); let mut output = vec![]; let mut printer: Printer> = Printer::new( &mut output, false, true, true, BorderStyle::Unicode, true, 4, 1, Base::Hexadecimal, Endianness::Big, CharacterTable::Default, ); printer.print_all(input).unwrap(); let actual_string: &str = str::from_utf8(&output).unwrap(); assert_eq!(actual_string, expected_string) } #[test] fn squeeze_works() { let input = io::Cursor::new(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); let expected_string = "\ ┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐ │00000000│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │* │ ┊ │ ┊ │ │00000020│ 00 ┊ │⋄ ┊ │ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘ " .to_owned(); assert_print_all_output(input, expected_string); } #[test] fn squeeze_nonzero() { let input = io::Cursor::new(b"000000000000000000000000000000000"); let expected_string = "\ ┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐ │00000000│ 30 30 30 30 30 30 30 30 ┊ 30 30 30 30 30 30 30 30 │00000000┊00000000│ │* │ ┊ │ ┊ │ │00000020│ 30 ┊ │0 ┊ │ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘ " .to_owned(); assert_print_all_output(input, expected_string); } #[test] fn squeeze_multiple_panels() { let input = io::Cursor::new(b"0000000000000000000000000000000000000000000000000"); let expected_string = "\ ┌────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬────────┬────────┬────────┐ │00000000│ 30 30 30 30 30 30 30 30 ┊ 30 30 30 30 30 30 30 30 ┊ 30 30 30 30 30 30 30 30 │00000000┊00000000┊00000000│ │* │ ┊ ┊ │ ┊ ┊ │ │00000030│ 30 ┊ ┊ │0 ┊ ┊ │ └────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴────────┴────────┴────────┘ " .to_owned(); let mut output = vec![]; let mut printer: Printer> = Printer::new( &mut output, false, true, true, BorderStyle::Unicode, true, 3, 1, Base::Hexadecimal, Endianness::Big, CharacterTable::Default, ); printer.print_all(input).unwrap(); let actual_string: &str = str::from_utf8(&output).unwrap(); assert_eq!(actual_string, expected_string) } } hexyl-0.16.0/src/main.rs000064400000000000000000000564171046102023000131370ustar 00000000000000use std::fs::File; use std::io::{self, prelude::*, BufWriter, SeekFrom}; use std::num::{NonZeroI64, NonZeroU64}; use std::path::PathBuf; use clap::builder::ArgPredicate; use clap::{ArgAction, Parser, ValueEnum}; use anyhow::{anyhow, bail, Context, Result}; use const_format::formatcp; use thiserror::Error as ThisError; use terminal_size::terminal_size; use hexyl::{Base, BorderStyle, CharacterTable, Endianness, Input, PrinterBuilder}; use hexyl::{ COLOR_ASCII_OTHER, COLOR_ASCII_PRINTABLE, COLOR_ASCII_WHITESPACE, COLOR_NONASCII, COLOR_NULL, COLOR_RESET, }; #[cfg(test)] mod tests; const DEFAULT_BLOCK_SIZE: i64 = 512; const LENGTH_HELP_TEXT: &str = "Only read N bytes from the input. The N argument can also include \ a unit with a decimal prefix (kB, MB, ..) or binary prefix (kiB, \ MiB, ..), or can be specified using a hex number. The short \ option '-l' can be used as an alias. Examples: --length=64, --length=4KiB, --length=0xff"; const SKIP_HELP_TEXT: &str = "Skip the first N bytes of the input. The N argument can also \ include a unit (see `--length` for details). A negative value is valid and will seek from the end of the file."; const BLOCK_SIZE_HELP_TEXT: &str = "Sets the size of the `block` unit to SIZE. Examples: --block-size=1024, --block-size=4kB"; const DISPLAY_OFFSET_HELP_TEXT: &str = "Add N bytes to the displayed file position. The N \ argument can also include a unit (see `--length` for \ details). A negative value is valid and calculates an offset relative to the end of the file."; const TERMINAL_WIDTH_HELP_TEXT: &str = "Sets the number of terminal columns to be displayed. Since the terminal width may not be an evenly divisible by the width per hex data column, this \ will use the greatest number of hex data panels that can \ fit in the requested width but still leave some space to \ the right. Cannot be used with other width-setting options."; #[derive(Debug, Parser)] #[command(version, about, max_term_width(90))] struct Opt { /// The file to display. If no FILE argument is given, read from STDIN. #[arg(value_name("FILE"))] file: Option, #[arg( help(LENGTH_HELP_TEXT), short('n'), long, visible_short_alias('c'), visible_alias("bytes"), short_alias('l'), value_name("N") )] length: Option, #[arg(help(SKIP_HELP_TEXT), short, long, value_name("N"))] skip: Option, #[arg( help(BLOCK_SIZE_HELP_TEXT), long, default_value(formatcp!("{DEFAULT_BLOCK_SIZE}")), value_name("SIZE") )] block_size: String, /// Displays all input data. Otherwise any number of groups of output lines /// which would be identical to the preceding group of lines, are replaced /// with a line comprised of a single asterisk. #[arg(short('v'), long)] no_squeezing: bool, /// When to use colors. #[arg( long, value_enum, default_value_t, value_name("WHEN"), default_value_if("plain", ArgPredicate::IsPresent, Some("never")) )] color: ColorWhen, /// Whether to draw a border. #[arg( long, value_enum, default_value_t, value_name("STYLE"), default_value_if("plain", ArgPredicate::IsPresent, Some("none")) )] border: BorderStyle, /// Display output with --no-characters, --no-position, --border=none, and /// --color=never. #[arg(short, long)] plain: bool, /// Do not show the character panel on the right. #[arg(long)] no_characters: bool, /// Show the character panel on the right. This is the default, unless /// --no-characters has been specified. #[arg( short('C'), long, action(ArgAction::SetTrue), overrides_with("no_characters") )] characters: (), /// Defines how bytes are mapped to characters. #[arg(long, value_enum, default_value_t, value_name("FORMAT"))] character_table: CharacterTable, /// Whether to display the position panel on the left. #[arg(short('P'), long)] no_position: bool, #[arg( help(DISPLAY_OFFSET_HELP_TEXT), short('o'), long, default_value("0"), value_name("N") )] display_offset: String, /// Sets the number of hex data panels to be displayed. `--panels=auto` will /// display the maximum number of hex data panels based on the current /// terminal width. By default, hexyl will show two panels, unless the /// terminal is not wide enough for that. #[arg(long, value_name("N"))] panels: Option, /// Number of bytes/octets that should be grouped together. You can use the /// '--endianness' option to control the ordering of the bytes within a /// group. '--groupsize' can be used as an alias (xxd-compatibility). #[arg( short('g'), long, value_enum, default_value_t, alias("groupsize"), value_name("N") )] group_size: GroupSize, /// Whether to print out groups in little-endian or big-endian format. This /// option only has an effect if the '--group-size' is larger than 1. '-e' /// can be used as an alias for '--endianness=little'. #[arg(long, value_enum, default_value_t, value_name("FORMAT"))] endianness: Endianness, /// An alias for '--endianness=little'. #[arg(short('e'), hide(true), overrides_with("endianness"))] little_endian_format: bool, /// Sets the base used for the bytes. The possible options are binary, /// octal, decimal, and hexadecimal. #[arg(short('b'), long, default_value("hexadecimal"), value_name("B"))] base: String, #[arg( help(TERMINAL_WIDTH_HELP_TEXT), long, value_name("N"), conflicts_with("panels") )] terminal_width: Option, /// Print a table showing how different types of bytes are colored. #[arg(long)] print_color_table: bool, } #[derive(Clone, Debug, Default, ValueEnum)] enum ColorWhen { /// Always use colorized output. #[default] Always, /// Only displays colors if the output goes to an interactive terminal. Auto, /// Do not use colorized output. Never, /// Override the NO_COLOR environment variable. Force, } #[derive(Clone, Debug, Default, ValueEnum)] enum GroupSize { /// Grouped together every byte/octet. #[default] #[value(name = "1")] One, /// Grouped together every 2 bytes/octets. #[value(name = "2")] Two, /// Grouped together every 4 bytes/octets. #[value(name = "4")] Four, /// Grouped together every 8 bytes/octets. #[value(name = "8")] Eight, } impl From for u8 { fn from(number: GroupSize) -> Self { match number { GroupSize::One => 1, GroupSize::Two => 2, GroupSize::Four => 4, GroupSize::Eight => 8, } } } fn run() -> Result<()> { let opt = Opt::parse(); if opt.print_color_table { return print_color_table().map_err(|e| anyhow!(e)); } let stdin = io::stdin(); 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)?; Input::File(file) } None => Input::Stdin(stdin.lock()), }; if let Some(hex_number) = try_parse_as_hex_number(&opt.block_size) { return hex_number .map_err(|e| anyhow!(e)) .and_then(|x| { PositiveI64::new(x).ok_or_else(|| anyhow!("block size argument must be positive")) }) .map(|_| ()); } let (num, unit) = extract_num_and_unit_from(&opt.block_size)?; if let Unit::Block { custom_size: _ } = unit { return Err(anyhow!( "can not use 'block(s)' as a unit to specify block size" )); }; let block_size = num .checked_mul(unit.get_multiplier()) .ok_or_else(|| anyhow!(ByteOffsetParseError::UnitMultiplicationOverflow)) .and_then(|x| { PositiveI64::new(x).ok_or_else(|| anyhow!("block size argument must be positive")) })?; let skip_arg = opt .skip .as_ref() .map(|s| { parse_byte_offset(s, block_size).context(anyhow!( "failed to parse `--skip` arg {:?} as byte count", s )) }) .transpose()?; let skip_offset = if let Some(ByteOffset { kind, value }) = skip_arg { let value = value.into_inner(); reader .seek(match kind { ByteOffsetKind::ForwardFromBeginning | ByteOffsetKind::ForwardFromLastOffset => { SeekFrom::Current(value) } ByteOffsetKind::BackwardFromEnd => SeekFrom::End(value.checked_neg().unwrap()), }) .map_err(|_| { anyhow!( "Failed to jump to the desired input position. \ This could be caused by a negative offset that is too large or by \ an input that is not seek-able (e.g. if the input comes from a pipe)." ) })? } else { 0 }; let parse_byte_count = |s| -> Result { Ok(parse_byte_offset(s, block_size)? .assume_forward_offset_from_start()? .into()) }; let mut reader = if let Some(ref length) = opt.length { let length = parse_byte_count(length).context(anyhow!( "failed to parse `--length` arg {:?} as byte count", length ))?; Box::new(reader.take(length)) } else { reader.into_inner() }; let no_color = std::env::var_os("NO_COLOR").is_some(); let show_color = match opt.color { ColorWhen::Never => false, ColorWhen::Always => !no_color, ColorWhen::Force => true, ColorWhen::Auto => { if no_color { false } else { supports_color::on(supports_color::Stream::Stdout) .map(|level| level.has_basic) .unwrap_or(false) } } }; let border_style = opt.border; let &squeeze = &!opt.no_squeezing; let show_char_panel = !opt.no_characters && !opt.plain; let show_position_panel = !opt.no_position && !opt.plain; let display_offset: u64 = parse_byte_count(&opt.display_offset).context(anyhow!( "failed to parse `--display-offset` arg {:?} as byte count", opt.display_offset ))?; let max_panels_fn = |terminal_width: u64, base_digits: u64, group_size: u64| { let offset = if show_position_panel { 10 } else { 1 }; let col_width = if show_char_panel { ((8 / group_size) * (base_digits * group_size + 1)) + 2 + 8 } else { ((8 / group_size) * (base_digits * group_size + 1)) + 2 }; if (terminal_width - offset) / col_width < 1 { 1 } else { (terminal_width - offset) / col_width } }; let base = if let Ok(base_num) = opt.base.parse::() { match base_num { 2 => Ok(Base::Binary), 8 => Ok(Base::Octal), 10 => Ok(Base::Decimal), 16 => Ok(Base::Hexadecimal), _ => Err(anyhow!( "The number provided is not a valid base. Valid bases are 2, 8, 10, and 16." )), } } else { match opt.base.as_str() { "b" | "bin" | "binary" => Ok(Base::Binary), "o" | "oct" | "octal" => Ok(Base::Octal), "d" | "dec" | "decimal" => Ok(Base::Decimal), "x" | "hex" | "hexadecimal" => Ok(Base::Hexadecimal), _ => Err(anyhow!( "The base provided is not valid. Valid bases are \"b\", \"o\", \"d\", and \"x\"." )), } }?; let base_digits = match base { Base::Binary => 8, Base::Octal => 3, Base::Decimal => 3, Base::Hexadecimal => 2, }; let group_size = u8::from(opt.group_size); let terminal_width = terminal_size().map(|s| s.0 .0 as u64).unwrap_or(80); let panels = if opt.panels.as_deref() == Some("auto") { max_panels_fn(terminal_width, base_digits, group_size.into()) } else if let Some(panels) = opt.panels { panels .parse::() .map(u64::from) .context(anyhow!( "failed to parse `--panels` arg {:?} as unsigned nonzero integer", panels ))? } else if let Some(terminal_width) = opt.terminal_width { max_panels_fn(terminal_width.into(), base_digits, group_size.into()) } else { std::cmp::min( 2, max_panels_fn(terminal_width, base_digits, group_size.into()), ) }; let endianness = if opt.little_endian_format { Endianness::Little } else { opt.endianness }; let character_table = opt.character_table; let mut stdout = BufWriter::new(io::stdout().lock()); let mut printer = PrinterBuilder::new(&mut stdout) .show_color(show_color) .show_char_panel(show_char_panel) .show_position_panel(show_position_panel) .with_border_style(border_style) .enable_squeezing(squeeze) .num_panels(panels) .group_size(group_size) .with_base(base) .endianness(endianness) .character_table(character_table) .build(); printer.display_offset(skip_offset + display_offset); printer.print_all(&mut reader).map_err(|e| anyhow!(e))?; Ok(()) } fn main() { let result = run(); if let Err(err) = result { if let Some(io_error) = err.downcast_ref::() { if io_error.kind() == ::std::io::ErrorKind::BrokenPipe { std::process::exit(0); } } eprintln!("Error: {err:?}"); std::process::exit(1); } } #[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)] pub struct NonNegativeI64(i64); impl NonNegativeI64 { pub fn new(x: i64) -> Option { if x.is_negative() { None } else { Some(Self(x)) } } pub fn into_inner(self) -> i64 { self.0 } } impl From for u64 { fn from(x: NonNegativeI64) -> u64 { u64::try_from(x.0) .expect("invariant broken: NonNegativeI64 should contain a non-negative i64 value") } } fn print_color_table() -> io::Result<()> { let mut stdout = BufWriter::new(io::stdout().lock()); writeln!(stdout, "hexyl color reference:\n")?; // NULL bytes stdout.write_all(COLOR_NULL)?; writeln!(stdout, "⋄ NULL bytes (0x00)")?; stdout.write_all(COLOR_RESET)?; // ASCII printable stdout.write_all(COLOR_ASCII_PRINTABLE)?; writeln!(stdout, "a ASCII printable characters (0x20 - 0x7E)")?; stdout.write_all(COLOR_RESET)?; // ASCII whitespace stdout.write_all(COLOR_ASCII_WHITESPACE)?; writeln!(stdout, "_ ASCII whitespace (0x09 - 0x0D, 0x20)")?; stdout.write_all(COLOR_RESET)?; // ASCII other stdout.write_all(COLOR_ASCII_OTHER)?; writeln!( stdout, "• ASCII control characters (except NULL and whitespace)" )?; stdout.write_all(COLOR_RESET)?; // Non-ASCII stdout.write_all(COLOR_NONASCII)?; writeln!(stdout, "× Non-ASCII bytes (0x80 - 0xFF)")?; stdout.write_all(COLOR_RESET)?; Ok(()) } #[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)] pub struct PositiveI64(i64); impl PositiveI64 { pub fn new(x: i64) -> Option { if x < 1 { None } else { Some(Self(x)) } } pub fn into_inner(self) -> i64 { self.0 } } impl From for u64 { fn from(x: PositiveI64) -> u64 { u64::try_from(x.0) .expect("invariant broken: PositiveI64 should contain a positive i64 value") } } #[derive(Debug, PartialEq)] enum Unit { Byte, Kilobyte, Megabyte, Gigabyte, Terabyte, Kibibyte, Mebibyte, Gibibyte, Tebibyte, /// a customizable amount of bytes Block { custom_size: Option, }, } impl Unit { const fn get_multiplier(self) -> i64 { match self { Self::Byte => 1, Self::Kilobyte => 1000, Self::Megabyte => 1_000_000, Self::Gigabyte => 1_000_000_000, Self::Terabyte => 1_000_000_000_000, Self::Kibibyte => 1 << 10, Self::Mebibyte => 1 << 20, Self::Gibibyte => 1 << 30, Self::Tebibyte => 1 << 40, Self::Block { custom_size: Some(size), } => size.get(), Self::Block { custom_size: None } => DEFAULT_BLOCK_SIZE, } } } const HEX_PREFIX: &str = "0x"; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] enum ByteOffsetKind { ForwardFromBeginning, ForwardFromLastOffset, BackwardFromEnd, } #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] struct ByteOffset { value: NonNegativeI64, kind: ByteOffsetKind, } #[derive(Clone, Debug, ThisError)] #[error( "negative offset specified, but only positive offsets (counts) are accepted in this context" )] struct NegativeOffsetSpecifiedError; impl ByteOffset { fn assume_forward_offset_from_start( &self, ) -> Result { let &Self { value, kind } = self; match kind { ByteOffsetKind::ForwardFromBeginning | ByteOffsetKind::ForwardFromLastOffset => { Ok(value) } ByteOffsetKind::BackwardFromEnd => Err(NegativeOffsetSpecifiedError), } } } #[derive(Clone, Debug, Eq, PartialEq, ThisError)] enum ByteOffsetParseError { #[error("no character data found, did you forget to write it?")] Empty, #[error("no digits found after sign, did you forget to write them?")] EmptyAfterSign, #[error( "found {0:?} sign after hex prefix ({:?}); signs should go before it", HEX_PREFIX )] SignFoundAfterHexPrefix(char), #[error("{0:?} is not of the expected form []")] InvalidNumAndUnit(String), #[error("{0:?} is a valid unit, but an integer should come before it")] EmptyWithUnit(String), #[error("invalid unit {0:?}")] InvalidUnit(String), #[error("failed to parse integer part")] ParseNum(#[source] std::num::ParseIntError), #[error("count multiplied by the unit overflowed a signed 64-bit integer; are you sure it should be that big?")] UnitMultiplicationOverflow, } fn parse_byte_offset(n: &str, block_size: PositiveI64) -> Result { use ByteOffsetParseError::*; let (n, kind) = process_sign_of(n)?; let into_byte_offset = |value| { Ok(ByteOffset { value: NonNegativeI64::new(value).unwrap(), kind, }) }; if let Some(hex_number) = try_parse_as_hex_number(n) { return hex_number.map(into_byte_offset)?; } let (num, mut unit) = extract_num_and_unit_from(n)?; if let Unit::Block { custom_size: None } = unit { unit = Unit::Block { custom_size: Some( NonZeroI64::new(block_size.into_inner()).expect("PositiveI64 was zero"), ), }; } num.checked_mul(unit.get_multiplier()) .ok_or(UnitMultiplicationOverflow) .and_then(into_byte_offset) } /// Takes a string containing a base-10 number and an optional unit, and returns them with their proper types. /// The unit must directly follow the number (e.g. no whitespace is allowed between them). /// When no unit is given, [Unit::Byte] is assumed. /// When the unit is [Unit::Block], it is returned without custom size. /// No normalization is performed, that is "1024" is extracted to (1024, Byte), not (1, Kibibyte). fn extract_num_and_unit_from(n: &str) -> Result<(i64, Unit), ByteOffsetParseError> { use ByteOffsetParseError::*; if n.is_empty() { return Err(Empty); } match n.chars().position(|c| !c.is_ascii_digit()) { Some(unit_begin_idx) => { let (n, raw_unit) = n.split_at(unit_begin_idx); let unit = match raw_unit.to_lowercase().as_str() { "" => Unit::Byte, // no "b" => Byte to allow hex nums with units "kb" => Unit::Kilobyte, "mb" => Unit::Megabyte, "gb" => Unit::Gigabyte, "tb" => Unit::Terabyte, "kib" => Unit::Kibibyte, "mib" => Unit::Mebibyte, "gib" => Unit::Gibibyte, "tib" => Unit::Tebibyte, "block" | "blocks" => Unit::Block { custom_size: None }, _ => { return if n.is_empty() { Err(InvalidNumAndUnit(raw_unit.to_string())) } else { Err(InvalidUnit(raw_unit.to_string())) } } }; let num = n.parse::().map_err(|e| { if n.is_empty() { EmptyWithUnit(raw_unit.to_owned()) } else { ParseNum(e) } })?; Ok((num, unit)) } None => { // no unit part let num = n.parse::().map_err(ParseNum)?; Ok((num, Unit::Byte)) } } } /// Extracts a [ByteOffsetKind] based on the sign at the beginning of the given string. /// Returns the input string without the sign (or an equal string if there wasn't any sign). fn process_sign_of(n: &str) -> Result<(&str, ByteOffsetKind), ByteOffsetParseError> { use ByteOffsetParseError::*; let mut chars = n.chars(); let next_char = chars.next(); let check_empty_after_sign = || { if chars.clone().next().is_none() { Err(EmptyAfterSign) } else { Ok(chars.as_str()) } }; match next_char { Some('+') => Ok(( check_empty_after_sign()?, ByteOffsetKind::ForwardFromLastOffset, )), Some('-') => Ok((check_empty_after_sign()?, ByteOffsetKind::BackwardFromEnd)), None => Err(Empty), _ => Ok((n, ByteOffsetKind::ForwardFromBeginning)), } } /// If `n` starts with a hex prefix, its remaining part is returned as some number (if possible), /// otherwise None is returned. fn try_parse_as_hex_number(n: &str) -> Option> { use ByteOffsetParseError::*; n.strip_prefix(HEX_PREFIX).map(|num| { let mut chars = num.chars(); match chars.next() { Some(c @ '+') | Some(c @ '-') => { return if chars.next().is_none() { Err(EmptyAfterSign) } else { Err(SignFoundAfterHexPrefix(c)) } } _ => (), } i64::from_str_radix(num, 16).map_err(ParseNum) }) } hexyl-0.16.0/src/tests.rs000064400000000000000000000135541046102023000133500ustar 00000000000000use super::*; #[test] fn unit_multipliers() { use Unit::*; assert_eq!(Kilobyte.get_multiplier(), 1000 * Byte.get_multiplier()); assert_eq!(Megabyte.get_multiplier(), 1000 * Kilobyte.get_multiplier()); assert_eq!(Gigabyte.get_multiplier(), 1000 * Megabyte.get_multiplier()); assert_eq!(Terabyte.get_multiplier(), 1000 * Gigabyte.get_multiplier()); assert_eq!(Kibibyte.get_multiplier(), 1024 * Byte.get_multiplier()); assert_eq!(Mebibyte.get_multiplier(), 1024 * Kibibyte.get_multiplier()); assert_eq!(Gibibyte.get_multiplier(), 1024 * Mebibyte.get_multiplier()); assert_eq!(Tebibyte.get_multiplier(), 1024 * Gibibyte.get_multiplier()); } #[test] fn test_process_sign() { use ByteOffsetKind::*; use ByteOffsetParseError::*; assert_eq!(process_sign_of("123"), Ok(("123", ForwardFromBeginning))); assert_eq!(process_sign_of("+123"), Ok(("123", ForwardFromLastOffset))); assert_eq!(process_sign_of("-123"), Ok(("123", BackwardFromEnd))); assert_eq!(process_sign_of("-"), Err(EmptyAfterSign)); assert_eq!(process_sign_of("+"), Err(EmptyAfterSign)); assert_eq!(process_sign_of(""), Err(Empty)); } #[test] fn test_parse_as_hex() { assert_eq!(try_parse_as_hex_number("73"), None); assert_eq!(try_parse_as_hex_number("0x1337"), Some(Ok(0x1337))); assert!(matches!(try_parse_as_hex_number("0xnope"), Some(Err(_)))); assert!(matches!(try_parse_as_hex_number("0x-1"), Some(Err(_)))); } #[test] fn extract_num_and_unit() { use ByteOffsetParseError::*; use Unit::*; // byte is default unit assert_eq!(extract_num_and_unit_from("4"), Ok((4, Byte))); // blocks are returned without customization assert_eq!( extract_num_and_unit_from("2blocks"), Ok((2, Block { custom_size: None })) ); // no normalization is performed assert_eq!(extract_num_and_unit_from("1024kb"), Ok((1024, Kilobyte))); // unit without number results in error assert_eq!( extract_num_and_unit_from("gib"), Err(EmptyWithUnit("gib".to_string())) ); // empty string results in error assert_eq!(extract_num_and_unit_from(""), Err(Empty)); // an invalid unit results in an error assert_eq!( extract_num_and_unit_from("25litres"), Err(InvalidUnit("litres".to_string())) ); } #[test] fn test_parse_byte_offset() { use ByteOffsetParseError::*; macro_rules! success { ($input: expr, $expected_kind: ident $expected_value: expr) => { success!($input, $expected_kind $expected_value; block_size: DEFAULT_BLOCK_SIZE) }; ($input: expr, $expected_kind: ident $expected_value: expr; block_size: $block_size: expr) => { assert_eq!( parse_byte_offset($input, PositiveI64::new($block_size).unwrap()), Ok( ByteOffset { value: NonNegativeI64::new($expected_value).unwrap(), kind: ByteOffsetKind::$expected_kind, } ), ); }; } macro_rules! error { ($input: expr, $expected_err: expr) => { assert_eq!( parse_byte_offset($input, PositiveI64::new(DEFAULT_BLOCK_SIZE).unwrap()), Err($expected_err), ); }; } success!("0", ForwardFromBeginning 0); success!("1", ForwardFromBeginning 1); success!("1", ForwardFromBeginning 1); success!("100", ForwardFromBeginning 100); success!("+100", ForwardFromLastOffset 100); success!("0x0", ForwardFromBeginning 0); success!("0xf", ForwardFromBeginning 15); success!("0xdeadbeef", ForwardFromBeginning 3_735_928_559); success!("1KB", ForwardFromBeginning 1000); success!("2MB", ForwardFromBeginning 2000000); success!("3GB", ForwardFromBeginning 3000000000); success!("4TB", ForwardFromBeginning 4000000000000); success!("+4TB", ForwardFromLastOffset 4000000000000); success!("1GiB", ForwardFromBeginning 1073741824); success!("2TiB", ForwardFromBeginning 2199023255552); success!("+2TiB", ForwardFromLastOffset 2199023255552); success!("0xff", ForwardFromBeginning 255); success!("0xEE", ForwardFromBeginning 238); success!("+0xFF", ForwardFromLastOffset 255); success!("1block", ForwardFromBeginning 512; block_size: 512); success!("2block", ForwardFromBeginning 1024; block_size: 512); success!("1block", ForwardFromBeginning 4; block_size: 4); success!("2block", ForwardFromBeginning 8; block_size: 4); // empty string is invalid error!("", Empty); // These are also bad. error!("+", EmptyAfterSign); error!("-", EmptyAfterSign); error!("K", InvalidNumAndUnit("K".to_owned())); error!("k", InvalidNumAndUnit("k".to_owned())); error!("m", InvalidNumAndUnit("m".to_owned())); error!("block", EmptyWithUnit("block".to_owned())); // leading/trailing space is invalid error!(" 0", InvalidNumAndUnit(" 0".to_owned())); error!("0 ", InvalidUnit(" ".to_owned())); // Signs after the hex prefix make no sense error!("0x-12", SignFoundAfterHexPrefix('-')); // This was previously accepted but shouldn't be. error!("0x+12", SignFoundAfterHexPrefix('+')); // invalid suffix error!("1234asdf", InvalidUnit("asdf".to_owned())); // bad numbers error!("asdf1234", InvalidNumAndUnit("asdf1234".to_owned())); error!("a1s2d3f4", InvalidNumAndUnit("a1s2d3f4".to_owned())); // multiplication overflows u64 error!("20000000TiB", UnitMultiplicationOverflow); assert!( match parse_byte_offset("99999999999999999999", PositiveI64::new(512).unwrap()) { // We can't check against the kind of the `ParseIntError`, so we'll just make sure it's the // same as trying to do the parse directly. Err(ParseNum(e)) => e == "99999999999999999999".parse::().unwrap_err(), _ => false, } ); } hexyl-0.16.0/tests/examples/.gitattributes000064400000000000000000000000221046102023000167060ustar 00000000000000ascii text eol=lf hexyl-0.16.0/tests/examples/ascii000064400000000000000000000000201046102023000150240ustar 000000000000000123456789abcde hexyl-0.16.0/tests/examples/empty000064400000000000000000000000001046102023000150700ustar 00000000000000hexyl-0.16.0/tests/examples/hello_world_elf64000075500000000000000000000204501046102023000172620ustar 00000000000000ELF>@@( @8@@@@@ @ @ @̀̀Hello, world! .shstrtab.text.data @ @  hexyl-0.16.0/tests/integration_tests.rs000064400000000000000000001210701046102023000163170ustar 00000000000000use assert_cmd::Command; fn hexyl() -> Command { let mut cmd = Command::cargo_bin("hexyl").unwrap(); cmd.current_dir("tests/examples"); cmd } trait PrettyAssert where S: AsRef, { fn pretty_stdout(self, other: S); } // https://github.com/assert-rs/assert_cmd/issues/121#issuecomment-849937376 // impl PrettyAssert for assert_cmd::assert::Assert where S: AsRef, { fn pretty_stdout(self, other: S) { println!("{}", other.as_ref().len()); let self_str = String::from_utf8(self.get_output().stdout.clone()).unwrap(); println!("{}", self_str.len()); pretty_assertions::assert_eq!(self_str, other.as_ref()); } } mod basic { use super::hexyl; #[test] fn can_print_simple_ascii_file() { hexyl() .arg("ascii") .arg("--color=never") .assert() .success() .stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\ │00000000│ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │01234567┊89abcde_│\n\ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n", ); } #[test] fn can_read_input_from_stdin() { hexyl() .arg("--color=never") .write_stdin("abc") .assert() .success() .stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\ │00000000│ 61 62 63 ┊ │abc ┊ │\n\ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n", ); } #[test] fn fails_on_non_existing_input() { hexyl().arg("non-existing").assert().failure(); } #[test] fn prints_warning_on_empty_content() { hexyl() .arg("empty") .arg("--color=never") .assert() .success() .stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\ │ │ No content │ │ │ │\n\ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n", ); } } mod length { use super::hexyl; #[test] fn length_restricts_output_size() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--length=32") .assert() .success() .stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\ │00000000│ 7f 45 4c 46 02 01 01 00 ┊ 00 00 00 00 00 00 00 00 │•ELF•••⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│\n\ │00000010│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │•⋄>⋄•⋄⋄⋄┊⋄•@⋄⋄⋄⋄⋄│\n\ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n", ); } #[test] fn fail_if_length_and_bytes_options_are_used_simultaneously() { hexyl() .arg("hello_world_elf64") .arg("--length=32") .arg("--bytes=10") .assert() .failure(); } #[test] fn fail_if_length_and_count_options_are_used_simultaneously() { hexyl() .arg("hello_world_elf64") .arg("--length=32") .arg("-l=10") .assert() .failure(); } } mod bytes { use super::hexyl; #[test] fn fail_if_bytes_and_count_options_are_used_simultaneously() { hexyl() .arg("hello_world_elf64") .arg("--bytes=32") .arg("-l=10") .assert() .failure(); } } mod skip { use super::hexyl; #[test] fn basic() { hexyl() .arg("ascii") .arg("--color=never") .arg("--skip=2") .arg("--length=4") .assert() .success() .stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\ │00000002│ 32 33 34 35 ┊ │2345 ┊ │\n\ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n", ); } #[test] fn prints_warning_when_skipping_past_the_end() { hexyl() .arg("ascii") .arg("--color=never") .arg("--skip=1000") .assert() .success() .stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\ │ │ No content │ │ │ │\n\ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n", ); } #[test] fn negative_offset() { hexyl() .arg("ascii") .arg("--color=never") .arg("--skip=-4") .arg("--length=3") .assert() .success() .stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\ │0000000c│ 63 64 65 ┊ │cde ┊ │\n\ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n", ); } #[test] fn fails_if_negative_offset_is_too_large() { hexyl() .arg("ascii") .arg("--color=never") .arg("--skip=-1MiB") .assert() .failure() .stderr(predicates::str::contains("Failed to jump")); } } mod display_offset { use super::hexyl; #[test] fn basic() { hexyl() .arg("ascii") .arg("--color=never") .arg("--display-offset=0xc0ffee") .assert() .success() .stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\ │00c0ffee│ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │01234567┊89abcde_│\n\ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n", ); } #[test] fn display_offset_and_skip() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--display-offset=0x20") .arg("--skip=0x10") .arg("--length=0x10") .assert() .success() .stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\ │00000030│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │•⋄>⋄•⋄⋄⋄┊⋄•@⋄⋄⋄⋄⋄│\n\ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n", ); } } mod blocksize { use super::hexyl; #[test] fn fails_for_zero_or_negative_blocksize() { hexyl() .arg("ascii") .arg("--block-size=0") .assert() .failure(); hexyl() .arg("ascii") .arg("--block-size=-16") .assert() .failure(); } } mod display_settings { use super::hexyl; #[test] fn plain() { hexyl() .arg("ascii") .arg("--plain") .assert() .success() .stdout(" 30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 0a \n"); } #[test] fn no_chars() { hexyl() .arg("ascii") .arg("--no-characters") .arg("--color=never") .assert() .success() .stdout( "┌────────┬─────────────────────────┬─────────────────────────┐\n\ │00000000│ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │\n\ └────────┴─────────────────────────┴─────────────────────────┘\n", ); } #[test] fn no_position() { hexyl() .arg("ascii") .arg("--no-position") .arg("--color=never") .assert() .success() .stdout( "┌─────────────────────────┬─────────────────────────┬────────┬────────┐\n\ │ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │01234567┊89abcde_│\n\ └─────────────────────────┴─────────────────────────┴────────┴────────┘\n", ); } } mod group_and_endianness { use super::hexyl; use super::PrettyAssert; #[test] fn group_2_bytes_be() { hexyl() .arg("ascii") .arg("--color=never") .arg("--group-size=2") .assert() .success() .stdout( "┌────────┬─────────────────────┬─────────────────────┬────────┬────────┐\n\ │00000000│ 3031 3233 3435 3637 ┊ 3839 6162 6364 650a │01234567┊89abcde_│\n\ └────────┴─────────────────────┴─────────────────────┴────────┴────────┘\n", ); } #[test] fn group_2_bytes_le() { hexyl() .arg("ascii") .arg("--color=never") .arg("--group-size=2") .arg("--endianness=little") .assert() .success() .stdout( "┌────────┬─────────────────────┬─────────────────────┬────────┬────────┐\n\ │00000000│ 3130 3332 3534 3736 ┊ 3938 6261 6463 0a65 │01234567┊89abcde_│\n\ └────────┴─────────────────────┴─────────────────────┴────────┴────────┘\n", ); } #[test] fn group_4_bytes_be() { hexyl() .arg("ascii") .arg("--color=never") .arg("--group-size=4") .assert() .success() .stdout( "┌────────┬───────────────────┬───────────────────┬────────┬────────┐\n\ │00000000│ 30313233 34353637 ┊ 38396162 6364650a │01234567┊89abcde_│\n\ └────────┴───────────────────┴───────────────────┴────────┴────────┘\n", ); } #[test] fn group_4_bytes_le() { hexyl() .arg("ascii") .arg("--color=never") .arg("--group-size=4") .arg("--endianness=little") .assert() .success() .stdout( "┌────────┬───────────────────┬───────────────────┬────────┬────────┐\n\ │00000000│ 33323130 37363534 ┊ 62613938 0a656463 │01234567┊89abcde_│\n\ └────────┴───────────────────┴───────────────────┴────────┴────────┘\n", ); } #[test] fn group_8_bytes_be() { hexyl() .arg("ascii") .arg("--color=never") .arg("--group-size=8") .assert() .success() .stdout( "┌────────┬──────────────────┬──────────────────┬────────┬────────┐\n\ │00000000│ 3031323334353637 ┊ 383961626364650a │01234567┊89abcde_│\n\ └────────┴──────────────────┴──────────────────┴────────┴────────┘\n", ); } #[test] fn group_8_bytes_le() { hexyl() .arg("ascii") .arg("--color=never") .arg("--group-size=8") .arg("--endianness=little") .assert() .success() .stdout( "┌────────┬──────────────────┬──────────────────┬────────┬────────┐\n\ │00000000│ 3736353433323130 ┊ 0a65646362613938 │01234567┊89abcde_│\n\ └────────┴──────────────────┴──────────────────┴────────┴────────┘\n", ); } #[test] fn group_size_plain() { hexyl() .arg("ascii") .arg("--color=never") .arg("--plain") .arg("--group-size=2") .assert() .success() .stdout(" 3031 3233 3435 3637 3839 6162 6364 650a \n"); } #[test] fn group_size_fill_space() { hexyl() .arg("--color=never") .arg("--group-size=2") .write_stdin("abc") .assert() .success() .stdout( "┌────────┬─────────────────────┬─────────────────────┬────────┬────────┐\n\ │00000000│ 6162 63 ┊ │abc ┊ │\n\ └────────┴─────────────────────┴─────────────────────┴────────┴────────┘\n", ); } #[test] fn group_size_invalid() { hexyl() .arg("ascii") .arg("--color=never") .arg("--plain") .arg("--group-size=3") .assert() .failure(); } #[test] fn squeeze_no_chars() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--skip=1024") .arg("--length=4096") .arg("--no-characters") .assert() .success() .pretty_stdout( "\ ┌────────┬─────────────────────────┬─────────────────────────┐ │00000400│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │ │* │ ┊ │ │00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │ │00001010│ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │ │00001020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │ │* │ ┊ │ │00001400│ ┊ │ └────────┴─────────────────────────┴─────────────────────────┘ ", ); } #[test] fn squeeze_no_chars_one_panel() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--skip=1024") .arg("--length=4096") .arg("--no-characters") .arg("--panels=1") .assert() .success() .pretty_stdout( "\ ┌────────┬─────────────────────────┐ │00000400│ 00 00 00 00 00 00 00 00 │ │* │ │ │00001000│ ba 0e 00 00 00 b9 00 20 │ │00001008│ 40 00 bb 01 00 00 00 b8 │ │00001010│ 04 00 00 00 cd 80 b8 01 │ │00001018│ 00 00 00 cd 80 00 00 00 │ │00001020│ 00 00 00 00 00 00 00 00 │ │* │ │ │00001400│ │ └────────┴─────────────────────────┘ ", ); } #[test] fn squeeze_no_position() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--skip=1024") .arg("--length=4096") .arg("--no-position") .assert() .success() .pretty_stdout( "\ ┌─────────────────────────┬─────────────────────────┬────────┬────────┐ │ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │* ┊ │ ┊ │ │ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │ו⋄⋄⋄×⋄ ┊@⋄ו⋄⋄⋄×│ │ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │•⋄⋄⋄××ו┊⋄⋄⋄××⋄⋄⋄│ │ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │* ┊ │ ┊ │ │* ┊ │ ┊ │ └─────────────────────────┴─────────────────────────┴────────┴────────┘ ", ); } #[test] fn squeeze_no_position_one_panel() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--skip=1024") .arg("--length=4096") .arg("--no-position") .arg("--panels=1") .assert() .success() .pretty_stdout( "\ ┌─────────────────────────┬────────┐ │ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄│ │* │ │ │ ba 0e 00 00 00 b9 00 20 │ו⋄⋄⋄×⋄ │ │ 40 00 bb 01 00 00 00 b8 │@⋄ו⋄⋄⋄×│ │ 04 00 00 00 cd 80 b8 01 │•⋄⋄⋄××ו│ │ 00 00 00 cd 80 00 00 00 │⋄⋄⋄××⋄⋄⋄│ │ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄│ │* │ │ │* │ │ └─────────────────────────┴────────┘ ", ); } #[test] fn squeeze_odd_panels_remainder_bytes() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--skip=1024") .arg("--length=4092") // 4 byte remainder .arg("--panels=3") .assert() .success() .pretty_stdout( "\ ┌────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬────────┬────────┬────────┐ │00000400│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │* │ ┊ ┊ │ ┊ ┊ │ │00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 ┊ 04 00 00 00 cd 80 b8 01 │ו⋄⋄⋄×⋄ ┊@⋄ו⋄⋄⋄×┊•⋄⋄⋄××ו│ │00001018│ 00 00 00 cd 80 00 00 00 ┊ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄××⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │00001030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │* │ ┊ ┊ │ ┊ ┊ │ │000013f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 ┊ │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄ ┊ │ └────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴────────┴────────┴────────┘ ", ); } #[test] fn squeeze_plain() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--skip=1024") .arg("--length=4096") .arg("--plain") .assert() .success() .pretty_stdout( " \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * ba 0e 00 00 00 b9 00 20 40 00 bb 01 00 00 00 b8 04 00 00 00 cd 80 b8 01 00 00 00 cd 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * * ", ); } #[test] fn squeeze_plain_remainder() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--skip=1024") .arg("--length=4092") // 4 byte remainder .arg("--plain") .assert() .success() .pretty_stdout( " \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * ba 0e 00 00 00 b9 00 20 40 00 bb 01 00 00 00 b8 04 00 00 00 cd 80 b8 01 00 00 00 cd 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * 00 00 00 00 00 00 00 00 00 00 00 00 ", ); } } mod base { use super::hexyl; use super::PrettyAssert; #[test] fn base2() { hexyl() .arg("ascii") .arg("--plain") .arg("--base=binary") .assert() .success() .pretty_stdout( " 00110000 00110001 00110010 00110011 00110100 00110101 00110110 00110111 \n \ 00111000 00111001 01100001 01100010 01100011 01100100 01100101 00001010 \n", ); } } mod character_table { use super::hexyl; use super::PrettyAssert; #[test] fn ascii() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--character-table=ascii") .assert() .success() .pretty_stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐ │00000000│ 7f 45 4c 46 02 01 01 00 ┊ 00 00 00 00 00 00 00 00 │.ELF....┊........│ │00000010│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │..>.....┊..@.....│ │00000020│ 40 00 00 00 00 00 00 00 ┊ 28 20 00 00 00 00 00 00 │@.......┊( ......│ │00000030│ 00 00 00 00 40 00 38 00 ┊ 03 00 40 00 04 00 03 00 │....@.8.┊..@.....│ │00000040│ 01 00 00 00 04 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │00000050│ 00 00 40 00 00 00 00 00 ┊ 00 00 40 00 00 00 00 00 │..@.....┊..@.....│ │00000060│ e8 00 00 00 00 00 00 00 ┊ e8 00 00 00 00 00 00 00 │........┊........│ │00000070│ 00 10 00 00 00 00 00 00 ┊ 01 00 00 00 05 00 00 00 │........┊........│ │00000080│ 00 10 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │........┊..@.....│ │00000090│ 00 10 40 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │..@.....┊........│ │000000a0│ 1d 00 00 00 00 00 00 00 ┊ 00 10 00 00 00 00 00 00 │........┊........│ │000000b0│ 01 00 00 00 06 00 00 00 ┊ 00 20 00 00 00 00 00 00 │........┊. ......│ │000000c0│ 00 20 40 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │. @.....┊. @.....│ │000000d0│ 0e 00 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │........┊........│ │000000e0│ 00 10 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │000000f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │* │ ┊ │ ┊ │ │00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │....... ┊@.......│ │00001010│ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │........┊........│ │00001020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │* │ ┊ │ ┊ │ │00002000│ 48 65 6c 6c 6f 2c 20 77 ┊ 6f 72 6c 64 21 0a 00 2e │Hello, w┊orld!...│ │00002010│ 73 68 73 74 72 74 61 62 ┊ 00 2e 74 65 78 74 00 2e │shstrtab┊..text..│ │00002020│ 64 61 74 61 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │data....┊........│ │00002030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │* │ ┊ │ ┊ │ │00002060│ 00 00 00 00 00 00 00 00 ┊ 0b 00 00 00 01 00 00 00 │........┊........│ │00002070│ 06 00 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │........┊..@.....│ │00002080│ 00 10 00 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │........┊........│ │00002090│ 00 00 00 00 00 00 00 00 ┊ 10 00 00 00 00 00 00 00 │........┊........│ │000020a0│ 00 00 00 00 00 00 00 00 ┊ 11 00 00 00 01 00 00 00 │........┊........│ │000020b0│ 03 00 00 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │........┊. @.....│ │000020c0│ 00 20 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │. ......┊........│ │000020d0│ 00 00 00 00 00 00 00 00 ┊ 04 00 00 00 00 00 00 00 │........┊........│ │000020e0│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 03 00 00 00 │........┊........│ │000020f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │00002100│ 0e 20 00 00 00 00 00 00 ┊ 17 00 00 00 00 00 00 00 │. ......┊........│ │00002110│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 00 00 00 00 │........┊........│ │00002120│ 00 00 00 00 00 00 00 00 ┊ │........┊ │ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘ ", ); } #[test] fn codepage_437() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--character-table=codepage-437") .assert() .success() .pretty_stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐ │00000000│ 7f 45 4c 46 02 01 01 00 ┊ 00 00 00 00 00 00 00 00 │⌂ELF☻☺☺⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │00000010│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │☻⋄>⋄☺⋄⋄⋄┊⋄►@⋄⋄⋄⋄⋄│ │00000020│ 40 00 00 00 00 00 00 00 ┊ 28 20 00 00 00 00 00 00 │@⋄⋄⋄⋄⋄⋄⋄┊( ⋄⋄⋄⋄⋄⋄│ │00000030│ 00 00 00 00 40 00 38 00 ┊ 03 00 40 00 04 00 03 00 │⋄⋄⋄⋄@⋄8⋄┊♥⋄@⋄♦⋄♥⋄│ │00000040│ 01 00 00 00 04 00 00 00 ┊ 00 00 00 00 00 00 00 00 │☺⋄⋄⋄♦⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │00000050│ 00 00 40 00 00 00 00 00 ┊ 00 00 40 00 00 00 00 00 │⋄⋄@⋄⋄⋄⋄⋄┊⋄⋄@⋄⋄⋄⋄⋄│ │00000060│ e8 00 00 00 00 00 00 00 ┊ e8 00 00 00 00 00 00 00 │Φ⋄⋄⋄⋄⋄⋄⋄┊Φ⋄⋄⋄⋄⋄⋄⋄│ │00000070│ 00 10 00 00 00 00 00 00 ┊ 01 00 00 00 05 00 00 00 │⋄►⋄⋄⋄⋄⋄⋄┊☺⋄⋄⋄♣⋄⋄⋄│ │00000080│ 00 10 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │⋄►⋄⋄⋄⋄⋄⋄┊⋄►@⋄⋄⋄⋄⋄│ │00000090│ 00 10 40 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │⋄►@⋄⋄⋄⋄⋄┊↔⋄⋄⋄⋄⋄⋄⋄│ │000000a0│ 1d 00 00 00 00 00 00 00 ┊ 00 10 00 00 00 00 00 00 │↔⋄⋄⋄⋄⋄⋄⋄┊⋄►⋄⋄⋄⋄⋄⋄│ │000000b0│ 01 00 00 00 06 00 00 00 ┊ 00 20 00 00 00 00 00 00 │☺⋄⋄⋄♠⋄⋄⋄┊⋄ ⋄⋄⋄⋄⋄⋄│ │000000c0│ 00 20 40 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │⋄ @⋄⋄⋄⋄⋄┊⋄ @⋄⋄⋄⋄⋄│ │000000d0│ 0e 00 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │♫⋄⋄⋄⋄⋄⋄⋄┊♫⋄⋄⋄⋄⋄⋄⋄│ │000000e0│ 00 10 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄►⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │000000f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │* │ ┊ │ ┊ │ │00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │║♫⋄⋄⋄╣⋄ ┊@⋄╗☺⋄⋄⋄╕│ │00001010│ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │♦⋄⋄⋄═Ç╕☺┊⋄⋄⋄═Ç⋄⋄⋄│ │00001020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │* │ ┊ │ ┊ │ │00002000│ 48 65 6c 6c 6f 2c 20 77 ┊ 6f 72 6c 64 21 0a 00 2e │Hello, w┊orld!◙⋄.│ │00002010│ 73 68 73 74 72 74 61 62 ┊ 00 2e 74 65 78 74 00 2e │shstrtab┊⋄.text⋄.│ │00002020│ 64 61 74 61 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │data⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │00002030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │* │ ┊ │ ┊ │ │00002060│ 00 00 00 00 00 00 00 00 ┊ 0b 00 00 00 01 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊♂⋄⋄⋄☺⋄⋄⋄│ │00002070│ 06 00 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │♠⋄⋄⋄⋄⋄⋄⋄┊⋄►@⋄⋄⋄⋄⋄│ │00002080│ 00 10 00 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │⋄►⋄⋄⋄⋄⋄⋄┊↔⋄⋄⋄⋄⋄⋄⋄│ │00002090│ 00 00 00 00 00 00 00 00 ┊ 10 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊►⋄⋄⋄⋄⋄⋄⋄│ │000020a0│ 00 00 00 00 00 00 00 00 ┊ 11 00 00 00 01 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊◄⋄⋄⋄☺⋄⋄⋄│ │000020b0│ 03 00 00 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │♥⋄⋄⋄⋄⋄⋄⋄┊⋄ @⋄⋄⋄⋄⋄│ │000020c0│ 00 20 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │⋄ ⋄⋄⋄⋄⋄⋄┊♫⋄⋄⋄⋄⋄⋄⋄│ │000020d0│ 00 00 00 00 00 00 00 00 ┊ 04 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊♦⋄⋄⋄⋄⋄⋄⋄│ │000020e0│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 03 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊☺⋄⋄⋄♥⋄⋄⋄│ │000020f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│ │00002100│ 0e 20 00 00 00 00 00 00 ┊ 17 00 00 00 00 00 00 00 │♫ ⋄⋄⋄⋄⋄⋄┊↨⋄⋄⋄⋄⋄⋄⋄│ │00002110│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊☺⋄⋄⋄⋄⋄⋄⋄│ │00002120│ 00 00 00 00 00 00 00 00 ┊ │⋄⋄⋄⋄⋄⋄⋄⋄┊ │ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘ ", ); } #[test] fn codepage_1047() { hexyl() .arg("hello_world_elf64") .arg("--color=never") .arg("--character-table=codepage-1047") .assert() .success() .pretty_stdout( "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐ │00000000│ 7f 45 4c 46 02 01 01 00 ┊ 00 00 00 00 00 00 00 00 │..<.....┊........│ │00000010│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │........┊.. .....│ │00000020│ 40 00 00 00 00 00 00 00 ┊ 28 20 00 00 00 00 00 00 │ .......┊........│ │00000030│ 00 00 00 00 40 00 38 00 ┊ 03 00 40 00 04 00 03 00 │.... ...┊.. .....│ │00000040│ 01 00 00 00 04 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │00000050│ 00 00 40 00 00 00 00 00 ┊ 00 00 40 00 00 00 00 00 │.. .....┊.. .....│ │00000060│ e8 00 00 00 00 00 00 00 ┊ e8 00 00 00 00 00 00 00 │Y.......┊Y.......│ │00000070│ 00 10 00 00 00 00 00 00 ┊ 01 00 00 00 05 00 00 00 │........┊........│ │00000080│ 00 10 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │........┊.. .....│ │00000090│ 00 10 40 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │.. .....┊........│ │000000a0│ 1d 00 00 00 00 00 00 00 ┊ 00 10 00 00 00 00 00 00 │........┊........│ │000000b0│ 01 00 00 00 06 00 00 00 ┊ 00 20 00 00 00 00 00 00 │........┊........│ │000000c0│ 00 20 40 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │.. .....┊.. .....│ │000000d0│ 0e 00 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │........┊........│ │000000e0│ 00 10 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │000000f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │* │ ┊ │ ┊ │ │00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │[.......┊ .].....│ │00001010│ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │........┊........│ │00001020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │* │ ┊ │ ┊ │ │00002000│ 48 65 6c 6c 6f 2c 20 77 ┊ 6f 72 6c 64 21 0a 00 2e │..%%?...┊?.%.....│ │00002010│ 73 68 73 74 72 74 61 62 ┊ 00 2e 74 65 78 74 00 2e │....../.┊........│ │00002020│ 64 61 74 61 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │././....┊........│ │00002030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │* │ ┊ │ ┊ │ │00002060│ 00 00 00 00 00 00 00 00 ┊ 0b 00 00 00 01 00 00 00 │........┊........│ │00002070│ 06 00 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │........┊.. .....│ │00002080│ 00 10 00 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │........┊........│ │00002090│ 00 00 00 00 00 00 00 00 ┊ 10 00 00 00 00 00 00 00 │........┊........│ │000020a0│ 00 00 00 00 00 00 00 00 ┊ 11 00 00 00 01 00 00 00 │........┊........│ │000020b0│ 03 00 00 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │........┊.. .....│ │000020c0│ 00 20 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │........┊........│ │000020d0│ 00 00 00 00 00 00 00 00 ┊ 04 00 00 00 00 00 00 00 │........┊........│ │000020e0│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 03 00 00 00 │........┊........│ │000020f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│ │00002100│ 0e 20 00 00 00 00 00 00 ┊ 17 00 00 00 00 00 00 00 │........┊........│ │00002110│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 00 00 00 00 │........┊........│ │00002120│ 00 00 00 00 00 00 00 00 ┊ │........┊ │ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘ ", ); } }