Skip to content

Convert newlines in text mode on Windows through the transcoder - #9707

Open
aminmansuri wants to merge 9 commits into
jruby:jruby-10.0from
aminmansuri:fix-windows-text-mode-newlines
Open

aminmansuri wants to merge 9 commits into
jruby:jruby-10.0from
aminmansuri:fix-windows-text-mode-newlines

Conversation

@aminmansuri

@aminmansuri aminmansuri commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Changes (Windows only):

  1. Reading: text-mode IO's read converter gets the universal-newline decorator, so \r\n becomes \n.
  2. Writing: CRLF decorator no longer masked out of the write-conversion check, so \n becomes \r\n.
  3. Defaults: popen pipes get the default text mode, as in CRuby. Also, new ARGF instance starts in text mode instead of inheriting a previous binmode.

Refs #9067 (the earlier approach) and #3742.

ruby/spec examples for CRLF on write and universal newline on read in text
mode, JUnit coverage of the decorator selection with the platform passed in,
and the Windows spec tags those examples replace. Red until the fix lands.
The JDK has no O_TEXT descriptors, so the default text mode MRI leaves to
the C runtime did nothing here: text-mode reads now get the universal
newline decorator, writes the CRLF one, and popen pipes the default text mode.
ARGF.binmode left the shared ARGF state in binmode for every later
ARGF.class.new, which MRI does not do; on Windows that hid text mode from
the ARGF specs. The File::BINARY flag check moves to the Windows tests.
The runner checks fixtures out with CRLF, so specs built on LF byte
offsets and lengths fail there for MRI as well; the ARGF byte readers
(each_byte, each_char, getc, readpartial) stay raw and are tagged too.
Its shared example is described as ARGF.getc, so the tag lives in the
readchar tags file; the reader stays byte-level like ARGF.getc.
MRI's rb_w32_write only lets the C runtime insert CRs on a file or on
stdout/stderr, so the default CRLF marker alone must not select the write
converter on a pipe.
MRI's _write does not count the CRs it inserts.
System.console() is non-null on JDK 22-24 when the standard streams are
redirected.

@headius headius left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The logic looks good in a first review pass but I need some changes to fully approve it.

Along with the changes in review comments, rebase this on top of master and recent changes there (which includes a bunch of tag removals). We won't make this big of a change in the 10.0 cycle.


// Poor man's isatty for stdio on Windows (jruby/jruby#3292). System.console() is non-null on JDK 22-24
// even when the standard streams are redirected; Console#isTerminal (JDK 22+) tells the cases apart.
static boolean stdioIsConsole() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I believe we have similar code elsewhere for calling isTerminal. Unify.


// MRI: part of prep_stdio
private static void prepStdioEcflags(OpenFile fptr, int fmode) {
// MRI: the newline decorators prep_stdio and pipe_open give an IO in the default text mode

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

the "MRI: ..." comments are meant to connect this implementation with equivalent code in CRuby (see #9782 for a new Java annotation for this purpose). The additional comments added here should instead be part of javadoc for this method.

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