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