about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2000-05-08 10:45:02 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2000-05-08 10:45:02 +0000
commit17d342160ae1c59687b61332bd4dee5e62bd509a (patch)
tree7ca5430438165cd96abb44d1d201819438625f11
parentcfcb3202ef71040a7019609da6cb21de57f16ad6 (diff)
downloadzsh-17d342160ae1c59687b61332bd4dee5e62bd509a.tar.gz
zsh-17d342160ae1c59687b61332bd4dee5e62bd509a.tar.xz
zsh-17d342160ae1c59687b61332bd4dee5e62bd509a.zip
11252: no colon at the end of zftp function contexts
-rw-r--r--ChangeLog5
-rw-r--r--Doc/Zsh/zftpsys.yo4
-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
24 files changed, 394 insertions, 230 deletions
diff --git a/ChangeLog b/ChangeLog
index 20c62f146..db511ca22 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2000-05-08  Peter Stephenson  <pws@cambridgesiliconradio.com>
+
+	* 11252: Functions/Zftp/zf*: context should be
+	e.g. `:zftp:zfopen', no colon at the end.
+
 2000-05-08  Sven Wischnowsky  <wischnow@zsh.org>
 
 	* 11253: Completion/Base/_arguments, Src/Zle/computil.c: more
diff --git a/Doc/Zsh/zftpsys.yo b/Doc/Zsh/zftpsys.yo
index e5b980cef..783de107f 100644
--- a/Doc/Zsh/zftpsys.yo
+++ b/Doc/Zsh/zftpsys.yo
@@ -522,14 +522,14 @@ given, although that is not useful in the cases described here).  These
 values will then be used throughout the zftp function system.  For more
 precise control, the first argument, which gives a context in which the
 style applies, can be modified to include a particular function, as for
-example `tt(:zftp:zfget:)': the style will then have the given value only
+example `tt(:zftp:zfget)': the style will then have the given value only
 in the tt(zfget) function.  Values for the same style in different contexts
 may be set; the most specific function will be used, where
 strings are held to be more specific than patterns, and longer patterns and
 shorter patterns.  Note that only the top level function name, as called by
 the user, is used; calling of lower level functions is transparent to the
 user.  Hence modifications to the title bar in tt(zftp_chpwd) use the
-contexts tt(:zftp:zfopen:), tt(:zftp:zfcd:), etc., depending where it was
+contexts tt(:zftp:zfopen), tt(:zftp:zfcd), etc., depending where it was
 called from.  The following styles are understood:
 
 startitem()
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