From 6a1637895c29eeb2af2cb9c6720254cba8ecfa63 Mon Sep 17 00:00:00 2001 From: Daniel Poggenpohl Date: Sat, 8 May 2021 21:38:16 +0200 Subject: [PATCH 1/2] #282 - Probably an universal solution for the line ending problem: Read the file, ignore how it converted line endings and just convert it to universal newline, then work on it also using just universal newlines. Comparison of old and new content then works. Finally, just write the file using universal newlines on all systems except Windows, where we convert to CRLF --- lib/vagrant-hostmanager/hosts_file/updater.rb | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/lib/vagrant-hostmanager/hosts_file/updater.rb b/lib/vagrant-hostmanager/hosts_file/updater.rb index ba8f38b..2b725bc 100644 --- a/lib/vagrant-hostmanager/hosts_file/updater.rb +++ b/lib/vagrant-hostmanager/hosts_file/updater.rb @@ -19,17 +19,17 @@ def update_guest(machine) if (machine.communicate.test("uname -s | grep SunOS")) realhostfile = "/etc/inet/hosts" - line_endings = "lf" + is_windows = false elsif (machine.communicate.test("test -d $Env:SystemRoot")) windir = "" machine.communicate.execute("echo %SYSTEMROOT%", {:shell => :cmd}) do |type, contents| windir << contents.gsub("\r\n", '') if type == :stdout end realhostfile = "#{windir}\\System32\\drivers\\etc\\hosts" - line_endings = "crlf" + is_windows = true else realhostfile = "/etc/hosts" - line_endings = "lf" + is_windows = false end # download and modify file with Vagrant-managed entries @@ -39,7 +39,7 @@ def update_guest(machine) @logger.debug("file is: #{file.to_s}") @logger.debug("class of file is: #{file.class}") - if update_file(file, machine, false, line_endings) + if update_file(file, machine, false, is_windows) # upload modified file and remove temporary file machine.communicate.upload(file.to_s, "/tmp/hosts.#{machine.name}") if windir @@ -62,38 +62,44 @@ class << self end hosts_location = "#{ENV['WINDIR']}\\System32\\drivers\\etc\\hosts" copy_proc = Proc.new { windows_copy_file(file, hosts_location) } - line_endings = "crlf" + is_windows=true else hosts_location = '/etc/hosts' copy_proc = Proc.new { `[ -w "#{hosts_location}" ] && cat "#{file}" > "#{hosts_location}" || sudo cp "#{file}" "#{hosts_location}"` } - line_endings = "lf" + is_windows=false end FileUtils.cp(hosts_location, file) - if update_file(file, nil, true, line_endings) + if update_file(file, nil, true, is_windows) copy_proc.call end end private - def update_file(file, resolving_machine = nil, include_id = true, line_endings) + def update_file(file, resolving_machine = nil, include_id = true, is_windows) file = Pathname.new(file) old_file_content = file.read - new_file_content = update_content(old_file_content, resolving_machine, include_id, line_endings) + # Don't care what's been read by Pathname, just convert to universal line endings for comparison + old_file_content = old_file_content.encode(old_file_content.encoding, universal_newline: true) + new_file_content = update_content(old_file_content, resolving_machine, include_id) + if is_windows + new_file_content = new_file_content.encode(new_file_content.encoding, :crlf_newline => true) + end + file.open('wb') { |io| io.write(new_file_content) } old_file_content != new_file_content end - def update_content(file_content, resolving_machine, include_id, line_endings) + def update_content(file_content, resolving_machine, include_id) id = include_id ? " id: #{read_or_create_id}" : "" header = "## vagrant-hostmanager-start#{id}" footer = "## vagrant-hostmanager-end" body = get_machines .map { |machine| get_hosts_file_entry(machine, resolving_machine) } .join - get_new_content(header, footer, body, file_content, line_endings) + get_new_content(header, footer, body, file_content) end def get_hosts_file_entry(machine, resolving_machine) @@ -142,7 +148,7 @@ def get_machines .reject(&:nil?) end - def get_new_content(header, footer, body, old_content, line_endings) + def get_new_content(header, footer, body, old_content) if body.empty? block = "\n" else @@ -151,16 +157,9 @@ def get_new_content(header, footer, body, old_content, line_endings) # Pattern for finding existing block header_pattern = Regexp.quote(header) footer_pattern = Regexp.quote(footer) - pattern = Regexp.new("[\r\n]*#{header_pattern}.*?#{footer_pattern}[\r\n]*", Regexp::MULTILINE) + pattern = Regexp.new("[\n]*#{header_pattern}.*?#{footer_pattern}[\n]*", Regexp::MULTILINE) # Replace existing block or append content = old_content.match(pattern) ? old_content.sub(pattern, block) : old_content.rstrip + block - if line_endings == "crlf" - content.encode(content.encoding, :universal_encoding => true).encode(content.encoding, :crlf_newline => true) - elsif line_endings == "lf" - content.encode(content.encoding, :universal_encoding => true) - else - content.encode(content.encoding, :universal_encoding => true) - end end def read_or_create_id From e9793f728c5cbbabaa1c6c6a988b0a3555f6cf1d Mon Sep 17 00:00:00 2001 From: Daniel Poggenpohl Date: Mon, 10 May 2021 16:28:43 +0200 Subject: [PATCH 2/2] - First, if we check the change at the end, the CRLF/LF problem still exists, so we store the comparison result earlier and not at the end - Second, if the file isn't changed we don't need to reencode the file contents nor do we need to save the temp hosts file. So just leave the original hosts file alone in that case. --- lib/vagrant-hostmanager/hosts_file/updater.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/vagrant-hostmanager/hosts_file/updater.rb b/lib/vagrant-hostmanager/hosts_file/updater.rb index 2b725bc..cc3a526 100644 --- a/lib/vagrant-hostmanager/hosts_file/updater.rb +++ b/lib/vagrant-hostmanager/hosts_file/updater.rb @@ -84,12 +84,16 @@ def update_file(file, resolving_machine = nil, include_id = true, is_windows) # Don't care what's been read by Pathname, just convert to universal line endings for comparison old_file_content = old_file_content.encode(old_file_content.encoding, universal_newline: true) new_file_content = update_content(old_file_content, resolving_machine, include_id) - if is_windows - new_file_content = new_file_content.encode(new_file_content.encoding, :crlf_newline => true) - end + is_hosts_file_changed = (old_file_content != new_file_content) + + if is_hosts_file_changed + if is_windows + new_file_content = new_file_content.encode(new_file_content.encoding, :crlf_newline => true) + end - file.open('wb') { |io| io.write(new_file_content) } - old_file_content != new_file_content + file.open('wb') { |io| io.write(new_file_content) } + end + is_hosts_file_changed end def update_content(file_content, resolving_machine, include_id)