Class: Rpatch::PatchHunk

Inherits:
Object
  • Object
show all
Defined in:
lib/rpatch/hunk.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(text, num = nil) ⇒ PatchHunk

Returns a new instance of PatchHunk.



89
90
91
92
93
# File 'lib/rpatch/hunk.rb', line 89

def initialize(text, num=nil)
  @title = text.chomp.sub(/^@@/, '').strip
  @num = num || "#"
  @diffs = []
end

Instance Attribute Details

#diffsObject (readonly)

Returns the value of attribute diffs.



87
88
89
# File 'lib/rpatch/hunk.rb', line 87

def diffs
  @diffs
end

#numObject (readonly)

Returns the value of attribute num.



87
88
89
# File 'lib/rpatch/hunk.rb', line 87

def num
  @num
end

#titleObject (readonly)

Returns the value of attribute title.



87
88
89
# File 'lib/rpatch/hunk.rb', line 87

def title
  @title
end

Instance Method Details

#convert(lines) ⇒ Object

Apply patch on lines. Return array of strings contain result of applying patch.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/rpatch/hunk.rb', line 135

def convert(lines)
  lines = lines.dup
  result = []
  i = j = 0
  while i < diffs.size
    case diffs[i]
    when /^\??\/? /
      while true
        match =  patterns[j].match lines.first
        if not match
          raise PatchFormatError.new("Hunk #{num} (#{title}) FAILED to apply. No match \"#{patterns[j]}\" with #{lines.first.inspect}.")
        elsif match > 0
          result << lines.shift
          break
        elsif match == 0
          result << lines.shift
        end
      end
    when /^\??\/?[-]/
      while true
        match =  patterns[j].match lines.first
        if not match
          raise PatchFormatError.new("Hunk #{num} (#{title}) FAILED to apply. No match pattern \"#{patterns[j]}\" against #{lines.first.inspect}.")
        elsif match > 0
          lines.shift
          break
        elsif match == 0
          lines.shift
        end
      end
    when /^(\??\/?\+)/
      result << diffs[i][$1.size..-1]
    when /^(<|>)$/
      # patterns do not have locaiton direction
      j -= 1
    else
      raise PatchFormatError.new("Hunk #{num} (#{title}) FAILED to apply. Unknow syntax in hunk: #{diffs[i]}")
    end
    i += 1
    j += 1
  end
  result
end

#feed_line(line) ⇒ Object



95
96
97
# File 'lib/rpatch/hunk.rb', line 95

def feed_line(line)
  @diffs << line.chomp
end

#get_matched_size(lines, patterns) ⇒ Object

Test whether patterns match against the beginning of lines Return nil if not match, or return number of lines matched with patterns (would be replaced later).



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/rpatch/hunk.rb', line 256

def get_matched_size(lines, patterns)
  i = 0
  found = true
  patterns.each do |pattern|
    unless lines[i]
      found = false
      break
    end

    while true
      match = pattern.match lines[i]
      if not match
        found = false
        break
      # Match precisely for the first line of pattern (i==0),
      # Not pass blank lines.
      elsif match == 0 and i == 0
        found = false
        break
      # Matched.
      elsif match > 0
        break
      # Match next line if this line is blank.
      elsif match == 0
        i += 1
        unless lines[i]
          found = false
          break
        end
      # Never comes here.
      else
        found = false
        break
      end
    end

    break unless found
    i += 1
  end

  if found
    i
  else
    nil
  end
end

#has_add?Boolean

Returns:

  • (Boolean)


107
108
109
110
111
# File 'lib/rpatch/hunk.rb', line 107

def has_add?
  @has_add ||= begin
    patterns.select{|p| p.is_add?}.any?
  end
end

#head?Boolean

Returns:

  • (Boolean)


99
100
101
# File 'lib/rpatch/hunk.rb', line 99

def head?
  not tail?
end

#match_after_patch(lines) ⇒ Object

Return [location, num_of_lines], which seems already patched, and match start with location and num lines are matched. Return nil, if nothing matched.



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/rpatch/hunk.rb', line 182

def match_after_patch(lines)
  if patterns_after_patch.size == 0
    return head? ? [0, 0] : [lines.size, 0]
  end

  if head?
    i = 0
    loop_n = lines.size - patterns_after_patch.size
    while i <= loop_n
      matched_size = get_matched_size(lines[i..-1], patterns_after_patch)
      if matched_size
        return [i, matched_size]
      else
        i += 1
      end
    end
  else
    i = lines.size - patterns_after_patch.size
    while i >= 0
      matched_size = get_matched_size(lines[i..-1], patterns_after_patch)
      if matched_size
        return [i, matched_size]
      else
        i -= 1
      end
    end
  end
  nil
end

#match_before_patch(lines) ⇒ Object

Return [location, num_of_lines], which could apply patch at at location, and num lines would be replaced. Return nil, if nothing matched.



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/rpatch/hunk.rb', line 215

def match_before_patch(lines)
# puts "="*90
# puts patterns_before_patch.inspect
# puts patterns_before_patch.size
# puts "-"*90
# puts diffs.inspect
#puts "-"*90
#puts patterns.map{|p| [p.pattern.inspect, p.is_in_before?.inspect, p.is_in_after?.inspect]}.inspect
  if patterns_before_patch.size == 0
    return head? ? [0, 0] : [lines.size, 0]
  end

  if head?
    i = 0
    loop_n = lines.size - patterns_before_patch.size

    while i <= loop_n
      matched_size = get_matched_size(lines[i..-1], patterns_before_patch)
      if matched_size
        return [i, matched_size]
      else
        i += 1
      end
    end
  else
    i = lines.size - patterns_before_patch.size
    while i >= 0
      matched_size = get_matched_size(lines[i..-1], patterns_before_patch)
      if matched_size
        return [i, matched_size]
      else
        i -= 1
      end
    end
  end
  nil
end

#patch(lines) ⇒ Object

Patch lines in place.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/rpatch/hunk.rb', line 114

def patch(lines)
  matched_before = match_before_patch lines
  matched_after = match_after_patch lines

  if matched_after
    if not matched_before
      raise AlreadyPatchedError.new("Hunk #{num} (#{title}) is already patched.")
    elsif has_add?
      raise AlreadyPatchedError.new("Hunk #{num} (#{title}) is already patched.")
    end
  elsif not matched_before
    raise PatchHunkError, "Hunk #{num} (#{title}) FAILED to apply. Match failed."
  end

  n, size = matched_before
  lines[n...(n+size)] = convert lines[n...(n+size)]
  lines
end

#patternsObject



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/rpatch/hunk.rb', line 315

def patterns
  @patterns ||= begin
    result = []
    diffs.each do |line|
      case line
      when /^(RE: |RE:-)/
        raise PatchFormatError.new("Obsolete pattern, subsitude \"RE:\" with \"/\":\n=> #{line}")
      when /^(<|>)$/
        # ignore locaiton direction
      else
        result << Pattern.new(line)
      end
    end
    result
  end
end

#patterns_after_patchObject



309
310
311
312
313
# File 'lib/rpatch/hunk.rb', line 309

def patterns_after_patch
  @patterns_after_patch ||= begin
    patterns.select{|p| p.is_in_after?}
  end
end

#patterns_before_patchObject



303
304
305
306
307
# File 'lib/rpatch/hunk.rb', line 303

def patterns_before_patch
  @patterns_before_patch ||= begin
    patterns.select{|p| p.is_in_before?}
  end
end

#tail?Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/rpatch/hunk.rb', line 103

def tail?
  diffs.first == ">"
end