#!/bin/akanga -p
#

fn checkarg {
	cac = $1;  can = $2;  shift 2
	if (~ $can -) {
		if (~ $#* 0) {
			echo $program: command needs arguments: $cac $#* $* >[1=2]
			exit 1
			}
	} else if (! ~ $#* $can) {
		echo $program: command needs $can 'argument(s)': $cac >[1=2]
		exit 1
		}

	return 0
	}

fn beverbose {
	if (! ~ $opts -v  &&  ! ~ $silent yes)
		opts = ($opts -v)

	return 0
	}

fn askyesno {
	askdefault = $1;  shift
	while () {
		echo -n $^* ^ ' ' >/dev/tty
		read askanswer
		if (~ $#askanswer 0  ||  ~ $askanswer '') {
			if (! ~ $askdefault -) {
				echo $askdefault
				return
				}
		} else if (~ $askanswer y* Y* n* N*) {
			expr 'strlwr(substr(askanswer, 1, 1))'
			return
			}
		}

	return 1
	}

fn getenv {
	if (~ $#$2 0  ||  ~ $$2 '') {
		echo $1
	} else {
		echo $$2
		}

	}


fn complete {
	completition = `{
		awk '
			BEGIN {
				par = ARGV[1]; ARGV[1] = "";
				while (getline > 0) {
					for (i=1; i <= NF; i++) {
						if ($i in seen)
							continue;

						if (par == $i) {
							printf ("%s\n", par);
							exit (0);
							}

						val[++n] = $i;
						seen[$i] = 1;
						}
					}

				plen = length(par);
				for (i=1; i <= n; i++) {
					if (substr(val[i], 1, plen) == par)
						printf ("%s\n", val[i]);
					}

				exit (0);
				}' $*
		}

	if (~ $#completition 0) {
		echo $1
	} else if (~ $#completition 1) {
		echo $completition
	} else if (! ~ $#completition 0 1) {
		echo $1: not unique: $completition >[1=2]
		exit 1
		}

	return
	}


fn getpath {
	{
		if (~ $#* 0) {
			echo .
		} else if (~ $#* 1) {
			echo $1
		} else if (~ $2 /*) {
			echo $2
		} else {
			echo $1/$2
			}
		} |
	awk '
		BEGIN {
			getline;
			if (substr($1, 1, 1) != "/")
				$1 = "/" $1;

			path = $1;
			for (i=2; i<=NF; i++)
				path = path "/" $i;

			n = split(path, x, "/");
			k = 1;
			for (i=1; i<=n; i++) {
				if (x[i] == ""  ||  x[i] == ".")
					continue;
				else if (x[i] == "..") {
					if (k > 1)
						k--;

					continue;
					}

				x[k++] = x[i];
				}

			path = "";
			for (i=1; i<k; i++)
				path = path "/" x[i];

			if (path == "")
				path = "/";

			print path;
			exit (0);
			}'
	return
	}

fn listserverconf {
	awk '
		BEGIN {
			server = ARGV[1];  ARGV[1] = "";
			while (getline > 0) {
				if ($0 == ""  ||  $1 ~ /^#/  ||  $0 ~ /^[ \t]*$/)
					continue;

				if ($4 == "")
					$4 = ".";

				printf ("%s\t%s\t%s\t%s\t%s\n", $1, $2, $3, $4, $5);
				}

			}' <$FTPS_CONFIG

	return
	}

fn getserverconf {
	listserverconf |
	awk '
		BEGIN {
			FS = "\t";
			server = ARGV[1];  ARGV[1] = "";
			while (getline > 0) {
				if (server == ""  ||  $1 == server) {
					if ($4 == "")
						$4 = ".";

					printf ("%s\t%s\t%s\t%s\t%s\n", $1,
							$2, $3, $4, $5);
					exit (0);
					}
				}

			printf ("%s\n", server == ""? "": "-");
			exit (0);
			}' $*

	return
	}


fn writehistory {
	mktemp temp
	awk '
		BEGIN {
			server = ARGV[1];  dir = ARGV[2];  options = ARGV[3];
			ARGV[1] = ARGV[2] = ARGV[3] = "";
			printf ("%s\t%s\t%s\n", server, dir, options);
			}

		/[^ \t]/ {
			if ($1 == server)
				next;

			printf ("%s\n", $0);
			}' $* <$FTPS_HISTORY >$temp

	mv $temp $FTPS_HISTORY
	return
	}

fn getlatest {
	awk '
		BEGIN {
			server = ARGV[1];  ARGV[1] = "";
			while (getline > 0) {
				if ($0 ~ /^[ \t]*$/)
					continue;
				else if (server == ""  ||  $1 == server) {
					printf ("%s\t%s\t%s\n", $1, $2, $3);
					exit (0);
					}
				}

			printf ("%s\n", server);
			}' $* <$FTPS_HISTORY

	return
	}



program = `{ basename $0 }

historyname = history
cachename = dir-cache

FTPS_CONFIG = $HOME/.ftps.server
FTPS_DIR = $HOME/.ftps
if (let 'test("-d", FTPS_DIR) == 0') {
	mkdir -m 0700 $FTPS_DIR  ||  exit 1
	}

FTPS_CACHE = $FTPS_DIR/dir-cache
FTPS_HISTORY = $FTPS_DIR/$historyname

tty = $FTPS_SESSION
if (~ $#tty 0) {
	tty = `{ tty }
	if (! ~ $#tty 0)
		let 'tty = basename(tty)'
	}

if (~ $#tty 0  ||  ~ $tty '') {
	echo $program: no session name defined >[1=2]
} else {
	FTPS_CACHE = $FTPS_DIR/$cachename.$tty
	FTPS_HISTORY = $FTPS_DIR/$historyname.$tty
	}


let 'tab = "\t"'

#
# Make sure that some important files exist.
#

if (let 'test("-f", FTPS_HISTORY) == 0')
	touch $FTPS_HISTORY

if (let 'test("-f", FTPS_CONFIG) == 0') {
	touch $FTPS_CONFIG
	chmod 600 $FTPS_CONFIG
	}

#
# Check permissions on our server table.
#

if (let 'substr(stat("-p", FTPS_CONFIG), 3, 2) ^!= "00"') {
	echo $program: wrong permissions on server table: $FTPS_CONFIG >[1=2]
	exit 1
	}


#
# Get the last server and directory from the history.
#

last = ``($tab $ifs) { getlatest }
server = $last(1)
curdir = $last(2)
lastoptions = $last(3)

if (~ $last(3) anon) {
	serveradr = $server
	login = anonymous:root
	homedir = /
#	serveroptions = pasv
} else {

	#
	# Read the server configuration from the server table.
	#

	serverconf = ``($tab $ifs) { getserverconf $server }
	if (! ~ $bqstatus 0) {
		echo $program: configuration error: $server >[1=2]
		exit 1
	} else if (~ $#serverconf 0) {
		echo $program: no server configured, edit server table: $FTPS_CONFIG >[1=2]
		exit 1
	} else if (~ $serverconf -  &&  ! ~ $1 server open .*) {
		echo $program: server not found: $server >[1=2]
		exit 1
		}

	server = $serverconf(1)
	serveradr = $serverconf(2)
	login = $serverconf(3)
	homedir = $serverconf(4)
	serveroptions = $serverconf(5)
	}

if (~ $#curdir 0  ||  ~ $curdir '')
	curdir = $homedir


initvars -s '' autolist glob passive silent verbose
parseopt '*' gqsvyz: $*  ||  exit 1

opts = ()
while (~ $1 -*) {
	switch ($1) {
	case -g
		glob = yes
		shift

	case -q
		autolist = yes
		shift

	case -s
		silent = yes
		shift

	case -v
		verbose = yes
		opts = ($opts -v)
		shift

	case -y
		passive = yes
		opts = ($opts -y)
		shift

	case - --
		shift
		break

	case *
		echo $0: unknown option: $1 >[1=2]
		exit 1
		}
	}


cmdlist = (cat cd cl del edit files get home list ls lsr mkdir open put \
		pwd retr rmdir server site stor view write \
		.edit .lc .lh .save .session .take)
if (~ $#* 0) {
	cmd = server
} else {
#	cmd = $1
	cmd = `{ echo $cmdlist | complete $1 }
	if (! ~ $bqstatus 0)
		exit 1

	shift
	}

if (~ $cmd home) {
	cmd = cd
	* = $homedir
	}


if (~ $glob yes)
	opts = ($opts -g)

if (~ $autolist yes  &&  ~ $cmd del mkdir put rmdir write)
	opts = ($opts -q)

if (~ $serveroptions passive pasv  &&  ! ~ $opts -y)
	opts = ($opts -y)


switch ($cmd) {
case cd cl
	if (~ $#* 0)
		* = .

	if (~ $cmd cl)
		autolist = yes

	dir = `{ getpath $curdir $1 }
	mktemp temp
	if (ftpc $(opts) -ld $login $dir $serveradr >$temp) {
		if (! ~ $silent yes)
			echo $dir

		if (~ $autolist yes)
			cat $temp

		mv $temp $FTPS_CACHE
		writehistory $server $dir $lastoptions
		}

case list ls
	mktemp temp
	dir = `{ getpath $curdir $1 }
	ftpc $opts -ld $login $dir $serveradr >$temp
	if (~ $1 ())
		cp $temp $FTPS_CACHE

	cat $temp

case lc ln cache
	if (~ $#* 0) {
		cat $FTPS_CACHE
	} else {
		cat $FTPS_CACHE |
		awk '
			BEGIN {
				listnames = ENVIRON["cmd"] == "ln";
				for (i=1; i < ARGC; i++) {
					pattern[++n] = ARGV[i];
					ARGV[i] = "";
					}
				}

			/./ {
				for (i=1; i <= n; i++) {
					if ($NF ~ pattern[i]) {
						printf ("%s\n", listnames != 0? $NF: $0);
						next;
						}
					}
				}' $*
		}

case lsr
	mktemp temp
	dir = `{ getpath $curdir $1 }
	ftpc $opts -o '-lR' -ld $login $dir $serveradr >$temp
	cat $temp

case files
	ftpc $opts -c nlist -ld $login $curdir $serveradr $*

case mtime
	ftpc $opts -c mtime -ld $login $curdir $serveradr $*

case pwd
	echo $curdir

case mkdir
	checkarg $cmd - $*
	ftpc $opts -c mkdir -ld $login $curdir $serveradr $*

case rmdir
	checkarg $cmd - $*
	ftpc $opts -c rmdir -ld $login $curdir $serveradr $*

case del
	checkarg $cmd - $*
	ftpc $opts -c del -ld $login $curdir $serveradr $*

case server
	if (~ $#* 0) {
		echo $server $serveradr $curdir
	} else {
		server = `{
				{ cat $FTPS_HISTORY; listserverconf } |
				awk '/./ { print $1 }' |
				complete $1
				}

		if (! ~ $bqstatus 0)
			exit 1

		data = ``($tab $ifs) { getlatest $server }
		writehistory $data
		if (! ~ $#* 0 1)
			$0 cl $2
		}

case get
	checkarg $cmd - $*
	if (! ~ $opts -v  &&  ! ~ $silent yes)
		opts = ($opts -v)

	ftpc $opts -ld $login $curdir $serveradr $*

case retr
	checkarg $cmd 2 $*
	beverbose
	ftpc $opts -c retr -ld $login $curdir $serveradr $*

case cat
	checkarg $cmd 1 $*
	ftpc $opts -p -ld $login $curdir $serveradr $1

case edit
	checkarg $cmd 1 $*
	mktemp temp orig
	if (! ftpc $opts -pc get -ld $login $curdir $serveradr $1 >$temp)
		exit 1

	cp $temp $orig
	editor = `{ getenv vi EDITOR }
	if ($editor $temp  &&  ! cmp -s $temp $orig) {
		ans = `{ askyesno - 'Store file on server?' }
		if (! ~ $ans y) {
			echo File not written.
		} else {
			if (~ $autolist yes)
				opts = ($opts -q)

			ftpc $opts -pc put -ld $login $curdir $serveradr $1 <$temp
			}
		}

case less view
	checkarg $cmd 1 $*
	mktemp temp
	if (! ftpc $opts -pc get -ld $login $curdir $serveradr $1 >$temp)
		exit 1

	pager = `{ getenv less PAGER }
	$pager $temp

case put
	checkarg $cmd - $*
	if (! ~ $opts -v  &&  ! ~ $silent yes)
		opts = ($opts -v)

	ftpc $opts -c put -ld $login $curdir $serveradr $*

case stor
	checkarg $cmd 2 $*
	beverbose
	ftpc $opts -c stor -ld $login $curdir $serveradr $*

case write
	checkarg $cmd 1 $*
	if (! ~ $opts -v  &&  ! ~ $silent yes)
		opts = ($opts -v)

	ftpc $opts -pc put -ld $login $curdir $serveradr $1

case site
	checkarg $cmd - $*
	ftpc $opts -c site -ld $login $curdir $serveradr $^*

case open
	checkarg $cmd 1 $*
	writehistory $1 / anon
	$0 cl .

case help
	echo 'FTP commands'
	echo '  cat <file> - get <file> from server to standard output'
	echo '  cd [<dir>] - change directory'
	echo '  cl [<dir>] - change directory and list'
	echo '  del <file> ... - delete files'
	echo '  edit <file> - edit <file> from server'
	echo '  files <dir> - enumerate files'
	echo '  get <file> ... - get files from the server'
	echo '  home - changes to the configured home directory'
	echo '  list [<dir>] - list files'
	echo '  lsr [<dir>] - list files recursivly'
	echo '  mkdir <dir> ... - create directories'
	echo '  open <server> - open server with anonymous account'
	echo '  put <file> ... - store files on server'
	echo '  pwd - print current working directory'
	echo '  retr <remote> <local> - retrieve file from server'
	echo '  rmdir <dir> ... - remote directories'
	echo '  server [<dir>] - print current server information'
	echo '  site <cmd> <arg> ... - execute server site command'
	echo '  stor <local> <remote> - store file on server'
	echo '  view <file> - get <file> into viewer'
	echo '  write <file> - store standard input in <file> on server'

case .edit
	editor = `{ getenv vi EDITOR }
	$editor $FTPS_CONFIG

case .lh
	cat $FTPS_HISTORY

case .lc
	listserverconf |
	awk '
		BEGIN {
			FS = "\t";
			}

		/./ {
			sub(/:.*$/, "", $3);  gsub(/[ \t]+/, "   ", $0);
			print $0;
			}'

case .man
	man ftps

case .save
	checkarg $cmd - $*
	name = $1;  shift
	let 'now = strftime("%H:%M:%S %d.%m.%y", systime())'
	{ echo
	  echo '# added' $now
	  printf '%s\t%s\t%s\t%s\t%s\n' $name $serveradr $login $curdir $^* } >>$FTPS_CONFIG

case .session
	cd $FTPS_DIR
	if (~ $#* 0) {
		awk '
			BEGIN {
				FS = "\t";
				}

			/./ {
				tty = FILENAME;
				sub(/^.*\./, "", tty);
				printf ("%s %s %s %s\n", tty, (tty == ENVIRON["tty"])? "*": " ", $1, $2);
				nextfile;
				}' `{ ls -t $historyname.* }
		}

case .take
	checkarg $cmd 1 $*
	filename = $FTPS_DIR/$historyname.$1
	if (let 'test("-f", filename) == 0') {
		echo $program: no such session: $1 >[1=2]
		exit 1
		}

	data = ``($tab $ifs) { head -1 $filename }
	if (~ $#data 0 1) {
		echo $program: session data error: $1 >[1=2]
		exit 1
		}

	echo take: $data
	$0 server $data(1)  &&  $0 -q cd $data(2)

case .help
	echo 'Control commands'
	echo '  .edit - edit configuration file'
	echo '  .lc - list configured servers'
	echo '  .lh - print session history'
	echo '  .save <name> - save current location as <name> to configuration'
	echo '  .session - list sessions'
	echo '  .take <tty> - take current location from <tty> session'

case .xx
	exec tmp/ftpi $FTPS_HISTORY $FTPS_CACHE

case *
	echo $program: unknown command: $cmd >[1=2]
	exit 1
	}

exit 0

