From 31371b2e24b03ccb0a03b622faf8c65e6cf6a31a Mon Sep 17 00:00:00 2001 From: KJ Tsanaktsidis Date: Thu, 28 Dec 2023 16:08:54 +1100 Subject: [PATCH] Fix CRLF -> LF conversion on read for rb_io_fdopen & rb_file_open When opening a file with `File.open`, and then setting the encoding with `IO#set_encoding`, it still correctly performs CRLF -> LF conversion on Windows when reading files with a CRLF line ending in them (in text mode). However, the file is opened instead with either the `rb_io_fdopen` or `rb_file_open` APIs from C, the CRLF conversion is _NOT_ set up correctly; it works if the encoding is not specified, but if `IO#set_encoding` is called, the conversion stops happening. This seems to be because the encflags never get ECONV_DEFAULT_NEWLINE_DECORATOR set in these codepaths. Concretely, this means that the conversion doesn't happen in the following circumstances: * When loading ruby files with require (that calls rb_io_fdopen) * When parsing ruuby files with RubyVM::AbstractSyntaxTree (that calls rb_file_open). This then causes the ErrorHighlight tests to fail on windows if git has checked them out with CRLF line endings - the error messages it's testing wind up with literal \r\n sequences in them because the iseq text from the parser contains un-newline-converted strings. This commit fixes the problem by copy-pasting the relevant snippet which sets this up in `rb_io_extract_modeenc` (for the File.open path) into the relevant codepaths for `rb_io_fdopen` and `rb_file_open`. [Bug #20101] --- ext/-test-/file/depend | 171 +++++++++++++++++++++++ ext/-test-/file/newline_conv.c | 73 ++++++++++ io.c | 33 ++++- test/ruby/test_file.rb | 246 +++++++++++++++++++++++++++++++++ 4 files changed, 518 insertions(+), 5 deletions(-) create mode 100644 ext/-test-/file/newline_conv.c diff --git a/ext/-test-/file/depend b/ext/-test-/file/depend index 662136f1ba..2c8a04e433 100644 --- a/ext/-test-/file/depend +++ b/ext/-test-/file/depend @@ -329,6 +329,177 @@ init.o: $(hdrdir)/ruby/ruby.h init.o: $(hdrdir)/ruby/st.h init.o: $(hdrdir)/ruby/subst.h init.o: init.c +newline_conv.o: $(RUBY_EXTCONF_H) +newline_conv.o: $(arch_hdrdir)/ruby/config.h +newline_conv.o: $(hdrdir)/ruby/assert.h +newline_conv.o: $(hdrdir)/ruby/backward.h +newline_conv.o: $(hdrdir)/ruby/backward/2/assume.h +newline_conv.o: $(hdrdir)/ruby/backward/2/attributes.h +newline_conv.o: $(hdrdir)/ruby/backward/2/bool.h +newline_conv.o: $(hdrdir)/ruby/backward/2/inttypes.h +newline_conv.o: $(hdrdir)/ruby/backward/2/limits.h +newline_conv.o: $(hdrdir)/ruby/backward/2/long_long.h +newline_conv.o: $(hdrdir)/ruby/backward/2/stdalign.h +newline_conv.o: $(hdrdir)/ruby/backward/2/stdarg.h +newline_conv.o: $(hdrdir)/ruby/defines.h +newline_conv.o: $(hdrdir)/ruby/encoding.h +newline_conv.o: $(hdrdir)/ruby/intern.h +newline_conv.o: $(hdrdir)/ruby/internal/abi.h +newline_conv.o: $(hdrdir)/ruby/internal/anyargs.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/char.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/double.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/int.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/long.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/short.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +newline_conv.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +newline_conv.o: $(hdrdir)/ruby/internal/assume.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/artificial.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/cold.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/const.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/constexpr.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/deprecated.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/error.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/forceinline.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/format.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/noalias.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/noexcept.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/noinline.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/nonnull.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/noreturn.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/pure.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/restrict.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/warning.h +newline_conv.o: $(hdrdir)/ruby/internal/attr/weakref.h +newline_conv.o: $(hdrdir)/ruby/internal/cast.h +newline_conv.o: $(hdrdir)/ruby/internal/compiler_is.h +newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +newline_conv.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +newline_conv.o: $(hdrdir)/ruby/internal/compiler_since.h +newline_conv.o: $(hdrdir)/ruby/internal/config.h +newline_conv.o: $(hdrdir)/ruby/internal/constant_p.h +newline_conv.o: $(hdrdir)/ruby/internal/core.h +newline_conv.o: $(hdrdir)/ruby/internal/core/rarray.h +newline_conv.o: $(hdrdir)/ruby/internal/core/rbasic.h +newline_conv.o: $(hdrdir)/ruby/internal/core/rbignum.h +newline_conv.o: $(hdrdir)/ruby/internal/core/rclass.h +newline_conv.o: $(hdrdir)/ruby/internal/core/rdata.h +newline_conv.o: $(hdrdir)/ruby/internal/core/rfile.h +newline_conv.o: $(hdrdir)/ruby/internal/core/rhash.h +newline_conv.o: $(hdrdir)/ruby/internal/core/robject.h +newline_conv.o: $(hdrdir)/ruby/internal/core/rregexp.h +newline_conv.o: $(hdrdir)/ruby/internal/core/rstring.h +newline_conv.o: $(hdrdir)/ruby/internal/core/rstruct.h +newline_conv.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +newline_conv.o: $(hdrdir)/ruby/internal/ctype.h +newline_conv.o: $(hdrdir)/ruby/internal/dllexport.h +newline_conv.o: $(hdrdir)/ruby/internal/dosish.h +newline_conv.o: $(hdrdir)/ruby/internal/encoding/coderange.h +newline_conv.o: $(hdrdir)/ruby/internal/encoding/ctype.h +newline_conv.o: $(hdrdir)/ruby/internal/encoding/encoding.h +newline_conv.o: $(hdrdir)/ruby/internal/encoding/pathname.h +newline_conv.o: $(hdrdir)/ruby/internal/encoding/re.h +newline_conv.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +newline_conv.o: $(hdrdir)/ruby/internal/encoding/string.h +newline_conv.o: $(hdrdir)/ruby/internal/encoding/symbol.h +newline_conv.o: $(hdrdir)/ruby/internal/encoding/transcode.h +newline_conv.o: $(hdrdir)/ruby/internal/error.h +newline_conv.o: $(hdrdir)/ruby/internal/eval.h +newline_conv.o: $(hdrdir)/ruby/internal/event.h +newline_conv.o: $(hdrdir)/ruby/internal/fl_type.h +newline_conv.o: $(hdrdir)/ruby/internal/gc.h +newline_conv.o: $(hdrdir)/ruby/internal/glob.h +newline_conv.o: $(hdrdir)/ruby/internal/globals.h +newline_conv.o: $(hdrdir)/ruby/internal/has/attribute.h +newline_conv.o: $(hdrdir)/ruby/internal/has/builtin.h +newline_conv.o: $(hdrdir)/ruby/internal/has/c_attribute.h +newline_conv.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +newline_conv.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +newline_conv.o: $(hdrdir)/ruby/internal/has/extension.h +newline_conv.o: $(hdrdir)/ruby/internal/has/feature.h +newline_conv.o: $(hdrdir)/ruby/internal/has/warning.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/array.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/bignum.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/class.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/compar.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/complex.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/cont.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/dir.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/enum.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/enumerator.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/error.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/eval.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/file.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/hash.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/io.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/load.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/marshal.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/numeric.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/object.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/parse.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/proc.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/process.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/random.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/range.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/rational.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/re.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/ruby.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/select.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/signal.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/sprintf.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/string.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/struct.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/thread.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/time.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/variable.h +newline_conv.o: $(hdrdir)/ruby/internal/intern/vm.h +newline_conv.o: $(hdrdir)/ruby/internal/interpreter.h +newline_conv.o: $(hdrdir)/ruby/internal/iterator.h +newline_conv.o: $(hdrdir)/ruby/internal/memory.h +newline_conv.o: $(hdrdir)/ruby/internal/method.h +newline_conv.o: $(hdrdir)/ruby/internal/module.h +newline_conv.o: $(hdrdir)/ruby/internal/newobj.h +newline_conv.o: $(hdrdir)/ruby/internal/scan_args.h +newline_conv.o: $(hdrdir)/ruby/internal/special_consts.h +newline_conv.o: $(hdrdir)/ruby/internal/static_assert.h +newline_conv.o: $(hdrdir)/ruby/internal/stdalign.h +newline_conv.o: $(hdrdir)/ruby/internal/stdbool.h +newline_conv.o: $(hdrdir)/ruby/internal/symbol.h +newline_conv.o: $(hdrdir)/ruby/internal/value.h +newline_conv.o: $(hdrdir)/ruby/internal/value_type.h +newline_conv.o: $(hdrdir)/ruby/internal/variable.h +newline_conv.o: $(hdrdir)/ruby/internal/warning_push.h +newline_conv.o: $(hdrdir)/ruby/internal/xmalloc.h +newline_conv.o: $(hdrdir)/ruby/io.h +newline_conv.o: $(hdrdir)/ruby/missing.h +newline_conv.o: $(hdrdir)/ruby/onigmo.h +newline_conv.o: $(hdrdir)/ruby/oniguruma.h +newline_conv.o: $(hdrdir)/ruby/ruby.h +newline_conv.o: $(hdrdir)/ruby/st.h +newline_conv.o: $(hdrdir)/ruby/subst.h +newline_conv.o: newline_conv.c stat.o: $(RUBY_EXTCONF_H) stat.o: $(arch_hdrdir)/ruby/config.h stat.o: $(hdrdir)/ruby/assert.h diff --git a/ext/-test-/file/newline_conv.c b/ext/-test-/file/newline_conv.c new file mode 100644 index 0000000000..2ac5aef801 --- /dev/null +++ b/ext/-test-/file/newline_conv.c @@ -0,0 +1,73 @@ +#include "ruby/ruby.h" +#include "ruby/io.h" +#include + +static VALUE +open_with_rb_file_open(VALUE self, VALUE filename, VALUE read_or_write, VALUE binary_or_text) +{ + char fmode[3] = { 0 }; + if (rb_sym2id(read_or_write) == rb_intern("read")) { + fmode[0] = 'r'; + } + else if (rb_sym2id(read_or_write) == rb_intern("write")) { + fmode[0] = 'w'; + } + else { + rb_raise(rb_eArgError, "read_or_write param must be :read or :write"); + } + + if (rb_sym2id(binary_or_text) == rb_intern("binary")) { + fmode[1] = 'b'; + } + else if (rb_sym2id(binary_or_text) == rb_intern("text")) { + + } + else { + rb_raise(rb_eArgError, "binary_or_text param must be :binary or :text"); + } + + return rb_file_open(StringValueCStr(filename), fmode); +} + +static VALUE +open_with_rb_io_fdopen(VALUE self, VALUE filename, VALUE read_or_write, VALUE binary_or_text) +{ + int omode = 0; + if (rb_sym2id(read_or_write) == rb_intern("read")) { + omode |= O_RDONLY; + } + else if (rb_sym2id(read_or_write) == rb_intern("write")) { + omode |= O_WRONLY; + } + else { + rb_raise(rb_eArgError, "read_or_write param must be :read or :write"); + } + + if (rb_sym2id(binary_or_text) == rb_intern("binary")) { +#ifdef O_BINARY + omode |= O_BINARY; +#endif + } + else if (rb_sym2id(binary_or_text) == rb_intern("text")) { + + } + else { + rb_raise(rb_eArgError, "binary_or_text param must be :binary or :text"); + } + + int fd = rb_cloexec_open(StringValueCStr(filename), omode, 0); + if (fd < 0) { + rb_raise(rb_eIOError, "failed to open the file"); + } + + rb_update_max_fd(fd); + return rb_io_fdopen(fd, omode, StringValueCStr(filename)); +} + +void +Init_newline_conv(VALUE module) +{ + VALUE newline_conv = rb_define_module_under(module, "NewlineConv"); + rb_define_module_function(newline_conv, "rb_file_open", open_with_rb_file_open, 3); + rb_define_module_function(newline_conv, "rb_io_fdopen", open_with_rb_io_fdopen, 3); +} diff --git a/io.c b/io.c index f6cd2c1a56..90bf245071 100644 --- a/io.c +++ b/io.c @@ -7166,8 +7166,6 @@ rb_file_open_internal(VALUE io, VALUE filename, const char *modestr) if (p) { parse_mode_enc(p+1, rb_usascii_encoding(), &convconfig.enc, &convconfig.enc2, &fmode); - convconfig.ecflags = 0; - convconfig.ecopts = Qnil; } else { rb_encoding *e; @@ -7175,10 +7173,19 @@ rb_file_open_internal(VALUE io, VALUE filename, const char *modestr) e = (fmode & FMODE_BINMODE) ? rb_ascii8bit_encoding() : NULL; rb_io_ext_int_to_encs(e, NULL, &convconfig.enc, &convconfig.enc2, fmode); - convconfig.ecflags = 0; - convconfig.ecopts = Qnil; } + convconfig.ecflags = (fmode & FMODE_READABLE) ? + MODE_BTMODE(ECONV_DEFAULT_NEWLINE_DECORATOR, + 0, ECONV_UNIVERSAL_NEWLINE_DECORATOR) : 0; +#ifdef TEXTMODE_NEWLINE_DECORATOR_ON_WRITE + convconfig.ecflags |= (fmode & FMODE_WRITABLE) ? + MODE_BTMODE(TEXTMODE_NEWLINE_DECORATOR_ON_WRITE, + 0, TEXTMODE_NEWLINE_DECORATOR_ON_WRITE) : 0; +#endif + SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(convconfig.enc2, convconfig.ecflags); + convconfig.ecopts = Qnil; + return rb_file_open_generic(io, filename, rb_io_fmode_oflags(fmode), fmode, @@ -9241,11 +9248,27 @@ static VALUE prep_io(int fd, int fmode, VALUE klass, const char *path) { VALUE path_value = Qnil; + rb_encoding *e; + struct rb_io_encoding convconfig; + if (path) { path_value = rb_obj_freeze(rb_str_new_cstr(path)); } - VALUE self = rb_io_open_descriptor(klass, fd, fmode, path_value, Qnil, NULL); + e = (fmode & FMODE_BINMODE) ? rb_ascii8bit_encoding() : NULL; + rb_io_ext_int_to_encs(e, NULL, &convconfig.enc, &convconfig.enc2, fmode); + convconfig.ecflags = (fmode & FMODE_READABLE) ? + MODE_BTMODE(ECONV_DEFAULT_NEWLINE_DECORATOR, + 0, ECONV_UNIVERSAL_NEWLINE_DECORATOR) : 0; +#ifdef TEXTMODE_NEWLINE_DECORATOR_ON_WRITE + convconfig.ecflags |= (fmode & FMODE_WRITABLE) ? + MODE_BTMODE(TEXTMODE_NEWLINE_DECORATOR_ON_WRITE, + 0, TEXTMODE_NEWLINE_DECORATOR_ON_WRITE) : 0; +#endif + SET_UNIVERSAL_NEWLINE_DECORATOR_IF_ENC2(convconfig.enc2, convconfig.ecflags); + convconfig.ecopts = Qnil; + + VALUE self = rb_io_open_descriptor(klass, fd, fmode, path_value, Qnil, &convconfig); rb_io_t*io = RFILE(self)->fptr; if (!io_check_tty(io)) { diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb index 409d21fc4e..aa10566bfa 100644 --- a/test/ruby/test_file.rb +++ b/test/ruby/test_file.rb @@ -554,4 +554,250 @@ class TestFile < Test::Unit::TestCase assert_file.absolute_path?("/foo/bar\\baz") end end + + class NewlineConvTests < Test::Unit::TestCase + TEST_STRING_WITH_CRLF = "line1\r\nline2\r\n".freeze + TEST_STRING_WITH_LF = "line1\nline2\n".freeze + + def setup + @tmpdir = Dir.mktmpdir(self.class.name) + @read_path_with_crlf = File.join(@tmpdir, "read_path_with_crlf") + File.binwrite(@read_path_with_crlf, TEST_STRING_WITH_CRLF) + @read_path_with_lf = File.join(@tmpdir, "read_path_with_lf") + File.binwrite(@read_path_with_lf, TEST_STRING_WITH_LF) + @write_path = File.join(@tmpdir, "write_path") + File.binwrite(@write_path, '') + end + + def teardown + FileUtils.rm_rf @tmpdir + end + + def windows? + /cygwin|mswin|mingw/ =~ RUBY_PLATFORM + end + + def open_file_with(method, filename, mode) + read_or_write = mode.include?('w') ? :write : :read + binary_or_text = mode.include?('b') ? :binary : :text + + f = case method + when :ruby_file_open + File.open(filename, mode) + when :c_rb_file_open + Bug::File::NewlineConv.rb_file_open(filename, read_or_write, binary_or_text) + when :c_rb_io_fdopen + Bug::File::NewlineConv.rb_io_fdopen(filename, read_or_write, binary_or_text) + else + raise "Don't know how to open with #{method}" + end + + begin + yield f + ensure + f.close + end + end + + def assert_file_contents_has_lf(f) + assert_equal TEST_STRING_WITH_LF, f.read + end + + def assert_file_contents_has_crlf(f) + assert_equal TEST_STRING_WITH_CRLF, f.read + end + + def assert_file_contents_has_lf_on_windows(f) + if windows? + assert_file_contents_has_lf(f) + else + assert_file_contents_has_crlf(f) + end + end + + def assert_file_contents_has_crlf_on_windows(f) + if windows? + assert_file_contents_has_crlf(f) + else + assert_file_contents_has_lf(f) + end + end + + def test_ruby_file_open_text_mode_read_crlf + open_file_with(:ruby_file_open, @read_path_with_crlf, 'r') { |f| assert_file_contents_has_lf_on_windows(f) } + end + + def test_ruby_file_open_bin_mode_read_crlf + open_file_with(:ruby_file_open, @read_path_with_crlf, 'rb') { |f| assert_file_contents_has_crlf(f) } + end + + def test_ruby_file_open_text_mode_read_lf + open_file_with(:ruby_file_open, @read_path_with_lf, 'r') { |f| assert_file_contents_has_lf(f) } + end + + def test_ruby_file_open_bin_mode_read_lf + open_file_with(:ruby_file_open, @read_path_with_lf, 'rb') { |f| assert_file_contents_has_lf(f) } + end + + def test_ruby_file_open_text_mode_read_crlf_with_utf8_encoding + open_file_with(:ruby_file_open, @read_path_with_crlf, 'r') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_lf_on_windows(f) + end + end + + def test_ruby_file_open_bin_mode_read_crlf_with_utf8_encoding + open_file_with(:ruby_file_open, @read_path_with_crlf, 'rb') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_crlf(f) + end + end + + def test_ruby_file_open_text_mode_read_lf_with_utf8_encoding + open_file_with(:ruby_file_open, @read_path_with_lf, 'r') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_lf(f) + end + end + + def test_ruby_file_open_bin_mode_read_lf_with_utf8_encoding + open_file_with(:ruby_file_open, @read_path_with_lf, 'rb') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_lf(f) + end + end + + def test_ruby_file_open_text_mode_write_lf + open_file_with(:ruby_file_open, @write_path, 'w') { |f| f.write TEST_STRING_WITH_LF } + File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf_on_windows(f) } + end + + def test_ruby_file_open_bin_mode_write_lf + open_file_with(:ruby_file_open, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_LF } + File.open(@write_path, 'rb') { |f| assert_file_contents_has_lf(f) } + end + + def test_ruby_file_open_bin_mode_write_crlf + open_file_with(:ruby_file_open, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_CRLF } + File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf(f) } + end + + def test_c_rb_file_open_text_mode_read_crlf + open_file_with(:c_rb_file_open, @read_path_with_crlf, 'r') { |f| assert_file_contents_has_lf_on_windows(f) } + end + + def test_c_rb_file_open_bin_mode_read_crlf + open_file_with(:c_rb_file_open, @read_path_with_crlf, 'rb') { |f| assert_file_contents_has_crlf(f) } + end + + def test_c_rb_file_open_text_mode_read_lf + open_file_with(:c_rb_file_open, @read_path_with_lf, 'r') { |f| assert_file_contents_has_lf(f) } + end + + def test_c_rb_file_open_bin_mode_read_lf + open_file_with(:c_rb_file_open, @read_path_with_lf, 'rb') { |f| assert_file_contents_has_lf(f) } + end + + def test_c_rb_file_open_text_mode_write_lf + open_file_with(:c_rb_file_open, @write_path, 'w') { |f| f.write TEST_STRING_WITH_LF } + File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf_on_windows(f) } + end + + def test_c_rb_file_open_bin_mode_write_lf + open_file_with(:c_rb_file_open, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_LF } + File.open(@write_path, 'rb') { |f| assert_file_contents_has_lf(f) } + end + + def test_c_rb_file_open_bin_mode_write_crlf + open_file_with(:c_rb_file_open, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_CRLF } + File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf(f) } + end + + def test_c_rb_file_open_text_mode_read_crlf_with_utf8_encoding + open_file_with(:c_rb_file_open, @read_path_with_crlf, 'r') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_lf_on_windows(f) + end + end + + def test_c_rb_file_open_bin_mode_read_crlf_with_utf8_encoding + open_file_with(:c_rb_file_open, @read_path_with_crlf, 'rb') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_crlf(f) + end + end + + def test_c_rb_file_open_text_mode_read_lf_with_utf8_encoding + open_file_with(:c_rb_file_open, @read_path_with_lf, 'r') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_lf(f) + end + end + + def test_c_rb_file_open_bin_mode_read_lf_with_utf8_encoding + open_file_with(:c_rb_file_open, @read_path_with_lf, 'rb') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_lf(f) + end + end + + def test_c_rb_io_fdopen_text_mode_read_crlf + open_file_with(:c_rb_io_fdopen, @read_path_with_crlf, 'r') { |f| assert_file_contents_has_lf_on_windows(f) } + end + + def test_c_rb_io_fdopen_bin_mode_read_crlf + open_file_with(:c_rb_io_fdopen, @read_path_with_crlf, 'rb') { |f| assert_file_contents_has_crlf(f) } + end + + def test_c_rb_io_fdopen_text_mode_read_lf + open_file_with(:c_rb_io_fdopen, @read_path_with_lf, 'r') { |f| assert_file_contents_has_lf(f) } + end + + def test_c_rb_io_fdopen_bin_mode_read_lf + open_file_with(:c_rb_io_fdopen, @read_path_with_lf, 'rb') { |f| assert_file_contents_has_lf(f) } + end + + def test_c_rb_io_fdopen_text_mode_write_lf + open_file_with(:c_rb_io_fdopen, @write_path, 'w') { |f| f.write TEST_STRING_WITH_LF } + File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf_on_windows(f) } + end + + def test_c_rb_io_fdopen_bin_mode_write_lf + open_file_with(:c_rb_io_fdopen, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_LF } + File.open(@write_path, 'rb') { |f| assert_file_contents_has_lf(f) } + end + + def test_c_rb_io_fdopen_bin_mode_write_crlf + open_file_with(:c_rb_io_fdopen, @write_path, 'wb') { |f| f.write TEST_STRING_WITH_CRLF } + File.open(@write_path, 'rb') { |f| assert_file_contents_has_crlf(f) } + end + + def test_c_rb_io_fdopen_text_mode_read_crlf_with_utf8_encoding + open_file_with(:c_rb_io_fdopen, @read_path_with_crlf, 'r') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_lf_on_windows(f) + end + end + + def test_c_rb_io_fdopen_bin_mode_read_crlf_with_utf8_encoding + open_file_with(:c_rb_io_fdopen, @read_path_with_crlf, 'rb') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_crlf(f) + end + end + + def test_c_rb_io_fdopen_text_mode_read_lf_with_utf8_encoding + open_file_with(:c_rb_io_fdopen, @read_path_with_lf, 'r') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_lf(f) + end + end + + def test_c_rb_io_fdopen_bin_mode_read_lf_with_utf8_encoding + open_file_with(:c_rb_io_fdopen, @read_path_with_lf, 'rb') do |f| + f.set_encoding Encoding::UTF_8, '-' + assert_file_contents_has_lf(f) + end + end + end end