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