xref: /illumos-gate/usr/src/cmd/dircmp/dircmp.sh (revision b31b5de1357c915fe7dab4d9646d9d84f9fe69bc)
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.
28PATH=/usr/bin
29USAGE="usage: dircmp [-d] [-s] [-wn] dir1 dir2"
30
31TEMPDIR=`mktemp -d /var/tmp/dir.XXXXXX`
32if [ -z "$TEMPDIR" ]; then exit 1; fi
33
34trap "rm -f -r $TEMPDIR;exit" 0 1 2 3 15
35typeset -i exitstat=0
36typeset -i sizediff
37typeset -i cmpdiff
38typeset -i Sflag=0
39typeset -i Dflag=0
40typeset -i fsize1
41typeset -i fsize2
42typeset -l LFBOUND=2147483648
43width=72
44
45#
46# function to generate consistent "diff" output whether or not files are intact
47#
48function 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#
101while getopts dsw: i
102do
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
115done
116shift `expr $OPTIND - 1`
117#
118D0=`pwd`
119D1=$1
120D2=$2
121if [ $# -lt 2 ]
122then echo $USAGE
123     exit 1
124elif [ ! -d "$D1" ]
125then echo $D1 not a directory !
126     exit 2
127elif [ ! -d "$D2" ]
128then echo $D2 not a directory !
129     exit 2
130fi
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#
136cd "$D1"
137find . -print | sort > $TEMPDIR/dc$$a
138cd "$D0"
139cd "$D2"
140find . -print | sort > $TEMPDIR/dc$$b
141comm $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"
145rm -f $TEMPDIR/dc$$a $TEMPDIR/dc$$b
146pr -w${width} -h "$D1 only and $D2 only" -m $TEMPDIR/dc$$e $TEMPDIR/dc$$d
147rm -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
159sed -e 's/..//'  -e  's/\([^-a-zA-Z0-9/_.]\)/\\\1/g' < $TEMPDIR/dc$$c > $TEMPDIR/dc$$f
160
161
162cat $TEMPDIR/dc$$f | xargs ls -lLgnd | \
163  sed -e '/^[bc]/ s/, *//' -e '/^l/ s/ -> .*//' > $TEMPDIR/dc$$i 2>/dev/null
164cd "$D0"
165cd "$D1"
166
167
168cat $TEMPDIR/dc$$f | xargs ls -lLgnd | \
169sed -e '/^[bc]/ s/, *//' -e '/^l/ s/ -> .*//' > $TEMPDIR/dc$$h 2>/dev/null
170cd "$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#
176while 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
235done 3<$TEMPDIR/dc$$h 4<$TEMPDIR/dc$$i | pr -r -h "Comparison of $D1 $D2"
236if (( Dflag == 1 ))
237then cat $TEMPDIR/dc$$g
238fi
239exit $exitstat
240