#! /usr/local/bin/gawk -f
# NAME
#
# idiff -- interactive diff
#
# USAGE
#
# idiff file1 file2 -- interactively merge file differences
#
# DESCRIPTION
#
# idiff takes two files file1 and file2, diffs them, and presents
# the difference to the user interactively. At each difference,
# the user may accept the text from file1 by responding <, or
# accept the text from file2 by responding >, or edit the difference
# by responding e (in which case whatever the user saves from the
# editor is entered into the output file), or execute a command
# by typing !cmd, in which case the user must then respond when
# the prompt is repeated. The assembled output with the selected
# differences is placed in file idiff.out.
#
# EXAMPLE
#
# $ cat file1
# This is
# a test
# of
# your
# skill
# and comprehension.
# $ cat file2
# This is
# not a test
# of
# our
# ability.
# $ diff file1 file2
# 2c2
# < a test
# ---
# > not a test
# 4,6d4,5
# < your
# < skill
# < and comprehension.
# ---
# > our
# > ability.
# $ idiff file1 file2
# 2c2
# < a test
# ---
# > not a test
# ? >
# 4,6c4,5
# < your
# < skill
# < and comprehension.
# ---
# > our
# > ability.
# ? <
# $ cat idiff.out
# This is
# not a test
# of
# your
# skill
# and comprehension.\014
BEGIN {
ed = ENVIRON["EDITOR"]; if (ed == "") { ed = "ed" }
outfile = "idiff.out"; tempfile = "idiff.tmp"; diffile = "idiff.dif"
file1 = ARGV[1]; file2 = ARGV[2]; ARGV[1] = ARGV[2] = ""
system("diff " file1 " " file2 " > " diffile)
nf1 = nf2 = 0
while (getline difline < diffile > 0) {
cmd = substr(difline, match(difline, /[acd]/), 1)
split(difline, linenums, cmd)
split(linenums[1], ones, ","); split(linenums[2], twos, ",")
from1 = ones[1]; to1 = ones[2] > 0 ? ones[2] : ones[1]
from2 = twos[1]; to2 = twos[2] > 0 ? twos[2] : twos[1]
nlines = to1-from1 + to2-from2 + 1
if (cmd == "a") { from1++ }
else if (cmd == "c") { nlines += 2 }
else if (cmd == "d") { from2++ }
print difline
while (nlines-- > 0) { getline difline < diffile; print difline }
do {
printf("? "); getline buf
if (substr(buf, 1, 1) == ">") {
nskip(file1, to1-nf1); ncopy(file2, to2-nf2, outfile) }
else if (substr(buf, 1, 1) == "<") {
nskip(file2, to2-nf2); ncopy(file1, to1-nf1, outfile) }
else if (substr(buf, 1, 1) == "e") {
ncopy(file1, from1-1-nf1, outfile)
nskip(file2, from2-1-nf2)
ncopy(file1, to1+1-from1, tempfile)
print "---" > tempfile
ncopy(file2, to2+1-from2, tempfile)
close(tempfile)
system(ed " " tempfile)
ncopy(tempfile, -1, outfile)
close(tempfile) }
else if (substr(buf, 1, 1) == "!") {
system(substr(buf, 2)); print "!" }
else { print "Enter < or > or e or !cmd" }
} while (substr(buf, 1, 1) !~ /[><e]/)
nf1 = to1; nf2 = to2
}
system("rm " tempfile " " diffile)
}
function nskip(fin, n, junk) {
while (n-- != 0) { getline junk < fin } }
function ncopy(fin, n, fout, line) {
while ((n-- != 0) && (getline line < fin > 0)) { print line > fout } }
IyEgL3Vzci9sb2NhbC9iaW4vZ2F3ayAtZgoKIyAgIE5BTUUKIwojICAgICAgIGlkaWZmIC0tIGludGVyYWN0aXZlIGRpZmYKIwojICAgVVNBR0UKIwojICAgICAgIGlkaWZmIGZpbGUxIGZpbGUyIC0tIGludGVyYWN0aXZlbHkgbWVyZ2UgZmlsZSBkaWZmZXJlbmNlcwojCiMgICBERVNDUklQVElPTgojCiMgICAgICAgaWRpZmYgdGFrZXMgdHdvIGZpbGVzIGZpbGUxIGFuZCBmaWxlMiwgZGlmZnMgdGhlbSwgYW5kIHByZXNlbnRzCiMgICAgICAgdGhlIGRpZmZlcmVuY2UgdG8gdGhlIHVzZXIgaW50ZXJhY3RpdmVseS4gQXQgZWFjaCBkaWZmZXJlbmNlLAojICAgICAgIHRoZSB1c2VyIG1heSBhY2NlcHQgdGhlIHRleHQgZnJvbSBmaWxlMSBieSByZXNwb25kaW5nIDwsIG9yCiMgICAgICAgYWNjZXB0IHRoZSB0ZXh0IGZyb20gZmlsZTIgYnkgcmVzcG9uZGluZyA+LCBvciBlZGl0IHRoZSBkaWZmZXJlbmNlCiMgICAgICAgYnkgcmVzcG9uZGluZyBlIChpbiB3aGljaCBjYXNlIHdoYXRldmVyIHRoZSB1c2VyIHNhdmVzIGZyb20gdGhlCiMgICAgICAgZWRpdG9yIGlzIGVudGVyZWQgaW50byB0aGUgb3V0cHV0IGZpbGUpLCBvciBleGVjdXRlIGEgY29tbWFuZAojICAgICAgIGJ5IHR5cGluZyAhY21kLCBpbiB3aGljaCBjYXNlIHRoZSB1c2VyIG11c3QgdGhlbiByZXNwb25kIHdoZW4KIyAgICAgICB0aGUgcHJvbXB0IGlzIHJlcGVhdGVkLiBUaGUgYXNzZW1ibGVkIG91dHB1dCB3aXRoIHRoZSBzZWxlY3RlZAojICAgICAgIGRpZmZlcmVuY2VzIGlzIHBsYWNlZCBpbiBmaWxlIGlkaWZmLm91dC4KIwojICAgRVhBTVBMRQojCiMgICAgICAgJCBjYXQgZmlsZTEKIyAgICAgICBUaGlzIGlzCiMgICAgICAgYSB0ZXN0CiMgICAgICAgb2YKIyAgICAgICB5b3VyCiMgICAgICAgc2tpbGwKIyAgICAgICBhbmQgY29tcHJlaGVuc2lvbi4KIyAgICAgICAkIGNhdCBmaWxlMgojICAgICAgIFRoaXMgaXMKIyAgICAgICBub3QgYSB0ZXN0CiMgICAgICAgb2YKIyAgICAgICBvdXIKIyAgICAgICBhYmlsaXR5LgojICAgICAgICQgZGlmZiBmaWxlMSBmaWxlMgojICAgICAgIDJjMgojICAgICAgIDwgYSB0ZXN0CiMgICAgICAgLS0tCiMgICAgICAgPiBub3QgYSB0ZXN0CiMgICAgICAgNCw2ZDQsNQojICAgICAgIDwgeW91cgojICAgICAgIDwgc2tpbGwKIyAgICAgICA8IGFuZCBjb21wcmVoZW5zaW9uLgojICAgICAgIC0tLQojICAgICAgID4gb3VyCiMgICAgICAgPiBhYmlsaXR5LgojICAgICAgICQgaWRpZmYgZmlsZTEgZmlsZTIKIyAgICAgICAyYzIKIyAgICAgICA8IGEgdGVzdAojICAgICAgIC0tLQojICAgICAgID4gbm90IGEgdGVzdAojICAgICAgID8gPgojICAgICAgIDQsNmM0LDUKIyAgICAgICA8IHlvdXIKIyAgICAgICA8IHNraWxsCiMgICAgICAgPCBhbmQgY29tcHJlaGVuc2lvbi4KIyAgICAgICAtLS0KIyAgICAgICA+IG91cgojICAgICAgID4gYWJpbGl0eS4KIyAgICAgICA/IDwKIyAgICAgICAkIGNhdCBpZGlmZi5vdXQKIyAgICAgICBUaGlzIGlzCiMgICAgICAgbm90IGEgdGVzdAojICAgICAgIG9mCiMgICAgICAgeW91cgojICAgICAgIHNraWxsCiMgICAgICAgYW5kIGNvbXByZWhlbnNpb24uXDAxNAoKQkVHSU4gewoKICAgIGVkID0gRU5WSVJPTlsiRURJVE9SIl07IGlmIChlZCA9PSAiIikgeyBlZCA9ICJlZCIgfQogICAgb3V0ZmlsZSA9ICJpZGlmZi5vdXQiOyB0ZW1wZmlsZSA9ICJpZGlmZi50bXAiOyBkaWZmaWxlID0gImlkaWZmLmRpZiIKICAgIGZpbGUxID0gQVJHVlsxXTsgZmlsZTIgPSBBUkdWWzJdOyBBUkdWWzFdID0gQVJHVlsyXSA9ICIiCiAgICBzeXN0ZW0oImRpZmYgIiBmaWxlMSAiICIgZmlsZTIgIiA+ICIgZGlmZmlsZSkKICAgIG5mMSA9IG5mMiA9IDAKCiAgICB3aGlsZSAoZ2V0bGluZSBkaWZsaW5lIDwgZGlmZmlsZSA+IDApIHsKCiAgICAgICAgY21kID0gc3Vic3RyKGRpZmxpbmUsIG1hdGNoKGRpZmxpbmUsIC9bYWNkXS8pLCAxKQogICAgICAgIHNwbGl0KGRpZmxpbmUsIGxpbmVudW1zLCBjbWQpCiAgICAgICAgc3BsaXQobGluZW51bXNbMV0sIG9uZXMsICIsIik7IHNwbGl0KGxpbmVudW1zWzJdLCB0d29zLCAiLCIpCiAgICAgICAgZnJvbTEgPSBvbmVzWzFdOyB0bzEgPSBvbmVzWzJdID4gMCA/IG9uZXNbMl0gOiBvbmVzWzFdCiAgICAgICAgZnJvbTIgPSB0d29zWzFdOyB0bzIgPSB0d29zWzJdID4gMCA/IHR3b3NbMl0gOiB0d29zWzFdCiAgICAgICAgbmxpbmVzID0gdG8xLWZyb20xICsgdG8yLWZyb20yICsgMQogICAgICAgIGlmICAgICAgKGNtZCA9PSAiYSIpIHsgZnJvbTErKyB9CiAgICAgICAgZWxzZSBpZiAoY21kID09ICJjIikgeyBubGluZXMgKz0gMiB9CiAgICAgICAgZWxzZSBpZiAoY21kID09ICJkIikgeyBmcm9tMisrIH0KCiAgICAgICAgcHJpbnQgZGlmbGluZQogICAgICAgIHdoaWxlIChubGluZXMtLSA+IDApIHsgZ2V0bGluZSBkaWZsaW5lIDwgZGlmZmlsZTsgcHJpbnQgZGlmbGluZSB9CgogICAgICAgIGRvIHsKICAgICAgICAgICAgcHJpbnRmKCI/ICIpOyBnZXRsaW5lIGJ1ZgogICAgICAgICAgICBpZiAoc3Vic3RyKGJ1ZiwgMSwgMSkgPT0gIj4iKSB7CiAgICAgICAgICAgICAgICBuc2tpcChmaWxlMSwgdG8xLW5mMSk7IG5jb3B5KGZpbGUyLCB0bzItbmYyLCBvdXRmaWxlKSB9CiAgICAgICAgICAgIGVsc2UgaWYgKHN1YnN0cihidWYsIDEsIDEpID09ICI8IikgewogICAgICAgICAgICAgICAgbnNraXAoZmlsZTIsIHRvMi1uZjIpOyBuY29weShmaWxlMSwgdG8xLW5mMSwgb3V0ZmlsZSkgfQogICAgICAgICAgICBlbHNlIGlmIChzdWJzdHIoYnVmLCAxLCAxKSA9PSAiZSIpIHsKICAgICAgICAgICAgICAgIG5jb3B5KGZpbGUxLCBmcm9tMS0xLW5mMSwgb3V0ZmlsZSkKICAgICAgICAgICAgICAgIG5za2lwKGZpbGUyLCBmcm9tMi0xLW5mMikKICAgICAgICAgICAgICAgIG5jb3B5KGZpbGUxLCB0bzErMS1mcm9tMSwgdGVtcGZpbGUpCiAgICAgICAgICAgICAgICBwcmludCAiLS0tIiA+IHRlbXBmaWxlCiAgICAgICAgICAgICAgICBuY29weShmaWxlMiwgdG8yKzEtZnJvbTIsIHRlbXBmaWxlKQogICAgICAgICAgICAgICAgY2xvc2UodGVtcGZpbGUpCiAgICAgICAgICAgICAgICBzeXN0ZW0oZWQgIiAiIHRlbXBmaWxlKQogICAgICAgICAgICAgICAgbmNvcHkodGVtcGZpbGUsIC0xLCBvdXRmaWxlKQogICAgICAgICAgICAgICAgY2xvc2UodGVtcGZpbGUpIH0KICAgICAgICAgICAgZWxzZSBpZiAoc3Vic3RyKGJ1ZiwgMSwgMSkgPT0gIiEiKSB7CiAgICAgICAgICAgICAgICBzeXN0ZW0oc3Vic3RyKGJ1ZiwgMikpOyBwcmludCAiISIgfQogICAgICAgICAgICBlbHNlIHsgcHJpbnQgIkVudGVyIDwgb3IgPiBvciBlIG9yICFjbWQiIH0KCiAgICAgICAgfSB3aGlsZSAoc3Vic3RyKGJ1ZiwgMSwgMSkgIX4gL1s+PGVdLykKICAgICAgICBuZjEgPSB0bzE7IG5mMiA9IHRvMgogICAgfQogICAgc3lzdGVtKCJybSAiIHRlbXBmaWxlICIgIiBkaWZmaWxlKQp9CgpmdW5jdGlvbiBuc2tpcChmaW4sIG4sICAgIGp1bmspIHsKICAgIHdoaWxlIChuLS0gIT0gMCkgeyBnZXRsaW5lIGp1bmsgPCBmaW4gfSB9CmZ1bmN0aW9uIG5jb3B5KGZpbiwgbiwgZm91dCwgICAgbGluZSkgewogICAgd2hpbGUgKChuLS0gIT0gMCkgJiYgKGdldGxpbmUgbGluZSA8IGZpbiA+IDApKSB7IHByaW50IGxpbmUgPiBmb3V0IH0gfQ==