Skip to content

Map /dev/null to the Windows null device - #568

Merged
bertysentry merged 2 commits into
mainfrom
567-windows-devnull-is-opened-as-a-regular-file-instead-of-the-null-device
Aug 18, 2026
Merged

Map /dev/null to the Windows null device#568
bertysentry merged 2 commits into
mainfrom
567-windows-devnull-is-opened-as-a-regular-file-instead-of-the-null-device

Conversation

@bertysentry

Copy link
Copy Markdown
Contributor

Closes #567.

Problem

On Windows, /dev/null was opened as the relative path \dev\null:

  • where a dev directory happened to exist on the current drive (as it does whenever sources live under C:\Dev), a redirection created and truncated dev\null and kept the output the script meant to discard, and a getline read back whatever that file held;
  • where it did not exist, the redirection was a fatal error and getline aborted the script.

/dev/null is not one of the four special filenames gawk documents under Special Files (those were #556), but it is a real device on every Unix system, so > "/dev/null" is a common idiom in portable AWK scripts, and gawk's Windows port maps the name to NUL.

Change

JRT translates the name at the three places a script-supplied filename is opened — the output sink, getline < file, and the ARGV file list — so on Windows:

  • print > "/dev/null" and printf > "/dev/null" discard their output and create no file;
  • getline < "/dev/null" reports end of input (0);
  • close("/dev/null") succeeds;
  • /dev/null as an operand reads as an empty input file.

Only the name is translated. Redirections stay keyed by the spelling the script used, so close("/dev/null") still reports the redirection it opened, and >/>> remain ordinary opens of a real device. Like gawk, only the Unix spelling is translated: Windows already opens NUL under its own name.

The per-file main input loop that BEGINFILE/ENDFILE select stats each operand before opening it, and Windows does not report its null device as an existing file, so that loop now recognizes the name instead of reporting No such file or directory in ERRNO.

On POSIX platforms nothing changes: the name is the device already.

Verification

  • New NullDeviceTest (10 tests, asserting on both platforms, no posixOnly()): output discarded, nothing readable back, >> accumulates nothing, getline reports end of input, close() semantics, /dev/null as a lone operand and between two real files, BEGINFILE/ENDFILE seeing it as a readable empty file, and one test pinning that no regular file is created. 8 of the 10 fail on Windows without the fix.
  • All expectations were checked against gawk 5.0.0 first.
  • mvn clean verify site: 800 unit tests pass; checkstyle, PMD, and SpotBugs report zero findings; no new Javadoc warnings.
  • Compatibility suites: GawkIT.test_delargv and GawkIT.test_iobug1 now pass. Both fail with FileNotFoundException: \dev\null without this change (verified after removing the stray C:\dev\null that the bug itself had left behind on the test machine — with that file present, the two tests passed for the wrong reason). Totals are otherwise unchanged.

🤖 Generated with Claude Code

On Windows, /dev/null was opened as the relative path \dev\null. Where a dev
directory happened to exist on the current drive — as it does whenever sources
live under C:\Dev — a redirection created and truncated dev\null and kept the
output the script meant to discard, and a getline read whatever that file held.
Where it did not exist, the redirection was a fatal error and getline aborted
the script. /dev/null is not one of the four special filenames gawk documents,
but it is a real device on every Unix system, so "> /dev/null" is a common
idiom in portable scripts, and gawk's Windows port maps the name to NUL.

JRT now translates the name where a redirection, a getline, or an ARGV operand
is opened, so print and printf discard their output and create no file, getline
reports end of input, close() succeeds, and /dev/null as an operand reads as an
empty input file. Only the name is translated: redirections stay keyed by the
spelling the script used, so close("/dev/null") still reports the redirection it
opened. The per-file main input loop that BEGINFILE and ENDFILE select stats
each operand before opening it, and Windows does not report its null device as
an existing file, so that loop now recognizes the name instead of concluding
that the file is missing.

This fixes GawkIT.test_delargv and GawkIT.test_iobug1, which both failed with
FileNotFound \dev\null on a machine with no C:\dev directory.

Closes #567

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@bertysentry

Copy link
Copy Markdown
Contributor Author

@codex please review

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eec0f0b752

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/main/java/io/jawk/jrt/StreamInputSource.java
From the Codex review. The per-file main input loop that BEGINFILE, ENDFILE and
nextfile select stats each operand before opening it, and Windows reports its
null device as nonexistent under either spelling, so an operand named NUL was
still reported as a missing file there — while the plain main input loop, which
opens the operand without stat'ing it first, read it happily.

isNullDeviceName now answers for both names the platform accepts, so the bypass
covers NUL on Windows. The translation itself stays gawk-exact: only /dev/null
is rewritten, since Windows already opens NUL as the device.

AwkTestSupport gains windowsOnly() for the tests this needs, the mirror of the
existing posixOnly(): the boolean POSIX flag becomes a PlatformRequirement, so
the two gates share one code path and one skip message.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@bertysentry

Copy link
Copy Markdown
Contributor Author

@codex please review again

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 6c9c3b23f1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@bertysentry
bertysentry merged commit 03021f7 into main Aug 18, 2026
5 checks passed
@bertysentry
bertysentry deleted the 567-windows-devnull-is-opened-as-a-regular-file-instead-of-the-null-device branch August 18, 2026 09:57
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.

Windows: /dev/null is opened as a regular file instead of the null device

1 participant