diff-lcs 1.3 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +518 -0
  3. data/CODE_OF_CONDUCT.md +128 -0
  4. data/CONTRIBUTING.md +71 -0
  5. data/CONTRIBUTORS.md +49 -0
  6. data/{License.md → LICENCE.md} +21 -20
  7. data/Manifest.txt +84 -6
  8. data/README.md +92 -0
  9. data/Rakefile +104 -46
  10. data/SECURITY.md +41 -0
  11. data/bin/htmldiff +9 -6
  12. data/bin/ldiff +4 -1
  13. data/docs/artistic.txt +1 -1
  14. data/lib/diff/lcs/array.rb +2 -2
  15. data/lib/diff/lcs/backports.rb +13 -0
  16. data/lib/diff/lcs/block.rb +5 -5
  17. data/lib/diff/lcs/callbacks.rb +22 -17
  18. data/lib/diff/lcs/change.rb +44 -51
  19. data/lib/diff/lcs/htmldiff.rb +25 -14
  20. data/lib/diff/lcs/hunk.rb +174 -71
  21. data/lib/diff/lcs/internals.rb +57 -56
  22. data/lib/diff/lcs/ldiff.rb +101 -79
  23. data/lib/diff/lcs/string.rb +1 -1
  24. data/lib/diff/lcs/version.rb +7 -0
  25. data/lib/diff/lcs.rb +229 -212
  26. data/lib/diff-lcs.rb +2 -2
  27. data/mise.toml +5 -0
  28. data/spec/change_spec.rb +58 -34
  29. data/spec/diff_spec.rb +13 -9
  30. data/spec/fixtures/123_x +2 -0
  31. data/spec/fixtures/456_x +2 -0
  32. data/spec/fixtures/aX +1 -0
  33. data/spec/fixtures/bXaX +1 -0
  34. data/spec/fixtures/empty +0 -0
  35. data/spec/fixtures/file1.bin +0 -0
  36. data/spec/fixtures/file2.bin +0 -0
  37. data/spec/fixtures/four_lines +4 -0
  38. data/spec/fixtures/four_lines_with_missing_new_line +4 -0
  39. data/spec/fixtures/ldiff/diff.missing_new_line1-e +1 -0
  40. data/spec/fixtures/ldiff/diff.missing_new_line1-f +1 -0
  41. data/spec/fixtures/ldiff/diff.missing_new_line2-e +1 -0
  42. data/spec/fixtures/ldiff/diff.missing_new_line2-f +1 -0
  43. data/spec/fixtures/ldiff/error.diff.chef-e +2 -0
  44. data/spec/fixtures/ldiff/error.diff.chef-f +2 -0
  45. data/spec/fixtures/ldiff/error.diff.missing_new_line1-e +1 -0
  46. data/spec/fixtures/ldiff/error.diff.missing_new_line1-f +1 -0
  47. data/spec/fixtures/ldiff/error.diff.missing_new_line2-e +1 -0
  48. data/spec/fixtures/ldiff/error.diff.missing_new_line2-f +1 -0
  49. data/spec/fixtures/ldiff/output.diff +4 -0
  50. data/spec/fixtures/ldiff/output.diff-c +7 -0
  51. data/spec/fixtures/ldiff/output.diff-e +3 -0
  52. data/spec/fixtures/ldiff/output.diff-f +3 -0
  53. data/spec/fixtures/ldiff/output.diff-u +5 -0
  54. data/spec/fixtures/ldiff/output.diff.bin1 +0 -0
  55. data/spec/fixtures/ldiff/output.diff.bin1-c +0 -0
  56. data/spec/fixtures/ldiff/output.diff.bin1-e +0 -0
  57. data/spec/fixtures/ldiff/output.diff.bin1-f +0 -0
  58. data/spec/fixtures/ldiff/output.diff.bin1-u +0 -0
  59. data/spec/fixtures/ldiff/output.diff.bin2 +1 -0
  60. data/spec/fixtures/ldiff/output.diff.bin2-c +1 -0
  61. data/spec/fixtures/ldiff/output.diff.bin2-e +1 -0
  62. data/spec/fixtures/ldiff/output.diff.bin2-f +1 -0
  63. data/spec/fixtures/ldiff/output.diff.bin2-u +1 -0
  64. data/spec/fixtures/ldiff/output.diff.chef +4 -0
  65. data/spec/fixtures/ldiff/output.diff.chef-c +15 -0
  66. data/spec/fixtures/ldiff/output.diff.chef-e +3 -0
  67. data/spec/fixtures/ldiff/output.diff.chef-f +3 -0
  68. data/spec/fixtures/ldiff/output.diff.chef-u +9 -0
  69. data/spec/fixtures/ldiff/output.diff.chef2 +7 -0
  70. data/spec/fixtures/ldiff/output.diff.chef2-c +20 -0
  71. data/spec/fixtures/ldiff/output.diff.chef2-d +7 -0
  72. data/spec/fixtures/ldiff/output.diff.chef2-e +7 -0
  73. data/spec/fixtures/ldiff/output.diff.chef2-f +7 -0
  74. data/spec/fixtures/ldiff/output.diff.chef2-u +16 -0
  75. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines +5 -0
  76. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-c +9 -0
  77. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-e +6 -0
  78. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-f +6 -0
  79. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-u +7 -0
  80. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty +5 -0
  81. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-c +9 -0
  82. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-e +1 -0
  83. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-f +1 -0
  84. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-u +7 -0
  85. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context +4 -0
  86. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-c +9 -0
  87. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-e +3 -0
  88. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-f +3 -0
  89. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-u +6 -0
  90. data/spec/fixtures/ldiff/output.diff.missing_new_line1 +5 -0
  91. data/spec/fixtures/ldiff/output.diff.missing_new_line1-c +14 -0
  92. data/spec/fixtures/ldiff/output.diff.missing_new_line1-e +0 -0
  93. data/spec/fixtures/ldiff/output.diff.missing_new_line1-f +0 -0
  94. data/spec/fixtures/ldiff/output.diff.missing_new_line1-u +9 -0
  95. data/spec/fixtures/ldiff/output.diff.missing_new_line2 +5 -0
  96. data/spec/fixtures/ldiff/output.diff.missing_new_line2-c +14 -0
  97. data/spec/fixtures/ldiff/output.diff.missing_new_line2-e +0 -0
  98. data/spec/fixtures/ldiff/output.diff.missing_new_line2-f +0 -0
  99. data/spec/fixtures/ldiff/output.diff.missing_new_line2-u +9 -0
  100. data/spec/fixtures/new-chef +4 -0
  101. data/spec/fixtures/new-chef2 +17 -0
  102. data/spec/fixtures/old-chef +4 -0
  103. data/spec/fixtures/old-chef2 +14 -0
  104. data/spec/hunk_spec.rb +49 -38
  105. data/spec/issues_spec.rb +132 -21
  106. data/spec/lcs_spec.rb +3 -3
  107. data/spec/ldiff_spec.rb +83 -30
  108. data/spec/patch_spec.rb +14 -20
  109. data/spec/sdiff_spec.rb +83 -81
  110. data/spec/spec_helper.rb +220 -165
  111. data/spec/traverse_balanced_spec.rb +138 -136
  112. data/spec/traverse_sequences_spec.rb +7 -9
  113. metadata +127 -77
  114. data/Code-of-Conduct.md +0 -74
  115. data/Contributing.md +0 -83
  116. data/History.md +0 -220
  117. data/README.rdoc +0 -84
  118. data/autotest/discover.rb +0 -1
