xref: /illumos-gate/usr/src/cmd/dircmp/dircmp.sh (revision c13e065d)
17c478bd9Sstevel@tonic-gate#!/usr/bin/ksh
27c478bd9Sstevel@tonic-gate#
37c478bd9Sstevel@tonic-gate# CDDL HEADER START
47c478bd9Sstevel@tonic-gate#
57c478bd9Sstevel@tonic-gate# The contents of this file are subject to the terms of the
6*c13e065dSRitwik Ghoshal# Common Development and Distribution License (the "License").
7*c13e065dSRitwik Ghoshal# You may not use this file except in compliance with the License.
87c478bd9Sstevel@tonic-gate#
97c478bd9Sstevel@tonic-gate# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate# or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate# See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate# and limitations under the License.
137c478bd9Sstevel@tonic-gate#
147c478bd9Sstevel@tonic-gate# When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate# If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate# fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate# information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate#
207c478bd9Sstevel@tonic-gate# CDDL HEADER END
217c478bd9Sstevel@tonic-gate#
227c478bd9Sstevel@tonic-gate#	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
237c478bd9Sstevel@tonic-gate#	  All Rights Reserved
247c478bd9Sstevel@tonic-gate
257c478bd9Sstevel@tonic-gate#
26*c13e065dSRitwik Ghoshal#	Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
27*c13e065dSRitwik Ghoshal#	Use is subject to license terms.
287c478bd9Sstevel@tonic-gatePATH=/usr/bin
297c478bd9Sstevel@tonic-gateUSAGE="usage: dircmp [-d] [-s] [-wn] dir1 dir2"
30*c13e065dSRitwik Ghoshal
31*c13e065dSRitwik GhoshalTEMPDIR=`mktemp -d /var/tmp/dir.XXXXXX`
32*c13e065dSRitwik Ghoshalif [ -z "$TEMPDIR" ]; then exit 1; fi
33*c13e065dSRitwik Ghoshal
34*c13e065dSRitwik Ghoshaltrap "rm -f -r $TEMPDIR;exit" 0 1 2 3 15
357c478bd9Sstevel@tonic-gatetypeset -i exitstat=0
367c478bd9Sstevel@tonic-gatetypeset -i sizediff
377c478bd9Sstevel@tonic-gatetypeset -i cmpdiff
387c478bd9Sstevel@tonic-gatetypeset -i Sflag=0
397c478bd9Sstevel@tonic-gatetypeset -i Dflag=0
407c478bd9Sstevel@tonic-gatetypeset -i fsize1
417c478bd9Sstevel@tonic-gatetypeset -i fsize2
427c478bd9Sstevel@tonic-gatetypeset -l LFBOUND=2147483648
437c478bd9Sstevel@tonic-gatewidth=72
447c478bd9Sstevel@tonic-gate
457c478bd9Sstevel@tonic-gate#
467c478bd9Sstevel@tonic-gate# function to generate consistent "diff" output whether or not files are intact
477c478bd9Sstevel@tonic-gate#
487c478bd9Sstevel@tonic-gatefunction dodiffs {
497c478bd9Sstevel@tonic-gate
507c478bd9Sstevel@tonic-gate	type=`LC_MESSAGES=C file $D1/"$a"`
517c478bd9Sstevel@tonic-gate	case "$type" in
527c478bd9Sstevel@tonic-gate		*text)  ;;
537c478bd9Sstevel@tonic-gate		*script) ;;
547c478bd9Sstevel@tonic-gate		*empty*) echo $D1/`basename "$a"` is an empty file |
55*c13e065dSRitwik Ghoshal			  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
567c478bd9Sstevel@tonic-gate			continue
577c478bd9Sstevel@tonic-gate        	;;
587c478bd9Sstevel@tonic-gate        	*cannot*) echo $D1/`basename "$a"` does not exist |
59*c13e065dSRitwik Ghoshal			  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
607c478bd9Sstevel@tonic-gate			continue
617c478bd9Sstevel@tonic-gate        	;;
627c478bd9Sstevel@tonic-gate        	*)	echo $D1/`basename "$a"` is an object file |
63*c13e065dSRitwik Ghoshal		   	  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
647c478bd9Sstevel@tonic-gate			continue
657c478bd9Sstevel@tonic-gate        	;;
667c478bd9Sstevel@tonic-gate	esac
677c478bd9Sstevel@tonic-gate	type=`LC_MESSAGES=C file $D2/"$a"`
687c478bd9Sstevel@tonic-gate	case "$type" in
697c478bd9Sstevel@tonic-gate        	*text)  ;;
707c478bd9Sstevel@tonic-gate        	*script) ;;
717c478bd9Sstevel@tonic-gate        	*empty*) echo $D2/`basename "$a"` is an empty file |
72*c13e065dSRitwik Ghoshal			  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
737c478bd9Sstevel@tonic-gate			continue
747c478bd9Sstevel@tonic-gate        	;;
757c478bd9Sstevel@tonic-gate        	*cannot*) echo $D2/`basename "$a"` does not exist |
76*c13e065dSRitwik Ghoshal			  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
777c478bd9Sstevel@tonic-gate			continue
787c478bd9Sstevel@tonic-gate        	;;
797c478bd9Sstevel@tonic-gate        	*)	echo $D2/`basename "$a"` is an object file |
80*c13e065dSRitwik Ghoshal			  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
817c478bd9Sstevel@tonic-gate			continue
827c478bd9Sstevel@tonic-gate        	;;
837c478bd9Sstevel@tonic-gate	esac
847c478bd9Sstevel@tonic-gate	#
857c478bd9Sstevel@tonic-gate	# If either is a "large file" use bdiff (LF aware),
867c478bd9Sstevel@tonic-gate	# else use diff.
877c478bd9Sstevel@tonic-gate	#
887c478bd9Sstevel@tonic-gate	if (( fsize1 < LFBOUND && fsize2 < LFBOUND ))
897c478bd9Sstevel@tonic-gate	then cmd="diff"
907c478bd9Sstevel@tonic-gate	else cmd="bdiff"
917c478bd9Sstevel@tonic-gate	fi
92*c13e065dSRitwik Ghoshal	($cmd "$D1"/"$a" "$D2"/"$a"; echo $? > $TEMPDIR/dc$$status) | \
93*c13e065dSRitwik Ghoshal	    pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
94*c13e065dSRitwik Ghoshal	if [[ `cat $TEMPDIR/dc$$status` != 0 ]]
957c478bd9Sstevel@tonic-gate	then exitstat=$diffstat
967c478bd9Sstevel@tonic-gate	fi
977c478bd9Sstevel@tonic-gate}
987c478bd9Sstevel@tonic-gate#
997c478bd9Sstevel@tonic-gate# dircmp entry point
1007c478bd9Sstevel@tonic-gate#
1017c478bd9Sstevel@tonic-gatewhile getopts dsw: i
1027c478bd9Sstevel@tonic-gatedo
1037c478bd9Sstevel@tonic-gate	case $i in
1047c478bd9Sstevel@tonic-gate	d)	Dflag=1;;
1057c478bd9Sstevel@tonic-gate	s)	Sflag=1;;
1067c478bd9Sstevel@tonic-gate	w)	width=`expr $OPTARG + 0 2>/dev/null`
1077c478bd9Sstevel@tonic-gate		if [ $? = 2 ]
1087c478bd9Sstevel@tonic-gate		then echo "dircmp: numeric argument required"
1097c478bd9Sstevel@tonic-gate			exit 2
1107c478bd9Sstevel@tonic-gate		fi
1117c478bd9Sstevel@tonic-gate		;;
1127c478bd9Sstevel@tonic-gate	\?)	echo $USAGE
1137c478bd9Sstevel@tonic-gate		exit 2;;
1147c478bd9Sstevel@tonic-gate	esac
1157c478bd9Sstevel@tonic-gatedone
1167c478bd9Sstevel@tonic-gateshift `expr $OPTIND - 1`
1177c478bd9Sstevel@tonic-gate#
1187c478bd9Sstevel@tonic-gateD0=`pwd`
1197c478bd9Sstevel@tonic-gateD1=$1
1207c478bd9Sstevel@tonic-gateD2=$2
1217c478bd9Sstevel@tonic-gateif [ $# -lt 2 ]
1227c478bd9Sstevel@tonic-gatethen echo $USAGE
1237c478bd9Sstevel@tonic-gate     exit 1
1247c478bd9Sstevel@tonic-gateelif [ ! -d "$D1" ]
1257c478bd9Sstevel@tonic-gatethen echo $D1 not a directory !
1267c478bd9Sstevel@tonic-gate     exit 2
1277c478bd9Sstevel@tonic-gateelif [ ! -d "$D2" ]
1287c478bd9Sstevel@tonic-gatethen echo $D2 not a directory !
1297c478bd9Sstevel@tonic-gate     exit 2
1307c478bd9Sstevel@tonic-gatefi
1317c478bd9Sstevel@tonic-gate#
1327c478bd9Sstevel@tonic-gate# find all dirs/files in both directory hierarchies. Use "comm" to identify
1337c478bd9Sstevel@tonic-gate# which are common to both hierarchies as well as unique to each.
1347c478bd9Sstevel@tonic-gate# At this point, print those that are unique.
1357c478bd9Sstevel@tonic-gate#
1367c478bd9Sstevel@tonic-gatecd "$D1"
137*c13e065dSRitwik Ghoshalfind . -print | sort > $TEMPDIR/dc$$a
1387c478bd9Sstevel@tonic-gatecd "$D0"
1397c478bd9Sstevel@tonic-gatecd "$D2"
140*c13e065dSRitwik Ghoshalfind . -print | sort > $TEMPDIR/dc$$b
141*c13e065dSRitwik Ghoshalcomm $TEMPDIR/dc$$a $TEMPDIR/dc$$b | sed -n \
142*c13e065dSRitwik Ghoshal	-e "/^		/w $TEMPDIR/dc$$c" \
143*c13e065dSRitwik Ghoshal	-e "/^	[^	]/w $TEMPDIR/dc$$d" \
144*c13e065dSRitwik Ghoshal	-e "/^[^	]/w $TEMPDIR/dc$$e"
145*c13e065dSRitwik Ghoshalrm -f $TEMPDIR/dc$$a $TEMPDIR/dc$$b
146*c13e065dSRitwik Ghoshalpr -w${width} -h "$D1 only and $D2 only" -m $TEMPDIR/dc$$e $TEMPDIR/dc$$d
147*c13e065dSRitwik Ghoshalrm -f $TEMPDIR/dc$$e $TEMPDIR/dc$$d
1487c478bd9Sstevel@tonic-gate#
1497c478bd9Sstevel@tonic-gate# Generate long ls listings for those dirs/files common to both hierarchies.
1507c478bd9Sstevel@tonic-gate# Use -lgn to avoid problem when user or group names are too long, causing
1517c478bd9Sstevel@tonic-gate# expected field separator to be missing
1527c478bd9Sstevel@tonic-gate# Avoid other potential problems by piping through sed:
1537c478bd9Sstevel@tonic-gate#  - Remove: Spaces in size field for block & character special files
1547c478bd9Sstevel@tonic-gate#      '71, 0' becomes '710'
1557c478bd9Sstevel@tonic-gate#  - For file name, do not print '-> some_file'
1567c478bd9Sstevel@tonic-gate#      '/tmp/foo -> FOO' becomes '/tmp/foo'
1577c478bd9Sstevel@tonic-gate
1587c478bd9Sstevel@tonic-gate# The following sed is to read filenames with special characters
159*c13e065dSRitwik Ghoshalsed -e 's/..//'  -e  's/\([^-a-zA-Z0-9/_.]\)/\\\1/g' < $TEMPDIR/dc$$c > $TEMPDIR/dc$$f
1607c478bd9Sstevel@tonic-gate
1617c478bd9Sstevel@tonic-gate
162*c13e065dSRitwik Ghoshalcat $TEMPDIR/dc$$f | xargs ls -lLgnd | \
163*c13e065dSRitwik Ghoshal  sed -e '/^[bc]/ s/, *//' -e '/^l/ s/ -> .*//' > $TEMPDIR/dc$$i 2>/dev/null
1647c478bd9Sstevel@tonic-gatecd "$D0"
1657c478bd9Sstevel@tonic-gatecd "$D1"
1667c478bd9Sstevel@tonic-gate
1677c478bd9Sstevel@tonic-gate
168*c13e065dSRitwik Ghoshalcat $TEMPDIR/dc$$f | xargs ls -lLgnd | \
169*c13e065dSRitwik Ghoshalsed -e '/^[bc]/ s/, *//' -e '/^l/ s/ -> .*//' > $TEMPDIR/dc$$h 2>/dev/null
1707c478bd9Sstevel@tonic-gatecd "$D0"
171*c13e065dSRitwik Ghoshal> $TEMPDIR/dc$$g
1727c478bd9Sstevel@tonic-gate#
1737c478bd9Sstevel@tonic-gate# Process the results of the 'ls -lLgnd' to obtain file size info
1747c478bd9Sstevel@tonic-gate# and identify a large file's existence.
1757c478bd9Sstevel@tonic-gate#
1767c478bd9Sstevel@tonic-gatewhile read -u3 tmp tmp tmp fsize1 tmp tmp tmp a &&
1777c478bd9Sstevel@tonic-gate      read -u4 tmp tmp tmp fsize2 tmp tmp tmp b; do
1787c478bd9Sstevel@tonic-gate	#
1797c478bd9Sstevel@tonic-gate	# A window of opportunity exists where the ls -lLgnd above
1807c478bd9Sstevel@tonic-gate	# could produce different
1817c478bd9Sstevel@tonic-gate	# results if any of the files were removed since the find command.
1827c478bd9Sstevel@tonic-gate	# If the pair of reads above results in different values (file names) for 'a'
1837c478bd9Sstevel@tonic-gate	# and 'b', then get the file pointers in sync before continuing, and display
1847c478bd9Sstevel@tonic-gate	# "different" message as customary.
1857c478bd9Sstevel@tonic-gate	#
1867c478bd9Sstevel@tonic-gate	if [[ "$a" != "$b" ]]; then
1877c478bd9Sstevel@tonic-gate	while [[ "$a" < "$b" ]]; do
1887c478bd9Sstevel@tonic-gate		if (( Sflag != 1 ))
1897c478bd9Sstevel@tonic-gate		then echo "different	$a"
1907c478bd9Sstevel@tonic-gate		dodiffs
1917c478bd9Sstevel@tonic-gate		fi
1927c478bd9Sstevel@tonic-gate		read -u3 tmp tmp tmp fsize1 tmp tmp tmp a
1937c478bd9Sstevel@tonic-gate	done
1947c478bd9Sstevel@tonic-gate	while [[ "$a" > "$b" ]]; do
1957c478bd9Sstevel@tonic-gate		if (( Sflag != 1 ))
1967c478bd9Sstevel@tonic-gate		then echo "different	$b"
1977c478bd9Sstevel@tonic-gate		dodiffs
1987c478bd9Sstevel@tonic-gate		fi
1997c478bd9Sstevel@tonic-gate		read -u4 tmp tmp tmp fsize2 tmp tmp tmp b
2007c478bd9Sstevel@tonic-gate	done
2017c478bd9Sstevel@tonic-gate	fi
2027c478bd9Sstevel@tonic-gate	cmpdiff=0
2037c478bd9Sstevel@tonic-gate	sizediff=0
2047c478bd9Sstevel@tonic-gate	if [ -d "$D1"/"$a" ]
2057c478bd9Sstevel@tonic-gate	then if (( Sflag != 1 ))
2067c478bd9Sstevel@tonic-gate	     then echo "directory	$a"
2077c478bd9Sstevel@tonic-gate	     fi
2087c478bd9Sstevel@tonic-gate	elif [ -f "$D1"/"$a" ]
2097c478bd9Sstevel@tonic-gate	then
2107c478bd9Sstevel@tonic-gate	     #
2117c478bd9Sstevel@tonic-gate	     # If the file sizes are different, then we can skip the run
2127c478bd9Sstevel@tonic-gate	     # of "cmp" which is only used to determine 'same' or 'different'.
2137c478bd9Sstevel@tonic-gate	     # If the file sizes are the same, we still need to run "cmp"
2147c478bd9Sstevel@tonic-gate	     #
2157c478bd9Sstevel@tonic-gate	     if (( fsize1 != fsize2 ))
2167c478bd9Sstevel@tonic-gate	     then
2177c478bd9Sstevel@tonic-gate		sizediff=1
2187c478bd9Sstevel@tonic-gate	     else
2197c478bd9Sstevel@tonic-gate		cmp -s "$D1"/"$a" "$D2"/"$a"
2207c478bd9Sstevel@tonic-gate		cmpdiff=$?
2217c478bd9Sstevel@tonic-gate	     fi
2227c478bd9Sstevel@tonic-gate	     if (( sizediff == 0 && cmpdiff == 0 ))
2237c478bd9Sstevel@tonic-gate	     then if (( Sflag != 1 ))
2247c478bd9Sstevel@tonic-gate		  then echo "same     	$a"
2257c478bd9Sstevel@tonic-gate		  fi
2267c478bd9Sstevel@tonic-gate	     else echo "different	$a"
2277c478bd9Sstevel@tonic-gate		  if (( Dflag == 1 ))
2287c478bd9Sstevel@tonic-gate		  then
2297c478bd9Sstevel@tonic-gate			dodiffs
2307c478bd9Sstevel@tonic-gate		  fi
2317c478bd9Sstevel@tonic-gate	     fi
2327c478bd9Sstevel@tonic-gate	elif (( Sflag != 1 ))
2337c478bd9Sstevel@tonic-gate	then echo "special  	$a"
2347c478bd9Sstevel@tonic-gate	fi
235*c13e065dSRitwik Ghoshaldone 3<$TEMPDIR/dc$$h 4<$TEMPDIR/dc$$i | pr -r -h "Comparison of $D1 $D2"
2367c478bd9Sstevel@tonic-gateif (( Dflag == 1 ))
237*c13e065dSRitwik Ghoshalthen cat $TEMPDIR/dc$$g
2387c478bd9Sstevel@tonic-gatefi
2397c478bd9Sstevel@tonic-gateexit $exitstat
240