#!/bin/ksh
# @(#) frcp.ksh 2.2 93/11/14
# 92/06/29 john h. dubois iii (john@armory.com)
# 92/10/14 Cleaned up, improved, added -d and -r options
# 92/11/11 Made work with a dest of '.'
# 93/07/09 Added -l and -n options, & login as anonymous if no .netrc entry
# 93/11/14 Use either passwd or password in .netrc, since ftp does.

# frcp: ftp front end with rcp-like syntax.
# Note: requires any machine names given to be listed with
#       user and password in .netrc

alias istrue="test 0 -ne"
alias isfalse="test 0 -eq"

# For each filename given, put the filename in filename[n]
# and the machine it is on in machine[n].
function SplitNames {
    typeset file
    typeset -i i=1

    unset filename[*] machine[*]
    for file; do
	if [[ "$file" = *:* ]]; then
	    machine[i]=${file%%:*}
	else
	    machine[i]=$LocalMach
	fi
	filename[i]=${file#*:}
	let i+=1
    done
}

function verboseprint {
    print -u1 "$*"
    print -u2 "$*"
}

function MakeDir {
    OFS=$IFS
    typeset IFS=/ dir component

    [[ $1 != /* ]] && dir=.
    set -- $1
    IFS=$OFS
    for component; do
	dir=$dir/$component
	if [ ! -d "$dir" ]; then
	    if mkdir "$dir"; then :; else
		print -u2 "Could not make directory $dir."
		return 1
	    fi
	fi
    done
    return 0
}

# CopyFiles: issue ftp(TC) commands to copy files.
# Usage: CopyFiles [sourcemachine:]sourcepath ... [destmachine:]destpath
# Global vars:
# Uses LocalMach (should be nmae of local machine)
# Sets global arrs machine[]/filename[]
function CopyFiles {
    unset machine[*] filename[*]
    SplitNames "$@"	# split names into filename[1..n] and machine[1..n]
    typeset DestMach=${machine[$#]}	# Machine to copy files to
    typeset DestPath=${filename[$#]} 	# Destination file/dir
    unset machine[$#] filename[$#]
    [ -z "$DestPath" ] && DestPath=.	# dest was given as machine:

    # Try to determine if destination should be a directory
    # so that it can be forced to be a directory.
    [[ $DestPath != */ &&	# don't add / if it already ends in /
    ( $# -gt 2 ||	# if more than two args given, last must be a dir
    # If dest in on local machine, we can check whether it is a directory
    $DestMach = $LocalMach && -d $DestPath || 
    # If dest ends with . or .., it is a directory
    $DestPath = ?(*/)@(.|..) ) ]] && DestPath=$DestPath/

    # If one of the above tests made us think dest is a directory,
    # but it isn't, complain
    if [[ ( "$DestPath" = */ ) && 
    ( "$DestMach" = $LocalMach ) && ! -d "$DestPath" ]];
    then
	print -u2 "Destination is not a directory."
	exit
    fi
    DoCopy "$DestMach" "$DestPath"
}

# Usage: OpenMachine machine-name
# Emits login sequence or doesn't, depending on .netrc file and global
# variables anon and noanon
function OpenMachine {
    typeset machine=$1 netrc=$HOME/.netrc user= password=

    if isfalse anon && [ -r $netrc ]; then
	awk '
	/machine (.* )?'"$machine"'($| )/,/^ *$/ {
	    Fields[$1] = $2
	    if ("passwd" in Fields)
		Fields["password"] = Fields["passwd"]
	    if ("login" in Fields && "password" in Fields) {
		print Fields["login"] " " Fields["password"]
		exit
	    }
	}
	' $netrc | read user password
    fi
    if [ -z "$password" ]; then
	if istrue noanon; then
	    print -u2 "No .netrc entry for machine $machine"
	    exit 1
	fi
	user=anonymous
	password=$USER@$LocalMach
    fi
    verboseprint open $machine
    print -u2 user $user "*******"
    print user $user $password
}

# Usage: DoCopy destination-machine destination-path
# Copies the files in global arrs machine[]/filename[] to the given dest
# Global vars:
# Uses machine[], filename[], LocalMach, check
function DoCopy {
    typeset DestMach=$1
    typeset DestPath=$2
    typeset OpenMach	# Machine that connection is currently open to
    typeset OWD=$PWD SourceMach SourceFile
    typeset -i i=1
    typeset FileName

    while [ i -le ${#machine[*]} ]; do
	istrue check && verboseprint "runique"

	SourceMach=${machine[i]}
	SourceFile=${filename[i]}

	DestFile=$DestPath
	# if DestPath is a dir,
	# add source filename to it without source path
	[[ "$DestFile" = */ ]] && DestFile=$DestFile${SourceFile##*/}

	if [ $SourceMach = $LocalMach ]; then
	    if [ $DestMach != "$OpenMach" ]; then
		OpenMachine $DestMach
		OpenMach=$DestMach
	    fi
	    verboseprint put $SourceFile $DestFile
	elif [ $DestMach = $LocalMach ]; then
	    if istrue check && [ -f "$DestFile" ]; then
		print -u2 "$DestFile already exists."
		continue
	    fi
	    # If destination is on local machine,
	    # the dest will be a full dir/filename
	    if istrue createdirs; then
		MakeDir "${DestFile%/*}" || continue
	    fi
	    if [ $SourceMach != "$OpenMach" ]; then
		OpenMachine $SourceMach
		OpenMach=$SourceMach
	    fi
	    # If source filename has wildcards ([, ], *, ?) do an mget
	    if [[ "$SourceFile" = *[][*?]* ]]; then
		verboseprint lcd "$DestFile"
		verboseprint mget "$SourceFile"
		verboseprint lcd $OWD
	    else
		verboseprint get "$SourceFile" "$DestFile"
	    fi
	else
	    print -u2  "Neither source machine \"$SourceMach\" "\
"nor destination machine \"$DestMach\" is local."
	fi
	let i+=1
    done
}

# Start of main program
name=${0##*/}

if [ "$1" = -h ]; then
    echo \
"$name: do ftp transfers using rcp-style parameters.
Usage: $name <source> <destpath>   or   $name <source> [<source> ...] <destdir>
At least one of <source> and <destpath> must be the local system.
A remote filename is given as machinename:filename
If remote filenames contain wildcards, they will be globbed on the remote
machine.  Make sure they are quoted when $name is invoked.
If the invoking user's .netrc file (see ftp(TC)) contains an entry for the
remote system with a login and password supplied, $name will log in using
the given login and password.  If not, $name will login in as user
anonymous and with the user@localsystem as the password.
Options:
-c: check: do not overwrite files.
-d: create directories as needed.
-f: force: overwrite files (default).
-h: print this help.
-l: fail if there is no entry with login and password for the remote system,
    instead of logging in as anonymous.
-n: log in as anonymous even if there is an entry for the remote system in
    the user's .netrc file.
-r: read source/dest filename pairs from the standard input,
    one pair per line, and copy files accordingly."
    exit 0
fi

typeset -i check=0 createdirs=0 readinput=0 anon=0 noanon=0

while getopts :cdflnr Option
do
    case "$Option" in
    c) check=1;;
    d) createdirs=1;;
    f) check=0;;
    l) noanon=1;;
    n) anon=1;;
    r) readinput=1;;
    \?) echo "$OPTARG: invalid option."; exit 1;;
    esac
done

shift $((OPTIND-1))

LocalMach=`uname -n` 

if istrue readinput; then
    while read line; do
	CopyFiles $line
    done | ftp -n
else
    if [ $# -lt 2 ]; then
	print -u2 "$name: Not enough arguments.  Use -h for help."
	exit
    fi
    CopyFiles "$@" | ftp -n
fi