@@ -1,4 +1,4 @@
1
- # -*- ruby encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  class << Diff::LCS
4
4
  def diff_traversal(method, seq1, seq2, callbacks, &block)
@@ -13,7 +13,7 @@ class << Diff::LCS
13
13
 
14
14
  if block
15
15
  callbacks.diffs.map do |hunk|
16
- if hunk.kind_of? Array
16
+ if hunk.is_a? Array
17
17
  hunk.map { |hunk_block| block[hunk_block] }
18
18
  else
19
19
  block[hunk]
@@ -44,47 +44,49 @@ class << Diff::LCS::Internals
44
44
  b_finish = b.size - 1
45
45
  vector = []
46
46
 
47
- # Prune off any common elements at the beginning...
48
- while ((a_start <= a_finish) and (b_start <= b_finish) and
49
- (a[a_start] == b[b_start]))
47
+ # Collect any common elements at the beginning...
48
+ while (a_start <= a_finish) && (b_start <= b_finish) && (a[a_start] == b[b_start])
50
49
  vector[a_start] = b_start
51
50
  a_start += 1
52
51
  b_start += 1
53
52
  end
54
- b_start = a_start
55
53
 
56
54
  # Now the end...
57
- while ((a_start <= a_finish) and (b_start <= b_finish) and
58
- (a[a_finish] == b[b_finish]))
55
+ while (a_start <= a_finish) && (b_start <= b_finish) && (a[a_finish] == b[b_finish])
59
56
  vector[a_finish] = b_finish
