From azcb0@ccc.amdahl.com Tue Apr 11 13:29:57 1995
Newsgroups: comp.lang.tcl
Path: dove.nist.gov!uunet!news.mathworks.com!udel!gatech!howland.reston.ans.net!news.sprintlink.net!news.clark.net!rahul.net!a2i!olivea!nntp-hub2.barrnet.net!nntp-sc.barrnet.net!news.fujitsu.com!amdahl.com!juts.ccc.amdahl.com!azcb0
From: azcb0@ccc.amdahl.com (Alistair G. Crooks)
Subject: idiff - interactive diff program
Message-ID: <1995Apr11.130256.4702@ccc.amdahl.com>
Reply-To: azcb0@JUTS.ccc.amdahl.com (Alistair G. Crooks)
Organization: Amdahl Corporation, Sunnyvale CA
Date: Tue, 11 Apr 1995 13:02:56 GMT
Lines: 195

I've translated the idiff program from Kernighan & Pike's "The Unix
Programming Environment" from C into tcl.  This version of idiff uses
expect.

For those of you still a bit unsure, idiff is an interactive diff
program, taking two files, running diff on them, and then prompting
the user to decide which version of each difference to include in
the output. Individual diffs may be edited, using the user's preferred
editor, and external commands may be run. The command line to run
idiff thus looks like:

idiff file1 file2

and the resulting output is placed in the file idiff.out. Within idiff,
3 commands may be used:

>	take the right hand diff, i.e. the version from file2
<	take the left hand diff, i.e. the version from file1
e	edit the left and right hand diffs, and include the result
!	execute an external command

I've uploaded it to ftp.aud.alcatel.com:/tcl/incoming, and it should
also be available from ftp://charon.amdahl.com/pub/agc/idiff

Cheers,
Alistair
--
Alistair G. Crooks (agc@uts.amdahl.com)			   +44 125 234 6377
Amdahl European HQ, Dogmersfield Park, Hartley Wintney, Hants RG27 8TE, UK.
[These are only my opinions, and certainly not those of Amdahl Corporation]

#! /usr/local/bin/expect
#
# idiff, version 5.15
#
# An interactive diff program, written in tcl from the version
# written in C from Kernighan & Pike's "The Unix Programming Environment"
# uses expect to spawn the vi process.
#
# Translated to tcl by
# Alistair G. Crooks (agc@uts.amdahl.com)
# 11th April 1995

# parse the diff command held in `s'
proc parse { s from1name to1name cmdname from2name to2name } {
	upvar $from1name from1
	upvar $to1name to1
	upvar $cmdname cmd
	upvar $from2name from2
	upvar $to2name to2

	# don't change the order of these regexps, otherwise you'll
	# recognise the wrong expression at the wrong time
	if { [regexp -- "(\[0-9\]+),(\[0-9\]+)(\[a-z\])(\[0-9\]+),(\[0-9\]+)" \
				$s a from1 to1 cmd to1 to2] } {
		return 1
	}
	if { [regexp -- "(\[0-9\]+)(\[a-z\])(\[0-9\]+),(\[0-9\]+)" \
				$s a from1 cmd from2 to2] } {
		set to1 $from1
		return 1
	}
	if { [regexp -- "(\[0-9\]+),(\[0-9\]+)(\[a-z\])(\[0-9\]+)" \
				$s a from1 to1 cmd from2] } {
		set to2 $from2
		return 1
	}
	if { [regexp -- "(\[0-9\]+)(\[a-z\])(\[0-9\]+)" \
				$s a from1 cmd from2] } {
		set to1 $from1
		set to2 $from2
		return 1
	}
	return 0
}

# skip `n' lines from `fin'
proc nskip { fin n } {
	for {} { $n > 0 } { incr n -1 } {
		gets $fin buf
	}
}

# copy `n' lines from `fin' to `fout'
proc ncopy { fin n fout } {
	if { $n == "all" } {
		while { [gets $fin buf] >= 0 } {
			puts $fout "$buf"
		}
	} else {
		for {} { $n > 0 } { incr n -1 } {
			if { [gets $fin buf] < 0 } {
				return
			}
			puts $fout "$buf"
		}
	}
}

# produce the interactive diff of files `f1' and `f2' from `fin' to `fout'
proc idiff { f1 f2 fin fout } {
	set nf1 0
	set nf2 0
	while { [gets $fin buf] >= 0 } {
		# parse the diff command
		if { ![parse $buf from1 to1 cmd from2 to2] } {
			break
		}
		# calculate the number of lines to print out
		set n [expr $to1-$from1+$to2-$from2+1]
		switch $cmd {
		c	{ incr n 2 }
		a	{ incr from1 1 }
		d	{ incr from2 1 }
		}
		# print out the diff
		puts stdout "$buf"
		for {} { $n > 0 } { incr n -1 } {
			gets $fin buf
			puts stdout $buf
		}
		while 1 {
			puts -nonewline stdout "? "
			gets stdin in
			switch -regexp -- $in {
			^\>	{
					nskip $f1 [expr $to1-$nf1]
					ncopy $f2 [expr $to2-$nf2] $fout
					break
				}
			^\<	{
					nskip $f2 [expr $to2-$nf2]
					ncopy $f1 [expr $to1-$nf1] $fout
					break
				}
			^e	{
					ncopy $f1 [expr $from1-1-$nf1] $fout
					nskip $f2 [expr $from2-1-$nf2]
					set tmp [open "idiff.tmp" "w"]
					ncopy $f1 [expr $to1+1-$from1] $tmp
					puts $tmp "---"
					ncopy $f2 [expr $to2+1-$from2] $tmp
					close $tmp
					global editor
					set pid [spawn $editor idiff.tmp]
					interact
					set tmp [open "idiff.tmp"]
					ncopy $tmp all $fout
					close $tmp
					exec rm idiff.tmp
					break
				}
			^!	{
					set pid [eval spawn \
						[string range $in 1 end]]
					interact
				}
			default	{
					puts stderr "Use `<', `>', `e' or `!'"
				}
			}
		}
		set nf1 $to1
		set nf2 $to2
	}
	ncopy $f1 all $fout
}

# check the command line arguments
if { [llength $argv] != 2 } {
	puts stderr "Usage: $argv0 file1 file2"
	exit 1
}

# find the user's editor
set editor "vi"
if { [info exists env(EDITOR)] } {
	set editor $env(EDITOR)
}
if { [info exists env(VISUAL)] } {
	set editor $env(VISUAL)
}

# open files
set fp1 [open [lindex $argv 0]]
set fp2 [open [lindex $argv 1]]

# open the diff pipeline
set fin [open "| diff [lindex $argv 0] [lindex $argv 1]"]
set fout [open idiff.out w]
idiff $fp1 $fp2 $fin $fout
puts stdout "Output in file idiff.out"
exit 0



