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.



7
8
9
10
11
# File 'lib/rpatch/hunk.rb', line 7

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.



5
6
7
# File 'lib/rpatch/hunk.rb', line 5

def diffs
  @diffs
end

#numObject (readonly)

Returns the value of attribute num.



5
6
7
# File 'lib/rpatch/hunk.rb', line 5

def num
  @num
end

#titleObject (readonly)

Returns the value of attribute title.



5
6
7
# File 'lib/rpatch/hunk.rb', line 5

def title
  @title
end

Instance Method Details

#convert(lines) ⇒ Object

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



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/rpatch/hunk.rb', line 45

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

#feed_line(line) ⇒ Object



13
14
15
# File 'lib/rpatch/hunk.rb', line 13

def feed_line(line)
  diffs << line.chomp
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.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/rpatch/hunk.rb', line 88

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

  loop_n = lines.size
  loop_n = loop_n - patterns_after_patch.size + 1 if patterns_after_patch.size > 0
  while i < loop_n
    matched_line_no = match_beginning(lines[i..-1], patterns_after_patch)
    if matched_line_no
      return [i, matched_line_no]
    else
      i += 1
    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.



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/rpatch/hunk.rb', line 110

def match_before_patch(lines)
  i = 0
  if patterns_before_patch.size == 0
    return [0, 0]
  end

  loop_n = lines.size
  loop_n = loop_n - patterns_before_patch.size + 1 if patterns_before_patch.size > 0

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

#match_beginning(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).



133
134
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
178
# File 'lib/rpatch/hunk.rb', line 133

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

    while true
      match = match_line lines[i], pattern
      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

#match_line(message, pattern) ⇒ Object



180
181
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
211
212
213
# File 'lib/rpatch/hunk.rb', line 180

def match_line(message , pattern)
  return nil unless message
  line = nil
  match = nil
  while true
    if pattern.is_a? Regexp
      # When match with regexp, do not twick message
      line ||= message.dup
      if pattern.match(line)
        match = 1
      end
    else
      line ||= message.strip.gsub(/\s+/, ' ')
      if pattern == line
        match = 1
      end
    end

    if match
      break
    elsif line.empty?
      match = 0
      break
    elsif line.start_with? "#"
      while line.start_with? "#"
        line = line[1..-1]
        line = line.strip unless pattern.is_a? Regexp
      end
    else
      break
    end
  end
  match
end

#patch(lines) ⇒ Object

Patch lines in place.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/rpatch/hunk.rb', line 18

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

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

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

#patternsObject



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/rpatch/hunk.rb', line 253

def patterns
  @patterns ||= begin
    result = []
    @diffs.each do |line|
      case line
      when /^( |\+|-)/
        result << line[1..-1].strip.gsub(/\s+/, ' ')
      when /^(RE: |RE:-)/
        result << Regexp.new(line[4..-1].strip)
      else
        raise PatchFormatError.new("Unknown pattern in diffs: #{line}")
      end
    end
    result
  end
end

#patterns_after_patchObject



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/rpatch/hunk.rb', line 234

def patterns_after_patch
  @patterns_after_patch ||= begin
    result = []
    @diffs.each do |line|
      case line
      when /^( |\+)/
        result << line[1..-1].strip.gsub(/\s+/, ' ')
      when /^(RE: )/
        result << Regexp.new(line[4..-1].strip)
      when /^(-|RE:-)/
        next
      else
        raise PatchFormatError.new("Unknown pattern in diffs: #{line}")
      end
    end
    result
  end
end

#patterns_before_patchObject



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/rpatch/hunk.rb', line 215

def patterns_before_patch
  @patterns_before_patch ||= begin
    result = []
    @diffs.each do |line|
      case line
      when /^( |-)/
        result << line[1..-1].strip.gsub(/\s+/, ' ')
      when /^(RE: |RE:-)/
        result << Regexp.new(line[4..-1].strip)
      when /^\+/
        next
      else
        raise PatchFormatError.new("Unknown pattern in diffs: #{line}")
      end
    end
    result
  end
end