60
57
  a_finish -= 1
61
58
  b_finish -= 1
62
59
  end
63
60
 
64
61
  # Now, compute the equivalence classes of positions of elements.
62
+ # An explanation for how this works: https://codeforces.com/topic/92191
65
63
  b_matches = position_hash(b, b_start..b_finish)
66
64
 
67
65
  thresh = []
68
- links = []
69
- string = a.kind_of?(String)
66
+ links = []
67
+ string = a.is_a?(String)
70
68
 
71
- (a_start .. a_finish).each do |i|
69
+ (a_start..a_finish).each do |i|
72
70
  ai = string ? a[i, 1] : a[i]
73
71
  bm = b_matches[ai]
74
72
  k = nil
75
73
  bm.reverse_each do |j|
76
- if k and (thresh[k] > j) and (thresh[k - 1] < j)
74
+ # Although the threshold check is not mandatory for this to work,
75
+ # it may have an optimization purpose
76
+ # An attempt to remove it: https://github.com/halostatue/diff-lcs/pull/72
77
+ # Why it is reintroduced: https://github.com/halostatue/diff-lcs/issues/78
78
+ if k && (thresh[k] > j) && (thresh[k - 1] < j)
77
79
  thresh[k] = j
78
80
  else
79
81
  k = replace_next_larger(thresh, j, k)
80
82
  end
81
- links[k] = [ (k > 0) ? links[k - 1] : nil, i, j ] unless k.nil?
83
+ links[k] = [k.positive? ? links[k - 1] : nil, i, j] unless k.nil?
82
84
  end
83
85
  end
84
86
 
85
87
  unless thresh.empty?
86
88
  link = links[thresh.size - 1]
87
- while not link.nil?
89
+ until link.nil?
88
90
  vector[link[1]] = link[2]
89
91
  link = link[0]
90
92
  end
@@ -93,14 +95,15 @@ class << Diff::LCS::Internals
93
95
  vector
94
96
  end
95
97
 
96
- # This method will analyze the provided patchset to provide a
97
- # single-pass normalization (conversion of the array form of
98
- # Diff::LCS::Change objects to the object form of same) and detection of
99
- # whether the patchset represents changes to be made.
98
+ # This method will analyze the provided patchset to provide a single-pass
99
+ # normalization (conversion of the array form of Diff::LCS::Change objects to
100
+ # the object form of same) and detection of whether the patchset represents
101
+ # changes to be made.
100
102
  def analyze_patchset(patchset, depth = 0)
101
- raise "Patchset too complex" if depth > 1
103
+ fail "Patchset too complex" if depth > 1
102
104
 
103
105
  has_changes = false
106
+ new_patchset = []
104
107
 
105
108
  # Format:
106
109
  # [ # patchset
@@ -110,29 +113,28 @@ class << Diff::LCS::Internals
110
113
  # ]
111
114
  # ]
112
115
 
113
- patchset = patchset.map do |hunk|
116
+ patchset.each do |hunk|
114
117
  case hunk
115
118
  when Diff::LCS::Change
116
119
  has_changes ||= !hunk.unchanged?
117
- hunk
120
+ new_patchset << hunk
118
121
  when Array
119
- # Detect if the 'hunk' is actually an array-format
120
- # Change object.
122
+ # Detect if the 'hunk' is actually an array-format change object.
121
123
  if Diff::LCS::Change.valid_action? hunk[0]
122
124
  hunk = Diff::LCS::Change.from_a(hunk)
123
125
  has_changes ||= !hunk.unchanged?
124
- hunk
126
+ new_patchset << hunk
125
127
  else
