Home | History | Annotate | Download | only in dircmp
      1 #!/usr/bin/ksh
      2 #
      3 # CDDL HEADER START
      4 #
      5 # The contents of this file are subject to the terms of the
      6 # Common Development and Distribution License (the "License").
      7 # You may not use this file except in compliance with the License.
      8 #
      9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10 # or http://www.opensolaris.org/os/licensing.
     11 # See the License for the specific language governing permissions
     12 # and limitations under the License.
     13 #
     14 # When distributing Covered Code, include this CDDL HEADER in each
     15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16 # If applicable, add the following below this CDDL HEADER, with the
     17 # fields enclosed by brackets "[]" replaced with your own identifying
     18 # information: Portions Copyright [yyyy] [name of copyright owner]
     19 #
     20 # CDDL HEADER END
     21 #
     22 #	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
     23 #	  All Rights Reserved
     24 
     25 #
     26 #	Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     27 #	Use is subject to license terms.
     28 PATH=/usr/bin
     29 USAGE="usage: dircmp [-d] [-s] [-wn] dir1 dir2"
     30 
     31 TEMPDIR=`mktemp -d /var/tmp/dir.XXXXXX`
     32 if [ -z "$TEMPDIR" ]; then exit 1; fi 
     33 
     34 trap "rm -f -r $TEMPDIR;exit" 0 1 2 3 15
     35 typeset -i exitstat=0
     36 typeset -i sizediff
     37 typeset -i cmpdiff
     38 typeset -i Sflag=0
     39 typeset -i Dflag=0
     40 typeset -i fsize1
     41 typeset -i fsize2
     42 typeset -l LFBOUND=2147483648
     43 width=72
     44 
     45 #
     46 # function to generate consistent "diff" output whether or not files are intact
     47 #
     48 function dodiffs {
     49 
     50 	type=`LC_MESSAGES=C file $D1/"$a"`
     51 	case "$type" in
     52 		*text)  ;;
     53 		*script) ;;
     54 		*empty*) echo $D1/`basename "$a"` is an empty file |
     55 			  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
     56 			continue
     57         	;;
     58         	*cannot*) echo $D1/`basename "$a"` does not exist |
     59 			  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
     60 			continue
     61         	;;
     62         	*)	echo $D1/`basename "$a"` is an object file |
     63 		   	  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
     64 			continue
     65         	;;
     66 	esac
     67 	type=`LC_MESSAGES=C file $D2/"$a"`
     68 	case "$type" in
     69         	*text)  ;;
     70         	*script) ;;
     71         	*empty*) echo $D2/`basename "$a"` is an empty file |
     72 			  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
     73 			continue
     74         	;;
     75         	*cannot*) echo $D2/`basename "$a"` does not exist |
     76 			  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
     77 			continue
     78         	;;
     79         	*)	echo $D2/`basename "$a"` is an object file |
     80 			  pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
     81 			continue
     82         	;;
     83 	esac
     84 	#   
     85 	# If either is a "large file" use bdiff (LF aware),
     86 	# else use diff.
     87 	#
     88 	if (( fsize1 < LFBOUND && fsize2 < LFBOUND ))
     89 	then cmd="diff"
     90 	else cmd="bdiff"
     91 	fi
     92 	($cmd "$D1"/"$a" "$D2"/"$a"; echo $? > $TEMPDIR/dc$$status) | \
     93 	    pr -h "diff of $a in $D1 and $D2" >> $TEMPDIR/dc$$g
     94 	if [[ `cat $TEMPDIR/dc$$status` != 0 ]]
     95 	then exitstat=$diffstat
     96 	fi
     97 }
     98 #
     99 # dircmp entry point
    100 #
    101 while getopts dsw: i
    102 do
    103 	case $i in
    104 	d)	Dflag=1;; 
    105 	s)	Sflag=1;; 
    106 	w)	width=`expr $OPTARG + 0 2>/dev/null`
    107 		if [ $? = 2 ]
    108 		then echo "dircmp: numeric argument required"
    109 			exit 2
    110 		fi
    111 		;;
    112 	\?)	echo $USAGE
    113 		exit 2;;
    114 	esac
    115 done
    116 shift `expr $OPTIND - 1`
    117 #
    118 D0=`pwd`
    119 D1=$1
    120 D2=$2
    121 if [ $# -lt 2 ]
    122 then echo $USAGE
    123      exit 1
    124 elif [ ! -d "$D1" ]
    125 then echo $D1 not a directory !
    126      exit 2
    127 elif [ ! -d "$D2" ]
    128 then echo $D2 not a directory !
    129      exit 2
    130 fi
    131 #
    132 # find all dirs/files in both directory hierarchies. Use "comm" to identify
    133 # which are common to both hierarchies as well as unique to each.
    134 # At this point, print those that are unique.
    135 #
    136 cd "$D1"
    137 find . -print | sort > $TEMPDIR/dc$$a
    138 cd "$D0"
    139 cd "$D2"
    140 find . -print | sort > $TEMPDIR/dc$$b
    141 comm $TEMPDIR/dc$$a $TEMPDIR/dc$$b | sed -n \
    142 	-e "/^		/w $TEMPDIR/dc$$c" \
    143 	-e "/^	[^	]/w $TEMPDIR/dc$$d" \
    144 	-e "/^[^	]/w $TEMPDIR/dc$$e"
    145 rm -f $TEMPDIR/dc$$a $TEMPDIR/dc$$b
    146 pr -w${width} -h "$D1 only and $D2 only" -m $TEMPDIR/dc$$e $TEMPDIR/dc$$d
    147 rm -f $TEMPDIR/dc$$e $TEMPDIR/dc$$d
    148 #
    149 # Generate long ls listings for those dirs/files common to both hierarchies.
    150 # Use -lgn to avoid problem when user or group names are too long, causing
    151 # expected field separator to be missing
    152 # Avoid other potential problems by piping through sed:
    153 #  - Remove: Spaces in size field for block & character special files
    154 #      '71, 0' becomes '710'
    155 #  - For file name, do not print '-> some_file'
    156 #      '/tmp/foo -> FOO' becomes '/tmp/foo'
    157 
    158 # The following sed is to read filenames with special characters
    159 sed -e 's/..//'  -e  's/\([^-a-zA-Z0-9/_.]\)/\\\1/g' < $TEMPDIR/dc$$c > $TEMPDIR/dc$$f
    160 
    161 
    162 cat $TEMPDIR/dc$$f | xargs ls -lLgnd | \
    163   sed -e '/^[bc]/ s/, *//' -e '/^l/ s/ -> .*//' > $TEMPDIR/dc$$i 2>/dev/null
    164 cd "$D0"
    165 cd "$D1"
    166 
    167 
    168 cat $TEMPDIR/dc$$f | xargs ls -lLgnd | \
    169 sed -e '/^[bc]/ s/, *//' -e '/^l/ s/ -> .*//' > $TEMPDIR/dc$$h 2>/dev/null
    170 cd "$D0"
    171 > $TEMPDIR/dc$$g
    172 #
    173 # Process the results of the 'ls -lLgnd' to obtain file size info
    174 # and identify a large file's existence.
    175 #
    176 while read -u3 tmp tmp tmp fsize1 tmp tmp tmp a &&
    177       read -u4 tmp tmp tmp fsize2 tmp tmp tmp b; do
    178 	#
    179 	# A window of opportunity exists where the ls -lLgnd above
    180 	# could produce different
    181 	# results if any of the files were removed since the find command.
    182 	# If the pair of reads above results in different values (file names) for 'a'
    183 	# and 'b', then get the file pointers in sync before continuing, and display
    184 	# "different" message as customary.
    185 	#
    186 	if [[ "$a" != "$b" ]]; then
    187 	while [[ "$a" < "$b" ]]; do
    188 		if (( Sflag != 1 ))
    189 		then echo "different	$a"
    190 		dodiffs
    191 		fi
    192 		read -u3 tmp tmp tmp fsize1 tmp tmp tmp a
    193 	done
    194 	while [[ "$a" > "$b" ]]; do
    195 		if (( Sflag != 1 ))
    196 		then echo "different	$b"
    197 		dodiffs
    198 		fi
    199 		read -u4 tmp tmp tmp fsize2 tmp tmp tmp b
    200 	done
    201 	fi
    202 	cmpdiff=0
    203 	sizediff=0
    204 	if [ -d "$D1"/"$a" ]
    205 	then if (( Sflag != 1 ))
    206 	     then echo "directory	$a"
    207 	     fi
    208 	elif [ -f "$D1"/"$a" ]
    209 	then 
    210 	     #
    211 	     # If the file sizes are different, then we can skip the run
    212 	     # of "cmp" which is only used to determine 'same' or 'different'.
    213 	     # If the file sizes are the same, we still need to run "cmp"
    214 	     #
    215 	     if (( fsize1 != fsize2 ))
    216 	     then
    217 		sizediff=1
    218 	     else
    219 		cmp -s "$D1"/"$a" "$D2"/"$a"
    220 		cmpdiff=$?
    221 	     fi
    222 	     if (( sizediff == 0 && cmpdiff == 0 ))
    223 	     then if (( Sflag != 1 ))
    224 		  then echo "same     	$a"
    225 		  fi
    226 	     else echo "different	$a"
    227 		  if (( Dflag == 1 ))
    228 		  then
    229 			dodiffs
    230 		  fi
    231 	     fi
    232 	elif (( Sflag != 1 ))
    233 	then echo "special  	$a"
    234 	fi
    235 done 3<$TEMPDIR/dc$$h 4<$TEMPDIR/dc$$i | pr -r -h "Comparison of $D1 $D2"
    236 if (( Dflag == 1 ))
    237 then cat $TEMPDIR/dc$$g
    238 fi
    239 exit $exitstat
    240