Skip to content

Fix multiple write commands to same file - #532

Open
dspinellis wants to merge 1 commit into
uutils:W-commandfrom
dspinellis:multiple-writers
Open

Fix multiple write commands to same file#532
dspinellis wants to merge 1 commit into
uutils:W-commandfrom
dspinellis:multiple-writers

Conversation

@dspinellis

Copy link
Copy Markdown
Collaborator

Maintaining different writers for the same path can fail due to buffering
or underlying OS restrictions. In particular, this fails on Windows.
Fix it through a map of canonicalized write paths.

Maintining different writers for the same path can fail due to buffering
or underlying OS restrictions. In particular, this fails on Windows.
Fix it through a map of canonicalized write paths.
Copilot AI lite review requested due to automatic review settings August 20, 2026 07:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

This PR fixes failures caused by creating multiple independent writers targeting the same output path (notably on Windows) by reusing writers keyed by a canonicalized path, and adds support/tests for the non-POSIX W command (write first line of pattern space).

Changes:

  • Reuse NamedWriter instances via a thread-local map keyed by canonicalized output paths.
  • Update write APIs to optionally append a trailing newline based on input termination, and implement W runtime handling.
  • Expand test coverage for w/W behaviors (sandbox rejection, multiple writes to same file, newline/no-newline cases).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/by-util/test_sed.rs Adds regression + behavior tests for w/W, sandbox behavior, newline handling, and Windows multi-writer failure prevention.
src/sed/processor.rs Updates w and s///w writing to respect newline-termination; adds W command execution logic.
src/sed/named_writer.rs Introduces canonical-path writer reuse via HashMap, optional-newline write API, and new unit tests.
src/sed/compiler.rs Enables W (non-POSIX) command compilation.
README.md Documents the new W command behavior.
Suppressed comments (1)

src/sed/named_writer.rs:136

  • Iterating HashMap::values() makes flush_all() traversal order non-deterministic. If multiple flushes could fail (e.g., due to OS-level I/O errors), which file’s error is returned first can vary run-to-run, making operational debugging and tests that assert specific failures less stable. If deterministic behavior matters here, consider iterating in a stable order (e.g., collect keys and sort, or store writers in an order-preserving structure for flushing).
/// Flush buffered content to all open files, returning descriptive errors.
pub fn flush_all() -> UResult<()> {
    WRITERS.with(|writers| {
        for writer in writers.borrow().values() {
            writer.borrow_mut().flush()?;
        }

        Ok(())
    })
}

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/sed/named_writer.rs
let canonical_path = canonicalize_output_path(&path, &location)?;

if let Some(writer) = WRITERS.with(|writers| writers.borrow().get(&canonical_path).cloned())
{
Comment thread tests/by-util/test_sed.rs
Comment on lines +1704 to +1716
#[test]
fn sandbox_rejects_first_line_write_command() -> std::io::Result<()> {
let temp = NamedTempFile::new()?;
let cmd = format!("W {}", temp.path().display());

new_ucmd!()
.args(&["--sandbox", &cmd, LINES1])
.fails()
.stderr_contains("command not allowed with --sandbox");

let mut actual = String::new();
temp.reopen()?.read_to_string(&mut actual)?;
assert!(actual.is_empty());
Comment thread README.md
* An `F` command outputs the name of the file currently being processed.
* A `Q` command (optionally followed by an exit code) quits immediately.
* The `q` command can be optionally followed by an exit code.
* A `W` command writes to a file the pattern's first line.
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.00000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.12%. Comparing base (714ec8a) to head (c8b5dc5).

Files with missing lines Patch % Lines
src/sed/named_writer.rs 88.00% 6 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           W-command     #532      +/-   ##
=============================================
+ Coverage      83.01%   83.12%   +0.11%     
=============================================
  Files             13       13              
  Lines           7037     7083      +46     
  Branches         401      401              
=============================================
+ Hits            5842     5888      +46     
  Misses          1192     1192              
  Partials           3        3              
Flag Coverage Δ
macos_latest 83.76% <88.00%> (+0.10%) ⬆️
ubuntu_latest 83.96% <88.00%> (+0.10%) ⬆️
windows_latest 0.00% <0.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dspinellis
dspinellis changed the base branch from main to W-command August 20, 2026 15:17
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.

2 participants