126
128
  with_changes, hunk = analyze_patchset(hunk, depth + 1)
127
129
  has_changes ||= with_changes
128
- hunk.flatten
130
+ new_patchset.concat(hunk)
129
131
  end
130
132
  else
131
- raise ArgumentError, "Cannot normalise a hunk of class #{hunk.class}."
133
+ fail ArgumentError, "Cannot normalise a hunk of class #{hunk.class}."
132
134
  end
133
135
  end
134
136
 
135
- [ has_changes, patchset.flatten(1) ]
137
+ [has_changes, new_patchset]
136
138
  end
137
139
 
138
140
  # Examine the patchset and the source to see in which direction the
@@ -143,7 +145,7 @@ class << Diff::LCS::Internals
143
145
  # Diff::LCS::Change as its source, as an array will cause the creation
144
146
  # of one of the above.
145
147
  def intuit_diff_direction(src, patchset, limit = nil)
146
- string = src.kind_of?(String)
148
+ string = src.is_a?(String)
147
149
  count = left_match = left_miss = right_match = right_miss = 0
148
150
 
149
151
  patchset.each do |change|
@@ -155,31 +157,29 @@ class << Diff::LCS::Internals
155
157
  re = string ? src[change.new_position, 1] : src[change.new_position]
156
158
 
157
159
  case change.action
158
- when '-' # Remove details from the old string
160
+ when "-" # Remove details from the old string
159
161
  if le == change.old_element
160
162
  left_match += 1
161
163
  else
162
164
  left_miss += 1
163
165
  end
164
- when '+'
166
+ when "+"
165
167
  if re == change.new_element
166
168
  right_match += 1
167
169
  else
168
170
  right_miss += 1
169
171
  end
170
- when '='
172
+ when "="
171
173
  left_miss += 1 if le != change.old_element
172
174
  right_miss += 1 if re != change.new_element
173
- when '!'
175
+ when "!"
174
176
  if le == change.old_element
175
177
  left_match += 1
178
+ elsif re == change.new_element
179
+ right_match += 1
176
180
  else
177
- if re == change.new_element
178
- right_match += 1
179
- else
180
- left_miss += 1
181
- right_miss += 1
182
- end
181
+ left_miss += 1
182
+ right_miss += 1
183
183
  end
184
184
  end
185
185
  when Diff::LCS::Change
@@ -189,19 +189,19 @@ class << Diff::LCS::Internals
189
189
  element = string ? src[change.position, 1] : src[change.position]
190
190
 
191
191
  case change.action
192
- when '-'
192
+ when "-"
193
193
  if element == change.element
194
194
  left_match += 1
195
195
  else
196
196
  left_miss += 1
197
197
  end
198
- when '+'
198
+ when "+"
199
199
  if element == change.element
200
200
  right_match += 1
201
201
  else
202
202
  right_miss += 1
203
203
  end
204
- when '='
204
+ when "="
205
205
  if element != change.element
206
206
  left_miss += 1
207
207
  right_miss += 1
@@ -209,16 +209,16 @@ class << Diff::LCS::Internals
209
209
  end
210
210
  end
211
211
 
212
- break if (not limit.nil?) && (count > limit)
212
+ break if !limit.nil? && (count > limit)
213
213
  end
214
214
 
215
- no_left = (left_match == 0) && (left_miss > 0)
216
- no_right = (right_match == 0) && (right_miss > 0)
215
+ no_left = left_match.zero? && left_miss.positive?
216
+ no_right = right_match.zero? && right_miss.positive?
217
217
 
218
- case [ no_left, no_right ]
219
- when [ false, true ]
218
+ case [no_left, no_right]
219
+ when [false, true]
220
220
  :patch
221
- when [ true, false ]
221
+ when [true, false]
222
222
  :unpatch
223
223
  else
224
224
  case left_match <=> right_match
@@ -235,7 +235,8 @@ class << Diff::LCS::Internals
235
235
  :patch
236
236
  end
237
237
  else
238
- raise "The provided patchset does not appear to apply to the provided enumerable as either source or destination value."
238
+ fail "The provided patchset does not appear to apply to the provided \
239
+ enumerable as either source or destination value."
239
240
  end
240
241
  end
241
242
  end
