#!/bin/sh # symlink-resolve --- dereference symbolic links # Copyright (C) 1993, 1995, 1998, 2000 Noah S. Friedman # Author: Noah Friedman # Created: 1993-02-07 # $Id: symlink-resolve,v 1.10 2000/01/13 19:48:00 friedman Exp $ # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, you can either send email to this # program's maintainer or write to: The Free Software Foundation, # Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA. # Commentary: # Resolve pathnames until there are no more symbolic links. Resulting # pathname may not be a file that actually exists (depending on whether # symlinks point to nonexistent files). # Code: exec ${PERL-perl} -Sx $0 ${1+"$@"} #!perl [perl will skip all lines in this file before this line] use Getopt::Long; use Cwd; my $cwd; # Args: basename, default-directory # # Convert basename to absolute, and canonicalize it. Second arg # default-directory is directory to start with if basename is relative # (does not start with slash); if default-directory is undefined, the # current working directory is used. File name components that are `.' are # removed, and so are file name components followed by `..', along with the # `..' itself; note that these simplifications are done without checking # that the file name actually exists in the file system. sub expand_file_name ($;$) { local $_ = shift; if ($_ !~ m|^/|o) { $cwd = getcwd() unless (defined $cwd); $_ = join ('/', (shift || $cwd), $_); } # These substitutions must be done in loops to handle overlapping `/' # characters in adjacent patterns. s|/\./|/|o while (m|/\./|o); s|//|/|o while (m|//|o); s|/[^/]+/\.\./|/|o while (m|/[^/]+/\.\./|o); s|/[^/]+/\.\.$||o; s|/.$||go; # Eliminate leading `..'. # It may be harmful to do it if the filesystem interprets `/..' as # something not equivalent to `/'. #s|^/\.\./|/|o while (m|^/\.\./|o); return $_; } sub dereference_links ($) { my $file = shift; my @p = split (m|/|, $file); my $link_count = 0; for (my $j = 0; $j <= $#p; $j++) { my $k = join ("/", @p[0 .. $j]); my $orig_component = $k; while (my $l = readlink ($k)) { $k = $l; # Simple way of detecting symlink loops (it unfortunately causes # the system to give up when there are simply too many levels, # even if resolution would eventually occur). This parameter is # adjustable, of course. Most unix kernels allow a depth of 8. if ($link_count++ == 64) { print STDERR "$0: $file: Too many levels of symbolic links\n"; return undef; } } next if ($k eq $orig_component); if (substr ($k, 0, 1) eq "/") { # Absolute link. Trash $p[0]-$p[$j+1] and replace with readlinked # path components. Set $j to -1 so that next iteration of loop # will check array @p from start. splice (@p, 0, $j + 1, split (m|/|, $k)); $j = -1; } else { # Insert partial (relative) path component into array in place of # current element $p[$j] splice (@p, $j, 1, split (m|/|, $k)); $j--; } } join ("/", @p); } sub show_links ($) { my $filelist = shift; my $multip = ($#$filelist > 0); for my $file (@$filelist) { my $linkto = dereference_links ($file); if ($file eq $linkto) { print "$file\n"; } else { $linkto = expand_file_name ($linkto) unless ($nocanonicalizep); print ($multip? "$file -> $linkto\n" : "$linkto\n"); } } } sub usage () { print "Usage: $0 {options} [path1] {path2} {...} Options are: -h, --help You're looking at it. -n, --no-canonicalize Do not canonicalize pathnames by resolving references to \`..', etc.\n"; exit (1); } sub main () { $0 =~ s|.*/||; $nocanonicalizep = 0; Getopt::Long::config ('bundling', 'autoabbrev'); GetOptions ("n|no-canonicalize", \$nocanonicalizep, "h|help", \&usage); show_links (\@ARGV); } main(); # local variables: # mode: perl # eval: (auto-fill-mode 1) # end: # symlink-resolve ends here