GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — diff - `-p`/`-F` function line is cut at 40 characters, not bytes, and keeps a trailing `\v` · Issue #359 · uutils/diffutils

diff - -p/-F function line is cut at 40 characters, not bytes, and keeps a trailing \v #359

New issue
Copy link
New issue
Copy link
Open
#375
Open
diff - -p/-F function line is cut at 40 characters, not bytes, and keeps a trailing \v#359
#375
Copy link
Labels
bugSomething isn't workingSomething isn't workinggnu-compatBehaviour differs from GNUBehaviour differs from GNUgood first issueGood for newcomersGood for newcomersoss-scannerFound by Anthropic's OSS ScannerFound by Anthropic's OSS Scanner

Description

@sylvestre
sylvestre
opened on Nov 9, 2026
Issue body actions

GNU diff shows at most 40 bytes of the matching function line after the @@/****
header, and then strips trailing whitespace with isspace(), which includes vertical tab
(\v). uutils counts 40 Unicode characters, so lines with multi-byte characters
come out much longer. It also trims with trim_ascii_end, which does not remove \v.

Reproduction

python3 -c "open('fn1','w').write('func_' + 'é'*40 + '()\n1\n2\n3\n4\n5\n6\n')"
sed 's/^5$/X/' fn1 > fn2
printf 'fg()\v\n1\n2\n3\n4\n5\n6\n' > fv1; sed 's/^5$/X/' fv1 > fv2

Rust (incorrect)

$ LC_ALL=C ./target/release/diffutils diff -u -p fn1 fn2 | grep '^@@' | cat -v
@@ -3,5 +3,5 @@ func_M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)
$ ./target/release/diffutils diff -u -F '^f' fv1 fv2 | grep '^@@' | cat -A
@@ -3,5 +3,5 @@ fg()^K$
# Exit code (diff): 1 in both cases

GNU (correct)

$ LC_ALL=C diff -u -p fn1 fn2 | grep '^@@' | cat -v
@@ -3,5 +3,5 @@ func_M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-CM-)M-C
$ LC_ALL=C diff -u -F '^f' fv1 fv2 | grep '^@@' | cat -A
@@ -3,5 +3,5 @@ fg()$
# Exit code (diff): 1 in both cases

GNU cuts at exactly 40 bytes, even in the middle of a UTF-8 sequence (same result with
LC_ALL=C.UTF-8).

Root cause

FunctionLineFinder::find in src/utils.rs:212-220:

let line = self.last_match?.trim_ascii_start();
let end = match std::str::from_utf8(line) {
    Ok(text) => text
        .char_indices()
        .nth(Self::MAX_WIDTH)
        .map_or(line.len(), |(end, _)| end),
    Err(_) => line.len().min(Self::MAX_WIDTH),
};
Some(line[..end].trim_ascii_end().to_vec())

Test

Add to tests/by-util/test_diff.rs:

#[test]
fn function_line_trailing_vertical_tab_is_trimmed() -> Result<(), Box<dyn std::error::Error>> {
    let mut old = NamedTempFile::new()?;
    old.write_all(b"fg()\x0b\n1\n2\n3\n4\n5\n6\n")?;
    let mut new = NamedTempFile::new()?;
    new.write_all(b"fg()\x0b\n1\n2\n3\n4\nX\n6\n")?;
    let mut cmd = cargo_bin_cmd!("diffutils");
    cmd.arg("diff").arg("-u").arg("-F").arg("^f").arg(old.path()).arg(new.path());
    cmd.assert()
        .code(predicate::eq(1))
        .stdout(predicate::str::contains("@@ -3,5 +3,5 @@ fg()\n"));
    Ok(())
}

plus a case with a 40+-byte UTF-8 function line asserting the header suffix is exactly
40 bytes.

Reported by Anthropic's OSS Scanner (ANT-2026-Y18VUFCM).

Activity

  1. sylvestre
    added
    bugSomething isn't workingSomething isn't working
    good first issueGood for newcomersGood for newcomers
    oss-scannerFound by Anthropic's OSS ScannerFound by Anthropic's OSS Scanner
    gnu-compatBehaviour differs from GNUBehaviour differs from GNU
    on Nov 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingSomething isn't workinggnu-compatBehaviour differs from GNUBehaviour differs from GNUgood first issueGood for newcomersGood for newcomersoss-scannerFound by Anthropic's OSS ScannerFound by Anthropic's OSS Scanner

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.