@@ -250,22 +251,22 @@ class << Diff::LCS::Internals
250
251
  # This operation preserves the sort order.
251
252
  def replace_next_larger(enum, value, last_index = nil)
252
253
  # Off the end?
253
- if enum.empty? or (value > enum[-1])
254
+ if enum.empty? || (value > enum[-1])
254
255
  enum << value
255
256
  return enum.size - 1
256
257
  end
257
258
 
258
259
  # Binary search for the insertion point
259
- last_index ||= enum.size
260
+ last_index ||= enum.size - 1
260
261
  first_index = 0
261
- while (first_index <= last_index)
262
+ while first_index <= last_index
262
263
  i = (first_index + last_index) >> 1
263
264
 
264
265
  found = enum[i]
265
266
 
266
- if value == found
267
- return nil
268
- elsif value > found
267
+ return nil if value == found
268
+
269
+ if value > found
269
270
  first_index = i + 1
270
271
  else
271
272
  last_index = i - 1
@@ -275,7 +276,7 @@ class << Diff::LCS::Internals
275
276
  # The insertion point is in first_index; overwrite the next larger
276
277
  # value.
277
278
  enum[first_index] = value
278
- return first_index
279
+ first_index
279
280
  end
280
281
  private :replace_next_larger
281
282
 
@@ -295,7 +296,7 @@ class << Diff::LCS::Internals
295
296
  # positions it occupies in the Enumerable, optionally restricted to the
296
297
  # elements specified in the range of indexes specified by +interval+.
297
298
  def position_hash(enum, interval)
298
- string = enum.kind_of?(String)
299
+ string = enum.is_a?(String)
299
300
  hash = Hash.new { |h, k| h[k] = [] }
300
301
  interval.each do |i|
301
302
  k = string ? enum[i, 1] : enum[i]
@@ -1,13 +1,13 @@
1
- # -*- ruby encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
- require 'optparse'
4
- require 'ostruct'
5
- require 'diff/lcs/hunk'
3
+ require "optparse"
4
+ require "diff/lcs/hunk"
6
5
 
7
- module Diff::LCS::Ldiff #:nodoc:
6
+ class Diff::LCS::Ldiff # :nodoc:
7
+ # standard:disable Layout/HeredocIndentation
8
8
  BANNER = <<-COPYRIGHT
9
9
  ldiff #{Diff::LCS::VERSION}
10
- Copyright 2004-2014 Austin Ziegler
10
+ Copyright 2004-2025 Austin Ziegler
11
11
 
12
12
  Part of Diff::LCS.
13
13
  https://github.com/halostatue/diff-lcs
@@ -15,48 +15,70 @@ ldiff #{Diff::LCS::VERSION}
15
15
  This program is free software. It may be redistributed and/or modified under
16
16
  the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
17
17
  MIT licence.
18
- COPYRIGHT
19
- end
18
+ COPYRIGHT
19
+ # standard:enable Layout/HeredocIndentation
20
+
21
+ InputInfo = Struct.new(:filename, :data, :stat) do
22
+ def initialize(filename)
23
+ super(filename, ::File.read(filename), ::File.stat(filename))
24
+ end
25
+ end
20
26
 
21
- class << Diff::LCS::Ldiff
22
- attr_reader :format, :lines #:nodoc:
23
- attr_reader :file_old, :file_new #:nodoc:
24
- attr_reader :data_old, :data_new #:nodoc:
27
+ attr_reader :format, :lines # :nodoc:
28
+ attr_reader :file_old, :file_new # :nodoc:
29
+ attr_reader :data_old, :data_new # :nodoc:
25
30
 
26
- def run(args, input = $stdin, output = $stdout, error = $stderr) #:nodoc:
31
+ def self.run(args, input = $stdin, output = $stdout, error = $stderr) # :nodoc:
32
+ new.run(args, input, output, error)
33
+ end
34
+
35
+ def initialize
27
36
  @binary = nil
37
+ @format = :old
38
+ @lines = 0
39
+ end
28
40
 
41
+ def run(args, _input = $stdin, output = $stdout, error = $stderr) # :nodoc:
29
42
  args.options do |o|
30
43
  o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile"
31
44
  o.separator ""
