rpatch 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ data.tar.gz: 99bef45fa909f75a0680dbd94b5bc7fd2bb29b56
4
+ metadata.gz: 27d3cc372f56d16a39e4cde00e50cda8e890b191
5
+ SHA512:
6
+ data.tar.gz: 9fe2efe77726f1226e3fc72d353b789b50584c435fddb0f9bb2a8f074223c79b8051a0d5fd0183cb5dd965fe1150e1dd5964f71d9b2c700d6e28c058b593c8e6
7
+ metadata.gz: bf804d1fbcff8dac5c7877b3adfd3ffef762947eafe6d33366efd77627a8fcdd98b873253d8701f1ff36ca2050535ec6673ede954362a145a5b5b2db1b2b2d1a
data/README.md CHANGED
@@ -4,12 +4,21 @@ rpatch: a patch utility support regexp in patchfile.
4
4
 
5
5
  Rpatch is a patch utility, more tolerant than GNU patch. It will ignore
6
6
  changes of blank lines and white spaces, and what's more you can write
7
- regexp ("RE: " and "RE:-") in patch file to match lines.
7
+ regexp ("/ " and "/-") in patch file to match lines.
8
8
 
9
- * Use "RE: " with a regexp to match unchanged line.
10
- * Use "RE:-" with a regexp to match removed line.
11
- * Use "@@" to starts a new patch hunk only and use it's text as
12
- description for the hunk only.
9
+ Three typical diff formats:
10
+
11
+ * Start with " ": exist in both original and target. (context)
12
+ * Start with "-": only in original file, not in target. (removed)
13
+ * Start with "+": not in original file, but in target. (added)
14
+
15
+ Additional diff formats that rpatch support:
16
+
17
+ * Start with "/ ": regexp which match both original and target.
18
+ * Start with "/-": regexp which match only original file.
19
+ * Start with "? ": in original file, but may not in target.
20
+ * Start with "?+": not in original file, but may or may not in target.
21
+ * Start with "?/ ": regexp which match original, but may not have in target.
13
22
 
14
23
  For example:
15
24
 
@@ -31,8 +40,8 @@ For example:
31
40
  +# Copyright (c) 2013 Jiang Xin
32
41
  +
33
42
  When I hack files using GNU patch,
34
- RE: sometimes fail because .*
35
- RE:-(blah\s*){3}
43
+ / [sS]ometimes fail because .*
44
+ /-(blah\s*){3}
36
45
  @@ add notes
37
46
  +If patch can ignore blank lines, support regex patterns
38
47
  +in patch, it will be nice.
data/lib/rpatch/entry.rb CHANGED
@@ -2,7 +2,9 @@
2
2
  #
3
3
 
4
4
  require 'stringio'
5
+ require 'fileutils'
5
6
  require 'rpatch/error'
7
+ require 'rpatch/utils'
6
8
  require 'rpatch/hunk'
7
9
 
8
10
  module Rpatch
@@ -49,7 +51,7 @@ module Rpatch
49
51
  ''
50
52
  end
51
53
  rescue Exception => e
52
- STDERR.puts "Error: #{e.message}"
54
+ Tty.error e.message
53
55
  end
54
56
 
55
57
  def patch_on_file(input, output=nil)
@@ -59,6 +61,9 @@ module Rpatch
59
61
  filename = "<#{newfile}>"
60
62
  else
61
63
  filename = output
64
+ unless File.exist?(File.dirname(output))
65
+ FileUtils.mkdir_p File.dirname(output)
66
+ end
62
67
  end
63
68
 
64
69
  if input.is_a? IO or input.is_a? StringIO
@@ -76,9 +81,9 @@ module Rpatch
76
81
  begin
77
82
  hunk.patch(lines)
78
83
  rescue AlreadyPatchedError => e
79
- STDERR.puts "#{filename}: #{e.message}"
84
+ Tty.info "#{filename}: #{e.message}"
80
85
  rescue Exception => e
81
- STDERR.puts "ERROR: #{filename}: #{e.message}"
86
+ Tty.error "#{filename}: #{e.message}"
82
87
  patch_status = false
83
88
  else
84
89
  patch_applied = true
