yugui-chkbuild 0.1.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.
- data/README.ja.rd +191 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/bin/last-build +28 -0
- data/bin/start-build +37 -0
- data/chkbuild.gemspec +107 -0
- data/core_ext/io.rb +17 -0
- data/core_ext/string.rb +10 -0
- data/lib/chkbuild.rb +45 -0
- data/lib/chkbuild/build.rb +718 -0
- data/lib/chkbuild/lock.rb +57 -0
- data/lib/chkbuild/logfile.rb +230 -0
- data/lib/chkbuild/main.rb +138 -0
- data/lib/chkbuild/options.rb +62 -0
- data/lib/chkbuild/scm/cvs.rb +132 -0
- data/lib/chkbuild/scm/git.rb +223 -0
- data/lib/chkbuild/scm/svn.rb +215 -0
- data/lib/chkbuild/scm/xforge.rb +33 -0
- data/lib/chkbuild/target.rb +180 -0
- data/lib/chkbuild/targets/gcc.rb +94 -0
- data/lib/chkbuild/targets/ruby.rb +456 -0
- data/lib/chkbuild/title.rb +107 -0
- data/lib/chkbuild/upload.rb +66 -0
- data/lib/misc/escape.rb +535 -0
- data/lib/misc/gdb.rb +74 -0
- data/lib/misc/timeoutcom.rb +174 -0
- data/lib/misc/udiff.rb +244 -0
- data/lib/misc/util.rb +232 -0
- data/sample/build-autoconf-ruby +69 -0
- data/sample/build-gcc-ruby +43 -0
- data/sample/build-ruby +37 -0
- data/sample/build-ruby2 +36 -0
- data/sample/build-svn +55 -0
- data/sample/build-yarv +35 -0
- data/sample/test-apr +12 -0
- data/sample/test-catcherr +23 -0
- data/sample/test-combfail +21 -0
- data/sample/test-core +14 -0
- data/sample/test-core2 +19 -0
- data/sample/test-date +9 -0
- data/sample/test-dep +17 -0
- data/sample/test-depver +14 -0
- data/sample/test-echo +9 -0
- data/sample/test-env +9 -0
- data/sample/test-error +9 -0
- data/sample/test-fail +18 -0
- data/sample/test-fmesg +16 -0
- data/sample/test-gcc-v +15 -0
- data/sample/test-git +11 -0
- data/sample/test-leave-proc +9 -0
- data/sample/test-limit +9 -0
- data/sample/test-make +9 -0
- data/sample/test-neterr +16 -0
- data/sample/test-savannah +14 -0
- data/sample/test-sleep +9 -0
- data/sample/test-timeout +9 -0
- data/sample/test-timeout2 +10 -0
- data/sample/test-timeout3 +9 -0
- data/sample/test-upload +13 -0
- data/sample/test-warn +13 -0
- data/setup/upload-rsync-ssh +572 -0
- data/test/misc/test-escape.rb +17 -0
- data/test/misc/test-logfile.rb +108 -0
- data/test/misc/test-timeoutcom.rb +23 -0
- data/test/test_helper.rb +9 -0
- metadata +123 -0
data/lib/misc/gdb.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Copyright (C) 2005,2006,2009 Tanaka Akira <akr@fsij.org>
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
5
|
+
#
|
6
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
7
|
+
# list of conditions and the following disclaimer.
|
8
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer in the documentation
|
10
|
+
# and/or other materials provided with the distribution.
|
11
|
+
# 3. The name of the author may not be used to endorse or promote products
|
12
|
+
# derived from this software without specific prior written permission.
|
13
|
+
#
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
15
|
+
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
16
|
+
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
17
|
+
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
18
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
19
|
+
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
20
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
21
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
22
|
+
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
23
|
+
# OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
require 'find'
|
26
|
+
require 'tempfile'
|
27
|
+
|
28
|
+
require 'escape'
|
29
|
+
|
30
|
+
module GDB
|
31
|
+
module_function
|
32
|
+
|
33
|
+
def check_core(dir)
|
34
|
+
binaries = {}
|
35
|
+
core_info = []
|
36
|
+
Find.find(dir.to_s) {|f|
|
37
|
+
stat = File.stat(f)
|
38
|
+
basename = File.basename(f)
|
39
|
+
binaries[basename] = f if stat.file? && stat.executable?
|
40
|
+
next if /\bcore\b/ !~ basename
|
41
|
+
next if /\.chkbuild\.\d+\z/ =~ basename
|
42
|
+
guess = `file #{f} 2>&1`
|
43
|
+
next if /\bcore\b.*from '(.*?)'/ !~ guess.sub(/\A.*?:/, '')
|
44
|
+
core_info << [f, $1]
|
45
|
+
}
|
46
|
+
gdb_command = nil
|
47
|
+
core_info.each {|core_path, binary|
|
48
|
+
next unless binary_path = binaries[binary]
|
49
|
+
core_path = rename_core(core_path)
|
50
|
+
unless gdb_command
|
51
|
+
gdb_command = Tempfile.new("gdb-bt")
|
52
|
+
gdb_command.puts "bt"
|
53
|
+
gdb_command.close
|
54
|
+
end
|
55
|
+
puts
|
56
|
+
puts "binary: #{binary_path}"
|
57
|
+
puts "core: #{core_path}"
|
58
|
+
command = %W[gdb -batch -n -x #{gdb_command.path} #{binary_path} #{core_path}]
|
59
|
+
gdb_output = `#{Escape.shell_command command}`
|
60
|
+
puts gdb_output
|
61
|
+
puts "gdb status: #{$?}"
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def rename_core(core_path)
|
66
|
+
suffix = ".chkbuild."
|
67
|
+
n = 1
|
68
|
+
while File.exist?(new_path = "#{core_path}.chkbuild.#{n}")
|
69
|
+
n += 1
|
70
|
+
end
|
71
|
+
File.rename(core_path, new_path)
|
72
|
+
new_path
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# Copyright (C) 2005,2006,2007,2008,2009 Tanaka Akira <akr@fsij.org>
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
5
|
+
#
|
6
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
7
|
+
# list of conditions and the following disclaimer.
|
8
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer in the documentation
|
10
|
+
# and/or other materials provided with the distribution.
|
11
|
+
# 3. The name of the author may not be used to endorse or promote products
|
12
|
+
# derived from this software without specific prior written permission.
|
13
|
+
#
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
15
|
+
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
16
|
+
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
17
|
+
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
18
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
19
|
+
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
20
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
21
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
22
|
+
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
23
|
+
# OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
class CommandTimeout < StandardError
|
26
|
+
end
|
27
|
+
|
28
|
+
module TimeoutCommand
|
29
|
+
|
30
|
+
module_function
|
31
|
+
|
32
|
+
def parse_timespan(arg)
|
33
|
+
case arg
|
34
|
+
when Integer, Float
|
35
|
+
timeout = arg
|
36
|
+
when Time
|
37
|
+
timeout = arg - Time.now
|
38
|
+
when /\A\d+(\.\d+)?(?:s|sec)?\z/
|
39
|
+
timeout = $&.to_f
|
40
|
+
when /\A\d+(\.\d+)?(?:m|min)\z/
|
41
|
+
timeout = $&.to_f * 60
|
42
|
+
when /\A\d+(\.\d+)?(?:h|hour)\z/
|
43
|
+
timeout = $&.to_f * 60 * 60
|
44
|
+
when /\A\d+(\.\d+)?(?:d|day)\z/
|
45
|
+
timeout = $&.to_f * 60 * 60 * 24
|
46
|
+
else
|
47
|
+
raise ArgumentError, "invalid time span: #{arg.inspect}"
|
48
|
+
end
|
49
|
+
timeout
|
50
|
+
end
|
51
|
+
|
52
|
+
def kill_processgroup(pgid, msgout)
|
53
|
+
begin
|
54
|
+
Process.kill('INT', -pgid)
|
55
|
+
msgout.puts "timeout: INT signal sent." if msgout
|
56
|
+
signals = ['INT', 'TERM', 'TERM', 'KILL']
|
57
|
+
signals.each {|sig|
|
58
|
+
Process.kill(0, -pgid); sleep 0.1
|
59
|
+
Process.kill(0, -pgid); sleep 0.2
|
60
|
+
Process.kill(0, -pgid); sleep 0.3
|
61
|
+
Process.kill(0, -pgid); sleep 0.4
|
62
|
+
Process.kill(0, -pgid)
|
63
|
+
4.times {
|
64
|
+
sleep 1
|
65
|
+
Process.kill(0, -pgid)
|
66
|
+
}
|
67
|
+
Process.kill(sig, -pgid)
|
68
|
+
msgout.puts "timeout: #{sig} signal sent." if msgout
|
69
|
+
}
|
70
|
+
rescue Errno::ESRCH # no process i.e. success to kill
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def process_alive?(pid)
|
75
|
+
begin
|
76
|
+
Process.kill(0, pid)
|
77
|
+
rescue Errno::ESRCH # no process
|
78
|
+
return false
|
79
|
+
end
|
80
|
+
return true
|
81
|
+
end
|
82
|
+
|
83
|
+
def processgroup_alive?(pgid)
|
84
|
+
process_alive?(-pgid)
|
85
|
+
end
|
86
|
+
|
87
|
+
def last_output_time
|
88
|
+
last_output_time = [STDOUT, STDERR].map {|f|
|
89
|
+
s = f.stat
|
90
|
+
if s.file?
|
91
|
+
s.mtime
|
92
|
+
else
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
}.compact
|
96
|
+
if last_output_time.empty?
|
97
|
+
nil
|
98
|
+
else
|
99
|
+
last_output_time.max
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def timeout_command(command_timeout, msgout=STDERR, opts={})
|
104
|
+
command_timeout = parse_timespan(command_timeout)
|
105
|
+
output_interval_timeout = nil
|
106
|
+
if opts[:output_interval_timeout]
|
107
|
+
output_interval_timeout = parse_timespan(opts[:output_interval_timeout])
|
108
|
+
end
|
109
|
+
if command_timeout < 0
|
110
|
+
raise CommandTimeout, 'no time to run a command'
|
111
|
+
end
|
112
|
+
pid = fork {
|
113
|
+
Process.setpgid($$, $$)
|
114
|
+
yield
|
115
|
+
}
|
116
|
+
begin
|
117
|
+
Process.setpgid(pid, pid)
|
118
|
+
rescue Errno::EACCES # already execed.
|
119
|
+
rescue Errno::ESRCH # already exited. (setpgid for a zombie fails on OpenBSD)
|
120
|
+
end
|
121
|
+
wait_thread = Thread.new {
|
122
|
+
Process.wait2(pid)[1]
|
123
|
+
}
|
124
|
+
begin
|
125
|
+
start_time = Time.now
|
126
|
+
limit_time = start_time + command_timeout
|
127
|
+
command_status = nil
|
128
|
+
while true
|
129
|
+
join_timeout = limit_time - Time.now
|
130
|
+
if join_timeout < 0
|
131
|
+
timeout_reason = "command execution time exceeds #{command_timeout} seconds."
|
132
|
+
break
|
133
|
+
end
|
134
|
+
if output_interval_timeout and
|
135
|
+
t = last_output_time and
|
136
|
+
(tmp_join_timeout = t + output_interval_timeout - Time.now) < join_timeout
|
137
|
+
join_timeout = tmp_join_timeout
|
138
|
+
if join_timeout < 0
|
139
|
+
timeout_reason = "output interval exceeds #{output_interval_timeout} seconds."
|
140
|
+
break
|
141
|
+
end
|
142
|
+
end
|
143
|
+
if wait_thread.join(join_timeout)
|
144
|
+
command_status = wait_thread.value
|
145
|
+
break
|
146
|
+
end
|
147
|
+
end
|
148
|
+
if command_status
|
149
|
+
return command_status
|
150
|
+
else
|
151
|
+
msgout.puts "timeout: #{timeout_reason}" if msgout
|
152
|
+
begin
|
153
|
+
Process.kill(0, -pid)
|
154
|
+
msgout.puts "timeout: the process group #{pid} is alive." if msgout
|
155
|
+
kill_processgroup(pid, msgout)
|
156
|
+
rescue Errno::ESRCH # no process
|
157
|
+
end
|
158
|
+
raise CommandTimeout, timeout_reason
|
159
|
+
end
|
160
|
+
rescue Interrupt
|
161
|
+
Process.kill("INT", -pid)
|
162
|
+
raise
|
163
|
+
rescue SignalException
|
164
|
+
Process.kill($!.message, -pid)
|
165
|
+
raise
|
166
|
+
ensure
|
167
|
+
if processgroup_alive?(pid)
|
168
|
+
msgout.puts "some descendant process in process group #{pid} remain." if msgout
|
169
|
+
kill_processgroup(pid, msgout)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
data/lib/misc/udiff.rb
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
# Copyright (C) 2005,2006,2007,2008 Tanaka Akira <akr@fsij.org>
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
5
|
+
#
|
6
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
7
|
+
# list of conditions and the following disclaimer.
|
8
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer in the documentation
|
10
|
+
# and/or other materials provided with the distribution.
|
11
|
+
# 3. The name of the author may not be used to endorse or promote products
|
12
|
+
# derived from this software without specific prior written permission.
|
13
|
+
#
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
15
|
+
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
16
|
+
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
17
|
+
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
18
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
19
|
+
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
20
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
21
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
22
|
+
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
23
|
+
# OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
require 'escape'
|
26
|
+
require 'tempfile'
|
27
|
+
|
28
|
+
class UDiff
|
29
|
+
def UDiff.diff(path1, path2, out, header="--- #{path1}\n+++ #{path2}\n")
|
30
|
+
UDiff.new(path1, path2, out, header).diff
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(path1, path2, out, header)
|
34
|
+
@path1 = path1
|
35
|
+
@path2 = path2
|
36
|
+
@out = out
|
37
|
+
@header = header
|
38
|
+
@context = 3
|
39
|
+
@beginning = true
|
40
|
+
@l1 = 0
|
41
|
+
@l2 = 0
|
42
|
+
@hunk_beg = [@l1, @l2]
|
43
|
+
@hunk = []
|
44
|
+
@lines_hash = {}
|
45
|
+
@lines_ary = []
|
46
|
+
end
|
47
|
+
|
48
|
+
def puts_line(line)
|
49
|
+
@hunk << line
|
50
|
+
if /\n\z/ !~ line
|
51
|
+
@hunk << "\n\\n"
|
52
|
+
end
|
53
|
+
@beginning = false
|
54
|
+
end
|
55
|
+
|
56
|
+
def puts_del_line(line)
|
57
|
+
line = /\A_/ =~ line ? $' : @lines_ary[line.to_i]
|
58
|
+
puts_line "-#{line}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def puts_add_line(line)
|
62
|
+
line = /\A_/ =~ line ? $' : @lines_ary[line.to_i]
|
63
|
+
puts_line "+#{line}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def puts_common_line(line)
|
67
|
+
line = /\A_/ =~ line ? $' : @lines_ary[line.to_i]
|
68
|
+
puts_line " #{line}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def encdump(str)
|
72
|
+
d = str.dump
|
73
|
+
if str.respond_to? :encoding
|
74
|
+
"#{d}.force_encoding(#{str.encoding.name.dump})"
|
75
|
+
else
|
76
|
+
d
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def gets_common_line(f1, f2)
|
81
|
+
v1 = f1.gets
|
82
|
+
v2 = f2.gets
|
83
|
+
if v1 != v2
|
84
|
+
raise "[bug] diff error: #{encdump v1} != #{encdump v2}"
|
85
|
+
end
|
86
|
+
if v1
|
87
|
+
@l1 += 1
|
88
|
+
@l2 += 1
|
89
|
+
end
|
90
|
+
return v1
|
91
|
+
end
|
92
|
+
|
93
|
+
def copy_common_part(f1, f2, n)
|
94
|
+
n.times {|i|
|
95
|
+
v = gets_common_line(f1, f2)
|
96
|
+
raise "[bug] diff error: unexpected EOF" unless v
|
97
|
+
puts_common_line(v)
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def skip_common_part(f1, f2, n)
|
102
|
+
n.times {|i|
|
103
|
+
v = gets_common_line(f1, f2)
|
104
|
+
raise "[bug] diff error: unexpected EOF" unless v
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def output_hunk
|
109
|
+
if @header
|
110
|
+
@out.print @header
|
111
|
+
@header = nil
|
112
|
+
end
|
113
|
+
l1_beg, l2_beg = @hunk_beg
|
114
|
+
@out.print "@@ -#{l1_beg+1},#{@l1-l1_beg} +#{l2_beg+1},#{@l2-l2_beg} @@\n"
|
115
|
+
@hunk.each {|s|
|
116
|
+
@out.print s
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
def output_common_part(f1, f2, common_num)
|
121
|
+
if @beginning
|
122
|
+
if common_num <= @context
|
123
|
+
copy_common_part(f1, f2, common_num)
|
124
|
+
else
|
125
|
+
skip_common_part(f1, f2, common_num-@context)
|
126
|
+
copy_common_part(f1, f2, @context)
|
127
|
+
end
|
128
|
+
elsif common_num <= @context * 2
|
129
|
+
copy_common_part(f1, f2, common_num)
|
130
|
+
else
|
131
|
+
copy_common_part(f1, f2, @context)
|
132
|
+
output_hunk
|
133
|
+
skip_common_part(f1, f2, common_num-@context*2)
|
134
|
+
@hunk_beg = [@l1, @l2]
|
135
|
+
@hunk = []
|
136
|
+
copy_common_part(f1, f2, @context)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def output_common_tail(f1, f2)
|
141
|
+
return if @beginning
|
142
|
+
@context.times {
|
143
|
+
v = gets_common_line(f1, f2)
|
144
|
+
break unless v
|
145
|
+
puts_common_line(v)
|
146
|
+
}
|
147
|
+
output_hunk
|
148
|
+
end
|
149
|
+
|
150
|
+
def process_commands(f1, f2, d)
|
151
|
+
has_diff = false
|
152
|
+
l1 = 0
|
153
|
+
while com = d.gets
|
154
|
+
case com
|
155
|
+
when /\Ad(\d+) (\d+)/
|
156
|
+
line = $1.to_i
|
157
|
+
num = $2.to_i
|
158
|
+
output_common_part(f1, f2, line-l1-1)
|
159
|
+
num.times {
|
160
|
+
v = f1.gets
|
161
|
+
@l1 += 1
|
162
|
+
puts_del_line(v)
|
163
|
+
has_diff = true
|
164
|
+
}
|
165
|
+
l1 = line + num - 1
|
166
|
+
when /\Aa(\d+) (\d+)/
|
167
|
+
line = $1.to_i
|
168
|
+
num = $2.to_i
|
169
|
+
common_num = line-l1
|
170
|
+
output_common_part(f1, f2, line-l1)
|
171
|
+
l1 = line
|
172
|
+
num.times {
|
173
|
+
v1 = d.gets
|
174
|
+
v2 = f2.gets
|
175
|
+
if v1 != v2
|
176
|
+
raise "[bug] diff error: #{encdump v1} != #{encdump v2}"
|
177
|
+
end
|
178
|
+
@l2 += 1
|
179
|
+
v = v1
|
180
|
+
puts_add_line(v)
|
181
|
+
has_diff = true
|
182
|
+
}
|
183
|
+
else
|
184
|
+
raise "[bug] unexpected diff line: #{com.inspect}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
has_diff
|
188
|
+
end
|
189
|
+
|
190
|
+
def run_diff(path1, path2)
|
191
|
+
has_diff = false
|
192
|
+
open(path1) {|f1|
|
193
|
+
f1.set_encoding "ascii-8bit" if f1.respond_to? :set_encoding
|
194
|
+
open(path2) {|f2|
|
195
|
+
f2.set_encoding "ascii-8bit" if f2.respond_to? :set_encoding
|
196
|
+
command = Escape.shell_command(%W[diff -n #{path1} #{path2}]).to_s
|
197
|
+
command = "LC_ALL='C' LANG='C' #{command}"
|
198
|
+
IO.popen(command) {|d|
|
199
|
+
d.set_encoding "ascii-8bit" if d.respond_to? :set_encoding
|
200
|
+
has_diff = process_commands(f1, f2, d)
|
201
|
+
}
|
202
|
+
output_common_tail(f1, f2)
|
203
|
+
}
|
204
|
+
}
|
205
|
+
has_diff
|
206
|
+
end
|
207
|
+
|
208
|
+
SAFE_LINE = /\A[\t -~]*\n\z/
|
209
|
+
def diff
|
210
|
+
t1 = Tempfile.new("udiff")
|
211
|
+
File.foreach(@path1) {|l|
|
212
|
+
if SAFE_LINE =~ l
|
213
|
+
l = "_" + l
|
214
|
+
else
|
215
|
+
if !@lines_hash[l]
|
216
|
+
@lines_ary[@lines_hash.size] = l
|
217
|
+
@lines_hash[l] = @lines_hash.size
|
218
|
+
end
|
219
|
+
l = @lines_hash[l].to_s + "\n"
|
220
|
+
end
|
221
|
+
t1.puts l
|
222
|
+
}
|
223
|
+
t2 = Tempfile.new("udiff")
|
224
|
+
File.foreach(@path2) {|l|
|
225
|
+
if SAFE_LINE =~ l
|
226
|
+
l = "_" + l
|
227
|
+
else
|
228
|
+
if !@lines_hash[l]
|
229
|
+
@lines_ary[@lines_hash.size] = l
|
230
|
+
@lines_hash[l] = @lines_hash.size
|
231
|
+
end
|
232
|
+
l = @lines_hash[l].to_s + "\n"
|
233
|
+
end
|
234
|
+
t2.puts l
|
235
|
+
}
|
236
|
+
t1.close
|
237
|
+
t2.close
|
238
|
+
run_diff(t1.path, t2.path)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
if $0 == __FILE__
|
243
|
+
UDiff.diff(ARGV[0], ARGV[1], STDOUT)
|
244
|
+
end
|