xref: /titanic_53/usr/src/tools/scripts/wsdiff.py (revision f6a1d7963180c91d5a022b2e7df19e2772fee46a)
196ccc8cbSesaxe#!/usr/sfw/bin/python
296ccc8cbSesaxe#
396ccc8cbSesaxe# CDDL HEADER START
496ccc8cbSesaxe#
596ccc8cbSesaxe# The contents of this file are subject to the terms of the
696ccc8cbSesaxe# Common Development and Distribution License (the "License").
796ccc8cbSesaxe# You may not use this file except in compliance with the License.
896ccc8cbSesaxe#
996ccc8cbSesaxe# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1096ccc8cbSesaxe# or http://www.opensolaris.org/os/licensing.
1196ccc8cbSesaxe# See the License for the specific language governing permissions
1296ccc8cbSesaxe# and limitations under the License.
1396ccc8cbSesaxe#
1496ccc8cbSesaxe# When distributing Covered Code, include this CDDL HEADER in each
1596ccc8cbSesaxe# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1696ccc8cbSesaxe# If applicable, add the following below this CDDL HEADER, with the
1796ccc8cbSesaxe# fields enclosed by brackets "[]" replaced with your own identifying
1896ccc8cbSesaxe# information: Portions Copyright [yyyy] [name of copyright owner]
1996ccc8cbSesaxe#
2096ccc8cbSesaxe# CDDL HEADER END
2196ccc8cbSesaxe#
2296ccc8cbSesaxe# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
2396ccc8cbSesaxe# Use is subject to license terms.
2496ccc8cbSesaxe#
2596ccc8cbSesaxe#ident	"%Z%%M%	%I%	%E% SMI"
2696ccc8cbSesaxe
2796ccc8cbSesaxe#
2896ccc8cbSesaxe# wsdiff(1) is a tool that can be used to determine which compiled objects
2996ccc8cbSesaxe# have changed as a result of a given source change. Developers backporting
3096ccc8cbSesaxe# new features, RFEs and bug fixes need to be able to identify the set of
3196ccc8cbSesaxe# patch deliverables necessary for feature/fix realization on a patched system.
3296ccc8cbSesaxe#
3396ccc8cbSesaxe# The tool works by comparing objects in two trees/proto areas (one build with,
3496ccc8cbSesaxe# and without the source changes.
3596ccc8cbSesaxe#
3696ccc8cbSesaxe# Using wsdiff(1) is fairly simple:
3796ccc8cbSesaxe#	- Bringover to a fresh workspace
3896ccc8cbSesaxe#	- Perform a full non-debug build (clobber if workspace isn't fresh)
3996ccc8cbSesaxe#	- Move the proto area aside, call it proto.old, or something.
4096ccc8cbSesaxe#	- Integrate your changes to the workspace
4196ccc8cbSesaxe#	- Perform another full non-debug clobber build.
4296ccc8cbSesaxe#	- Use wsdiff(1) to see what changed:
4396ccc8cbSesaxe#		$ wsdiff proto.old proto
4496ccc8cbSesaxe#
4596ccc8cbSesaxe# By default, wsdiff will print the list of changed objects / deliverables to
4696ccc8cbSesaxe# stdout. If a results file is specified via -r, the list of differing objects,
4796ccc8cbSesaxe# and details about why wsdiff(1) thinks they are different will be logged to
4896ccc8cbSesaxe# the results file.
4996ccc8cbSesaxe#
5096ccc8cbSesaxe# By invoking nightly(1) with the -w option to NIGHTLY_FLAGS, nightly(1) will use
5196ccc8cbSesaxe# wsdiff(1) to report on what objects changed since the last build.
5296ccc8cbSesaxe#
5396ccc8cbSesaxe# For patch deliverable purposes, it's advised to have nightly do a clobber,
5496ccc8cbSesaxe# non-debug build.
5596ccc8cbSesaxe#
5696ccc8cbSesaxe# Think about the results. Was something flagged that you don't expect? Go look
5796ccc8cbSesaxe# at the results file to see details about the differences.
5896ccc8cbSesaxe#
5996ccc8cbSesaxe# Use the -i option in conjunction with -v and -V to dive deeper and have wsdiff(1)
6096ccc8cbSesaxe# report with more verbosity.
6196ccc8cbSesaxe#
6296ccc8cbSesaxe# Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new
6396ccc8cbSesaxe#
6496ccc8cbSesaxe# Where "old" is the path to the proto area build without the changes, and
6596ccc8cbSesaxe# "new" is the path to the proto area built with the changes. The following
6696ccc8cbSesaxe# options are supported:
6796ccc8cbSesaxe#
6896ccc8cbSesaxe#        -v      Do not truncate observed diffs in results
6996ccc8cbSesaxe#        -V      Log *all* ELF sect diffs vs. logging the first diff found
7096ccc8cbSesaxe#        -t      Use onbld tools in $SRC/tools
7196ccc8cbSesaxe#        -r      Log results and observed differences
7296ccc8cbSesaxe#        -i      Tell wsdiff which objects to compare via an input file list
7396ccc8cbSesaxe
7496ccc8cbSesaxeimport datetime, fnmatch, getopt, profile, os, popen2, commands
7596ccc8cbSesaxeimport re, select, string, struct, sys, tempfile, time
7696ccc8cbSesaxefrom stat import *
7796ccc8cbSesaxe
7896ccc8cbSesaxe# Human readable diffs truncated by default if longer than this
7996ccc8cbSesaxe# Specifying -v on the command line will override
8096ccc8cbSesaxediffs_sz_thresh = 4096
8196ccc8cbSesaxe
8296ccc8cbSesaxe# Default search path for wsdiff
8396ccc8cbSesaxewsdiff_path = [ "/usr/bin",
8496ccc8cbSesaxe		"/usr/ccs/bin",
8596ccc8cbSesaxe		"/lib/svc/bin",
8696ccc8cbSesaxe		"/opt/onbld/bin" ]
8796ccc8cbSesaxe
8896ccc8cbSesaxe# These are objects that wsdiff will notice look different, but will not report.
8996ccc8cbSesaxe# Existence of an exceptions list, and adding things here is *dangerous*,
9096ccc8cbSesaxe# and therefore the *only* reasons why anything would be listed here is because
9196ccc8cbSesaxe# the objects do not build deterministically, yet we *cannot* fix this.
9296ccc8cbSesaxe#
9396ccc8cbSesaxe# These perl libraries use __DATE__ and therefore always look different.
9496ccc8cbSesaxe# Ideally, we would purge use the use of __DATE__ from the source, but because
9596ccc8cbSesaxe# this is source we wish to distribute with Solaris "unchanged", we cannot modify.
9696ccc8cbSesaxe#
9796ccc8cbSesaxewsdiff_exceptions = [ "usr/perl5/5.8.4/lib/sun4-solaris-64int/CORE/libperl.so.1",
9896ccc8cbSesaxe		      "usr/perl5/5.6.1/lib/sun4-solaris-64int/CORE/libperl.so.1",
9996ccc8cbSesaxe		      "usr/perl5/5.8.4/lib/i86pc-solaris-64int/CORE/libperl.so.1",
10096ccc8cbSesaxe		      "usr/perl5/5.6.1/lib/i86pc-solaris-64int/CORE/libperl.so.1"
10196ccc8cbSesaxe		      ]
10296ccc8cbSesaxe
10396ccc8cbSesaxe#####
10496ccc8cbSesaxe# Logging routines
10596ccc8cbSesaxe#
10696ccc8cbSesaxe
10796ccc8cbSesaxe# Informational message to be printed to the screen, and the log file
10896ccc8cbSesaxedef info(msg) :
10996ccc8cbSesaxe
11096ccc8cbSesaxe	print >> sys.stdout, msg
11196ccc8cbSesaxe	if logging :
11296ccc8cbSesaxe		print >> log, msg
11396ccc8cbSesaxe	sys.stdout.flush()
11496ccc8cbSesaxe
11596ccc8cbSesaxe# Error message to be printed to the screen, and the log file
11696ccc8cbSesaxedef error(msg) :
11796ccc8cbSesaxe
11896ccc8cbSesaxe	print >> sys.stderr, "ERROR:", msg
11996ccc8cbSesaxe	sys.stderr.flush()
12096ccc8cbSesaxe	if logging :
12196ccc8cbSesaxe		print >> log, "ERROR:", msg
12296ccc8cbSesaxe		log.flush()
12396ccc8cbSesaxe
12496ccc8cbSesaxe# Informational message to be printed only to the log, if there is one.
12596ccc8cbSesaxedef v_info(msg) :
12696ccc8cbSesaxe
12796ccc8cbSesaxe	if logging :
12896ccc8cbSesaxe		print >> log, msg
12996ccc8cbSesaxe		log.flush()
13096ccc8cbSesaxe
13196ccc8cbSesaxe#
13296ccc8cbSesaxe# Flag a detected file difference
13396ccc8cbSesaxe# Display the fileName to stdout, and log the difference
13496ccc8cbSesaxe#
13596ccc8cbSesaxedef difference(f, dtype, diffs) :
13696ccc8cbSesaxe
13796ccc8cbSesaxe	if f in wsdiff_exceptions :
13896ccc8cbSesaxe		return
13996ccc8cbSesaxe
14096ccc8cbSesaxe	print >> sys.stdout, f
14196ccc8cbSesaxe	sys.stdout.flush()
14296ccc8cbSesaxe
14396ccc8cbSesaxe	log_difference(f, dtype, diffs)
14496ccc8cbSesaxe
14596ccc8cbSesaxe#
14696ccc8cbSesaxe# Do the actual logging of the difference to the results file
14796ccc8cbSesaxe#
14896ccc8cbSesaxedef log_difference(f, dtype, diffs) :
14996ccc8cbSesaxe	if logging :
15096ccc8cbSesaxe		print >> log, f
15196ccc8cbSesaxe		print >> log, "NOTE:", dtype, "difference detected."
15296ccc8cbSesaxe
15396ccc8cbSesaxe		difflen = len(diffs)
15496ccc8cbSesaxe		if difflen > 0 :
15596ccc8cbSesaxe			print >> log
15696ccc8cbSesaxe
15796ccc8cbSesaxe			if not vdiffs and difflen > diffs_sz_thresh :
15896ccc8cbSesaxe				print >> log, diffs[:diffs_sz_thresh]
15996ccc8cbSesaxe				print >> log, \
16096ccc8cbSesaxe				      "... truncated due to length: " \
16196ccc8cbSesaxe				      "use -v to override ..."
16296ccc8cbSesaxe			else :
16396ccc8cbSesaxe				print >> log, diffs
16496ccc8cbSesaxe			print >> log, "\n"
16596ccc8cbSesaxe		log.flush()
16696ccc8cbSesaxe
16796ccc8cbSesaxe
16896ccc8cbSesaxe#####
16996ccc8cbSesaxe# diff generating routines
17096ccc8cbSesaxe#
17196ccc8cbSesaxe
17296ccc8cbSesaxe#
17396ccc8cbSesaxe# Return human readable diffs from two temporary files
17496ccc8cbSesaxe#
17596ccc8cbSesaxedef diffFileData(tmpf1, tmpf2) :
17696ccc8cbSesaxe
17796ccc8cbSesaxe	# Filter the data through od(1) if the data is detected
17896ccc8cbSesaxe	# as being binary
17996ccc8cbSesaxe	if isBinary(tmpf1) or isBinary(tmpf2) :
18096ccc8cbSesaxe		tmp_od1 = tmpf1 + ".od"
18196ccc8cbSesaxe		tmp_od2 = tmpf2 + ".od"
18296ccc8cbSesaxe
18396ccc8cbSesaxe		cmd = od_cmd + " -c -t x4" + " " + tmpf1 + " > " + tmp_od1
18496ccc8cbSesaxe		os.system(cmd)
18596ccc8cbSesaxe		cmd = od_cmd + " -c -t x4" + " " + tmpf2 + " > " + tmp_od2
18696ccc8cbSesaxe		os.system(cmd)
18796ccc8cbSesaxe
18896ccc8cbSesaxe		tmpf1 = tmp_od1
18996ccc8cbSesaxe		tmpf2 = tmp_od2
19096ccc8cbSesaxe
19196ccc8cbSesaxe	data = commands.getoutput(diff_cmd + " " + tmpf1 + " " + tmpf2)
19296ccc8cbSesaxe
19396ccc8cbSesaxe	return data
19496ccc8cbSesaxe
19596ccc8cbSesaxe#
19696ccc8cbSesaxe# Return human readable diffs betweeen two datasets
19796ccc8cbSesaxe#
19896ccc8cbSesaxedef diffData(d1, d2) :
19996ccc8cbSesaxe
20096ccc8cbSesaxe	global tmpFile1
20196ccc8cbSesaxe	global tmpFile2
20296ccc8cbSesaxe
20396ccc8cbSesaxe	try:
20496ccc8cbSesaxe		fd1 = open(tmpFile1, "w")
20596ccc8cbSesaxe	except:
20696ccc8cbSesaxe		error("failed to open: " + tmpFile1)
20796ccc8cbSesaxe		cleanup(1)
20896ccc8cbSesaxe	try:
20996ccc8cbSesaxe		fd2 = open(tmpFile2, "w")
21096ccc8cbSesaxe	except:
21196ccc8cbSesaxe		error("failed to open: " + tmpFile2)
21296ccc8cbSesaxe		cleanup(1)
21396ccc8cbSesaxe
21496ccc8cbSesaxe	fd1.write(d1)
21596ccc8cbSesaxe	fd2.write(d2)
21696ccc8cbSesaxe	fd1.close()
21796ccc8cbSesaxe	fd2.close()
21896ccc8cbSesaxe
21996ccc8cbSesaxe	return diffFileData(tmpFile1, tmpFile2)
22096ccc8cbSesaxe
22196ccc8cbSesaxe#####
22296ccc8cbSesaxe# Misc utility functions
22396ccc8cbSesaxe#
22496ccc8cbSesaxe
22596ccc8cbSesaxe# Prune off the leading prefix from string s
22696ccc8cbSesaxedef str_prefix_trunc(s, prefix) :
22796ccc8cbSesaxe	snipLen = len(prefix)
22896ccc8cbSesaxe	return s[snipLen:]
22996ccc8cbSesaxe
23096ccc8cbSesaxe#
23196ccc8cbSesaxe# Prune off leading proto path goo (if there is one) to yield
23296ccc8cbSesaxe# the deliverable's eventual path relative to root
23396ccc8cbSesaxe# e.g. proto.base/root_sparc/usr/src/cmd/prstat => usr/src/cmd/prstat
23496ccc8cbSesaxe#
23596ccc8cbSesaxedef fnFormat(fn) :
23696ccc8cbSesaxe	root_arch_str = "root_" + arch
23796ccc8cbSesaxe
23896ccc8cbSesaxe	pos = fn.find(root_arch_str)
23996ccc8cbSesaxe	if pos == -1 :
24096ccc8cbSesaxe		return fn
24196ccc8cbSesaxe
24296ccc8cbSesaxe	pos = fn.find("/", pos)
24396ccc8cbSesaxe	if pos == -1 :
24496ccc8cbSesaxe		return fn
24596ccc8cbSesaxe
24696ccc8cbSesaxe	return fn[pos + 1:]
24796ccc8cbSesaxe
24896ccc8cbSesaxe#####
24996ccc8cbSesaxe# Usage / argument processing
25096ccc8cbSesaxe#
25196ccc8cbSesaxe
25296ccc8cbSesaxe#
25396ccc8cbSesaxe# Display usage message
25496ccc8cbSesaxe#
25596ccc8cbSesaxedef usage() :
25696ccc8cbSesaxe	sys.stdout.flush()
25796ccc8cbSesaxe	print >> sys.stderr, """Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new
25896ccc8cbSesaxe        -v      Do not truncate observed diffs in results
25996ccc8cbSesaxe        -V      Log *all* ELF sect diffs vs. logging the first diff found
26096ccc8cbSesaxe        -t      Use onbld tools in $SRC/tools
26196ccc8cbSesaxe        -r      Log results and observed differences
26296ccc8cbSesaxe        -i      Tell wsdiff which objects to compare via an input file list"""
26396ccc8cbSesaxe	sys.exit(1)
26496ccc8cbSesaxe
26596ccc8cbSesaxe#
26696ccc8cbSesaxe# Process command line options
26796ccc8cbSesaxe#
26896ccc8cbSesaxedef args() :
26996ccc8cbSesaxe
27096ccc8cbSesaxe	global logging
27196ccc8cbSesaxe	global vdiffs
27296ccc8cbSesaxe	global reportAllSects
27396ccc8cbSesaxe
27496ccc8cbSesaxe	validOpts = 'i:r:vVt?'
27596ccc8cbSesaxe
27696ccc8cbSesaxe	baseRoot = ""
27796ccc8cbSesaxe	ptchRoot = ""
27896ccc8cbSesaxe	fileNamesFile = ""
27996ccc8cbSesaxe	results = ""
28096ccc8cbSesaxe	localTools = False
28196ccc8cbSesaxe
28296ccc8cbSesaxe	# getopt.getopt() returns:
28396ccc8cbSesaxe	#	an option/value tuple
28496ccc8cbSesaxe	#	a list of remaining non-option arguments
28596ccc8cbSesaxe	#
28696ccc8cbSesaxe	# A correct wsdiff invocation will have exactly two non option
28796ccc8cbSesaxe	# arguments, the paths to the base (old), ptch (new) proto areas
28896ccc8cbSesaxe	try:
28996ccc8cbSesaxe		optlist, args = getopt.getopt(sys.argv[1:], validOpts)
29096ccc8cbSesaxe	except getopt.error, val:
29196ccc8cbSesaxe		usage()
29296ccc8cbSesaxe
29396ccc8cbSesaxe	if len(args) != 2 :
29496ccc8cbSesaxe		usage();
29596ccc8cbSesaxe
29696ccc8cbSesaxe	for opt,val in optlist :
29796ccc8cbSesaxe		if opt == '-i' :
29896ccc8cbSesaxe			fileNamesFile = val
29996ccc8cbSesaxe		elif opt == '-r' :
30096ccc8cbSesaxe			results = val
30196ccc8cbSesaxe			logging = True
30296ccc8cbSesaxe		elif opt == '-v' :
30396ccc8cbSesaxe			vdiffs = True
30496ccc8cbSesaxe		elif opt == '-V' :
30596ccc8cbSesaxe			reportAllSects = True
30696ccc8cbSesaxe		elif opt == '-t':
30796ccc8cbSesaxe			localTools = True
30896ccc8cbSesaxe		else:
30996ccc8cbSesaxe			usage()
31096ccc8cbSesaxe
31196ccc8cbSesaxe	baseRoot = args[0]
31296ccc8cbSesaxe	ptchRoot = args[1]
31396ccc8cbSesaxe
31496ccc8cbSesaxe	if len(baseRoot) == 0 or len(ptchRoot) == 0 :
31596ccc8cbSesaxe		usage()
31696ccc8cbSesaxe
31796ccc8cbSesaxe	if logging and len(results) == 0 :
31896ccc8cbSesaxe		usage()
31996ccc8cbSesaxe
32096ccc8cbSesaxe	if vdiffs and not logging :
32196ccc8cbSesaxe		error("The -v option requires a results file (-r)")
32296ccc8cbSesaxe		sys.exit(1)
32396ccc8cbSesaxe
32496ccc8cbSesaxe	if reportAllSects and not logging :
32596ccc8cbSesaxe		error("The -V option requires a results file (-r)")
32696ccc8cbSesaxe		sys.exit(1)
32796ccc8cbSesaxe
32896ccc8cbSesaxe	# alphabetical order
32996ccc8cbSesaxe	return	baseRoot, fileNamesFile, localTools, ptchRoot, results
33096ccc8cbSesaxe
33196ccc8cbSesaxe#####
33296ccc8cbSesaxe# File identification
33396ccc8cbSesaxe#
33496ccc8cbSesaxe
33596ccc8cbSesaxe#
33696ccc8cbSesaxe# Identify the file type.
33796ccc8cbSesaxe# If it's not ELF, use the file extension to identify
33896ccc8cbSesaxe# certain file types that require special handling to
33996ccc8cbSesaxe# compare. Otherwise just return a basic "ASCII" type.
34096ccc8cbSesaxe#
34196ccc8cbSesaxedef getTheFileType(f) :
34296ccc8cbSesaxe
34396ccc8cbSesaxe	extensions = { 'a'	:	'ELF Object Archive',
34496ccc8cbSesaxe		       'jar'	:	'Java Archive',
34596ccc8cbSesaxe		       'html'	:	'HTML',
34696ccc8cbSesaxe		       'ln'	:	'Lint Library',
34796ccc8cbSesaxe		       'db'	:	'Sqlite Database' }
34896ccc8cbSesaxe
34996ccc8cbSesaxe	if os.stat(f)[ST_SIZE] == 0 :
35096ccc8cbSesaxe		return 'ASCII'
35196ccc8cbSesaxe
35296ccc8cbSesaxe	if isELF(f) == 1 :
35396ccc8cbSesaxe		return 'ELF'
35496ccc8cbSesaxe
35596ccc8cbSesaxe	fnamelist = f.split('.')
35696ccc8cbSesaxe	if len(fnamelist) > 1 :	# Test the file extension
35796ccc8cbSesaxe		extension = fnamelist[-1]
35896ccc8cbSesaxe		if extension in extensions.keys():
35996ccc8cbSesaxe			return extensions[extension]
36096ccc8cbSesaxe
36196ccc8cbSesaxe	return 'ASCII'
36296ccc8cbSesaxe
36396ccc8cbSesaxe#
36496ccc8cbSesaxe# Return non-zero if "f" is an ELF file
36596ccc8cbSesaxe#
36696ccc8cbSesaxeelfmagic = '\177ELF'
36796ccc8cbSesaxedef isELF(f) :
36896ccc8cbSesaxe	try:
36996ccc8cbSesaxe		fd = open(f)
37096ccc8cbSesaxe	except:
37196ccc8cbSesaxe		error("failed to open: " + f)
37296ccc8cbSesaxe		return 0
37396ccc8cbSesaxe	magic = fd.read(len(elfmagic))
37496ccc8cbSesaxe	fd.close()
37596ccc8cbSesaxe
37696ccc8cbSesaxe	if magic == elfmagic :
37796ccc8cbSesaxe		return 1
37896ccc8cbSesaxe	return 0
37996ccc8cbSesaxe
38096ccc8cbSesaxe#
38196ccc8cbSesaxe# Return non-zero is "f" is binary.
38296ccc8cbSesaxe# Consider the file to be binary if it contains any null characters
38396ccc8cbSesaxe#
38496ccc8cbSesaxedef isBinary(f) :
38596ccc8cbSesaxe	try:
38696ccc8cbSesaxe		fd = open(f)
38796ccc8cbSesaxe	except:
38896ccc8cbSesaxe		error("failed to open: " + f)
38996ccc8cbSesaxe		return 0
39096ccc8cbSesaxe	s = fd.read()
39196ccc8cbSesaxe	fd.close()
39296ccc8cbSesaxe
39396ccc8cbSesaxe	if s.find('\0') == -1 :
39496ccc8cbSesaxe		return 0
39596ccc8cbSesaxe	else :
39696ccc8cbSesaxe		return 1
39796ccc8cbSesaxe
39896ccc8cbSesaxe#####
39996ccc8cbSesaxe# Directory traversal and file finding
40096ccc8cbSesaxe#
40196ccc8cbSesaxe
40296ccc8cbSesaxe#
40396ccc8cbSesaxe# Return a sorted list of files found under the specified directory
40496ccc8cbSesaxe#
40596ccc8cbSesaxedef findFiles(d) :
40696ccc8cbSesaxe	for path, subdirs, files in os.walk(d) :
40796ccc8cbSesaxe		files.sort()
40896ccc8cbSesaxe		for name in files :
40996ccc8cbSesaxe			yield os.path.join(path, name)
41096ccc8cbSesaxe
41196ccc8cbSesaxe#
41296ccc8cbSesaxe# Examine all files in base, ptch
41396ccc8cbSesaxe#
41496ccc8cbSesaxe# Return a list of files appearing in both proto areas,
41596ccc8cbSesaxe# a list of new files (files found only in ptch) and
41696ccc8cbSesaxe# a list of deleted files (files found only in base)
41796ccc8cbSesaxe#
41896ccc8cbSesaxedef protoCatalog(base, ptch) :
41996ccc8cbSesaxe	compFiles = []		# List of files in both proto areas
42096ccc8cbSesaxe	ptchList = []		# List of file in patch proto area
42196ccc8cbSesaxe
42296ccc8cbSesaxe	newFiles = []		# New files detected
42396ccc8cbSesaxe	deletedFiles = []	# Deleted files
42496ccc8cbSesaxe
42596ccc8cbSesaxe	baseFilesList = list(findFiles(base))
42696ccc8cbSesaxe	baseStringLength = len(base)
42796ccc8cbSesaxe
42896ccc8cbSesaxe	ptchFilesList = list(findFiles(ptch))
42996ccc8cbSesaxe	ptchStringLength = len(ptch)
43096ccc8cbSesaxe
43196ccc8cbSesaxe	# Inventory files in the base proto area
43296ccc8cbSesaxe	for fn in baseFilesList :
43396ccc8cbSesaxe		if os.path.islink(fn) :
43496ccc8cbSesaxe			continue
43596ccc8cbSesaxe
43696ccc8cbSesaxe		fileName = fn[baseStringLength:]
43796ccc8cbSesaxe		compFiles.append(fileName)
43896ccc8cbSesaxe
43996ccc8cbSesaxe	# Inventory files in the patch proto area
44096ccc8cbSesaxe	for fn in ptchFilesList :
44196ccc8cbSesaxe		if os.path.islink(fn) :
44296ccc8cbSesaxe			continue
44396ccc8cbSesaxe
44496ccc8cbSesaxe		fileName = fn[ptchStringLength:]
44596ccc8cbSesaxe		ptchList.append(fileName)
44696ccc8cbSesaxe
44796ccc8cbSesaxe	# Deleted files appear in the base area, but not the patch area
44896ccc8cbSesaxe	for fileName in compFiles :
44996ccc8cbSesaxe		if not fileName in ptchList :
45096ccc8cbSesaxe			deletedFiles.append(fileName)
45196ccc8cbSesaxe
45296ccc8cbSesaxe	# Eliminate "deleted" files from the list of objects appearing
45396ccc8cbSesaxe	# in both the base and patch proto areas
45496ccc8cbSesaxe	for fileName in deletedFiles :
45596ccc8cbSesaxe		try:
45696ccc8cbSesaxe		       	compFiles.remove(fileName)
45796ccc8cbSesaxe		except:
45896ccc8cbSesaxe			error("filelist.remove() failed")
45996ccc8cbSesaxe
46096ccc8cbSesaxe	# New files appear in the patch area, but not the base
46196ccc8cbSesaxe	for fileName in ptchList :
46296ccc8cbSesaxe		if not fileName in compFiles :
46396ccc8cbSesaxe			newFiles.append(fileName)
46496ccc8cbSesaxe
46596ccc8cbSesaxe	return compFiles, newFiles, deletedFiles
46696ccc8cbSesaxe
46796ccc8cbSesaxe#
46896ccc8cbSesaxe# Examine the files listed in the input file list
46996ccc8cbSesaxe#
47096ccc8cbSesaxe# Return a list of files appearing in both proto areas,
47196ccc8cbSesaxe# a list of new files (files found only in ptch) and
47296ccc8cbSesaxe# a list of deleted files (files found only in base)
47396ccc8cbSesaxe#
47496ccc8cbSesaxedef flistCatalog(base, ptch, flist) :
47596ccc8cbSesaxe	compFiles = []		# List of files in both proto areas
47696ccc8cbSesaxe	newFiles = []		# New files detected
47796ccc8cbSesaxe	deletedFiles = []	# Deleted files
47896ccc8cbSesaxe
47996ccc8cbSesaxe	try:
48096ccc8cbSesaxe		fd = open(flist, "r")
48196ccc8cbSesaxe	except:
48296ccc8cbSesaxe		error("could not open: " + flist)
48396ccc8cbSesaxe		cleanup(1)
48496ccc8cbSesaxe
48596ccc8cbSesaxe	files = []
48696ccc8cbSesaxe	files = fd.readlines()
48796ccc8cbSesaxe
48896ccc8cbSesaxe	for f in files :
48996ccc8cbSesaxe		ptch_present = True
49096ccc8cbSesaxe		base_present = True
49196ccc8cbSesaxe
49296ccc8cbSesaxe		if f == '\n' :
49396ccc8cbSesaxe			continue
49496ccc8cbSesaxe
49596ccc8cbSesaxe		# the fileNames have a trailing '\n'
49696ccc8cbSesaxe		f = f.rstrip()
49796ccc8cbSesaxe
49896ccc8cbSesaxe		# The objects in the file list have paths relative
49996ccc8cbSesaxe		# to $ROOT or to the base/ptch directory specified on
50096ccc8cbSesaxe		# the command line.
50196ccc8cbSesaxe		# If it's relative to $ROOT, we'll need to add back the
50296ccc8cbSesaxe		# root_`uname -p` goo we stripped off in fnFormat()
50396ccc8cbSesaxe		if os.path.exists(base + f) :
50496ccc8cbSesaxe			fn = f;
50596ccc8cbSesaxe		elif os.path.exists(base + "root_" + arch + "/" + f) :
50696ccc8cbSesaxe			fn = "root_" + arch + "/" + f
50796ccc8cbSesaxe		else :
50896ccc8cbSesaxe			base_present = False
50996ccc8cbSesaxe
51096ccc8cbSesaxe		if base_present :
51196ccc8cbSesaxe			if not os.path.exists(ptch + fn) :
51296ccc8cbSesaxe				ptch_present = False
51396ccc8cbSesaxe		else :
51496ccc8cbSesaxe			if os.path.exists(ptch + f) :
51596ccc8cbSesaxe				fn = f
51696ccc8cbSesaxe			elif os.path.exists(ptch + "root_" + arch + "/" + f) :
51796ccc8cbSesaxe				fn = "root_" + arch + "/" + f
51896ccc8cbSesaxe			else :
51996ccc8cbSesaxe				ptch_present = False
52096ccc8cbSesaxe
52196ccc8cbSesaxe		if os.path.islink(base + fn) :	# ignore links
52296ccc8cbSesaxe			base_present = False
52396ccc8cbSesaxe		if os.path.islink(ptch + fn) :
52496ccc8cbSesaxe			ptch_present = False
52596ccc8cbSesaxe
52696ccc8cbSesaxe		if base_present and ptch_present :
52796ccc8cbSesaxe			compFiles.append(fn)
52896ccc8cbSesaxe		elif base_present :
52996ccc8cbSesaxe			deletedFiles.append(fn)
53096ccc8cbSesaxe		elif ptch_present :
53196ccc8cbSesaxe			newFiles.append(fn)
53296ccc8cbSesaxe		else :
53396ccc8cbSesaxe			if os.path.islink(base + fn) and os.path.islink(ptch + fn) :
53496ccc8cbSesaxe				continue
53596ccc8cbSesaxe			error(f + " in file list, but not in either tree. Skipping...")
53696ccc8cbSesaxe
53796ccc8cbSesaxe	return compFiles, newFiles, deletedFiles
53896ccc8cbSesaxe
53996ccc8cbSesaxe
54096ccc8cbSesaxe#
54196ccc8cbSesaxe# Build a fully qualified path to an external tool/utility.
54296ccc8cbSesaxe# Consider the default system locations. For onbld tools, if
54396ccc8cbSesaxe# the -t option was specified, we'll try to use built tools in $SRC tools,
54496ccc8cbSesaxe# and otherwise, we'll fall back on /opt/onbld/
54596ccc8cbSesaxe#
54696ccc8cbSesaxedef find_tool(tool) :
54796ccc8cbSesaxe
54896ccc8cbSesaxe	# First, check what was passed
54996ccc8cbSesaxe	if os.path.exists(tool) :
55096ccc8cbSesaxe		return tool
55196ccc8cbSesaxe
55296ccc8cbSesaxe	# Next try in wsdiff path
55396ccc8cbSesaxe	for pdir in wsdiff_path :
55496ccc8cbSesaxe		location = pdir + "/" + tool
55596ccc8cbSesaxe		if os.path.exists(location) :
55696ccc8cbSesaxe			return location + " "
55796ccc8cbSesaxe
55896ccc8cbSesaxe		location = pdir + "/" + arch + "/" + tool
55996ccc8cbSesaxe		if os.path.exists(location) :
56096ccc8cbSesaxe			return location + " "
56196ccc8cbSesaxe
56296ccc8cbSesaxe	error("Could not find path to: " + tool);
56396ccc8cbSesaxe	sys.exit(1);
56496ccc8cbSesaxe
56596ccc8cbSesaxe
56696ccc8cbSesaxe#####
56796ccc8cbSesaxe# ELF file comparison helper routines
56896ccc8cbSesaxe#
56996ccc8cbSesaxe
57096ccc8cbSesaxe#
57196ccc8cbSesaxe# Return a dictionary of ELF section types keyed by section name
57296ccc8cbSesaxe#
57396ccc8cbSesaxedef get_elfheader(f) :
57496ccc8cbSesaxe
57596ccc8cbSesaxe	header = {}
57696ccc8cbSesaxe
57796ccc8cbSesaxe	hstring = commands.getoutput(elfdump_cmd + " -c " + f)
57896ccc8cbSesaxe
57996ccc8cbSesaxe	if len(hstring) == 0 :
58096ccc8cbSesaxe		error("Failed to dump ELF header for " + f)
58196ccc8cbSesaxe		return
58296ccc8cbSesaxe
58396ccc8cbSesaxe	# elfdump(1) dumps the section headers with the section name
58496ccc8cbSesaxe	# following "sh_name:", and the section type following "sh_type:"
58596ccc8cbSesaxe	sections = hstring.split("Section Header")
58696ccc8cbSesaxe	for sect in sections :
58796ccc8cbSesaxe		datap = sect.find("sh_name:");
58896ccc8cbSesaxe		if datap == -1 :
58996ccc8cbSesaxe			continue
59096ccc8cbSesaxe		section = sect[datap:].split()[1]
59196ccc8cbSesaxe		datap = sect.find("sh_type:");
59296ccc8cbSesaxe		if datap == -1 :
59396ccc8cbSesaxe			error("Could not get type for sect: " + section + \
59496ccc8cbSesaxe			      " in " + f)
59596ccc8cbSesaxe		sh_type = sect[datap:].split()[2]
59696ccc8cbSesaxe		header[section] = sh_type
59796ccc8cbSesaxe
59896ccc8cbSesaxe	return header
59996ccc8cbSesaxe
60096ccc8cbSesaxe#
60196ccc8cbSesaxe# Extract data in the specified ELF section from the given file
60296ccc8cbSesaxe#
60396ccc8cbSesaxedef extract_elf_section(f, section) :
60496ccc8cbSesaxe
60596ccc8cbSesaxe	data = commands.getoutput(dump_cmd + " -sn " + section + " " + f)
60696ccc8cbSesaxe
60796ccc8cbSesaxe	if len(data) == 0 :
60896ccc8cbSesaxe		error(cmd + " yielded no data")
60996ccc8cbSesaxe		return
61096ccc8cbSesaxe
61196ccc8cbSesaxe	# dump(1) displays the file name to start...
61296ccc8cbSesaxe	# get past it to the data itself
61396ccc8cbSesaxe	dbegin = data.find(":") + 1
61496ccc8cbSesaxe	data = data[dbegin:];
61596ccc8cbSesaxe
61696ccc8cbSesaxe	return (data)
61796ccc8cbSesaxe
61896ccc8cbSesaxe#
61996ccc8cbSesaxe# Return a (hopefully meaningful) human readable set of diffs
62096ccc8cbSesaxe# for the specified ELF section between f1 and f2
62196ccc8cbSesaxe#
62296ccc8cbSesaxe# Depending on the section, various means for dumping and diffing
62396ccc8cbSesaxe# the data may be employed.
62496ccc8cbSesaxe#
62596ccc8cbSesaxetext_sections = [ '.text', '.init', '.fini' ]
62696ccc8cbSesaxedef diff_elf_section(f1, f2, section, sh_type) :
62796ccc8cbSesaxe
62896ccc8cbSesaxe	if (sh_type == "SHT_RELA") : # sh_type == SHT_RELA
62996ccc8cbSesaxe		cmd1 = elfdump_cmd + " -r " + f1 + " > " + tmpFile1
63096ccc8cbSesaxe		cmd2 = elfdump_cmd + " -r " + f2 + " > " + tmpFile2
63196ccc8cbSesaxe	elif (section == ".group") :
63296ccc8cbSesaxe		cmd1 = elfdump_cmd + " -g " + f1 + " > " + tmpFile1
63396ccc8cbSesaxe		cmd2 = elfdump_cmd + " -g " + f2 + " > " + tmpFile2
63496ccc8cbSesaxe	elif (section == ".hash") :
63596ccc8cbSesaxe		cmd1 = elfdump_cmd + " -h " + f1 + " > " + tmpFile1
63696ccc8cbSesaxe		cmd2 = elfdump_cmd + " -h " + f2 + " > " + tmpFile2
63796ccc8cbSesaxe	elif (section == ".dynamic") :
63896ccc8cbSesaxe		cmd1 = elfdump_cmd + " -d " + f1 + " > " + tmpFile1
63996ccc8cbSesaxe		cmd2 = elfdump_cmd + " -d " + f2 + " > " + tmpFile2
64096ccc8cbSesaxe	elif (section == ".got") :
64196ccc8cbSesaxe		cmd1 = elfdump_cmd + " -G " + f1 + " > " + tmpFile1
64296ccc8cbSesaxe		cmd2 = elfdump_cmd + " -G " + f2 + " > " + tmpFile2
64396ccc8cbSesaxe	elif (section == ".SUNW_cap") :
64496ccc8cbSesaxe		cmd1 = elfdump_cmd + " -H " + f1 + " > " + tmpFile1
64596ccc8cbSesaxe		cmd2 = elfdump_cmd + " -H " + f2 + " > " + tmpFile2
64696ccc8cbSesaxe	elif (section == ".interp") :
64796ccc8cbSesaxe		cmd1 = elfdump_cmd + " -i " + f1 + " > " + tmpFile1
64896ccc8cbSesaxe		cmd2 = elfdump_cmd + " -i " + f2 + " > " + tmpFile2
64996ccc8cbSesaxe	elif (section == ".symtab" or section == ".dynsym") :
65096ccc8cbSesaxe		cmd1 = elfdump_cmd + " -s -N " + section + " " + f1 + " > " + tmpFile1
65196ccc8cbSesaxe		cmd2 = elfdump_cmd + " -s -N " + section + " " + f2 + " > " + tmpFile2
65296ccc8cbSesaxe	elif (section in text_sections) :
65396ccc8cbSesaxe		# dis sometimes complains when it hits something it doesn't
65496ccc8cbSesaxe		# know how to disassemble. Just ignore it, as the output
65596ccc8cbSesaxe		# being generated here is human readable, and we've already
65696ccc8cbSesaxe		# correctly flagged the difference.
65796ccc8cbSesaxe		cmd1 = dis_cmd + " -t " + section + " " + f1 + \
65896ccc8cbSesaxe		       " 2>/dev/null | grep -v disassembly > " + tmpFile1
65996ccc8cbSesaxe		cmd2 = dis_cmd + " -t " + section + " " + f2 + \
66096ccc8cbSesaxe		       " 2>/dev/null | grep -v disassembly > " + tmpFile2
66196ccc8cbSesaxe	else :
66296ccc8cbSesaxe		cmd1 = elfdump_cmd + " -w " + tmpFile1 + " -N " + \
66396ccc8cbSesaxe		       section + " " + f1
66496ccc8cbSesaxe		cmd2 = elfdump_cmd + " -w " + tmpFile2 + " -N " + \
66596ccc8cbSesaxe		       section + " " + f2
66696ccc8cbSesaxe
66796ccc8cbSesaxe	os.system(cmd1)
66896ccc8cbSesaxe	os.system(cmd2)
66996ccc8cbSesaxe
67096ccc8cbSesaxe	data = diffFileData(tmpFile1, tmpFile2)
67196ccc8cbSesaxe
67296ccc8cbSesaxe	return (data)
67396ccc8cbSesaxe
67496ccc8cbSesaxe#
67596ccc8cbSesaxe# compare the relevant sections of two ELF binaries
67696ccc8cbSesaxe# and report any differences
67796ccc8cbSesaxe#
67896ccc8cbSesaxe# Returns: 1 if any differenes found
67996ccc8cbSesaxe#          0 if no differences found
68096ccc8cbSesaxe#	  -1 on error
68196ccc8cbSesaxe#
68296ccc8cbSesaxe
68396ccc8cbSesaxe# Sections deliberately not considered when comparing two ELF
68496ccc8cbSesaxe# binaries. Differences observed in these sections are not considered
68596ccc8cbSesaxe# significant where patch deliverable identification is concerned.
68696ccc8cbSesaxesections_to_skip = [ ".SUNW_signature",
68796ccc8cbSesaxe		     ".comment",
68896ccc8cbSesaxe		     ".SUNW_ctf",
68996ccc8cbSesaxe		     ".debug",
69096ccc8cbSesaxe		     ".plt",
69196ccc8cbSesaxe		     ".rela.bss",
69296ccc8cbSesaxe		     ".rela.plt",
69396ccc8cbSesaxe		     ".line",
69496ccc8cbSesaxe		     ".note",
695*f6a1d796Sesaxe		     ".compcom",
69696ccc8cbSesaxe		     ]
69796ccc8cbSesaxe
69896ccc8cbSesaxesections_preferred = [ ".rodata.str1.8",
69996ccc8cbSesaxe		       ".rodata.str1.1",
70096ccc8cbSesaxe		       ".rodata",
70196ccc8cbSesaxe		       ".data1",
70296ccc8cbSesaxe		       ".data",
70396ccc8cbSesaxe		       ".text",
70496ccc8cbSesaxe		       ]
70596ccc8cbSesaxe
70696ccc8cbSesaxedef compareElfs(base, ptch, quiet) :
70796ccc8cbSesaxe
70896ccc8cbSesaxe	global logging
70996ccc8cbSesaxe
71096ccc8cbSesaxe	base_header = get_elfheader(base)
71196ccc8cbSesaxe 	sections = base_header.keys()
71296ccc8cbSesaxe
71396ccc8cbSesaxe	ptch_header = get_elfheader(ptch)
71496ccc8cbSesaxe	e2_only_sections = ptch_header.keys()
71596ccc8cbSesaxe
71696ccc8cbSesaxe	e1_only_sections = []
71796ccc8cbSesaxe
71896ccc8cbSesaxe	fileName = fnFormat(base)
71996ccc8cbSesaxe
72096ccc8cbSesaxe	# Derive the list of ELF sections found only in
72196ccc8cbSesaxe	# either e1 or e2.
72296ccc8cbSesaxe	for sect in sections :
72396ccc8cbSesaxe		if not sect in e2_only_sections :
72496ccc8cbSesaxe			e1_only_sections.append(sect)
72596ccc8cbSesaxe		else :
72696ccc8cbSesaxe			e2_only_sections.remove(sect)
72796ccc8cbSesaxe
72896ccc8cbSesaxe	if len(e1_only_sections) > 0 :
72996ccc8cbSesaxe		if quiet :
73096ccc8cbSesaxe			return 1
73196ccc8cbSesaxe		info(fileName);
73296ccc8cbSesaxe		if not logging :
73396ccc8cbSesaxe			return 1
73496ccc8cbSesaxe
73596ccc8cbSesaxe		slist = ""
73696ccc8cbSesaxe		for sect in e1_only_sections :
73796ccc8cbSesaxe			slist = slist + sect + "\t"
73896ccc8cbSesaxe		v_info("\nELF sections found in " + \
73996ccc8cbSesaxe		      base + " but not in " + ptch)
74096ccc8cbSesaxe		v_info("\n" + slist)
74196ccc8cbSesaxe		return 1
74296ccc8cbSesaxe
74396ccc8cbSesaxe	if len(e2_only_sections) > 0 :
74496ccc8cbSesaxe		if quiet :
74596ccc8cbSesaxe			return 1
74696ccc8cbSesaxe
74796ccc8cbSesaxe		info(fileName);
74896ccc8cbSesaxe		if not logging :
74996ccc8cbSesaxe			return 1
75096ccc8cbSesaxe
75196ccc8cbSesaxe		slist = ""
75296ccc8cbSesaxe		for sect in e2_only_sections :
75396ccc8cbSesaxe			slist = slist + sect + "\t"
75496ccc8cbSesaxe		v_info("\nELF sections found in " + \
75596ccc8cbSesaxe		      ptch + " but not in " + base)
75696ccc8cbSesaxe		v_info("\n" + slist)
75796ccc8cbSesaxe		return 1
75896ccc8cbSesaxe
75996ccc8cbSesaxe	# Look for preferred sections, and put those at the
76096ccc8cbSesaxe	# top of the list of sections to compare
76196ccc8cbSesaxe	for psect in sections_preferred :
76296ccc8cbSesaxe		if psect in sections :
76396ccc8cbSesaxe			sections.remove(psect)
76496ccc8cbSesaxe			sections.insert(0, psect)
76596ccc8cbSesaxe
76696ccc8cbSesaxe	# Compare ELF sections
76796ccc8cbSesaxe	first_section = True
76896ccc8cbSesaxe	for sect in sections :
76996ccc8cbSesaxe
77096ccc8cbSesaxe		if sect in sections_to_skip :
77196ccc8cbSesaxe			continue
77296ccc8cbSesaxe
77396ccc8cbSesaxe		s1 = extract_elf_section(base, sect);
77496ccc8cbSesaxe		s2 = extract_elf_section(ptch, sect);
77596ccc8cbSesaxe
77696ccc8cbSesaxe		if len(s1) != len (s2) or s1 != s2:
77796ccc8cbSesaxe			if not quiet:
77896ccc8cbSesaxe				sh_type = base_header[sect]
77996ccc8cbSesaxe				data = diff_elf_section(base, ptch, sect, \
78096ccc8cbSesaxe							sh_type)
78196ccc8cbSesaxe
78296ccc8cbSesaxe				# If all ELF sections are being reported, then
78396ccc8cbSesaxe				# invoke difference() to flag the file name to
78496ccc8cbSesaxe				# stdout only once. Any other section differences
78596ccc8cbSesaxe				# should be logged to the results file directly
78696ccc8cbSesaxe				if not first_section :
78796ccc8cbSesaxe					log_difference(fileName, "ELF " + sect, data)
78896ccc8cbSesaxe				else :
78996ccc8cbSesaxe					difference(fileName, "ELF " + sect, data)
79096ccc8cbSesaxe
79196ccc8cbSesaxe			if not reportAllSects :
79296ccc8cbSesaxe				return 1
79396ccc8cbSesaxe			first_section = False
79496ccc8cbSesaxe	return 0
79596ccc8cbSesaxe
79696ccc8cbSesaxe#####
79796ccc8cbSesaxe# Archive object comparison
79896ccc8cbSesaxe#
79996ccc8cbSesaxe# Returns 1 if difference detected
80096ccc8cbSesaxe#         0 if no difference detected
80196ccc8cbSesaxe#        -1 on error
80296ccc8cbSesaxe#
80396ccc8cbSesaxedef compareArchives(base, ptch, fileType) :
80496ccc8cbSesaxe
80596ccc8cbSesaxe	fileName = fnFormat(base)
80696ccc8cbSesaxe
80796ccc8cbSesaxe	# clear the temp directories
80896ccc8cbSesaxe	baseCmd = "rm -rf " + tmpDir1 + "*"
80996ccc8cbSesaxe	status, output = commands.getstatusoutput(baseCmd)
81096ccc8cbSesaxe	if status != 0 :
81196ccc8cbSesaxe		error(baseCmd + " failed: " + output)
81296ccc8cbSesaxe		return -1
81396ccc8cbSesaxe
81496ccc8cbSesaxe	ptchCmd = "rm -rf " + tmpDir2 + "*"
81596ccc8cbSesaxe	status, output = commands.getstatusoutput(ptchCmd)
81696ccc8cbSesaxe	if status != 0 :
81796ccc8cbSesaxe		error(ptchCmd + " failed: " + output)
81896ccc8cbSesaxe		return -1
81996ccc8cbSesaxe
82096ccc8cbSesaxe	#
82196ccc8cbSesaxe	# Be optimistic and first try a straight file compare
82296ccc8cbSesaxe	# as it will allow us to finish up quickly.
82396ccc8cbSesaxe	if compareBasic(base, ptch, True, fileType) == 0 :
82496ccc8cbSesaxe		return 0
82596ccc8cbSesaxe
82696ccc8cbSesaxe	# copy over the objects to the temp areas, and
82796ccc8cbSesaxe	# unpack them
82896ccc8cbSesaxe	baseCmd = "cp -fp " + base + " " + tmpDir1
82996ccc8cbSesaxe	status, output = commands.getstatusoutput(baseCmd)
83096ccc8cbSesaxe	if status != 0 :
83196ccc8cbSesaxe		error(baseCmd + " failed: " + output)
83296ccc8cbSesaxe		return -1
83396ccc8cbSesaxe
83496ccc8cbSesaxe	ptchCmd = "cp -fp " + ptch + " " + tmpDir2
83596ccc8cbSesaxe	status, output = commands.getstatusoutput(ptchCmd)
83696ccc8cbSesaxe	if status != 0 :
83796ccc8cbSesaxe		error(ptchCmd + " failed: " + output)
83896ccc8cbSesaxe		return -1
83996ccc8cbSesaxe
84096ccc8cbSesaxe	bname = string.split(fileName, '/')[-1]
84196ccc8cbSesaxe	if fileType == "Java Archive" :
84296ccc8cbSesaxe		baseCmd = "cd " + tmpDir1 + "; " + "jar xf " + bname + \
84396ccc8cbSesaxe			  "; rm -f " + bname + " META-INF/MANIFEST.MF"
84496ccc8cbSesaxe		ptchCmd = "cd " + tmpDir2 + "; " + "jar xf " + bname + \
84596ccc8cbSesaxe			  "; rm -f " + bname + " META-INF/MANIFEST.MF"
84696ccc8cbSesaxe	elif fileType == "ELF Object Archive" :
84796ccc8cbSesaxe		baseCmd = "cd " + tmpDir1 + "; " + "/usr/ccs/bin/ar x " + \
84896ccc8cbSesaxe			  bname + "; rm -f " + bname
84996ccc8cbSesaxe		ptchCmd = "cd " + tmpDir2 + "; " + "/usr/ccs/bin/ar x " + \
85096ccc8cbSesaxe			  bname + "; rm -f " + bname
85196ccc8cbSesaxe	else :
85296ccc8cbSesaxe		error("unexpected file type: " + fileType)
85396ccc8cbSesaxe		return -1
85496ccc8cbSesaxe
85596ccc8cbSesaxe	os.system(baseCmd)
85696ccc8cbSesaxe	os.system(ptchCmd)
85796ccc8cbSesaxe
85896ccc8cbSesaxe	baseFlist = list(findFiles(tmpDir1))
85996ccc8cbSesaxe	ptchFlist = list(findFiles(tmpDir2))
86096ccc8cbSesaxe
86196ccc8cbSesaxe	# Trim leading path off base/ptch file lists
86296ccc8cbSesaxe	flist = []
86396ccc8cbSesaxe	for fn in baseFlist :
86496ccc8cbSesaxe		flist.append(str_prefix_trunc(fn, tmpDir1))
86596ccc8cbSesaxe	baseFlist = flist
86696ccc8cbSesaxe
86796ccc8cbSesaxe	flist = []
86896ccc8cbSesaxe	for fn in ptchFlist :
86996ccc8cbSesaxe		flist.append(str_prefix_trunc(fn, tmpDir2))
87096ccc8cbSesaxe	ptchFlist = flist
87196ccc8cbSesaxe
87296ccc8cbSesaxe	for fn in ptchFlist :
87396ccc8cbSesaxe		if not fn in baseFlist :
87496ccc8cbSesaxe			difference(fileName, fileType, \
87596ccc8cbSesaxe				   fn + " added to " + fileName)
87696ccc8cbSesaxe			return 1
87796ccc8cbSesaxe
87896ccc8cbSesaxe	for fn in baseFlist :
87996ccc8cbSesaxe		if not fn in ptchFlist :
88096ccc8cbSesaxe			difference(fileName, fileType, \
88196ccc8cbSesaxe				   fn + " removed from " + fileName)
88296ccc8cbSesaxe			return 1
88396ccc8cbSesaxe
88496ccc8cbSesaxe		differs = compareOneFile((tmpDir1 + fn), (tmpDir2 + fn), True)
88596ccc8cbSesaxe		if differs :
88696ccc8cbSesaxe			difference(fileName, fileType, \
88796ccc8cbSesaxe				   fn + " in " + fileName + " differs")
88896ccc8cbSesaxe			return 1
88996ccc8cbSesaxe	return 0
89096ccc8cbSesaxe
89196ccc8cbSesaxe#####
89296ccc8cbSesaxe# (Basic) file comparison
89396ccc8cbSesaxe#
89496ccc8cbSesaxe# There's some special case code here for Javadoc HTML files
89596ccc8cbSesaxe#
89696ccc8cbSesaxe# Returns 1 if difference detected
89796ccc8cbSesaxe#         0 if no difference detected
89896ccc8cbSesaxe#        -1 on error
89996ccc8cbSesaxe#
90096ccc8cbSesaxedef compareBasic(base, ptch, quiet, fileType) :
90196ccc8cbSesaxe
90296ccc8cbSesaxe	fileName = fnFormat(base);
90396ccc8cbSesaxe
90496ccc8cbSesaxe	if quiet and os.stat(base)[ST_SIZE] != os.stat(ptch)[ST_SIZE] :
90596ccc8cbSesaxe		return 1
90696ccc8cbSesaxe
90796ccc8cbSesaxe	try:
90896ccc8cbSesaxe		baseFile = open(base)
90996ccc8cbSesaxe	except:
91096ccc8cbSesaxe		error("could not open " + base)
91196ccc8cbSesaxe		return -1
91296ccc8cbSesaxe	try:
91396ccc8cbSesaxe		ptchFile = open(ptch)
91496ccc8cbSesaxe	except:
91596ccc8cbSesaxe		error("could not open " + ptch)
91696ccc8cbSesaxe		return -1
91796ccc8cbSesaxe
91896ccc8cbSesaxe	baseData = baseFile.read()
91996ccc8cbSesaxe	ptchData = ptchFile.read()
92096ccc8cbSesaxe
92196ccc8cbSesaxe	baseFile.close()
92296ccc8cbSesaxe	ptchFile.close()
92396ccc8cbSesaxe
92496ccc8cbSesaxe	needToSnip = False
92596ccc8cbSesaxe	if fileType == "HTML" :
92696ccc8cbSesaxe		needToSnip = True
92796ccc8cbSesaxe		toSnipBeginStr = "<!-- Generated by javadoc"
92896ccc8cbSesaxe		toSnipEndStr = "-->\n"
92996ccc8cbSesaxe
93096ccc8cbSesaxe	if needToSnip :
93196ccc8cbSesaxe		toSnipBegin = string.find(baseData, toSnipBeginStr)
93296ccc8cbSesaxe		if toSnipBegin != -1 :
93396ccc8cbSesaxe			toSnipEnd = string.find(baseData[toSnipBegin:], \
93496ccc8cbSesaxe						toSnipEndStr) + \
93596ccc8cbSesaxe						len(toSnipEndStr)
93696ccc8cbSesaxe			baseData = baseData[:toSnipBegin] + \
93796ccc8cbSesaxe				   baseData[toSnipBegin + toSnipEnd:]
93896ccc8cbSesaxe			ptchData = ptchData[:toSnipBegin] + \
93996ccc8cbSesaxe				   ptchData[toSnipBegin + toSnipEnd:]
94096ccc8cbSesaxe
94196ccc8cbSesaxe	if quiet :
94296ccc8cbSesaxe		if baseData != ptchData :
94396ccc8cbSesaxe			return 1
94496ccc8cbSesaxe	else :
94596ccc8cbSesaxe		if len(baseData) != len(ptchData) or baseData != ptchData :
94696ccc8cbSesaxe			diffs = diffData(baseData, ptchData)
94796ccc8cbSesaxe			difference(fileName, fileType, diffs)
94896ccc8cbSesaxe			return 1
94996ccc8cbSesaxe	return 0
95096ccc8cbSesaxe
95196ccc8cbSesaxe
95296ccc8cbSesaxe#####
95396ccc8cbSesaxe# Compare two objects by producing a data dump from
95496ccc8cbSesaxe# each object, and then comparing the dump data
95596ccc8cbSesaxe#
95696ccc8cbSesaxe# Returns: 1 if a difference is detected
95796ccc8cbSesaxe#          0 if no difference detected
95896ccc8cbSesaxe#         -1 upon error
95996ccc8cbSesaxe#
96096ccc8cbSesaxedef compareByDumping(base, ptch, quiet, fileType) :
96196ccc8cbSesaxe
96296ccc8cbSesaxe	fileName = fnFormat(base);
96396ccc8cbSesaxe
96496ccc8cbSesaxe	if fileType == "Lint Library" :
96596ccc8cbSesaxe		baseCmd = lintdump_cmd + " -ir " + base + \
96696ccc8cbSesaxe			  " | grep -v LINTLIB:" + " > " + tmpFile1
96796ccc8cbSesaxe		ptchCmd = lintdump_cmd + " -ir " + ptch + \
96896ccc8cbSesaxe			  " | grep -v LINTLIB:" + " > " + tmpFile2
96996ccc8cbSesaxe	elif fileType == "Sqlite Database" :
97096ccc8cbSesaxe		baseCmd = "echo .dump | " + sqlite_cmd + base + " > " + \
97196ccc8cbSesaxe			  tmpFile1
97296ccc8cbSesaxe		ptchCmd = "echo .dump | " + sqlite_cmd + ptch + " > " + \
97396ccc8cbSesaxe			  tmpFile2
97496ccc8cbSesaxe
97596ccc8cbSesaxe	os.system(baseCmd)
97696ccc8cbSesaxe	os.system(ptchCmd)
97796ccc8cbSesaxe
97896ccc8cbSesaxe	try:
97996ccc8cbSesaxe		baseFile = open(tmpFile1)
98096ccc8cbSesaxe	except:
98196ccc8cbSesaxe		error("could not open: " + tmpFile1)
98296ccc8cbSesaxe	try:
98396ccc8cbSesaxe		ptchFile = open(tmpFile2)
98496ccc8cbSesaxe	except:
98596ccc8cbSesaxe		error("could not open: " + tmpFile2)
98696ccc8cbSesaxe
98796ccc8cbSesaxe	baseData = baseFile.read()
98896ccc8cbSesaxe	ptchData = ptchFile.read()
98996ccc8cbSesaxe
99096ccc8cbSesaxe	baseFile.close()
99196ccc8cbSesaxe	ptchFile.close()
99296ccc8cbSesaxe
99396ccc8cbSesaxe	if len(baseData) != len(ptchData) or baseData != ptchData :
99496ccc8cbSesaxe		if not quiet :
99596ccc8cbSesaxe			data = diffFileData(tmpFile1, tmpFile2);
99696ccc8cbSesaxe			difference(fileName, fileType, data)
99796ccc8cbSesaxe 		return 1
99896ccc8cbSesaxe	return 0
99996ccc8cbSesaxe
100096ccc8cbSesaxe#####
100196ccc8cbSesaxe# Compare two objects. Detect type changes.
100296ccc8cbSesaxe# Vector off to the appropriate type specific
100396ccc8cbSesaxe# compare routine based on the type.
100496ccc8cbSesaxe#
100596ccc8cbSesaxedef compareOneFile(base, ptch, quiet) :
100696ccc8cbSesaxe
100796ccc8cbSesaxe	# Verify the file types.
100896ccc8cbSesaxe	# If they are different, indicate this and move on
100996ccc8cbSesaxe	btype = getTheFileType(base)
101096ccc8cbSesaxe	ptype = getTheFileType(ptch)
101196ccc8cbSesaxe
101296ccc8cbSesaxe	fileName = fnFormat(base)
101396ccc8cbSesaxe
101496ccc8cbSesaxe	if (btype != ptype) :
101596ccc8cbSesaxe		difference(fileName, "file type", btype + " to " + ptype)
101696ccc8cbSesaxe		return 1
101796ccc8cbSesaxe	else :
101896ccc8cbSesaxe		fileType = btype
101996ccc8cbSesaxe
102096ccc8cbSesaxe	if (fileType == 'ELF') :
102196ccc8cbSesaxe		return compareElfs(base, ptch, quiet)
102296ccc8cbSesaxe
102396ccc8cbSesaxe	elif (fileType == 'Java Archive' or fileType == 'ELF Object Archive') :
102496ccc8cbSesaxe		return compareArchives(base, ptch, fileType)
102596ccc8cbSesaxe
102696ccc8cbSesaxe	elif (fileType == 'HTML') :
102796ccc8cbSesaxe		return compareBasic(base, ptch, quiet, fileType)
102896ccc8cbSesaxe
102996ccc8cbSesaxe	elif ( fileType == 'Lint Library' ) :
103096ccc8cbSesaxe		return compareByDumping(base, ptch, quiet, fileType)
103196ccc8cbSesaxe
103296ccc8cbSesaxe	elif ( fileType == 'Sqlite Database' ) :
103396ccc8cbSesaxe		return compareByDumping(base, ptch, quiet, fileType)
103496ccc8cbSesaxe	else :
103596ccc8cbSesaxe		# it has to be some variety of text file
103696ccc8cbSesaxe		return compareBasic(base, ptch, quiet, fileType)
103796ccc8cbSesaxe
103896ccc8cbSesaxe# Cleanup and self-terminate
103996ccc8cbSesaxedef cleanup(ret) :
104096ccc8cbSesaxe
104196ccc8cbSesaxe	if len(tmpDir1) > 0 and len(tmpDir2) > 0 :
104296ccc8cbSesaxe
104396ccc8cbSesaxe		baseCmd = "rm -rf " + tmpDir1
104496ccc8cbSesaxe		ptchCmd = "rm -rf " + tmpDir2
104596ccc8cbSesaxe
104696ccc8cbSesaxe		os.system(baseCmd)
104796ccc8cbSesaxe		os.system(ptchCmd)
104896ccc8cbSesaxe
104996ccc8cbSesaxe	if logging :
105096ccc8cbSesaxe		log.close()
105196ccc8cbSesaxe
105296ccc8cbSesaxe	sys.exit(ret)
105396ccc8cbSesaxe
105496ccc8cbSesaxedef main() :
105596ccc8cbSesaxe
105696ccc8cbSesaxe	# Log file handle
105796ccc8cbSesaxe	global log
105896ccc8cbSesaxe
105996ccc8cbSesaxe	# Globals relating to command line options
106096ccc8cbSesaxe	global logging, vdiffs, reportAllSects
106196ccc8cbSesaxe
106296ccc8cbSesaxe	# Named temporary files / directories
106396ccc8cbSesaxe	global tmpDir1, tmpDir2, tmpFile1, tmpFile2
106496ccc8cbSesaxe
106596ccc8cbSesaxe	# Command paths
106696ccc8cbSesaxe	global lintdump_cmd, elfdump_cmd, dump_cmd, dis_cmd, od_cmd, diff_cmd, sqlite_cmd
106796ccc8cbSesaxe
106896ccc8cbSesaxe	# Default search path
106996ccc8cbSesaxe	global wsdiff_path
107096ccc8cbSesaxe
107196ccc8cbSesaxe	# Essentially "uname -p"
107296ccc8cbSesaxe	global arch
107396ccc8cbSesaxe
107496ccc8cbSesaxe	# Some globals need to be initialized
107596ccc8cbSesaxe	logging = vdiffs = reportAllSects = False
107696ccc8cbSesaxe
107796ccc8cbSesaxe
107896ccc8cbSesaxe	# Process command line arguments
107996ccc8cbSesaxe	# Return values are returned from args() in alpha order
108096ccc8cbSesaxe	# (Yes, python functions can return multiple values (ewww))
108196ccc8cbSesaxe	# Note that args() also set the globals:
108296ccc8cbSesaxe	#	logging to True if verbose logging (to a file) was enabled
108396ccc8cbSesaxe	#	vdiffs to True if logged differences aren't to be truncated
108496ccc8cbSesaxe	#	reportAllSects to True if all ELF section differences are to be reported
108596ccc8cbSesaxe	#
108696ccc8cbSesaxe	baseRoot, fileNamesFile, localTools, ptchRoot, results = args()
108796ccc8cbSesaxe
108896ccc8cbSesaxe	#
108996ccc8cbSesaxe	# Set up the results/log file
109096ccc8cbSesaxe	#
109196ccc8cbSesaxe	if logging :
109296ccc8cbSesaxe		try:
109396ccc8cbSesaxe			log = open(results, "w")
109496ccc8cbSesaxe		except:
109596ccc8cbSesaxe			logging = False
109696ccc8cbSesaxe			error("failed to open log file: " + log)
109796ccc8cbSesaxe			sys.exit(1)
109896ccc8cbSesaxe
109996ccc8cbSesaxe		dateTimeStr= "# %d/%d/%d at %d:%d:%d" % time.localtime()[:6]
110096ccc8cbSesaxe		v_info("# This file was produced by wsdiff")
110196ccc8cbSesaxe		v_info(dateTimeStr)
110296ccc8cbSesaxe
110396ccc8cbSesaxe	#
110496ccc8cbSesaxe	# Build paths to the tools required tools
110596ccc8cbSesaxe	#
110696ccc8cbSesaxe	# Try to look for tools in $SRC/tools if the "-t" option
110796ccc8cbSesaxe	# was specified
110896ccc8cbSesaxe	#
110996ccc8cbSesaxe	arch = commands.getoutput("uname -p")
111096ccc8cbSesaxe	if localTools :
111196ccc8cbSesaxe		try:
111296ccc8cbSesaxe			src = os.environ['SRC']
111396ccc8cbSesaxe		except:
111496ccc8cbSesaxe			error("-t specified, but $SRC not set. Cannot find $SRC/tools")
111596ccc8cbSesaxe			src = ""
111696ccc8cbSesaxe		if len(src) > 0 :
111796ccc8cbSesaxe			wsdiff_path.insert(0, src + "/tools/proto/opt/onbld/bin")
111896ccc8cbSesaxe
111996ccc8cbSesaxe	lintdump_cmd = find_tool("lintdump")
112096ccc8cbSesaxe	elfdump_cmd = find_tool("elfdump")
112196ccc8cbSesaxe	dump_cmd = find_tool("dump")
112296ccc8cbSesaxe	od_cmd = find_tool("od")
112396ccc8cbSesaxe	dis_cmd = find_tool("dis")
112496ccc8cbSesaxe	diff_cmd = find_tool("diff")
112596ccc8cbSesaxe	sqlite_cmd = find_tool("sqlite")
112696ccc8cbSesaxe
112796ccc8cbSesaxe	#
112896ccc8cbSesaxe	# validate the base and patch paths
112996ccc8cbSesaxe	#
113096ccc8cbSesaxe	if baseRoot[-1] != '/' :
113196ccc8cbSesaxe		baseRoot += '/'
113296ccc8cbSesaxe
113396ccc8cbSesaxe	if ptchRoot[-1] != '/' :
113496ccc8cbSesaxe		ptchRoot += '/'
113596ccc8cbSesaxe
113696ccc8cbSesaxe	if not os.path.exists(baseRoot) :
113796ccc8cbSesaxe		error("old proto area: " + baseRoot + " does not exist")
113896ccc8cbSesaxe		sys.exit(1)
113996ccc8cbSesaxe
114096ccc8cbSesaxe	if not os.path.exists(ptchRoot) :
114196ccc8cbSesaxe		error("new proto area: " + ptchRoot + \
114296ccc8cbSesaxe		      " does not exist")
114396ccc8cbSesaxe		sys.exit(1)
114496ccc8cbSesaxe
114596ccc8cbSesaxe	#
114696ccc8cbSesaxe	# log some information identifying the run
114796ccc8cbSesaxe	#
114896ccc8cbSesaxe	v_info("Old proto area: " + baseRoot)
114996ccc8cbSesaxe	v_info("New proto area: " + ptchRoot)
115096ccc8cbSesaxe	v_info("Results file: " + results + "\n")
115196ccc8cbSesaxe
115296ccc8cbSesaxe	#
115396ccc8cbSesaxe	# Set up the temporary directories / files
115496ccc8cbSesaxe	# Could use python's tmpdir routines, but these should
115596ccc8cbSesaxe	# be easier to identify / keep around for debugging
115696ccc8cbSesaxe	pid = os.getpid()
115796ccc8cbSesaxe	tmpDir1 = "/tmp/wsdiff_tmp1_" + str(pid) + "/"
115896ccc8cbSesaxe	tmpDir2 = "/tmp/wsdiff_tmp2_" + str(pid) + "/"
115996ccc8cbSesaxe	if not os.path.exists(tmpDir1) :
116096ccc8cbSesaxe		os.makedirs(tmpDir1)
116196ccc8cbSesaxe	if not os.path.exists(tmpDir2) :
116296ccc8cbSesaxe		os.makedirs(tmpDir2)
116396ccc8cbSesaxe
116496ccc8cbSesaxe	tmpFile1 = tmpDir1 + "f1"
116596ccc8cbSesaxe	tmpFile2 = tmpDir2 + "f2"
116696ccc8cbSesaxe
116796ccc8cbSesaxe	# Derive a catalog of new, deleted, and to-be-compared objects
116896ccc8cbSesaxe	# either from the specified base and patch proto areas, or from
116996ccc8cbSesaxe	# from an input file list
117096ccc8cbSesaxe	newOrDeleted = False
117196ccc8cbSesaxe
117296ccc8cbSesaxe	if fileNamesFile != "" :
117396ccc8cbSesaxe		changedFiles, newFiles, deletedFiles = \
117496ccc8cbSesaxe			      flistCatalog(baseRoot, ptchRoot, fileNamesFile)
117596ccc8cbSesaxe	else :
117696ccc8cbSesaxe		changedFiles, newFiles, deletedFiles = protoCatalog(baseRoot, ptchRoot)
117796ccc8cbSesaxe
117896ccc8cbSesaxe	if len(newFiles) > 0 :
117996ccc8cbSesaxe		newOrDeleted = True
118096ccc8cbSesaxe		info("\nNew objects found: ")
118196ccc8cbSesaxe
118296ccc8cbSesaxe		for fn in newFiles :
118396ccc8cbSesaxe			info(fnFormat(fn))
118496ccc8cbSesaxe
118596ccc8cbSesaxe	if len(deletedFiles) > 0 :
118696ccc8cbSesaxe		newOrDeleted = True
118796ccc8cbSesaxe		info("\nObjects removed: ")
118896ccc8cbSesaxe
118996ccc8cbSesaxe		for fn in deletedFiles :
119096ccc8cbSesaxe			info(fnFormat(fn))
119196ccc8cbSesaxe
119296ccc8cbSesaxe	if newOrDeleted :
119396ccc8cbSesaxe		info("\nChanged objects: ");
119496ccc8cbSesaxe
119596ccc8cbSesaxe
119696ccc8cbSesaxe	# Here's where all the heavy lifting happens
119796ccc8cbSesaxe	# Perform a comparison on each object appearing in
119896ccc8cbSesaxe	# both proto areas. compareOneFile will examine the
119996ccc8cbSesaxe	# file types of each object, and will vector off to
120096ccc8cbSesaxe	# the appropriate comparison routine, where the compare
120196ccc8cbSesaxe	# will happen, and any differences will be reported / logged
120296ccc8cbSesaxe	for fn in changedFiles :
120396ccc8cbSesaxe		base = baseRoot + fn
120496ccc8cbSesaxe		ptch = ptchRoot + fn
120596ccc8cbSesaxe
120696ccc8cbSesaxe		compareOneFile(base, ptch, False)
120796ccc8cbSesaxe
120896ccc8cbSesaxe	# We're done, cleanup.
120996ccc8cbSesaxe	cleanup(0)
121096ccc8cbSesaxe
121196ccc8cbSesaxeif __name__ == '__main__' :
121296ccc8cbSesaxe	try:
121396ccc8cbSesaxe		main()
121496ccc8cbSesaxe	except KeyboardInterrupt :
121596ccc8cbSesaxe		cleanup(1);
121696ccc8cbSesaxe
121796ccc8cbSesaxe
1218