227 lines
7.2 KiB
Ruby
227 lines
7.2 KiB
Ruby
|
#!/usr/bin/env ruby
|
|||
|
|
|||
|
require 'optparse'
|
|||
|
require 'ostruct'
|
|||
|
require 'diff/lcs/hunk'
|
|||
|
|
|||
|
# == ldiff Usage
|
|||
|
# ldiff [options] oldfile newfile
|
|||
|
#
|
|||
|
# -c:: Displays a context diff with 3 lines of context.
|
|||
|
# -C [LINES], --context [LINES]:: Displays a context diff with LINES lines of context. Default 3 lines.
|
|||
|
# -u:: Displays a unified diff with 3 lines of context.
|
|||
|
# -U [LINES], --unified [LINES]:: Displays a unified diff with LINES lines of context. Default 3 lines.
|
|||
|
# -e:: Creates an 'ed' script to change oldfile to newfile.
|
|||
|
# -f:: Creates an 'ed' script to change oldfile to newfile in reverse order.
|
|||
|
# -a, --text:: Treats the files as text and compares them line-by-line, even if they do not seem to be text.
|
|||
|
# --binary:: Treats the files as binary.
|
|||
|
# -q, --brief:: Reports only whether or not the files differ, not the details.
|
|||
|
# --help:: Shows the command-line help.
|
|||
|
# --version:: Shows the version of Diff::LCS.
|
|||
|
#
|
|||
|
# By default, runs produces an "old-style" diff, with output like UNIX diff.
|
|||
|
#
|
|||
|
# == Copyright
|
|||
|
# Copyright © 2004 Austin Ziegler
|
|||
|
#
|
|||
|
# Part of Diff::LCS <http://rubyforge.org/projects/ruwiki/>
|
|||
|
# Austin Ziegler <diff-lcs@halostatue.ca>
|
|||
|
#
|
|||
|
# This program is free software. It may be redistributed and/or modified under
|
|||
|
# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
|
|||
|
# Ruby licence.
|
|||
|
module Diff::LCS::Ldiff
|
|||
|
BANNER = <<-COPYRIGHT
|
|||
|
ldiff #{Diff::LCS::VERSION}
|
|||
|
Copyright <EFBFBD> 2004 Austin Ziegler
|
|||
|
|
|||
|
Part of Diff::LCS.
|
|||
|
http://rubyforge.org/projects/ruwiki/
|
|||
|
|
|||
|
Austin Ziegler <diff-lcs@halostatue.ca>
|
|||
|
|
|||
|
This program is free software. It may be redistributed and/or modified under
|
|||
|
the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
|
|||
|
Ruby licence.
|
|||
|
|
|||
|
$Id: ldiff.rb,v 1.1 2004/09/26 01:37:49 austin Exp $
|
|||
|
COPYRIGHT
|
|||
|
|
|||
|
class << self
|
|||
|
attr_reader :format, :lines #:nodoc:
|
|||
|
attr_reader :file_old, :file_new #:nodoc:
|
|||
|
attr_reader :data_old, :data_new #:nodoc:
|
|||
|
|
|||
|
def run(args, input = $stdin, output = $stdout, error = $stderr) #:nodoc:
|
|||
|
args.options do |o|
|
|||
|
o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile"
|
|||
|
o.separator ""
|
|||
|
o.on('-c',
|
|||
|
'Displays a context diff with 3 lines of',
|
|||
|
'context.') do |ctx|
|
|||
|
@format = :context
|
|||
|
@lines = 3
|
|||
|
end
|
|||
|
o.on('-C', '--context [LINES]', Numeric,
|
|||
|
'Displays a context diff with LINES lines',
|
|||
|
'of context. Default 3 lines.') do |ctx|
|
|||
|
@format = :context
|
|||
|
@lines = ctx || 3
|
|||
|
end
|
|||
|
o.on('-u',
|
|||
|
'Displays a unified diff with 3 lines of',
|
|||
|
'context.') do |ctx|
|
|||
|
@format = :unified
|
|||
|
@lines = 3
|
|||
|
end
|
|||
|
o.on('-U', '--unified [LINES]', Numeric,
|
|||
|
'Displays a unified diff with LINES lines',
|
|||
|
'of context. Default 3 lines.') do |ctx|
|
|||
|
@format = :unified
|
|||
|
@lines = ctx || 3
|
|||
|
end
|
|||
|
o.on('-e',
|
|||
|
'Creates an \'ed\' script to change',
|
|||
|
'oldfile to newfile.') do |ctx|
|
|||
|
@format = :ed
|
|||
|
end
|
|||
|
o.on('-f',
|
|||
|
'Creates an \'ed\' script to change',
|
|||
|
'oldfile to newfile in reverse order.') do |ctx|
|
|||
|
@format = :reverse_ed
|
|||
|
end
|
|||
|
o.on('-a', '--text',
|
|||
|
'Treat the files as text and compare them',
|
|||
|
'line-by-line, even if they do not seem',
|
|||
|
'to be text.') do |txt|
|
|||
|
@binary = false
|
|||
|
end
|
|||
|
o.on('--binary',
|
|||
|
'Treats the files as binary.') do |bin|
|
|||
|
@binary = true
|
|||
|
end
|
|||
|
o.on('-q', '--brief',
|
|||
|
'Report only whether or not the files',
|
|||
|
'differ, not the details.') do |ctx|
|
|||
|
@format = :report
|
|||
|
end
|
|||
|
o.on_tail('--help', 'Shows this text.') do
|
|||
|
error << o
|
|||
|
return 0
|
|||
|
end
|
|||
|
o.on_tail('--version', 'Shows the version of Diff::LCS.') do
|
|||
|
error << BANNER
|
|||
|
return 0
|
|||
|
end
|
|||
|
o.on_tail ""
|
|||
|
o.on_tail 'By default, runs produces an "old-style" diff, with output like UNIX diff.'
|
|||
|
o.parse!
|
|||
|
end
|
|||
|
|
|||
|
unless args.size == 2
|
|||
|
error << args.options
|
|||
|
return 127
|
|||
|
end
|
|||
|
|
|||
|
# Defaults are for old-style diff
|
|||
|
@format ||= :old
|
|||
|
@lines ||= 0
|
|||
|
|
|||
|
file_old, file_new = *ARGV
|
|||
|
|
|||
|
case @format
|
|||
|
when :context
|
|||
|
char_old = '*' * 3
|
|||
|
char_new = '-' * 3
|
|||
|
when :unified
|
|||
|
char_old = '-' * 3
|
|||
|
char_new = '+' * 3
|
|||
|
end
|
|||
|
|
|||
|
# After we've read up to a certain point in each file, the number of
|
|||
|
# items we've read from each file will differ by FLD (could be 0).
|
|||
|
file_length_difference = 0
|
|||
|
|
|||
|
if @binary.nil? or @binary
|
|||
|
data_old = IO::read(file_old)
|
|||
|
data_new = IO::read(file_new)
|
|||
|
|
|||
|
# Test binary status
|
|||
|
if @binary.nil?
|
|||
|
old_txt = data_old[0...4096].grep(/\0/).empty?
|
|||
|
new_txt = data_new[0...4096].grep(/\0/).empty?
|
|||
|
@binary = (not old_txt) or (not new_txt)
|
|||
|
old_txt = new_txt = nil
|
|||
|
end
|
|||
|
|
|||
|
unless @binary
|
|||
|
data_old = data_old.split(/\n/).map! { |e| e.chomp }
|
|||
|
data_new = data_new.split(/\n/).map! { |e| e.chomp }
|
|||
|
end
|
|||
|
else
|
|||
|
data_old = IO::readlines(file_old).map! { |e| e.chomp }
|
|||
|
data_new = IO::readlines(file_new).map! { |e| e.chomp }
|
|||
|
end
|
|||
|
|
|||
|
# diff yields lots of pieces, each of which is basically a Block object
|
|||
|
if @binary
|
|||
|
diffs = (data_old == data_new)
|
|||
|
else
|
|||
|
diffs = Diff::LCS.diff(data_old, data_new)
|
|||
|
diffs = nil if diffs.empty?
|
|||
|
end
|
|||
|
|
|||
|
return 0 unless diffs
|
|||
|
|
|||
|
if (@format == :report) and diffs
|
|||
|
output << "Files #{file_old} and #{file_new} differ\n"
|
|||
|
return 1
|
|||
|
end
|
|||
|
|
|||
|
if (@format == :unified) or (@format == :context)
|
|||
|
ft = File.stat(file_old).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S %z')
|
|||
|
puts "#{char_old} #{file_old}\t#{ft}"
|
|||
|
ft = File.stat(file_new).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S %z')
|
|||
|
puts "#{char_new} #{file_new}\t#{ft}"
|
|||
|
end
|
|||
|
|
|||
|
# Loop over hunks. If a hunk overlaps with the last hunk, join them.
|
|||
|
# Otherwise, print out the old one.
|
|||
|
oldhunk = hunk = nil
|
|||
|
|
|||
|
if @format == :ed
|
|||
|
real_output = output
|
|||
|
output = []
|
|||
|
end
|
|||
|
|
|||
|
diffs.each do |piece|
|
|||
|
begin
|
|||
|
hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @lines,
|
|||
|
file_length_difference)
|
|||
|
file_length_difference = hunk.file_length_difference
|
|||
|
|
|||
|
next unless oldhunk
|
|||
|
|
|||
|
if (@lines > 0) and hunk.overlaps?(oldhunk)
|
|||
|
hunk.unshift(oldhunk)
|
|||
|
else
|
|||
|
output << oldhunk.diff(@format)
|
|||
|
end
|
|||
|
ensure
|
|||
|
oldhunk = hunk
|
|||
|
output << "\n"
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
output << oldhunk.diff(@format)
|
|||
|
output << "\n"
|
|||
|
|
|||
|
if @format == :ed
|
|||
|
output.reverse_each { |e| real_output << e.diff(:ed_finish) }
|
|||
|
end
|
|||
|
|
|||
|
return 1
|
|||
|
end
|
|||
|
end
|
|||
|
end
|