diff options
Diffstat (limited to 'lib/diff/lcs/ldiff.rb')
-rw-r--r-- | lib/diff/lcs/ldiff.rb | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/lib/diff/lcs/ldiff.rb b/lib/diff/lcs/ldiff.rb new file mode 100644 index 0000000..61abc47 --- /dev/null +++ b/lib/diff/lcs/ldiff.rb @@ -0,0 +1,226 @@ +#!/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 © 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$ + 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 |