32
- o.on('-c', '-C', '--context [LINES]', Numeric, 'Displays a context diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
45
+ o.on(
46
+ "-c", "-C", "--context [LINES]", Integer,
47
+ "Displays a context diff with LINES lines", "of context. Default 3 lines."
48
+ ) do |ctx|
33
49
  @format = :context
34
- @lines = ctx || 3
50
+ @lines = ctx || 3
35
51
  end
36
- o.on('-u', '-U', '--unified [LINES]', Numeric, 'Displays a unified diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
52
+ o.on(
53
+ "-u", "-U", "--unified [LINES]", Integer,
54
+ "Displays a unified diff with LINES lines", "of context. Default 3 lines."
55
+ ) do |ctx|
37
56
  @format = :unified
38
- @lines = ctx || 3
57
+ @lines = ctx || 3
39
58
  end
40
- o.on('-e', 'Creates an \'ed\' script to change', 'oldfile to newfile.') do |ctx|
59
+ o.on("-e", "Creates an 'ed' script to change", "oldfile to newfile.") do |_ctx|
41
60
  @format = :ed
42
61
  end
43
- o.on('-f', 'Creates an \'ed\' script to change', 'oldfile to newfile in reverse order.') do |ctx|
62
+ o.on("-f", "Creates an 'ed' script to change", "oldfile to newfile in reverse order.") do |_ctx|
44
63
  @format = :reverse_ed
45
64
  end
46
- o.on('-a', '--text', 'Treat the files as text and compare them', 'line-by-line, even if they do not seem', 'to be text.') do |txt|
65
+ o.on(
66
+ "-a", "--text",
67
+ "Treat the files as text and compare them", "line-by-line, even if they do not seem", "to be text."
68
+ ) do |_txt|
47
69
  @binary = false
48
70
  end
49
- o.on('--binary', 'Treats the files as binary.') do |bin|
71
+ o.on("--binary", "Treats the files as binary.") do |_bin|
50
72
  @binary = true
51
73
  end
52
- o.on('-q', '--brief', 'Report only whether or not the files', 'differ, not the details.') do |ctx|
74
+ o.on("-q", "--brief", "Report only whether or not the files", "differ, not the details.") do |_ctx|
53
75
  @format = :report
54
76
  end
55
- o.on_tail('--help', 'Shows this text.') do
77
+ o.on_tail("--help", "Shows this text.") do
56
78
  error << o
57
79
  return 0
58
80
  end
59
- o.on_tail('--version', 'Shows the version of Diff::LCS.') do
81
+ o.on_tail("--version", "Shows the version of Diff::LCS.") do
60
82
  error << Diff::LCS::Ldiff::BANNER
61
83
  return 0
62
84
  end
@@ -72,96 +94,96 @@ class << Diff::LCS::Ldiff
72
94
 
73
95
  # Defaults are for old-style diff
74
96
  @format ||= :old
75
- @lines ||= 0
97
+ @lines ||= 0
76
98
 
77
99
  file_old, file_new = *ARGV
100
+ diff?(
101
+ InputInfo.new(file_old),
102
+ InputInfo.new(file_new),
103
+ @format,
104
+ output,
105
+ binary: @binary,
106
+ lines: @lines
107
+ ) ? 1 : 0
108
+ end
78
109
 
79
- case @format
110
+ def diff?(info_old, info_new, format, output, binary: nil, lines: 0)
111
+ case format
80
112
  when :context
81
- char_old = '*' * 3
82
- char_new = '-' * 3
113
+ char_old = "*" * 3
114
+ char_new = "-" * 3
83
115
  when :unified
84
- char_old = '-' * 3
85
- char_new = '+' * 3
116
+ char_old = "-" * 3
117
+ char_new = "+" * 3
86
118
  end
87
119
 
88
120
  # After we've read up to a certain point in each file, the number of
89
121
  # items we've read from each file will differ by FLD (could be 0).
90
122
  file_length_difference = 0
91
123
 
92
- if @binary.nil? or @binary
93
- data_old = IO.read(file_old)
94
- data_new = IO.read(file_new)
95
-
96
- # Test binary status
97
- if @binary.nil?
98
- old_txt = data_old[0, 4096].scan(/\0/).empty?
99
- new_txt = data_new[0, 4096].scan(/\0/).empty?
100
- @binary = (not old_txt) or (not new_txt)
101
- old_txt = new_txt = nil
102
- end
103
-
104
- unless @binary
105
- data_old = data_old.split($/).map { |e| e.chomp }
106
- data_new = data_new.split($/).map { |e| e.chomp }
107
- end
108
- else
109
- data_old = IO.readlines(file_old).map { |e| e.chomp }
110
- data_new = IO.readlines(file_new).map { |e| e.chomp }
124
+ # Test binary status
125
+ if binary.nil?
126
+ old_bin = info_old.data[0, 4096].include?("\0")
127
+ new_bin = info_new.data[0, 4096].include?("\0")
128
+ binary = old_bin || new_bin
111
129
  end
112
130
 
113
131
  # diff yields lots of pieces, each of which is basically a Block object
114
- if @binary
115
- diffs = (data_old == data_new)
132
+ if binary
133
+ has_diffs = (info_old.data != info_new.data)
134
+ if format != :report
135
+ if has_diffs
136
+ output << "Binary files #{info_old.filename} and #{info_new.filename} differ\n"
137
+ return true
138
+ end
139
+ return false
140
+ end
116
141
  else
142
+ data_old = info_old.data.lines.to_a
143
+ data_new = info_new.data.lines.to_a
117
144
  diffs = Diff::LCS.diff(data_old, data_new)
118
- diffs = nil if diffs.empty?
119
- end
120
-
121
- return 0 unless diffs
122
-
123
- if @format == :report
124
- output << "Files #{file_old} and #{file_new} differ\n"
125
- return 1
145
+ return false if diffs.empty?
126
146
  end
127
147
 
128
- if (@format == :unified) or (@format == :context)
129
- ft = File.stat(file_old).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z')
130
- output << "#{char_old} #{file_old}\t#{ft}\n"
131
- ft = File.stat(file_new).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z')
132
- output << "#{char_new} #{file_new}\t#{ft}\n"
148
+ case format
149
+ when :report
150
+ output << "Files #{info_old.filename} and #{info_new.filename} differ\n"
151
+ return true
152
+ when :unified, :context
153
+ ft = info_old.stat.mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
154
+ output << "#{char_old} #{info_old.filename}\t#{ft}\n"
155
+ ft = info_new.stat.mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
156
+ output << "#{char_new} #{info_new.filename}\t#{ft}\n"
157
+ when :ed
158
+ real_output = output
159
+ output = []
133
160
  end
134
161
 
135
162
  # Loop over hunks. If a hunk overlaps with the last hunk, join them.
136
163
  # Otherwise, print out the old one.
137
164
  oldhunk = hunk = nil
138
-
139
- if @format == :ed
140
- real_output = output
141
- output = []
142
- end
143
-
144
165
  diffs.each do |piece|
145
166
  begin
146
- hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @lines,
147
- file_length_difference)
167
+ hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, lines, file_length_difference)
148
168
  file_length_difference = hunk.file_length_difference
149
169
 
150
170
  next unless oldhunk
151
- next if (@lines > 0) and hunk.merge(oldhunk)
171
+ next if lines.positive? && hunk.merge(oldhunk)
152
172
 
153
- output << oldhunk.diff(@format) << "\n"
173
+ output << oldhunk.diff(format)
174
+ output << "\n" if format == :unified
154
175
  ensure
155
176
  oldhunk = hunk
156
177
  end
157
178
  end
158
179
 
159
- output << oldhunk.diff(@format) << "\n"
180
+ last = oldhunk.diff(format, true)
181
+ last << "\n" unless last.is_a?(Diff::LCS::Hunk) || last.empty? || last.end_with?("\n")
160
182
 
161
- if @format == :ed
162
- output.reverse_each { |e| real_output << e.diff(:ed_finish) }
163
- end
183
+ output << last
184
+
185
+ output.reverse_each { |e| real_output << e.diff(:ed_finish, e == output[0]) } if format == :ed
164
186
 
165
- return 1
187
+ true
166
188
  end
167
189
  end
@@ -1,4 +1,4 @@
1
- # -*- ruby encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  class String
4
4
  include Diff::LCS
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Diff
4
+ module LCS
5
+ VERSION = "1.6.2"
6
+ end
7
+ end