1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
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
|