xref: /titanic_53/usr/src/tools/scripts/wsdiff.py (revision 619b4598e0f434792bdadf91cc947739dc734758)
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#
22*619b4598Srotondo# Copyright 2007 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',
347*619b4598Srotondo		       'esa'	:	'Elfsign Activation',
34896ccc8cbSesaxe		       'db'	:	'Sqlite Database' }
34996ccc8cbSesaxe
350*619b4598Srotondo	try:
35196ccc8cbSesaxe		if os.stat(f)[ST_SIZE] == 0 :
35296ccc8cbSesaxe			return 'ASCII'
353*619b4598Srotondo	except:
354*619b4598Srotondo		error("failed to stat " + f)
355*619b4598Srotondo		return 'Error'
35696ccc8cbSesaxe
35796ccc8cbSesaxe	if isELF(f) == 1 :
35896ccc8cbSesaxe		return 'ELF'
35996ccc8cbSesaxe
36096ccc8cbSesaxe	fnamelist = f.split('.')
36196ccc8cbSesaxe	if len(fnamelist) > 1 :	# Test the file extension
36296ccc8cbSesaxe		extension = fnamelist[-1]
36396ccc8cbSesaxe		if extension in extensions.keys():
36496ccc8cbSesaxe			return extensions[extension]
36596ccc8cbSesaxe
36696ccc8cbSesaxe	return 'ASCII'
36796ccc8cbSesaxe
36896ccc8cbSesaxe#
36996ccc8cbSesaxe# Return non-zero if "f" is an ELF file
37096ccc8cbSesaxe#
37196ccc8cbSesaxeelfmagic = '\177ELF'
37296ccc8cbSesaxedef isELF(f) :
37396ccc8cbSesaxe	try:
37496ccc8cbSesaxe		fd = open(f)
37596ccc8cbSesaxe	except:
37696ccc8cbSesaxe		error("failed to open: " + f)
37796ccc8cbSesaxe		return 0
37896ccc8cbSesaxe	magic = fd.read(len(elfmagic))
37996ccc8cbSesaxe	fd.close()
38096ccc8cbSesaxe
38196ccc8cbSesaxe	if magic == elfmagic :
38296ccc8cbSesaxe		return 1
38396ccc8cbSesaxe	return 0
38496ccc8cbSesaxe
38596ccc8cbSesaxe#
38696ccc8cbSesaxe# Return non-zero is "f" is binary.
38796ccc8cbSesaxe# Consider the file to be binary if it contains any null characters
38896ccc8cbSesaxe#
38996ccc8cbSesaxedef isBinary(f) :
39096ccc8cbSesaxe	try:
39196ccc8cbSesaxe		fd = open(f)
39296ccc8cbSesaxe	except:
39396ccc8cbSesaxe		error("failed to open: " + f)
39496ccc8cbSesaxe		return 0
39596ccc8cbSesaxe	s = fd.read()
39696ccc8cbSesaxe	fd.close()
39796ccc8cbSesaxe
39896ccc8cbSesaxe	if s.find('\0') == -1 :
39996ccc8cbSesaxe		return 0
40096ccc8cbSesaxe	else :
40196ccc8cbSesaxe		return 1
40296ccc8cbSesaxe
40396ccc8cbSesaxe#####
40496ccc8cbSesaxe# Directory traversal and file finding
40596ccc8cbSesaxe#
40696ccc8cbSesaxe
40796ccc8cbSesaxe#
40896ccc8cbSesaxe# Return a sorted list of files found under the specified directory
40996ccc8cbSesaxe#
41096ccc8cbSesaxedef findFiles(d) :
41196ccc8cbSesaxe	for path, subdirs, files in os.walk(d) :
41296ccc8cbSesaxe		files.sort()
41396ccc8cbSesaxe		for name in files :
41496ccc8cbSesaxe			yield os.path.join(path, name)
41596ccc8cbSesaxe
41696ccc8cbSesaxe#
41796ccc8cbSesaxe# Examine all files in base, ptch
41896ccc8cbSesaxe#
41996ccc8cbSesaxe# Return a list of files appearing in both proto areas,
42096ccc8cbSesaxe# a list of new files (files found only in ptch) and
42196ccc8cbSesaxe# a list of deleted files (files found only in base)
42296ccc8cbSesaxe#
42396ccc8cbSesaxedef protoCatalog(base, ptch) :
42496ccc8cbSesaxe	compFiles = []		# List of files in both proto areas
42596ccc8cbSesaxe	ptchList = []		# List of file in patch proto area
42696ccc8cbSesaxe
42796ccc8cbSesaxe	newFiles = []		# New files detected
42896ccc8cbSesaxe	deletedFiles = []	# Deleted files
42996ccc8cbSesaxe
43096ccc8cbSesaxe	baseFilesList = list(findFiles(base))
43196ccc8cbSesaxe	baseStringLength = len(base)
43296ccc8cbSesaxe
43396ccc8cbSesaxe	ptchFilesList = list(findFiles(ptch))
43496ccc8cbSesaxe	ptchStringLength = len(ptch)
43596ccc8cbSesaxe
43696ccc8cbSesaxe	# Inventory files in the base proto area
43796ccc8cbSesaxe	for fn in baseFilesList :
43896ccc8cbSesaxe		if os.path.islink(fn) :
43996ccc8cbSesaxe			continue
44096ccc8cbSesaxe
44196ccc8cbSesaxe		fileName = fn[baseStringLength:]
44296ccc8cbSesaxe		compFiles.append(fileName)
44396ccc8cbSesaxe
44496ccc8cbSesaxe	# Inventory files in the patch proto area
44596ccc8cbSesaxe	for fn in ptchFilesList :
44696ccc8cbSesaxe		if os.path.islink(fn) :
44796ccc8cbSesaxe			continue
44896ccc8cbSesaxe
44996ccc8cbSesaxe		fileName = fn[ptchStringLength:]
45096ccc8cbSesaxe		ptchList.append(fileName)
45196ccc8cbSesaxe
45296ccc8cbSesaxe	# Deleted files appear in the base area, but not the patch area
45396ccc8cbSesaxe	for fileName in compFiles :
45496ccc8cbSesaxe		if not fileName in ptchList :
45596ccc8cbSesaxe			deletedFiles.append(fileName)
45696ccc8cbSesaxe
45796ccc8cbSesaxe	# Eliminate "deleted" files from the list of objects appearing
45896ccc8cbSesaxe	# in both the base and patch proto areas
45996ccc8cbSesaxe	for fileName in deletedFiles :
46096ccc8cbSesaxe		try:
46196ccc8cbSesaxe		       	compFiles.remove(fileName)
46296ccc8cbSesaxe		except:
46396ccc8cbSesaxe			error("filelist.remove() failed")
46496ccc8cbSesaxe
46596ccc8cbSesaxe	# New files appear in the patch area, but not the base
46696ccc8cbSesaxe	for fileName in ptchList :
46796ccc8cbSesaxe		if not fileName in compFiles :
46896ccc8cbSesaxe			newFiles.append(fileName)
46996ccc8cbSesaxe
47096ccc8cbSesaxe	return compFiles, newFiles, deletedFiles
47196ccc8cbSesaxe
47296ccc8cbSesaxe#
47396ccc8cbSesaxe# Examine the files listed in the input file list
47496ccc8cbSesaxe#
47596ccc8cbSesaxe# Return a list of files appearing in both proto areas,
47696ccc8cbSesaxe# a list of new files (files found only in ptch) and
47796ccc8cbSesaxe# a list of deleted files (files found only in base)
47896ccc8cbSesaxe#
47996ccc8cbSesaxedef flistCatalog(base, ptch, flist) :
48096ccc8cbSesaxe	compFiles = []		# List of files in both proto areas
48196ccc8cbSesaxe	newFiles = []		# New files detected
48296ccc8cbSesaxe	deletedFiles = []	# Deleted files
48396ccc8cbSesaxe
48496ccc8cbSesaxe	try:
48596ccc8cbSesaxe		fd = open(flist, "r")
48696ccc8cbSesaxe	except:
48796ccc8cbSesaxe		error("could not open: " + flist)
48896ccc8cbSesaxe		cleanup(1)
48996ccc8cbSesaxe
49096ccc8cbSesaxe	files = []
49196ccc8cbSesaxe	files = fd.readlines()
49296ccc8cbSesaxe
49396ccc8cbSesaxe	for f in files :
49496ccc8cbSesaxe		ptch_present = True
49596ccc8cbSesaxe		base_present = True
49696ccc8cbSesaxe
49796ccc8cbSesaxe		if f == '\n' :
49896ccc8cbSesaxe			continue
49996ccc8cbSesaxe
50096ccc8cbSesaxe		# the fileNames have a trailing '\n'
50196ccc8cbSesaxe		f = f.rstrip()
50296ccc8cbSesaxe
50396ccc8cbSesaxe		# The objects in the file list have paths relative
50496ccc8cbSesaxe		# to $ROOT or to the base/ptch directory specified on
50596ccc8cbSesaxe		# the command line.
50696ccc8cbSesaxe		# If it's relative to $ROOT, we'll need to add back the
50796ccc8cbSesaxe		# root_`uname -p` goo we stripped off in fnFormat()
50896ccc8cbSesaxe		if os.path.exists(base + f) :
50996ccc8cbSesaxe			fn = f;
51096ccc8cbSesaxe		elif os.path.exists(base + "root_" + arch + "/" + f) :
51196ccc8cbSesaxe			fn = "root_" + arch + "/" + f
51296ccc8cbSesaxe		else :
51396ccc8cbSesaxe			base_present = False
51496ccc8cbSesaxe
51596ccc8cbSesaxe		if base_present :
51696ccc8cbSesaxe			if not os.path.exists(ptch + fn) :
51796ccc8cbSesaxe				ptch_present = False
51896ccc8cbSesaxe		else :
51996ccc8cbSesaxe			if os.path.exists(ptch + f) :
52096ccc8cbSesaxe				fn = f
52196ccc8cbSesaxe			elif os.path.exists(ptch + "root_" + arch + "/" + f) :
52296ccc8cbSesaxe				fn = "root_" + arch + "/" + f
52396ccc8cbSesaxe			else :
52496ccc8cbSesaxe				ptch_present = False
52596ccc8cbSesaxe
52696ccc8cbSesaxe		if os.path.islink(base + fn) :	# ignore links
52796ccc8cbSesaxe			base_present = False
52896ccc8cbSesaxe		if os.path.islink(ptch + fn) :
52996ccc8cbSesaxe			ptch_present = False
53096ccc8cbSesaxe
53196ccc8cbSesaxe		if base_present and ptch_present :
53296ccc8cbSesaxe			compFiles.append(fn)
53396ccc8cbSesaxe		elif base_present :
53496ccc8cbSesaxe			deletedFiles.append(fn)
53596ccc8cbSesaxe		elif ptch_present :
53696ccc8cbSesaxe			newFiles.append(fn)
53796ccc8cbSesaxe		else :
53896ccc8cbSesaxe			if os.path.islink(base + fn) and os.path.islink(ptch + fn) :
53996ccc8cbSesaxe				continue
54096ccc8cbSesaxe			error(f + " in file list, but not in either tree. Skipping...")
54196ccc8cbSesaxe
54296ccc8cbSesaxe	return compFiles, newFiles, deletedFiles
54396ccc8cbSesaxe
54496ccc8cbSesaxe
54596ccc8cbSesaxe#
54696ccc8cbSesaxe# Build a fully qualified path to an external tool/utility.
54796ccc8cbSesaxe# Consider the default system locations. For onbld tools, if
54896ccc8cbSesaxe# the -t option was specified, we'll try to use built tools in $SRC tools,
54996ccc8cbSesaxe# and otherwise, we'll fall back on /opt/onbld/
55096ccc8cbSesaxe#
55196ccc8cbSesaxedef find_tool(tool) :
55296ccc8cbSesaxe
55396ccc8cbSesaxe	# First, check what was passed
55496ccc8cbSesaxe	if os.path.exists(tool) :
55596ccc8cbSesaxe		return tool
55696ccc8cbSesaxe
55796ccc8cbSesaxe	# Next try in wsdiff path
55896ccc8cbSesaxe	for pdir in wsdiff_path :
55996ccc8cbSesaxe		location = pdir + "/" + tool
56096ccc8cbSesaxe		if os.path.exists(location) :
56196ccc8cbSesaxe			return location + " "
56296ccc8cbSesaxe
56396ccc8cbSesaxe		location = pdir + "/" + arch + "/" + tool
56496ccc8cbSesaxe		if os.path.exists(location) :
56596ccc8cbSesaxe			return location + " "
56696ccc8cbSesaxe
56796ccc8cbSesaxe	error("Could not find path to: " + tool);
56896ccc8cbSesaxe	sys.exit(1);
56996ccc8cbSesaxe
57096ccc8cbSesaxe
57196ccc8cbSesaxe#####
57296ccc8cbSesaxe# ELF file comparison helper routines
57396ccc8cbSesaxe#
57496ccc8cbSesaxe
57596ccc8cbSesaxe#
57696ccc8cbSesaxe# Return a dictionary of ELF section types keyed by section name
57796ccc8cbSesaxe#
57896ccc8cbSesaxedef get_elfheader(f) :
57996ccc8cbSesaxe
58096ccc8cbSesaxe	header = {}
58196ccc8cbSesaxe
58296ccc8cbSesaxe	hstring = commands.getoutput(elfdump_cmd + " -c " + f)
58396ccc8cbSesaxe
58496ccc8cbSesaxe	if len(hstring) == 0 :
58596ccc8cbSesaxe		error("Failed to dump ELF header for " + f)
58696ccc8cbSesaxe		return
58796ccc8cbSesaxe
58896ccc8cbSesaxe	# elfdump(1) dumps the section headers with the section name
58996ccc8cbSesaxe	# following "sh_name:", and the section type following "sh_type:"
59096ccc8cbSesaxe	sections = hstring.split("Section Header")
59196ccc8cbSesaxe	for sect in sections :
59296ccc8cbSesaxe		datap = sect.find("sh_name:");
59396ccc8cbSesaxe		if datap == -1 :
59496ccc8cbSesaxe			continue
59596ccc8cbSesaxe		section = sect[datap:].split()[1]
59696ccc8cbSesaxe		datap = sect.find("sh_type:");
59796ccc8cbSesaxe		if datap == -1 :
59896ccc8cbSesaxe			error("Could not get type for sect: " + section + \
59996ccc8cbSesaxe			      " in " + f)
60096ccc8cbSesaxe		sh_type = sect[datap:].split()[2]
60196ccc8cbSesaxe		header[section] = sh_type
60296ccc8cbSesaxe
60396ccc8cbSesaxe	return header
60496ccc8cbSesaxe
60596ccc8cbSesaxe#
60696ccc8cbSesaxe# Extract data in the specified ELF section from the given file
60796ccc8cbSesaxe#
60896ccc8cbSesaxedef extract_elf_section(f, section) :
60996ccc8cbSesaxe
61096ccc8cbSesaxe	data = commands.getoutput(dump_cmd + " -sn " + section + " " + f)
61196ccc8cbSesaxe
61296ccc8cbSesaxe	if len(data) == 0 :
61396ccc8cbSesaxe		error(cmd + " yielded no data")
61496ccc8cbSesaxe		return
61596ccc8cbSesaxe
61696ccc8cbSesaxe	# dump(1) displays the file name to start...
61796ccc8cbSesaxe	# get past it to the data itself
61896ccc8cbSesaxe	dbegin = data.find(":") + 1
61996ccc8cbSesaxe	data = data[dbegin:];
62096ccc8cbSesaxe
62196ccc8cbSesaxe	return (data)
62296ccc8cbSesaxe
62396ccc8cbSesaxe#
62496ccc8cbSesaxe# Return a (hopefully meaningful) human readable set of diffs
62596ccc8cbSesaxe# for the specified ELF section between f1 and f2
62696ccc8cbSesaxe#
62796ccc8cbSesaxe# Depending on the section, various means for dumping and diffing
62896ccc8cbSesaxe# the data may be employed.
62996ccc8cbSesaxe#
63096ccc8cbSesaxetext_sections = [ '.text', '.init', '.fini' ]
63196ccc8cbSesaxedef diff_elf_section(f1, f2, section, sh_type) :
63296ccc8cbSesaxe
63396ccc8cbSesaxe	if (sh_type == "SHT_RELA") : # sh_type == SHT_RELA
63496ccc8cbSesaxe		cmd1 = elfdump_cmd + " -r " + f1 + " > " + tmpFile1
63596ccc8cbSesaxe		cmd2 = elfdump_cmd + " -r " + f2 + " > " + tmpFile2
63696ccc8cbSesaxe	elif (section == ".group") :
63796ccc8cbSesaxe		cmd1 = elfdump_cmd + " -g " + f1 + " > " + tmpFile1
63896ccc8cbSesaxe		cmd2 = elfdump_cmd + " -g " + f2 + " > " + tmpFile2
63996ccc8cbSesaxe	elif (section == ".hash") :
64096ccc8cbSesaxe		cmd1 = elfdump_cmd + " -h " + f1 + " > " + tmpFile1
64196ccc8cbSesaxe		cmd2 = elfdump_cmd + " -h " + f2 + " > " + tmpFile2
64296ccc8cbSesaxe	elif (section == ".dynamic") :
64396ccc8cbSesaxe		cmd1 = elfdump_cmd + " -d " + f1 + " > " + tmpFile1
64496ccc8cbSesaxe		cmd2 = elfdump_cmd + " -d " + f2 + " > " + tmpFile2
64596ccc8cbSesaxe	elif (section == ".got") :
64696ccc8cbSesaxe		cmd1 = elfdump_cmd + " -G " + f1 + " > " + tmpFile1
64796ccc8cbSesaxe		cmd2 = elfdump_cmd + " -G " + f2 + " > " + tmpFile2
64896ccc8cbSesaxe	elif (section == ".SUNW_cap") :
64996ccc8cbSesaxe		cmd1 = elfdump_cmd + " -H " + f1 + " > " + tmpFile1
65096ccc8cbSesaxe		cmd2 = elfdump_cmd + " -H " + f2 + " > " + tmpFile2
65196ccc8cbSesaxe	elif (section == ".interp") :
65296ccc8cbSesaxe		cmd1 = elfdump_cmd + " -i " + f1 + " > " + tmpFile1
65396ccc8cbSesaxe		cmd2 = elfdump_cmd + " -i " + f2 + " > " + tmpFile2
65496ccc8cbSesaxe	elif (section == ".symtab" or section == ".dynsym") :
65596ccc8cbSesaxe		cmd1 = elfdump_cmd + " -s -N " + section + " " + f1 + " > " + tmpFile1
65696ccc8cbSesaxe		cmd2 = elfdump_cmd + " -s -N " + section + " " + f2 + " > " + tmpFile2
65796ccc8cbSesaxe	elif (section in text_sections) :
65896ccc8cbSesaxe		# dis sometimes complains when it hits something it doesn't
65996ccc8cbSesaxe		# know how to disassemble. Just ignore it, as the output
66096ccc8cbSesaxe		# being generated here is human readable, and we've already
66196ccc8cbSesaxe		# correctly flagged the difference.
66296ccc8cbSesaxe		cmd1 = dis_cmd + " -t " + section + " " + f1 + \
66396ccc8cbSesaxe		       " 2>/dev/null | grep -v disassembly > " + tmpFile1
66496ccc8cbSesaxe		cmd2 = dis_cmd + " -t " + section + " " + f2 + \
66596ccc8cbSesaxe		       " 2>/dev/null | grep -v disassembly > " + tmpFile2
66696ccc8cbSesaxe	else :
66796ccc8cbSesaxe		cmd1 = elfdump_cmd + " -w " + tmpFile1 + " -N " + \
66896ccc8cbSesaxe		       section + " " + f1
66996ccc8cbSesaxe		cmd2 = elfdump_cmd + " -w " + tmpFile2 + " -N " + \
67096ccc8cbSesaxe		       section + " " + f2
67196ccc8cbSesaxe
67296ccc8cbSesaxe	os.system(cmd1)
67396ccc8cbSesaxe	os.system(cmd2)
67496ccc8cbSesaxe
67596ccc8cbSesaxe	data = diffFileData(tmpFile1, tmpFile2)
67696ccc8cbSesaxe
67796ccc8cbSesaxe	return (data)
67896ccc8cbSesaxe
67996ccc8cbSesaxe#
68096ccc8cbSesaxe# compare the relevant sections of two ELF binaries
68196ccc8cbSesaxe# and report any differences
68296ccc8cbSesaxe#
68396ccc8cbSesaxe# Returns: 1 if any differenes found
68496ccc8cbSesaxe#          0 if no differences found
68596ccc8cbSesaxe#	  -1 on error
68696ccc8cbSesaxe#
68796ccc8cbSesaxe
68896ccc8cbSesaxe# Sections deliberately not considered when comparing two ELF
68996ccc8cbSesaxe# binaries. Differences observed in these sections are not considered
69096ccc8cbSesaxe# significant where patch deliverable identification is concerned.
69196ccc8cbSesaxesections_to_skip = [ ".SUNW_signature",
69296ccc8cbSesaxe		     ".comment",
69396ccc8cbSesaxe		     ".SUNW_ctf",
69496ccc8cbSesaxe		     ".debug",
69596ccc8cbSesaxe		     ".plt",
69696ccc8cbSesaxe		     ".rela.bss",
69796ccc8cbSesaxe		     ".rela.plt",
69896ccc8cbSesaxe		     ".line",
69996ccc8cbSesaxe		     ".note",
700f6a1d796Sesaxe		     ".compcom",
70196ccc8cbSesaxe		     ]
70296ccc8cbSesaxe
70396ccc8cbSesaxesections_preferred = [ ".rodata.str1.8",
70496ccc8cbSesaxe		       ".rodata.str1.1",
70596ccc8cbSesaxe		       ".rodata",
70696ccc8cbSesaxe		       ".data1",
70796ccc8cbSesaxe		       ".data",
70896ccc8cbSesaxe		       ".text",
70996ccc8cbSesaxe		       ]
71096ccc8cbSesaxe
71196ccc8cbSesaxedef compareElfs(base, ptch, quiet) :
71296ccc8cbSesaxe
71396ccc8cbSesaxe	global logging
71496ccc8cbSesaxe
71596ccc8cbSesaxe	base_header = get_elfheader(base)
71696ccc8cbSesaxe 	sections = base_header.keys()
71796ccc8cbSesaxe
71896ccc8cbSesaxe	ptch_header = get_elfheader(ptch)
71996ccc8cbSesaxe	e2_only_sections = ptch_header.keys()
72096ccc8cbSesaxe
72196ccc8cbSesaxe	e1_only_sections = []
72296ccc8cbSesaxe
72396ccc8cbSesaxe	fileName = fnFormat(base)
72496ccc8cbSesaxe
72596ccc8cbSesaxe	# Derive the list of ELF sections found only in
72696ccc8cbSesaxe	# either e1 or e2.
72796ccc8cbSesaxe	for sect in sections :
72896ccc8cbSesaxe		if not sect in e2_only_sections :
72996ccc8cbSesaxe			e1_only_sections.append(sect)
73096ccc8cbSesaxe		else :
73196ccc8cbSesaxe			e2_only_sections.remove(sect)
73296ccc8cbSesaxe
73396ccc8cbSesaxe	if len(e1_only_sections) > 0 :
73496ccc8cbSesaxe		if quiet :
73596ccc8cbSesaxe			return 1
73696ccc8cbSesaxe		info(fileName);
73796ccc8cbSesaxe		if not logging :
73896ccc8cbSesaxe			return 1
73996ccc8cbSesaxe
74096ccc8cbSesaxe		slist = ""
74196ccc8cbSesaxe		for sect in e1_only_sections :
74296ccc8cbSesaxe			slist = slist + sect + "\t"
74396ccc8cbSesaxe		v_info("\nELF sections found in " + \
74496ccc8cbSesaxe		      base + " but not in " + ptch)
74596ccc8cbSesaxe		v_info("\n" + slist)
74696ccc8cbSesaxe		return 1
74796ccc8cbSesaxe
74896ccc8cbSesaxe	if len(e2_only_sections) > 0 :
74996ccc8cbSesaxe		if quiet :
75096ccc8cbSesaxe			return 1
75196ccc8cbSesaxe
75296ccc8cbSesaxe		info(fileName);
75396ccc8cbSesaxe		if not logging :
75496ccc8cbSesaxe			return 1
75596ccc8cbSesaxe
75696ccc8cbSesaxe		slist = ""
75796ccc8cbSesaxe		for sect in e2_only_sections :
75896ccc8cbSesaxe			slist = slist + sect + "\t"
75996ccc8cbSesaxe		v_info("\nELF sections found in " + \
76096ccc8cbSesaxe		      ptch + " but not in " + base)
76196ccc8cbSesaxe		v_info("\n" + slist)
76296ccc8cbSesaxe		return 1
76396ccc8cbSesaxe
76496ccc8cbSesaxe	# Look for preferred sections, and put those at the
76596ccc8cbSesaxe	# top of the list of sections to compare
76696ccc8cbSesaxe	for psect in sections_preferred :
76796ccc8cbSesaxe		if psect in sections :
76896ccc8cbSesaxe			sections.remove(psect)
76996ccc8cbSesaxe			sections.insert(0, psect)
77096ccc8cbSesaxe
77196ccc8cbSesaxe	# Compare ELF sections
77296ccc8cbSesaxe	first_section = True
77396ccc8cbSesaxe	for sect in sections :
77496ccc8cbSesaxe
77596ccc8cbSesaxe		if sect in sections_to_skip :
77696ccc8cbSesaxe			continue
77796ccc8cbSesaxe
77896ccc8cbSesaxe		s1 = extract_elf_section(base, sect);
77996ccc8cbSesaxe		s2 = extract_elf_section(ptch, sect);
78096ccc8cbSesaxe
78196ccc8cbSesaxe		if len(s1) != len (s2) or s1 != s2:
78296ccc8cbSesaxe			if not quiet:
78396ccc8cbSesaxe				sh_type = base_header[sect]
78496ccc8cbSesaxe				data = diff_elf_section(base, ptch, sect, \
78596ccc8cbSesaxe							sh_type)
78696ccc8cbSesaxe
78796ccc8cbSesaxe				# If all ELF sections are being reported, then
78896ccc8cbSesaxe				# invoke difference() to flag the file name to
78996ccc8cbSesaxe				# stdout only once. Any other section differences
79096ccc8cbSesaxe				# should be logged to the results file directly
79196ccc8cbSesaxe				if not first_section :
79296ccc8cbSesaxe					log_difference(fileName, "ELF " + sect, data)
79396ccc8cbSesaxe				else :
79496ccc8cbSesaxe					difference(fileName, "ELF " + sect, data)
79596ccc8cbSesaxe
79696ccc8cbSesaxe			if not reportAllSects :
79796ccc8cbSesaxe				return 1
79896ccc8cbSesaxe			first_section = False
79996ccc8cbSesaxe	return 0
80096ccc8cbSesaxe
80196ccc8cbSesaxe#####
80296ccc8cbSesaxe# Archive object comparison
80396ccc8cbSesaxe#
80496ccc8cbSesaxe# Returns 1 if difference detected
80596ccc8cbSesaxe#         0 if no difference detected
80696ccc8cbSesaxe#        -1 on error
80796ccc8cbSesaxe#
80896ccc8cbSesaxedef compareArchives(base, ptch, fileType) :
80996ccc8cbSesaxe
81096ccc8cbSesaxe	fileName = fnFormat(base)
81196ccc8cbSesaxe
81296ccc8cbSesaxe	# clear the temp directories
81396ccc8cbSesaxe	baseCmd = "rm -rf " + tmpDir1 + "*"
81496ccc8cbSesaxe	status, output = commands.getstatusoutput(baseCmd)
81596ccc8cbSesaxe	if status != 0 :
81696ccc8cbSesaxe		error(baseCmd + " failed: " + output)
81796ccc8cbSesaxe		return -1
81896ccc8cbSesaxe
81996ccc8cbSesaxe	ptchCmd = "rm -rf " + tmpDir2 + "*"
82096ccc8cbSesaxe	status, output = commands.getstatusoutput(ptchCmd)
82196ccc8cbSesaxe	if status != 0 :
82296ccc8cbSesaxe		error(ptchCmd + " failed: " + output)
82396ccc8cbSesaxe		return -1
82496ccc8cbSesaxe
82596ccc8cbSesaxe	#
82696ccc8cbSesaxe	# Be optimistic and first try a straight file compare
82796ccc8cbSesaxe	# as it will allow us to finish up quickly.
82896ccc8cbSesaxe	if compareBasic(base, ptch, True, fileType) == 0 :
82996ccc8cbSesaxe		return 0
83096ccc8cbSesaxe
83196ccc8cbSesaxe	# copy over the objects to the temp areas, and
83296ccc8cbSesaxe	# unpack them
83396ccc8cbSesaxe	baseCmd = "cp -fp " + base + " " + tmpDir1
83496ccc8cbSesaxe	status, output = commands.getstatusoutput(baseCmd)
83596ccc8cbSesaxe	if status != 0 :
83696ccc8cbSesaxe		error(baseCmd + " failed: " + output)
83796ccc8cbSesaxe		return -1
83896ccc8cbSesaxe
83996ccc8cbSesaxe	ptchCmd = "cp -fp " + ptch + " " + tmpDir2
84096ccc8cbSesaxe	status, output = commands.getstatusoutput(ptchCmd)
84196ccc8cbSesaxe	if status != 0 :
84296ccc8cbSesaxe		error(ptchCmd + " failed: " + output)
84396ccc8cbSesaxe		return -1
84496ccc8cbSesaxe
84596ccc8cbSesaxe	bname = string.split(fileName, '/')[-1]
84696ccc8cbSesaxe	if fileType == "Java Archive" :
84796ccc8cbSesaxe		baseCmd = "cd " + tmpDir1 + "; " + "jar xf " + bname + \
84896ccc8cbSesaxe			  "; rm -f " + bname + " META-INF/MANIFEST.MF"
84996ccc8cbSesaxe		ptchCmd = "cd " + tmpDir2 + "; " + "jar xf " + bname + \
85096ccc8cbSesaxe			  "; rm -f " + bname + " META-INF/MANIFEST.MF"
85196ccc8cbSesaxe	elif fileType == "ELF Object Archive" :
85296ccc8cbSesaxe		baseCmd = "cd " + tmpDir1 + "; " + "/usr/ccs/bin/ar x " + \
85396ccc8cbSesaxe			  bname + "; rm -f " + bname
85496ccc8cbSesaxe		ptchCmd = "cd " + tmpDir2 + "; " + "/usr/ccs/bin/ar x " + \
85596ccc8cbSesaxe			  bname + "; rm -f " + bname
85696ccc8cbSesaxe	else :
85796ccc8cbSesaxe		error("unexpected file type: " + fileType)
85896ccc8cbSesaxe		return -1
85996ccc8cbSesaxe
86096ccc8cbSesaxe	os.system(baseCmd)
86196ccc8cbSesaxe	os.system(ptchCmd)
86296ccc8cbSesaxe
86396ccc8cbSesaxe	baseFlist = list(findFiles(tmpDir1))
86496ccc8cbSesaxe	ptchFlist = list(findFiles(tmpDir2))
86596ccc8cbSesaxe
86696ccc8cbSesaxe	# Trim leading path off base/ptch file lists
86796ccc8cbSesaxe	flist = []
86896ccc8cbSesaxe	for fn in baseFlist :
86996ccc8cbSesaxe		flist.append(str_prefix_trunc(fn, tmpDir1))
87096ccc8cbSesaxe	baseFlist = flist
87196ccc8cbSesaxe
87296ccc8cbSesaxe	flist = []
87396ccc8cbSesaxe	for fn in ptchFlist :
87496ccc8cbSesaxe		flist.append(str_prefix_trunc(fn, tmpDir2))
87596ccc8cbSesaxe	ptchFlist = flist
87696ccc8cbSesaxe
87796ccc8cbSesaxe	for fn in ptchFlist :
87896ccc8cbSesaxe		if not fn in baseFlist :
87996ccc8cbSesaxe			difference(fileName, fileType, \
88096ccc8cbSesaxe				   fn + " added to " + fileName)
88196ccc8cbSesaxe			return 1
88296ccc8cbSesaxe
88396ccc8cbSesaxe	for fn in baseFlist :
88496ccc8cbSesaxe		if not fn in ptchFlist :
88596ccc8cbSesaxe			difference(fileName, fileType, \
88696ccc8cbSesaxe				   fn + " removed from " + fileName)
88796ccc8cbSesaxe			return 1
88896ccc8cbSesaxe
88996ccc8cbSesaxe		differs = compareOneFile((tmpDir1 + fn), (tmpDir2 + fn), True)
89096ccc8cbSesaxe		if differs :
89196ccc8cbSesaxe			difference(fileName, fileType, \
89296ccc8cbSesaxe				   fn + " in " + fileName + " differs")
89396ccc8cbSesaxe			return 1
89496ccc8cbSesaxe	return 0
89596ccc8cbSesaxe
89696ccc8cbSesaxe#####
89796ccc8cbSesaxe# (Basic) file comparison
89896ccc8cbSesaxe#
89996ccc8cbSesaxe# There's some special case code here for Javadoc HTML files
90096ccc8cbSesaxe#
90196ccc8cbSesaxe# Returns 1 if difference detected
90296ccc8cbSesaxe#         0 if no difference detected
90396ccc8cbSesaxe#        -1 on error
90496ccc8cbSesaxe#
90596ccc8cbSesaxedef compareBasic(base, ptch, quiet, fileType) :
90696ccc8cbSesaxe
90796ccc8cbSesaxe	fileName = fnFormat(base);
90896ccc8cbSesaxe
90996ccc8cbSesaxe	if quiet and os.stat(base)[ST_SIZE] != os.stat(ptch)[ST_SIZE] :
91096ccc8cbSesaxe		return 1
91196ccc8cbSesaxe
91296ccc8cbSesaxe	try:
91396ccc8cbSesaxe		baseFile = open(base)
91496ccc8cbSesaxe	except:
91596ccc8cbSesaxe		error("could not open " + base)
91696ccc8cbSesaxe		return -1
91796ccc8cbSesaxe	try:
91896ccc8cbSesaxe		ptchFile = open(ptch)
91996ccc8cbSesaxe	except:
92096ccc8cbSesaxe		error("could not open " + ptch)
92196ccc8cbSesaxe		return -1
92296ccc8cbSesaxe
92396ccc8cbSesaxe	baseData = baseFile.read()
92496ccc8cbSesaxe	ptchData = ptchFile.read()
92596ccc8cbSesaxe
92696ccc8cbSesaxe	baseFile.close()
92796ccc8cbSesaxe	ptchFile.close()
92896ccc8cbSesaxe
92996ccc8cbSesaxe	needToSnip = False
93096ccc8cbSesaxe	if fileType == "HTML" :
93196ccc8cbSesaxe		needToSnip = True
93296ccc8cbSesaxe		toSnipBeginStr = "<!-- Generated by javadoc"
93396ccc8cbSesaxe		toSnipEndStr = "-->\n"
93496ccc8cbSesaxe
93596ccc8cbSesaxe	if needToSnip :
93696ccc8cbSesaxe		toSnipBegin = string.find(baseData, toSnipBeginStr)
93796ccc8cbSesaxe		if toSnipBegin != -1 :
93896ccc8cbSesaxe			toSnipEnd = string.find(baseData[toSnipBegin:], \
93996ccc8cbSesaxe						toSnipEndStr) + \
94096ccc8cbSesaxe						len(toSnipEndStr)
94196ccc8cbSesaxe			baseData = baseData[:toSnipBegin] + \
94296ccc8cbSesaxe				   baseData[toSnipBegin + toSnipEnd:]
94396ccc8cbSesaxe			ptchData = ptchData[:toSnipBegin] + \
94496ccc8cbSesaxe				   ptchData[toSnipBegin + toSnipEnd:]
94596ccc8cbSesaxe
94696ccc8cbSesaxe	if quiet :
94796ccc8cbSesaxe		if baseData != ptchData :
94896ccc8cbSesaxe			return 1
94996ccc8cbSesaxe	else :
95096ccc8cbSesaxe		if len(baseData) != len(ptchData) or baseData != ptchData :
95196ccc8cbSesaxe			diffs = diffData(baseData, ptchData)
95296ccc8cbSesaxe			difference(fileName, fileType, diffs)
95396ccc8cbSesaxe			return 1
95496ccc8cbSesaxe	return 0
95596ccc8cbSesaxe
95696ccc8cbSesaxe
95796ccc8cbSesaxe#####
95896ccc8cbSesaxe# Compare two objects by producing a data dump from
95996ccc8cbSesaxe# each object, and then comparing the dump data
96096ccc8cbSesaxe#
96196ccc8cbSesaxe# Returns: 1 if a difference is detected
96296ccc8cbSesaxe#          0 if no difference detected
96396ccc8cbSesaxe#         -1 upon error
96496ccc8cbSesaxe#
96596ccc8cbSesaxedef compareByDumping(base, ptch, quiet, fileType) :
96696ccc8cbSesaxe
96796ccc8cbSesaxe	fileName = fnFormat(base);
96896ccc8cbSesaxe
96996ccc8cbSesaxe	if fileType == "Lint Library" :
97096ccc8cbSesaxe		baseCmd = lintdump_cmd + " -ir " + base + \
97196ccc8cbSesaxe			  " | grep -v LINTLIB:" + " > " + tmpFile1
97296ccc8cbSesaxe		ptchCmd = lintdump_cmd + " -ir " + ptch + \
97396ccc8cbSesaxe			  " | grep -v LINTLIB:" + " > " + tmpFile2
97496ccc8cbSesaxe	elif fileType == "Sqlite Database" :
97596ccc8cbSesaxe		baseCmd = "echo .dump | " + sqlite_cmd + base + " > " + \
97696ccc8cbSesaxe			  tmpFile1
97796ccc8cbSesaxe		ptchCmd = "echo .dump | " + sqlite_cmd + ptch + " > " + \
97896ccc8cbSesaxe			  tmpFile2
97996ccc8cbSesaxe
98096ccc8cbSesaxe	os.system(baseCmd)
98196ccc8cbSesaxe	os.system(ptchCmd)
98296ccc8cbSesaxe
98396ccc8cbSesaxe	try:
98496ccc8cbSesaxe		baseFile = open(tmpFile1)
98596ccc8cbSesaxe	except:
98696ccc8cbSesaxe		error("could not open: " + tmpFile1)
98796ccc8cbSesaxe	try:
98896ccc8cbSesaxe		ptchFile = open(tmpFile2)
98996ccc8cbSesaxe	except:
99096ccc8cbSesaxe		error("could not open: " + tmpFile2)
99196ccc8cbSesaxe
99296ccc8cbSesaxe	baseData = baseFile.read()
99396ccc8cbSesaxe	ptchData = ptchFile.read()
99496ccc8cbSesaxe
99596ccc8cbSesaxe	baseFile.close()
99696ccc8cbSesaxe	ptchFile.close()
99796ccc8cbSesaxe
99896ccc8cbSesaxe	if len(baseData) != len(ptchData) or baseData != ptchData :
99996ccc8cbSesaxe		if not quiet :
100096ccc8cbSesaxe			data = diffFileData(tmpFile1, tmpFile2);
100196ccc8cbSesaxe			difference(fileName, fileType, data)
100296ccc8cbSesaxe 		return 1
100396ccc8cbSesaxe	return 0
100496ccc8cbSesaxe
100596ccc8cbSesaxe#####
1006*619b4598Srotondo# Compare two elfsign activation files. This ignores the activation
1007*619b4598Srotondo# files themselves and reports a difference if and only if the
1008*619b4598Srotondo# corresponding base files are different.
1009*619b4598Srotondo#
1010*619b4598Srotondo# Returns 1 if difference detected
1011*619b4598Srotondo#         0 if no difference detected
1012*619b4598Srotondo#        -1 on error
1013*619b4598Srotondo#
1014*619b4598Srotondodef compareActivation(base, ptch, quiet, fileType) :
1015*619b4598Srotondo
1016*619b4598Srotondo	fileName = fnFormat(base)
1017*619b4598Srotondo
1018*619b4598Srotondo	# Drop the .esa suffix from both filenames.
1019*619b4598Srotondo	base = base[0:base.rfind('.esa')]
1020*619b4598Srotondo	ptch = ptch[0:ptch.rfind('.esa')]
1021*619b4598Srotondo
1022*619b4598Srotondo	result = compareOneFile(base, ptch, True)
1023*619b4598Srotondo	if result == -1 :
1024*619b4598Srotondo		error("unable to compare " + fileName)
1025*619b4598Srotondo	elif result == 1 :
1026*619b4598Srotondo		if not quiet :
1027*619b4598Srotondo			difference(fileName, fileType, \
1028*619b4598Srotondo				"change in corresponding ELF file")
1029*619b4598Srotondo
1030*619b4598Srotondo	return result
1031*619b4598Srotondo
1032*619b4598Srotondo#####
103396ccc8cbSesaxe# Compare two objects. Detect type changes.
103496ccc8cbSesaxe# Vector off to the appropriate type specific
103596ccc8cbSesaxe# compare routine based on the type.
103696ccc8cbSesaxe#
103796ccc8cbSesaxedef compareOneFile(base, ptch, quiet) :
103896ccc8cbSesaxe
103996ccc8cbSesaxe	# Verify the file types.
104096ccc8cbSesaxe	# If they are different, indicate this and move on
104196ccc8cbSesaxe	btype = getTheFileType(base)
104296ccc8cbSesaxe	ptype = getTheFileType(ptch)
104396ccc8cbSesaxe
1044*619b4598Srotondo	if btype == 'Error' or ptype == 'Error' :
1045*619b4598Srotondo		return -1
1046*619b4598Srotondo
104796ccc8cbSesaxe	fileName = fnFormat(base)
104896ccc8cbSesaxe
104996ccc8cbSesaxe	if (btype != ptype) :
1050*619b4598Srotondo		if not quiet :
105196ccc8cbSesaxe			difference(fileName, "file type", btype + " to " + ptype)
105296ccc8cbSesaxe		return 1
105396ccc8cbSesaxe	else :
105496ccc8cbSesaxe		fileType = btype
105596ccc8cbSesaxe
105696ccc8cbSesaxe	if (fileType == 'ELF') :
105796ccc8cbSesaxe		return compareElfs(base, ptch, quiet)
105896ccc8cbSesaxe
105996ccc8cbSesaxe	elif (fileType == 'Java Archive' or fileType == 'ELF Object Archive') :
106096ccc8cbSesaxe		return compareArchives(base, ptch, fileType)
106196ccc8cbSesaxe
106296ccc8cbSesaxe	elif (fileType == 'HTML') :
106396ccc8cbSesaxe		return compareBasic(base, ptch, quiet, fileType)
106496ccc8cbSesaxe
106596ccc8cbSesaxe	elif ( fileType == 'Lint Library' ) :
106696ccc8cbSesaxe		return compareByDumping(base, ptch, quiet, fileType)
106796ccc8cbSesaxe
106896ccc8cbSesaxe	elif ( fileType == 'Sqlite Database' ) :
106996ccc8cbSesaxe		return compareByDumping(base, ptch, quiet, fileType)
1070*619b4598Srotondo
1071*619b4598Srotondo	elif ( fileType == 'Elfsign Activation' ) :
1072*619b4598Srotondo		return compareActivation(base, ptch, quiet, fileType)
1073*619b4598Srotondo
107496ccc8cbSesaxe	else :
107596ccc8cbSesaxe		# it has to be some variety of text file
107696ccc8cbSesaxe		return compareBasic(base, ptch, quiet, fileType)
107796ccc8cbSesaxe
107896ccc8cbSesaxe# Cleanup and self-terminate
107996ccc8cbSesaxedef cleanup(ret) :
108096ccc8cbSesaxe
108196ccc8cbSesaxe	if len(tmpDir1) > 0 and len(tmpDir2) > 0 :
108296ccc8cbSesaxe
108396ccc8cbSesaxe		baseCmd = "rm -rf " + tmpDir1
108496ccc8cbSesaxe		ptchCmd = "rm -rf " + tmpDir2
108596ccc8cbSesaxe
108696ccc8cbSesaxe		os.system(baseCmd)
108796ccc8cbSesaxe		os.system(ptchCmd)
108896ccc8cbSesaxe
108996ccc8cbSesaxe	if logging :
109096ccc8cbSesaxe		log.close()
109196ccc8cbSesaxe
109296ccc8cbSesaxe	sys.exit(ret)
109396ccc8cbSesaxe
109496ccc8cbSesaxedef main() :
109596ccc8cbSesaxe
109696ccc8cbSesaxe	# Log file handle
109796ccc8cbSesaxe	global log
109896ccc8cbSesaxe
109996ccc8cbSesaxe	# Globals relating to command line options
110096ccc8cbSesaxe	global logging, vdiffs, reportAllSects
110196ccc8cbSesaxe
110296ccc8cbSesaxe	# Named temporary files / directories
110396ccc8cbSesaxe	global tmpDir1, tmpDir2, tmpFile1, tmpFile2
110496ccc8cbSesaxe
110596ccc8cbSesaxe	# Command paths
110696ccc8cbSesaxe	global lintdump_cmd, elfdump_cmd, dump_cmd, dis_cmd, od_cmd, diff_cmd, sqlite_cmd
110796ccc8cbSesaxe
110896ccc8cbSesaxe	# Default search path
110996ccc8cbSesaxe	global wsdiff_path
111096ccc8cbSesaxe
111196ccc8cbSesaxe	# Essentially "uname -p"
111296ccc8cbSesaxe	global arch
111396ccc8cbSesaxe
111496ccc8cbSesaxe	# Some globals need to be initialized
111596ccc8cbSesaxe	logging = vdiffs = reportAllSects = False
111696ccc8cbSesaxe
111796ccc8cbSesaxe
111896ccc8cbSesaxe	# Process command line arguments
111996ccc8cbSesaxe	# Return values are returned from args() in alpha order
112096ccc8cbSesaxe	# (Yes, python functions can return multiple values (ewww))
112196ccc8cbSesaxe	# Note that args() also set the globals:
112296ccc8cbSesaxe	#	logging to True if verbose logging (to a file) was enabled
112396ccc8cbSesaxe	#	vdiffs to True if logged differences aren't to be truncated
112496ccc8cbSesaxe	#	reportAllSects to True if all ELF section differences are to be reported
112596ccc8cbSesaxe	#
112696ccc8cbSesaxe	baseRoot, fileNamesFile, localTools, ptchRoot, results = args()
112796ccc8cbSesaxe
112896ccc8cbSesaxe	#
112996ccc8cbSesaxe	# Set up the results/log file
113096ccc8cbSesaxe	#
113196ccc8cbSesaxe	if logging :
113296ccc8cbSesaxe		try:
113396ccc8cbSesaxe			log = open(results, "w")
113496ccc8cbSesaxe		except:
113596ccc8cbSesaxe			logging = False
113696ccc8cbSesaxe			error("failed to open log file: " + log)
113796ccc8cbSesaxe			sys.exit(1)
113896ccc8cbSesaxe
113996ccc8cbSesaxe		dateTimeStr= "# %d/%d/%d at %d:%d:%d" % time.localtime()[:6]
114096ccc8cbSesaxe		v_info("# This file was produced by wsdiff")
114196ccc8cbSesaxe		v_info(dateTimeStr)
114296ccc8cbSesaxe
114396ccc8cbSesaxe	#
114496ccc8cbSesaxe	# Build paths to the tools required tools
114596ccc8cbSesaxe	#
114696ccc8cbSesaxe	# Try to look for tools in $SRC/tools if the "-t" option
114796ccc8cbSesaxe	# was specified
114896ccc8cbSesaxe	#
114996ccc8cbSesaxe	arch = commands.getoutput("uname -p")
115096ccc8cbSesaxe	if localTools :
115196ccc8cbSesaxe		try:
115296ccc8cbSesaxe			src = os.environ['SRC']
115396ccc8cbSesaxe		except:
115496ccc8cbSesaxe			error("-t specified, but $SRC not set. Cannot find $SRC/tools")
115596ccc8cbSesaxe			src = ""
115696ccc8cbSesaxe		if len(src) > 0 :
115796ccc8cbSesaxe			wsdiff_path.insert(0, src + "/tools/proto/opt/onbld/bin")
115896ccc8cbSesaxe
115996ccc8cbSesaxe	lintdump_cmd = find_tool("lintdump")
116096ccc8cbSesaxe	elfdump_cmd = find_tool("elfdump")
116196ccc8cbSesaxe	dump_cmd = find_tool("dump")
116296ccc8cbSesaxe	od_cmd = find_tool("od")
116396ccc8cbSesaxe	dis_cmd = find_tool("dis")
116496ccc8cbSesaxe	diff_cmd = find_tool("diff")
116596ccc8cbSesaxe	sqlite_cmd = find_tool("sqlite")
116696ccc8cbSesaxe
116796ccc8cbSesaxe	#
116896ccc8cbSesaxe	# validate the base and patch paths
116996ccc8cbSesaxe	#
117096ccc8cbSesaxe	if baseRoot[-1] != '/' :
117196ccc8cbSesaxe		baseRoot += '/'
117296ccc8cbSesaxe
117396ccc8cbSesaxe	if ptchRoot[-1] != '/' :
117496ccc8cbSesaxe		ptchRoot += '/'
117596ccc8cbSesaxe
117696ccc8cbSesaxe	if not os.path.exists(baseRoot) :
117796ccc8cbSesaxe		error("old proto area: " + baseRoot + " does not exist")
117896ccc8cbSesaxe		sys.exit(1)
117996ccc8cbSesaxe
118096ccc8cbSesaxe	if not os.path.exists(ptchRoot) :
118196ccc8cbSesaxe		error("new proto area: " + ptchRoot + \
118296ccc8cbSesaxe		      " does not exist")
118396ccc8cbSesaxe		sys.exit(1)
118496ccc8cbSesaxe
118596ccc8cbSesaxe	#
118696ccc8cbSesaxe	# log some information identifying the run
118796ccc8cbSesaxe	#
118896ccc8cbSesaxe	v_info("Old proto area: " + baseRoot)
118996ccc8cbSesaxe	v_info("New proto area: " + ptchRoot)
119096ccc8cbSesaxe	v_info("Results file: " + results + "\n")
119196ccc8cbSesaxe
119296ccc8cbSesaxe	#
119396ccc8cbSesaxe	# Set up the temporary directories / files
119496ccc8cbSesaxe	# Could use python's tmpdir routines, but these should
119596ccc8cbSesaxe	# be easier to identify / keep around for debugging
119696ccc8cbSesaxe	pid = os.getpid()
119796ccc8cbSesaxe	tmpDir1 = "/tmp/wsdiff_tmp1_" + str(pid) + "/"
119896ccc8cbSesaxe	tmpDir2 = "/tmp/wsdiff_tmp2_" + str(pid) + "/"
119996ccc8cbSesaxe	if not os.path.exists(tmpDir1) :
120096ccc8cbSesaxe		os.makedirs(tmpDir1)
120196ccc8cbSesaxe	if not os.path.exists(tmpDir2) :
120296ccc8cbSesaxe		os.makedirs(tmpDir2)
120396ccc8cbSesaxe
120496ccc8cbSesaxe	tmpFile1 = tmpDir1 + "f1"
120596ccc8cbSesaxe	tmpFile2 = tmpDir2 + "f2"
120696ccc8cbSesaxe
120796ccc8cbSesaxe	# Derive a catalog of new, deleted, and to-be-compared objects
120896ccc8cbSesaxe	# either from the specified base and patch proto areas, or from
120996ccc8cbSesaxe	# from an input file list
121096ccc8cbSesaxe	newOrDeleted = False
121196ccc8cbSesaxe
121296ccc8cbSesaxe	if fileNamesFile != "" :
121396ccc8cbSesaxe		changedFiles, newFiles, deletedFiles = \
121496ccc8cbSesaxe			      flistCatalog(baseRoot, ptchRoot, fileNamesFile)
121596ccc8cbSesaxe	else :
121696ccc8cbSesaxe		changedFiles, newFiles, deletedFiles = protoCatalog(baseRoot, ptchRoot)
121796ccc8cbSesaxe
121896ccc8cbSesaxe	if len(newFiles) > 0 :
121996ccc8cbSesaxe		newOrDeleted = True
122096ccc8cbSesaxe		info("\nNew objects found: ")
122196ccc8cbSesaxe
122296ccc8cbSesaxe		for fn in newFiles :
122396ccc8cbSesaxe			info(fnFormat(fn))
122496ccc8cbSesaxe
122596ccc8cbSesaxe	if len(deletedFiles) > 0 :
122696ccc8cbSesaxe		newOrDeleted = True
122796ccc8cbSesaxe		info("\nObjects removed: ")
122896ccc8cbSesaxe
122996ccc8cbSesaxe		for fn in deletedFiles :
123096ccc8cbSesaxe			info(fnFormat(fn))
123196ccc8cbSesaxe
123296ccc8cbSesaxe	if newOrDeleted :
123396ccc8cbSesaxe		info("\nChanged objects: ");
123496ccc8cbSesaxe
123596ccc8cbSesaxe
123696ccc8cbSesaxe	# Here's where all the heavy lifting happens
123796ccc8cbSesaxe	# Perform a comparison on each object appearing in
123896ccc8cbSesaxe	# both proto areas. compareOneFile will examine the
123996ccc8cbSesaxe	# file types of each object, and will vector off to
124096ccc8cbSesaxe	# the appropriate comparison routine, where the compare
124196ccc8cbSesaxe	# will happen, and any differences will be reported / logged
124296ccc8cbSesaxe	for fn in changedFiles :
124396ccc8cbSesaxe		base = baseRoot + fn
124496ccc8cbSesaxe		ptch = ptchRoot + fn
124596ccc8cbSesaxe
124696ccc8cbSesaxe		compareOneFile(base, ptch, False)
124796ccc8cbSesaxe
124896ccc8cbSesaxe	# We're done, cleanup.
124996ccc8cbSesaxe	cleanup(0)
125096ccc8cbSesaxe
125196ccc8cbSesaxeif __name__ == '__main__' :
125296ccc8cbSesaxe	try:
125396ccc8cbSesaxe		main()
125496ccc8cbSesaxe	except KeyboardInterrupt :
125596ccc8cbSesaxe		cleanup(1);
125696ccc8cbSesaxe
125796ccc8cbSesaxe
1258