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