summary refs log tree commit diff
path: root/Functions/Zftp
diff options
context:
space:
mode:
Diffstat (limited to 'Functions/Zftp')
-rw-r--r--Functions/Zftp/zfanon44
-rw-r--r--Functions/Zftp/zfcd13
-rw-r--r--Functions/Zftp/zfcget31
-rw-r--r--Functions/Zftp/zfclose1
-rw-r--r--Functions/Zftp/zfcput1
-rw-r--r--Functions/Zftp/zfdir35
-rw-r--r--Functions/Zftp/zfgcp34
-rw-r--r--Functions/Zftp/zfget50
-rw-r--r--Functions/Zftp/zfgoto81
-rw-r--r--Functions/Zftp/zfhere1
-rw-r--r--Functions/Zftp/zfls1
-rw-r--r--Functions/Zftp/zfmark51
-rw-r--r--Functions/Zftp/zfopen51
-rw-r--r--Functions/Zftp/zfparams23
-rw-r--r--Functions/Zftp/zfpcp6
-rw-r--r--Functions/Zftp/zfput59
-rw-r--r--Functions/Zftp/zfsession1
-rw-r--r--Functions/Zftp/zfstat30
-rw-r--r--Functions/Zftp/zftransfer7
-rw-r--r--Functions/Zftp/zftype1
-rw-r--r--Functions/Zftp/zfuget51
-rw-r--r--Functions/Zftp/zfuput43
22 files changed, 387 insertions, 228 deletions
diff --git a/Functions/Zftp/zfanon b/Functions/Zftp/zfanon
index d8a9d06a3..5dc22617c 100644
--- a/Functions/Zftp/zfanon
+++ b/Functions/Zftp/zfanon
@@ -2,31 +2,20 @@
 
 emulate -L zsh
 
-local opt optlist once
+[[ $curcontext = :zf* ]] || local curcontext=:zfanon
+local opt opt_1 dir
 
