1*75ce41a5SAli Bahrami#!/usr/bin/perl -w 2*75ce41a5SAli Bahrami# 3*75ce41a5SAli Bahrami# CDDL HEADER START 4*75ce41a5SAli Bahrami# 5*75ce41a5SAli Bahrami# The contents of this file are subject to the terms of the 6*75ce41a5SAli Bahrami# Common Development and Distribution License (the "License"). 7*75ce41a5SAli Bahrami# You may not use this file except in compliance with the License. 8*75ce41a5SAli Bahrami# 9*75ce41a5SAli Bahrami# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*75ce41a5SAli Bahrami# or http://www.opensolaris.org/os/licensing. 11*75ce41a5SAli Bahrami# See the License for the specific language governing permissions 12*75ce41a5SAli Bahrami# and limitations under the License. 13*75ce41a5SAli Bahrami# 14*75ce41a5SAli Bahrami# When distributing Covered Code, include this CDDL HEADER in each 15*75ce41a5SAli Bahrami# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*75ce41a5SAli Bahrami# If applicable, add the following below this CDDL HEADER, with the 17*75ce41a5SAli Bahrami# fields enclosed by brackets "[]" replaced with your own identifying 18*75ce41a5SAli Bahrami# information: Portions Copyright [yyyy] [name of copyright owner] 19*75ce41a5SAli Bahrami# 20*75ce41a5SAli Bahrami# CDDL HEADER END 21*75ce41a5SAli Bahrami# 22*75ce41a5SAli Bahrami 23*75ce41a5SAli Bahrami# 24*75ce41a5SAli Bahrami# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25*75ce41a5SAli Bahrami# Use is subject to license terms. 26*75ce41a5SAli Bahrami# 27*75ce41a5SAli Bahrami 28*75ce41a5SAli Bahrami# 29*75ce41a5SAli Bahrami# Find ELF executables and sharable objects 30*75ce41a5SAli Bahrami# 31*75ce41a5SAli Bahrami# This script descends a directory hierarchy and reports the ELF 32*75ce41a5SAli Bahrami# objects found, one object per line of output. 33*75ce41a5SAli Bahrami# 34*75ce41a5SAli Bahrami# find_elf [-frs] path 35*75ce41a5SAli Bahrami# 36*75ce41a5SAli Bahrami# Where path is a file or directory. 37*75ce41a5SAli Bahrami# 38*75ce41a5SAli Bahrami# Each line of output is of the form: 39*75ce41a5SAli Bahrami# 40*75ce41a5SAli Bahrami# ELFCLASS ELFTYPE VERDEF|NOVERDEF relpath 41*75ce41a5SAli Bahrami# 42*75ce41a5SAli Bahrami# where relpath is the path relative to the directory from which the 43*75ce41a5SAli Bahrami# search started. 44*75ce41a5SAli Bahrami 45*75ce41a5SAli Bahramiuse strict; 46*75ce41a5SAli Bahrami 47*75ce41a5SAli Bahramiuse vars qw($Prog %Output @SaveArgv); 48*75ce41a5SAli Bahramiuse vars qw(%opt $HaveElfedit); 49*75ce41a5SAli Bahrami 50*75ce41a5SAli Bahrami# Hashes used to detect aliases --- symlinks that reference a common file 51*75ce41a5SAli Bahrami# 52*75ce41a5SAli Bahrami# id_hash - Maps the unique st_dev/st_ino pair to the real file 53*75ce41a5SAli Bahrami# alias_hash - Maps symlinks to the real file they reference 54*75ce41a5SAli Bahrami# 55*75ce41a5SAli Bahramiuse vars qw(%id_hash %alias_hash); 56*75ce41a5SAli Bahrami 57*75ce41a5SAli Bahramiuse POSIX qw(getenv); 58*75ce41a5SAli Bahramiuse Getopt::Std; 59*75ce41a5SAli Bahramiuse File::Basename; 60*75ce41a5SAli Bahrami 61*75ce41a5SAli Bahrami 62*75ce41a5SAli Bahrami## GetObjectInfo(path) 63*75ce41a5SAli Bahrami# 64*75ce41a5SAli Bahrami# Return a 3 element output array describing the object 65*75ce41a5SAli Bahrami# given by path. The elements of the array contain: 66*75ce41a5SAli Bahrami# 67*75ce41a5SAli Bahrami# Index Meaning 68*75ce41a5SAli Bahrami# ----------------------------------------------- 69*75ce41a5SAli Bahrami# 0 ELFCLASS of object (0 if not an ELF object) 70*75ce41a5SAli Bahrami# 1 Type of object (NONE if not an ELF object) 71*75ce41a5SAli Bahrami# 2 VERDEF if object defines versions, NOVERDEF otherwise 72*75ce41a5SAli Bahrami# 73*75ce41a5SAli Bahramisub GetObjectInfo { 74*75ce41a5SAli Bahrami my $path = $_[0]; 75*75ce41a5SAli Bahrami 76*75ce41a5SAli Bahrami # If elfedit is available, we use it to obtain the desired information 77*75ce41a5SAli Bahrami # by executing three commands in order, to produce a 0, 2, or 3 78*75ce41a5SAli Bahrami # element output array. 79*75ce41a5SAli Bahrami # 80*75ce41a5SAli Bahrami # Command Meaning 81*75ce41a5SAli Bahrami # ----------------------------------------------- 82*75ce41a5SAli Bahrami # ehdr:ei_class ELFCLASS of object 83*75ce41a5SAli Bahrami # ehdr:ei_e_type Type of object 84*75ce41a5SAli Bahrami # dyn:tag verdef Address of verdef items 85*75ce41a5SAli Bahrami # 86*75ce41a5SAli Bahrami # We discard stderr, and simply examine the resulting array to 87*75ce41a5SAli Bahrami # determine the situation: 88*75ce41a5SAli Bahrami # 89*75ce41a5SAli Bahrami # # Array Elements Meaning 90*75ce41a5SAli Bahrami # ----------------------------------------------- 91*75ce41a5SAli Bahrami # 0 File is not ELF object 92*75ce41a5SAli Bahrami # 2 Object with no versions (no VERDEF) 93*75ce41a5SAli Bahrami # 3 Object that has versions 94*75ce41a5SAli Bahrami if ($HaveElfedit) { 95*75ce41a5SAli Bahrami my $ecmd = "elfedit -r -o simple -e ehdr:ei_class " . 96*75ce41a5SAli Bahrami "-e ehdr:e_type -e 'dyn:tag verdef'"; 97*75ce41a5SAli Bahrami my @Elf = split(/\n/, `$ecmd $path 2>/dev/null`); 98*75ce41a5SAli Bahrami 99*75ce41a5SAli Bahrami my $ElfCnt = scalar @Elf; 100*75ce41a5SAli Bahrami 101*75ce41a5SAli Bahrami # Return ET_NONE array if not an ELF object 102*75ce41a5SAli Bahrami return (0, 'NONE', 'NOVERDEF') if ($ElfCnt == 0); 103*75ce41a5SAli Bahrami 104*75ce41a5SAli Bahrami # Otherwise, convert the result to standard form 105*75ce41a5SAli Bahrami $Elf[0] =~ s/^ELFCLASS//; 106*75ce41a5SAli Bahrami $Elf[1] =~ s/^ET_//; 107*75ce41a5SAli Bahrami $Elf[2] = ($ElfCnt == 3) ? 'VERDEF' : 'NOVERDEF'; 108*75ce41a5SAli Bahrami return @Elf; 109*75ce41a5SAli Bahrami } 110*75ce41a5SAli Bahrami 111*75ce41a5SAli Bahrami # For older platforms, we use elfdump to get the desired information. 112*75ce41a5SAli Bahrami my @Elf = split(/\n/, `elfdump -ed $path 2>&1`); 113*75ce41a5SAli Bahrami my $Header = 'None'; 114*75ce41a5SAli Bahrami my $Verdef = 'NOVERDEF'; 115*75ce41a5SAli Bahrami my ($Class, $Type); 116*75ce41a5SAli Bahrami 117*75ce41a5SAli Bahrami foreach my $Line (@Elf) { 118*75ce41a5SAli Bahrami # If we have an invalid file type (which we can tell from the 119*75ce41a5SAli Bahrami # first line), or we're processing an archive, bail. 120*75ce41a5SAli Bahrami if ($Header eq 'None') { 121*75ce41a5SAli Bahrami if (($Line =~ /invalid file/) || 122*75ce41a5SAli Bahrami ($Line =~ /$path(.*):/)) { 123*75ce41a5SAli Bahrami return (0, 'NONE', 'NOVERDEF'); 124*75ce41a5SAli Bahrami } 125*75ce41a5SAli Bahrami } 126*75ce41a5SAli Bahrami 127*75ce41a5SAli Bahrami if ($Line =~ /^ELF Header/) { 128*75ce41a5SAli Bahrami $Header = 'Ehdr'; 129*75ce41a5SAli Bahrami next; 130*75ce41a5SAli Bahrami } 131*75ce41a5SAli Bahrami 132*75ce41a5SAli Bahrami if ($Line =~ /^Dynamic Section/) { 133*75ce41a5SAli Bahrami $Header = 'Dyn'; 134*75ce41a5SAli Bahrami next; 135*75ce41a5SAli Bahrami } 136*75ce41a5SAli Bahrami 137*75ce41a5SAli Bahrami if ($Header eq 'Ehdr') { 138*75ce41a5SAli Bahrami if ($Line =~ /e_type:\s*ET_([^\s]+)/) { 139*75ce41a5SAli Bahrami $Type = $1; 140*75ce41a5SAli Bahrami next; 141*75ce41a5SAli Bahrami } 142*75ce41a5SAli Bahrami if ($Line =~ /ei_class:\s+ELFCLASS(\d+)/) { 143*75ce41a5SAli Bahrami $Class = $1; 144*75ce41a5SAli Bahrami next; 145*75ce41a5SAli Bahrami } 146*75ce41a5SAli Bahrami next; 147*75ce41a5SAli Bahrami } 148*75ce41a5SAli Bahrami 149*75ce41a5SAli Bahrami if (($Header eq 'Dyn') && 150*75ce41a5SAli Bahrami ($Line =~ /^\s*\[\d+\]\s+VERDEF\s+/)) { 151*75ce41a5SAli Bahrami $Verdef = 'VERDEF'; 152*75ce41a5SAli Bahrami next; 153*75ce41a5SAli Bahrami } 154*75ce41a5SAli Bahrami } 155*75ce41a5SAli Bahrami return ($Class, $Type, $Verdef); 156*75ce41a5SAli Bahrami} 157*75ce41a5SAli Bahrami 158*75ce41a5SAli Bahrami 159*75ce41a5SAli Bahrami## ProcFile(FullPath, RelPath, AliasedPath, IsSymLink, dev, ino) 160*75ce41a5SAli Bahrami# 161*75ce41a5SAli Bahrami# Determine whether this a ELF dynamic object and if so, add a line 162*75ce41a5SAli Bahrami# of output for it to @Output describing it. 163*75ce41a5SAli Bahrami# 164*75ce41a5SAli Bahrami# entry: 165*75ce41a5SAli Bahrami# FullPath - Fully qualified path 166*75ce41a5SAli Bahrami# RelPath - Path relative to starting root directory 167*75ce41a5SAli Bahrami# AliasedPath - True if RelPath contains a symlink directory component. 168*75ce41a5SAli Bahrami# Such a path represents an alias to the same file found 169*75ce41a5SAli Bahrami# completely via actual directories. 170*75ce41a5SAli Bahrami# IsSymLink - True if basename (final component) of path is a symlink. 171*75ce41a5SAli Bahrami# 172*75ce41a5SAli Bahramisub ProcFile { 173*75ce41a5SAli Bahrami my($FullPath, $RelPath, $AliasedPath, $IsSymLink, $dev, $ino) = @_; 174*75ce41a5SAli Bahrami my(@Elf, @Pvs, @Pvs_don, @Vers, %TopVer); 175*75ce41a5SAli Bahrami my($Aud, $Max, $Priv, $Pub, $ElfCnt, $Val, $Ttl, $NotPlugin); 176*75ce41a5SAli Bahrami 177*75ce41a5SAli Bahrami my $uniqid = sprintf("%llx-%llx", $dev, $ino); 178*75ce41a5SAli Bahrami 179*75ce41a5SAli Bahrami # Remove ./ from front of relative path 180*75ce41a5SAli Bahrami $RelPath =~ s/^\.\///; 181*75ce41a5SAli Bahrami 182*75ce41a5SAli Bahrami my $name = $opt{r} ? $RelPath : $FullPath; 183*75ce41a5SAli Bahrami 184*75ce41a5SAli Bahrami # If this is a symlink, or the path contains a symlink, put it in 185*75ce41a5SAli Bahrami # the alias hash for later analysis. We do this before testing to 186*75ce41a5SAli Bahrami # see if it is an ELF file, because that's a relatively expensive 187*75ce41a5SAli Bahrami # test. The tradeoff is that the alias hash will contain some files 188*75ce41a5SAli Bahrami # we don't care about. That is a small cost. 189*75ce41a5SAli Bahrami if ($IsSymLink || $AliasedPath) { 190*75ce41a5SAli Bahrami $alias_hash{$name} = $uniqid; 191*75ce41a5SAli Bahrami return; 192*75ce41a5SAli Bahrami } 193*75ce41a5SAli Bahrami 194*75ce41a5SAli Bahrami # Obtain the ELF information for this object. 195*75ce41a5SAli Bahrami @Elf = GetObjectInfo($FullPath); 196*75ce41a5SAli Bahrami 197*75ce41a5SAli Bahrami # Return quietly if: 198*75ce41a5SAli Bahrami # - Not an executable or sharable object 199*75ce41a5SAli Bahrami # - An executable, but the -s option was used. 200*75ce41a5SAli Bahrami if ((($Elf[1] ne 'EXEC') && ($Elf[1] ne 'DYN')) || 201*75ce41a5SAli Bahrami (($Elf[1] eq 'EXEC') && $opt{s})) { 202*75ce41a5SAli Bahrami return; 203*75ce41a5SAli Bahrami } 204*75ce41a5SAli Bahrami 205*75ce41a5SAli Bahrami $Output{$name} = sprintf("OBJECT %2s %-4s %-8s %s\n", 206*75ce41a5SAli Bahrami $Elf[0], $Elf[1], $Elf[2], $name); 207*75ce41a5SAli Bahrami 208*75ce41a5SAli Bahrami # Remember it for later alias analysis 209*75ce41a5SAli Bahrami $id_hash{$uniqid} = $name; 210*75ce41a5SAli Bahrami} 211*75ce41a5SAli Bahrami 212*75ce41a5SAli Bahrami 213*75ce41a5SAli Bahrami## ProcDir(FullPath, RelPath, AliasedPath, SelfSymlink) 214*75ce41a5SAli Bahrami# 215*75ce41a5SAli Bahrami# Recursively search directory for dynamic ELF objects, calling 216*75ce41a5SAli Bahrami# ProcFile() on each one. 217*75ce41a5SAli Bahrami# 218*75ce41a5SAli Bahrami# entry: 219*75ce41a5SAli Bahrami# FullPath - Fully qualified path 220*75ce41a5SAli Bahrami# RelPath - Path relative to starting root directory 221*75ce41a5SAli Bahrami# AliasedPath - True if RelPath contains a symlink directory component. 222*75ce41a5SAli Bahrami# Such a path represents an alias to the same file found 223*75ce41a5SAli Bahrami# completely via actual directories. 224*75ce41a5SAli Bahrami# SelfSymlink - True (1) if the last segment in the path is a symlink 225*75ce41a5SAli Bahrami# that points at the same directory (i.e. 32->.). If SelfSymlink 226*75ce41a5SAli Bahrami# is True, ProcDir() examines the given directory for objects, 227*75ce41a5SAli Bahrami# but does not recurse past it. This captures the aliases for 228*75ce41a5SAli Bahrami# those objects, while avoiding entering a recursive loop, 229*75ce41a5SAli Bahrami# or generating nonsensical paths (i.e., 32/amd64/...). 230*75ce41a5SAli Bahrami# 231*75ce41a5SAli Bahramisub ProcDir { 232*75ce41a5SAli Bahrami my($FullDir, $RelDir, $AliasedPath, $SelfSymlink) = @_; 233*75ce41a5SAli Bahrami my($NewFull, $NewRel, $Entry); 234*75ce41a5SAli Bahrami 235*75ce41a5SAli Bahrami # Open the directory and read each entry, omit files starting with "." 236*75ce41a5SAli Bahrami if (opendir(DIR, $FullDir)) { 237*75ce41a5SAli Bahrami foreach $Entry (readdir(DIR)) { 238*75ce41a5SAli Bahrami 239*75ce41a5SAli Bahrami if ($Entry =~ /^\./) { 240*75ce41a5SAli Bahrami next; 241*75ce41a5SAli Bahrami } 242*75ce41a5SAli Bahrami $NewFull = join('/', $FullDir, $Entry); 243*75ce41a5SAli Bahrami 244*75ce41a5SAli Bahrami # We need to follow symlinks in order to capture 245*75ce41a5SAli Bahrami # all possible aliases for each object. However, 246*75ce41a5SAli Bahrami # symlinks that point back at the same directory 247*75ce41a5SAli Bahrami # (e.g. 32->.) must be flagged via the SelfSymlink 248*75ce41a5SAli Bahrami # argument to our recursive self in order to avoid 249*75ce41a5SAli Bahrami # taking it more than one level down. 250*75ce41a5SAli Bahrami my $RecurseAliasedPath = $AliasedPath; 251*75ce41a5SAli Bahrami my $RecurseSelfSymlink = 0; 252*75ce41a5SAli Bahrami my $IsSymLink = -l $NewFull; 253*75ce41a5SAli Bahrami if ($IsSymLink) { 254*75ce41a5SAli Bahrami my $trans = readlink($NewFull); 255*75ce41a5SAli Bahrami 256*75ce41a5SAli Bahrami $trans =~ s/\/*$//; 257*75ce41a5SAli Bahrami $RecurseSelfSymlink = 1 if $trans eq '.'; 258*75ce41a5SAli Bahrami $RecurseAliasedPath = 1; 259*75ce41a5SAli Bahrami } 260*75ce41a5SAli Bahrami 261*75ce41a5SAli Bahrami if (!stat($NewFull)) { 262*75ce41a5SAli Bahrami next; 263*75ce41a5SAli Bahrami } 264*75ce41a5SAli Bahrami $NewRel = join('/', $RelDir, $Entry); 265*75ce41a5SAli Bahrami 266*75ce41a5SAli Bahrami # Descend into and process any directories. 267*75ce41a5SAli Bahrami if (-d _) { 268*75ce41a5SAli Bahrami # If we have recursed here via a $SelfSymlink, 269*75ce41a5SAli Bahrami # then do not persue directories. We only 270*75ce41a5SAli Bahrami # want to find objects in the same directory 271*75ce41a5SAli Bahrami # via that link. 272*75ce41a5SAli Bahrami next if $SelfSymlink; 273*75ce41a5SAli Bahrami 274*75ce41a5SAli Bahrami ProcDir($NewFull, $NewRel, $RecurseAliasedPath, 275*75ce41a5SAli Bahrami $RecurseSelfSymlink); 276*75ce41a5SAli Bahrami next; 277*75ce41a5SAli Bahrami } 278*75ce41a5SAli Bahrami 279*75ce41a5SAli Bahrami # In fast mode, we skip objects unless they end with 280*75ce41a5SAli Bahrami # a .so extension, or are executable. We touch 281*75ce41a5SAli Bahrami # considerably fewer files this way. 282*75ce41a5SAli Bahrami if ($opt{f} && !($Entry =~ /\.so$/) && 283*75ce41a5SAli Bahrami !($Entry =~ /\.so\./) && 284*75ce41a5SAli Bahrami ($opt{s} || (! -x _))) { 285*75ce41a5SAli Bahrami next; 286*75ce41a5SAli Bahrami } 287*75ce41a5SAli Bahrami 288*75ce41a5SAli Bahrami # Process any standard files. 289*75ce41a5SAli Bahrami if (-f _) { 290*75ce41a5SAli Bahrami my ($dev, $ino) = stat(_); 291*75ce41a5SAli Bahrami ProcFile($NewFull, $NewRel, $AliasedPath, 292*75ce41a5SAli Bahrami $IsSymLink, $dev, $ino); 293*75ce41a5SAli Bahrami next; 294*75ce41a5SAli Bahrami } 295*75ce41a5SAli Bahrami 296*75ce41a5SAli Bahrami } 297*75ce41a5SAli Bahrami closedir(DIR); 298*75ce41a5SAli Bahrami } 299*75ce41a5SAli Bahrami} 300*75ce41a5SAli Bahrami 301*75ce41a5SAli Bahrami 302*75ce41a5SAli Bahrami# ----------------------------------------------------------------------------- 303*75ce41a5SAli Bahrami 304*75ce41a5SAli Bahrami# Establish a program name for any error diagnostics. 305*75ce41a5SAli Bahramichomp($Prog = `basename $0`); 306*75ce41a5SAli Bahrami 307*75ce41a5SAli Bahrami# The onbld_elfmod package is maintained in the same directory as this 308*75ce41a5SAli Bahrami# script, and is installed in ../lib/perl. Use the local one if present, 309*75ce41a5SAli Bahrami# and the installed one otherwise. 310*75ce41a5SAli Bahramimy $moddir = dirname($0); 311*75ce41a5SAli Bahrami$moddir = "$moddir/../lib/perl" if ! -f "$moddir/onbld_elfmod.pm"; 312*75ce41a5SAli Bahramirequire "$moddir/onbld_elfmod.pm"; 313*75ce41a5SAli Bahrami 314*75ce41a5SAli Bahrami# Check that we have arguments. 315*75ce41a5SAli Bahrami@SaveArgv = @ARGV; 316*75ce41a5SAli Bahramiif ((getopts('frs', \%opt) == 0) || (scalar(@ARGV) != 1)) { 317*75ce41a5SAli Bahrami print "usage: $Prog [-frs] file | dir\n"; 318*75ce41a5SAli Bahrami print "\t[-f]\tuse file name at mode to speed search\n"; 319*75ce41a5SAli Bahrami print "\t[-r]\treport relative paths\n"; 320*75ce41a5SAli Bahrami print "\t[-s]\tonly remote sharable (ET_DYN) objects\n"; 321*75ce41a5SAli Bahrami exit 1; 322*75ce41a5SAli Bahrami} 323*75ce41a5SAli Bahrami 324*75ce41a5SAli Bahrami%Output = (); 325*75ce41a5SAli Bahrami%id_hash = (); 326*75ce41a5SAli Bahrami%alias_hash = (); 327*75ce41a5SAli Bahrami$HaveElfedit = -x '/usr/bin/elfedit'; 328*75ce41a5SAli Bahrami 329*75ce41a5SAli Bahramimy $Arg = $ARGV[0]; 330*75ce41a5SAli Bahramimy $Error = 0; 331*75ce41a5SAli Bahrami 332*75ce41a5SAli BahramiARG: { 333*75ce41a5SAli Bahrami # Process simple files. 334*75ce41a5SAli Bahrami if (-f $Arg) { 335*75ce41a5SAli Bahrami my($RelPath) = $Arg; 336*75ce41a5SAli Bahrami 337*75ce41a5SAli Bahrami if ($opt{r}) { 338*75ce41a5SAli Bahrami my $Prefix = $Arg; 339*75ce41a5SAli Bahrami 340*75ce41a5SAli Bahrami $Prefix =~ s/(^.*)\/.*$/$1/; 341*75ce41a5SAli Bahrami $Prefix = '.' if ($Prefix eq $Arg); 342*75ce41a5SAli Bahrami print "PREFIX $Prefix\n"; 343*75ce41a5SAli Bahrami } 344*75ce41a5SAli Bahrami $RelPath =~ s/^.*\//.\//; 345*75ce41a5SAli Bahrami my ($dev, $ino) = stat(_); 346*75ce41a5SAli Bahrami my $IsSymLink = -l $Arg; 347*75ce41a5SAli Bahrami ProcFile($Arg, $RelPath, 0, $IsSymLink, $dev, $ino); 348*75ce41a5SAli Bahrami next; 349*75ce41a5SAli Bahrami } 350*75ce41a5SAli Bahrami 351*75ce41a5SAli Bahrami # Process directories. 352*75ce41a5SAli Bahrami if (-d $Arg) { 353*75ce41a5SAli Bahrami $Arg =~ s/\/$//; 354*75ce41a5SAli Bahrami print "PREFIX $Arg\n" if $opt{r}; 355*75ce41a5SAli Bahrami ProcDir($Arg, ".", 0, 0); 356*75ce41a5SAli Bahrami next; 357*75ce41a5SAli Bahrami } 358*75ce41a5SAli Bahrami 359*75ce41a5SAli Bahrami print "$Arg is not a file or directory\n"; 360*75ce41a5SAli Bahrami $Error = 1; 361*75ce41a5SAli Bahrami} 362*75ce41a5SAli Bahrami 363*75ce41a5SAli Bahrami# Build a hash, using the primary file name as the key, that has the 364*75ce41a5SAli Bahrami# strings for any aliases to that file. 365*75ce41a5SAli Bahramimy %alias_text = (); 366*75ce41a5SAli Bahramiforeach my $Alias (sort keys %alias_hash) { 367*75ce41a5SAli Bahrami my $id = $alias_hash{$Alias}; 368*75ce41a5SAli Bahrami if (defined($id_hash{$id})) { 369*75ce41a5SAli Bahrami my $obj = $id_hash{$id}; 370*75ce41a5SAli Bahrami my $str = "ALIAS $id_hash{$id}\t$Alias\n"; 371*75ce41a5SAli Bahrami 372*75ce41a5SAli Bahrami if (defined($alias_text{$obj})) { 373*75ce41a5SAli Bahrami $alias_text{$obj} .= $str; 374*75ce41a5SAli Bahrami } else { 375*75ce41a5SAli Bahrami $alias_text{$obj} = $str; 376*75ce41a5SAli Bahrami } 377*75ce41a5SAli Bahrami } 378*75ce41a5SAli Bahrami} 379*75ce41a5SAli Bahrami 380*75ce41a5SAli Bahrami# Output the main files sorted by name. Place the alias lines immediately 381*75ce41a5SAli Bahrami# following each main file. 382*75ce41a5SAli Bahramiforeach my $Path (sort keys %Output) { 383*75ce41a5SAli Bahrami print $Output{$Path}; 384*75ce41a5SAli Bahrami print $alias_text{$Path} if defined($alias_text{$Path}); 385*75ce41a5SAli Bahrami} 386*75ce41a5SAli Bahrami 387*75ce41a5SAli Bahramiexit $Error; 388