Skip to content

Remove clap for echo #7603

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 4, 2025
Merged

Remove clap for echo #7603

merged 11 commits into from
May 4, 2025

Conversation

cerdelen
Copy link
Contributor

fixes #7595

removed clap as a parsing tool for echo. Added some tests from previous issues comment sections regarding double hyphen problematics.

Copy link

GNU testsuite comparison:

Skipping an intermittent issue tests/misc/stdbuf (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/misc/usage_vs_getopt (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/timeout/timeout (passes in this run but fails in the 'main' branch)

Copy link

GNU testsuite comparison:

Skipping an intermittent issue tests/misc/stdbuf (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/misc/usage_vs_getopt (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/timeout/timeout (passes in this run but fails in the 'main' branch)

@sylvestre sylvestre requested a review from Copilot March 28, 2025 21:18
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR removes the dependency on clap for echo’s argument parsing and introduces a custom flag parsing logic alongside additional tests to verify handling of double hyphen and flag-like arguments.

  • Removed clap-specific parsing code and replaced it with custom logic in echo.rs
  • Updated the POSIXLY_CORRECT check to compare against "1"
  • Added new tests in tests/by-util/test_echo.rs for various argument scenarios

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
tests/by-util/test_echo.rs Added tests covering parsing of flags and double hyphen cases
src/uu/echo/src/echo.rs Removed clap usage; implemented custom parsing and updated env var check
Comments suppressed due to low confidence (2)

tests/by-util/test_echo.rs:296

  • This test case omits a trailing newline in its expected output, which is inconsistent with similar tests that include a newline. Please verify whether a trailing newline should be expected here.
.stdout_only("- --");

src/uu/echo/src/echo.rs:101

  • Changing the POSIXLY_CORRECT check from verifying the existence of the variable to comparing its value to "1" may affect behavior if the variable is set to any truthy value other than "1". Please confirm that this change aligns with the intended behavior.
posixly_correct == "1"

fn is_echo_flag(arg: &OsString) -> bool {
matches!(arg.to_str(), Some("-e" | "-E" | "-n"))
struct EchoFlags {
pub n: bool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please make this more explicit

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this better?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes for the name but your comments are a bit hard to understand IMHO :)

Copy link

GNU testsuite comparison:

Skip an intermittent issue tests/misc/stdbuf (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/timeout/timeout (fails in this run but passes in the 'main' branch)

Copy link

GNU testsuite comparison:

Skip an intermittent issue tests/timeout/timeout (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/misc/stdbuf (passes in this run but fails in the 'main' branch)

}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let is_posixly_correct = env::var("POSIXLY_CORRECT").is_ok();
// Check POSIX compatibility mode
let is_posixly_correct = if let Ok(posixly_correct) = env::var("POSIXLY_CORRECT") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain this change. As far as I know GNU checks whether the env var is defined not whether it's 1. Is echo an exception?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't sure about this. I checked in the test files and there it was set to be equal to one.

If the POSIXLY_CORRECT environment variable is set found this on gnu.org on echo man pages. So i just need to check if its set at all, not whether it is set to any true value like 1 or true?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted the change to now only check if it is defined at all

Copy link

GNU testsuite comparison:

Skip an intermittent issue tests/misc/stdbuf (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/timeout/timeout (passes in this run but fails in the 'main' branch)

@cerdelen cerdelen requested a review from tertsdiepraam March 29, 2025 12:37
Copy link
Contributor

@blyxxyz blyxxyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I think this behavior is exactly correct.

Comment on lines 48 to 58
if bytes.first() == Some(&b'-') {
let mut flags = EchoFlags {
disable_newline: false,
escape: false,
is_single_hyphen: false,
};
// Single hyphen case: "-" (stops flag processing, no other effect)
if arg.len() == 1 {
flags.is_single_hyphen = true;
return Some(flags);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if bytes.first() == Some(&b'-') {
let mut flags = EchoFlags {
disable_newline: false,
escape: false,
is_single_hyphen: false,
};
// Single hyphen case: "-" (stops flag processing, no other effect)
if arg.len() == 1 {
flags.is_single_hyphen = true;
return Some(flags);
}
if bytes.first() == Some(&b'-') && arg != "-" {
let mut flags = EchoFlags {
disable_newline: false,
escape: false,
};

Would this work? I think the is_single_hyphen case is identical to the None case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also correct. Changed this part too. Thanks!

if echo_flags.disable_newline {
trailing_newline = false;
}
escape = echo_flags.escape;
Copy link
Contributor

@blyxxyz blyxxyz Mar 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too aggressive since it means -e -n disables escape. Maybe is_echo_flag can mutably borrow a single EchoFlags value that's reused and returned?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great observation. Indeed this was an issue. I fixed this and also added some test as there was no test in the test suite so far to check for this behaviour.

Copy link

GNU testsuite comparison:

Skip an intermittent issue tests/timeout/timeout (fails in this run but passes in the 'main' branch)

Enabling POSIXLY_CORRECT flag if value is not UTF-8

Co-authored-by: Jan Verbeek <jan.verbeek@posteo.nl>
Copy link

GNU testsuite comparison:

Skipping an intermittent issue tests/timeout/timeout (passes in this run but fails in the 'main' branch)

@sylvestre sylvestre requested a review from BenWiederhake April 17, 2025 19:48
Copy link
Collaborator

@BenWiederhake BenWiederhake left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! I can't check right now whether this is truly equal to the GNU behavior, but it seems sound to me. Feel free to address the nitpick or not :)

}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let is_posixly_correct = env::var("POSIXLY_CORRECT").is_ok();
// Check POSIX compatibility mode
let is_posixly_correct = env::var_os("POSIXLY_CORRECT").is_some();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a subtle change in behavior here: POSIXLY_CORRECT=<non-utf8-sequence> is now interpreted as true, whereas before it was interpreted as false. I assume this is a correct change. (I can't test GNU easily right now.)

$ POSIXLY_CORRECT=$'\377asd' cargo run echo 'asd\nqwe' # current main
asd\nqwe
$ POSIXLY_CORRECT=$'\377asd' cargo run echo 'asd\nqwe' # your PR
asd
qwe

Nitpick: If you fix subtleties like this, it would be extra awesome if you also add a test, so that we don't lose it later :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggested that change upthread when I saw this code at the edge of a diff 😅

The following test (in test_echo.rs inside mod posixly_correct {}) should do the trick:

    #[cfg(unix)]
    #[test]
    fn non_utf8_variable() {
        use std::{ffi::OsStr, os::unix::ffi::OsStrExt};

        new_ucmd!()
            .env("POSIXLY_CORRECT", OsStr::from_bytes(b"\xFF"))
            .arg("-e")
            .succeeds()
            .stdout_only("-e\n");
    }

(I can confirm that this matches GNU, GNU activates POSIX mode regardless of whether the variable's value is 1 or 0 or empty string or \xFF.)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! But I would argue that if you change the behavior, then an old test cannot possibly correctly test for the new behavior :D

In particular, this test only tests whether -e is interpreted or considered as part of the output. It does not test whether escape sequences are interpreted or taken literally, i.e. whether "escape mode" is initially set or not.

Like I said, it's not a major thing, and doesn't block this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not an old test, I meant that was where I'd put it. It fails if env::var(...).is_ok() is used.

Copy link
Member

@tertsdiepraam tertsdiepraam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work!

@BenWiederhake since I baked this behaviour into uutils-args, this is a good candidate to port next. But nevertheless, it's great to have this implementation (and tests!) first.

We could also decide to take the behaviour out of uutils-args to simplify it.

@BenWiederhake
Copy link
Collaborator

@BenWiederhake since I baked this behaviour into uutils-args, this is a good candidate to port next. But nevertheless, it's great to have this implementation (and tests!) first.

I agree! The reason I tackled du -B first was that I didn't want to put this PR into cross-fire.

Btw, can't be merged currently due to merge conflicts.

@sylvestre sylvestre merged commit 13c0a81 into uutils:main May 4, 2025
68 checks passed
RenjiSann pushed a commit to RenjiSann/coreutils that referenced this pull request May 23, 2025
* Parsing echo flags manually without clap as clap introduced various problematic interactions with hyphens

* fixed error where multiple flags would parse wrong

* Spelling & formatting fixes

* docu for EchoFlag struct

* more extensive comment/documentation

* revert POSIXLY_CORRECT check to only check if it is set

* Fixed problem of overwriting flags. Added test for same issue

* cargo fmt

* cspell

* Update src/uu/echo/src/echo.rs

Enabling POSIXLY_CORRECT flag if value is not UTF-8

Co-authored-by: Jan Verbeek <jan.verbeek@posteo.nl>

---------

Co-authored-by: Jan Verbeek <jan.verbeek@posteo.nl>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

remove clap as a parser for echo flags as it creates problems with hyphens
5 participants