-while [[ $1 = -* ]]; do
-  if [[ $1 = - || $1 = -- ]]; then
-    shift;
-    break;
-  fi
-  optlist=${1#-}
-  for (( i = 1; i <= $#optlist; i++)); do
-    opt=$optlist[$i]
-    case $optlist[$i] in
-      1) once=1
-	 ;;
-      *) print option $opt not recognised >&2
-	 ;;
-    esac
-  done
-  shift
+while getopts :1 opt; do
+  [[ $opt = "?" ]] && print "zfanon: bad option: -$OPTARG" >&2 && return 1
+  eval "opt_$opt=1"
 done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
 
 if [[ -z $EMAIL_ADDR ]]; then
   # Exercise in futility.  There's a poem by Wallace Stevens
   # called something like `N ways of looking at a blackbird',
   # where N is somewhere around 0x14 to 0x18.  Now zftp is
-  # ashamed to prsent `N ways of looking at a hostname'.
+  # ashamed to present `N ways of looking at a hostname'.
   local domain host
   # First, maybe we've already got it.  Zen-like.
   if [[ $HOST = *.* ]]; then
@@ -38,7 +27,8 @@ if [[ -z $EMAIL_ADDR ]]; then
     [[ -n $domain ]] && host=$HOST.$domain
   fi
   # Next, maybe we've got nlsookup.  May not work on LINUX.
-  [[ -z $host ]] && host=$(nslookup $HOST | awk '/Name:/ { print $2 }')
+  [[ -z $host ]] && host=$(nslookup $HOST 2>/dev/null |
+    awk '/Name:/ { print $2 }')
   if [[ -z $host ]]; then
     # we're running out of ideas, but this should work.
     # after all, i wrote it...
@@ -61,10 +51,20 @@ if [[ -z $EMAIL_ADDR ]]; then
   print "Using $EMAIL_ADDR as anonymous FTP password."
 fi
 
-if [[ $once = 1 ]]; then
-  zftp open $1 anonymous $EMAIL_ADDR
+if [[ $1 = */* ]]; then
+  1=${1##ftp://}
+  dir=${1#*/}
+  1=${1%%/*}
+fi
+
+if [[ $opt_1 = 1 ]]; then
+  zftp open $1 anonymous $EMAIL_ADDR || return 1
 else
   zftp params $1 anonymous $EMAIL_ADDR
-  zftp open
+  zftp open || return 1
+fi
+
+if [[ -n $dir ]]; then
+  zfcd $dir
 fi
 # }
diff --git a/Functions/Zftp/zfcd b/Functions/Zftp/zfcd
index b726d9f55..05b7dc998 100644
--- a/Functions/Zftp/zfcd
+++ b/Functions/Zftp/zfcd
@@ -20,11 +20,12 @@
 #     work just as long as the directory structures under the home match.
 
 emulate -L zsh
+[[ $curcontext = :zf* ]] || local curcontext=:zfcd
 
 if [[ $1 = /* ]]; then
-  zfautocheck -dn
+  zfautocheck -dn || return 1
 else
-  zfautocheck -d
+  zfautocheck -d || return 1
 fi
 
 if [[ $1 = $HOME || $1 = $HOME/* ]]; then
@@ -36,7 +37,7 @@ if (( $# == 0 )); then
   set -- '~'
 elif [[ $# -eq 1 && $1 = - ]]; then
   # Emulate `cd -' behaviour.
-  set -- $zflastdir
+  set -- $zfconfig[lastdir_$ZFTP_SESSION]
 elif [[ $# -eq 2 ]]; then
   # Emulate `cd old new' behaviour.
   # We have to find a character not in $1 or $2; ! is a good bet.
@@ -47,6 +48,8 @@ fi
 # if we want to keep it.
 local lastdir=$ZFTP_PWD
 
-zftp cd "$@"  &&  zflastdir=$lastdir
-print $zflastsession
+zftp cd "$@" && [[ $lastdir != $ZFTP_PWD ]] &&
+zfconfig[lastdir_$ZFTP_SESSION]=$lastdir
+
+print $zfconfig[lastloc_$ZFTP_SESSION]
 # }
diff --git a/Functions/Zftp/zfcget b/Functions/Zftp/zfcget
index fd6accfed..476a730a6 100644
--- a/Functions/Zftp/zfcget
+++ b/Functions/Zftp/zfcget
@@ -12,35 +12,22 @@
 
 emulate -L zsh
 
-local loc rem stat=0 optlist opt nglob remlist locst remst
-local tmpfile=${TMPPREFIX}zfcget$$ rstat tsize time
+[[ $curcontext = :zf* ]] || local curcontext=:zfcget
+local loc rem stat=0 opt opt_G opt_t remlist locst remst
+local tmpfile=${TMPPREFIX}zfcget$$ rstat tsize
 
-while [[ $1 = -* ]]; do
-  if [[ $1 = - || $1 = -- ]]; then
-    shift;
-    break;
-  fi
-  optlist=${1#-}
-  for (( i = 1; i <= $#optlist; i++)); do
-    opt=$optlist[$i]
-    case $optlist[$i] in
-      G) nglob=1
-	 ;;
-      t) time=1
-	 ;;
-      *) print option $opt not recognised >&2
-	 ;;
-    esac
-  done
-  shift
+while getopts :Gt opt; do
+  [[ $opt = '?' ]] && print "zfcget: bad option: -$OPTARG" && return 1
+  eval "opt_$opt=1"
 done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
 
 for remlist in $*; do
   # zfcd directory hack to put the front back to ~
   if [[ $remlist = $HOME || $remlist = $HOME/* ]]; then
     remlist="~${remlist#$HOME}"
   fi
-  if [[ $nglob != 1 ]]; then
+  if [[ $opt_G != 1 ]]; then
     zfrglob remlist
   fi
   if (( $#remlist )); then
@@ -73,7 +60,7 @@ for remlist in $*; do
 	  continue
 	else
 	  if zftp getat $rem $locst[1] >>$loc; then
-	    [[ $time = 1 ]] && zfrtime $loc $rem $remst[2]
+	    [[ $opt_t = 1 ]] && zfrtime $loc $rem $remst[2]
 	  else
 	    stat=1
 	  fi
diff --git a/Functions/Zftp/zfclose b/Functions/Zftp/zfclose
index fb49efd51..b1823fb49 100644
--- a/Functions/Zftp/zfclose
+++ b/Functions/Zftp/zfclose
@@ -1,3 +1,4 @@
 # function zfclose {
+[[ $curcontext = :zf* ]] || local curcontext=:zfclose
 zftp close
 # }
diff --git a/Functions/Zftp/zfcput b/Functions/Zftp/zfcput
index fad5c3f86..d8a8a60a3 100644
--- a/Functions/Zftp/zfcput
+++ b/Functions/Zftp/zfcput
@@ -12,6 +12,7 @@
 
 emulate -L zsh
 
+[[ $curcontext = :zf* ]] || local curcontext=:zfcput
 local loc rem stat=0 locst remst offs tailtype
 local tmpfile=${TMPPREFIX}zfcget$$ rstat
 
diff --git a/Functions/Zftp/zfdir b/Functions/Zftp/zfdir
index 55befe000..4818dc973 100644
--- a/Functions/Zftp/zfdir
+++ b/Functions/Zftp/zfdir
@@ -22,7 +22,10 @@
 emulate -L zsh
 setopt extendedglob
 
+[[ $curcontext = :zf* ]] || local curcontext=:zfdir
 local file opt optlist redir i newargs force
+local curdir=$zfconfig[curdir_$ZFTP_SESSION]
+local otherdir=$zfconfig[otherdir_$ZFTP_SESSION]
 
 while [[ $1 = -* ]]; do
   if [[ $1 = - || $1 = -- ]]; then
@@ -40,9 +43,9 @@ while [[ $1 = -* ]]; do
 	 ;;
       f) force=1
 	 ;;
-      d) [[ -n $zfcurdir && -f $zfcurdir ]] && rm -f $zfcurdir
-	 [[ -n $zfotherdir && -f $zfotherdir ]] && rm -f $zfotherdir
-	 zftp_fcache=()
+      d) [[ -n $curdir && -f $curdir ]] && rm -f $curdir
+	 [[ -n $otherdir && -f $otherdir ]] && rm -f $otherdir
+	 zffcache -d
 	 return 0
 	 ;;
     esac
@@ -50,7 +53,7 @@ while [[ $1 = -* ]]; do
   shift
 done
 
-zfautocheck -d
+zfautocheck -d || return 1
 
 # directory hack, see zfcd
 for (( i = 1; i <= $#argv; i++ )); do
@@ -62,26 +65,32 @@ done
 if [[ $# -eq 0 ]]; then
   # Cache it in the current directory file.  This means that repeated
   # calls to zfdir with no arguments always use a cached file.
-  [[ -z $zfcurdir ]] && zfcurdir=${TMPPREFIX}zfcurdir$$
-  file=$zfcurdir
+  if [[ -z $curdir ]]; then
+    curdir=${TMPPREFIX}zfcurdir_${ZFTP_SESSION}_$$
+    zfconfig[curdir_$ZFTP_SESSION]=$curdir
+  fi
+  file=$curdir
 else
   # Last directly looked at was not the current one, or at least
   # had non-standard arguments.
-  [[ -z $zfotherdir ]] && zfotherdir=${TMPPREFIX}zfotherdir$$
-  file=$zfotherdir
+  if [[ -z $otherdir ]]; then
+    otherdir=${TMPPREFIX}zfotherdir_${ZFTP_SESSION}_$$
+    zfconfig[otherdir_$ZFTP_SESSION]=$otherdir
+  fi
+  file=$otherdir
   newargs="$*"
   if [[ -f $file && $redir != 1 && $force -ne 1 ]]; then
     # Don't use the cached file if the arguments changed.
-    [[ $newargs = $zfotherargs ]] || rm -f $file
+    [[ $newargs = $zfconfig[otherargs_$ZFTP_SESSION] ]] || rm -f $file
   fi
-  zfotherargs=$newargs
+  zfconfig[otherargs_$ZFTP_SESSION]=$newargs
 fi
 
 if [[ $force -eq 1 ]]; then
   rm -f $file
   # if it looks like current directory has changed, better invalidate
   # the filename cache, too.
-  (( $# == 0 )) && zftp_fcache=()
+  (( $# == 0 )) && zffcache -d
 fi
 
 if [[ -n $file && -f $file ]]; then
@@ -89,11 +98,11 @@ if [[ -n $file && -f $file ]]; then
 else
   if (zftp test); then
     # Works OK in subshells
-    zftp dir $* | tee $file | eval ${PAGER-:more}
+    zftp dir $* | tee $file | eval ${PAGER:-more}
   else
     # Doesn't work in subshells (IRIX 6.2 --- why?)
     zftp dir $* >$file
-    eval ${PAGER-:more} $file
+    eval ${PAGER:-more} $file
   fi
 fi
 # }
diff --git a/Functions/Zftp/zfgcp b/Functions/Zftp/zfgcp
index 26a08697d..c9045c951 100644
--- a/Functions/Zftp/zfgcp
+++ b/Functions/Zftp/zfgcp
@@ -16,30 +16,17 @@
 
 emulate -L zsh
 
-local opt optlist nglob remlist rem loc time
+[[ $curcontext = :zf* ]] || local curcontext=:zfgcp
+local opt remlist rem loc opt_G opt_t
 integer stat do_close
 
-while [[ $1 == -* ]]; do
-  if [[ $1 == - || $1 == -- ]]; then
-    shift;
-    break;
-  fi
-  optlist=${1#-}
-  for (( i = 1; i <= $#optlist; i++)); do
-    opt=$optlist[$i]
-    case $opt in
-      G) nglob=1
-	 ;;
-      t) time=1
-	 ;;
-      *) print option $opt not recognised >&2
-	 ;;
-    esac
-  done
-  shift
+while getopts :Gt opt; do
+  [[ $opt = '?' ]] && print "zfgcp: bad option: -$OPTARG" >&2 && return 1
+  eval "opt_$opt=1"
 done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
 
-zfautocheck
+zfautocheck || return 1
 
 # hmm, we should really check this after expanding the glob,
 # but we shouldn't expand the last argument remotely anyway.
@@ -59,14 +46,14 @@ if [[ -d $argv[-1] ]]; then
     if [[ $remlist = $HOME || $remlist = $HOME/* ]]; then
       remlist="~${remlist#$HOME}"
     fi
-    if [[ $nglob != 1 ]]; then
+    if [[ $opt_G != 1 ]]; then
       zfrglob remlist
     fi
     if (( $#remlist )); then
       for rem in $remlist; do
 	loc=$dir/${rem:t}
 	if zftp get $rem >$loc; then
-	  [[ $time = 1 ]] && zfrtime $rem $loc
+	  [[ $opt_t = 1 ]] && zfrtime $rem $loc
 	else
 	  stat=1
 	fi
@@ -74,6 +61,9 @@ if [[ -d $argv[-1] ]]; then
     fi
   done
 else
+  if [[ $1 = $HOME || $1 = $HOME/* ]]; then
+    1="~${1#$HOME}"
+  fi
   zftp get $1 >$2 || stat=$?
 fi
 
diff --git a/Functions/Zftp/zfget b/Functions/Zftp/zfget
index 878a36346..ad9658ec3 100644
--- a/Functions/Zftp/zfget
+++ b/Functions/Zftp/zfget
@@ -1,10 +1,16 @@
 # function zfget {
 # Get files from remote server.  Options:
+#   -c   cat: dump files to stdout.
+#          alias zfcat="zfget -c"
+#          zfpage() { zfget -c "$@" | eval $PAGER }
+#        are sensible things to do, but aren't done for you.  Note the
+#        second doesn't work on all OS's.
 #   -G   don't to remote globbing, else do
 #   -t   update the local file times to the same time as the remote.
 #        Currently this only works if you have the `perl' command,
 #        and that perl is version 5 with the standard library.
-#        See the function zfrtime for more gory details.
+#        See the function zfrtime for more gory details.  This has
+#        no effect with the -c option.
 #
 # If the connection is not currently open, try to open it with the current
 # parameters (set by a previous zfopen or zfparams), then close it after
@@ -13,46 +19,38 @@
 
 emulate -L zsh
 
-local loc rem optlist opt nglob remlist time
+[[ $curcontext = :zf* ]] || local curcontext=:zfget
+local loc rem opt remlist opt_G opt_t opt_c
 integer stat do_close
 
-while [[ $1 == -* ]]; do
-  if [[ $1 == - || $1 == -- ]]; then
-    shift;
-    break;
-  fi
-  optlist=${1#-}
-  for (( i = 1; i <= $#optlist; i++)); do
-    opt=$optlist[$i]
-    case $opt in
-      G) nglob=1
-	 ;;
-      t) time=1
-	 ;;
-      *) print option $opt not recognised >&2
-	 ;;
-    esac
-  done
-  shift
+while getopts :Gtc opt; do
+  [[ $opt = '?' ]] && print "zfget: bad option: -$OPTARG" && return 1
+  eval "opt_$opt=1"
 done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
 
-zfautocheck
+zfautocheck || return 1
 
 for remlist in $*; do
   # zfcd directory hack to put the front back to ~
   if [[ $remlist == $HOME || $remlist == $HOME/* ]]; then
     remlist="~${remlist#$HOME}"
   fi
-  if [[ $nglob != 1 ]]; then
+  if [[ $opt_G != 1 ]]; then
     zfrglob remlist
   fi
   if (( $#remlist )); then
     for rem in $remlist; do
-      loc=${rem:t}
-      if zftp get $rem >$loc; then
-	[[ $time = 1 ]] && zfrtime $rem $loc
+      if [[ -n $opt_c ]]; then
+	zftp get $rem
+	stat=$?
       else
-	stat=1
+	loc=${rem:t}
+	if zftp get $rem >$loc; then
+	  [[ $opt_t = 1 ]] && zfrtime $rem $loc
+	else
+	  stat=1
+	fi
       fi
     done
   fi
diff --git a/Functions/Zftp/zfgoto b/Functions/Zftp/zfgoto
index e69de29bb..86942721c 100644
--- a/Functions/Zftp/zfgoto
+++ b/Functions/Zftp/zfgoto
@@ -0,0 +1,81 @@
+# zfgoto bname
+# Go to bookmark bname, a location on a remote FTP host.  Unless
+# this was the last session or is for anonymous FTP, prompt for
+# the user's password.
+#
+# Maybe this should try and look for an appropriate session to use
+# for the transfer.
+
+emulate -L zsh
+setopt extendedglob
+[[ $curcontext = :zf* ]] || local curcontext=:zfgoto
+
+# Set ZFTP_BMFILE if not already set.  This should agree with
+# the corresponding line in zfmark.
+: ${ZFTP_BMFILE:=${ZFDOTDIR:-$HOME}/.zfbkmarks}
+
+typeset -A bkmarks
+local line opt_n opt
+
+while getopts :n opt; do
+  [[ $opt = '?' ]] && print "zfgoto: bad option: -$OPTARG" && return 1
+  eval "opt_$opt=1"
+done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
+
+if (( $# != 1 )); then
+  print "Usage: zfgoto bookmark" >&2
+  return 1
+fi
+
+if [[ -n $opt_n && -f ~/.ncftp/bookmarks ]]; then
+  local oldifs=$IFS
+  IFS=,
+  while read -rA line; do
+    bkmarks[$line[1]]="${line[3]:-anonymous}@${line[2]}:${line[6]}"
+  done < ~/.ncftp/bookmarks
+  IFS=$oldifs
+elif [[ -f $ZFTP_BMFILE ]]; then
+  # read in file:  could optimise this by recording last read time
+  # comparing with file
+  while read -r line; do
+    # ignore blank and comment lines
+    [[ $line = [[:blank:]]# || $line = [[:blank:]]#'#'* ]] && continue
+    bkmarks[${line%% *}]="${line#* }"
+  done <$ZFTP_BMFILE
+fi
+
+line=${bkmarks[$1]}
+
+if [[ -z $line ]]; then
+  print "Bookmark \`$1' not found" >&2
+  return 1
+fi
+
+local user host dir
+user=${line%%@*}
+line=${line#*@}
+host=${line%%:*}
+dir=${line#*:}
+
+if [[ $ZFTP_USER = $user && $ZFTP_HOST = $host ]]; then
+  # We're already there, just change directory
+  zfcd ${dir:-~}
+elif [[ $user = ftp || $user = anonymous ]]; then
+  # Anonymous ftp, so we don't need password etc.
+  zfanon $host && [[ -n $dir ]] && zfcd $dir
+elif [[ $zfconfig[lastloc_$ZFTP_SESSION] = ${host}:* &&
+  $user = $zfconfig[lastuser_$ZFTP_SESSION] ]]; then
+  # This was the last session, so assume it's still setup in the
+  # open parameters
+  zfopen && [[ -n $dir ]] && zfcd $dir
+else
+  # Last try: see if it's in the parameters.
+  local params
+  params=($(zftp params))
+  if [[ $host = $params[1] && $user = $params[2] ]]; then
+    zfopen && [[ -n $dir ]] && zfcd $dir
+  else
+    zfopen $host $user && [[ -n $dir ]] && zfcd $dir
+  fi
+fi
diff --git a/Functions/Zftp/zfhere b/Functions/Zftp/zfhere
index 43e599d3a..8fa607e28 100644
--- a/Functions/Zftp/zfhere
+++ b/Functions/Zftp/zfhere
@@ -1,5 +1,6 @@
 # function zfhere {
 # Change to the directory corresponding to $PWD on the server.
 # See zfcd for how this works.
+[[ $curcontext = :zf* ]] || local curcontext=:zfhere
 zfcd $PWD
 # }
diff --git a/Functions/Zftp/zfls b/Functions/Zftp/zfls
index e8d3cfb28..73211b46a 100644
--- a/Functions/Zftp/zfls
+++ b/Functions/Zftp/zfls
@@ -1,6 +1,7 @@
 # function zfls {
 
 emulate -L zsh
+[[ $curcontext = :zf* ]] || local curcontext=:zfls
 
 # directory hack, see zfcd
 if [[ $1 = $HOME || $1 = $HOME/* ]]; then
diff --git a/Functions/Zftp/zfmark b/Functions/Zftp/zfmark
index e69de29bb..e81cc27d8 100644
--- a/Functions/Zftp/zfmark
+++ b/Functions/Zftp/zfmark
@@ -0,0 +1,51 @@
+# zfmark [bname]
+# Set a bookmark for the current zftp connection, or use the
+# information about the last session if there isn't one.
+# A bookmark includes both the host *and* the directory on that host.
+#
+# Without bname, list the current bookmarks and their locations.
+
+emulate -L zsh
+setopt extendedglob
+[[ $curcontext = :zf* ]] || local curcontext=:zfmark
+
+# Set ZFTP_BMFILE if not already set.  This should agree with
+# the corresponding line in zfgoto.
+: ${ZFTP_BMFILE:=${ZDOTDIR:-$HOME}/.zfbkmarks}
+
+typeset -A bkmarks
+local line
+
+if [[ -f $ZFTP_BMFILE ]]; then
+  # read in file:  could optimise this by recording last read time
+  # comparing with file
+  while read -r line; do
+    # ignore blank and comment lines
+    [[ $line = [[:blank:]]# || $line = [[:blank:]]#'#'* ]] && continue
+    bkmarks[${line%% *}]="${line#* }"
+  done <$ZFTP_BMFILE
+fi
+
+if (( $# == 0 )); then
+  for line in ${(ko)bkmarks}; do
+    print -r- "$line ${bkmarks[$line]}"
+  done
+  return 0
+elif (( $# > 1 )); then
+  print "Usage: zfmark [bookmark]" >&2
+  return 1
+fi
+
+if [[ -n $ZFTP_HOST ]]; then
+  bkmarks[$1]="${ZFTP_USER}@${ZFTP_HOST}:${ZFTP_PWD}"
+elif [[ -n $zfconfig[lastloc_$ZFTP_SESSION] ]]; then
+  bkmarks[$1]="${zfconig[lastuser_$ZFTP_SESSION]}@\
+${zfconfig[lastloc_$ZFTP_SESSION]}"
+else
+  print "No current or recent ZFTP session to bookmark." >&2
+  return 1
+fi
+
+for line in ${(ko)bkmarks}; do
+  print -r- "$line ${bkmarks[$line]}"
+done >$ZFTP_BMFILE
diff --git a/Functions/Zftp/zfopen b/Functions/Zftp/zfopen
index fa9b4f81d..30e9cbfc9 100644
--- a/Functions/Zftp/zfopen
+++ b/Functions/Zftp/zfopen
@@ -7,36 +7,45 @@
 
 emulate -L zsh
 
-local optlist opt once
+[[ $curcontext = :zf* ]] || local curcontext=:zfopen
+local opt dir opt_1 setparams
 
-while [[ $1 = -* ]]; do
-  if [[ $1 = - || $1 = -- ]]; then
-    shift;
-    break;
-  fi
-  optlist=${1#-}
-  for (( i = 1; i <= $#optlist; i++)); do
-    opt=$optlist[$i]
-    case $optlist[$i] in
-      1) once=1
-	 ;;
-      *) print option $opt not recognised >&2
-	 ;;
-    esac
-  done
-  shift
+while getopts :1 opt; do
+  [[ $opt = "?" ]] && print "zfopen: bad option: -$OPTARG" >&2 && return 1
+  eval "opt_$opt=1"
 done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
 
 # This is where we should try and do same name-lookupage in
 # both .netrc and .ncftp/bookmarks .  We could even try saving
 # the info in their for new hosts, like ncftp does.
 
-if [[ $once = 1 ]]; then
-  zftp open $*
+if [[ $1 = */* ]]; then
+  1=${1##ftp://}
+  dir=${1#*/}
+  1=${1%%/*}
+fi
+
+if [[ $opt_1 = 1 ]]; then
+  zftp open $* || return 1
+  if [[ $# = 1 ]]; then
+    if ! zftp login; then
+      zftp close
+      return 1
+    fi
+  fi
 else
   # set parameters, but only if there was at least a host
-  (( $# > 0 )) && zfparams $*
+  (( $# > 0 )) && zfparams $* && setparams=1
   # now call with no parameters
-  zftp open
+  if ! zftp open; then
+    [[ -n $ZFTP_HOST ]] && zftp close
+    [[ -n $setparams ]] && zfparams -
+    return 1
+  fi
+fi
+
+if [[ -n $dir ]]; then
+  zfcd $dir
 fi
 # }
diff --git a/Functions/Zftp/zfparams b/Functions/Zftp/zfparams
index 5c5262c52..59fea0ed4 100644
--- a/Functions/Zftp/zfparams
+++ b/Functions/Zftp/zfparams
@@ -1,12 +1,27 @@
 # function zfparams {
 
 emulate -L zsh
+[[ $curcontext = :zf* ]] || local curcontext=:zfparams
 
-# Set to prompt for any user or password if not given.
-# Don't worry about accounts here.
-if (( $# > 0 )); then
+if [[ $# -eq 1 && $1 = - ]]; then
+  # Delete existing parameter set.
+  local sess=$ZFTP_SESSION key
+  key=${zfconfig[fcache_$sess]}
+  [[ -n $key ]] && unset $key
+  for key in fcache lastloc lastdir curdir otherdir otherargs lastuser; do
+    unset "zfconfig[${key}_${sess}]"
+  done
+elif (( $# > 0 )); then
+  # Set to prompt for any user or password if not given.
+  # Don't worry about accounts here.
   (( $# < 2 )) && 2='?'
-  (( $# < 3 )) && 3='?'
+  if (( $# < 3 )); then
+    if [[ $2 = '?'* ]]; then
+      3="?Password on ${1}: "
+    else
+      3="?Password for ${2##\\?} on ${1}: "
+    fi
+  fi
 fi
 zftp params $*
 # }
diff --git a/Functions/Zftp/zfpcp b/Functions/Zftp/zfpcp
index ddd570e59..e74ee34f8 100644
--- a/Functions/Zftp/zfpcp
+++ b/Functions/Zftp/zfpcp
@@ -14,10 +14,11 @@
 
 emulate -L zsh
 
+[[ $curcontext = :zf* ]] || local curcontext=:zfpcp
 local rem loc
 integer stat do_close
 
-zfautocheck
+zfautocheck || return 1
 
 if [[ $# -gt 2 || $2 = (.|..) || $2 = */ ]]; then
   local dir=$argv[-1]
@@ -32,6 +33,9 @@ if [[ $# -gt 2 || $2 = (.|..) || $2 = */ ]]; then
     zftp put $rem <$loc || stat=1
   done
 else
+  if [[ $2 = $HOME || $2 = $HOME/* ]]; then
+    2="~${2#$HOME}"
+  fi
   zftp put $2 <$1
   stat=$?
   if [[ stat -ne 0 && $ZFTP_CODE = 553 && $ZFTP_REPLY = *'Is a directory'* ]]
diff --git a/Functions/Zftp/zfput b/Functions/Zftp/zfput
index 0687163f0..68ef5be5c 100644
--- a/Functions/Zftp/zfput
+++ b/Functions/Zftp/zfput
@@ -3,19 +3,64 @@
 # off any directory parts to get the remote filename (i.e. always
 # goes into current remote directory).  Use zfpcp to specify new
 # file name or new directory at remote end.
+#
+# -r means put recursively:  any directories encountered will have
+#    all their contents to arbitrary depth transferred.  Note that
+#    this creates the required directories.  Any files in subdirectories
+#    whose names begin with a `.' will also be included.
 
 emulate -L zsh
 
-local loc rem
-integer stat do_close
+[[ $curcontext = :zf* ]] || local curcontext=:zfput
+local opt opt_r
+integer stat do_close abort
+
+while getopts :r opt; do
+  [[ $opt = '?' ]] && print "zfget: bad option: -$OPTARG" && return 1
+  eval "opt_$opt=1"
+done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
 
 zfautocheck
 
-for loc in $*; do
-  rem=${loc:t}
-  zftp put $rem <$loc
-  [[ $? == 0 ]] || stat=$?
-done
+zfput_sub() {
+  local subdirs loc rem
+  integer stat
+  subdirs=()
+
+  for loc in $*; do
+    if [[ -n $opt_r ]]; then
+      if [[ -d $loc ]]; then
+	subdirs=($subdirs $loc)
+	continue
+      else
+	rem=$loc
+      fi
+    else
+      rem=${loc:t}
+    fi
+
+    zftp put $rem <$loc
+    (( $? )) && stat=$?
+    if ! zftp test; then
+      abort=1
+      (( stat )) || stat=1
+      break;
+    fi
+  done
+
+  while (( $#subdirs  && ! abort )); do
+    zftp mkdir ${subdirs[1]}
+    zfput_sub ${subdirs[1]}/*(ND)
+    (( $? )) && stat=$?
+    shift subdirs
+  done
+
+  return $stat
+}
+
+zfput_sub $*
+stat=$?
 
 (( $do_close )) && zfclose
 
diff --git a/Functions/Zftp/zfsession b/Functions/Zftp/zfsession
index 9cd0d918f..79d4da93c 100644
--- a/Functions/Zftp/zfsession
+++ b/Functions/Zftp/zfsession
@@ -3,6 +3,7 @@
 
 emulate -L zsh
 
+[[ $curcontext = :zf* ]] || local curcontext=:zfsession
 local opt opt_l opt_v opt_o opt_d hadopts
 
 while getopts ":lovd" opt; do
diff --git a/Functions/Zftp/zfstat b/Functions/Zftp/zfstat
index 0ca755d03..033e6976b 100644
--- a/Functions/Zftp/zfstat
+++ b/Functions/Zftp/zfstat
@@ -6,25 +6,16 @@
 setopt localoptions unset
 unsetopt ksharrays
 
-local i stat=0 opt optlist verbose
+[[ $curcontext = :zf* ]] || local curcontext=:zfstat
+local i stat=0 opt opt_v
 
-while [[ $1 = -* ]]; do
-  if [[ $1 = - || $1 = -- ]]; then
-    shift;
-    break;
-  fi
-  optlist=${1#-}
-  for (( i = 1; i <= $#optlist; i++)); do
-    opt=$optlist[$i]
-    case $opt in
-      v) verbose=1
-	 ;;
-      *) print option $opt not recognised >&2
-	 ;;
-    esac
-  done
-  shift
+while getopts :v opt; do
+  [[ $opt = "?" ]] && print "zfstat: bad option: -$OPTARG" >&2 && return 1
+  eval "opt_$opt=1"
 done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
+
+[[ -n $ZFTP_SESSION ]] && print "Session:\t$ZFTP_SESSION"
 
 if [[ -n $ZFTP_HOST ]]; then
   print "Host:\t\t$ZFTP_HOST"
@@ -55,7 +46,8 @@ if [[ -n $ZFTP_HOST ]]; then
   fi
 else
   print "Not connected."
-  [[ -n $zflastsession ]] && print "Last session:\t$zflastsession"
+  [[ -n $zfconfig[lastloc_$ZFTP_SESSION] ]] &&
+  print "Last location:\t$zfconfig[lastloc_$ZFTP_SESSION]"
   stat=1
 fi
 
@@ -77,7 +69,7 @@ for (( i = 1; i <= ${#ZFTP_PREFS}; i++ )); do
 done
 print
 
-if [[ -n $ZFTP_HOST && $verbose = 1 ]]; then
+if [[ -n $ZFTP_HOST && $opt_v = 1 ]]; then
   zfautocheck -d
   print "Status of remote server:"
   # make sure we print the reply
diff --git a/Functions/Zftp/zftransfer b/Functions/Zftp/zftransfer
index 929f099d2..c70bf7248 100644
--- a/Functions/Zftp/zftransfer
+++ b/Functions/Zftp/zftransfer
@@ -4,6 +4,7 @@
 
 emulate -L zsh
 
+[[ $curcontext = :zf* ]] || local curcontext=:zftransfer
 local sess1 sess2 file1 file2 oldsess=${ZFTP_SESSION}
 
 if [[ $# -ne 2 ]]; then
@@ -39,7 +40,9 @@ zfautocheck || return 1
 # the size from the pipe --- and if it does, it's probably wrong.
 # To avoid that, try to get the size and set it for the progress to
 # see.
-if [[ $zfconfig[progress] != none ]]; then
+local style
+zstyle -s ':zftp:zftransfer' progress style
+if [[ -n $style && $style != none ]]; then
   local ZFTP_TSIZE array tmpfile=${TMPPREFIX}zft$$
   zftp remote $file1 >$tmpfile 2>/dev/null
   array=($(<$tmpfile))
@@ -49,7 +52,7 @@ fi
 
 # We do the RHS of the pipeline in a subshell, too, so that
 # the LHS can get SIGPIPE when it exits.
-{ zfconfig[progress]=none
+{ zstyle '*' progress none
   zftp get $file1 } |
 ( zftp session $sess2
   zfautocheck && zftp put $file2 )
diff --git a/Functions/Zftp/zftype b/Functions/Zftp/zftype
index c3b93b7a0..0cdf7e2aa 100644
--- a/Functions/Zftp/zftype
+++ b/Functions/Zftp/zftype
@@ -1,5 +1,6 @@
 # function zftype {
 local type zftmp=${TMPPREFIX}zftype$$
+[[ $curcontext = :zf* ]] || local curcontext=:zftype
 
 zfautocheck -d
 
diff --git a/Functions/Zftp/zfuget b/Functions/Zftp/zfuget
index 482da42e9..c1033c930 100644
--- a/Functions/Zftp/zfuget
+++ b/Functions/Zftp/zfuget
@@ -25,8 +25,9 @@
 
 emulate -L zsh
 
+[[ $curcontext = :zf* ]] || local curcontext=:zfuget
 local loc rem locstats remstats doit tmpfile=${TMPPREFIX}zfuget$$
-local rstat remlist verbose optlist opt bad i silent nglob time
+local rstat remlist opt opt_v opt_s opt_G opt_t
 integer stat do_close
 
 zfuget_print_time() {
@@ -43,40 +44,20 @@ zfuget_print () {
   print ", $locstats[1] bytes)"
 }
 
-while [[ $1 = -* ]]; do
-  if [[ $1 = - || $1 = -- ]]; then
-    shift;
-    break;
-  fi
-  optlist=${1#-}
-  for (( i = 1; i <= $#optlist; i++)); do
-    opt=$optlist[$i]
-    case $optlist[$i] in
-      v) verbose=1
-	 ;;
-      s) silent=1
-	 ;;
-      G) nglob=1
-	 ;;
-      t) time=1
-	 ;;
-      *) print option $opt not recognised >&2
-	 ;;
-    esac
-  done
-  shift
+while getopts :vsGt opt; do
+  [[ $opt = "?" ]] && print "zfuget: bad option: -$OPTARG" >&2 && return 1
+  eval "opt_$opt=1"
 done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
 
-[[ -n $bad ]] && return 1
-
-zfautocheck
+zfautocheck || return 1
 
 for remlist in $*; do
   # zfcd directory hack to put the front back to ~
   if [[ $remlist == $HOME || $remlist == $HOME/* ]]; then
     remlist="~${remlist#$HOME}"
   fi
-  if [[ $nglob != 1 ]]; then
+  if [[ $opt_n != 1 ]]; then
     zfrglob remlist
   fi
   if (( $#remlist )); then
@@ -99,11 +80,11 @@ for remlist in $*; do
 	  stat=1
 	  continue
 	fi
-	[[ $verbose = 1 ]] && zfuget_print
+	[[ $opt_v = 1 ]] && zfuget_print
 	if (( $locstats[1] != $remstats[1] )); then
 	  # Files have different sizes
-	  if [[ $locstats[2] > $remstats[2] && $silent != 1 ]]; then
-	    [[ $verbose != 1 ]] && zfuget_print
+	  if [[ $locstats[2] > $remstats[2] && $opt_s != 1 ]]; then
+	    [[ $opt_v != 1 ]] && zfuget_print
 	    print "Local file $loc more recent than remote," 1>&2
 	    print -n "but sizes are different.  Transfer anyway [y/n]? " 1>&2
 	    read -q doit
@@ -111,24 +92,24 @@ for remlist in $*; do
 	else
 	  # Files have same size
 	  if [[ $locstats[2] < $remstats[2] ]]; then
-	    if [[ $silent != 1 ]]; then
-	      [[ $verbose != 1 ]] && zfuget_print
+	    if [[ $opt_s != 1 ]]; then
+	      [[ $opt_v != 1 ]] && zfuget_print
 	      print "Local file $loc has same size as remote," 1>&2
 	      print -n "but local file is older. Transfer anyway [y/n]? " 1>&2
 	      read -q doit
 	    fi
 	  else
 	    # presumably same file, so don't get it.
-	    [[ $verbose = 1 ]] && print Not transferring
+	    [[ $opt_v = 1 ]] && print Not transferring
 	    doit=n
 	  fi
 	fi
       else
-	[[ $verbose = 1 ]] && print New file $loc
+	[[ $opt_v = 1 ]] && print New file $loc
       fi
       if [[ $doit = y ]]; then
 	if zftp get $rem >$loc; then
-	  if [[ $time = 1 ]]; then
+	  if [[ $opt_t = 1 ]]; then
 	    # if $remstats is set, it's second element is the remote time
 	    zfrtime $loc $rem $remstats[2]
 	  fi
diff --git a/Functions/Zftp/zfuput b/Functions/Zftp/zfuput
index b54d0d0d4..4e0e42dcd 100644
--- a/Functions/Zftp/zfuput
+++ b/Functions/Zftp/zfuput
@@ -11,8 +11,9 @@
 
 emulate -L zsh
 
+[[ $curcontext = :zf* ]] || local curcontext=:zfuput
 local loc rem locstats remstats doit tmpfile=${TMPPREFIX}zfuput$$
-local rstat verbose optlist opt bad i silent
+local rstat opt opt_v opt_s
 integer stat do_close
 
 zfuput_print_time() {
@@ -29,29 +30,13 @@ zfuput_print () {
   print ", $locstats[1] bytes)"
 }
 
-while [[ $1 = -* ]]; do
-  if [[ $1 = - || $1 = -- ]]; then
-    shift;
-    break;
-  fi
-  optlist=${1#-}
-  for (( i = 1; i <= $#optlist; i++)); do
-    opt=$optlist[$i]
-    case $optlist[$i] in
-      v) verbose=1
-	 ;;
-      s) silent=1
-	 ;;
-      *) print option $opt not recognised >&2
-	 ;;
-    esac
-  done
-  shift
+while getopts :vs opt; do
+  [[ $opt = "?" ]] && print "zfuget: bad option: -$OPTARG" >&2 && return 1
+  eval "opt_$opt=1"
 done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
 
-[[ -n $bad ]] && return 1
-
-zfautocheck
+zfautocheck || return 1
 
 if [[ $ZFTP_VERBOSE = *5* ]]; then
   # should we turn it off locally?
@@ -77,13 +62,13 @@ for rem in $*; do
     print "Server does not implement full command set required." 1>&2
     return 1
   elif [[ $rstat = 1 ]]; then
-    [[ $verbose = 1 ]] && print New file $loc
+    [[ $opt_v = 1 ]] && print New file $loc
   else
-    [[ $verbose = 1 ]] && zfuput_print
+    [[ $opt_v = 1 ]] && zfuput_print
     if (( $locstats[1] != $remstats[1] )); then
       # Files have different sizes
-      if [[ $locstats[2] < $remstats[2] && $silent != 1 ]]; then
-	[[ $verbose != 1 ]] && zfuput_print
+      if [[ $locstats[2] < $remstats[2] && $opt_s != 1 ]]; then
+	[[ $opt_v != 1 ]] && zfuput_print
 	print "Remote file $rem more recent than local," 1>&2
 	print -n "but sizes are different.  Transfer anyway [y/n]? " 1>&2
 	read -q doit
@@ -91,15 +76,15 @@ for rem in $*; do
     else
       # Files have same size
       if [[ $locstats[2] > $remstats[2] ]]; then
-	if [[ $silent != 1 ]]; then
-	  [[ $verbose != 1 ]] && zfuput_print
+	if [[ $opt_s != 1 ]]; then
+	  [[ $opt_v != 1 ]] && zfuput_print
 	  print "Remote file $rem has same size as local," 1>&2
 	  print -n "but remote file is older. Transfer anyway [y/n]? " 1>&2
 	  read -q doit
 	fi
       else
 	# presumably same file, so don't get it.
-	[[ $verbose = 1 ]] && print Not transferring
+	[[ $opt_v = 1 ]] && print Not transferring
 	doit=n
       fi
     fi