#!/bin/ksh
# @(#) comptree.ksh 1.1 93/06/17
# 90/12/13 john h. dubois iii (john@armory.com)
# 91/06/07 added help message.
# 91/10/14 renamed from compare_files
# 92/02/07 added check for whether root dirs are really dirs
# 93/06/13 added pipes to valid file types
# 93/06/17 Added -a test before executing l on a file.
#          Remove diff files.

usage=\
"$0: compare files from two directory trees.
Usage: $0 root1 root2 base < filelist
$0 assumes that files that exist in the two
trees are at least of the same type.
root1 is the root of the first directory tree;
root2 is the root of the second directory tree;
base is the basename of the output files.
Each filename read from the standard input is concatenated to the
two root names and the permissions, owner, group, and contents or
major/minor number of the resulting files is compared.
The filename is written to one or more files created by adding a
suffix to base as follows:
base.s: Same: ordinary files, directories, and devices that have identical
        permissions, owner, and group, and identical contents or major/minor
        number.
base.d: Different: ordinary files that have different contents.
base.i: Inode difference: files, directories, or devices that have different
        permissions, owner, or group, or different major/minor number.
base.a: Access denied: ordinary files whose contents could not be accessed
        in one or both of the directory trees.
base.1: 1st tree only: files that exist only in the first directory tree.
base.2: 2nd tree only: files that exist only in the second directory tree."

verbose=false
case "$1" in
    -v) verbose=true; shift;;
    -h) echo "$usage"; exit;;
esac

if [ $# -lt 3 ]; then
    echo "$usage"
    exit
fi

root1=$1
root2=$2

same=$3.s
diff=$3.d
diffi=$3.i
nofile1=$3.2
nofile2=$3.1
noaccess=$3.a

rm -f $same $diff $diffi $nofile1 $nofile2 $noaccess

if [ ! -d $root1 ]; then
    echo "$root1 is not a directory."
    exit 1
fi

if [ ! -d $root2 ]; then
    echo "$root2 is not a directory."
    exit 1
fi

unset ENV

function ChkFile {
    if [ -a "$1" ] && set - ""`/bin/l -d "$1" 2> /dev/null`; then
	echo $1 $3 $4 $5 $5$6
	return 0
    else
	return 1
    fi
}

while read f; do
    idiff=false
    file1=$root1/$f
    file2=$root2/$f
    if ChkFile "$file1" | read perms1 owner1 group1 size1 dev1; then :; else
	echo $f >> $nofile1
	continue
    fi
    if ChkFile "$file2" | read perms2 owner2 group2 size2 dev2; then :; else
	echo $f >> $nofile2
	continue
    fi

    if [ $perms1 != $perms2 -o $owner1 != $owner2 -o $group1 != $group2 ]; then
	[ $verbose = true ] && echo -n \
	"$perms1 $perms2 $owner1\t$owner2\t$group1\t$group2\t" >> $diffi
	echo $f >> $diffi
	idiff=true
    fi

    if [ -c $file1 -o -b $file1 ]; then
	if [ $idiff = false ]; then
		if [ $dev1 != $dev2 ]; then
		    [ $verbose = true ] && echo -n "$dev1\t$dev2\t" >> $diffi
		    echo $f >> $diffi
		else
		    echo $f >> $same
		fi
	fi
    elif [ -f $file1 ]; then
	cmp -s $file1 $file2
	case $? in
	    0) [ $idiff = false ] && echo $f >> $same;;
	    1) echo $f >> $diff;;
	    2) echo $f >> $noaccess;;
	    *) echo "Invalid return code for from cmp: $? (comparing $f).";;
	esac
    elif [ -d $file1 -o -p $file1 ]; then
	[ $idiff = false ] && echo $f >> $same
    else
	echo "Invalid file type for $file1."
    fi
done