@@ -88,7 +93,7 @@ module Rpatch
88
93
  if output.is_a? IO or output.is_a? StringIO
89
94
  output.write lines * "\n" + "\n"
90
95
  elsif not patch_applied
91
- STDERR.puts "#{filename}: nothing changed"
96
+ Tty.notice "#{filename}: nothing changed"
92
97
  if input != output
93
98
  File.open(output, "w") do |io|
94
99
  io.write lines * "\n" + "\n"
@@ -96,31 +101,32 @@ module Rpatch
96
101
  end
97
102
  else
98
103
  unless patch_status
99
- STDERR.puts "Warning: saved orignal file as \"#{output}.orig\"."
104
+ Tty.warning "saved orignal file as \"#{output}.orig\"."
100
105
  File.open("#{output}.orig", "w") do |io|
101
106
  io.write lines_dup * "\n" + "\n"
102
107
  end
103
108
  end
104
109
  if lines.size > 0
105
- STDERR.puts "Patched \"#{output}\"."
106
110
  File.open(output, "w") do |io|
107
111
  io.write lines * "\n" + "\n"
108
112
  end
113
+ Tty.notice "Patched \"#{output}\"."
109
114
  else
110
- STDERR.puts "Remove \"#{output}\"."
111
115
  File.unlink output
116
+ Tty.notice "Remove \"#{output}\"."
112
117
  end
113
118
  end
114
119
  return patch_status
115
120
  end
116
121
 
117
122
  def patch_on_directory(inputdir, outputdir=nil)
118
- outputdir ||=inputdir
119
- input = oldfile.start_with?('/') ? oldfile : File.join(inputdir, oldfile)
120
- output = outputdir
121
- if File.directory? outputdir
122
- output = newfile.start_with?('/') ? newfile : File.join(outputdir, newfile)
123
+ outputdir ||= inputdir
124
+ if oldfile == '/dev/null'
125
+ input = newfile.start_with?('/') ? newfile : File.join(inputdir, newfile)
126
+ else
127
+ input = oldfile.start_with?('/') ? oldfile : File.join(inputdir, oldfile)
123
128
  end
129
+ output = newfile.start_with?('/') ? newfile : File.join(outputdir, newfile)
124
130
  patch_on_file(input, output)
125
131
  end
126
132
 
data/lib/rpatch/hunk.rb CHANGED
@@ -1,6 +1,88 @@
1
1
  require 'rpatch/error'
2
2
 
3
3
  module Rpatch
4
+
5
+ class Pattern
6
+ attr_reader :text
7
+
8
+ def initialize(line)
9
+ @text = line.chomp
10
+ end
11
+
12
+ def is_in_before?
13
+ @is_in_before ||= begin
14
+ @text =~ /^( |-|\/ |\/-|\? |\?-|\?\/ |\?\/-)/
15
+ end
16
+ end
17
+
18
+ def is_in_after?
19
+ @is_in_after ||= begin
20
+ @text =~ /^( |\+|\/ |\/\+)/
21
+ end
22
+ end
23
+
24
+ def is_add?
25
+ @is_add ||= begin
26
+ @text =~ /^\+/
27
+ end
28
+ end
29
+
30
+ def is_pattern?
31
+ @is_pattern ||= pattern.is_a?(Regexp)
32
+ end
33
+
34
+ def pattern
35
+ @pattern ||= begin
36
+ if text =~ /^\/( |-|\+)/
37
+ Regexp.new(text[2..-1].strip)
38
+ elsif text =~ /^\?\/( |-|\+)/
39
+ Regexp.new(text[3..-1].strip)
40
+ elsif text =~ /^( |-|\+)/
41
+ pattern = text[1..-1].strip.gsub(/\s+/, ' ')
42
+ elsif text =~ /^\?( |-|\+)/
43
+ pattern = text[2..-1].strip.gsub(/\s+/, ' ')
44
+ else
45
+ raise PatchFormatError.new("-0---Unknown pattern in diffs: #{text.inspect}")
46
+ end
47
+ end
48
+ end
49
+
50
+ def match(message)
51
+ return nil unless message
52
+ message = message.chomp
53
+ match = nil
54
+ while true
55
+ if is_pattern?
56
+ # When match with regexp, do not twick message
57
+ if pattern.match(message)
58
+ match = 1
59
+ end
60
+ else
61
+ message = message.strip.gsub(/\s+/, ' ')
62
+ if pattern == message
63
+ match = 1
64
+ end
65
+ end
66
+
67
+ if match
68
+ break
69
+ elsif message.empty?
70
+ match = 0
71
+ break
72
+ # compare without leading "#"
73
+ elsif message.start_with? "#"
74
+ while message.start_with? "#"
75
+ message = message[1..-1]
76
+ message = message.strip unless is_pattern?
77
+ end
78
+ else
79
+ break
80
+ end
81
+ end
82
+ match
83
+ end
84
+ end
85
+
4
86
  class PatchHunk
