[comp.lang.perl] slinky.pl -- a symbolic link walker

vsh@etnibsd.UUCP (Steve Harris) (10/18/90)

Here is a perl script to trace symbolic links.

On our network, with all its NFS mount points and symlinks to get from
one place to another, a file path often has several symlinks in it.
"ls -l" will tell you if the final component is a symlink, but not if
any of the internal pathname components are, nor will it tell you if
the thing pointed to is itself a symlink.  Slinky gives you all this info.

It's also my first script to use splice.  Neat stuff!

Any suggestions/improvements/bug-fixes welcome.


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- cut here =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	slinky.pl
# This archive created: Wed Oct 17 14:09:58 1990
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'slinky.pl'" '(2968 characters)'
if test -f 'slinky.pl'
then
	echo shar: "will not over-write existing file 'slinky.pl'"
else
sed 's/^	X//' << \SHAR_EOF > 'slinky.pl'
	X#!/usr/bin/perl
	X
	X########################################################################
	X# 
	X# slinky.pl -- a symbolic link walker
	X# 
	X# For each file name on the command line, this script will resolve
	X# all symbolic links in the path.  If -v (verbose) option, each step
	X# of the symlink resolution will be displayed.  Uses splice statement.
	X# 
	X# outline of program (for a single path):
	X# 
	X# split the path into array @path
	X# set initial $offset to 1
	X# loop: while (not too many links) {
	X# 	join the first [0..$offset] components into $path
	X# 	if ($path is a link) {
	X# 		read the link into $link
	X# 		if ($link is absolute) {
	X# 			splice $link into start of @path
	X# 			set $offset to 1 (re-start at beginning of path)
	X# 		} else {
	X# 			splice $link into middle of @path
	X# 		}
	X# 	} elsif ($path exists) {
	X# 		increment $offset
	X# 	} else {
	X# 		set $notexist flag and break out of loop
	X# 	}
	X# }
	X#
	X########################################################################
	X
	X
	X# setup program path and name, and usage strings
	X@prog = split(/\//, $0);
	X$prog = $prog[$#prog];
	X$ustr = "usage: $prog [-Uv] file...\n";
	X$Ustr =<<EOF;
	Xwhere
	X	-v	verbose (show each step of symlink resolution)
	X	-U	usage (show this message)
	XEOF
	X
	Xsub usage {
	X	print STDERR $ustr;
	X	print STDERR $Ustr if $_[0];
	X	exit 1;
	X}
	X
	Xwhile ($_ = $ARGV[0], /^-/) {
	X	shift;
	X	if ($_ eq '--')	{ last; }
	X	if (/v/)	{ $verbose++; next; }
	X	if (/U/)	{ &usage(1); }
	X	&usage();
	X}
	X
	X&usage() if $#ARGV < 0;
	X
	X$maxlinks = 32;
	Xwhile ($#ARGV >= 0) {
	X	$_ = shift;
	X	s!^!./! unless m!^\.{0,2}/!;	# relative pathname: start with "./"
	X	print "$_:\n" if $verbose;
	X	@path = split(/\//, $_);
	X	$notexist = 0;
	X	$offset = 1;
	X	for ($nlinks=0; $nlinks<$maxlinks; ) {
	X		last if $offset > $#path;		# done???
	X		$path = join('/', @path[0..$offset]);	# get path head
	X		if ( -l $path ) {			# it's a link:
	X			$nlinks++;
	X			printf "%5d:\t", $nlinks if $verbose;
	X			&printit($offset-1, $offset) if $verbose;
	X			print " -> " if $verbose;
	X			$link = readlink($path);	# get the link
	X			@tmp = split(/\//, $link);
	X			if ($link =~ m!^/!) {
	X				# link is absolute: replace @path head
	X				splice(@path,0,$offset+1,@tmp);
	X				&printit(0,$#tmp,1) if $verbose;
	X				$offset = 1;		# and start at top
	X			} else {
	X				# link is relative: replace @path middle
	X				splice(@path,$offset,1,@tmp);
	X				&printit($offset-1,$offset+$#tmp) if $verbose;
	X			}
	X			print "\n" if $verbose;
	X		} elsif ( -e $path ) {		# not a link
	X			$offset++;		# do next component of @path
	X		} else {
	X			$notexist = 1;		# does not exist
	X			last;
	X		}
	X	}
	X	if ($nlinks >= $maxlinks) {
	X		die "tracelinks: too many links, aborted";
	X	}
	X	print "$_";
	X	print " -> ", join('/',@path) if $nlinks;
	X	print " (does not exist)" if $notexist;
	X	print "\n";
	X	print "\n" if $verbose;
	X}
	X
	Xsub printit {
	X	local($d1,$d2,$abs) = @_;
	X	if ($abs) {
	X		print "{/";
	X	} else {
	X		print join('/', @path[0..$d1]);
	X		print "/{";
	X	}
	X	print join('/', @path[$d1+1..$d2]), "}";
	X	print "/", join('/', @path[$d2+1..$#path]) if $d2 < $#path;
	X}
SHAR_EOF
if test 2968 -ne "`wc -c < 'slinky.pl'`"
then
	echo shar: "error transmitting 'slinky.pl'" '(should have been 2968 characters)'
fi
fi
exit 0
#	End of shell archive
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- cut here =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-- 
Steve Harris - Eaton Corp. - Beverly, MA - uunet!etnibsd!vsh