about summary refs log tree commit diff
path: root/Functions/TCP/tcp_open
diff options
context:
space:
mode:
Diffstat (limited to 'Functions/TCP/tcp_open')
-rw-r--r--Functions/TCP/tcp_open197
1 files changed, 197 insertions, 0 deletions
diff --git a/Functions/TCP/tcp_open b/Functions/TCP/tcp_open
new file mode 100644
index 000000000..d9d5a96da
--- /dev/null
+++ b/Functions/TCP/tcp_open
@@ -0,0 +1,197 @@
+# Open a TCP session, add it to the list, handle it with zle if that's running.
+# Unless using -a, -f, -l or -s, first two arguments are host and port.
+#
+# Remaining argument, if any, is the name of the session, which mustn't
+# clash with an existing one.  If none is given, the number of the
+# connection is used (i.e. first connection is 1, etc.), or the first
+# available integer if that is already in use.
+#
+# Session names, whether provided on the command line or in the
+# .ztcp_sessions file should not be `clever'.  A clever name is one
+# with characters that won't work.  This includes whitespace and an
+# inconsistent set of punctuation characters.  If in doubt, stick
+# to alphanumeric, underscore and non-initial hyphen.
+#
+# -a fd   Accept a connection on fd and make that the session.
+#         This will block until a successful incoming connection is received.
+#
+#         fd is probably a value returned by ztcp -l; no front-end
+#         is currently provided for that but it should simply be
+#         a matter of calling `ztcp -l port' and storing $REPLY, then
+#         closing the listened port with `ztcp -c $stored_fd'.
+#
+# -f fd   `Fake' tcp connection on the given file descriptor.  This
+#         could be, for example, a file descriptor already opened to
+#         a named pipe.  It should not be a regular file, however.
+#         Note that it is not a good idea for two different sessions
+#         to be attempting to read from the same named pipe, so if
+#         both ends of the pipe are to be handled by zsh, at least
+#         one should use the `-z' option.
+#
+# -l sesslist
+# -s sessname
+#         Open by session name or comma separated list; either may
+#         be repeated as many times as necessary.  The session must be
+#	  listed in the file ${ZDOTDIR:-$HOME}/.ztcp_sessions.  Lines in
+#	  this file look exactly like a tcp_open command line except the
+#	  session name is at the start, for example
+#           sess1 pwspc 2811
+#         has the effect of
+#           tcp_open pwspc 2811 sess1
+#         Remaining arguments (other than options) to tcp_open are
+#         not allowed.  Options in .ztcp_sessions are not handled.
+#	  The file must be edited by hand.
+#
+# -z      Don't install a zle handler for reading on the file descriptor.
+#	  Otherwise, if zle is enabled, the file descriptor will
+#         be tested while at the shell prompt and any input automatically
+#         printed in the same way as job control notification.
+#
+# If a session is successfully opened, and if the function `tcp_on_open'
+# exists, it is run with the arguments session_name, session_fd.
+
+emulate -L zsh
+setopt extendedglob cbases
+
+zmodload -i zsh/net/tcp || return 1
+autoload -U zgprintf tcp_alias tcp_close tcp_command tcp_expect tcp_fd_handler
+autoload -U tcp_log tcp_output tcp_proxy tcp_read tcp_rename tcp_send
+autoload -U tcp_sess tcp_spam tcp_talk tcp_wait
+
+local opt accept fake nozle sessfile sess quiet
+local -a sessnames sessargs
+integer stat
+
+while getopts "a:f:l:qs:z" opt; do
+    case $opt in
+	(a) accept=$OPTARG
+            if [[ $accept != [[:digit:]]## ]]; then
+		print "option -a takes a file descriptor" >&2
+		return 1
+	    fi
+	    ;;
+	(f) fake=$OPTARG
+	    if [[ $fake != [[:digit:]]## ]]; then
+		print "option -f takes a file descriptor" >&2
+		return 1
+	    fi
+	    ;;
+	(l) sessnames+=(${(s.,.)OPTARG})
+            ;;
+	(q) quiet=1
+            ;;
+	(s) sessnames+=($OPTARG)
+            ;;
+	(z) nozle=1
+            ;;
+	(*) return 1
+            ;;
+    esac
+done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
+
+(( ${+tcp_by_fd} ))   || typeset -gA tcp_by_fd
+(( ${+tcp_by_name} )) || typeset -gA tcp_by_name 
+typeset -A sessassoc
+
+if (( ${#sessnames} )); then
+    if [[ $# -ne 0 || -n $accept || -n $fake ]]; then
+	print "Incompatible arguments with \`-s' option." >&2
+	return 1
+    fi
+    for sess in ${sessnames}; do
+	sessassoc[$sess]=
+    done
+
+    sessfile=${ZDOTDIR:-$HOME}/.ztcp_sessions
+    if [[ ! -r $sessfile ]]; then
+	print "No session file: $sessfile" >&2
+	return 1
+    fi
+    while read -A sessargs; do
+	[[ ${sessargs[1]} = '#'* ]] && continue
+	if ((  ${+sessassoc[${sessargs[1]}]} )); then
+	    sessassoc[${sessargs[1]}]="${sessargs[2,-1]}"
+	fi
+    done < $sessfile
+    for sess in ${sessnames}; do
+	if [[ -z $sessassoc[$sess] ]]; then
+	    print "Couldn't find session $sess in $sessfile." >&2
+	    return 1
+	fi
+    done
+else
+    if [[ -z $accept && -z $fake ]]; then
+	if (( $# < 2 )); then
+	    set -- wrong number of arguments
+	else
+	    host=$1 port=$2
+	    shift $(( $# > 1 ? 2 : 1 ))
+	fi
+    fi
+    if [[ -n $1 ]]; then
+	sessnames=($1)
+	shift
+    else
+	sessnames=($(( ${#tcp_by_fd} + 1 )))
+	while [[ -n $tcp_by_name[$sessnames[1]] ]]; do
+	    (( sessnames[1]++ ))
+	done
+    fi
+    sessassoc[$sessnames[1]]="$host $port"
+fi
+
+if (( $# )); then
+  print "Usage: $0 [-z] [-a fd | -f fd | host port [ session ] ]
+  $0 [-z] [ -s session | -l sesslist ] ..." >&2
+  return 1
+fi
+
+local REPLY fd
+for sess in $sessnames; do
+    if [[ -n $tcp_by_name[$sess] ]]; then
+	print "Session \`$sess' already exists." >&2
+	return 1
+    fi
+
+    sessargs=()
+    if [[ -n $fake ]]; then
+	fd=$fake;
+    else
+	if [[ -n $accept ]]; then
+	    ztcp -a $accept || return 1
+	else
+	    sessargs=(${=sessassoc[$sess]})
+	    ztcp $sessargs || return 1
+	fi
+	fd=$REPLY
+    fi
+
+    tcp_by_fd[$fd]=$sess
+    tcp_by_name[$sess]=$fd
+
+    [[ -o zle && -z $nozle ]] && zle -F $fd tcp_fd_handler
+    if [[ -z $quiet ]]; then
+	if (( ${#sessargs} )); then
+	    print "Session $sess" \
+"(host $sessargs[1], port $sessargs[2] fd $fd) opened OK."
+	else
+	    print "Session $sess (fd $fd) opened OK."
+	fi
+    fi
+
+    # needed for new completion system, so I'm not too sanguine
+    # about requiring this here...
+    if zmodload -i zsh/parameter; then
+	if (( ${+functions[tcp_on_open]} )); then
+	    tcp_on_open $sess $fd
+	fi
+    fi
+done
+
+if [[ -z $TCP_SESS ]]; then
+    [[ -z $quiet ]] && print "Setting default TCP session $sessnames[1]"
+    TCP_SESS=$sessnames[1]
+fi
+
+return $stat