5
87
  attr_reader :title, :diffs, :num
6
88
 
@@ -11,7 +93,21 @@ module Rpatch
11
93
  end
12
94
 
13
95
  def feed_line(line)
14
- diffs << line.chomp
96
+ @diffs << line.chomp
97
+ end
98
+
99
+ def head?
100
+ not tail?
101
+ end
102
+
103
+ def tail?
104
+ diffs.first == ">"
105
+ end
106
+
107
+ def has_add?
108
+ @has_add ||= begin
109
+ patterns.select{|p| p.is_add?}.any?
110
+ end
15
111
  end
16
112
 
17
113
  # Patch lines in place.
@@ -19,20 +115,14 @@ module Rpatch
19
115
  matched_before = match_before_patch lines
20
116
  matched_after = match_after_patch lines
21
117
 
22
- if patterns_before_patch.size > patterns_after_patch.size
118
+ if matched_after
23
119
  if not matched_before
24
- if matched_after
25
- raise AlreadyPatchedError.new("Hunk #{num} (#{title}) is already patched.")
26
- else
27
- raise PatchHunkError, "Hunk #{num} (#{title}) FAILED to apply. Match failed."
28
- end
29
- end
30
- else
31
- if matched_after
32
120
  raise AlreadyPatchedError.new("Hunk #{num} (#{title}) is already patched.")
33
- elsif not matched_before
34
- raise PatchHunkError, "Hunk #{num} (#{title}) FAILED to apply. Match failed."
121
+ elsif has_add?
122
+ raise AlreadyPatchedError.new("Hunk #{num} (#{title}) is already patched.")
35
123
  end
124
+ elsif not matched_before
125
+ raise PatchHunkError, "Hunk #{num} (#{title}) FAILED to apply. Match failed."
36
126
  end
37
127
 
38
128
  n, size = matched_before
@@ -43,41 +133,45 @@ module Rpatch
43
133
  # Apply patch on lines.
44
134
  # Return array of strings contain result of applying patch.
45
135
  def convert(lines)
46
- lines_dup = lines.dup
136
+ lines = lines.dup
47
137
  result = []
48
- i = 0
49
- while i < @diffs.size
50
- case @diffs[i]
51
- when /^( |RE: )/
138
+ i = j = 0
139
+ while i < diffs.size
140
+ case diffs[i]
141
+ when /^\??\/? /
52
142
  while true
53
- match = match_line lines_dup.first, patterns[i]
143
+ match = patterns[j].match lines.first
54
144
  if not match
55
- raise PatchFormatError.new("Hunk #{num} (#{title}) FAILED to apply. No match \"#{patterns[i]}\" with #{lines_dup.first.inspect}.")
145
+ raise PatchFormatError.new("Hunk #{num} (#{title}) FAILED to apply. No match \"#{patterns[j]}\" with #{lines.first.inspect}.")
56
146
  elsif match > 0
57
- result << lines_dup.shift
147
+ result << lines.shift
58
148
  break
59
149
  elsif match == 0
60
- result << lines_dup.shift
150
+ result << lines.shift
61
151
  end
62
152
  end
63
- when /^(-|RE:-)/
153
+ when /^\??\/?[-]/
64
154
  while true
65
- match = match_line lines_dup.first, patterns[i]
155
+ match = patterns[j].match lines.first
66
156
  if not match
