#!/bin/sh

# seepath - see status of each component of a path
# http://www.cs.duke.edu/~des/scripts.html
# https://kinzler.com/me/home.html#other

#  @(#)	~des/src/seepath/seepath	1.8	98/05/01	01:59:16
#
#  seepath: long list each component of a path
#
# Copyright (c) 1997 by Daniel E. Singer.  All rights reserved.
# Permission is granted to reproduce and distribute this program
# with the following conditions:
#   1) This copyright notice and the author identification below
#      must be left intact in the program and in any copies.
#   2) Any modifications to the program must be clearly identified
#      in the source file.
#
# Written by Daniel E. Singer, Duke Univ. Dept. of Computer Science, 1/96
#
# Modifications:
#
# 2/97, D.Singer
#	added '-L';
#	lots rewritten;
#
# 2/97, D.Singer
#	added '-f';
#
# 12/97, D.Singer
#	more flexible 'ls' testing;
#	new option parsing;
#	changed '-L' to '-F';
#	various cleaning up and commenting;
#
# 12/97, D.Singer
#	added '-h';
#
# 5/98, D.Singer
#	a few little fixes, including some for HP-UX Posix shell;
#

PATH='/bin:/usr/bin:/sbin:/usr/sbin'
export PATH

PROG=`basename $0`
USAGE="
Usage:  $PROG [-f|-F] [-h] [path]

		-f	follow links that occur as the final component
			of the path only;
		-F	list out links in gory detail, ie, follow links
			that occur anywhere in the path; overrides -f;
		-h	print help message and exit;
		path	the file or path to analyze; default is current
			directory;
"
SYNTAX="$PROG: option syntax error."

CPATH=		# used in follow_path() to hold the full path
COMPONENT=	# used in follow_path() to hold each path component

FOLLOW_LINKS=0		# -f flag
REALLY_FOLLOW_LINKS=0	# -F flag

#
# figure out which form of 'ls' to use, we want the user and group to show;
# longer one wins;
#
LS_LD_LEN=`expr "\`ls -ld .\`" : '.*'`
LS_LDG_LEN=`expr "\`ls -ldg .\`" : '.*'`
if [ "$LS_LD_LEN" -gt "$LS_LDG_LEN" ]; then
 	LL="ls -ld"
 	LLF="ls -ldF"
  else
 	LL="ls -ldg"
 	LLF="ls -ldFg"
  fi
#echo "LL=$LL"
#echo "LLF=$LLF"

#
# process command line options
#

# print messages and exit
syntax_error() {
	echo "$USAGE" >&2
	echo "$SYNTAX" >&2
	exit 1
}

# check that an option has an argument
arg_syntax_check() {
	[ "$1" -lt 2 ] && syntax_error
}

while [ "$#" -gt 0 ]; do
	OPT="$1"
	case "$OPT" in
	  # options without arguments
	  -f)
		FOLLOW_LINKS=1
		;;
	  -F)
		REALLY_FOLLOW_LINKS=1
		;;
	  -h)
		echo "$USAGE"
		exit 0
		;;
	#  # options with arguments
	#  -c)
	#	CFLAG=1
	#	arg_syntax_check "$#"
	#	CARG="$2"
	#	shift
	#	;;
	  --)
		shift
		break
		;;
	  # unknown option
	  -?)
		syntax_error
		;;
	  # compound option
	  -??*)
		# break up a compound option
		NEW_OPTS=`nawk 'BEGIN {
			OPT_STR = "'"$OPT"'";
			LEN = length(OPT_STR);
			NEW_OPTS = "";
			STATUS = 0;
			for (POS=2; POS+0 <= LEN; ++POS) {
				OPT = substr(OPT_STR,POS,1);
				if (OPT !~ /[a-zA-Z0-9_]/)
					STATUS = 1;
				NEW_OPTS = NEW_OPTS " -" OPT;
			}
			print NEW_OPTS;
			exit STATUS;
		  }' <&-` || {
			syntax_error
		  }
		shift
		set -- $NEW_OPTS ${1:+"$@"}
		continue
		;;
	  # end of options, just arguments left
	  *)
		break
	  esac
	shift
  done

#
# REALLY_FOLLOW_LINKS overrides FOLLOW_LINKS, and in fact they
# will interfere with each other if both are set;
#
[ "$FOLLOW_LINKS$REALLY_FOLLOW_LINKS" = 11 ] && FOLLOW_LINKS=0


#
# get the object that the symbolic link is pointing to
#
get_symlink() {
	$LL "$1" | sed 's/.* -> //'
}


#
# follow and list each component of a path, possibly recursively;
#
follow_path() {
	CPATH="$1"

	#
	# break up the path into its components
	# by setting them into positional parameters
	#
	IFS_HOLD="$IFS"	# input field separater
	IFS='/'		# input field separater
	set -- X $CPATH	# put path components into the positional parameters
	shift
	IFS="$IFS_HOLD"

	#
	# special case of path beginning with '/';
	# this will be skipped when following relative symbolic links;
	#
	case "$CPATH" in
	  /*)
		$LL /
		cd /
	  esac

	#
	# analyze each path component;
	#
	for COMPONENT do

		#
		# this stanza is added for compatibility by the Posix
		# conformant shell as found on HP-UX 10.20, as pointed out
		# by Bruce Foster of Northwestern University;
		# Bourne shell discards implicit null parameters in the
		# "set" above, the Posix shell does not;
		#
		[ -z "$COMPONENT" ] && {
			shift
			continue
		}

		#
		# list this component
		#
		$LLF "$COMPONENT"

		if [ "$REALLY_FOLLOW_LINKS" = 1 -a -h "$COMPONENT" ]; then
			#
			# follow the symbolic link
			#

			#
			# get the link, and append on the rest of the path
			# we're following;
			#
			COMPONENT=`get_symlink "$COMPONENT"`
			shift
			for C do
				[ -z "$C" ] && {
					shift
					continue
				}
				COMPONENT="$COMPONENT/$C"
			  done

			#
			# recursively follow the link
			#
			follow_path "$COMPONENT"
			break
		  fi

		shift
		[ "$#" = 0 ] && break
		cd "$COMPONENT"
	  done

	if [ "$FOLLOW_LINKS" = 1 ]; then

		#
		# in this case, we're only following the link if it's
		# the last component in the path;
		#

		if [ -h "$COMPONENT" ]; then
			follow_path "`get_symlink \"$COMPONENT\"`"
		  fi
	  fi
}


set -f		# turn off filename generation

#
# adjust the starting point
#
case "$#" in
  0)
	# no arguments means current directory
	INIT_PATH=`pwd`
	;;
  1)
	# the path to analyze
	INIT_PATH="$1"
	case "$INIT_PATH" in
	  /*)
		;;
	  *)
		INIT_PATH="`pwd`/$1"
	  esac
	;;
  *)
	# Oops!
	echo "$PROG: too many arguments." >&2
	echo "$USAGE" >&2
	exit 1
  esac


##
## do it...
##

echo "$PROG: $INIT_PATH"

follow_path "$INIT_PATH"

exit
