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