67
- raise PatchFormatError.new("Hunk #{num} (#{title}) FAILED to apply. No match pattern \"#{patterns[i]}\" against #{lines_dup.first.inspect}.")
157
+ raise PatchFormatError.new("Hunk #{num} (#{title}) FAILED to apply. No match pattern \"#{patterns[j]}\" against #{lines.first.inspect}.")
68
158
  elsif match > 0
69
- lines_dup.shift
159
+ lines.shift
70
160
  break
71
161
  elsif match == 0
72
- lines_dup.shift
162
+ lines.shift
73
163
  end
74
164
  end
75
- when /^\+/
76
- result << @diffs[i][1..-1]
165
+ when /^(\??\/?\+)/
166
+ result << diffs[i][$1.size..-1]
167
+ when /^(<|>)$/
168
+ # patterns do not have locaiton direction
169
+ j -= 1
77
170
  else
78
- raise PatchFormatError.new("Hunk #{num} (#{title}) FAILED to apply. Unknow syntax in hunk: #{@diffs[i]}")
171
+ raise PatchFormatError.new("Hunk #{num} (#{title}) FAILED to apply. Unknow syntax in hunk: #{diffs[i]}")
79
172
  end
80
173
  i += 1
174
+ j += 1
81
175
  end
82
176
  result
83
177
  end
@@ -86,19 +180,30 @@ module Rpatch
86
180
  # match start with location and +num lines are matched.
87
181
  # Return nil, if nothing matched.
88
182
  def match_after_patch(lines)
89
- i = 0
90
183
  if patterns_after_patch.size == 0
91
- return [0, 0]
184
+ return head? ? [0, 0] : [lines.size, 0]
92
185
  end
93
186
 
94
- loop_n = lines.size
95
- loop_n = loop_n - patterns_after_patch.size + 1 if patterns_after_patch.size > 0
96
- while i < loop_n
97
- matched_line_no = match_beginning(lines[i..-1], patterns_after_patch)
98
- if matched_line_no
99
- return [i, matched_line_no]
100
- else
101
- i += 1
187
+ if head?
188
+ i = 0
189
+ loop_n = lines.size - patterns_after_patch.size
190
+ while i <= loop_n
191
+ matched_size = get_matched_size(lines[i..-1], patterns_after_patch)
192
+ if matched_size
193
+ return [i, matched_size]
194
+ else
195
+ i += 1
196
+ end
197
+ end
198
+ else
199
+ i = lines.size - patterns_after_patch.size
200
+ while i >= 0
201
+ matched_size = get_matched_size(lines[i..-1], patterns_after_patch)
202
+ if matched_size
203
+ return [i, matched_size]
204
+ else
205
+ i -= 1
206
+ end
102
207
  end
103
208
  end
104
209
  nil
@@ -108,20 +213,38 @@ module Rpatch
108
213
  # at location, and +num lines would be replaced.
109
214
  # Return nil, if nothing matched.
110
215
  def match_before_patch(lines)
111
- i = 0
216
+ # puts "="*90
217
+ # puts patterns_before_patch.inspect
218
+ # puts patterns_before_patch.size
219
+ # puts "-"*90
220
+ # puts diffs.inspect
221
+ #puts "-"*90
222
+ #puts patterns.map{|p| [p.pattern.inspect, p.is_in_before?.inspect, p.is_in_after?.inspect]}.inspect
112
223
  if patterns_before_patch.size == 0
113
- return [0, 0]
224
+ return head? ? [0, 0] : [lines.size, 0]
114
225
  end
115
226
 
116
- loop_n = lines.size
117
- loop_n = loop_n - patterns_before_patch.size + 1 if patterns_before_patch.size > 0
227
+ if head?
228
+ i = 0
229
+ loop_n = lines.size - patterns_before_patch.size
118
230
 
