mirror of
https://github.com/uutils/diffutils.git
synced 2026-06-10 15:48:59 -07:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d5719810f | |||
| 7480068e7d | |||
| 763074a804 | |||
| fac8dab182 | |||
| 2e681301b4 | |||
| 50057412bd | |||
| 68292b370d | |||
| 26bcc102c0 | |||
| 50198ef2c1 | |||
| bfdbf6d7b2 | |||
| f75c187971 | |||
| d07c0438b5 | |||
| 72c7802f06 | |||
| c1b66e4a47 | |||
| 9103365691 | |||
| 7574243de1 | |||
| 6f3834d69d | |||
| d8b91fd60e | |||
| 7c9c2a1ab2 | |||
| 63d51bcd69 | |||
| d5bce65a29 | |||
| 9db1eab1d0 | |||
| 2392acfad1 | |||
| 2a899a9fc7 | |||
| 6ec8370b4b | |||
| dbabf399d5 | |||
| b815162b80 | |||
| 12b205e655 | |||
| 8a6504dd83 | |||
| 67ef43083a | |||
| b1738538a8 | |||
| eea6b62b20 | |||
| f08a3bf512 | |||
| e55ee893dd | |||
| 24245ee098 | |||
| 11f815a7c2 | |||
| e9a8141618 | |||
| c9a756eb43 | |||
| 468c4bf934 | |||
| 1e8fdd58d9 | |||
| e98b5e179e | |||
| 1901982375 | |||
| eee6f49920 | |||
| 8a3a977d2c | |||
| fa4e0c6097 | |||
| d362046ae5 | |||
| 7964afa336 | |||
| 80b993141b | |||
| d922313c8c | |||
| 3e246ab36c | |||
| 4b70969ff1 | |||
| 767c6f6c4a | |||
| 1f896ca1ac | |||
| 713bd210ab | |||
| 61314eaf4e | |||
| bf9147733d | |||
| ce8457cbdb | |||
| df778c610b | |||
| d92132e721 | |||
| 99d4d02985 | |||
| e7dc6558c6 | |||
| 8c6a648aef | |||
| 0304391bc5 | |||
| 8de0ca60d1 | |||
| 43b9c524d9 | |||
| 3dc3fdf5cd | |||
| b7261a43f4 | |||
| 37fe1ae808 | |||
| 22d973fce6 | |||
| fe28610f21 | |||
| 476e69ee20 | |||
| 65993d6a13 | |||
| 39d2ece187 |
@@ -28,6 +28,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: install GNU patch on MacOS
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install gpatch
|
||||
- name: set up PATH on Windows
|
||||
# Needed to use GNU's patch.exe instead of Strawberry Perl patch
|
||||
if: runner.os == 'Windows'
|
||||
@@ -107,6 +110,10 @@ jobs:
|
||||
|
||||
- name: rust toolchain ~ install
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
- run: rustup component add llvm-tools-preview
|
||||
- name: install GNU patch on MacOS
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install gpatch
|
||||
- name: set up PATH on Windows
|
||||
# Needed to use GNU's patch.exe instead of Strawberry Perl patch
|
||||
if: runner.os == 'Windows'
|
||||
@@ -116,8 +123,9 @@ jobs:
|
||||
env:
|
||||
CARGO_INCREMENTAL: "0"
|
||||
RUSTC_WRAPPER: ""
|
||||
RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
||||
RUSTFLAGS: "-Cinstrument-coverage -Zcoverage-options=branch -Ccodegen-units=1 -Copt-level=0 -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
||||
RUSTDOCFLAGS: "-Cpanic=abort"
|
||||
LLVM_PROFILE_FILE: "diffutils-%p-%m.profraw"
|
||||
- name: "`grcov` ~ install"
|
||||
id: build_grcov
|
||||
shell: bash
|
||||
@@ -145,9 +153,9 @@ jobs:
|
||||
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
|
||||
mkdir -p "${COVERAGE_REPORT_DIR}"
|
||||
# display coverage files
|
||||
grcov . --output-type files --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique
|
||||
grcov . --output-type files --binary-path "${COVERAGE_REPORT_DIR}" | sort --unique
|
||||
# generate coverage report
|
||||
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
|
||||
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --binary-path "${COVERAGE_REPORT_DIR}" --branch
|
||||
echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT
|
||||
- name: Upload coverage results (to Codecov.io)
|
||||
uses: codecov/codecov-action@v4
|
||||
|
||||
@@ -41,6 +41,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
test-target:
|
||||
- { name: fuzz_cmp, should_pass: true }
|
||||
- { name: fuzz_cmp_args, should_pass: true }
|
||||
- { name: fuzz_ed, should_pass: true }
|
||||
- { name: fuzz_normal, should_pass: true }
|
||||
- { name: fuzz_patch, should_pass: true }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2022-2023, axodotdev
|
||||
# Copyright 2022-2024, axodotdev
|
||||
# SPDX-License-Identifier: MIT or Apache-2.0
|
||||
#
|
||||
# CI that:
|
||||
@@ -6,9 +6,9 @@
|
||||
# * checks for a Git Tag that looks like a release
|
||||
# * builds artifacts with cargo-dist (archives, installers, hashes)
|
||||
# * uploads those artifacts to temporary workflow zip
|
||||
# * on success, uploads the artifacts to a Github Release
|
||||
# * on success, uploads the artifacts to a GitHub Release
|
||||
#
|
||||
# Note that the Github Release will be created with a generated
|
||||
# Note that the GitHub Release will be created with a generated
|
||||
# title/body based on your changelogs.
|
||||
|
||||
name: Release
|
||||
@@ -31,7 +31,7 @@ permissions:
|
||||
# packages versioned/released in lockstep).
|
||||
#
|
||||
# If you push multiple tags at once, separate instances of this workflow will
|
||||
# spin up, creating an independent announcement for each one. However Github
|
||||
# spin up, creating an independent announcement for each one. However, GitHub
|
||||
# will hard limit this to 3 tags per commit, as it will assume more tags is a
|
||||
# mistake.
|
||||
#
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
# we specify bash to get pipefail; it guards against the `curl` command
|
||||
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
||||
shell: bash
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.12.0/cargo-dist-installer.sh | sh"
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.13.3/cargo-dist-installer.sh | sh"
|
||||
# sure would be cool if github gave us proper conditionals...
|
||||
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
|
||||
# functionality based on whether this is a pull_request, and whether it's from a fork.
|
||||
@@ -105,10 +105,15 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
|
||||
steps:
|
||||
- name: enable windows longpaths
|
||||
run: |
|
||||
git config --global core.longpaths true
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: swatinem/rust-cache@v2
|
||||
with:
|
||||
key: ${{ join(matrix.targets, '-') }}
|
||||
- name: Install cargo-dist
|
||||
run: ${{ matrix.install_dist }}
|
||||
# Get the dist-manifest
|
||||
@@ -135,7 +140,7 @@ jobs:
|
||||
run: |
|
||||
# Parse out what we just built and upload it to scratch storage
|
||||
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||
jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||
@@ -162,7 +167,7 @@ jobs:
|
||||
submodules: recursive
|
||||
- name: Install cargo-dist
|
||||
shell: bash
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.12.0/cargo-dist-installer.sh | sh"
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.13.3/cargo-dist-installer.sh | sh"
|
||||
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
|
||||
- name: Fetch local artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -178,7 +183,7 @@ jobs:
|
||||
|
||||
# Parse out what we just built and upload it to scratch storage
|
||||
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||
jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||
@@ -207,7 +212,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cargo-dist
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.12.0/cargo-dist-installer.sh | sh"
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.13.3/cargo-dist-installer.sh | sh"
|
||||
# Fetch artifacts from scratch-storage
|
||||
- name: Fetch artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -215,7 +220,7 @@ jobs:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
merge-multiple: true
|
||||
# This is a harmless no-op for Github Releases, hosting for that happens in "announce"
|
||||
# This is a harmless no-op for GitHub Releases, hosting for that happens in "announce"
|
||||
- id: host
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -230,7 +235,7 @@ jobs:
|
||||
name: artifacts-dist-manifest
|
||||
path: dist-manifest.json
|
||||
|
||||
# Create a Github Release while uploading all files to it
|
||||
# Create a GitHub Release while uploading all files to it
|
||||
announce:
|
||||
needs:
|
||||
- plan
|
||||
@@ -246,7 +251,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: "Download Github Artifacts"
|
||||
- name: "Download GitHub Artifacts"
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
@@ -256,7 +261,7 @@ jobs:
|
||||
run: |
|
||||
# Remove the granular manifests
|
||||
rm -f artifacts/*-dist-manifest.json
|
||||
- name: Create Github Release
|
||||
- name: Create GitHub Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ needs.plan.outputs.tag }}
|
||||
|
||||
Generated
+72
-47
@@ -34,13 +34,14 @@ checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "2.0.14"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8"
|
||||
checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"bstr",
|
||||
"doc-comment",
|
||||
"libc",
|
||||
"predicates",
|
||||
"predicates-core",
|
||||
"predicates-tree",
|
||||
@@ -122,11 +123,12 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||
|
||||
[[package]]
|
||||
name = "diffutils"
|
||||
version = "0.4.0"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"chrono",
|
||||
"diff",
|
||||
"itoa",
|
||||
"predicates",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
@@ -148,14 +150,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.1"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
@@ -189,6 +191,12 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
@@ -200,15 +208,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
version = "0.2.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -245,9 +253,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.1.0"
|
||||
version = "3.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8"
|
||||
checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"difflib",
|
||||
@@ -275,9 +283,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
|
||||
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
|
||||
dependencies = [
|
||||
"diff",
|
||||
"yansi",
|
||||
@@ -303,9 +311,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.4"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
||||
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -315,9 +323,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.5"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -326,21 +334,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.31"
|
||||
version = "0.38.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
|
||||
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -385,14 +393,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.1"
|
||||
version = "3.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
|
||||
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -409,9 +418,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
@@ -526,14 +535,24 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.2"
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d98532992affa02e52709d5b4d145a3668ae10d9081eea4a7f26f719a8476f71"
|
||||
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",
|
||||
@@ -542,48 +561,54 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7269c1442e75af9fa59290383f7665b828efc76c429cc0b7f2ecb33cf51ebae"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f70ab2cebf332b7ecbdd98900c2da5298a8c862472fb35c75fc297eabb9d89b8"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "679f235acf6b1639408c0f6db295697a19d103b0cdc88146aa1b992c580c647d"
|
||||
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.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3480ac194b55ae274a7e135c21645656825da4a7f5b6e9286291b2113c94a78b"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42c46bab241c121402d1cb47d028ea3680ee2f359dcc287482dcf7fdddc73363"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc885a4332ee1afb9a1bacf11514801011725570d35675abc229ce7e3afe4d20"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.1"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e440c60457f84b0bee09208e62acc7ade264b38c4453f6312b8c9ab1613e73c"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
+5
-4
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "diffutils"
|
||||
version = "0.4.0"
|
||||
version = "0.4.2"
|
||||
edition = "2021"
|
||||
description = "A CLI app for generating diff files"
|
||||
license = "MIT OR Apache-2.0"
|
||||
@@ -17,12 +17,13 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
chrono = "0.4.38"
|
||||
diff = "0.1.13"
|
||||
itoa = "1.0.11"
|
||||
regex = "1.10.4"
|
||||
same-file = "1.0.6"
|
||||
unicode-width = "0.1.11"
|
||||
unicode-width = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1"
|
||||
pretty_assertions = "1.4.0"
|
||||
assert_cmd = "2.0.14"
|
||||
predicates = "3.1.0"
|
||||
tempfile = "3.10.1"
|
||||
@@ -35,7 +36,7 @@ lto = "thin"
|
||||
# Config for 'cargo dist'
|
||||
[workspace.metadata.dist]
|
||||
# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
|
||||
cargo-dist-version = "0.12.0"
|
||||
cargo-dist-version = "0.13.3"
|
||||
# CI backends to support
|
||||
ci = ["github"]
|
||||
# The installers to generate for each app
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
Copyright (c) Michael Howell
|
||||
Copyright (c) uutils developers
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
Copyright (c) Michael Howell
|
||||
Copyright (c) uutils developers
|
||||
|
||||
Permission 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
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
[](https://codecov.io/gh/uutils/diffutils)
|
||||
|
||||
The goal of this package is to be a drop-in replacement for the [diffutils commands](https://www.gnu.org/software/diffutils/) in Rust.
|
||||
The goal of this package is to be a drop-in replacement for the [diffutils commands](https://www.gnu.org/software/diffutils/) (diff, cmp, diff3, sdiff) in Rust.
|
||||
|
||||
Based on the incomplete diff generator in https://github.com/rust-lang/rust/blob/master/src/tools/compiletest/src/runtest.rs, and made to be compatible with GNU's diff and patch tools.
|
||||
|
||||
|
||||
+13
-1
@@ -9,13 +9,25 @@ edition = "2018"
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.4"
|
||||
libfuzzer-sys = "0.4.7"
|
||||
diffutils = { path = "../" }
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_cmp"
|
||||
path = "fuzz_targets/fuzz_cmp.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_cmp_args"
|
||||
path = "fuzz_targets/fuzz_cmp_args.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_patch"
|
||||
path = "fuzz_targets/fuzz_patch.rs"
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
"-l"
|
||||
"--verbose"
|
||||
"-b"
|
||||
"--print-bytes"
|
||||
"-lb"
|
||||
"-bl"
|
||||
"-n"
|
||||
"--bytes"
|
||||
"--bytes="
|
||||
"--bytes=1024"
|
||||
"--bytes=99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
||||
"-i"
|
||||
"--ignore-initial"
|
||||
"--ignore-initial="
|
||||
"--ignore-initial=1024"
|
||||
"--ignore-initial=99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999:9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
||||
"-s"
|
||||
"-q"
|
||||
"--quiet"
|
||||
"--silent"
|
||||
"-"
|
||||
"--"
|
||||
"1kB"
|
||||
"1G"
|
||||
"1GB"
|
||||
"1T"
|
||||
"1TB"
|
||||
"1P"
|
||||
"1PB"
|
||||
"1Z"
|
||||
"1ZB"
|
||||
"1Y"
|
||||
"1YB"
|
||||
"1Y"
|
||||
"0"
|
||||
"1:2"
|
||||
@@ -0,0 +1,51 @@
|
||||
#![no_main]
|
||||
#[macro_use]
|
||||
extern crate libfuzzer_sys;
|
||||
use diffutilslib::cmp::{self, Cmp};
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
fn os(s: &str) -> OsString {
|
||||
OsString::from(s)
|
||||
}
|
||||
|
||||
fuzz_target!(|x: (Vec<u8>, Vec<u8>)| {
|
||||
let args = vec!["cmp", "-l", "-b", "target/fuzz.cmp.a", "target/fuzz.cmp.b"]
|
||||
.into_iter()
|
||||
.map(|s| os(s))
|
||||
.peekable();
|
||||
|
||||
let (from, to) = x;
|
||||
|
||||
File::create("target/fuzz.cmp.a")
|
||||
.unwrap()
|
||||
.write_all(&from)
|
||||
.unwrap();
|
||||
|
||||
File::create("target/fuzz.cmp.b")
|
||||
.unwrap()
|
||||
.write_all(&to)
|
||||
.unwrap();
|
||||
|
||||
let params =
|
||||
cmp::parse_params(args).unwrap_or_else(|e| panic!("Failed to parse params: {}", e));
|
||||
let ret = cmp::cmp(¶ms);
|
||||
if from == to && !matches!(ret, Ok(Cmp::Equal)) {
|
||||
panic!(
|
||||
"target/fuzz.cmp.a and target/fuzz.cmp.b are equal, but cmp returned {:?}.",
|
||||
ret
|
||||
);
|
||||
} else if from != to && !matches!(ret, Ok(Cmp::Different)) {
|
||||
panic!(
|
||||
"target/fuzz.cmp.a and target/fuzz.cmp.b are different, but cmp returned {:?}.",
|
||||
ret
|
||||
);
|
||||
} else if ret.is_err() {
|
||||
panic!(
|
||||
"target/fuzz.cmp.a and target/fuzz.cmp.b caused cmp to error ({:?}).",
|
||||
ret
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
#![no_main]
|
||||
#[macro_use]
|
||||
extern crate libfuzzer_sys;
|
||||
use diffutilslib::cmp;
|
||||
|
||||
use libfuzzer_sys::Corpus;
|
||||
use std::ffi::OsString;
|
||||
|
||||
fn os(s: &str) -> OsString {
|
||||
OsString::from(s)
|
||||
}
|
||||
|
||||
fuzz_target!(|x: Vec<OsString>| -> Corpus {
|
||||
if x.len() > 6 {
|
||||
// Make sure we try to parse an option when we get longer args. x[0] will be
|
||||
// the executable name.
|
||||
if ![os("-l"), os("-b"), os("-s"), os("-n"), os("-i")].contains(&x[1]) {
|
||||
return Corpus::Reject;
|
||||
}
|
||||
}
|
||||
let _ = cmp::parse_params(x.into_iter().peekable());
|
||||
Corpus::Keep
|
||||
});
|
||||
+1185
File diff suppressed because it is too large
Load Diff
+20
-20
@@ -439,26 +439,26 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/ab.diff"))
|
||||
File::create(format!("{target}/ab.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alef")).unwrap();
|
||||
fa.write_all(&alef[..]).unwrap();
|
||||
let mut fb = File::create(&format!("{target}/bet")).unwrap();
|
||||
let mut fb = File::create(format!("{target}/bet")).unwrap();
|
||||
fb.write_all(&bet[..]).unwrap();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg("--context")
|
||||
.stdin(File::open(&format!("{target}/ab.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/ab.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = fs::read(&format!("{target}/alef")).unwrap();
|
||||
let alef = fs::read(format!("{target}/alef")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
}
|
||||
}
|
||||
@@ -520,26 +520,26 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/ab_.diff"))
|
||||
File::create(format!("{target}/ab_.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef_")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alef_")).unwrap();
|
||||
fa.write_all(&alef[..]).unwrap();
|
||||
let mut fb = File::create(&format!("{target}/bet_")).unwrap();
|
||||
let mut fb = File::create(format!("{target}/bet_")).unwrap();
|
||||
fb.write_all(&bet[..]).unwrap();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg("--context")
|
||||
.stdin(File::open(&format!("{target}/ab_.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/ab_.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = fs::read(&format!("{target}/alef_")).unwrap();
|
||||
let alef = fs::read(format!("{target}/alef_")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
}
|
||||
}
|
||||
@@ -604,26 +604,26 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/abx.diff"))
|
||||
File::create(format!("{target}/abx.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefx")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefx")).unwrap();
|
||||
fa.write_all(&alef[..]).unwrap();
|
||||
let mut fb = File::create(&format!("{target}/betx")).unwrap();
|
||||
let mut fb = File::create(format!("{target}/betx")).unwrap();
|
||||
fb.write_all(&bet[..]).unwrap();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg("--context")
|
||||
.stdin(File::open(&format!("{target}/abx.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/abx.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = fs::read(&format!("{target}/alefx")).unwrap();
|
||||
let alef = fs::read(format!("{target}/alefx")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
}
|
||||
}
|
||||
@@ -691,26 +691,26 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
File::create(&format!("{target}/abr.diff"))
|
||||
File::create(format!("{target}/abr.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefr")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefr")).unwrap();
|
||||
fa.write_all(&alef[..]).unwrap();
|
||||
let mut fb = File::create(&format!("{target}/betr")).unwrap();
|
||||
let mut fb = File::create(format!("{target}/betr")).unwrap();
|
||||
fb.write_all(&bet[..]).unwrap();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg("--context")
|
||||
.stdin(File::open(&format!("{target}/abr.diff")).unwrap())
|
||||
.stdin(File::open(format!("{target}/abr.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = fs::read(&format!("{target}/alefr")).unwrap();
|
||||
let alef = fs::read(format!("{target}/alefr")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
}
|
||||
}
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
// This file is part of the uutils diffutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE-*
|
||||
// files that was distributed with this source code.
|
||||
|
||||
use crate::params::{parse_params, Format};
|
||||
use crate::utils::report_failure_to_read_input_file;
|
||||
use crate::{context_diff, ed_diff, normal_diff, unified_diff};
|
||||
use std::env::ArgsOs;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::iter::Peekable;
|
||||
use std::process::{exit, ExitCode};
|
||||
|
||||
// Exit codes are documented at
|
||||
// https://www.gnu.org/software/diffutils/manual/html_node/Invoking-diff.html.
|
||||
// An exit status of 0 means no differences were found,
|
||||
// 1 means some differences were found,
|
||||
// and 2 means trouble.
|
||||
pub fn main(opts: Peekable<ArgsOs>) -> ExitCode {
|
||||
let params = parse_params(opts).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
});
|
||||
// if from and to are the same file, no need to perform any comparison
|
||||
let maybe_report_identical_files = || {
|
||||
if params.report_identical_files {
|
||||
println!(
|
||||
"Files {} and {} are identical",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy(),
|
||||
);
|
||||
}
|
||||
};
|
||||
if params.from == "-" && params.to == "-"
|
||||
|| same_file::is_same_file(¶ms.from, ¶ms.to).unwrap_or(false)
|
||||
{
|
||||
maybe_report_identical_files();
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
|
||||
// read files
|
||||
fn read_file_contents(filepath: &OsString) -> io::Result<Vec<u8>> {
|
||||
if filepath == "-" {
|
||||
let mut content = Vec::new();
|
||||
io::stdin().read_to_end(&mut content).and(Ok(content))
|
||||
} else {
|
||||
fs::read(filepath)
|
||||
}
|
||||
}
|
||||
let mut io_error = false;
|
||||
let from_content = match read_file_contents(¶ms.from) {
|
||||
Ok(from_content) => from_content,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.from, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
let to_content = match read_file_contents(¶ms.to) {
|
||||
Ok(to_content) => to_content,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.to, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
if io_error {
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
|
||||
// run diff
|
||||
let result: Vec<u8> = match params.format {
|
||||
Format::Normal => normal_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Unified => unified_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Context => context_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Ed => ed_diff::diff(&from_content, &to_content, ¶ms).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
}),
|
||||
};
|
||||
if params.brief && !result.is_empty() {
|
||||
println!(
|
||||
"Files {} and {} differ",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy()
|
||||
);
|
||||
} else {
|
||||
io::stdout().write_all(&result).unwrap();
|
||||
}
|
||||
if result.is_empty() {
|
||||
maybe_report_identical_files();
|
||||
ExitCode::SUCCESS
|
||||
} else {
|
||||
ExitCode::from(1)
|
||||
}
|
||||
}
|
||||
+18
-18
@@ -225,13 +225,13 @@ mod tests {
|
||||
// This test diff is intentionally reversed.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff_w(&alef, &bet, &format!("{target}/alef")).unwrap();
|
||||
File::create(&format!("{target}/ab.ed"))
|
||||
File::create(format!("{target}/ab.ed"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alef")).unwrap();
|
||||
fa.write_all(&alef[..]).unwrap();
|
||||
let mut fb = File::create(&format!("{target}/bet")).unwrap();
|
||||
let mut fb = File::create(format!("{target}/bet")).unwrap();
|
||||
fb.write_all(&bet[..]).unwrap();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
@@ -239,14 +239,14 @@ mod tests {
|
||||
{
|
||||
use std::process::Command;
|
||||
let output = Command::new("ed")
|
||||
.arg(&format!("{target}/alef"))
|
||||
.stdin(File::open(&format!("{target}/ab.ed")).unwrap())
|
||||
.arg(format!("{target}/alef"))
|
||||
.stdin(File::open(format!("{target}/ab.ed")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = std::fs::read(&format!("{target}/alef")).unwrap();
|
||||
let alef = std::fs::read(format!("{target}/alef")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
}
|
||||
}
|
||||
@@ -299,13 +299,13 @@ mod tests {
|
||||
// This test diff is intentionally reversed.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff_w(&alef, &bet, &format!("{target}/alef_")).unwrap();
|
||||
File::create(&format!("{target}/ab_.ed"))
|
||||
File::create(format!("{target}/ab_.ed"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef_")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alef_")).unwrap();
|
||||
fa.write_all(&alef[..]).unwrap();
|
||||
let mut fb = File::create(&format!("{target}/bet_")).unwrap();
|
||||
let mut fb = File::create(format!("{target}/bet_")).unwrap();
|
||||
fb.write_all(&bet[..]).unwrap();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
@@ -313,14 +313,14 @@ mod tests {
|
||||
{
|
||||
use std::process::Command;
|
||||
let output = Command::new("ed")
|
||||
.arg(&format!("{target}/alef_"))
|
||||
.stdin(File::open(&format!("{target}/ab_.ed")).unwrap())
|
||||
.arg(format!("{target}/alef_"))
|
||||
.stdin(File::open(format!("{target}/ab_.ed")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = std::fs::read(&format!("{target}/alef_")).unwrap();
|
||||
let alef = std::fs::read(format!("{target}/alef_")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
}
|
||||
}
|
||||
@@ -379,13 +379,13 @@ mod tests {
|
||||
// This test diff is intentionally reversed.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff_w(&alef, &bet, &format!("{target}/alefr")).unwrap();
|
||||
File::create(&format!("{target}/abr.ed"))
|
||||
File::create(format!("{target}/abr.ed"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefr")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefr")).unwrap();
|
||||
fa.write_all(&alef[..]).unwrap();
|
||||
let mut fb = File::create(&format!("{target}/betr")).unwrap();
|
||||
let mut fb = File::create(format!("{target}/betr")).unwrap();
|
||||
fb.write_all(&bet[..]).unwrap();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
@@ -393,14 +393,14 @@ mod tests {
|
||||
{
|
||||
use std::process::Command;
|
||||
let output = Command::new("ed")
|
||||
.arg(&format!("{target}/alefr"))
|
||||
.stdin(File::open(&format!("{target}/abr.ed")).unwrap())
|
||||
.arg(format!("{target}/alefr"))
|
||||
.stdin(File::open(format!("{target}/abr.ed")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = std::fs::read(&format!("{target}/alefr")).unwrap();
|
||||
let alef = std::fs::read(format!("{target}/alefr")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod cmp;
|
||||
pub mod context_diff;
|
||||
pub mod ed_diff;
|
||||
pub mod macros;
|
||||
|
||||
+61
-77
@@ -3,14 +3,17 @@
|
||||
// For the full copyright and license information, please view the LICENSE-*
|
||||
// files that was distributed with this source code.
|
||||
|
||||
use crate::params::{parse_params, Format};
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::process::{exit, ExitCode};
|
||||
use std::{
|
||||
env::ArgsOs,
|
||||
ffi::{OsStr, OsString},
|
||||
iter::Peekable,
|
||||
path::{Path, PathBuf},
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
mod cmp;
|
||||
mod context_diff;
|
||||
mod diff;
|
||||
mod ed_diff;
|
||||
mod macros;
|
||||
mod normal_diff;
|
||||
@@ -18,79 +21,60 @@ mod params;
|
||||
mod unified_diff;
|
||||
mod utils;
|
||||
|
||||
// Exit codes are documented at
|
||||
// https://www.gnu.org/software/diffutils/manual/html_node/Invoking-diff.html.
|
||||
// An exit status of 0 means no differences were found,
|
||||
// 1 means some differences were found,
|
||||
// and 2 means trouble.
|
||||
/// # Panics
|
||||
/// Panics if the binary path cannot be determined
|
||||
fn binary_path(args: &mut Peekable<ArgsOs>) -> PathBuf {
|
||||
match args.peek() {
|
||||
Some(ref s) if !s.is_empty() => PathBuf::from(s),
|
||||
_ => std::env::current_exe().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// #Panics
|
||||
/// Panics if path has no UTF-8 valid name
|
||||
fn name(binary_path: &Path) -> &OsStr {
|
||||
binary_path.file_stem().unwrap()
|
||||
}
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
fn usage(name: &str) {
|
||||
println!("{name} {VERSION} (multi-call binary)\n");
|
||||
println!("Usage: {name} [function [arguments...]]\n");
|
||||
println!("Currently defined functions:\n");
|
||||
println!(" cmp, diff\n");
|
||||
}
|
||||
|
||||
fn second_arg_error(name: &OsStr) -> ! {
|
||||
eprintln!("Expected utility name as second argument, got nothing.");
|
||||
usage(&name.to_string_lossy());
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let opts = env::args_os();
|
||||
let params = parse_params(opts).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
});
|
||||
// if from and to are the same file, no need to perform any comparison
|
||||
let maybe_report_identical_files = || {
|
||||
if params.report_identical_files {
|
||||
println!(
|
||||
"Files {} and {} are identical",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy(),
|
||||
);
|
||||
}
|
||||
let mut args = std::env::args_os().peekable();
|
||||
|
||||
let exe_path = binary_path(&mut args);
|
||||
let exe_name = name(&exe_path);
|
||||
|
||||
let util_name = if exe_name == "diffutils" {
|
||||
// Discard the item we peeked.
|
||||
let _ = args.next();
|
||||
|
||||
args.peek()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| second_arg_error(exe_name))
|
||||
} else {
|
||||
OsString::from(exe_name)
|
||||
};
|
||||
if params.from == "-" && params.to == "-"
|
||||
|| same_file::is_same_file(¶ms.from, ¶ms.to).unwrap_or(false)
|
||||
{
|
||||
maybe_report_identical_files();
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
// read files
|
||||
fn read_file_contents(filepath: &OsString) -> io::Result<Vec<u8>> {
|
||||
if filepath == "-" {
|
||||
let mut content = Vec::new();
|
||||
io::stdin().read_to_end(&mut content).and(Ok(content))
|
||||
} else {
|
||||
fs::read(filepath)
|
||||
|
||||
match util_name.to_str() {
|
||||
Some("diff") => diff::main(args),
|
||||
Some("cmp") => cmp::main(args),
|
||||
Some(name) => {
|
||||
eprintln!("{}: utility not supported", name);
|
||||
ExitCode::from(2)
|
||||
}
|
||||
}
|
||||
let from_content = match read_file_contents(¶ms.from) {
|
||||
Ok(from_content) => from_content,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to read from-file: {e}");
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
};
|
||||
let to_content = match read_file_contents(¶ms.to) {
|
||||
Ok(to_content) => to_content,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to read to-file: {e}");
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
};
|
||||
// run diff
|
||||
let result: Vec<u8> = match params.format {
|
||||
Format::Normal => normal_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Unified => unified_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Context => context_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Ed => ed_diff::diff(&from_content, &to_content, ¶ms).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
}),
|
||||
};
|
||||
if params.brief && !result.is_empty() {
|
||||
println!(
|
||||
"Files {} and {} differ",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy()
|
||||
);
|
||||
} else {
|
||||
io::stdout().write_all(&result).unwrap();
|
||||
}
|
||||
if result.is_empty() {
|
||||
maybe_report_identical_files();
|
||||
ExitCode::SUCCESS
|
||||
} else {
|
||||
ExitCode::from(1)
|
||||
None => second_arg_error(exe_name),
|
||||
}
|
||||
}
|
||||
|
||||
+24
-24
@@ -275,26 +275,26 @@ mod tests {
|
||||
// This test diff is intentionally reversed.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff(&alef, &bet, &Params::default());
|
||||
File::create(&format!("{target}/ab.diff"))
|
||||
File::create(format!("{target}/ab.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alef")).unwrap();
|
||||
fa.write_all(&alef[..]).unwrap();
|
||||
let mut fb = File::create(&format!("{target}/bet")).unwrap();
|
||||
let mut fb = File::create(format!("{target}/bet")).unwrap();
|
||||
fb.write_all(&bet[..]).unwrap();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg(&format!("{target}/alef"))
|
||||
.stdin(File::open(&format!("{target}/ab.diff")).unwrap())
|
||||
.arg(format!("{target}/alef"))
|
||||
.stdin(File::open(format!("{target}/ab.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = fs::read(&format!("{target}/alef")).unwrap();
|
||||
let alef = fs::read(format!("{target}/alef")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
}
|
||||
}
|
||||
@@ -367,27 +367,27 @@ mod tests {
|
||||
// This test diff is intentionally reversed.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff(&alef, &bet, &Params::default());
|
||||
File::create(&format!("{target}/abn.diff"))
|
||||
File::create(format!("{target}/abn.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefn")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefn")).unwrap();
|
||||
fa.write_all(&alef[..]).unwrap();
|
||||
let mut fb = File::create(&format!("{target}/betn")).unwrap();
|
||||
let mut fb = File::create(format!("{target}/betn")).unwrap();
|
||||
fb.write_all(&bet[..]).unwrap();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg("--normal")
|
||||
.arg(&format!("{target}/alefn"))
|
||||
.stdin(File::open(&format!("{target}/abn.diff")).unwrap())
|
||||
.arg(format!("{target}/alefn"))
|
||||
.stdin(File::open(format!("{target}/abn.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = fs::read(&format!("{target}/alefn")).unwrap();
|
||||
let alef = fs::read(format!("{target}/alefn")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
}
|
||||
}
|
||||
@@ -441,26 +441,26 @@ mod tests {
|
||||
// This test diff is intentionally reversed.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff(&alef, &bet, &Params::default());
|
||||
File::create(&format!("{target}/ab_.diff"))
|
||||
File::create(format!("{target}/ab_.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alef_")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alef_")).unwrap();
|
||||
fa.write_all(&alef[..]).unwrap();
|
||||
let mut fb = File::create(&format!("{target}/bet_")).unwrap();
|
||||
let mut fb = File::create(format!("{target}/bet_")).unwrap();
|
||||
fb.write_all(&bet[..]).unwrap();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg(&format!("{target}/alef_"))
|
||||
.stdin(File::open(&format!("{target}/ab_.diff")).unwrap())
|
||||
.arg(format!("{target}/alef_"))
|
||||
.stdin(File::open(format!("{target}/ab_.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = fs::read(&format!("{target}/alef_")).unwrap();
|
||||
let alef = fs::read(format!("{target}/alef_")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
}
|
||||
}
|
||||
@@ -519,26 +519,26 @@ mod tests {
|
||||
// This test diff is intentionally reversed.
|
||||
// We want it to turn the alef into bet.
|
||||
let diff = diff(&alef, &bet, &Params::default());
|
||||
File::create(&format!("{target}/abr.diff"))
|
||||
File::create(format!("{target}/abr.diff"))
|
||||
.unwrap()
|
||||
.write_all(&diff)
|
||||
.unwrap();
|
||||
let mut fa = File::create(&format!("{target}/alefr")).unwrap();
|
||||
let mut fa = File::create(format!("{target}/alefr")).unwrap();
|
||||
fa.write_all(&alef[..]).unwrap();
|
||||
let mut fb = File::create(&format!("{target}/betr")).unwrap();
|
||||
let mut fb = File::create(format!("{target}/betr")).unwrap();
|
||||
fb.write_all(&bet[..]).unwrap();
|
||||
let _ = fa;
|
||||
let _ = fb;
|
||||
let output = Command::new("patch")
|
||||
.arg("-p0")
|
||||
.arg(&format!("{target}/alefr"))
|
||||
.stdin(File::open(&format!("{target}/abr.diff")).unwrap())
|
||||
.arg(format!("{target}/alefr"))
|
||||
.stdin(File::open(format!("{target}/abr.diff")).unwrap())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
||||
let alef = fs::read(&format!("{target}/alefr")).unwrap();
|
||||
let alef = fs::read(format!("{target}/alefr")).unwrap();
|
||||
assert_eq!(alef, bet);
|
||||
}
|
||||
}
|
||||
|
||||
+467
-99
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user