rpatch 0.0.1 → 0.0.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.
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