119
- while i < loop_n
120
- matched_size = match_beginning(lines[i..-1], patterns_before_patch)
121
- if matched_size
122
- return [i, matched_size]
123
- else
124
- i += 1
231
+ while i <= loop_n
232
+ matched_size = get_matched_size(lines[i..-1], patterns_before_patch)
233
+ if matched_size
234
+ return [i, matched_size]
235
+ else
236
+ i += 1
237
+ end
238
+ end
239
+ else
240
+ i = lines.size - patterns_before_patch.size
241
+ while i >= 0
242
+ matched_size = get_matched_size(lines[i..-1], patterns_before_patch)
243
+ if matched_size
244
+ return [i, matched_size]
245
+ else
246
+ i -= 1
247
+ end
125
248
  end
126
249
  end
127
250
  nil
@@ -130,7 +253,7 @@ module Rpatch
130
253
  # Test whether patterns match against the beginning of lines
131
254
  # Return nil if not match, or return number of lines matched
132
255
  # with patterns (would be replaced later).
133
- def match_beginning(lines, patterns)
256
+ def get_matched_size(lines, patterns)
134
257
  i = 0
135
258
  found = true
136
259
  patterns.each do |pattern|
@@ -140,7 +263,7 @@ module Rpatch
140
263
  end
141
264
 
142
265
  while true
143
- match = match_line lines[i], pattern
266
+ match = pattern.match lines[i]
144
267
  if not match
145
268
  found = false
146
269
  break
@@ -177,90 +300,29 @@ module Rpatch
177
300
  end
178
301
  end
179
302
 
180
- def match_line(message , pattern)
181
- return nil unless message
182
- line = nil
183
- match = nil
184
- while true
185
- if pattern.is_a? Regexp
186
- # When match with regexp, do not twick message
187
- line ||= message.dup
188
- if pattern.match(line)
189
- match = 1
190
- end
191
- else
192
- line ||= message.strip.gsub(/\s+/, ' ')
193
- if pattern == line
194
- match = 1
195
- end
196
- end
197
-
198
- if match
199
- break
200
- elsif line.empty?
201
- match = 0
202
- break
203
- elsif line.start_with? "#"
204
- while line.start_with? "#"
205
- line = line[1..-1]
206
- line = line.strip unless pattern.is_a? Regexp
207
- end
208
- else
209
- break
210
- end
211
- end
212
- match
213
- end
214
-
215
303
  def patterns_before_patch
216
304
  @patterns_before_patch ||= begin
217
- result = []
218
- @diffs.each do |line|
219
- case line
220
- when /^( |-)/
221
- result << line[1..-1].strip.gsub(/\s+/, ' ')
222
- when /^(RE: |RE:-)/
223
- result << Regexp.new(line[4..-1].strip)
224
- when /^\+/
225
- next
226
- else
227
- raise PatchFormatError.new("Unknown pattern in diffs: #{line}")
228
- end
229
- end
230
- result
305
+ patterns.select{|p| p.is_in_before?}
231
306
  end
232
307
  end
233
308
 
234
309
  def patterns_after_patch
235
310
  @patterns_after_patch ||= begin
236
- result = []
237
- @diffs.each do |line|
238
- case line
239
- when /^( |\+)/
240
- result << line[1..-1].strip.gsub(/\s+/, ' ')
241
- when /^(RE: )/
242
- result << Regexp.new(line[4..-1].strip)
243
- when /^(-|RE:-)/
244
- next
245
- else
246
- raise PatchFormatError.new("Unknown pattern in diffs: #{line}")
247
- end
248
- end
249
- result
311
+ patterns.select{|p| p.is_in_after?}
250
312
  end
251
313
  end
252
314
 
253
315
  def patterns
254
316
  @patterns ||= begin
255
317
  result = []
256
- @diffs.each do |line|
318
+ diffs.each do |line|
257
319
  case line
258
- when /^( |\+|-)/
259
- result << line[1..-1].strip.gsub(/\s+/, ' ')
260
320
  when /^(RE: |RE:-)/
261
- result << Regexp.new(line[4..-1].strip)
321
+ raise PatchFormatError.new("Obsolete pattern, subsitude \"RE:\" with \"/\":\n=> #{line}")
322
+ when /^(<|>)$/
323
+ # ignore locaiton direction
262
324
  else
263
- raise PatchFormatError.new("Unknown pattern in diffs: #{line}")
325
+ result << Pattern.new(line)
264
326
  end
265
327
  end
266
328
  result