diff options
Diffstat (limited to 'Functions/TCP/tcp_open')
-rw-r--r-- | Functions/TCP/tcp_open | 197 |
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 |