Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: TCP function improvements
- X-seq: zsh-workers 18810
- From: Peter Stephenson <pws@xxxxxxx>
- To: zsh-workers@xxxxxxxxxx (Zsh hackers list)
- Subject: PATCH: TCP function improvements
- Date: Fri, 04 Jul 2003 17:22:43 +0100
- Mailing-list: contact zsh-workers-help@xxxxxxxxxx; run by ezmlm
Here's an assortment of improvements to the TCP functions which I've
been playing with (as well as using regularly). There are also one or
two minor fixes I noticed.
New functions tcp_point and tcp_shoot provide one-shot file transfer
without any session.
tcp_spam can take option -e to evaluate a line.
tcp_on_read associative array provides pattern triggers on data read.
%c in TCP prompts provides way of output different text for current or
uncurrent sessions by using the ternary expression enhancement to the
zformat builtin.
SECONDS is used as floating point for greater timing accuracy. Use
TCP_SECONDS_START to provide workaround if this makes global SECONDS
invisible.
Variables were incorrectly put into the function index.
Sanity check for existing session in tcp_send.
Index: Doc/Zsh/tcpsys.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/tcpsys.yo,v
retrieving revision 1.3
diff -u -r1.3 tcpsys.yo
--- Doc/Zsh/tcpsys.yo 25 Apr 2003 11:19:09 -0000 1.3
+++ Doc/Zsh/tcpsys.yo 4 Jul 2003 16:13:47 -0000
@@ -353,7 +353,7 @@
connections. It continues to accept new connections until interrupted.
)
findex(tcp_spam)
-item(tt(tcp_spam [-rtv] [ -a | -s ) var(sess) tt(| -l) var(sess)tt(,... ]) var(cmd) tt(...))(
+item(tt(tcp_spam [-ertv] [ -a | -s ) var(sess) tt(| -l) var(sess)tt(,... ]) var(cmd) tt(...))(
Execute `var(cmd) tt(...)' for each session in turn. Note this executes
the command and arguments; it does not send the command line as data
unless the tt(-t) (transmit) option is given.
@@ -374,6 +374,9 @@
session. This is output after any modification to TCP_SESS by the
user-defined tt(tcp_on_spam) function described below. (Obviously that
function is able to generate its own output.)
+
+If the option tt(-e) is present, the line given as var(cmd ...) is executed
+using tt(eval), otherwise it is executed without any further processing.
)
findex(tcp_talk)
item(tt(tcp_talk))(
@@ -403,7 +406,33 @@
)
enditem()
-sect(TCP User-defined Function)
+subsect(`One-shot' file transfer)
+startitem()
+xitem(tt(tcp_point) var(port))
+item(tt(tcp_shoot) var(host) var(port))(
+This pair of functions provide a simple way to transfer a file between
+two hosts within the shell. Note, however, that bulk data transfer is
+currently done using tt(cat). tt(tcp_point) reads any data arriving at
+var(port) and sends it to standard output; tt(tcp_shoot) connects to
+var(port) on var(host) and sends its standard input. Any unused var(port)
+may be used; the standard mechanism for picking a port is to think of a
+random four-digit number above 1024 until one works.
+
+To transfer a file from host tt(woodcock) to host tt(springes), on
+tt(springes):
+
+example(tcp_point 8091 >output_file)
+
+and on tt(woodcock):
+
+example(tcp_shoot springes 8091 <input_file)
+
+As these two functions do not require tt(tcp_open) to set up a TCP
+connection first, they may need to be autoloaded separately.
+)
+enditem()
+
+sect(TCP User-defined Functions)
Certain functions, if defined by the user, will be called by the function
system in certain contexts. This facility depends on the module
@@ -485,9 +514,23 @@
tt(tcp_send).
The var(prompt) to use is specified by tt(-P); the default is the empty
-string. It can contain `tt(%s)' which is replaced by the session name, or
-`tt(%f)' which is replaced by the session's file descriptor; `tt(%%)' is
-replaced by a single `tt(%)'.
+string. It can contain:
+startitem()
+item(tt(%c))(
+Expands to 1 if the session is the current session, otherwise 0. Used
+with ternary expresions such as `tt(%LPAR()c.-.PLUS()RPAR())' to
+output `tt(PLUS())' for the current session and `tt(-)' otherwise.
+)
+item(tt(%f))(
+Replaced by the session's file descriptor.
+)
+item(tt(%s))(
+Replaced by the session name.
+)
+item(tt(%%))(
+Replaced by a single `tt(%)'.
+)
+enditem()
The option tt(-q) suppresses output to standard output, but not to any log
files which are configured.
@@ -514,33 +557,33 @@
sets a session for the duration of a function.
startitem()
-findex(tcp_expect_lines)
+vindex(tcp_expect_lines)
item(tt(tcp_expect_lines))(
Array. The set of lines read during the last call to tt(tcp_expect),
including the last (tt($TCP_LINE)).
)
-findex(tcp_filter)
+vindex(tcp_filter)
item(tt(tcp_filter))(
Array. May be set directly. A set of extended globbing patterns which,
if matched in tt(tcp_output), will cause the line not to be printed to
standard output. The patterns should be defined as described for the
arguments to tt(tcp_expect). Output of line to log files is not affected.
)
-findex(TCP_LINE)
+vindex(TCP_LINE)
item(tt(TCP_LINE))(
The last line read by tt(tcp_read), and hence also tt(tcp_expect).
)
-findex(TCP_LINE_FD)
+vindex(TCP_LINE_FD)
item(tt(TCP_LINE_FD))(
The file descriptor from which tt($TCP_LINE) was read.
tt(${tcp_by_fd[$TCP_LINE_FD]}) will give the corresponding session name.
)
-findex(tcp_lines)
+vindex(tcp_lines)
item(tt(tcp_lines))(
Array. The set of lines read during the last call to tt(tcp_read),
including the last (tt($TCP_LINE)).
)
-findex(TCP_LOG)
+vindex(TCP_LOG)
item(tt(TCP_LOG))(
May be set directly, although it is also controlled by tt(tcp_log).
The name of a file to which output from all sessions will be sent.
@@ -555,11 +598,11 @@
Output to each file is raw; no prompt is added. If it is not an absolute
path name, it will follow the user's current directory.
)
-findex(tcp_nospam_list)
+vindex(tcp_nospam_list)
item(tt(tcp_nospam_list))(
Array. May be set directly. See tt(tcp_spam) for how this is used.
)
-findex(TCP_OUTPUT)
+vindex(TCP_OUTPUT)
item(tt(TCP_OUTPUT))(
May be set directly. If a non-empty string, any data sent to a session by
tt(tcp_send) will be logged. The prompt has the same format as
@@ -567,44 +610,85 @@
specified by tt($TCP_LOG), but not in a file generated from
tt($TCP_LOG_SESS).
)
-findex(TCP_PROMPT)
+vindex(TCP_PROMPT)
item(tt(TCP_PROMPT))(
May be set directly. Used as the prefix for data read by tt(tcp_read)
which is printed to standard output or to the log file given by
tt($TCP_LOG), if any. Any `tt(%s)', `tt(%f)' or `tt(%%)' occurring in the
string will be replaced by the name of the session, the session's
-underlying file descriptor, or a single `tt(%)', respectively.
+underlying file descriptor, or a single `tt(%)', respectively. The
+expression `tt(%c)' expands to 1 if the session being read is the current
+session, else 0; this is most useful in ternary expressions such as
+`tt(%LPAR()c.-.PLUS()RPAR())' which outputs `tt(PLUS())' if the session is
+the current one, else `tt(-)'.
)
-findex(TCP_READ_DEBUG)
+vindex(TCP_READ_DEBUG)
item(tt(TCP_READ_DEBUG))(
May be set directly. If this has non-zero length, tt(tcp_read) will give
some limited diagnostics about data being read.
)
-findex(TCP_SESS)
+vindex(TCP_SECONDS_START)
+item(tt(TCP_SECONDS_START))(
+This value is created and initialised to zero by tcp_open.
+
+The functions tt(tcp_read) and tt(tcp_expect) use the shell's
+tt(SECONDS) parameter for their own timing purposes. If that parameter
+is not of floating point type on entry to one of the functions, it will
+create a local parameter tt(SECONDS) which is floating point and set the
+parameter tt(TCP_SECONDS_START) to the previous value of tt($SECONDS).
+If the parameter is already floating point, it is used without a local
+copy being created and tt(TCP_SECONDS_START) is not set. As the global
+value is zero, the shell elapsed time is guaranteed to be the sum of
+tt($SECONDS) and tt($TCP_SECONDS_START).
+
+This can be avoided by setting tt(SECONDS) globally to a floating point
+value using `tt(typeset -F SECONDS)'; then the TCP functions will never
+make a local copy and never set tt(TCP_SECONDS_START) to a non-zero value.
+)
+vindex(TCP_SESS)
item(tt(TCP_SESS))(
May be set directly. The current session; must refer to one of the
sessions established by tt(tcp_open).
)
-findex(TCP_SILENT)
+vindex(TCP_SILENT)
item(tt(TCP_SILENT))(
May be set directly, although it is also controlled by tt(tcp_log).
If of non-zero length, data read by tt(tcp_read) will not be written to
standard output, though may still be written to a log file.
)
-findex(tcp_spam_list)
+vindex(tcp_spam_list)
item(tt(tcp_spam_list))(
Array. May be set directly. See the description of the function
tt(tcp_spam) for how this is used.
)
-findex(TCP_TALK_ESCAPE)
+vindex(TCP_TALK_ESCAPE)
item(tt(TCP_TALK_ESCAPE))(
May be set directly. See the description of the function tt(tcp_talk) for
how this is used.
)
-findex(TCP_TIMEOUT)
+vindex(TCP_TIMEOUT)
item(tt(TCP_TIMEOUT))(
May be set directly. Currently this is only used by the function
tt(tcp_command), see above.
+)
+enditem()
+
+sect(TCP User-defined Parameters)
+
+The following parameters are not set by the function system, but have
+a special effect if set by the user.
+
+startitem()
+vindex(tcp_on_read)
+item(tt(tcp_on_read))(
+This should be an associative array; if it is not, the behaviour is
+undefined. Each key is the name of a shell function or other command,
+and the corresponding value is a shell pattern (using tt(EXTENDED_GLOB)).
+Every line read from a TCP session directly or indirectly using
+tt(tcp_read) (which includes lines read by tt(tcp_expect)) is compared
+against the pattern. If the line matches, the command given in the key is
+called with two arguments: the name of the session from which the line was
+read, and the line itself.
)
enditem()
Index: Functions/TCP/tcp_expect
===================================================================
RCS file: /cvsroot/zsh/zsh/Functions/TCP/tcp_expect,v
retrieving revision 1.1
diff -u -r1.1 tcp_expect
--- Functions/TCP/tcp_expect 6 Feb 2003 12:21:51 -0000 1.1
+++ Functions/TCP/tcp_expect 4 Jul 2003 16:13:47 -0000
@@ -37,8 +37,12 @@
emulate -L zsh
setopt extendedglob
-# Get extra accuracy by making SECONDS floating point locally
-typeset -F SECONDS
+if [[ ${(t)SECONDS} != float* ]]; then
+ # If called from another function, use that
+ typeset -F TCP_SECONDS_START=$SECONDS
+ # Get extra accuracy by making SECONDS floating point locally
+ typeset -F SECONDS
+fi
# Variables are all named _expect_* to avoid problems with the -p param.
local _expect_opt _expect_pvar
Index: Functions/TCP/tcp_open
===================================================================
RCS file: /cvsroot/zsh/zsh/Functions/TCP/tcp_open,v
retrieving revision 1.2
diff -u -r1.2 tcp_open
--- Functions/TCP/tcp_open 23 Feb 2003 23:24:32 -0000 1.2
+++ Functions/TCP/tcp_open 4 Jul 2003 16:13:47 -0000
@@ -53,11 +53,19 @@
emulate -L zsh
setopt extendedglob cbases
+# Global set up for TCP function suite.
+
zmodload -i zsh/net/tcp || return 1
zmodload -i zsh/zutil
autoload -U 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
+autoload -U tcp_sess tcp_spam tcp_talk tcp_wait tcp_point tcp_shoot
+
+# TCP_SECONDS_START is only set if we override TCP_SECONDS locally,
+# so provide a global value for convenience. Should probably always be 0.
+(( ${+TCP_SECONDS_START} )) || typeset -gF TCP_SECONDS_START
+
+# Processing for new connection.
local opt accept fake nozle sessfile sess quiet
local -a sessnames sessargs
Index: Functions/TCP/tcp_output
===================================================================
RCS file: /cvsroot/zsh/zsh/Functions/TCP/tcp_output,v
retrieving revision 1.2
diff -u -r1.2 tcp_output
--- Functions/TCP/tcp_output 23 Feb 2003 23:24:32 -0000 1.2
+++ Functions/TCP/tcp_output 4 Jul 2003 16:13:47 -0000
@@ -1,7 +1,7 @@
emulate -L zsh
setopt extendedglob
-local opt tprompt sess read_fd tpat quiet
+local opt tprompt sess read_fd tpat quiet cursess
while getopts "F:P:qS:" opt; do
case $opt in
@@ -29,7 +29,12 @@
# where data is coming from; also, it allows more predictable
# behaviour in tcp_expect.
if [[ -n $tprompt ]]; then
- zformat -f REPLY $tprompt "s:$sess" "f:$read_fd"
+ if [[ $sess = $TCP_SESS ]]; then
+ cursess="c:1"
+ else
+ cursess="c:0"
+ fi
+ zformat -f REPLY $tprompt "s:$sess" "f:$read_fd" $cursess
# We will pass this back up.
REPLY="$REPLY$*"
else
Index: Functions/TCP/tcp_point
===================================================================
RCS file: Functions/TCP/tcp_point
diff -N Functions/TCP/tcp_point
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Functions/TCP/tcp_point 4 Jul 2003 16:13:47 -0000
@@ -0,0 +1,29 @@
+emulate -L zsh
+setopt extendedglob cbases
+
+
+if [[ $# -ne 1 ]]; then
+ print "Usage: $0 port
+Listen on the given port; send anything that arrives to standard output." >&2
+ return 1
+fi
+
+local REPLY lfd afd
+if ! ztcp -l $1; then
+ print "Failed to listen on port $1" >&2
+ return 1
+fi
+
+lfd=$REPLY
+
+if ! ztcp -a $lfd; then
+ print "Failed to accept on fd $lfd" >&2
+ ztcp -c $lfd
+fi
+
+afd=$REPLY
+
+cat <&$afd
+
+ztcp -c $lfd
+ztcp -c $afd
Index: Functions/TCP/tcp_read
===================================================================
RCS file: /cvsroot/zsh/zsh/Functions/TCP/tcp_read,v
retrieving revision 1.2
diff -u -r1.2 tcp_read
--- Functions/TCP/tcp_read 25 Apr 2003 11:19:10 -0000 1.2
+++ Functions/TCP/tcp_read 4 Jul 2003 16:13:47 -0000
@@ -60,7 +60,7 @@
zmodload -i zsh/mathfunc
-local opt drain line quiet block read_fd all sess
+local opt drain line quiet block read_fd all sess key val
local -A read_fds
read_fds=()
float timeout timeout_all endtime
@@ -139,8 +139,12 @@
local helper_stat=2 skip tpat reply REPLY
float newtimeout
-# Get extra accuracy by making SECONDS floating point locally
-typeset -F SECONDS
+if [[ ${(t)SECONDS} != float* ]]; then
+ # If called from another function, don't override
+ typeset -F TCP_SECONDS_START=$SECONDS
+ # Get extra accuracy by making SECONDS floating point locally
+ typeset -F SECONDS
+fi
if (( timeout_all )); then
(( endtime = SECONDS + timeout_all ))
@@ -194,11 +198,23 @@
helper_stat=0
sess=${tcp_by_fd[$read_fd]}
- tcp_output -P "${TCP_PROMPT:=<-[%s] }" -S $sess -F $read_fd \
+ tcp_output -P "${TCP_PROMPT=<-[%s] }" -S $sess -F $read_fd \
${TCP_SILENT:+-q} "$line"
# REPLY is now set to the line with an appropriate prompt.
tcp_lines+=($REPLY)
TCP_LINE=$REPLY TCP_LINE_FD=$read_fd
+
+ # Handle user-defined triggers
+ if (( ${+tcp_on_read} )); then
+ # Call the function given in the key for each matching value.
+ # It is this way round because function names must be
+ # unique, while patterns do not need to be. Furthermore,
+ # this keeps the use of subscripting under control.
+ for key val in ${(kv)tcp_on_read}; do
+ [[ $line = ${~val} ]] && $key "$sess" "$line"
+ done
+ fi
+
# Only handle one line from one device at a time unless draining.
[[ -z $drain ]] && return $stat
done
Index: Functions/TCP/tcp_send
===================================================================
RCS file: /cvsroot/zsh/zsh/Functions/TCP/tcp_send,v
retrieving revision 1.1
diff -u -r1.1 tcp_send
--- Functions/TCP/tcp_send 6 Feb 2003 12:21:51 -0000 1.1
+++ Functions/TCP/tcp_send 4 Jul 2003 16:13:47 -0000
@@ -3,6 +3,7 @@
local opt quiet all sess fd nonewline
local -a sessions write_fds
+integer mystat
while getopts "al:nqs:" opt; do
case $opt in
@@ -56,6 +57,11 @@
for TCP_SESS in $sessions; do
fd=${tcp_by_name[$TCP_SESS]}
+ if [[ -z $fd ]]; then
+ print "No such session: $TCP_SESS" >&2
+ mystat=1
+ continue
+ fi
print $nonewline -r -- $* >&$fd
if [[ $? -ne 0 || -n $TCP_FD_CLOSED ]]; then
print "Session ${TCP_SESS}: fd $fd unusable." >&2
@@ -65,3 +71,5 @@
tcp_output -P "$TCP_OUTPUT" -S $TCP_SESS -F $fd -q "${(j. .)*}"
fi
done
+
+return $mystat
Index: Functions/TCP/tcp_shoot
===================================================================
RCS file: Functions/TCP/tcp_shoot
diff -N Functions/TCP/tcp_shoot
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Functions/TCP/tcp_shoot 4 Jul 2003 16:13:47 -0000
@@ -0,0 +1,21 @@
+emulate -L zsh
+setopt extendedglob
+
+local REPLY tfd
+
+if [[ $# -ne 2 ]]; then
+ print "Usage: tcp_dump host port
+Connect to the given host and port; send standard input.">&2
+ return 1
+fi
+
+if ! ztcp $1 $2; then
+ print "Failed to open connection to host $1 port $2" >&2
+ return 1
+fi
+
+tfd=$REPLY
+
+cat >&$tfd
+
+ztcp -c $tfd
Index: Functions/TCP/tcp_spam
===================================================================
RCS file: /cvsroot/zsh/zsh/Functions/TCP/tcp_spam,v
retrieving revision 1.4
diff -u -r1.4 tcp_spam
--- Functions/TCP/tcp_spam 2 May 2003 10:59:07 -0000 1.4
+++ Functions/TCP/tcp_spam 4 Jul 2003 16:13:47 -0000
@@ -4,6 +4,8 @@
# If not given and tcp_spam_list is set to a list of sessions,
# only those will be spammed. If tcp_no_spam_list is set, those
# will (also) be excluded from spamming.
+# -e use `eval' to run the command list instead of executing as
+# a normal command line.
# -l sess1,sess2 give comma separated list of sessions to spam
# -r reverse, spam in opposite order (default is alphabetic, -r means
# omegapsiic). Note tcp_spam_list is not sorted (but may be reversed).
@@ -19,14 +21,17 @@
emulate -L zsh
setopt extendedglob
-local TCP_SESS cmd opt verbose reverse sesslist transmit all
+local cursess=$TCP_SESS sessstr
+local TCP_SESS cmd opt verbose reverse sesslist transmit all eval
local match mbegin mend REPLY
local -a sessions
-while getopts "al:rtv" opt; do
+while getopts "ael:rtv" opt; do
case $opt in
(a) all=1
;;
+ (e) eval=1
+ ;;
(l) sessions+=(${(s.,.)OPTARG})
;;
(r) reverse=1
@@ -82,7 +87,7 @@
if [[ -n $transmit ]]; then
cmd=tcp_send
-else
+elif [[ -z $eval ]]; then
cmd=$1
shift
fi
@@ -95,7 +100,18 @@
tcp_on_spam $TCP_SESS $cmd $*
[[ $REPLY = done ]] && continue
fi
- [[ -n $verbose ]] && zformat -f REPLY $TCP_PROMPT "s:$TCP_SESS" \
- "f:${tcp_by_name[$TCP_SESS]}" && print -r $REPLY
- eval $cmd '$*'
+ if [[ -n $verbose ]]; then
+ if [[ $TCP_SESS = $cursess ]]; then
+ sessstr="c:1"
+ else
+ sessstr="c:0"
+ fi
+ zformat -f REPLY $TCP_PROMPT "s:$TCP_SESS" \
+ "f:${tcp_by_name[$TCP_SESS]}" $sessstr && print -r $REPLY
+ fi
+ if [[ -n $eval ]]; then
+ eval $*
+ else
+ eval $cmd '$*'
+ fi
done
Index: Functions/TCP/tcp_wait
===================================================================
RCS file: /cvsroot/zsh/zsh/Functions/TCP/tcp_wait,v
retrieving revision 1.1
diff -u -r1.1 tcp_wait
--- Functions/TCP/tcp_wait 6 Feb 2003 12:21:51 -0000 1.1
+++ Functions/TCP/tcp_wait 4 Jul 2003 16:13:47 -0000
@@ -1,7 +1,14 @@
# Wait for given number of seconds, reading any data from
# all TCP connections while doing so.
-typeset -F SECONDS to end
+if [[ ${(t)SECONDS} != float* ]]; then
+ # If called from tcp_expect, don't override
+ typeset -F TCP_SECONDS_START=$SECONDS
+ # Get extra accuracy by making SECONDS floating point locally
+ typeset -F SECONDS
+fi
+
+typeset to end
(( to = $1, end = SECONDS + to ))
while (( SECONDS < end )); do
--
Peter Stephenson <pws@xxxxxxx> Software Engineer
CSR Ltd., Science Park, Milton Road,
Cambridge, CB4 0WH, UK Tel: +44 (0)1223 692070
**********************************************************************
The information transmitted is intended only for the person or
entity to which it is addressed and may contain confidential
and/or privileged material.
Any review, retransmission, dissemination or other use of, or
taking of any action in reliance upon, this information by
persons or entities other than the intended recipient is
prohibited.
If you received this in error, please contact the sender and
delete the material from any computer.
**********************************************************************
Messages sorted by:
Reverse Date,
Date,
Thread,
Author