Skip to content
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

(PUP-927) Read preserving line endings #3616

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions lib/puppet/file_system.rb
Expand Up @@ -126,6 +126,19 @@ def self.read(path)
@impl.read(assert_path(path))
end

# Read a file keeping the original line endings intact. This
# attempts to open files using binary mode using some encoding
# overrides and falling back to IO.read when none of the
# encodings are valid.
#
# @return [String] The contents of the file
#
# @api public
#
def self.read_preserve_line_endings(path)
@impl.read_preserve_line_endings(assert_path(path))
end

# @return [String] The binary contents of the file
#
# @api public
Expand Down
8 changes: 8 additions & 0 deletions lib/puppet/file_system/file_impl.rb
Expand Up @@ -75,6 +75,14 @@ def read(path)
path.read
end

def read_preserve_line_endings(path)
contents = path.read( :mode => 'rb', :encoding => Encoding::UTF_8)
contents = path.read( :mode => 'rb', :encoding => Encoding::default_external) unless contents.valid_encoding?
contents = path.read unless contents.valid_encoding?

contents
end

def binread(path)
raise NotImplementedError
end
Expand Down
4 changes: 4 additions & 0 deletions lib/puppet/file_system/memory_impl.rb
Expand Up @@ -44,6 +44,10 @@ def read(path)
handle.read
end

def read_preserve_line_endings(path)
read(path)
end

def open(path, *args, &block)
handle = assert_path(path).handle
if block_given?
Expand Down
4 changes: 3 additions & 1 deletion lib/puppet/parser/functions/file.rb
@@ -1,3 +1,5 @@
require 'puppet/file_system'

Puppet::Parser::Functions::newfunction(
:file, :arity => -2, :type => :rvalue,
:doc => "Loads a file from a module and returns its contents as a string.
Expand All @@ -24,7 +26,7 @@
end

if path
File.read(path)
Puppet::FileSystem.read_preserve_line_endings(path)
else
raise Puppet::ParseError, "Could not find any files from #{vals.join(", ")}"
end
Expand Down
3 changes: 2 additions & 1 deletion lib/puppet/parser/templatewrapper.rb
@@ -1,5 +1,6 @@
require 'puppet/parser/files'
require 'erb'
require 'puppet/file_system'

# A simple wrapper for templates, so they don't have full access to
# the scope objects.
Expand Down Expand Up @@ -70,7 +71,7 @@ def result(string = nil)
if string
template_source = "inline template"
else
string = File.read(@__file__)
string = Puppet::FileSystem.read_preserve_line_endings(@__file__)
template_source = @__file__
end

Expand Down
34 changes: 34 additions & 0 deletions spec/unit/file_system_spec.rb
Expand Up @@ -5,6 +5,14 @@
describe "Puppet::FileSystem" do
include PuppetSpec::Files

def with_file_content(content)
path = tmpfile('file-system')
file = File.new(path, 'wb')
file.sync = true
file.print content
yield path
end

context "#exclusive_open" do
it "opens ands allows updating of an existing file" do
file = file_containing("file_to_update", "the contents")
Expand Down Expand Up @@ -96,6 +104,32 @@ def increment_counter_in_multiple_processes(file, num_procs, options)
end
end

context "read_preserve_line_endings" do
it "should read a file with line feed" do
with_file_content("file content \n") do |file|
expect(Puppet::FileSystem.read_preserve_line_endings(file)).to eq("file content \n")
end
end

it "should read a file with carriage return line feed" do
with_file_content("file content \r\n") do |file|
expect(Puppet::FileSystem.read_preserve_line_endings(file)).to eq("file content \r\n")
end
end

it "should read a mixed file using only the first line newline when lf" do
with_file_content("file content \nsecond line \r\n") do |file|
expect(Puppet::FileSystem.read_preserve_line_endings(file)).to eq("file content \nsecond line \r\n")
end
end

it "should read a mixed file using only the first line newline when crlf" do
with_file_content("file content \r\nsecond line \n") do |file|
expect(Puppet::FileSystem.read_preserve_line_endings(file)).to eq("file content \r\nsecond line \n")
end
end
end

describe "symlink",
:if => ! Puppet.features.manages_symlinks? &&
Puppet.features.microsoft_windows? do
Expand Down
10 changes: 8 additions & 2 deletions spec/unit/parser/functions/file_spec.rb
Expand Up @@ -15,15 +15,21 @@

def with_file_content(content)
path = tmpfile('file-function')
file = File.new(path, 'w')
file = File.new(path, 'wb')
file.sync = true
file.print content
yield path
end

it "should read a file" do
with_file_content('file content') do |name|
expect(scope.function_file([name])).to eq("file content")
expect(scope.function_file([name])).to eq('file content')
end
end

it "should read a file keeping line endings intact" do
with_file_content("file content\r\n") do |name|
expect(scope.function_file([name])).to eq("file content\r\n")
end
end

Expand Down
2 changes: 1 addition & 1 deletion spec/unit/parser/functions/template_spec.rb
Expand Up @@ -82,7 +82,7 @@
end

def eval_template(content)
File.stubs(:read).with("template").returns(content)
Puppet::FileSystem.stubs(:read_preserve_line_endings).with("template").returns(content)
Puppet::Parser::Files.stubs(:find_template).returns("template")
scope.function_template(['template'])
end
Expand Down
2 changes: 1 addition & 1 deletion spec/unit/parser/templatewrapper_spec.rb
Expand Up @@ -94,7 +94,7 @@ def given_a_template_file(name, contents)
Puppet::Parser::Files.stubs(:find_template).
with(name, anything()).
returns(full_name)
File.stubs(:read).with(full_name).returns(contents)
Puppet::FileSystem.stubs(:read_preserve_line_endings).with(full_name).returns(contents)

full_name
end
Expand Down