#!/usr/bin/env bash
# netpath -- expand local paths to NFS paths
#
# Default mode:
#     Arguments are assumed to be local paths; will be expanded to absolute
#     paths, prefixed with "/net/<localhost>" and output to stdout.
#
# Command mode aka 'which' mode (-w):
#     Arguments are assumed to be command names; will be looked up in $PATH,
#     prefixed with "/net/<localhost>" and output to stdout.
#
# Execute mode (-x):
#     Arguments are assumed to be a command line; path-like arguments (i.e.
#     absolute "/foo" or dot-relative "./foo", but not any others) will be
#     expanded to absolute paths, prefixed with "/net/<localhost>", and the
#     resulting command executed.
#
# Example:
#
#     Run a remote editor on a local file:
#
#         $ ssh -t foobox vim $(netpath foo.c)
#         $ netpath -x ssh -t foobox vim ./foo.c
#
#         (This is similar in effect to `on foobox vim foo.c`, except without
#         changing the editor's "working directory" into NFS.)
#
#     Execute a local program on a remote system:
#
#         $ ssh foobox $(netpath -w frob)
#         $ ssh foobox $(netpath ~/bin/frob)

. lib.bash || exit

usage() {
	echo "Usage: $progname <path...>"
	echo "       $progname -w <command...>"
	echo "       $progname -x <command> [<args>]"
	echo ""
	echo "In default mode, convert local paths to their /net NFS equivalents."
	echo "With -w specified, look up arguments in PATH before converting."
	echo ""
	echo "In -x mode, execute a command after converting all path-looking"
	echo "arguments. This is generally only useful for running 'ssh' or similar"
	echo "commands that would make a remote host refer back to local paths."
	echo ""
	echo_opt "-H <host>"	"use a different hostname instead of local host"
	echo_opt "-w"		"perform PATH lookup for each arg"
	echo_opt "-x"		"execute a command with expanded paths"
	echo_opt "-n"		"dry-run (only for '-x')"
}

host=$HOSTNAME
n_flag=0
w_flag=0
x_flag=0

while getopts ":H:nwx" OPT; do
	case $OPT in
	H) host=$OPTARG;;
	n) n_flag=1;;
	w) w_flag=1;;
	x) x_flag=1;;
	*) lib:die_getopts;;
	esac
done; shift $((OPTIND-1))

if (( w_flag + x_flag > 1 )); then
	vdie "-w and -x modes are mutually exclusive"
fi
if (( n_flag && !x_flag )); then
	vmsg "-n has no effect in modes other than -x"
fi
if (( ! $# )); then
	vdie "no arguments specified"
fi

args=()
for iarg; do
	if (( w_flag )); then
		arg=$(command -v "$iarg") || vdie "'$iarg' not found in PATH"
	elif (( x_flag )); then
		if [[ $iarg == @(.|./*) ]]; then
			arg=$(realpath -s "$iarg")
		else
			arg=$iarg
		fi
	else
		arg=$(realpath -s "$iarg")
	fi

	if [[ -L "$arg" ]]; then
		# Pre-expand absolute symlink targets as otherwise they would
		# point to the client's local path, making the /net indirection
		# moot.
		target=$(readlink "$arg")
		if [[ $target == /* && $target != /@(afs|n|net)/* ]]; then
			arg=$target
		fi
	fi

	if [[ $arg == /n/*/* ]]; then
		nhost=${arg#/n/}; nhost=${nhost%%/*}
		arg="/net/$nhost$HOME/${arg#/n/*/}"
	elif [[ $arg == /* && $arg != /@(afs|n|net)/* ]]; then
		arg="/net/$host$arg"
	fi
	args+=("$arg")
done

if (( x_flag )); then
	if (( n_flag )); then
		echo "${args[@]@Q}"
	else
		(PS4="+ "; set -x; "${args[@]}")
	fi
else
	printf '%s\n' "${args[@]}"
fi
