xref: /titanic_44/usr/src/tools/scripts/find_elf.pl (revision 6d0f2021fe1487a6c87087b48f3f0924f81b9859)
175ce41a5SAli Bahrami#!/usr/bin/perl -w
275ce41a5SAli Bahrami#
375ce41a5SAli Bahrami# CDDL HEADER START
475ce41a5SAli Bahrami#
575ce41a5SAli Bahrami# The contents of this file are subject to the terms of the
675ce41a5SAli Bahrami# Common Development and Distribution License (the "License").
775ce41a5SAli Bahrami# You may not use this file except in compliance with the License.
875ce41a5SAli Bahrami#
975ce41a5SAli Bahrami# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1075ce41a5SAli Bahrami# or http://www.opensolaris.org/os/licensing.
1175ce41a5SAli Bahrami# See the License for the specific language governing permissions
1275ce41a5SAli Bahrami# and limitations under the License.
1375ce41a5SAli Bahrami#
1475ce41a5SAli Bahrami# When distributing Covered Code, include this CDDL HEADER in each
1575ce41a5SAli Bahrami# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1675ce41a5SAli Bahrami# If applicable, add the following below this CDDL HEADER, with the
1775ce41a5SAli Bahrami# fields enclosed by brackets "[]" replaced with your own identifying
1875ce41a5SAli Bahrami# information: Portions Copyright [yyyy] [name of copyright owner]
1975ce41a5SAli Bahrami#
2075ce41a5SAli Bahrami# CDDL HEADER END
2175ce41a5SAli Bahrami#
2275ce41a5SAli Bahrami
2375ce41a5SAli Bahrami#
245253169eSAli Bahrami# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2575ce41a5SAli Bahrami#
2675ce41a5SAli Bahrami
2775ce41a5SAli Bahrami#
2875ce41a5SAli Bahrami# Find ELF executables and sharable objects
2975ce41a5SAli Bahrami#
3075ce41a5SAli Bahrami# This script descends a directory hierarchy and reports the ELF
3175ce41a5SAli Bahrami# objects found, one object per line of output.
3275ce41a5SAli Bahrami#
3375ce41a5SAli Bahrami#	find_elf [-frs] path
3475ce41a5SAli Bahrami#
3575ce41a5SAli Bahrami# Where path is a file or directory.
3675ce41a5SAli Bahrami#
3775ce41a5SAli Bahrami# Each line of output is of the form:
3875ce41a5SAli Bahrami#
3975ce41a5SAli Bahrami#	ELFCLASS  ELFTYPE VERDEF|NOVERDEF relpath
4075ce41a5SAli Bahrami#
4175ce41a5SAli Bahrami# where relpath is the path relative to the directory from which the
4275ce41a5SAli Bahrami# search started.
4375ce41a5SAli Bahrami
4475ce41a5SAli Bahramiuse strict;
4575ce41a5SAli Bahrami
4675ce41a5SAli Bahramiuse vars  qw($Prog %Output @SaveArgv);
4775ce41a5SAli Bahramiuse vars  qw(%opt $HaveElfedit);
4875ce41a5SAli Bahrami
4975ce41a5SAli Bahrami# Hashes used to detect aliases --- symlinks that reference a common file
5075ce41a5SAli Bahrami#
5175ce41a5SAli Bahrami#	id_hash - Maps the unique st_dev/st_ino pair to the real file
5275ce41a5SAli Bahrami#	alias_hash - Maps symlinks to the real file they reference
5375ce41a5SAli Bahrami#
5475ce41a5SAli Bahramiuse vars  qw(%id_hash %alias_hash);
5575ce41a5SAli Bahrami
5675ce41a5SAli Bahramiuse POSIX qw(getenv);
5775ce41a5SAli Bahramiuse Getopt::Std;
5875ce41a5SAli Bahramiuse File::Basename;
59*6d0f2021SRichard Loweuse IO::Dir;
6075ce41a5SAli Bahrami
6175ce41a5SAli Bahrami
6275ce41a5SAli Bahrami## GetObjectInfo(path)
6375ce41a5SAli Bahrami#
6475ce41a5SAli Bahrami# Return a 3 element output array describing the object
6575ce41a5SAli Bahrami# given by path. The elements of the array contain:
6675ce41a5SAli Bahrami#
6775ce41a5SAli Bahrami#	Index   Meaning
6875ce41a5SAli Bahrami#	-----------------------------------------------
6975ce41a5SAli Bahrami#	0	ELFCLASS of object (0 if not an ELF object)
7075ce41a5SAli Bahrami#	1	Type of object (NONE if not an ELF object)
7175ce41a5SAli Bahrami#	2	VERDEF if object defines versions, NOVERDEF otherwise
7275ce41a5SAli Bahrami#
7375ce41a5SAli Bahramisub GetObjectInfo {
7475ce41a5SAli Bahrami	my $path = $_[0];
7575ce41a5SAli Bahrami
7675ce41a5SAli Bahrami	# If elfedit is available, we use it to obtain the desired information
7775ce41a5SAli Bahrami	# by executing three commands in order, to produce a 0, 2, or 3
7875ce41a5SAli Bahrami	# element output array.
7975ce41a5SAli Bahrami	#
8075ce41a5SAli Bahrami	#	Command                 Meaning
8175ce41a5SAli Bahrami	#	-----------------------------------------------
8275ce41a5SAli Bahrami	#	ehdr:ei_class		ELFCLASS of object
8375ce41a5SAli Bahrami	#	ehdr:ei_e_type		Type of object
8475ce41a5SAli Bahrami	#	dyn:tag verdef		Address of verdef items
8575ce41a5SAli Bahrami	#
8675ce41a5SAli Bahrami	# We discard stderr, and simply examine the resulting array to
8775ce41a5SAli Bahrami	# determine the situation:
8875ce41a5SAli Bahrami	#
8975ce41a5SAli Bahrami	#	# Array Elements	Meaning
9075ce41a5SAli Bahrami	#	-----------------------------------------------
9175ce41a5SAli Bahrami	#	  0			File is not ELF object
9275ce41a5SAli Bahrami	#	  2			Object with no versions (no VERDEF)
9375ce41a5SAli Bahrami	#	  3			Object that has versions
9475ce41a5SAli Bahrami	if ($HaveElfedit) {
9575ce41a5SAli Bahrami		my $ecmd = "elfedit -r -o simple -e ehdr:ei_class " .
9675ce41a5SAli Bahrami		    "-e ehdr:e_type -e 'dyn:tag verdef'";
9775ce41a5SAli Bahrami		my @Elf = split(/\n/, `$ecmd $path 2>/dev/null`);
9875ce41a5SAli Bahrami
9975ce41a5SAli Bahrami		my $ElfCnt = scalar @Elf;
10075ce41a5SAli Bahrami
10175ce41a5SAli Bahrami		# Return ET_NONE array if not an ELF object
10275ce41a5SAli Bahrami		return (0, 'NONE', 'NOVERDEF') if ($ElfCnt == 0);
10375ce41a5SAli Bahrami
10475ce41a5SAli Bahrami		# Otherwise, convert the result to standard form
10575ce41a5SAli Bahrami		$Elf[0] =~ s/^ELFCLASS//;
10675ce41a5SAli Bahrami		$Elf[1] =~ s/^ET_//;
10775ce41a5SAli Bahrami		$Elf[2] = ($ElfCnt == 3) ? 'VERDEF' : 'NOVERDEF';
10875ce41a5SAli Bahrami		return @Elf;
10975ce41a5SAli Bahrami	}
11075ce41a5SAli Bahrami
11175ce41a5SAli Bahrami	# For older platforms, we use elfdump to get the desired information.
11275ce41a5SAli Bahrami	my @Elf = split(/\n/, `elfdump -ed $path 2>&1`);
11375ce41a5SAli Bahrami	my $Header = 'None';
11475ce41a5SAli Bahrami	my $Verdef = 'NOVERDEF';
11575ce41a5SAli Bahrami	my ($Class, $Type);
11675ce41a5SAli Bahrami
11775ce41a5SAli Bahrami	foreach my $Line (@Elf) {
11875ce41a5SAli Bahrami		# If we have an invalid file type (which we can tell from the
11975ce41a5SAli Bahrami		# first line), or we're processing an archive, bail.
12075ce41a5SAli Bahrami		if ($Header eq 'None') {
12175ce41a5SAli Bahrami			if (($Line =~ /invalid file/) ||
12275ce41a5SAli Bahrami			    ($Line =~ /$path(.*):/)) {
12375ce41a5SAli Bahrami				return (0, 'NONE', 'NOVERDEF');
12475ce41a5SAli Bahrami			}
12575ce41a5SAli Bahrami		}
12675ce41a5SAli Bahrami
12775ce41a5SAli Bahrami		if ($Line =~ /^ELF Header/) {
12875ce41a5SAli Bahrami			$Header = 'Ehdr';
12975ce41a5SAli Bahrami			next;
13075ce41a5SAli Bahrami		}
13175ce41a5SAli Bahrami
13275ce41a5SAli Bahrami		if ($Line =~ /^Dynamic Section/) {
13375ce41a5SAli Bahrami			$Header = 'Dyn';
13475ce41a5SAli Bahrami			next;
13575ce41a5SAli Bahrami		}
13675ce41a5SAli Bahrami
13775ce41a5SAli Bahrami		if ($Header eq 'Ehdr') {
13875ce41a5SAli Bahrami			if ($Line =~ /e_type:\s*ET_([^\s]+)/) {
13975ce41a5SAli Bahrami				$Type = $1;
14075ce41a5SAli Bahrami				next;
14175ce41a5SAli Bahrami			}
14275ce41a5SAli Bahrami			if ($Line =~ /ei_class:\s+ELFCLASS(\d+)/) {
14375ce41a5SAli Bahrami				$Class = $1;
14475ce41a5SAli Bahrami				next;
14575ce41a5SAli Bahrami			}
14675ce41a5SAli Bahrami			next;
14775ce41a5SAli Bahrami		}
14875ce41a5SAli Bahrami
14975ce41a5SAli Bahrami		if (($Header eq 'Dyn') &&
15075ce41a5SAli Bahrami		    ($Line =~ /^\s*\[\d+\]\s+VERDEF\s+/)) {
15175ce41a5SAli Bahrami			$Verdef = 'VERDEF';
15275ce41a5SAli Bahrami			next;
15375ce41a5SAli Bahrami		}
15475ce41a5SAli Bahrami	}
15575ce41a5SAli Bahrami	return ($Class, $Type, $Verdef);
15675ce41a5SAli Bahrami}
15775ce41a5SAli Bahrami
15875ce41a5SAli Bahrami
15975ce41a5SAli Bahrami## ProcFile(FullPath, RelPath, AliasedPath, IsSymLink, dev, ino)
16075ce41a5SAli Bahrami#
16175ce41a5SAli Bahrami# Determine whether this a ELF dynamic object and if so, add a line
16275ce41a5SAli Bahrami# of output for it to @Output describing it.
16375ce41a5SAli Bahrami#
16475ce41a5SAli Bahrami# entry:
16575ce41a5SAli Bahrami#	FullPath - Fully qualified path
16675ce41a5SAli Bahrami#	RelPath - Path relative to starting root directory
16775ce41a5SAli Bahrami#	AliasedPath - True if RelPath contains a symlink directory component.
16875ce41a5SAli Bahrami#		Such a path represents an alias to the same file found
16975ce41a5SAli Bahrami#		completely via actual directories.
17075ce41a5SAli Bahrami#	IsSymLink - True if basename (final component) of path is a symlink.
17175ce41a5SAli Bahrami#
17275ce41a5SAli Bahramisub ProcFile {
17375ce41a5SAli Bahrami	my($FullPath, $RelPath, $AliasedPath, $IsSymLink, $dev, $ino) = @_;
17475ce41a5SAli Bahrami	my(@Elf, @Pvs, @Pvs_don, @Vers, %TopVer);
17575ce41a5SAli Bahrami	my($Aud, $Max, $Priv, $Pub, $ElfCnt, $Val, $Ttl, $NotPlugin);
17675ce41a5SAli Bahrami
17775ce41a5SAli Bahrami	my $uniqid = sprintf("%llx-%llx", $dev, $ino);
17875ce41a5SAli Bahrami
17975ce41a5SAli Bahrami	# Remove ./ from front of relative path
18075ce41a5SAli Bahrami	$RelPath =~ s/^\.\///;
18175ce41a5SAli Bahrami
18275ce41a5SAli Bahrami	my $name = $opt{r} ? $RelPath : $FullPath;
18375ce41a5SAli Bahrami
18475ce41a5SAli Bahrami	# If this is a symlink, or the path contains a symlink, put it in
18575ce41a5SAli Bahrami	# the alias hash for later analysis. We do this before testing to
18675ce41a5SAli Bahrami	# see if it is an ELF file, because that's a relatively expensive
18775ce41a5SAli Bahrami	# test. The tradeoff is that the alias hash will contain some files
18875ce41a5SAli Bahrami	# we don't care about. That is a small cost.
1895253169eSAli Bahrami	if (($IsSymLink || $AliasedPath) && !$opt{a}) {
19075ce41a5SAli Bahrami		$alias_hash{$name} = $uniqid;
19175ce41a5SAli Bahrami		return;
19275ce41a5SAli Bahrami	}
19375ce41a5SAli Bahrami
19475ce41a5SAli Bahrami	# Obtain the ELF information for this object.
19575ce41a5SAli Bahrami	@Elf = GetObjectInfo($FullPath);
19675ce41a5SAli Bahrami
19775ce41a5SAli Bahrami        # Return quietly if:
19875ce41a5SAli Bahrami	#	- Not an executable or sharable object
19975ce41a5SAli Bahrami	#	- An executable, but the -s option was used.
20075ce41a5SAli Bahrami	if ((($Elf[1] ne 'EXEC') && ($Elf[1] ne 'DYN')) ||
20175ce41a5SAli Bahrami	    (($Elf[1] eq 'EXEC') && $opt{s})) {
20275ce41a5SAli Bahrami		return;
20375ce41a5SAli Bahrami	}
20475ce41a5SAli Bahrami
20575ce41a5SAli Bahrami	$Output{$name} = sprintf("OBJECT %2s %-4s %-8s %s\n",
20675ce41a5SAli Bahrami	    $Elf[0], $Elf[1], $Elf[2], $name);
20775ce41a5SAli Bahrami
20875ce41a5SAli Bahrami	# Remember it for later alias analysis
20975ce41a5SAli Bahrami	$id_hash{$uniqid} = $name;
21075ce41a5SAli Bahrami}
21175ce41a5SAli Bahrami
21275ce41a5SAli Bahrami
21375ce41a5SAli Bahrami## ProcDir(FullPath, RelPath, AliasedPath, SelfSymlink)
21475ce41a5SAli Bahrami#
21575ce41a5SAli Bahrami# Recursively search directory for dynamic ELF objects, calling
21675ce41a5SAli Bahrami# ProcFile() on each one.
21775ce41a5SAli Bahrami#
21875ce41a5SAli Bahrami# entry:
21975ce41a5SAli Bahrami#	FullPath - Fully qualified path
22075ce41a5SAli Bahrami#	RelPath - Path relative to starting root directory
22175ce41a5SAli Bahrami#	AliasedPath - True if RelPath contains a symlink directory component.
22275ce41a5SAli Bahrami#		Such a path represents an alias to the same file found
22375ce41a5SAli Bahrami#		completely via actual directories.
22475ce41a5SAli Bahrami#	SelfSymlink - True (1) if the last segment in the path is a symlink
22575ce41a5SAli Bahrami#		that points at the same directory (i.e. 32->.). If SelfSymlink
22675ce41a5SAli Bahrami#		is True, ProcDir() examines the given directory for objects,
22775ce41a5SAli Bahrami#		but does not recurse past it. This captures the aliases for
22875ce41a5SAli Bahrami#		those objects, while avoiding entering a recursive loop,
22975ce41a5SAli Bahrami#		or generating nonsensical paths (i.e., 32/amd64/...).
23075ce41a5SAli Bahrami#
23175ce41a5SAli Bahramisub ProcDir {
23275ce41a5SAli Bahrami	my($FullDir, $RelDir, $AliasedPath, $SelfSymlink) = @_;
23375ce41a5SAli Bahrami	my($NewFull, $NewRel, $Entry);
23475ce41a5SAli Bahrami
23575ce41a5SAli Bahrami	# Open the directory and read each entry, omit files starting with "."
236*6d0f2021SRichard Lowe	my $Dir = IO::Dir->new($FullDir);
237*6d0f2021SRichard Lowe	if (defined($Dir)) {
238*6d0f2021SRichard Lowe		foreach $Entry ($Dir->read()) {
23975ce41a5SAli Bahrami
2405253169eSAli Bahrami			# In fast mode, we skip any file name that starts
2415253169eSAli Bahrami			# with a dot, which by side effect also skips the
2425253169eSAli Bahrami			# '.' and '..' entries. In regular mode, we must
2435253169eSAli Bahrami			# explicitly filter out those entries.
2445253169eSAli Bahrami			if ($opt{f}) {
2455253169eSAli Bahrami				next if ($Entry =~ /^\./);
2465253169eSAli Bahrami			} else {
2475253169eSAli Bahrami				next if ($Entry =~ /^\.\.?$/);
24875ce41a5SAli Bahrami			}
2495253169eSAli Bahrami
25075ce41a5SAli Bahrami			$NewFull = join('/', $FullDir, $Entry);
25175ce41a5SAli Bahrami
25275ce41a5SAli Bahrami			# We need to follow symlinks in order to capture
25375ce41a5SAli Bahrami			# all possible aliases for each object. However,
25475ce41a5SAli Bahrami			# symlinks that point back at the same directory
25575ce41a5SAli Bahrami			# (e.g. 32->.) must be flagged via the SelfSymlink
25675ce41a5SAli Bahrami			# argument to our recursive self in order to avoid
25775ce41a5SAli Bahrami			# taking it more than one level down.
25875ce41a5SAli Bahrami			my $RecurseAliasedPath = $AliasedPath;
25975ce41a5SAli Bahrami			my $RecurseSelfSymlink = 0;
26075ce41a5SAli Bahrami			my $IsSymLink = -l $NewFull;
26175ce41a5SAli Bahrami			if ($IsSymLink) {
26275ce41a5SAli Bahrami				my $trans = readlink($NewFull);
26375ce41a5SAli Bahrami
26475ce41a5SAli Bahrami				$trans =~ s/\/*$//;
26575ce41a5SAli Bahrami				$RecurseSelfSymlink = 1 if $trans eq '.';
26675ce41a5SAli Bahrami				$RecurseAliasedPath = 1;
26775ce41a5SAli Bahrami			}
26875ce41a5SAli Bahrami
26975ce41a5SAli Bahrami			if (!stat($NewFull)) {
27075ce41a5SAli Bahrami				next;
27175ce41a5SAli Bahrami			}
27275ce41a5SAli Bahrami			$NewRel = join('/', $RelDir, $Entry);
27375ce41a5SAli Bahrami
27475ce41a5SAli Bahrami			# Descend into and process any directories.
27575ce41a5SAli Bahrami			if (-d _) {
27675ce41a5SAli Bahrami				# If we have recursed here via a $SelfSymlink,
27775ce41a5SAli Bahrami				# then do not persue directories. We only
27875ce41a5SAli Bahrami				# want to find objects in the same directory
27975ce41a5SAli Bahrami				# via that link.
28075ce41a5SAli Bahrami				next if $SelfSymlink;
28175ce41a5SAli Bahrami
28275ce41a5SAli Bahrami				ProcDir($NewFull, $NewRel, $RecurseAliasedPath,
28375ce41a5SAli Bahrami				    $RecurseSelfSymlink);
28475ce41a5SAli Bahrami				next;
28575ce41a5SAli Bahrami			}
28675ce41a5SAli Bahrami
28775ce41a5SAli Bahrami			# In fast mode, we skip objects unless they end with
28875ce41a5SAli Bahrami			# a .so extension, or are executable. We touch
28975ce41a5SAli Bahrami			# considerably fewer files this way.
29075ce41a5SAli Bahrami			if ($opt{f} && !($Entry =~ /\.so$/) &&
29175ce41a5SAli Bahrami			    !($Entry =~ /\.so\./) &&
29275ce41a5SAli Bahrami			    ($opt{s} || (! -x _))) {
29375ce41a5SAli Bahrami			    next;
29475ce41a5SAli Bahrami			}
29575ce41a5SAli Bahrami
29675ce41a5SAli Bahrami			# Process any standard files.
29775ce41a5SAli Bahrami			if (-f _) {
29875ce41a5SAli Bahrami				my ($dev, $ino) = stat(_);
29975ce41a5SAli Bahrami				ProcFile($NewFull, $NewRel, $AliasedPath,
30075ce41a5SAli Bahrami				    $IsSymLink, $dev, $ino);
30175ce41a5SAli Bahrami				next;
30275ce41a5SAli Bahrami			}
30375ce41a5SAli Bahrami
30475ce41a5SAli Bahrami		}
305*6d0f2021SRichard Lowe		$Dir->close();
30675ce41a5SAli Bahrami	}
30775ce41a5SAli Bahrami}
30875ce41a5SAli Bahrami
30975ce41a5SAli Bahrami
31075ce41a5SAli Bahrami# -----------------------------------------------------------------------------
31175ce41a5SAli Bahrami
31275ce41a5SAli Bahrami# Establish a program name for any error diagnostics.
31375ce41a5SAli Bahramichomp($Prog = `basename $0`);
31475ce41a5SAli Bahrami
31575ce41a5SAli Bahrami# The onbld_elfmod package is maintained in the same directory as this
31675ce41a5SAli Bahrami# script, and is installed in ../lib/perl. Use the local one if present,
31775ce41a5SAli Bahrami# and the installed one otherwise.
31875ce41a5SAli Bahramimy $moddir = dirname($0);
31975ce41a5SAli Bahrami$moddir = "$moddir/../lib/perl" if ! -f "$moddir/onbld_elfmod.pm";
32075ce41a5SAli Bahramirequire "$moddir/onbld_elfmod.pm";
32175ce41a5SAli Bahrami
32275ce41a5SAli Bahrami# Check that we have arguments.
32375ce41a5SAli Bahrami@SaveArgv = @ARGV;
3245253169eSAli Bahramiif ((getopts('afrs', \%opt) == 0) || (scalar(@ARGV) != 1)) {
32575ce41a5SAli Bahrami	print "usage: $Prog [-frs] file | dir\n";
3265253169eSAli Bahrami	print "\t[-a]\texpand symlink aliases\n";
32775ce41a5SAli Bahrami	print "\t[-f]\tuse file name at mode to speed search\n";
32875ce41a5SAli Bahrami	print "\t[-r]\treport relative paths\n";
32975ce41a5SAli Bahrami	print "\t[-s]\tonly remote sharable (ET_DYN) objects\n";
33075ce41a5SAli Bahrami	exit 1;
33175ce41a5SAli Bahrami}
33275ce41a5SAli Bahrami
33375ce41a5SAli Bahrami%Output = ();
33475ce41a5SAli Bahrami%id_hash = ();
33575ce41a5SAli Bahrami%alias_hash = ();
33675ce41a5SAli Bahrami$HaveElfedit = -x '/usr/bin/elfedit';
33775ce41a5SAli Bahrami
33875ce41a5SAli Bahramimy $Arg = $ARGV[0];
33975ce41a5SAli Bahramimy $Error = 0;
34075ce41a5SAli Bahrami
34175ce41a5SAli BahramiARG: {
34275ce41a5SAli Bahrami	# Process simple files.
34375ce41a5SAli Bahrami	if (-f $Arg) {
34475ce41a5SAli Bahrami		my($RelPath) = $Arg;
34575ce41a5SAli Bahrami
34675ce41a5SAli Bahrami		if ($opt{r}) {
34775ce41a5SAli Bahrami			my $Prefix = $Arg;
34875ce41a5SAli Bahrami
34975ce41a5SAli Bahrami			$Prefix =~ s/(^.*)\/.*$/$1/;
35075ce41a5SAli Bahrami			$Prefix = '.' if ($Prefix eq $Arg);
35175ce41a5SAli Bahrami			print "PREFIX $Prefix\n";
35275ce41a5SAli Bahrami		}
35375ce41a5SAli Bahrami		$RelPath =~ s/^.*\//.\//;
35475ce41a5SAli Bahrami		my ($dev, $ino) = stat(_);
35575ce41a5SAli Bahrami		my $IsSymLink = -l $Arg;
35675ce41a5SAli Bahrami		ProcFile($Arg, $RelPath, 0, $IsSymLink, $dev, $ino);
35775ce41a5SAli Bahrami		next;
35875ce41a5SAli Bahrami	}
35975ce41a5SAli Bahrami
36075ce41a5SAli Bahrami	# Process directories.
36175ce41a5SAli Bahrami	if (-d $Arg) {
36275ce41a5SAli Bahrami		$Arg =~ s/\/$//;
36375ce41a5SAli Bahrami		print "PREFIX $Arg\n" if $opt{r};
36475ce41a5SAli Bahrami		ProcDir($Arg, ".", 0, 0);
36575ce41a5SAli Bahrami		next;
36675ce41a5SAli Bahrami	}
36775ce41a5SAli Bahrami
3685253169eSAli Bahrami	print STDERR "$Prog: not a file or directory: $Arg\n";
36975ce41a5SAli Bahrami	$Error = 1;
37075ce41a5SAli Bahrami}
37175ce41a5SAli Bahrami
37275ce41a5SAli Bahrami# Build a hash, using the primary file name as the key, that has the
37375ce41a5SAli Bahrami# strings for any aliases to that file.
37475ce41a5SAli Bahramimy %alias_text = ();
37575ce41a5SAli Bahramiforeach my $Alias (sort keys %alias_hash) {
37675ce41a5SAli Bahrami	my $id = $alias_hash{$Alias};
37775ce41a5SAli Bahrami	if (defined($id_hash{$id})) {
37875ce41a5SAli Bahrami		my $obj = $id_hash{$id};
37975ce41a5SAli Bahrami		my $str = "ALIAS                   $id_hash{$id}\t$Alias\n";
38075ce41a5SAli Bahrami
38175ce41a5SAli Bahrami		if (defined($alias_text{$obj})) {
38275ce41a5SAli Bahrami			$alias_text{$obj} .= $str;
38375ce41a5SAli Bahrami		} else {
38475ce41a5SAli Bahrami			$alias_text{$obj} = $str;
38575ce41a5SAli Bahrami		}
38675ce41a5SAli Bahrami	}
38775ce41a5SAli Bahrami}
38875ce41a5SAli Bahrami
38975ce41a5SAli Bahrami# Output the main files sorted by name. Place the alias lines immediately
39075ce41a5SAli Bahrami# following each main file.
39175ce41a5SAli Bahramiforeach my $Path (sort keys %Output) {
39275ce41a5SAli Bahrami	print $Output{$Path};
39375ce41a5SAli Bahrami	print $alias_text{$Path} if defined($alias_text{$Path});
39475ce41a5SAli Bahrami}
39575ce41a5SAli Bahrami
39675ce41a5SAli Bahramiexit $Error;
397