12538Sesaxe#!/usr/sfw/bin/python
22538Sesaxe#
32538Sesaxe# CDDL HEADER START
42538Sesaxe#
52538Sesaxe# The contents of this file are subject to the terms of the
62538Sesaxe# Common Development and Distribution License (the "License").
72538Sesaxe# You may not use this file except in compliance with the License.
82538Sesaxe#
92538Sesaxe# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
102538Sesaxe# or http://www.opensolaris.org/os/licensing.
112538Sesaxe# See the License for the specific language governing permissions
122538Sesaxe# and limitations under the License.
132538Sesaxe#
142538Sesaxe# When distributing Covered Code, include this CDDL HEADER in each
152538Sesaxe# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
162538Sesaxe# If applicable, add the following below this CDDL HEADER, with the
172538Sesaxe# fields enclosed by brackets "[]" replaced with your own identifying
182538Sesaxe# information: Portions Copyright [yyyy] [name of copyright owner]
192538Sesaxe#
202538Sesaxe# CDDL HEADER END
212538Sesaxe#
22*5298Srotondo# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
232538Sesaxe# Use is subject to license terms.
242538Sesaxe#
252538Sesaxe#ident	"%Z%%M%	%I%	%E% SMI"
262538Sesaxe
272538Sesaxe#
282538Sesaxe# wsdiff(1) is a tool that can be used to determine which compiled objects
292538Sesaxe# have changed as a result of a given source change. Developers backporting
302538Sesaxe# new features, RFEs and bug fixes need to be able to identify the set of
312538Sesaxe# patch deliverables necessary for feature/fix realization on a patched system.
322538Sesaxe#
332538Sesaxe# The tool works by comparing objects in two trees/proto areas (one build with,
342538Sesaxe# and without the source changes.
352538Sesaxe#
362538Sesaxe# Using wsdiff(1) is fairly simple:
372538Sesaxe#	- Bringover to a fresh workspace
382538Sesaxe#	- Perform a full non-debug build (clobber if workspace isn't fresh)
392538Sesaxe#	- Move the proto area aside, call it proto.old, or something.
402538Sesaxe#	- Integrate your changes to the workspace
412538Sesaxe#	- Perform another full non-debug clobber build.
422538Sesaxe#	- Use wsdiff(1) to see what changed:
432538Sesaxe#		$ wsdiff proto.old proto
442538Sesaxe#
452538Sesaxe# By default, wsdiff will print the list of changed objects / deliverables to
462538Sesaxe# stdout. If a results file is specified via -r, the list of differing objects,
472538Sesaxe# and details about why wsdiff(1) thinks they are different will be logged to
482538Sesaxe# the results file.
492538Sesaxe#
502538Sesaxe# By invoking nightly(1) with the -w option to NIGHTLY_FLAGS, nightly(1) will use
512538Sesaxe# wsdiff(1) to report on what objects changed since the last build.
522538Sesaxe#
532538Sesaxe# For patch deliverable purposes, it's advised to have nightly do a clobber,
542538Sesaxe# non-debug build.
552538Sesaxe#
562538Sesaxe# Think about the results. Was something flagged that you don't expect? Go look
572538Sesaxe# at the results file to see details about the differences.
582538Sesaxe#
592538Sesaxe# Use the -i option in conjunction with -v and -V to dive deeper and have wsdiff(1)
602538Sesaxe# report with more verbosity.
612538Sesaxe#
622538Sesaxe# Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new
632538Sesaxe#
642538Sesaxe# Where "old" is the path to the proto area build without the changes, and
652538Sesaxe# "new" is the path to the proto area built with the changes. The following
662538Sesaxe# options are supported:
672538Sesaxe#
682538Sesaxe#        -v      Do not truncate observed diffs in results
692538Sesaxe#        -V      Log *all* ELF sect diffs vs. logging the first diff found
702538Sesaxe#        -t      Use onbld tools in $SRC/tools
712538Sesaxe#        -r      Log results and observed differences
722538Sesaxe#        -i      Tell wsdiff which objects to compare via an input file list
732538Sesaxe
742538Sesaxeimport datetime, fnmatch, getopt, profile, os, popen2, commands
752538Sesaxeimport re, select, string, struct, sys, tempfile, time
762538Sesaxefrom stat import *
772538Sesaxe
782538Sesaxe# Human readable diffs truncated by default if longer than this
792538Sesaxe# Specifying -v on the command line will override
802538Sesaxediffs_sz_thresh = 4096
812538Sesaxe
822538Sesaxe# Default search path for wsdiff
832538Sesaxewsdiff_path = [ "/usr/bin",
842538Sesaxe		"/usr/ccs/bin",
852538Sesaxe		"/lib/svc/bin",
862538Sesaxe		"/opt/onbld/bin" ]
872538Sesaxe
882538Sesaxe# These are objects that wsdiff will notice look different, but will not report.
892538Sesaxe# Existence of an exceptions list, and adding things here is *dangerous*,
902538Sesaxe# and therefore the *only* reasons why anything would be listed here is because
912538Sesaxe# the objects do not build deterministically, yet we *cannot* fix this.
922538Sesaxe#
932538Sesaxe# These perl libraries use __DATE__ and therefore always look different.
942538Sesaxe# Ideally, we would purge use the use of __DATE__ from the source, but because
952538Sesaxe# this is source we wish to distribute with Solaris "unchanged", we cannot modify.
962538Sesaxe#
972538Sesaxewsdiff_exceptions = [ "usr/perl5/5.8.4/lib/sun4-solaris-64int/CORE/libperl.so.1",
982538Sesaxe		      "usr/perl5/5.6.1/lib/sun4-solaris-64int/CORE/libperl.so.1",
992538Sesaxe		      "usr/perl5/5.8.4/lib/i86pc-solaris-64int/CORE/libperl.so.1",
1002538Sesaxe		      "usr/perl5/5.6.1/lib/i86pc-solaris-64int/CORE/libperl.so.1"
1012538Sesaxe		      ]
1022538Sesaxe
1032538Sesaxe#####
1042538Sesaxe# Logging routines
1052538Sesaxe#
1062538Sesaxe
1072538Sesaxe# Informational message to be printed to the screen, and the log file
1082538Sesaxedef info(msg) :
1092538Sesaxe
1102538Sesaxe	print >> sys.stdout, msg
1112538Sesaxe	if logging :
1122538Sesaxe		print >> log, msg
1132538Sesaxe	sys.stdout.flush()
1142538Sesaxe
1152538Sesaxe# Error message to be printed to the screen, and the log file
1162538Sesaxedef error(msg) :
1172538Sesaxe
1182538Sesaxe	print >> sys.stderr, "ERROR:", msg
1192538Sesaxe	sys.stderr.flush()
1202538Sesaxe	if logging :
1212538Sesaxe		print >> log, "ERROR:", msg
1222538Sesaxe		log.flush()
1232538Sesaxe
1242538Sesaxe# Informational message to be printed only to the log, if there is one.
1252538Sesaxedef v_info(msg) :
1262538Sesaxe
1272538Sesaxe	if logging :
1282538Sesaxe		print >> log, msg
1292538Sesaxe		log.flush()
1302538Sesaxe
1312538Sesaxe#
1322538Sesaxe# Flag a detected file difference
1332538Sesaxe# Display the fileName to stdout, and log the difference
1342538Sesaxe#
1352538Sesaxedef difference(f, dtype, diffs) :
1362538Sesaxe
1372538Sesaxe	if f in wsdiff_exceptions :
1382538Sesaxe		return
1392538Sesaxe
1402538Sesaxe	print >> sys.stdout, f
1412538Sesaxe	sys.stdout.flush()
1422538Sesaxe
1432538Sesaxe	log_difference(f, dtype, diffs)
1442538Sesaxe
1452538Sesaxe#
1462538Sesaxe# Do the actual logging of the difference to the results file
1472538Sesaxe#
1482538Sesaxedef log_difference(f, dtype, diffs) :
1492538Sesaxe	if logging :
1502538Sesaxe		print >> log, f
1512538Sesaxe		print >> log, "NOTE:", dtype, "difference detected."
1522538Sesaxe
1532538Sesaxe		difflen = len(diffs)
1542538Sesaxe		if difflen > 0 :
1552538Sesaxe			print >> log
1562538Sesaxe
1572538Sesaxe			if not vdiffs and difflen > diffs_sz_thresh :
1582538Sesaxe				print >> log, diffs[:diffs_sz_thresh]
1592538Sesaxe				print >> log, \
1602538Sesaxe				      "... truncated due to length: " \
1612538Sesaxe				      "use -v to override ..."
1622538Sesaxe			else :
1632538Sesaxe				print >> log, diffs
1642538Sesaxe			print >> log, "\n"
1652538Sesaxe		log.flush()
1662538Sesaxe
1672538Sesaxe
1682538Sesaxe#####
1692538Sesaxe# diff generating routines
1702538Sesaxe#
1712538Sesaxe
1722538Sesaxe#
1732538Sesaxe# Return human readable diffs from two temporary files
1742538Sesaxe#
1752538Sesaxedef diffFileData(tmpf1, tmpf2) :
1762538Sesaxe
1772538Sesaxe	# Filter the data through od(1) if the data is detected
1782538Sesaxe	# as being binary
1792538Sesaxe	if isBinary(tmpf1) or isBinary(tmpf2) :
1802538Sesaxe		tmp_od1 = tmpf1 + ".od"
1812538Sesaxe		tmp_od2 = tmpf2 + ".od"
1822538Sesaxe
1832538Sesaxe		cmd = od_cmd + " -c -t x4" + " " + tmpf1 + " > " + tmp_od1
1842538Sesaxe		os.system(cmd)
1852538Sesaxe		cmd = od_cmd + " -c -t x4" + " " + tmpf2 + " > " + tmp_od2
1862538Sesaxe		os.system(cmd)
1872538Sesaxe
1882538Sesaxe		tmpf1 = tmp_od1
1892538Sesaxe		tmpf2 = tmp_od2
1902538Sesaxe
1912538Sesaxe	data = commands.getoutput(diff_cmd + " " + tmpf1 + " " + tmpf2)
1922538Sesaxe
1932538Sesaxe	return data
1942538Sesaxe
1952538Sesaxe#
1962538Sesaxe# Return human readable diffs betweeen two datasets
1972538Sesaxe#
1982538Sesaxedef diffData(d1, d2) :
1992538Sesaxe
2002538Sesaxe	global tmpFile1
2012538Sesaxe	global tmpFile2
2022538Sesaxe
2032538Sesaxe	try:
2042538Sesaxe		fd1 = open(tmpFile1, "w")
2052538Sesaxe	except:
2062538Sesaxe		error("failed to open: " + tmpFile1)
2072538Sesaxe		cleanup(1)
2082538Sesaxe	try:
2092538Sesaxe		fd2 = open(tmpFile2, "w")
2102538Sesaxe	except:
2112538Sesaxe		error("failed to open: " + tmpFile2)
2122538Sesaxe		cleanup(1)
2132538Sesaxe
2142538Sesaxe	fd1.write(d1)
2152538Sesaxe	fd2.write(d2)
2162538Sesaxe	fd1.close()
2172538Sesaxe	fd2.close()
2182538Sesaxe
2192538Sesaxe	return diffFileData(tmpFile1, tmpFile2)
2202538Sesaxe
2212538Sesaxe#####
2222538Sesaxe# Misc utility functions
2232538Sesaxe#
2242538Sesaxe
2252538Sesaxe# Prune off the leading prefix from string s
2262538Sesaxedef str_prefix_trunc(s, prefix) :
2272538Sesaxe	snipLen = len(prefix)
2282538Sesaxe	return s[snipLen:]
2292538Sesaxe
2302538Sesaxe#
2312538Sesaxe# Prune off leading proto path goo (if there is one) to yield
2322538Sesaxe# the deliverable's eventual path relative to root
2332538Sesaxe# e.g. proto.base/root_sparc/usr/src/cmd/prstat => usr/src/cmd/prstat
2342538Sesaxe#
2352538Sesaxedef fnFormat(fn) :
2362538Sesaxe	root_arch_str = "root_" + arch
2372538Sesaxe
2382538Sesaxe	pos = fn.find(root_arch_str)
2392538Sesaxe	if pos == -1 :
2402538Sesaxe		return fn
2412538Sesaxe
2422538Sesaxe	pos = fn.find("/", pos)
2432538Sesaxe	if pos == -1 :
2442538Sesaxe		return fn
2452538Sesaxe
2462538Sesaxe	return fn[pos + 1:]
2472538Sesaxe
2482538Sesaxe#####
2492538Sesaxe# Usage / argument processing
2502538Sesaxe#
2512538Sesaxe
2522538Sesaxe#
2532538Sesaxe# Display usage message
2542538Sesaxe#
2552538Sesaxedef usage() :
2562538Sesaxe	sys.stdout.flush()
2572538Sesaxe	print >> sys.stderr, """Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new
2582538Sesaxe        -v      Do not truncate observed diffs in results
2592538Sesaxe        -V      Log *all* ELF sect diffs vs. logging the first diff found
2602538Sesaxe        -t      Use onbld tools in $SRC/tools
2612538Sesaxe        -r      Log results and observed differences
2622538Sesaxe        -i      Tell wsdiff which objects to compare via an input file list"""
2632538Sesaxe	sys.exit(1)
2642538Sesaxe
2652538Sesaxe#
2662538Sesaxe# Process command line options
2672538Sesaxe#
2682538Sesaxedef args() :
2692538Sesaxe
2702538Sesaxe	global logging
2712538Sesaxe	global vdiffs
2722538Sesaxe	global reportAllSects
2732538Sesaxe
2742538Sesaxe	validOpts = 'i:r:vVt?'
2752538Sesaxe
2762538Sesaxe	baseRoot = ""
2772538Sesaxe	ptchRoot = ""
2782538Sesaxe	fileNamesFile = ""
2792538Sesaxe	results = ""
2802538Sesaxe	localTools = False
2812538Sesaxe
2822538Sesaxe	# getopt.getopt() returns:
2832538Sesaxe	#	an option/value tuple
2842538Sesaxe	#	a list of remaining non-option arguments
2852538Sesaxe	#
2862538Sesaxe	# A correct wsdiff invocation will have exactly two non option
2872538Sesaxe	# arguments, the paths to the base (old), ptch (new) proto areas
2882538Sesaxe	try:
2892538Sesaxe		optlist, args = getopt.getopt(sys.argv[1:], validOpts)
2902538Sesaxe	except getopt.error, val:
2912538Sesaxe		usage()
2922538Sesaxe
2932538Sesaxe	if len(args) != 2 :
2942538Sesaxe		usage();
2952538Sesaxe
2962538Sesaxe	for opt,val in optlist :
2972538Sesaxe		if opt == '-i' :
2982538Sesaxe			fileNamesFile = val
2992538Sesaxe		elif opt == '-r' :
3002538Sesaxe			results = val
3012538Sesaxe			logging = True
3022538Sesaxe		elif opt == '-v' :
3032538Sesaxe			vdiffs = True
3042538Sesaxe		elif opt == '-V' :
3052538Sesaxe			reportAllSects = True
3062538Sesaxe		elif opt == '-t':
3072538Sesaxe			localTools = True
3082538Sesaxe		else:
3092538Sesaxe			usage()
3102538Sesaxe
3112538Sesaxe	baseRoot = args[0]
3122538Sesaxe	ptchRoot = args[1]
3132538Sesaxe
3142538Sesaxe	if len(baseRoot) == 0 or len(ptchRoot) == 0 :
3152538Sesaxe		usage()
3162538Sesaxe
3172538Sesaxe	if logging and len(results) == 0 :
3182538Sesaxe		usage()
3192538Sesaxe
3202538Sesaxe	if vdiffs and not logging :
3212538Sesaxe		error("The -v option requires a results file (-r)")
3222538Sesaxe		sys.exit(1)
3232538Sesaxe
3242538Sesaxe	if reportAllSects and not logging :
3252538Sesaxe		error("The -V option requires a results file (-r)")
3262538Sesaxe		sys.exit(1)
3272538Sesaxe
3282538Sesaxe	# alphabetical order
3292538Sesaxe	return	baseRoot, fileNamesFile, localTools, ptchRoot, results
3302538Sesaxe
3312538Sesaxe#####
3322538Sesaxe# File identification
3332538Sesaxe#
3342538Sesaxe
3352538Sesaxe#
3362538Sesaxe# Identify the file type.
3372538Sesaxe# If it's not ELF, use the file extension to identify
3382538Sesaxe# certain file types that require special handling to
3392538Sesaxe# compare. Otherwise just return a basic "ASCII" type.
3402538Sesaxe#
3412538Sesaxedef getTheFileType(f) :
3422538Sesaxe
3432538Sesaxe	extensions = { 'a'	:	'ELF Object Archive',
3442538Sesaxe		       'jar'	:	'Java Archive',
3452538Sesaxe		       'html'	:	'HTML',
3462538Sesaxe		       'ln'	:	'Lint Library',
347*5298Srotondo		       'esa'	:	'Elfsign Activation',
3482538Sesaxe		       'db'	:	'Sqlite Database' }
3492538Sesaxe
350*5298Srotondo	try:
351*5298Srotondo		if os.stat(f)[ST_SIZE] == 0 :
352*5298Srotondo			return 'ASCII'
353*5298Srotondo	except:
354*5298Srotondo		error("failed to stat " + f)
355*5298Srotondo		return 'Error'
3562538Sesaxe
3572538Sesaxe	if isELF(f) == 1 :
3582538Sesaxe		return 'ELF'
3592538Sesaxe
3602538Sesaxe	fnamelist = f.split('.')
3612538Sesaxe	if len(fnamelist) > 1 :	# Test the file extension
3622538Sesaxe		extension = fnamelist[-1]
3632538Sesaxe		if extension in extensions.keys():
3642538Sesaxe			return extensions[extension]
3652538Sesaxe
3662538Sesaxe	return 'ASCII'
3672538Sesaxe
3682538Sesaxe#
3692538Sesaxe# Return non-zero if "f" is an ELF file
3702538Sesaxe#
3712538Sesaxeelfmagic = '\177ELF'
3722538Sesaxedef isELF(f) :
3732538Sesaxe	try:
3742538Sesaxe		fd = open(f)
3752538Sesaxe	except:
3762538Sesaxe		error("failed to open: " + f)
3772538Sesaxe		return 0
3782538Sesaxe	magic = fd.read(len(elfmagic))
3792538Sesaxe	fd.close()
3802538Sesaxe
3812538Sesaxe	if magic == elfmagic :
3822538Sesaxe		return 1
3832538Sesaxe	return 0
3842538Sesaxe
3852538Sesaxe#
3862538Sesaxe# Return non-zero is "f" is binary.
3872538Sesaxe# Consider the file to be binary if it contains any null characters
3882538Sesaxe#
3892538Sesaxedef isBinary(f) :
3902538Sesaxe	try:
3912538Sesaxe		fd = open(f)
3922538Sesaxe	except:
3932538Sesaxe		error("failed to open: " + f)
3942538Sesaxe		return 0
3952538Sesaxe	s = fd.read()
3962538Sesaxe	fd.close()
3972538Sesaxe
3982538Sesaxe	if s.find('\0') == -1 :
3992538Sesaxe		return 0
4002538Sesaxe	else :
4012538Sesaxe		return 1
4022538Sesaxe
4032538Sesaxe#####
4042538Sesaxe# Directory traversal and file finding
4052538Sesaxe#
4062538Sesaxe
4072538Sesaxe#
4082538Sesaxe# Return a sorted list of files found under the specified directory
4092538Sesaxe#
4102538Sesaxedef findFiles(d) :
4112538Sesaxe	for path, subdirs, files in os.walk(d) :
4122538Sesaxe		files.sort()
4132538Sesaxe		for name in files :
4142538Sesaxe			yield os.path.join(path, name)
4152538Sesaxe
4162538Sesaxe#
4172538Sesaxe# Examine all files in base, ptch
4182538Sesaxe#
4192538Sesaxe# Return a list of files appearing in both proto areas,
4202538Sesaxe# a list of new files (files found only in ptch) and
4212538Sesaxe# a list of deleted files (files found only in base)
4222538Sesaxe#
4232538Sesaxedef protoCatalog(base, ptch) :
4242538Sesaxe	compFiles = []		# List of files in both proto areas
4252538Sesaxe	ptchList = []		# List of file in patch proto area
4262538Sesaxe
4272538Sesaxe	newFiles = []		# New files detected
4282538Sesaxe	deletedFiles = []	# Deleted files
4292538Sesaxe
4302538Sesaxe	baseFilesList = list(findFiles(base))
4312538Sesaxe	baseStringLength = len(base)
4322538Sesaxe
4332538Sesaxe	ptchFilesList = list(findFiles(ptch))
4342538Sesaxe	ptchStringLength = len(ptch)
4352538Sesaxe
4362538Sesaxe	# Inventory files in the base proto area
4372538Sesaxe	for fn in baseFilesList :
4382538Sesaxe		if os.path.islink(fn) :
4392538Sesaxe			continue
4402538Sesaxe
4412538Sesaxe		fileName = fn[baseStringLength:]
4422538Sesaxe		compFiles.append(fileName)
4432538Sesaxe
4442538Sesaxe	# Inventory files in the patch proto area
4452538Sesaxe	for fn in ptchFilesList :
4462538Sesaxe		if os.path.islink(fn) :
4472538Sesaxe			continue
4482538Sesaxe
4492538Sesaxe		fileName = fn[ptchStringLength:]
4502538Sesaxe		ptchList.append(fileName)
4512538Sesaxe
4522538Sesaxe	# Deleted files appear in the base area, but not the patch area
4532538Sesaxe	for fileName in compFiles :
4542538Sesaxe		if not fileName in ptchList :
4552538Sesaxe			deletedFiles.append(fileName)
4562538Sesaxe
4572538Sesaxe	# Eliminate "deleted" files from the list of objects appearing
4582538Sesaxe	# in both the base and patch proto areas
4592538Sesaxe	for fileName in deletedFiles :
4602538Sesaxe		try:
4612538Sesaxe		       	compFiles.remove(fileName)
4622538Sesaxe		except:
4632538Sesaxe			error("filelist.remove() failed")
4642538Sesaxe
4652538Sesaxe	# New files appear in the patch area, but not the base
4662538Sesaxe	for fileName in ptchList :
4672538Sesaxe		if not fileName in compFiles :
4682538Sesaxe			newFiles.append(fileName)
4692538Sesaxe
4702538Sesaxe	return compFiles, newFiles, deletedFiles
4712538Sesaxe
4722538Sesaxe#
4732538Sesaxe# Examine the files listed in the input file list
4742538Sesaxe#
4752538Sesaxe# Return a list of files appearing in both proto areas,
4762538Sesaxe# a list of new files (files found only in ptch) and
4772538Sesaxe# a list of deleted files (files found only in base)
4782538Sesaxe#
4792538Sesaxedef flistCatalog(base, ptch, flist) :
4802538Sesaxe	compFiles = []		# List of files in both proto areas
4812538Sesaxe	newFiles = []		# New files detected
4822538Sesaxe	deletedFiles = []	# Deleted files
4832538Sesaxe
4842538Sesaxe	try:
4852538Sesaxe		fd = open(flist, "r")
4862538Sesaxe	except:
4872538Sesaxe		error("could not open: " + flist)
4882538Sesaxe		cleanup(1)
4892538Sesaxe
4902538Sesaxe	files = []
4912538Sesaxe	files = fd.readlines()
4922538Sesaxe
4932538Sesaxe	for f in files :
4942538Sesaxe		ptch_present = True
4952538Sesaxe		base_present = True
4962538Sesaxe
4972538Sesaxe		if f == '\n' :
4982538Sesaxe			continue
4992538Sesaxe
5002538Sesaxe		# the fileNames have a trailing '\n'
5012538Sesaxe		f = f.rstrip()
5022538Sesaxe
5032538Sesaxe		# The objects in the file list have paths relative
5042538Sesaxe		# to $ROOT or to the base/ptch directory specified on
5052538Sesaxe		# the command line.
5062538Sesaxe		# If it's relative to $ROOT, we'll need to add back the
5072538Sesaxe		# root_`uname -p` goo we stripped off in fnFormat()
5082538Sesaxe		if os.path.exists(base + f) :
5092538Sesaxe			fn = f;
5102538Sesaxe		elif os.path.exists(base + "root_" + arch + "/" + f) :
5112538Sesaxe			fn = "root_" + arch + "/" + f
5122538Sesaxe		else :
5132538Sesaxe			base_present = False
5142538Sesaxe
5152538Sesaxe		if base_present :
5162538Sesaxe			if not os.path.exists(ptch + fn) :
5172538Sesaxe				ptch_present = False
5182538Sesaxe		else :
5192538Sesaxe			if os.path.exists(ptch + f) :
5202538Sesaxe				fn = f
5212538Sesaxe			elif os.path.exists(ptch + "root_" + arch + "/" + f) :
5222538Sesaxe				fn = "root_" + arch + "/" + f
5232538Sesaxe			else :
5242538Sesaxe				ptch_present = False
5252538Sesaxe
5262538Sesaxe		if os.path.islink(base + fn) :	# ignore links
5272538Sesaxe			base_present = False
5282538Sesaxe		if os.path.islink(ptch + fn) :
5292538Sesaxe			ptch_present = False
5302538Sesaxe
5312538Sesaxe		if base_present and ptch_present :
5322538Sesaxe			compFiles.append(fn)
5332538Sesaxe		elif base_present :
5342538Sesaxe			deletedFiles.append(fn)
5352538Sesaxe		elif ptch_present :
5362538Sesaxe			newFiles.append(fn)
5372538Sesaxe		else :
5382538Sesaxe			if os.path.islink(base + fn) and os.path.islink(ptch + fn) :
5392538Sesaxe				continue
5402538Sesaxe			error(f + " in file list, but not in either tree. Skipping...")
5412538Sesaxe
5422538Sesaxe	return compFiles, newFiles, deletedFiles
5432538Sesaxe
5442538Sesaxe
5452538Sesaxe#
5462538Sesaxe# Build a fully qualified path to an external tool/utility.
5472538Sesaxe# Consider the default system locations. For onbld tools, if
5482538Sesaxe# the -t option was specified, we'll try to use built tools in $SRC tools,
5492538Sesaxe# and otherwise, we'll fall back on /opt/onbld/
5502538Sesaxe#
5512538Sesaxedef find_tool(tool) :
5522538Sesaxe
5532538Sesaxe	# First, check what was passed
5542538Sesaxe	if os.path.exists(tool) :
5552538Sesaxe		return tool
5562538Sesaxe
5572538Sesaxe	# Next try in wsdiff path
5582538Sesaxe	for pdir in wsdiff_path :
5592538Sesaxe		location = pdir + "/" + tool
5602538Sesaxe		if os.path.exists(location) :
5612538Sesaxe			return location + " "
5622538Sesaxe
5632538Sesaxe		location = pdir + "/" + arch + "/" + tool
5642538Sesaxe		if os.path.exists(location) :
5652538Sesaxe			return location + " "
5662538Sesaxe
5672538Sesaxe	error("Could not find path to: " + tool);
5682538Sesaxe	sys.exit(1);
5692538Sesaxe
5702538Sesaxe
5712538Sesaxe#####
5722538Sesaxe# ELF file comparison helper routines
5732538Sesaxe#
5742538Sesaxe
5752538Sesaxe#
5762538Sesaxe# Return a dictionary of ELF section types keyed by section name
5772538Sesaxe#
5782538Sesaxedef get_elfheader(f) :
5792538Sesaxe
5802538Sesaxe	header = {}
5812538Sesaxe
5822538Sesaxe	hstring = commands.getoutput(elfdump_cmd + " -c " + f)
5832538Sesaxe
5842538Sesaxe	if len(hstring) == 0 :
5852538Sesaxe		error("Failed to dump ELF header for " + f)
5862538Sesaxe		return
5872538Sesaxe
5882538Sesaxe	# elfdump(1) dumps the section headers with the section name
5892538Sesaxe	# following "sh_name:", and the section type following "sh_type:"
5902538Sesaxe	sections = hstring.split("Section Header")
5912538Sesaxe	for sect in sections :
5922538Sesaxe		datap = sect.find("sh_name:");
5932538Sesaxe		if datap == -1 :
5942538Sesaxe			continue
5952538Sesaxe		section = sect[datap:].split()[1]
5962538Sesaxe		datap = sect.find("sh_type:");
5972538Sesaxe		if datap == -1 :
5982538Sesaxe			error("Could not get type for sect: " + section + \
5992538Sesaxe			      " in " + f)
6002538Sesaxe		sh_type = sect[datap:].split()[2]
6012538Sesaxe		header[section] = sh_type
6022538Sesaxe
6032538Sesaxe	return header
6042538Sesaxe
6052538Sesaxe#
6062538Sesaxe# Extract data in the specified ELF section from the given file
6072538Sesaxe#
6082538Sesaxedef extract_elf_section(f, section) :
6092538Sesaxe
6102538Sesaxe	data = commands.getoutput(dump_cmd + " -sn " + section + " " + f)
6112538Sesaxe
6122538Sesaxe	if len(data) == 0 :
6132538Sesaxe		error(cmd + " yielded no data")
6142538Sesaxe		return
6152538Sesaxe
6162538Sesaxe	# dump(1) displays the file name to start...
6172538Sesaxe	# get past it to the data itself
6182538Sesaxe	dbegin = data.find(":") + 1
6192538Sesaxe	data = data[dbegin:];
6202538Sesaxe
6212538Sesaxe	return (data)
6222538Sesaxe
6232538Sesaxe#
6242538Sesaxe# Return a (hopefully meaningful) human readable set of diffs
6252538Sesaxe# for the specified ELF section between f1 and f2
6262538Sesaxe#
6272538Sesaxe# Depending on the section, various means for dumping and diffing
6282538Sesaxe# the data may be employed.
6292538Sesaxe#
6302538Sesaxetext_sections = [ '.text', '.init', '.fini' ]
6312538Sesaxedef diff_elf_section(f1, f2, section, sh_type) :
6322538Sesaxe
6332538Sesaxe	if (sh_type == "SHT_RELA") : # sh_type == SHT_RELA
6342538Sesaxe		cmd1 = elfdump_cmd + " -r " + f1 + " > " + tmpFile1
6352538Sesaxe		cmd2 = elfdump_cmd + " -r " + f2 + " > " + tmpFile2
6362538Sesaxe	elif (section == ".group") :
6372538Sesaxe		cmd1 = elfdump_cmd + " -g " + f1 + " > " + tmpFile1
6382538Sesaxe		cmd2 = elfdump_cmd + " -g " + f2 + " > " + tmpFile2
6392538Sesaxe	elif (section == ".hash") :
6402538Sesaxe		cmd1 = elfdump_cmd + " -h " + f1 + " > " + tmpFile1
6412538Sesaxe		cmd2 = elfdump_cmd + " -h " + f2 + " > " + tmpFile2
6422538Sesaxe	elif (section == ".dynamic") :
6432538Sesaxe		cmd1 = elfdump_cmd + " -d " + f1 + " > " + tmpFile1
6442538Sesaxe		cmd2 = elfdump_cmd + " -d " + f2 + " > " + tmpFile2
6452538Sesaxe	elif (section == ".got") :
6462538Sesaxe		cmd1 = elfdump_cmd + " -G " + f1 + " > " + tmpFile1
6472538Sesaxe		cmd2 = elfdump_cmd + " -G " + f2 + " > " + tmpFile2
6482538Sesaxe	elif (section == ".SUNW_cap") :
6492538Sesaxe		cmd1 = elfdump_cmd + " -H " + f1 + " > " + tmpFile1
6502538Sesaxe		cmd2 = elfdump_cmd + " -H " + f2 + " > " + tmpFile2
6512538Sesaxe	elif (section == ".interp") :
6522538Sesaxe		cmd1 = elfdump_cmd + " -i " + f1 + " > " + tmpFile1
6532538Sesaxe		cmd2 = elfdump_cmd + " -i " + f2 + " > " + tmpFile2
6542538Sesaxe	elif (section == ".symtab" or section == ".dynsym") :
6552538Sesaxe		cmd1 = elfdump_cmd + " -s -N " + section + " " + f1 + " > " + tmpFile1
6562538Sesaxe		cmd2 = elfdump_cmd + " -s -N " + section + " " + f2 + " > " + tmpFile2
6572538Sesaxe	elif (section in text_sections) :
6582538Sesaxe		# dis sometimes complains when it hits something it doesn't
6592538Sesaxe		# know how to disassemble. Just ignore it, as the output
6602538Sesaxe		# being generated here is human readable, and we've already
6612538Sesaxe		# correctly flagged the difference.
6622538Sesaxe		cmd1 = dis_cmd + " -t " + section + " " + f1 + \
6632538Sesaxe		       " 2>/dev/null | grep -v disassembly > " + tmpFile1
6642538Sesaxe		cmd2 = dis_cmd + " -t " + section + " " + f2 + \
6652538Sesaxe		       " 2>/dev/null | grep -v disassembly > " + tmpFile2
6662538Sesaxe	else :
6672538Sesaxe		cmd1 = elfdump_cmd + " -w " + tmpFile1 + " -N " + \
6682538Sesaxe		       section + " " + f1
6692538Sesaxe		cmd2 = elfdump_cmd + " -w " + tmpFile2 + " -N " + \
6702538Sesaxe		       section + " " + f2
6712538Sesaxe
6722538Sesaxe	os.system(cmd1)
6732538Sesaxe	os.system(cmd2)
6742538Sesaxe
6752538Sesaxe	data = diffFileData(tmpFile1, tmpFile2)
6762538Sesaxe
6772538Sesaxe	return (data)
6782538Sesaxe
6792538Sesaxe#
6802538Sesaxe# compare the relevant sections of two ELF binaries
6812538Sesaxe# and report any differences
6822538Sesaxe#
6832538Sesaxe# Returns: 1 if any differenes found
6842538Sesaxe#          0 if no differences found
6852538Sesaxe#	  -1 on error
6862538Sesaxe#
6872538Sesaxe
6882538Sesaxe# Sections deliberately not considered when comparing two ELF
6892538Sesaxe# binaries. Differences observed in these sections are not considered
6902538Sesaxe# significant where patch deliverable identification is concerned.
6912538Sesaxesections_to_skip = [ ".SUNW_signature",
6922538Sesaxe		     ".comment",
6932538Sesaxe		     ".SUNW_ctf",
6942538Sesaxe		     ".debug",
6952538Sesaxe		     ".plt",
6962538Sesaxe		     ".rela.bss",
6972538Sesaxe		     ".rela.plt",
6982538Sesaxe		     ".line",
6992538Sesaxe		     ".note",
7003002Sesaxe		     ".compcom",
7012538Sesaxe		     ]
7022538Sesaxe
7032538Sesaxesections_preferred = [ ".rodata.str1.8",
7042538Sesaxe		       ".rodata.str1.1",
7052538Sesaxe		       ".rodata",
7062538Sesaxe		       ".data1",
7072538Sesaxe		       ".data",
7082538Sesaxe		       ".text",
7092538Sesaxe		       ]
7102538Sesaxe
7112538Sesaxedef compareElfs(base, ptch, quiet) :
7122538Sesaxe
7132538Sesaxe	global logging
7142538Sesaxe
7152538Sesaxe	base_header = get_elfheader(base)
7162538Sesaxe 	sections = base_header.keys()
7172538Sesaxe
7182538Sesaxe	ptch_header = get_elfheader(ptch)
7192538Sesaxe	e2_only_sections = ptch_header.keys()
7202538Sesaxe
7212538Sesaxe	e1_only_sections = []
7222538Sesaxe
7232538Sesaxe	fileName = fnFormat(base)
7242538Sesaxe
7252538Sesaxe	# Derive the list of ELF sections found only in
7262538Sesaxe	# either e1 or e2.
7272538Sesaxe	for sect in sections :
7282538Sesaxe		if not sect in e2_only_sections :
7292538Sesaxe			e1_only_sections.append(sect)
7302538Sesaxe		else :
7312538Sesaxe			e2_only_sections.remove(sect)
7322538Sesaxe
7332538Sesaxe	if len(e1_only_sections) > 0 :
7342538Sesaxe		if quiet :
7352538Sesaxe			return 1
7362538Sesaxe		info(fileName);
7372538Sesaxe		if not logging :
7382538Sesaxe			return 1
7392538Sesaxe
7402538Sesaxe		slist = ""
7412538Sesaxe		for sect in e1_only_sections :
7422538Sesaxe			slist = slist + sect + "\t"
7432538Sesaxe		v_info("\nELF sections found in " + \
7442538Sesaxe		      base + " but not in " + ptch)
7452538Sesaxe		v_info("\n" + slist)
7462538Sesaxe		return 1
7472538Sesaxe
7482538Sesaxe	if len(e2_only_sections) > 0 :
7492538Sesaxe		if quiet :
7502538Sesaxe			return 1
7512538Sesaxe
7522538Sesaxe		info(fileName);
7532538Sesaxe		if not logging :
7542538Sesaxe			return 1
7552538Sesaxe
7562538Sesaxe		slist = ""
7572538Sesaxe		for sect in e2_only_sections :
7582538Sesaxe			slist = slist + sect + "\t"
7592538Sesaxe		v_info("\nELF sections found in " + \
7602538Sesaxe		      ptch + " but not in " + base)
7612538Sesaxe		v_info("\n" + slist)
7622538Sesaxe		return 1
7632538Sesaxe
7642538Sesaxe	# Look for preferred sections, and put those at the
7652538Sesaxe	# top of the list of sections to compare
7662538Sesaxe	for psect in sections_preferred :
7672538Sesaxe		if psect in sections :
7682538Sesaxe			sections.remove(psect)
7692538Sesaxe			sections.insert(0, psect)
7702538Sesaxe
7712538Sesaxe	# Compare ELF sections
7722538Sesaxe	first_section = True
7732538Sesaxe	for sect in sections :
7742538Sesaxe
7752538Sesaxe		if sect in sections_to_skip :
7762538Sesaxe			continue
7772538Sesaxe
7782538Sesaxe		s1 = extract_elf_section(base, sect);
7792538Sesaxe		s2 = extract_elf_section(ptch, sect);
7802538Sesaxe
7812538Sesaxe		if len(s1) != len (s2) or s1 != s2:
7822538Sesaxe			if not quiet:
7832538Sesaxe				sh_type = base_header[sect]
7842538Sesaxe				data = diff_elf_section(base, ptch, sect, \
7852538Sesaxe							sh_type)
7862538Sesaxe
7872538Sesaxe				# If all ELF sections are being reported, then
7882538Sesaxe				# invoke difference() to flag the file name to
7892538Sesaxe				# stdout only once. Any other section differences
7902538Sesaxe				# should be logged to the results file directly
7912538Sesaxe				if not first_section :
7922538Sesaxe					log_difference(fileName, "ELF " + sect, data)
7932538Sesaxe				else :
7942538Sesaxe					difference(fileName, "ELF " + sect, data)
7952538Sesaxe
7962538Sesaxe			if not reportAllSects :
7972538Sesaxe				return 1
7982538Sesaxe			first_section = False
7992538Sesaxe	return 0
8002538Sesaxe
8012538Sesaxe#####
8022538Sesaxe# Archive object comparison
8032538Sesaxe#
8042538Sesaxe# Returns 1 if difference detected
8052538Sesaxe#         0 if no difference detected
8062538Sesaxe#        -1 on error
8072538Sesaxe#
8082538Sesaxedef compareArchives(base, ptch, fileType) :
8092538Sesaxe
8102538Sesaxe	fileName = fnFormat(base)
8112538Sesaxe
8122538Sesaxe	# clear the temp directories
8132538Sesaxe	baseCmd = "rm -rf " + tmpDir1 + "*"
8142538Sesaxe	status, output = commands.getstatusoutput(baseCmd)
8152538Sesaxe	if status != 0 :
8162538Sesaxe		error(baseCmd + " failed: " + output)
8172538Sesaxe		return -1
8182538Sesaxe
8192538Sesaxe	ptchCmd = "rm -rf " + tmpDir2 + "*"
8202538Sesaxe	status, output = commands.getstatusoutput(ptchCmd)
8212538Sesaxe	if status != 0 :
8222538Sesaxe		error(ptchCmd + " failed: " + output)
8232538Sesaxe		return -1
8242538Sesaxe
8252538Sesaxe	#
8262538Sesaxe	# Be optimistic and first try a straight file compare
8272538Sesaxe	# as it will allow us to finish up quickly.
8282538Sesaxe	if compareBasic(base, ptch, True, fileType) == 0 :
8292538Sesaxe		return 0
8302538Sesaxe
8312538Sesaxe	# copy over the objects to the temp areas, and
8322538Sesaxe	# unpack them
8332538Sesaxe	baseCmd = "cp -fp " + base + " " + tmpDir1
8342538Sesaxe	status, output = commands.getstatusoutput(baseCmd)
8352538Sesaxe	if status != 0 :
8362538Sesaxe		error(baseCmd + " failed: " + output)
8372538Sesaxe		return -1
8382538Sesaxe
8392538Sesaxe	ptchCmd = "cp -fp " + ptch + " " + tmpDir2
8402538Sesaxe	status, output = commands.getstatusoutput(ptchCmd)
8412538Sesaxe	if status != 0 :
8422538Sesaxe		error(ptchCmd + " failed: " + output)
8432538Sesaxe		return -1
8442538Sesaxe
8452538Sesaxe	bname = string.split(fileName, '/')[-1]
8462538Sesaxe	if fileType == "Java Archive" :
8472538Sesaxe		baseCmd = "cd " + tmpDir1 + "; " + "jar xf " + bname + \
8482538Sesaxe			  "; rm -f " + bname + " META-INF/MANIFEST.MF"
8492538Sesaxe		ptchCmd = "cd " + tmpDir2 + "; " + "jar xf " + bname + \
8502538Sesaxe			  "; rm -f " + bname + " META-INF/MANIFEST.MF"
8512538Sesaxe	elif fileType == "ELF Object Archive" :
8522538Sesaxe		baseCmd = "cd " + tmpDir1 + "; " + "/usr/ccs/bin/ar x " + \
8532538Sesaxe			  bname + "; rm -f " + bname
8542538Sesaxe		ptchCmd = "cd " + tmpDir2 + "; " + "/usr/ccs/bin/ar x " + \
8552538Sesaxe			  bname + "; rm -f " + bname
8562538Sesaxe	else :
8572538Sesaxe		error("unexpected file type: " + fileType)
8582538Sesaxe		return -1
8592538Sesaxe
8602538Sesaxe	os.system(baseCmd)
8612538Sesaxe	os.system(ptchCmd)
8622538Sesaxe
8632538Sesaxe	baseFlist = list(findFiles(tmpDir1))
8642538Sesaxe	ptchFlist = list(findFiles(tmpDir2))
8652538Sesaxe
8662538Sesaxe	# Trim leading path off base/ptch file lists
8672538Sesaxe	flist = []
8682538Sesaxe	for fn in baseFlist :
8692538Sesaxe		flist.append(str_prefix_trunc(fn, tmpDir1))
8702538Sesaxe	baseFlist = flist
8712538Sesaxe
8722538Sesaxe	flist = []
8732538Sesaxe	for fn in ptchFlist :
8742538Sesaxe		flist.append(str_prefix_trunc(fn, tmpDir2))
8752538Sesaxe	ptchFlist = flist
8762538Sesaxe
8772538Sesaxe	for fn in ptchFlist :
8782538Sesaxe		if not fn in baseFlist :
8792538Sesaxe			difference(fileName, fileType, \
8802538Sesaxe				   fn + " added to " + fileName)
8812538Sesaxe			return 1
8822538Sesaxe
8832538Sesaxe	for fn in baseFlist :
8842538Sesaxe		if not fn in ptchFlist :
8852538Sesaxe			difference(fileName, fileType, \
8862538Sesaxe				   fn + " removed from " + fileName)
8872538Sesaxe			return 1
8882538Sesaxe
8892538Sesaxe		differs = compareOneFile((tmpDir1 + fn), (tmpDir2 + fn), True)
8902538Sesaxe		if differs :
8912538Sesaxe			difference(fileName, fileType, \
8922538Sesaxe				   fn + " in " + fileName + " differs")
8932538Sesaxe			return 1
8942538Sesaxe	return 0
8952538Sesaxe
8962538Sesaxe#####
8972538Sesaxe# (Basic) file comparison
8982538Sesaxe#
8992538Sesaxe# There's some special case code here for Javadoc HTML files
9002538Sesaxe#
9012538Sesaxe# Returns 1 if difference detected
9022538Sesaxe#         0 if no difference detected
9032538Sesaxe#        -1 on error
9042538Sesaxe#
9052538Sesaxedef compareBasic(base, ptch, quiet, fileType) :
9062538Sesaxe
9072538Sesaxe	fileName = fnFormat(base);
9082538Sesaxe
9092538Sesaxe	if quiet and os.stat(base)[ST_SIZE] != os.stat(ptch)[ST_SIZE] :
9102538Sesaxe		return 1
9112538Sesaxe
9122538Sesaxe	try:
9132538Sesaxe		baseFile = open(base)
9142538Sesaxe	except:
9152538Sesaxe		error("could not open " + base)
9162538Sesaxe		return -1
9172538Sesaxe	try:
9182538Sesaxe		ptchFile = open(ptch)
9192538Sesaxe	except:
9202538Sesaxe		error("could not open " + ptch)
9212538Sesaxe		return -1
9222538Sesaxe
9232538Sesaxe	baseData = baseFile.read()
9242538Sesaxe	ptchData = ptchFile.read()
9252538Sesaxe
9262538Sesaxe	baseFile.close()
9272538Sesaxe	ptchFile.close()
9282538Sesaxe
9292538Sesaxe	needToSnip = False
9302538Sesaxe	if fileType == "HTML" :
9312538Sesaxe		needToSnip = True
9322538Sesaxe		toSnipBeginStr = "<!-- Generated by javadoc"
9332538Sesaxe		toSnipEndStr = "-->\n"
9342538Sesaxe
9352538Sesaxe	if needToSnip :
9362538Sesaxe		toSnipBegin = string.find(baseData, toSnipBeginStr)
9372538Sesaxe		if toSnipBegin != -1 :
9382538Sesaxe			toSnipEnd = string.find(baseData[toSnipBegin:], \
9392538Sesaxe						toSnipEndStr) + \
9402538Sesaxe						len(toSnipEndStr)
9412538Sesaxe			baseData = baseData[:toSnipBegin] + \
9422538Sesaxe				   baseData[toSnipBegin + toSnipEnd:]
9432538Sesaxe			ptchData = ptchData[:toSnipBegin] + \
9442538Sesaxe				   ptchData[toSnipBegin + toSnipEnd:]
9452538Sesaxe
9462538Sesaxe	if quiet :
9472538Sesaxe		if baseData != ptchData :
9482538Sesaxe			return 1
9492538Sesaxe	else :
9502538Sesaxe		if len(baseData) != len(ptchData) or baseData != ptchData :
9512538Sesaxe			diffs = diffData(baseData, ptchData)
9522538Sesaxe			difference(fileName, fileType, diffs)
9532538Sesaxe			return 1
9542538Sesaxe	return 0
9552538Sesaxe
9562538Sesaxe
9572538Sesaxe#####
9582538Sesaxe# Compare two objects by producing a data dump from
9592538Sesaxe# each object, and then comparing the dump data
9602538Sesaxe#
9612538Sesaxe# Returns: 1 if a difference is detected
9622538Sesaxe#          0 if no difference detected
9632538Sesaxe#         -1 upon error
9642538Sesaxe#
9652538Sesaxedef compareByDumping(base, ptch, quiet, fileType) :
9662538Sesaxe
9672538Sesaxe	fileName = fnFormat(base);
9682538Sesaxe
9692538Sesaxe	if fileType == "Lint Library" :
9702538Sesaxe		baseCmd = lintdump_cmd + " -ir " + base + \
9712538Sesaxe			  " | grep -v LINTLIB:" + " > " + tmpFile1
9722538Sesaxe		ptchCmd = lintdump_cmd + " -ir " + ptch + \
9732538Sesaxe			  " | grep -v LINTLIB:" + " > " + tmpFile2
9742538Sesaxe	elif fileType == "Sqlite Database" :
9752538Sesaxe		baseCmd = "echo .dump | " + sqlite_cmd + base + " > " + \
9762538Sesaxe			  tmpFile1
9772538Sesaxe		ptchCmd = "echo .dump | " + sqlite_cmd + ptch + " > " + \
9782538Sesaxe			  tmpFile2
9792538Sesaxe
9802538Sesaxe	os.system(baseCmd)
9812538Sesaxe	os.system(ptchCmd)
9822538Sesaxe
9832538Sesaxe	try:
9842538Sesaxe		baseFile = open(tmpFile1)
9852538Sesaxe	except:
9862538Sesaxe		error("could not open: " + tmpFile1)
9872538Sesaxe	try:
9882538Sesaxe		ptchFile = open(tmpFile2)
9892538Sesaxe	except:
9902538Sesaxe		error("could not open: " + tmpFile2)
9912538Sesaxe
9922538Sesaxe	baseData = baseFile.read()
9932538Sesaxe	ptchData = ptchFile.read()
9942538Sesaxe
9952538Sesaxe	baseFile.close()
9962538Sesaxe	ptchFile.close()
9972538Sesaxe
9982538Sesaxe	if len(baseData) != len(ptchData) or baseData != ptchData :
9992538Sesaxe		if not quiet :
10002538Sesaxe			data = diffFileData(tmpFile1, tmpFile2);
10012538Sesaxe			difference(fileName, fileType, data)
10022538Sesaxe 		return 1
10032538Sesaxe	return 0
10042538Sesaxe
10052538Sesaxe#####
1006*5298Srotondo# Compare two elfsign activation files. This ignores the activation
1007*5298Srotondo# files themselves and reports a difference if and only if the
1008*5298Srotondo# corresponding base files are different.
1009*5298Srotondo#
1010*5298Srotondo# Returns 1 if difference detected
1011*5298Srotondo#         0 if no difference detected
1012*5298Srotondo#        -1 on error
1013*5298Srotondo#
1014*5298Srotondodef compareActivation(base, ptch, quiet, fileType) :
1015*5298Srotondo
1016*5298Srotondo	fileName = fnFormat(base)
1017*5298Srotondo
1018*5298Srotondo	# Drop the .esa suffix from both filenames.
1019*5298Srotondo	base = base[0:base.rfind('.esa')]
1020*5298Srotondo	ptch = ptch[0:ptch.rfind('.esa')]
1021*5298Srotondo
1022*5298Srotondo	result = compareOneFile(base, ptch, True)
1023*5298Srotondo	if result == -1 :
1024*5298Srotondo		error("unable to compare " + fileName)
1025*5298Srotondo	elif result == 1 :
1026*5298Srotondo		if not quiet :
1027*5298Srotondo			difference(fileName, fileType, \
1028*5298Srotondo				"change in corresponding ELF file")
1029*5298Srotondo
1030*5298Srotondo	return result
1031*5298Srotondo
1032*5298Srotondo#####
10332538Sesaxe# Compare two objects. Detect type changes.
10342538Sesaxe# Vector off to the appropriate type specific
10352538Sesaxe# compare routine based on the type.
10362538Sesaxe#
10372538Sesaxedef compareOneFile(base, ptch, quiet) :
10382538Sesaxe
10392538Sesaxe	# Verify the file types.
10402538Sesaxe	# If they are different, indicate this and move on
10412538Sesaxe	btype = getTheFileType(base)
10422538Sesaxe	ptype = getTheFileType(ptch)
10432538Sesaxe
1044*5298Srotondo	if btype == 'Error' or ptype == 'Error' :
1045*5298Srotondo		return -1
1046*5298Srotondo
10472538Sesaxe	fileName = fnFormat(base)
10482538Sesaxe
10492538Sesaxe	if (btype != ptype) :
1050*5298Srotondo		if not quiet :
1051*5298Srotondo			difference(fileName, "file type", btype + " to " + ptype)
10522538Sesaxe		return 1
10532538Sesaxe	else :
10542538Sesaxe		fileType = btype
10552538Sesaxe
10562538Sesaxe	if (fileType == 'ELF') :
10572538Sesaxe		return compareElfs(base, ptch, quiet)
10582538Sesaxe
10592538Sesaxe	elif (fileType == 'Java Archive' or fileType == 'ELF Object Archive') :
10602538Sesaxe		return compareArchives(base, ptch, fileType)
10612538Sesaxe
10622538Sesaxe	elif (fileType == 'HTML') :
10632538Sesaxe		return compareBasic(base, ptch, quiet, fileType)
10642538Sesaxe
10652538Sesaxe	elif ( fileType == 'Lint Library' ) :
10662538Sesaxe		return compareByDumping(base, ptch, quiet, fileType)
10672538Sesaxe
10682538Sesaxe	elif ( fileType == 'Sqlite Database' ) :
10692538Sesaxe		return compareByDumping(base, ptch, quiet, fileType)
1070*5298Srotondo
1071*5298Srotondo	elif ( fileType == 'Elfsign Activation' ) :
1072*5298Srotondo		return compareActivation(base, ptch, quiet, fileType)
1073*5298Srotondo
10742538Sesaxe	else :
10752538Sesaxe		# it has to be some variety of text file
10762538Sesaxe		return compareBasic(base, ptch, quiet, fileType)
10772538Sesaxe
10782538Sesaxe# Cleanup and self-terminate
10792538Sesaxedef cleanup(ret) :
10802538Sesaxe
10812538Sesaxe	if len(tmpDir1) > 0 and len(tmpDir2) > 0 :
10822538Sesaxe
10832538Sesaxe		baseCmd = "rm -rf " + tmpDir1
10842538Sesaxe		ptchCmd = "rm -rf " + tmpDir2
10852538Sesaxe
10862538Sesaxe		os.system(baseCmd)
10872538Sesaxe		os.system(ptchCmd)
10882538Sesaxe
10892538Sesaxe	if logging :
10902538Sesaxe		log.close()
10912538Sesaxe
10922538Sesaxe	sys.exit(ret)
10932538Sesaxe
10942538Sesaxedef main() :
10952538Sesaxe
10962538Sesaxe	# Log file handle
10972538Sesaxe	global log
10982538Sesaxe
10992538Sesaxe	# Globals relating to command line options
11002538Sesaxe	global logging, vdiffs, reportAllSects
11012538Sesaxe
11022538Sesaxe	# Named temporary files / directories
11032538Sesaxe	global tmpDir1, tmpDir2, tmpFile1, tmpFile2
11042538Sesaxe
11052538Sesaxe	# Command paths
11062538Sesaxe	global lintdump_cmd, elfdump_cmd, dump_cmd, dis_cmd, od_cmd, diff_cmd, sqlite_cmd
11072538Sesaxe
11082538Sesaxe	# Default search path
11092538Sesaxe	global wsdiff_path
11102538Sesaxe
11112538Sesaxe	# Essentially "uname -p"
11122538Sesaxe	global arch
11132538Sesaxe
11142538Sesaxe	# Some globals need to be initialized
11152538Sesaxe	logging = vdiffs = reportAllSects = False
11162538Sesaxe
11172538Sesaxe
11182538Sesaxe	# Process command line arguments
11192538Sesaxe	# Return values are returned from args() in alpha order
11202538Sesaxe	# (Yes, python functions can return multiple values (ewww))
11212538Sesaxe	# Note that args() also set the globals:
11222538Sesaxe	#	logging to True if verbose logging (to a file) was enabled
11232538Sesaxe	#	vdiffs to True if logged differences aren't to be truncated
11242538Sesaxe	#	reportAllSects to True if all ELF section differences are to be reported
11252538Sesaxe	#
11262538Sesaxe	baseRoot, fileNamesFile, localTools, ptchRoot, results = args()
11272538Sesaxe
11282538Sesaxe	#
11292538Sesaxe	# Set up the results/log file
11302538Sesaxe	#
11312538Sesaxe	if logging :
11322538Sesaxe		try:
11332538Sesaxe			log = open(results, "w")
11342538Sesaxe		except:
11352538Sesaxe			logging = False
11362538Sesaxe			error("failed to open log file: " + log)
11372538Sesaxe			sys.exit(1)
11382538Sesaxe
11392538Sesaxe		dateTimeStr= "# %d/%d/%d at %d:%d:%d" % time.localtime()[:6]
11402538Sesaxe		v_info("# This file was produced by wsdiff")
11412538Sesaxe		v_info(dateTimeStr)
11422538Sesaxe
11432538Sesaxe	#
11442538Sesaxe	# Build paths to the tools required tools
11452538Sesaxe	#
11462538Sesaxe	# Try to look for tools in $SRC/tools if the "-t" option
11472538Sesaxe	# was specified
11482538Sesaxe	#
11492538Sesaxe	arch = commands.getoutput("uname -p")
11502538Sesaxe	if localTools :
11512538Sesaxe		try:
11522538Sesaxe			src = os.environ['SRC']
11532538Sesaxe		except:
11542538Sesaxe			error("-t specified, but $SRC not set. Cannot find $SRC/tools")
11552538Sesaxe			src = ""
11562538Sesaxe		if len(src) > 0 :
11572538Sesaxe			wsdiff_path.insert(0, src + "/tools/proto/opt/onbld/bin")
11582538Sesaxe
11592538Sesaxe	lintdump_cmd = find_tool("lintdump")
11602538Sesaxe	elfdump_cmd = find_tool("elfdump")
11612538Sesaxe	dump_cmd = find_tool("dump")
11622538Sesaxe	od_cmd = find_tool("od")
11632538Sesaxe	dis_cmd = find_tool("dis")
11642538Sesaxe	diff_cmd = find_tool("diff")
11652538Sesaxe	sqlite_cmd = find_tool("sqlite")
11662538Sesaxe
11672538Sesaxe	#
11682538Sesaxe	# validate the base and patch paths
11692538Sesaxe	#
11702538Sesaxe	if baseRoot[-1] != '/' :
11712538Sesaxe		baseRoot += '/'
11722538Sesaxe
11732538Sesaxe	if ptchRoot[-1] != '/' :
11742538Sesaxe		ptchRoot += '/'
11752538Sesaxe
11762538Sesaxe	if not os.path.exists(baseRoot) :
11772538Sesaxe		error("old proto area: " + baseRoot + " does not exist")
11782538Sesaxe		sys.exit(1)
11792538Sesaxe
11802538Sesaxe	if not os.path.exists(ptchRoot) :
11812538Sesaxe		error("new proto area: " + ptchRoot + \
11822538Sesaxe		      " does not exist")
11832538Sesaxe		sys.exit(1)
11842538Sesaxe
11852538Sesaxe	#
11862538Sesaxe	# log some information identifying the run
11872538Sesaxe	#
11882538Sesaxe	v_info("Old proto area: " + baseRoot)
11892538Sesaxe	v_info("New proto area: " + ptchRoot)
11902538Sesaxe	v_info("Results file: " + results + "\n")
11912538Sesaxe
11922538Sesaxe	#
11932538Sesaxe	# Set up the temporary directories / files
11942538Sesaxe	# Could use python's tmpdir routines, but these should
11952538Sesaxe	# be easier to identify / keep around for debugging
11962538Sesaxe	pid = os.getpid()
11972538Sesaxe	tmpDir1 = "/tmp/wsdiff_tmp1_" + str(pid) + "/"
11982538Sesaxe	tmpDir2 = "/tmp/wsdiff_tmp2_" + str(pid) + "/"
11992538Sesaxe	if not os.path.exists(tmpDir1) :
12002538Sesaxe		os.makedirs(tmpDir1)
12012538Sesaxe	if not os.path.exists(tmpDir2) :
12022538Sesaxe		os.makedirs(tmpDir2)
12032538Sesaxe
12042538Sesaxe	tmpFile1 = tmpDir1 + "f1"
12052538Sesaxe	tmpFile2 = tmpDir2 + "f2"
12062538Sesaxe
12072538Sesaxe	# Derive a catalog of new, deleted, and to-be-compared objects
12082538Sesaxe	# either from the specified base and patch proto areas, or from
12092538Sesaxe	# from an input file list
12102538Sesaxe	newOrDeleted = False
12112538Sesaxe
12122538Sesaxe	if fileNamesFile != "" :
12132538Sesaxe		changedFiles, newFiles, deletedFiles = \
12142538Sesaxe			      flistCatalog(baseRoot, ptchRoot, fileNamesFile)
12152538Sesaxe	else :
12162538Sesaxe		changedFiles, newFiles, deletedFiles = protoCatalog(baseRoot, ptchRoot)
12172538Sesaxe
12182538Sesaxe	if len(newFiles) > 0 :
12192538Sesaxe		newOrDeleted = True
12202538Sesaxe		info("\nNew objects found: ")
12212538Sesaxe
12222538Sesaxe		for fn in newFiles :
12232538Sesaxe			info(fnFormat(fn))
12242538Sesaxe
12252538Sesaxe	if len(deletedFiles) > 0 :
12262538Sesaxe		newOrDeleted = True
12272538Sesaxe		info("\nObjects removed: ")
12282538Sesaxe
12292538Sesaxe		for fn in deletedFiles :
12302538Sesaxe			info(fnFormat(fn))
12312538Sesaxe
12322538Sesaxe	if newOrDeleted :
12332538Sesaxe		info("\nChanged objects: ");
12342538Sesaxe
12352538Sesaxe
12362538Sesaxe	# Here's where all the heavy lifting happens
12372538Sesaxe	# Perform a comparison on each object appearing in
12382538Sesaxe	# both proto areas. compareOneFile will examine the
12392538Sesaxe	# file types of each object, and will vector off to
12402538Sesaxe	# the appropriate comparison routine, where the compare
12412538Sesaxe	# will happen, and any differences will be reported / logged
12422538Sesaxe	for fn in changedFiles :
12432538Sesaxe		base = baseRoot + fn
12442538Sesaxe		ptch = ptchRoot + fn
12452538Sesaxe
12462538Sesaxe		compareOneFile(base, ptch, False)
12472538Sesaxe
12482538Sesaxe	# We're done, cleanup.
12492538Sesaxe	cleanup(0)
12502538Sesaxe
12512538Sesaxeif __name__ == '__main__' :
12522538Sesaxe	try:
12532538Sesaxe		main()
12542538Sesaxe	except KeyboardInterrupt :
12552538Sesaxe		cleanup(1);
12562538Sesaxe
12572538Sesaxe
1258