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#
222538Sesaxe# Copyright 2006 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',
3472538Sesaxe		       'db'	:	'Sqlite Database' }
3482538Sesaxe
3492538Sesaxe	if os.stat(f)[ST_SIZE] == 0 :
3502538Sesaxe		return 'ASCII'
3512538Sesaxe
3522538Sesaxe	if isELF(f) == 1 :
3532538Sesaxe		return 'ELF'
3542538Sesaxe
3552538Sesaxe	fnamelist = f.split('.')
3562538Sesaxe	if len(fnamelist) > 1 :	# Test the file extension
3572538Sesaxe		extension = fnamelist[-1]
3582538Sesaxe		if extension in extensions.keys():
3592538Sesaxe			return extensions[extension]
3602538Sesaxe
3612538Sesaxe	return 'ASCII'
3622538Sesaxe
3632538Sesaxe#
3642538Sesaxe# Return non-zero if "f" is an ELF file
3652538Sesaxe#
3662538Sesaxeelfmagic = '\177ELF'
3672538Sesaxedef isELF(f) :
3682538Sesaxe	try:
3692538Sesaxe		fd = open(f)
3702538Sesaxe	except:
3712538Sesaxe		error("failed to open: " + f)
3722538Sesaxe		return 0
3732538Sesaxe	magic = fd.read(len(elfmagic))
3742538Sesaxe	fd.close()
3752538Sesaxe
3762538Sesaxe	if magic == elfmagic :
3772538Sesaxe		return 1
3782538Sesaxe	return 0
3792538Sesaxe
3802538Sesaxe#
3812538Sesaxe# Return non-zero is "f" is binary.
3822538Sesaxe# Consider the file to be binary if it contains any null characters
3832538Sesaxe#
3842538Sesaxedef isBinary(f) :
3852538Sesaxe	try:
3862538Sesaxe		fd = open(f)
3872538Sesaxe	except:
3882538Sesaxe		error("failed to open: " + f)
3892538Sesaxe		return 0
3902538Sesaxe	s = fd.read()
3912538Sesaxe	fd.close()
3922538Sesaxe
3932538Sesaxe	if s.find('\0') == -1 :
3942538Sesaxe		return 0
3952538Sesaxe	else :
3962538Sesaxe		return 1
3972538Sesaxe
3982538Sesaxe#####
3992538Sesaxe# Directory traversal and file finding
4002538Sesaxe#
4012538Sesaxe
4022538Sesaxe#
4032538Sesaxe# Return a sorted list of files found under the specified directory
4042538Sesaxe#
4052538Sesaxedef findFiles(d) :
4062538Sesaxe	for path, subdirs, files in os.walk(d) :
4072538Sesaxe		files.sort()
4082538Sesaxe		for name in files :
4092538Sesaxe			yield os.path.join(path, name)
4102538Sesaxe
4112538Sesaxe#
4122538Sesaxe# Examine all files in base, ptch
4132538Sesaxe#
4142538Sesaxe# Return a list of files appearing in both proto areas,
4152538Sesaxe# a list of new files (files found only in ptch) and
4162538Sesaxe# a list of deleted files (files found only in base)
4172538Sesaxe#
4182538Sesaxedef protoCatalog(base, ptch) :
4192538Sesaxe	compFiles = []		# List of files in both proto areas
4202538Sesaxe	ptchList = []		# List of file in patch proto area
4212538Sesaxe
4222538Sesaxe	newFiles = []		# New files detected
4232538Sesaxe	deletedFiles = []	# Deleted files
4242538Sesaxe
4252538Sesaxe	baseFilesList = list(findFiles(base))
4262538Sesaxe	baseStringLength = len(base)
4272538Sesaxe
4282538Sesaxe	ptchFilesList = list(findFiles(ptch))
4292538Sesaxe	ptchStringLength = len(ptch)
4302538Sesaxe
4312538Sesaxe	# Inventory files in the base proto area
4322538Sesaxe	for fn in baseFilesList :
4332538Sesaxe		if os.path.islink(fn) :
4342538Sesaxe			continue
4352538Sesaxe
4362538Sesaxe		fileName = fn[baseStringLength:]
4372538Sesaxe		compFiles.append(fileName)
4382538Sesaxe
4392538Sesaxe	# Inventory files in the patch proto area
4402538Sesaxe	for fn in ptchFilesList :
4412538Sesaxe		if os.path.islink(fn) :
4422538Sesaxe			continue
4432538Sesaxe
4442538Sesaxe		fileName = fn[ptchStringLength:]
4452538Sesaxe		ptchList.append(fileName)
4462538Sesaxe
4472538Sesaxe	# Deleted files appear in the base area, but not the patch area
4482538Sesaxe	for fileName in compFiles :
4492538Sesaxe		if not fileName in ptchList :
4502538Sesaxe			deletedFiles.append(fileName)
4512538Sesaxe
4522538Sesaxe	# Eliminate "deleted" files from the list of objects appearing
4532538Sesaxe	# in both the base and patch proto areas
4542538Sesaxe	for fileName in deletedFiles :
4552538Sesaxe		try:
4562538Sesaxe		       	compFiles.remove(fileName)
4572538Sesaxe		except:
4582538Sesaxe			error("filelist.remove() failed")
4592538Sesaxe
4602538Sesaxe	# New files appear in the patch area, but not the base
4612538Sesaxe	for fileName in ptchList :
4622538Sesaxe		if not fileName in compFiles :
4632538Sesaxe			newFiles.append(fileName)
4642538Sesaxe
4652538Sesaxe	return compFiles, newFiles, deletedFiles
4662538Sesaxe
4672538Sesaxe#
4682538Sesaxe# Examine the files listed in the input file list
4692538Sesaxe#
4702538Sesaxe# Return a list of files appearing in both proto areas,
4712538Sesaxe# a list of new files (files found only in ptch) and
4722538Sesaxe# a list of deleted files (files found only in base)
4732538Sesaxe#
4742538Sesaxedef flistCatalog(base, ptch, flist) :
4752538Sesaxe	compFiles = []		# List of files in both proto areas
4762538Sesaxe	newFiles = []		# New files detected
4772538Sesaxe	deletedFiles = []	# Deleted files
4782538Sesaxe
4792538Sesaxe	try:
4802538Sesaxe		fd = open(flist, "r")
4812538Sesaxe	except:
4822538Sesaxe		error("could not open: " + flist)
4832538Sesaxe		cleanup(1)
4842538Sesaxe
4852538Sesaxe	files = []
4862538Sesaxe	files = fd.readlines()
4872538Sesaxe
4882538Sesaxe	for f in files :
4892538Sesaxe		ptch_present = True
4902538Sesaxe		base_present = True
4912538Sesaxe
4922538Sesaxe		if f == '\n' :
4932538Sesaxe			continue
4942538Sesaxe
4952538Sesaxe		# the fileNames have a trailing '\n'
4962538Sesaxe		f = f.rstrip()
4972538Sesaxe
4982538Sesaxe		# The objects in the file list have paths relative
4992538Sesaxe		# to $ROOT or to the base/ptch directory specified on
5002538Sesaxe		# the command line.
5012538Sesaxe		# If it's relative to $ROOT, we'll need to add back the
5022538Sesaxe		# root_`uname -p` goo we stripped off in fnFormat()
5032538Sesaxe		if os.path.exists(base + f) :
5042538Sesaxe			fn = f;
5052538Sesaxe		elif os.path.exists(base + "root_" + arch + "/" + f) :
5062538Sesaxe			fn = "root_" + arch + "/" + f
5072538Sesaxe		else :
5082538Sesaxe			base_present = False
5092538Sesaxe
5102538Sesaxe		if base_present :
5112538Sesaxe			if not os.path.exists(ptch + fn) :
5122538Sesaxe				ptch_present = False
5132538Sesaxe		else :
5142538Sesaxe			if os.path.exists(ptch + f) :
5152538Sesaxe				fn = f
5162538Sesaxe			elif os.path.exists(ptch + "root_" + arch + "/" + f) :
5172538Sesaxe				fn = "root_" + arch + "/" + f
5182538Sesaxe			else :
5192538Sesaxe				ptch_present = False
5202538Sesaxe
5212538Sesaxe		if os.path.islink(base + fn) :	# ignore links
5222538Sesaxe			base_present = False
5232538Sesaxe		if os.path.islink(ptch + fn) :
5242538Sesaxe			ptch_present = False
5252538Sesaxe
5262538Sesaxe		if base_present and ptch_present :
5272538Sesaxe			compFiles.append(fn)
5282538Sesaxe		elif base_present :
5292538Sesaxe			deletedFiles.append(fn)
5302538Sesaxe		elif ptch_present :
5312538Sesaxe			newFiles.append(fn)
5322538Sesaxe		else :
5332538Sesaxe			if os.path.islink(base + fn) and os.path.islink(ptch + fn) :
5342538Sesaxe				continue
5352538Sesaxe			error(f + " in file list, but not in either tree. Skipping...")
5362538Sesaxe
5372538Sesaxe	return compFiles, newFiles, deletedFiles
5382538Sesaxe
5392538Sesaxe
5402538Sesaxe#
5412538Sesaxe# Build a fully qualified path to an external tool/utility.
5422538Sesaxe# Consider the default system locations. For onbld tools, if
5432538Sesaxe# the -t option was specified, we'll try to use built tools in $SRC tools,
5442538Sesaxe# and otherwise, we'll fall back on /opt/onbld/
5452538Sesaxe#
5462538Sesaxedef find_tool(tool) :
5472538Sesaxe
5482538Sesaxe	# First, check what was passed
5492538Sesaxe	if os.path.exists(tool) :
5502538Sesaxe		return tool
5512538Sesaxe
5522538Sesaxe	# Next try in wsdiff path
5532538Sesaxe	for pdir in wsdiff_path :
5542538Sesaxe		location = pdir + "/" + tool
5552538Sesaxe		if os.path.exists(location) :
5562538Sesaxe			return location + " "
5572538Sesaxe
5582538Sesaxe		location = pdir + "/" + arch + "/" + tool
5592538Sesaxe		if os.path.exists(location) :
5602538Sesaxe			return location + " "
5612538Sesaxe
5622538Sesaxe	error("Could not find path to: " + tool);
5632538Sesaxe	sys.exit(1);
5642538Sesaxe
5652538Sesaxe
5662538Sesaxe#####
5672538Sesaxe# ELF file comparison helper routines
5682538Sesaxe#
5692538Sesaxe
5702538Sesaxe#
5712538Sesaxe# Return a dictionary of ELF section types keyed by section name
5722538Sesaxe#
5732538Sesaxedef get_elfheader(f) :
5742538Sesaxe
5752538Sesaxe	header = {}
5762538Sesaxe
5772538Sesaxe	hstring = commands.getoutput(elfdump_cmd + " -c " + f)
5782538Sesaxe
5792538Sesaxe	if len(hstring) == 0 :
5802538Sesaxe		error("Failed to dump ELF header for " + f)
5812538Sesaxe		return
5822538Sesaxe
5832538Sesaxe	# elfdump(1) dumps the section headers with the section name
5842538Sesaxe	# following "sh_name:", and the section type following "sh_type:"
5852538Sesaxe	sections = hstring.split("Section Header")
5862538Sesaxe	for sect in sections :
5872538Sesaxe		datap = sect.find("sh_name:");
5882538Sesaxe		if datap == -1 :
5892538Sesaxe			continue
5902538Sesaxe		section = sect[datap:].split()[1]
5912538Sesaxe		datap = sect.find("sh_type:");
5922538Sesaxe		if datap == -1 :
5932538Sesaxe			error("Could not get type for sect: " + section + \
5942538Sesaxe			      " in " + f)
5952538Sesaxe		sh_type = sect[datap:].split()[2]
5962538Sesaxe		header[section] = sh_type
5972538Sesaxe
5982538Sesaxe	return header
5992538Sesaxe
6002538Sesaxe#
6012538Sesaxe# Extract data in the specified ELF section from the given file
6022538Sesaxe#
6032538Sesaxedef extract_elf_section(f, section) :
6042538Sesaxe
6052538Sesaxe	data = commands.getoutput(dump_cmd + " -sn " + section + " " + f)
6062538Sesaxe
6072538Sesaxe	if len(data) == 0 :
6082538Sesaxe		error(cmd + " yielded no data")
6092538Sesaxe		return
6102538Sesaxe
6112538Sesaxe	# dump(1) displays the file name to start...
6122538Sesaxe	# get past it to the data itself
6132538Sesaxe	dbegin = data.find(":") + 1
6142538Sesaxe	data = data[dbegin:];
6152538Sesaxe
6162538Sesaxe	return (data)
6172538Sesaxe
6182538Sesaxe#
6192538Sesaxe# Return a (hopefully meaningful) human readable set of diffs
6202538Sesaxe# for the specified ELF section between f1 and f2
6212538Sesaxe#
6222538Sesaxe# Depending on the section, various means for dumping and diffing
6232538Sesaxe# the data may be employed.
6242538Sesaxe#
6252538Sesaxetext_sections = [ '.text', '.init', '.fini' ]
6262538Sesaxedef diff_elf_section(f1, f2, section, sh_type) :
6272538Sesaxe
6282538Sesaxe	if (sh_type == "SHT_RELA") : # sh_type == SHT_RELA
6292538Sesaxe		cmd1 = elfdump_cmd + " -r " + f1 + " > " + tmpFile1
6302538Sesaxe		cmd2 = elfdump_cmd + " -r " + f2 + " > " + tmpFile2
6312538Sesaxe	elif (section == ".group") :
6322538Sesaxe		cmd1 = elfdump_cmd + " -g " + f1 + " > " + tmpFile1
6332538Sesaxe		cmd2 = elfdump_cmd + " -g " + f2 + " > " + tmpFile2
6342538Sesaxe	elif (section == ".hash") :
6352538Sesaxe		cmd1 = elfdump_cmd + " -h " + f1 + " > " + tmpFile1
6362538Sesaxe		cmd2 = elfdump_cmd + " -h " + f2 + " > " + tmpFile2
6372538Sesaxe	elif (section == ".dynamic") :
6382538Sesaxe		cmd1 = elfdump_cmd + " -d " + f1 + " > " + tmpFile1
6392538Sesaxe		cmd2 = elfdump_cmd + " -d " + f2 + " > " + tmpFile2
6402538Sesaxe	elif (section == ".got") :
6412538Sesaxe		cmd1 = elfdump_cmd + " -G " + f1 + " > " + tmpFile1
6422538Sesaxe		cmd2 = elfdump_cmd + " -G " + f2 + " > " + tmpFile2
6432538Sesaxe	elif (section == ".SUNW_cap") :
6442538Sesaxe		cmd1 = elfdump_cmd + " -H " + f1 + " > " + tmpFile1
6452538Sesaxe		cmd2 = elfdump_cmd + " -H " + f2 + " > " + tmpFile2
6462538Sesaxe	elif (section == ".interp") :
6472538Sesaxe		cmd1 = elfdump_cmd + " -i " + f1 + " > " + tmpFile1
6482538Sesaxe		cmd2 = elfdump_cmd + " -i " + f2 + " > " + tmpFile2
6492538Sesaxe	elif (section == ".symtab" or section == ".dynsym") :
6502538Sesaxe		cmd1 = elfdump_cmd + " -s -N " + section + " " + f1 + " > " + tmpFile1
6512538Sesaxe		cmd2 = elfdump_cmd + " -s -N " + section + " " + f2 + " > " + tmpFile2
6522538Sesaxe	elif (section in text_sections) :
6532538Sesaxe		# dis sometimes complains when it hits something it doesn't
6542538Sesaxe		# know how to disassemble. Just ignore it, as the output
6552538Sesaxe		# being generated here is human readable, and we've already
6562538Sesaxe		# correctly flagged the difference.
6572538Sesaxe		cmd1 = dis_cmd + " -t " + section + " " + f1 + \
6582538Sesaxe		       " 2>/dev/null | grep -v disassembly > " + tmpFile1
6592538Sesaxe		cmd2 = dis_cmd + " -t " + section + " " + f2 + \
6602538Sesaxe		       " 2>/dev/null | grep -v disassembly > " + tmpFile2
6612538Sesaxe	else :
6622538Sesaxe		cmd1 = elfdump_cmd + " -w " + tmpFile1 + " -N " + \
6632538Sesaxe		       section + " " + f1
6642538Sesaxe		cmd2 = elfdump_cmd + " -w " + tmpFile2 + " -N " + \
6652538Sesaxe		       section + " " + f2
6662538Sesaxe
6672538Sesaxe	os.system(cmd1)
6682538Sesaxe	os.system(cmd2)
6692538Sesaxe
6702538Sesaxe	data = diffFileData(tmpFile1, tmpFile2)
6712538Sesaxe
6722538Sesaxe	return (data)
6732538Sesaxe
6742538Sesaxe#
6752538Sesaxe# compare the relevant sections of two ELF binaries
6762538Sesaxe# and report any differences
6772538Sesaxe#
6782538Sesaxe# Returns: 1 if any differenes found
6792538Sesaxe#          0 if no differences found
6802538Sesaxe#	  -1 on error
6812538Sesaxe#
6822538Sesaxe
6832538Sesaxe# Sections deliberately not considered when comparing two ELF
6842538Sesaxe# binaries. Differences observed in these sections are not considered
6852538Sesaxe# significant where patch deliverable identification is concerned.
6862538Sesaxesections_to_skip = [ ".SUNW_signature",
6872538Sesaxe		     ".comment",
6882538Sesaxe		     ".SUNW_ctf",
6892538Sesaxe		     ".debug",
6902538Sesaxe		     ".plt",
6912538Sesaxe		     ".rela.bss",
6922538Sesaxe		     ".rela.plt",
6932538Sesaxe		     ".line",
6942538Sesaxe		     ".note",
695*3002Sesaxe		     ".compcom",
6962538Sesaxe		     ]
6972538Sesaxe
6982538Sesaxesections_preferred = [ ".rodata.str1.8",
6992538Sesaxe		       ".rodata.str1.1",
7002538Sesaxe		       ".rodata",
7012538Sesaxe		       ".data1",
7022538Sesaxe		       ".data",
7032538Sesaxe		       ".text",
7042538Sesaxe		       ]
7052538Sesaxe
7062538Sesaxedef compareElfs(base, ptch, quiet) :
7072538Sesaxe
7082538Sesaxe	global logging
7092538Sesaxe
7102538Sesaxe	base_header = get_elfheader(base)
7112538Sesaxe 	sections = base_header.keys()
7122538Sesaxe
7132538Sesaxe	ptch_header = get_elfheader(ptch)
7142538Sesaxe	e2_only_sections = ptch_header.keys()
7152538Sesaxe
7162538Sesaxe	e1_only_sections = []
7172538Sesaxe
7182538Sesaxe	fileName = fnFormat(base)
7192538Sesaxe
7202538Sesaxe	# Derive the list of ELF sections found only in
7212538Sesaxe	# either e1 or e2.
7222538Sesaxe	for sect in sections :
7232538Sesaxe		if not sect in e2_only_sections :
7242538Sesaxe			e1_only_sections.append(sect)
7252538Sesaxe		else :
7262538Sesaxe			e2_only_sections.remove(sect)
7272538Sesaxe
7282538Sesaxe	if len(e1_only_sections) > 0 :
7292538Sesaxe		if quiet :
7302538Sesaxe			return 1
7312538Sesaxe		info(fileName);
7322538Sesaxe		if not logging :
7332538Sesaxe			return 1
7342538Sesaxe
7352538Sesaxe		slist = ""
7362538Sesaxe		for sect in e1_only_sections :
7372538Sesaxe			slist = slist + sect + "\t"
7382538Sesaxe		v_info("\nELF sections found in " + \
7392538Sesaxe		      base + " but not in " + ptch)
7402538Sesaxe		v_info("\n" + slist)
7412538Sesaxe		return 1
7422538Sesaxe
7432538Sesaxe	if len(e2_only_sections) > 0 :
7442538Sesaxe		if quiet :
7452538Sesaxe			return 1
7462538Sesaxe
7472538Sesaxe		info(fileName);
7482538Sesaxe		if not logging :
7492538Sesaxe			return 1
7502538Sesaxe
7512538Sesaxe		slist = ""
7522538Sesaxe		for sect in e2_only_sections :
7532538Sesaxe			slist = slist + sect + "\t"
7542538Sesaxe		v_info("\nELF sections found in " + \
7552538Sesaxe		      ptch + " but not in " + base)
7562538Sesaxe		v_info("\n" + slist)
7572538Sesaxe		return 1
7582538Sesaxe
7592538Sesaxe	# Look for preferred sections, and put those at the
7602538Sesaxe	# top of the list of sections to compare
7612538Sesaxe	for psect in sections_preferred :
7622538Sesaxe		if psect in sections :
7632538Sesaxe			sections.remove(psect)
7642538Sesaxe			sections.insert(0, psect)
7652538Sesaxe
7662538Sesaxe	# Compare ELF sections
7672538Sesaxe	first_section = True
7682538Sesaxe	for sect in sections :
7692538Sesaxe
7702538Sesaxe		if sect in sections_to_skip :
7712538Sesaxe			continue
7722538Sesaxe
7732538Sesaxe		s1 = extract_elf_section(base, sect);
7742538Sesaxe		s2 = extract_elf_section(ptch, sect);
7752538Sesaxe
7762538Sesaxe		if len(s1) != len (s2) or s1 != s2:
7772538Sesaxe			if not quiet:
7782538Sesaxe				sh_type = base_header[sect]
7792538Sesaxe				data = diff_elf_section(base, ptch, sect, \
7802538Sesaxe							sh_type)
7812538Sesaxe
7822538Sesaxe				# If all ELF sections are being reported, then
7832538Sesaxe				# invoke difference() to flag the file name to
7842538Sesaxe				# stdout only once. Any other section differences
7852538Sesaxe				# should be logged to the results file directly
7862538Sesaxe				if not first_section :
7872538Sesaxe					log_difference(fileName, "ELF " + sect, data)
7882538Sesaxe				else :
7892538Sesaxe					difference(fileName, "ELF " + sect, data)
7902538Sesaxe
7912538Sesaxe			if not reportAllSects :
7922538Sesaxe				return 1
7932538Sesaxe			first_section = False
7942538Sesaxe	return 0
7952538Sesaxe
7962538Sesaxe#####
7972538Sesaxe# Archive object comparison
7982538Sesaxe#
7992538Sesaxe# Returns 1 if difference detected
8002538Sesaxe#         0 if no difference detected
8012538Sesaxe#        -1 on error
8022538Sesaxe#
8032538Sesaxedef compareArchives(base, ptch, fileType) :
8042538Sesaxe
8052538Sesaxe	fileName = fnFormat(base)
8062538Sesaxe
8072538Sesaxe	# clear the temp directories
8082538Sesaxe	baseCmd = "rm -rf " + tmpDir1 + "*"
8092538Sesaxe	status, output = commands.getstatusoutput(baseCmd)
8102538Sesaxe	if status != 0 :
8112538Sesaxe		error(baseCmd + " failed: " + output)
8122538Sesaxe		return -1
8132538Sesaxe
8142538Sesaxe	ptchCmd = "rm -rf " + tmpDir2 + "*"
8152538Sesaxe	status, output = commands.getstatusoutput(ptchCmd)
8162538Sesaxe	if status != 0 :
8172538Sesaxe		error(ptchCmd + " failed: " + output)
8182538Sesaxe		return -1
8192538Sesaxe
8202538Sesaxe	#
8212538Sesaxe	# Be optimistic and first try a straight file compare
8222538Sesaxe	# as it will allow us to finish up quickly.
8232538Sesaxe	if compareBasic(base, ptch, True, fileType) == 0 :
8242538Sesaxe		return 0
8252538Sesaxe
8262538Sesaxe	# copy over the objects to the temp areas, and
8272538Sesaxe	# unpack them
8282538Sesaxe	baseCmd = "cp -fp " + base + " " + tmpDir1
8292538Sesaxe	status, output = commands.getstatusoutput(baseCmd)
8302538Sesaxe	if status != 0 :
8312538Sesaxe		error(baseCmd + " failed: " + output)
8322538Sesaxe		return -1
8332538Sesaxe
8342538Sesaxe	ptchCmd = "cp -fp " + ptch + " " + tmpDir2
8352538Sesaxe	status, output = commands.getstatusoutput(ptchCmd)
8362538Sesaxe	if status != 0 :
8372538Sesaxe		error(ptchCmd + " failed: " + output)
8382538Sesaxe		return -1
8392538Sesaxe
8402538Sesaxe	bname = string.split(fileName, '/')[-1]
8412538Sesaxe	if fileType == "Java Archive" :
8422538Sesaxe		baseCmd = "cd " + tmpDir1 + "; " + "jar xf " + bname + \
8432538Sesaxe			  "; rm -f " + bname + " META-INF/MANIFEST.MF"
8442538Sesaxe		ptchCmd = "cd " + tmpDir2 + "; " + "jar xf " + bname + \
8452538Sesaxe			  "; rm -f " + bname + " META-INF/MANIFEST.MF"
8462538Sesaxe	elif fileType == "ELF Object Archive" :
8472538Sesaxe		baseCmd = "cd " + tmpDir1 + "; " + "/usr/ccs/bin/ar x " + \
8482538Sesaxe			  bname + "; rm -f " + bname
8492538Sesaxe		ptchCmd = "cd " + tmpDir2 + "; " + "/usr/ccs/bin/ar x " + \
8502538Sesaxe			  bname + "; rm -f " + bname
8512538Sesaxe	else :
8522538Sesaxe		error("unexpected file type: " + fileType)
8532538Sesaxe		return -1
8542538Sesaxe
8552538Sesaxe	os.system(baseCmd)
8562538Sesaxe	os.system(ptchCmd)
8572538Sesaxe
8582538Sesaxe	baseFlist = list(findFiles(tmpDir1))
8592538Sesaxe	ptchFlist = list(findFiles(tmpDir2))
8602538Sesaxe
8612538Sesaxe	# Trim leading path off base/ptch file lists
8622538Sesaxe	flist = []
8632538Sesaxe	for fn in baseFlist :
8642538Sesaxe		flist.append(str_prefix_trunc(fn, tmpDir1))
8652538Sesaxe	baseFlist = flist
8662538Sesaxe
8672538Sesaxe	flist = []
8682538Sesaxe	for fn in ptchFlist :
8692538Sesaxe		flist.append(str_prefix_trunc(fn, tmpDir2))
8702538Sesaxe	ptchFlist = flist
8712538Sesaxe
8722538Sesaxe	for fn in ptchFlist :
8732538Sesaxe		if not fn in baseFlist :
8742538Sesaxe			difference(fileName, fileType, \
8752538Sesaxe				   fn + " added to " + fileName)
8762538Sesaxe			return 1
8772538Sesaxe
8782538Sesaxe	for fn in baseFlist :
8792538Sesaxe		if not fn in ptchFlist :
8802538Sesaxe			difference(fileName, fileType, \
8812538Sesaxe				   fn + " removed from " + fileName)
8822538Sesaxe			return 1
8832538Sesaxe
8842538Sesaxe		differs = compareOneFile((tmpDir1 + fn), (tmpDir2 + fn), True)
8852538Sesaxe		if differs :
8862538Sesaxe			difference(fileName, fileType, \
8872538Sesaxe				   fn + " in " + fileName + " differs")
8882538Sesaxe			return 1
8892538Sesaxe	return 0
8902538Sesaxe
8912538Sesaxe#####
8922538Sesaxe# (Basic) file comparison
8932538Sesaxe#
8942538Sesaxe# There's some special case code here for Javadoc HTML files
8952538Sesaxe#
8962538Sesaxe# Returns 1 if difference detected
8972538Sesaxe#         0 if no difference detected
8982538Sesaxe#        -1 on error
8992538Sesaxe#
9002538Sesaxedef compareBasic(base, ptch, quiet, fileType) :
9012538Sesaxe
9022538Sesaxe	fileName = fnFormat(base);
9032538Sesaxe
9042538Sesaxe	if quiet and os.stat(base)[ST_SIZE] != os.stat(ptch)[ST_SIZE] :
9052538Sesaxe		return 1
9062538Sesaxe
9072538Sesaxe	try:
9082538Sesaxe		baseFile = open(base)
9092538Sesaxe	except:
9102538Sesaxe		error("could not open " + base)
9112538Sesaxe		return -1
9122538Sesaxe	try:
9132538Sesaxe		ptchFile = open(ptch)
9142538Sesaxe	except:
9152538Sesaxe		error("could not open " + ptch)
9162538Sesaxe		return -1
9172538Sesaxe
9182538Sesaxe	baseData = baseFile.read()
9192538Sesaxe	ptchData = ptchFile.read()
9202538Sesaxe
9212538Sesaxe	baseFile.close()
9222538Sesaxe	ptchFile.close()
9232538Sesaxe
9242538Sesaxe	needToSnip = False
9252538Sesaxe	if fileType == "HTML" :
9262538Sesaxe		needToSnip = True
9272538Sesaxe		toSnipBeginStr = "<!-- Generated by javadoc"
9282538Sesaxe		toSnipEndStr = "-->\n"
9292538Sesaxe
9302538Sesaxe	if needToSnip :
9312538Sesaxe		toSnipBegin = string.find(baseData, toSnipBeginStr)
9322538Sesaxe		if toSnipBegin != -1 :
9332538Sesaxe			toSnipEnd = string.find(baseData[toSnipBegin:], \
9342538Sesaxe						toSnipEndStr) + \
9352538Sesaxe						len(toSnipEndStr)
9362538Sesaxe			baseData = baseData[:toSnipBegin] + \
9372538Sesaxe				   baseData[toSnipBegin + toSnipEnd:]
9382538Sesaxe			ptchData = ptchData[:toSnipBegin] + \
9392538Sesaxe				   ptchData[toSnipBegin + toSnipEnd:]
9402538Sesaxe
9412538Sesaxe	if quiet :
9422538Sesaxe		if baseData != ptchData :
9432538Sesaxe			return 1
9442538Sesaxe	else :
9452538Sesaxe		if len(baseData) != len(ptchData) or baseData != ptchData :
9462538Sesaxe			diffs = diffData(baseData, ptchData)
9472538Sesaxe			difference(fileName, fileType, diffs)
9482538Sesaxe			return 1
9492538Sesaxe	return 0
9502538Sesaxe
9512538Sesaxe
9522538Sesaxe#####
9532538Sesaxe# Compare two objects by producing a data dump from
9542538Sesaxe# each object, and then comparing the dump data
9552538Sesaxe#
9562538Sesaxe# Returns: 1 if a difference is detected
9572538Sesaxe#          0 if no difference detected
9582538Sesaxe#         -1 upon error
9592538Sesaxe#
9602538Sesaxedef compareByDumping(base, ptch, quiet, fileType) :
9612538Sesaxe
9622538Sesaxe	fileName = fnFormat(base);
9632538Sesaxe
9642538Sesaxe	if fileType == "Lint Library" :
9652538Sesaxe		baseCmd = lintdump_cmd + " -ir " + base + \
9662538Sesaxe			  " | grep -v LINTLIB:" + " > " + tmpFile1
9672538Sesaxe		ptchCmd = lintdump_cmd + " -ir " + ptch + \
9682538Sesaxe			  " | grep -v LINTLIB:" + " > " + tmpFile2
9692538Sesaxe	elif fileType == "Sqlite Database" :
9702538Sesaxe		baseCmd = "echo .dump | " + sqlite_cmd + base + " > " + \
9712538Sesaxe			  tmpFile1
9722538Sesaxe		ptchCmd = "echo .dump | " + sqlite_cmd + ptch + " > " + \
9732538Sesaxe			  tmpFile2
9742538Sesaxe
9752538Sesaxe	os.system(baseCmd)
9762538Sesaxe	os.system(ptchCmd)
9772538Sesaxe
9782538Sesaxe	try:
9792538Sesaxe		baseFile = open(tmpFile1)
9802538Sesaxe	except:
9812538Sesaxe		error("could not open: " + tmpFile1)
9822538Sesaxe	try:
9832538Sesaxe		ptchFile = open(tmpFile2)
9842538Sesaxe	except:
9852538Sesaxe		error("could not open: " + tmpFile2)
9862538Sesaxe
9872538Sesaxe	baseData = baseFile.read()
9882538Sesaxe	ptchData = ptchFile.read()
9892538Sesaxe
9902538Sesaxe	baseFile.close()
9912538Sesaxe	ptchFile.close()
9922538Sesaxe
9932538Sesaxe	if len(baseData) != len(ptchData) or baseData != ptchData :
9942538Sesaxe		if not quiet :
9952538Sesaxe			data = diffFileData(tmpFile1, tmpFile2);
9962538Sesaxe			difference(fileName, fileType, data)
9972538Sesaxe 		return 1
9982538Sesaxe	return 0
9992538Sesaxe
10002538Sesaxe#####
10012538Sesaxe# Compare two objects. Detect type changes.
10022538Sesaxe# Vector off to the appropriate type specific
10032538Sesaxe# compare routine based on the type.
10042538Sesaxe#
10052538Sesaxedef compareOneFile(base, ptch, quiet) :
10062538Sesaxe
10072538Sesaxe	# Verify the file types.
10082538Sesaxe	# If they are different, indicate this and move on
10092538Sesaxe	btype = getTheFileType(base)
10102538Sesaxe	ptype = getTheFileType(ptch)
10112538Sesaxe
10122538Sesaxe	fileName = fnFormat(base)
10132538Sesaxe
10142538Sesaxe	if (btype != ptype) :
10152538Sesaxe		difference(fileName, "file type", btype + " to " + ptype)
10162538Sesaxe		return 1
10172538Sesaxe	else :
10182538Sesaxe		fileType = btype
10192538Sesaxe
10202538Sesaxe	if (fileType == 'ELF') :
10212538Sesaxe		return compareElfs(base, ptch, quiet)
10222538Sesaxe
10232538Sesaxe	elif (fileType == 'Java Archive' or fileType == 'ELF Object Archive') :
10242538Sesaxe		return compareArchives(base, ptch, fileType)
10252538Sesaxe
10262538Sesaxe	elif (fileType == 'HTML') :
10272538Sesaxe		return compareBasic(base, ptch, quiet, fileType)
10282538Sesaxe
10292538Sesaxe	elif ( fileType == 'Lint Library' ) :
10302538Sesaxe		return compareByDumping(base, ptch, quiet, fileType)
10312538Sesaxe
10322538Sesaxe	elif ( fileType == 'Sqlite Database' ) :
10332538Sesaxe		return compareByDumping(base, ptch, quiet, fileType)
10342538Sesaxe	else :
10352538Sesaxe		# it has to be some variety of text file
10362538Sesaxe		return compareBasic(base, ptch, quiet, fileType)
10372538Sesaxe
10382538Sesaxe# Cleanup and self-terminate
10392538Sesaxedef cleanup(ret) :
10402538Sesaxe
10412538Sesaxe	if len(tmpDir1) > 0 and len(tmpDir2) > 0 :
10422538Sesaxe
10432538Sesaxe		baseCmd = "rm -rf " + tmpDir1
10442538Sesaxe		ptchCmd = "rm -rf " + tmpDir2
10452538Sesaxe
10462538Sesaxe		os.system(baseCmd)
10472538Sesaxe		os.system(ptchCmd)
10482538Sesaxe
10492538Sesaxe	if logging :
10502538Sesaxe		log.close()
10512538Sesaxe
10522538Sesaxe	sys.exit(ret)
10532538Sesaxe
10542538Sesaxedef main() :
10552538Sesaxe
10562538Sesaxe	# Log file handle
10572538Sesaxe	global log
10582538Sesaxe
10592538Sesaxe	# Globals relating to command line options
10602538Sesaxe	global logging, vdiffs, reportAllSects
10612538Sesaxe
10622538Sesaxe	# Named temporary files / directories
10632538Sesaxe	global tmpDir1, tmpDir2, tmpFile1, tmpFile2
10642538Sesaxe
10652538Sesaxe	# Command paths
10662538Sesaxe	global lintdump_cmd, elfdump_cmd, dump_cmd, dis_cmd, od_cmd, diff_cmd, sqlite_cmd
10672538Sesaxe
10682538Sesaxe	# Default search path
10692538Sesaxe	global wsdiff_path
10702538Sesaxe
10712538Sesaxe	# Essentially "uname -p"
10722538Sesaxe	global arch
10732538Sesaxe
10742538Sesaxe	# Some globals need to be initialized
10752538Sesaxe	logging = vdiffs = reportAllSects = False
10762538Sesaxe
10772538Sesaxe
10782538Sesaxe	# Process command line arguments
10792538Sesaxe	# Return values are returned from args() in alpha order
10802538Sesaxe	# (Yes, python functions can return multiple values (ewww))
10812538Sesaxe	# Note that args() also set the globals:
10822538Sesaxe	#	logging to True if verbose logging (to a file) was enabled
10832538Sesaxe	#	vdiffs to True if logged differences aren't to be truncated
10842538Sesaxe	#	reportAllSects to True if all ELF section differences are to be reported
10852538Sesaxe	#
10862538Sesaxe	baseRoot, fileNamesFile, localTools, ptchRoot, results = args()
10872538Sesaxe
10882538Sesaxe	#
10892538Sesaxe	# Set up the results/log file
10902538Sesaxe	#
10912538Sesaxe	if logging :
10922538Sesaxe		try:
10932538Sesaxe			log = open(results, "w")
10942538Sesaxe		except:
10952538Sesaxe			logging = False
10962538Sesaxe			error("failed to open log file: " + log)
10972538Sesaxe			sys.exit(1)
10982538Sesaxe
10992538Sesaxe		dateTimeStr= "# %d/%d/%d at %d:%d:%d" % time.localtime()[:6]
11002538Sesaxe		v_info("# This file was produced by wsdiff")
11012538Sesaxe		v_info(dateTimeStr)
11022538Sesaxe
11032538Sesaxe	#
11042538Sesaxe	# Build paths to the tools required tools
11052538Sesaxe	#
11062538Sesaxe	# Try to look for tools in $SRC/tools if the "-t" option
11072538Sesaxe	# was specified
11082538Sesaxe	#
11092538Sesaxe	arch = commands.getoutput("uname -p")
11102538Sesaxe	if localTools :
11112538Sesaxe		try:
11122538Sesaxe			src = os.environ['SRC']
11132538Sesaxe		except:
11142538Sesaxe			error("-t specified, but $SRC not set. Cannot find $SRC/tools")
11152538Sesaxe			src = ""
11162538Sesaxe		if len(src) > 0 :
11172538Sesaxe			wsdiff_path.insert(0, src + "/tools/proto/opt/onbld/bin")
11182538Sesaxe
11192538Sesaxe	lintdump_cmd = find_tool("lintdump")
11202538Sesaxe	elfdump_cmd = find_tool("elfdump")
11212538Sesaxe	dump_cmd = find_tool("dump")
11222538Sesaxe	od_cmd = find_tool("od")
11232538Sesaxe	dis_cmd = find_tool("dis")
11242538Sesaxe	diff_cmd = find_tool("diff")
11252538Sesaxe	sqlite_cmd = find_tool("sqlite")
11262538Sesaxe
11272538Sesaxe	#
11282538Sesaxe	# validate the base and patch paths
11292538Sesaxe	#
11302538Sesaxe	if baseRoot[-1] != '/' :
11312538Sesaxe		baseRoot += '/'
11322538Sesaxe
11332538Sesaxe	if ptchRoot[-1] != '/' :
11342538Sesaxe		ptchRoot += '/'
11352538Sesaxe
11362538Sesaxe	if not os.path.exists(baseRoot) :
11372538Sesaxe		error("old proto area: " + baseRoot + " does not exist")
11382538Sesaxe		sys.exit(1)
11392538Sesaxe
11402538Sesaxe	if not os.path.exists(ptchRoot) :
11412538Sesaxe		error("new proto area: " + ptchRoot + \
11422538Sesaxe		      " does not exist")
11432538Sesaxe		sys.exit(1)
11442538Sesaxe
11452538Sesaxe	#
11462538Sesaxe	# log some information identifying the run
11472538Sesaxe	#
11482538Sesaxe	v_info("Old proto area: " + baseRoot)
11492538Sesaxe	v_info("New proto area: " + ptchRoot)
11502538Sesaxe	v_info("Results file: " + results + "\n")
11512538Sesaxe
11522538Sesaxe	#
11532538Sesaxe	# Set up the temporary directories / files
11542538Sesaxe	# Could use python's tmpdir routines, but these should
11552538Sesaxe	# be easier to identify / keep around for debugging
11562538Sesaxe	pid = os.getpid()
11572538Sesaxe	tmpDir1 = "/tmp/wsdiff_tmp1_" + str(pid) + "/"
11582538Sesaxe	tmpDir2 = "/tmp/wsdiff_tmp2_" + str(pid) + "/"
11592538Sesaxe	if not os.path.exists(tmpDir1) :
11602538Sesaxe		os.makedirs(tmpDir1)
11612538Sesaxe	if not os.path.exists(tmpDir2) :
11622538Sesaxe		os.makedirs(tmpDir2)
11632538Sesaxe
11642538Sesaxe	tmpFile1 = tmpDir1 + "f1"
11652538Sesaxe	tmpFile2 = tmpDir2 + "f2"
11662538Sesaxe
11672538Sesaxe	# Derive a catalog of new, deleted, and to-be-compared objects
11682538Sesaxe	# either from the specified base and patch proto areas, or from
11692538Sesaxe	# from an input file list
11702538Sesaxe	newOrDeleted = False
11712538Sesaxe
11722538Sesaxe	if fileNamesFile != "" :
11732538Sesaxe		changedFiles, newFiles, deletedFiles = \
11742538Sesaxe			      flistCatalog(baseRoot, ptchRoot, fileNamesFile)
11752538Sesaxe	else :
11762538Sesaxe		changedFiles, newFiles, deletedFiles = protoCatalog(baseRoot, ptchRoot)
11772538Sesaxe
11782538Sesaxe	if len(newFiles) > 0 :
11792538Sesaxe		newOrDeleted = True
11802538Sesaxe		info("\nNew objects found: ")
11812538Sesaxe
11822538Sesaxe		for fn in newFiles :
11832538Sesaxe			info(fnFormat(fn))
11842538Sesaxe
11852538Sesaxe	if len(deletedFiles) > 0 :
11862538Sesaxe		newOrDeleted = True
11872538Sesaxe		info("\nObjects removed: ")
11882538Sesaxe
11892538Sesaxe		for fn in deletedFiles :
11902538Sesaxe			info(fnFormat(fn))
11912538Sesaxe
11922538Sesaxe	if newOrDeleted :
11932538Sesaxe		info("\nChanged objects: ");
11942538Sesaxe
11952538Sesaxe
11962538Sesaxe	# Here's where all the heavy lifting happens
11972538Sesaxe	# Perform a comparison on each object appearing in
11982538Sesaxe	# both proto areas. compareOneFile will examine the
11992538Sesaxe	# file types of each object, and will vector off to
12002538Sesaxe	# the appropriate comparison routine, where the compare
12012538Sesaxe	# will happen, and any differences will be reported / logged
12022538Sesaxe	for fn in changedFiles :
12032538Sesaxe		base = baseRoot + fn
12042538Sesaxe		ptch = ptchRoot + fn
12052538Sesaxe
12062538Sesaxe		compareOneFile(base, ptch, False)
12072538Sesaxe
12082538Sesaxe	# We're done, cleanup.
12092538Sesaxe	cleanup(0)
12102538Sesaxe
12112538Sesaxeif __name__ == '__main__' :
12122538Sesaxe	try:
12132538Sesaxe		main()
12142538Sesaxe	except KeyboardInterrupt :
12152538Sesaxe		cleanup(1);
12162538Sesaxe
12172538Sesaxe
1218