mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-01-01 05:16:05 +01:00
18202: New TCP function system plus small error message change in ztcp.
This commit is contained in:
parent
809ab19dff
commit
5c1f3b65a6
27 changed files with 2139 additions and 5 deletions
|
@ -1,5 +1,13 @@
|
|||
2003-02-06 Peter Stephenson <pws@csr.com>
|
||||
|
||||
* 18202: Functions/TCP/*, Doc/Makefile.in, Doc/zsh.yo,
|
||||
Doc/zshtcpsys.yo, Doc/Zsh/manual.yo, Doc/Zsh/modules.yo,
|
||||
Doc/zsh/tcpsys.yo, Doc/Zsh/zftpsys.yo, Src/Modules/tcp.c,
|
||||
Src/Modules/tcp.mdd: New set of TCP functions tcp_* which
|
||||
run on top of ztcp, documented in zshtcpsys manual. Also
|
||||
sneaked in more informative error message in zsh/net/tcp
|
||||
for failure to bind to a port.
|
||||
|
||||
* Greg Klanderman <gak@klanderman.net>: 18191:
|
||||
Src/Zle/compresult.c: `compctl -y' didn't obey the listpacked
|
||||
and listrowsfirst options.
|
||||
|
|
|
@ -47,7 +47,7 @@ TEXI2HTML = texi2html -expand info -split chapter
|
|||
# man pages to install
|
||||
MAN = zsh.1 zshbuiltins.1 zshcompctl.1 zshcompwid.1 zshcompsys.1 \
|
||||
zshcontrib.1 zshexpn.1 zshmisc.1 zshmodules.1 \
|
||||
zshoptions.1 zshparam.1 zshzftpsys.1 zshzle.1 zshall.1
|
||||
zshoptions.1 zshparam.1 zshtcpsys.1 zshzftpsys.1 zshzle.1 zshall.1
|
||||
|
||||
# yodl documentation
|
||||
|
||||
|
@ -72,7 +72,7 @@ Zsh/filelist.yo Zsh/files.yo Zsh/func.yo Zsh/grammar.yo Zsh/manual.yo \
|
|||
Zsh/index.yo Zsh/intro.yo Zsh/invoke.yo Zsh/jobs.yo Zsh/metafaq.yo \
|
||||
Zsh/modules.yo Zsh/modlist.yo Zsh/modmenu.yo Zsh/manmodmenu.yo $(MODDOCSRC) \
|
||||
Zsh/options.yo Zsh/params.yo Zsh/prompt.yo Zsh/redirect.yo Zsh/restricted.yo \
|
||||
Zsh/seealso.yo Zsh/zftpsys.yo Zsh/zle.yo
|
||||
Zsh/seealso.yo Zsh/tcpsys.yo Zsh/zftpsys.yo Zsh/zle.yo
|
||||
|
||||
# ========== DEPENDENCIES FOR BUILDING ==========
|
||||
|
||||
|
@ -182,6 +182,8 @@ zshoptions.1: Zsh/options.yo
|
|||
|
||||
zshparam.1: Zsh/params.yo
|
||||
|
||||
zshtcpsys.1: Zsh/tcpsys.yo
|
||||
|
||||
zshzftpsys.1: Zsh/zftpsys.yo
|
||||
|
||||
zshzle.1: Zsh/zle.yo
|
||||
|
|
|
@ -33,6 +33,7 @@ menu(Completion Widgets)
|
|||
menu(Completion System)
|
||||
menu(Completion Using compctl)
|
||||
menu(Zsh Modules)
|
||||
menu(TCP Function System)
|
||||
menu(Zftp Function System)
|
||||
menu(User Contributions)
|
||||
|
||||
|
@ -137,6 +138,13 @@ Zsh Modules
|
|||
|
||||
includefile(Zsh/manmodmenu.yo)
|
||||
|
||||
TCP Function System
|
||||
|
||||
menu(TCP Functions)
|
||||
menu(TCP Parameters)
|
||||
menu(TCP Examples)
|
||||
menu(TCP Bugs)
|
||||
|
||||
Zftp Function System
|
||||
|
||||
menu(Installation)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
texinode(Zsh Modules)(Zftp Function System)(Completion Using compctl)(Top)
|
||||
texinode(Zsh Modules)(TCP Function System)(Completion Using compctl)(Top)
|
||||
chapter(Zsh Modules)
|
||||
cindex(modules)
|
||||
sect(Description)
|
||||
|
|
694
Doc/Zsh/tcpsys.yo
Normal file
694
Doc/Zsh/tcpsys.yo
Normal file
|
@ -0,0 +1,694 @@
|
|||
texinode(TCP Function System)(Zftp Function System)(Zsh Modules)(Top)
|
||||
chapter(TCP Function System)
|
||||
cindex(TCP function system)
|
||||
cindex(ztcp, function system based on)
|
||||
sect(Description)
|
||||
|
||||
A module tt(zsh/net/tcp) is provided to provide network I/O over
|
||||
TCP/IP from within the shell; see its description in
|
||||
ifzman(\
|
||||
zmanref(zshmodules)
|
||||
)\
|
||||
ifnzman(\
|
||||
noderef(Zsh Modules)
|
||||
). This manual page describes a function suite based on the module. The
|
||||
functions will usually be installed at the same time as the module if that
|
||||
is present on your system, in which case they will be available for
|
||||
autoloading in the default function search path. In addition to the
|
||||
tt(zsh/net/tcp) module, the tt(zsh/zselect) module is used to implement
|
||||
timeouts on read operations. For troubleshooting tips, consult the
|
||||
corresponding advice for the tt(zftp) functions described in
|
||||
ifzman(\
|
||||
zmanref(zshftpsys)
|
||||
)\
|
||||
ifnzman(\
|
||||
noderef(Zftp Function System)
|
||||
).
|
||||
|
||||
There are functions corresponding to the basic I/O operations open, close,
|
||||
read and send, named tt(tcp_open) etc., as well as a function
|
||||
tt(tcp_expect) for pattern match analysis of data read as input. The
|
||||
system makes it easy to receive data from and send data to multiple named
|
||||
sessions at once. In addition, it can be linked with the shell's line
|
||||
editor in such a way that input data is automatically shown at the
|
||||
terminal. Other facilities available including logging, filtering and
|
||||
configurable output prompts.
|
||||
|
||||
To use the system where it is available, it should be enough to
|
||||
`tt(autoload -U tcp_open)' and run tt(tcp_open) as documented below to
|
||||
start a session. The tt(tcp_open) function will autoload the remaining
|
||||
functions.
|
||||
|
||||
startmenu()
|
||||
menu(TCP Functions)
|
||||
menu(TCP Parameters)
|
||||
menu(TCP Examples)
|
||||
menu(TCP Bugs)
|
||||
endmenu()
|
||||
|
||||
texinode(TCP Functions)(TCP Parameters)()(TCP Function System)
|
||||
sect(TCP User Functions)
|
||||
|
||||
subsect(Basic I/O)
|
||||
|
||||
startitem()
|
||||
findex(tcp_open)
|
||||
xitem(tt(tcp_open [-qz]) var(host port) tt([) var(sess) tt(]))
|
||||
xitem(tt(tcp_open [-qz] [ -s) var(sess) tt(| -l) var(sess)tt(,... ] ... ))
|
||||
item(tt(tcp_open [-qz] [-a) var(fd) tt(| -f) var(fd) tt(] [) var(sess) tt(]))(
|
||||
Open a new session. In the first and simplest form, open a TCP connection
|
||||
to host var(host) at port var(port); numeric and symbolic forms are
|
||||
understood for both.
|
||||
|
||||
If var(sess) is given, this becomes the name of the session which can be
|
||||
used to refer to multiple different TCP connections. If var(sess) is
|
||||
not given, the function will invent a numeric name value (note this is
|
||||
em(not) the same as the file descriptor to which the session is attached).
|
||||
It is recommended that session names not include `funny' characters, where
|
||||
funny characters are not well-defined but certainly do not include
|
||||
alphanumerics or underscores, and certainly do include whitespace.
|
||||
|
||||
In the second case, one or more sessions to be opened are given by name.
|
||||
A single session name is given after tt(-s) and a comma-separated list
|
||||
after tt(-l); both options may be repeated as many times as necessary.
|
||||
The host and port are read from the file tt(.ztcp_sessions) in the same
|
||||
directory as the user's zsh initialisation files, i.e. usually the home
|
||||
directory, but tt($ZDOTDIR) if that is set. The file consists of lines
|
||||
each giving a session name and the corresponding host and port, in that
|
||||
order (note the session name comes first, not last), separated by
|
||||
whitespace.
|
||||
|
||||
The third form allows passive and fake TCP connections. If the option
|
||||
tt(-a) is used, its argument is a file descriptor open for listening for
|
||||
connections. No function front-end is provided to open such a file
|
||||
descriptor, but a call to `tt(ztcp -l) var(port)' will create one with the
|
||||
file descriptor stored in the parameter tt($REPLY). The listening port can
|
||||
be closed with `tt(ztcp -c) var(fd)'. A call to `tt(tcp_open -a) var(fd)'
|
||||
will block until a remote TCP connection is made to var(port) on the local
|
||||
machine. At this point, a session is created in the usual way and is
|
||||
largely indistinguishable from an active connection created with one of the
|
||||
first two forms.
|
||||
|
||||
If the option tt(-f) is used, its argument is a file descriptor which is
|
||||
used directly as if it were a TCP session. How well the remainder of the
|
||||
TCP function system copes with this depends on what actually underlies this
|
||||
file descriptor. A regular file is likely to be unusable; a FIFO (pipe) of
|
||||
some sort will work better, but note that it is not a good idea for two
|
||||
different sessions to attempt to read from the same FIFO at once.
|
||||
|
||||
If the option tt(-q) is given with any of the three forms, tt(tcp_open)
|
||||
will not print informational messages, although it will in any case exit
|
||||
with an appropriate status.
|
||||
|
||||
If the line editor (zle) is in use, which it usually is if and only if the
|
||||
shell is interactive, tt(tcp_open) installs a handler inside tt(zle) which
|
||||
will check for new data at the same time as it checks for keyboard input.
|
||||
This is convenient as the shell consumes no CPU time while waiting; the
|
||||
test is performed by the operating systems. However, if incoming data
|
||||
is only to be read explicitly, the option tt(-z) to any of the forms of
|
||||
tt(tcp_open) prevents the handler from being installed. Note this is not
|
||||
necessary for executing complete sets of send and read commands from a
|
||||
function, as zle is not active at this point. Generally speaking, the
|
||||
handler is only active when the shell is waiting for input at a command
|
||||
prompt or in the tt(vared) builtin. The option has no effect if zle is not
|
||||
active; `tt([[ -o zle]])' will test for this.
|
||||
|
||||
The first session to be opened becomes the current session; subsequent
|
||||
calls to tt(tcp_open) will not change this. The current session is stored
|
||||
in the parameter tt($TCP_SESS); see below for more detail about the
|
||||
parameters used by the system.
|
||||
)
|
||||
findex(tcp_close)
|
||||
item(tt(tcp_close [-qn] [ -a | -l) var(sess)tt(,... |) var(sess) tt(... ]))(
|
||||
Close the named sessions, or the current session if none is given,
|
||||
or all open sessions if tt(-a) is given. The options tt(-l) and tt(-s) are
|
||||
both handled for consistency with tt(tcp_open), although the latter is
|
||||
redundant.
|
||||
|
||||
If the session being closed is the current one, tt($TCP_SESS) is unset,
|
||||
leaving no current session, even if there are other sessions still open.
|
||||
|
||||
If the session was opened with tt(tcp_open -f), the file descriptor is
|
||||
closed so long as it is in the range 0 to 9 accessible directly from the
|
||||
command line. If the option tt(-n) is given, no attempt will be made to
|
||||
close file descriptors in this case. The tt(-n) option is not used for
|
||||
genuine tt(ztcp) session; the file descriptors are always closed with the
|
||||
session.
|
||||
|
||||
If the option tt(-q) is given, no informational messages will be printed.
|
||||
)
|
||||
findex(tcp_read)
|
||||
xitem(tt(tcp_read [-bdq] [ -t) var(TO) tt(] [ -T) var(TO) tt(]))
|
||||
item( tt([ -a | -u) var(fd) tt(... | -l) var(sess)tt(,... | -s) var(sess) tt(...]))(
|
||||
Perform a read operation on the current session, or on a list of sessions
|
||||
if any are given (the first form), or all open sessions (the second form).
|
||||
Any of the tt(-u), tt(-l) or tt(-s) options may be repeated or mixed
|
||||
together. The tt(-u) option specifies a file descriptor directly (only
|
||||
those managed by this system are useful), the other two specify sessions as
|
||||
described for tt(tcp_open) above. If tt(-a) is given, all sessions ares
|
||||
examined for new data.
|
||||
|
||||
The function checks for new data available on all the sessions listed.
|
||||
Unless the tt(-b) option is given, it will not block waiting for new data.
|
||||
Any one line of data from any of the available sessions will be read,
|
||||
stored in the parameter tt($TCP_LINE), and displayed to standard output
|
||||
unless tt($TCP_SILENT) contains a non-empty string. When printed to
|
||||
standard output the string tt($TCP_PROMPT) will be shown at the start of
|
||||
the line; the default form for this includes the name of the session being
|
||||
read. See below for more information on these parameters. In this mode,
|
||||
tt(tcp_read) can be called repeatedly until it returns status 2 which
|
||||
indicates all pending input from all specified sessions has been handled.
|
||||
|
||||
With the option tt(-b), equivalent to an infinite timeout, the function
|
||||
will block until a line is available to read from one of the specified
|
||||
sessions. However, only a single line is returned.
|
||||
|
||||
The option tt(-d) indicates that all pending input should be drained. In
|
||||
this case tt(tcp_read) may process multiple lines in the manner given
|
||||
above; only the last is stored in tt($TCP_LINE), but the complete set is
|
||||
stored in the array tt($tcp_lines). This is cleared at the start of each
|
||||
call to tt(tcp_read).
|
||||
|
||||
The options tt(-t) and tt(-T) specify a timeout in seconds, which may be a
|
||||
floating point number for increased accuracy. With tt(-t) the timeout is
|
||||
applied before each line read. With tt(-T), the timeout applies to the
|
||||
overall operation, possibly including multiple read operations if the
|
||||
option tt(-d) is present; without this option, there is no distinction
|
||||
between tt(-t) and tt(-T).
|
||||
|
||||
The function does not print informational messages, but if the option
|
||||
tt(-q) is given, no error message is printed for a non-existent session.
|
||||
|
||||
A return value of 2 indicates a timeout or no data to read. Any other
|
||||
non-zero return value indicates some error condition.
|
||||
|
||||
See tt(tcp_log) for how to control where data is sent by tt(tcp_read).
|
||||
)
|
||||
findex(tcp_send)
|
||||
xitem(tt(tcp_send [-nq] [ -s) var(sess) tt(| -l) var(sess)tt(,... ]) var(data) tt(...))
|
||||
item(tt(tcp_send [-nq] -a) var(data) tt(...))(
|
||||
Send the supplied data strings to all the specified sessions in turn. The
|
||||
underlying operation differs little from a `tt(print -r)' to the session's
|
||||
file descriptor, although it attempts to prevent the shell from dying owing
|
||||
to a tt(SIGPIPE) caused by an attempt to write to a defunct session.
|
||||
|
||||
The option tt(-n) prevents tt(tcp_send) from putting a newline at the end
|
||||
of the data strings.
|
||||
|
||||
The remaining options all behave as for tt(tcp_read).
|
||||
|
||||
The data arguments are not further processed once they have been passed to
|
||||
tt(tcp_send); they are simply passed down to tt(print -r).
|
||||
|
||||
If the parameter tt($TCP_OUTPUT) is a non-empty string and logging is
|
||||
enabled then the data sent to each session will be echoed to the log
|
||||
file(s) with tt($TCP_OUTPUT) in front where appropriate, much in the manner
|
||||
of tt($TCP_PROMPT).
|
||||
)
|
||||
enditem()
|
||||
|
||||
subsect(Session Management)
|
||||
|
||||
startitem()
|
||||
findex(tcp_alias)
|
||||
xitem(tt(tcp_alias [-q]) var(alias)tt(=)var(sess) tt(...))
|
||||
xitem(tt(tcp_alias [-q] [) var(alias) tt(] ...))
|
||||
item(tt(tcp_alias -d [-q]) var(alias) tt(...))(
|
||||
This function is not particularly well tested.
|
||||
|
||||
The first form creates an alias for a session name; var(alias) can then be
|
||||
used to refer to the existing session var(sess). As many aliases may be
|
||||
listed as required.
|
||||
|
||||
The second form lists any aliases specified, or all aliases if none.
|
||||
|
||||
The third form deletes all the aliases listed. The underlying sessions are
|
||||
not affected.
|
||||
|
||||
The option tt(-q) suppresses an inconsistently chosen subset of error
|
||||
messages.
|
||||
)
|
||||
findex(tcp_log)
|
||||
item(tt(tcp_log [-asc] [ -n | -N ] [) var(logfile) tt(]))(
|
||||
With an argument var(logfile), all future input from tt(tcp_read) will be
|
||||
logged to the named file. Unless tt(-a) (append) is given, this file will
|
||||
first be truncated or created empty. With no arguments, show the current
|
||||
status of logging.
|
||||
|
||||
With the option tt(-s), per-session logging is enabled. Input from
|
||||
tt(tcp_read) is output to the file var(logfile).var(sess). As the
|
||||
session is automatically discriminated by the filename, the contents are
|
||||
raw (no tt($TCP_PROMPT)). The option tt(-a) applies as above.
|
||||
Per-session logging and logging of all data in one file are not mutually
|
||||
exclusive.
|
||||
|
||||
The option tt(-c) closes all logging, both complete and per-session logs.
|
||||
|
||||
The options tt(-n) and tt(-N) respectively turn off or restore output of
|
||||
data read by tt(tcp_read) to standard output; hence `tt(tcp_log -cn)' turns
|
||||
off all output by tt(tcp_read).
|
||||
|
||||
The function is purely a convenient front end to setting the parameters
|
||||
tt($TCP_LOG), tt($TCP_LOG_SESS), tt($TCP_SILENT), which are described below.
|
||||
)
|
||||
findex(tcp_rename)
|
||||
item(tt(tcp_rename) var(old) var(new))(
|
||||
Rename session var(old) to session var(new). The old name becomes invalid.
|
||||
)
|
||||
findex(tcp_sess)
|
||||
item(tt(tcp_sess [) var(sess) tt([) var(command) tt(... ] ]))(
|
||||
With no arguments, list all the open sessions and associated file
|
||||
descriptors. The current session is marked with a star. For use in
|
||||
functions, direct access to the parameters tt($tcp_by_name), tt($tcp_by_fd)
|
||||
and tt($TCP_SESS) is probably more convenient; see below.
|
||||
|
||||
With a var(sess) argument, set the current session to var(sess).
|
||||
This is equivalent to changing tt($TCP_SESS) directly.
|
||||
|
||||
With additional arguments, temporarily set the current session while
|
||||
executing the string tt(command ...). The first argument is re-evaluated
|
||||
so as to expand aliases etc., but the remaining arguments are passed
|
||||
through as the appear to tt(tcp_sess). The original session is restored
|
||||
when tt(tcp_sess) exits.
|
||||
)
|
||||
enditem()
|
||||
|
||||
subsect(Advanced I/O)
|
||||
|
||||
startitem()
|
||||
findex(tcp_command)
|
||||
item(tt(tcp_command) var(send-options) tt(...) var(send-arguments) tt(...))(
|
||||
This is a convenient front-end to tt(tcp_send). All arguments are passed
|
||||
to tt(tcp_send), then the function pauses waiting for data. While data is
|
||||
arriving at least every tt($TCP_TIMEOUT) (default 0.3) seconds, data is
|
||||
handled and printed out according to the current settings. Status 0 is
|
||||
always returned.
|
||||
|
||||
This is generally only useful for interactive use, to prevent the display
|
||||
becomming fragmented by output returned from the connection. Within a
|
||||
programe or function it is generally better to handle reading data by a
|
||||
more explicit method.
|
||||
)
|
||||
findex(tcp_expect)
|
||||
xitem(tt(tcp_expect [ -q ] [ -p) var(var) tt(] [ -t ) var(to) tt(| -T) var(TO)tt(]))
|
||||
item(tt( [ -a | -s) var(sess) tt(... | -l) var(sess)tt(,... ]) var(pattern) ...)(
|
||||
Wait for input matching any of the given var(pattern)s from any of the
|
||||
specified sessions. Input is ignored until an input line matches one of
|
||||
the given patterns; at this point status zero is returned, the matching
|
||||
line is stored in tt($TCP_LINE), and the full set of lines read during the
|
||||
call to tt(tcp_expect) is stored in the array tt($tcp_expect_lines).
|
||||
|
||||
Sessions are specified in the same way as tt(tcp_read): the default is to
|
||||
use the current session, otherwise the sessions specified by tt(-a),
|
||||
tt(-s), or tt(-l) are used.
|
||||
|
||||
Each var(pattern) is a standard zsh extended-globbing pattern; note that it
|
||||
needs to be quoted to avoid it being expanded immediately by filename
|
||||
generation. It must match the full line, so to match a substring there
|
||||
must be a `tt(*)' at the start and end. The line matched against includes
|
||||
the tt($TCP_PROMPT) added by tt(tcp_read). It is possible to include the
|
||||
globbing flags `tt(#b)' or `tt(#m)' in the patterns to make backreferences
|
||||
available in the parameters tt($MATCH), tt($match), etc., as described in
|
||||
the base zsh documentation on pattern matching.
|
||||
|
||||
Unlike tt(tcp_read), the default behaviour of tt(tcp_expect) is to block
|
||||
indefinitely until the required input is found. This can be modified by
|
||||
specifying a timeout with tt(-t) or tt(-T); these function as in
|
||||
tt(tcp_read), specifying a per-read or overall timeout, respectively, in
|
||||
seconds, as an integer or floating-point number. As tt(tcp_read), the
|
||||
function returns status 2 if a timeout occurs.
|
||||
|
||||
The function returns as soon as any one of the patterns given match. If
|
||||
the caller needs to know which of the patterns matched, the option tt(-p)
|
||||
var(var) can be used; on return, tt($var) is set to the number of the
|
||||
pattern using ordinary zsh indexing, i.e. the first is 1, and so on. Note
|
||||
tha absence of a `tt($)' in front of var(var). To avoid clashes, the
|
||||
parameter cannot begin with `tt(_expect)'.
|
||||
|
||||
The option tt(-q) is passed directly down to tt(tcp_read).
|
||||
|
||||
As all input is done via tt(tcp_read), all the usual rules about output of
|
||||
lines read apply. One exception is that the parameter tt($tcp_lines) will
|
||||
only reflect the line actually matched by tt(tcp_expect); use
|
||||
tt($tcp_expect_lines) for the full set of lines read during the function
|
||||
call.
|
||||
)
|
||||
findex(tcp_proxy)
|
||||
item(tt(tcp_proxy))(
|
||||
This is a simple-minded function to accept a TCP connection and execute a
|
||||
command with I/O redirected to the connection. Extreme caution should be
|
||||
taken as there is no security whatsoever and this can leave your computer
|
||||
open to the world. Ideally, it should only be used behind a firewall.
|
||||
|
||||
The first argument is a TCP port on which the function will listen.
|
||||
|
||||
The remaining arguments give a command and its arguments to execute with
|
||||
standard input, standard output and standard error redirected to the
|
||||
file descriptor on which the TCP session has been accepted.
|
||||
If no command is given, a new zsh is started. This gives everyone on
|
||||
your network direct access to your account, which in many cases will be a
|
||||
bad thing.
|
||||
|
||||
The command is run in the background, so tt(tcp_proxy) can then accept new
|
||||
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(...))(
|
||||
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.
|
||||
|
||||
The sessions may be selected explicitly with the standard tt(-a), tt(-s) or
|
||||
tt(-l) options, or may be chosen implicitly. If none of the three options
|
||||
is given the rules are: first, if the array tt($tcp_spam_list) is set, this
|
||||
is taken as the list of sessions, otherwise all sessions are taken.
|
||||
Second, any sessions given in the array tt($tcp_no_spam_list) are removed
|
||||
from the list of sessions.
|
||||
|
||||
Normally, any sessions added by the `tt(-a)' flag or when all sessions are
|
||||
chosen implicitly are spammed in alphabetic order; sessions given by the
|
||||
tt($tcp_spam_list) array or on the command line are spammed in the order
|
||||
given. The tt(-r) flag reverses the order however it was arrived it.
|
||||
|
||||
The tt(-v) flag specifies that a tt($TCP_PROMPT) will be output before each
|
||||
session. This is output after any modfication to TCP_SESS by the
|
||||
user-defined tt(tcp_on_spam) function described below. (Obviously that
|
||||
function is able to generate its own output.)
|
||||
)
|
||||
findex(tcp_talk)
|
||||
item(tt(tcp_talk))(
|
||||
This is a fairly simple-minded attempt to force input to the line editor to
|
||||
go straight to the default TCP_SESSION.
|
||||
|
||||
An escape string, tt($TCP_TALK_ESCAPE), default `:', is used to allow
|
||||
access to normal shell operation. If it is on its own at the start of the
|
||||
line, or followed only by whitespace, the line editor returns to normal
|
||||
operation. Otherwise, the string and any following whitespace are skipped
|
||||
and the remainder of the line executed as shell input without any change of
|
||||
the line editor's operating mode.
|
||||
|
||||
The current implementation is somewhat deficient in terms of use of the
|
||||
command history. For this reason, many users will prefer to use some form
|
||||
of alternative approach for sending data easily to the current session.
|
||||
One simple approach is to alias some special character (such as `tt(%)') to
|
||||
`tt(tcp_command --)'.
|
||||
)
|
||||
findex(tcp_wait)
|
||||
item(tt(tcp_wait))(
|
||||
The sole argument is an integer or floating point number which gives the
|
||||
seconds to delay. The shell will do nothing for that period except wait
|
||||
for input on all TCP sessions by calling tt(tcp_read -a). This is similar
|
||||
to the interactive behaviour at the command prompt when zle handlers are
|
||||
installed.
|
||||
)
|
||||
enditem()
|
||||
|
||||
sect(TCP User-defined Function)
|
||||
|
||||
Certain functions, if defined by the user, will be called by the function
|
||||
system in certain contexts. This facility depends on the module
|
||||
tt(zsh/parameter), which is usually available in interactive shells as the
|
||||
completion system depends on it. None of the functions need by defined;
|
||||
they simply provide convenient hooks when necessary.
|
||||
|
||||
Typically, these are called after the requested action has been taken, so
|
||||
that the various parameters will reflect the new state.
|
||||
|
||||
startitem()
|
||||
findex(tcp_on_alias)
|
||||
item(tt(tcp_on_alias) var(alias) var(fd))(
|
||||
When an alias is defined, this function will be called with two arguments:
|
||||
the name of the alias, and the file descriptor of the corresponding session.
|
||||
)
|
||||
findex(tcp_on_close)
|
||||
item(tt(tcp_on_close) var(sess) var(fd))(
|
||||
This is called with the name of a session being closed and the file
|
||||
descriptor which corresponded to that session. Both will be invalid by
|
||||
the time the function is called.
|
||||
)
|
||||
findex(tcp_on_open)
|
||||
item(tt(tcp_on_open) var(sess) var(fd))(
|
||||
This is called after a new session has been defined with the session name
|
||||
and file descriptor as arguments.
|
||||
)
|
||||
findex(tcp_on_rename)
|
||||
item(tt(tcp_on_rename) var(oldsess) var(fd) var(newsess))(
|
||||
This is called after a session has been renamed with the three arguments
|
||||
old session name, file descriptor, new session name.
|
||||
)
|
||||
findex(tcp_on_spam)
|
||||
item(tt(tcp_on_spam) var(sess) var(command) tt(...))(
|
||||
This is called once for each session spammed, just em(before) a command is
|
||||
executed for a session by tt(tcp_spam). The arguments are the session name
|
||||
followed by the command list to be executed. If tt(tcp_spam) was called
|
||||
with the option tt(-t), the first command will be tt(tcp_send).
|
||||
|
||||
This function is called after tt($TCP_SESS) is set to reflect the session
|
||||
to be spammed, but before any use of it is made. Hence it is possible to
|
||||
alter the value of tt($TCP_SESS) within this function. For example, the
|
||||
session arguments to tt(tcp_spam) could include extra information to be
|
||||
stripped off and processed in tt(tcp_on_spam).
|
||||
|
||||
If the function sets the parameter tt($REPLY) to `tt(done)', the command
|
||||
line is not executed; in addition, no prompt is printed for the tt(-v)
|
||||
option to tt(tcp_spam).
|
||||
)
|
||||
findex(tcp_on_unalias)
|
||||
item(tt(tcp_on_unalias) var(alias) var(fd))(
|
||||
This is called with the name of an alias and the corresponding session's
|
||||
file descriptor after an alias has been deleted.
|
||||
)
|
||||
enditem()
|
||||
|
||||
sect(TCP Utility Functions)
|
||||
|
||||
The following functions are used by the TCP function system but will rarely
|
||||
if ever need to be called directly.
|
||||
|
||||
startitem()
|
||||
findex(tcp_fd_handler)
|
||||
item(tt(tcp_fd_handler))(
|
||||
This is the function installed by tt(tcp_open) for handling input from
|
||||
within the line editor, if that is required. It is in the format
|
||||
documented for the builtin `tt(zle -F)' in
|
||||
ifzman(\
|
||||
zmanref(zshzle)
|
||||
)\
|
||||
ifnzman(\
|
||||
noderef(Zle Builtins)
|
||||
).
|
||||
)
|
||||
findex(tcp_output)
|
||||
item(tt(tcp_output [ -q ] -P) var(prompt) tt(-F) var(fd) tt(-S) var(sess))(
|
||||
This function is used for both logging and handling output to standard
|
||||
output, from within tt(tcp_read) and (if tt($TCP_OUTPUT) is set)
|
||||
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(%)'.
|
||||
|
||||
The option tt(-q) suppresses output to standard output, but not to any log
|
||||
files which are configured.
|
||||
|
||||
The tt(-S) and tt(-F) options are used to pass in the session name and file
|
||||
descriptor for possible replacement in the prompt.
|
||||
)
|
||||
findex(zgprintf)
|
||||
item(tt(zgprintf) tt(-rPR -%)var(X)tt(=)var(subst) var(fmt) tt([) var(val) tt(... ]))(
|
||||
This function is used for performing tt(%)-replacement in prompts supplied
|
||||
to tt(tcp_output). The var(fmt) string is printed to standard output.
|
||||
The option tt(-%)var(X)tt(=)var(subst) specifies that any occurrence
|
||||
of tt(%)var(X) in the var(fmt) string should be replaced by var(subst).
|
||||
These arguments may be repeated for arbitrary var(X).
|
||||
|
||||
The option tt(-r) specifies that the normal tt(print) conventions are not
|
||||
to be used, as with the corresponding argument to the tt(print) builtin.
|
||||
|
||||
The option tt(-R) specifies that the output is to be left in the parameter
|
||||
tt($REPLY) instead of being printed.
|
||||
|
||||
The option tt(-P) specifies that unhandled tt(%)-escapes should be
|
||||
formatted by a call to tt(printf). Each is assumed to consume exactly one
|
||||
additional var(val) argument. This option is only minimally implemented.
|
||||
)
|
||||
enditem()
|
||||
|
||||
texinode(TCP Parameters)(TCP Examples)(TCP Functions)(TCP Function System)
|
||||
sect(TCP User Parameters)
|
||||
|
||||
Parameters follow the usual convention that uppercase is used for scalars
|
||||
and integers, while lowercase is used for normal and associative array.
|
||||
It is always safe for user code to read these parameters; some parameters
|
||||
may also be set, which are noted explicitly. Other are included in this
|
||||
group as they are set by the function system for the user's benefit,
|
||||
i.e. setting them is typically not useful but is benign.
|
||||
|
||||
It is often also useful to make settable parameters local to a function.
|
||||
For example, `tt(local TCP_SILENT=1)' specifies that data read during the
|
||||
function call will not be printed to standard output, regardless of the
|
||||
setting outside the function. Likewise, `tt(local TCP_SESS=)var(sess)'
|
||||
sets a session for the duration of a function.
|
||||
|
||||
startitem()
|
||||
findex(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)
|
||||
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)
|
||||
item(tt(TCP_LINE))(
|
||||
The last line read by tt(tcp_read), and hence also tt(tcp_expect).
|
||||
)
|
||||
findex(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)
|
||||
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)
|
||||
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.
|
||||
The output is proceeded by the usual tt($TCP_PROMPT). If it is not an
|
||||
absolute path name, it will follow the user's current directory.
|
||||
)
|
||||
findex(TCP_LOG_SESS)
|
||||
item(tt(TCP_LOG_SESS))(
|
||||
May be set directly, although it is also controlled by tt(tcp_log).
|
||||
The prefix for a set of files to which output from each session separately
|
||||
will be sent; the full filename is tt(${TCP_LOG_SESS}.)var(sess).
|
||||
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)
|
||||
item(tt(tcp_nospam_list))(
|
||||
Array. May be set directly. See tt(tcp_spam) for how this is used.
|
||||
)
|
||||
findex(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
|
||||
tt(TCP_PROMPT) and the same rules for its use apply: it is used in a file
|
||||
specified by tt($TCP_LOG), but not in a file generated from
|
||||
tt($TCP_LOG_SESS).
|
||||
)
|
||||
findex(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.
|
||||
)
|
||||
findex(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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
item(tt(TCP_TIMEOUT))(
|
||||
May be set directly. Currently this is only used by the function
|
||||
tt(tcp_command), see above.
|
||||
)
|
||||
enditem()
|
||||
|
||||
sect(TCP Utility Parameters)
|
||||
|
||||
These parameters are controlled by the function system; they may be read
|
||||
directly, but should not usually be set by user code.
|
||||
|
||||
startitem()
|
||||
findex(tcp_aliases)
|
||||
item(tt(tcp_aliases))(
|
||||
Associative array. The keys are the names of sessions established with
|
||||
tt(tcp_open); each value is a space-separated list of aliases which refer
|
||||
to that session.
|
||||
)
|
||||
findex(tcp_by_fd)
|
||||
item(tt(tcp_by_fd))(
|
||||
Associative array. The keys are session file descriptors; each
|
||||
value is the name of that session.
|
||||
)
|
||||
findex(tcp_by_name)
|
||||
item(tt(tcp_by_name))(
|
||||
Associative array. The keys are the names of sessions; each value is the
|
||||
file descriptor associated with that session.
|
||||
)
|
||||
enditem()
|
||||
|
||||
texinode(TCP Examples)(TCP Bugs)(TCP Parameters)(TCP Function System)
|
||||
sect(TCP Examples)
|
||||
|
||||
Here is a trivial example using a remote calculator.
|
||||
|
||||
TO create a calculator server on port 7337 (see the tt(dc) manual page for
|
||||
quite how infuriating the underlying command is):
|
||||
|
||||
example(tcp_proxy 7337 dc)
|
||||
|
||||
To connect to this from the same host with a session also named `tt(dc)':
|
||||
|
||||
example(tcp_open localhost 7337 dc)
|
||||
|
||||
To send a command to the remote session and wait a short while for output
|
||||
(assuming tt(dc) is the current session):
|
||||
|
||||
example(tcp_command 2 4 + p)
|
||||
|
||||
To close the session:
|
||||
|
||||
example(tcp_close)
|
||||
|
||||
The tt(tcp_proxy) needs to be killed to be stopped. Note this will not
|
||||
usually kill any connections which have already been accepted, and also
|
||||
that the port is not immediately available for reuse.
|
||||
|
||||
The following chunk of code puts a list of sessions into an xterm header,
|
||||
with the current session followed by a star.
|
||||
|
||||
example(print -n "\033]2;TCP:" ${(k)tcp_by_name:/$TCP_SESS/$TCP_SESS\*} "\a")
|
||||
|
||||
texinode(TCP Bugs)()(TCP Examples)(TCP Function System)
|
||||
sect(TCP Bugs)
|
||||
|
||||
The function tt(tcp_read) uses the shell's normal tt(read) builtin. As
|
||||
this reads a complete line at once, data arriving without a terminating
|
||||
newline can cause the function to block indefinitely.
|
||||
|
||||
Though the function suite works well for interactive use and for data
|
||||
arriving in small amounts, the performance when large amounts of data are
|
||||
being exchanged is likely to be extremely poor.
|
|
@ -1,4 +1,4 @@
|
|||
texinode(Zftp Function System)(User Contributions)(Zsh Modules)(Top)
|
||||
texinode(Zftp Function System)(User Contributions)(TCP Function System)(Top)
|
||||
chapter(Zftp Function System)
|
||||
cindex(zftp function system)
|
||||
cindex(FTP, functions for using shell as client)
|
||||
|
|
|
@ -63,6 +63,7 @@ ifnzman(includefile(Zsh/compwid.yo))
|
|||
ifnzman(includefile(Zsh/compsys.yo))
|
||||
ifnzman(includefile(Zsh/compctl.yo))
|
||||
ifnzman(includefile(Zsh/modules.yo))
|
||||
ifnzman(includefile(Zsh/tcpsys.yo))
|
||||
ifnzman(includefile(Zsh/zftpsys.yo))
|
||||
ifnzman(includefile(Zsh/contrib.yo))
|
||||
ifzshall(\
|
||||
|
@ -78,6 +79,7 @@ source(zshcompwid)
|
|||
source(zshcompsys)
|
||||
source(zshcompctl)
|
||||
source(zshmodules)
|
||||
source(zshtcpsys)
|
||||
source(zshzftpsys)
|
||||
source(zshcontrib)
|
||||
manpage(ZSHALL)(1)(date())(zsh version())
|
||||
|
|
3
Doc/zshtcpsys.yo
Normal file
3
Doc/zshtcpsys.yo
Normal file
|
@ -0,0 +1,3 @@
|
|||
manpage(ZSHTCPSYS)(1)(date())(zsh version())
|
||||
manpagename(zshtcpsys)(zsh tcpletion system)
|
||||
includefile(Zsh/tcpsys.yo)
|
156
Functions/TCP/tcp_alias
Normal file
156
Functions/TCP/tcp_alias
Normal file
|
@ -0,0 +1,156 @@
|
|||
# Create an alias for a TCP session.
|
||||
#
|
||||
# The syntax is similar to the `alias' builtin. Aliases with a trailing
|
||||
# `=' are assigned, while those without are listed.
|
||||
#
|
||||
# The alias can be used to refer to the session, however any output
|
||||
# from the session will be shown using information for the base
|
||||
# session name. Likewise, any other reference to the session's file
|
||||
# descriptor will cause the original session name rather than the alias to
|
||||
# be used.
|
||||
#
|
||||
# It is an error to attempt to create an alias for a non-existent session.
|
||||
# The alias will be removed when the session is closed.
|
||||
#
|
||||
# An alias can be reused without the session having to be closed.
|
||||
# However, a base session name cannot be used as an alias. If this
|
||||
# becomes necessary, the base session should be renamed with tcp_rename
|
||||
# first.
|
||||
#
|
||||
# With no arguments, list aliases.
|
||||
#
|
||||
# With the option -d, delete the alias. No value is allowed in this case.
|
||||
#
|
||||
# With the option -q (quiet), just return status 1 on failure. This
|
||||
# does not apply to bad syntax, which is always reported. Bad syntax
|
||||
# includes deleting aliases when supplying a value.
|
||||
|
||||
emulate -L zsh
|
||||
setopt extendedglob cbases
|
||||
|
||||
local opt quiet base value alias delete arg match mbegin mend fd array
|
||||
integer stat index
|
||||
|
||||
while getopts "qd" opt; do
|
||||
case $opt in
|
||||
(q) quiet=1
|
||||
;;
|
||||
(d) delete=1
|
||||
;;
|
||||
(*) return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
|
||||
|
||||
if (( ! $# )); then
|
||||
if (( ${#tcp_aliases} )); then
|
||||
for fd value in ${(kv)tcp_aliases}; do
|
||||
for alias in ${=value}; do
|
||||
print -r - \
|
||||
"${alias}: alias for session ${tcp_by_fd[$fd]:-unnamed fd $fd}"
|
||||
done
|
||||
done
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
for arg in $*; do
|
||||
if [[ $arg = (#b)([^=]##)=(*) ]]; then
|
||||
if [[ -n $delete ]]; then
|
||||
print "$0: value given with deletion command." >&2
|
||||
stat=1
|
||||
continue
|
||||
fi
|
||||
alias=$match[1]
|
||||
base=$match[2]
|
||||
if [[ -z $base ]]; then
|
||||
# hmm, this is very nearly a syntax error...
|
||||
[[ -z $quiet ]] && print "$0: empty value for alias $alias" >&2
|
||||
stat=1
|
||||
continue
|
||||
fi
|
||||
if [[ ${+tcp_by_name} -eq 0 || -z ${tcp_by_name[$base]} ]]; then
|
||||
[[ -z $quiet ]] && print "$0: no base session \`$base' for alias"
|
||||
stat=1
|
||||
continue
|
||||
fi
|
||||
if [[ -n ${tcp_by_name[$alias]} ]]; then
|
||||
# already exists, OK if this is an alias...
|
||||
fd=${tcp_by_name[$alias]}
|
||||
array=(${=tcp_aliases[$fd]})
|
||||
if [[ -n ${array[(r)$alias]} ]]; then
|
||||
# yes, we're OK; delete the old alias.
|
||||
unset "tcp_by_name[$alias]"
|
||||
index=${array[(i)$alias]}
|
||||
array=(${array[1,index-1]} ${array[index+1,-1]})
|
||||
if [[ -z "$array" ]]; then
|
||||
unset "tcp_aliase[$fd]"
|
||||
else
|
||||
tcp_aliases[$fd]="$array"
|
||||
fi
|
||||
else
|
||||
# oops
|
||||
if [[ -z $quiet ]]; then
|
||||
print "$0: \`$alias' is already a session name." >&2
|
||||
fi
|
||||
stat=1
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
(( ! ${+tcp_aliases} )) && typeset -gA tcp_aliases
|
||||
fd=${tcp_by_name[$base]}
|
||||
if [[ -n ${tcp_aliases[$fd]} ]]; then
|
||||
tcp_aliases[$fd]+=" $alias"
|
||||
else
|
||||
tcp_aliases[$fd]=$alias
|
||||
fi
|
||||
tcp_by_name[$alias]=$fd
|
||||
if zmodload -i zsh/parameter; then
|
||||
if (( ${+functions[tcp_on_alias]} )); then
|
||||
tcp_on_alias $alias $fd
|
||||
fi
|
||||
fi
|
||||
else
|
||||
alias=$arg
|
||||
fd=${tcp_by_name[$alias]}
|
||||
if [[ -z $fd ]]; then
|
||||
print "$0: no such alias \`$alias'" >&2
|
||||
stat=1
|
||||
continue
|
||||
fi
|
||||
# OK if this is an alias...
|
||||
array=(${=tcp_aliases[$fd]})
|
||||
if [[ -n ${array[(r)$alias]} ]]; then
|
||||
# yes, we're OK
|
||||
if [[ -n $delete ]]; then
|
||||
unset "tcp_by_name[$alias]"
|
||||
index=${array[(i)$alias]}
|
||||
array=(${array[1,index-1]} ${array[index+1,-1]})
|
||||
if [[ -z "$array" ]]; then
|
||||
unset "tcp_aliases[$fd]"
|
||||
else
|
||||
tcp_aliases[$fd]="$array"
|
||||
fi
|
||||
|
||||
if zmodload -i zsh/parameter; then
|
||||
if (( ${+functions[tcp_on_unalias]} )); then
|
||||
tcp_on_unalias $alias $fd
|
||||
fi
|
||||
fi
|
||||
else
|
||||
print -r - \
|
||||
"${alias}: alias for session ${tcp_by_fd[$fd]:-unnamed fd $fd}"
|
||||
fi
|
||||
else
|
||||
# oops
|
||||
if [[ -z $quiet ]]; then
|
||||
print "$0: \`$alias' is a session name." >&2
|
||||
fi
|
||||
stat=1
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
return $stat
|
134
Functions/TCP/tcp_close
Normal file
134
Functions/TCP/tcp_close
Normal file
|
@ -0,0 +1,134 @@
|
|||
# Usage:
|
||||
# tcp_close [-q] [ -a | session ... ]
|
||||
# -a means all sessions.
|
||||
# -n means don't close a fake session's fd.
|
||||
# -q means quiet.
|
||||
#
|
||||
# Accepts the -s and -l arguments for consistenty with other functions,
|
||||
# but there is no particular gain in using them
|
||||
emulate -L zsh
|
||||
setopt extendedglob cbases
|
||||
|
||||
local all quiet opt alias noclose
|
||||
local -a sessnames
|
||||
|
||||
while getopts "aql:ns:" opt; do
|
||||
case $opt in
|
||||
(a) all=1
|
||||
;;
|
||||
(q) quiet=1
|
||||
;;
|
||||
(l) sessnames+=(${(s.,.)OPTARG})
|
||||
;;
|
||||
(n) noclose=1
|
||||
;;
|
||||
(s) sessnames+=($OPTARG)
|
||||
;;
|
||||
(*) return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
(( OPTIND > 1 )) && shift $(( OPTIND - 1))
|
||||
|
||||
if [[ -n $all ]]; then
|
||||
if (( $# )); then
|
||||
print "Usage: $0 [ -q ] [ -a | [ session ... ] ]" >&2
|
||||
return 1
|
||||
fi
|
||||
sessnames=(${(k)tcp_by_name})
|
||||
if (( ! ${#sessnames} )); then
|
||||
[[ -z $quiet ]] && print "No TCP sessions open." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
sessnames+=($*)
|
||||
|
||||
if (( ! ${#sessnames} )); then
|
||||
sessnames+=($TCP_SESS)
|
||||
fi
|
||||
|
||||
if (( ! ${#sessnames} )); then
|
||||
[[ -z $quiet ]] && print "No current TCP session." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local tcp_sess fd
|
||||
integer stat curstat
|
||||
|
||||
# Check to see if the fd is opened for a TCP session, or was opened
|
||||
# to a pre-existing fd. We could remember this from tcp_open.
|
||||
local -A ztcp_fds
|
||||
local line match mbegin mend
|
||||
|
||||
if zmodload -e zsh/net/tcp; then
|
||||
ztcp | while read line; do
|
||||
if [[ $line = (#b)*fd\ ([0-9]##) ]]; then
|
||||
ztcp_fds[$match[1]]=1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
for tcp_sess in $sessnames; do
|
||||
curstat=0
|
||||
fd=${tcp_by_name[$tcp_sess]}
|
||||
if [[ -z $fd ]]; then
|
||||
print "No TCP session $tcp_sess!" >&2
|
||||
stat=1
|
||||
continue
|
||||
fi
|
||||
# We need the base name if this is an alias.
|
||||
tcp_sess=${tcp_by_fd[$fd]}
|
||||
if [[ -z $tcp_sess ]]; then
|
||||
if [[ -z $quiet ]]; then
|
||||
print "Aaargh! Session for fd $fd has disappeared!" >&2
|
||||
fi
|
||||
stat=1
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ ${+tcp_aliases} -ne 0 && -n ${tcp_aliases[$fd]} ]]; then
|
||||
for alias in ${=tcp_aliases[$fd]}; do
|
||||
if (( ${+functions[tcp_on_unalias]} )); then
|
||||
tcp_on_unalias $alias $fd
|
||||
fi
|
||||
unset "tcp_by_name[$alias]"
|
||||
done
|
||||
unset "tcp_aliases[$fd]"
|
||||
fi
|
||||
|
||||
# Don't return just because the zle handler couldn't be uninstalled...
|
||||
if [[ -o zle ]]; then
|
||||
zle -F $fd || print "[Ignoring...]" >&2
|
||||
fi
|
||||
|
||||
if [[ -n $ztcp_fds[$fd] ]]; then
|
||||
# It's a ztcp session.
|
||||
if ! ztcp -c $fd; then
|
||||
stat=1
|
||||
curstat=1
|
||||
fi
|
||||
elif [[ -z $noclose ]]; then
|
||||
# It's not, just close it normally.
|
||||
# Careful: syntax for closing fd's is quite strict.
|
||||
if [[ ${#fd} -gt 1 ]]; then
|
||||
[[ -z $quiet ]] && print "Can't close fd $fd; will leave open." >&2
|
||||
else
|
||||
eval "exec $fd>&-"
|
||||
fi
|
||||
fi
|
||||
|
||||
unset "tcp_by_name[$tcp_sess]"
|
||||
unset "tcp_by_fd[$fd]"
|
||||
if [[ -z $quiet && $curstat -eq 0 ]]; then
|
||||
print "Session $tcp_sess successfully closed."
|
||||
fi
|
||||
[[ $tcp_sess = $TCP_SESS ]] && unset TCP_SESS
|
||||
|
||||
if (( ${+functions[tcp_on_close]} )); then
|
||||
tcp_on_close $tcp_sess $fd
|
||||
fi
|
||||
done
|
||||
|
||||
return $stat
|
3
Functions/TCP/tcp_command
Normal file
3
Functions/TCP/tcp_command
Normal file
|
@ -0,0 +1,3 @@
|
|||
tcp_send $* || return 1
|
||||
tcp_read -d -t ${TCP_TIMEOUT:=0.3}
|
||||
return 0
|
115
Functions/TCP/tcp_expect
Normal file
115
Functions/TCP/tcp_expect
Normal file
|
@ -0,0 +1,115 @@
|
|||
# Expect one of a series of regular expressions from $TCP_SESS.
|
||||
# Can make backreferences to be handled by $match. Returns 0 for
|
||||
# successful match, 1 for error, 2 for timeout.
|
||||
#
|
||||
# This function has no facility for conditionally calling code based
|
||||
# the regular expression found. This should be done in the calling code
|
||||
# by testing $TCP_LINE, which contains the line which matched the
|
||||
# regular expression. The complete set of lines read while waiting for
|
||||
# this line is available in the array $tcp_expect_lines (including $TCP_LINE
|
||||
# itself which will be the final element). Alternatively, use -p pind
|
||||
# which sets $pind to the index of the pattern which matched. It
|
||||
# will be set to 0 otherwise.
|
||||
#
|
||||
# Many of the options are passed straight down to tcp_read.
|
||||
#
|
||||
# Options:
|
||||
# -a Run tcp_expect across all sessions; the first pattern matched
|
||||
# from any session is used. The TCP output prompt can be
|
||||
# used to decide which session matched.
|
||||
# -l list
|
||||
# Comma-separated list of sessions as for tcp_read.
|
||||
# -p pv If the Nth of a series of patterns matches, set the parameter
|
||||
# whose name is given by $pv to N; in the case of a timeout,
|
||||
# set it to -1; otherwise (unless the function exited prematurely),
|
||||
# set it to 0.
|
||||
# To avoid namespace clashes, the parameter's name must
|
||||
# not begin with `_expect'.
|
||||
# -q Quiet, passed down to tcp_read. Bad option and argument
|
||||
# usage is always reported.
|
||||
# -s sess
|
||||
# Expect from session sess. May be repeated for multiple sessions.
|
||||
# -t to Timeout in seconds (may be floating point) per read operation.
|
||||
# tcp_expect will only time out if every read operation takes longer
|
||||
# than to
|
||||
# -T TO Overall timeout; tcp_expect will time out if the overall operation
|
||||
# takes longer than this many seconds.
|
||||
emulate -L zsh
|
||||
setopt extendedglob
|
||||
|
||||
# Get extra accuracy by making SECONDS floating point locally
|
||||
typeset -F SECONDS
|
||||
|
||||
# Variables are all named _expect_* to avoid problems with the -p param.
|
||||
local _expect_opt _expect_pvar
|
||||
local -a _expect_read_args
|
||||
float _expect_to1 _expect_to_all _expect_to _expect_new_to
|
||||
integer _expect_i _expect_stat
|
||||
|
||||
while getopts "al:p:qs:t:T:" _expect_opt; do
|
||||
case $_expect_opt in
|
||||
(a) _expect_read_args+=(-a)
|
||||
;;
|
||||
(l) _expect_read_args+=(-l $OPTARG)
|
||||
;;
|
||||
(p) _expect_pvar=$OPTARG
|
||||
if [[ $_expect_pvar != [a-zA-Z_][a-zA-Z_0-9]# ]]; then
|
||||
print "invalid parameter name: $_expect_pvar" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ $_expect_pvar = _expect* ]]; then
|
||||
print "$0: parameter names staring \`_expect' are reserved."
|
||||
return 1
|
||||
fi
|
||||
eval "$_expect_pvar=0"
|
||||
;;
|
||||
(q) _expect_read_args+=(-q)
|
||||
;;
|
||||
(s) _expect_read_args+=(-s $OPTARG)
|
||||
;;
|
||||
(t) _expect_to1=$OPTARG
|
||||
;;
|
||||
(T) _expect_to_all=$(( SECONDS + $OPTARG ))
|
||||
;;
|
||||
(\?) return 1
|
||||
;;
|
||||
(*) print Unhandled option $_expect_opt, complain >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
|
||||
|
||||
tcp_expect_lines=()
|
||||
while true; do
|
||||
if (( _expect_to_all || _expect_to1 )); then
|
||||
_expect_to=0
|
||||
(( _expect_to1 )) && (( _expect_to = _expect_to1 ))
|
||||
if (( _expect_to_all )); then
|
||||
# overall timeout, see if it has already triggered
|
||||
if (( (_expect_new_to = (_expect_to_all - SECONDS)) <= 0 )); then
|
||||
[[ -n $_expect_pvar ]] && eval "$_expect_pvar=-1"
|
||||
return 2
|
||||
fi
|
||||
if (( _expect_to <= 0 || _expect_new_to < _expect_to )); then
|
||||
_expect_to=$_expect_new_to
|
||||
fi
|
||||
fi
|
||||
tcp_read $_expect_read_args -t $_expect_to
|
||||
_expect_stat=$?
|
||||
else
|
||||
tcp_read $_expect_read_args -b
|
||||
_expect_stat=$?
|
||||
fi
|
||||
if (( _expect_stat )); then
|
||||
[[ -n $_expect_pvar ]] && eval "$_expect_pvar=-1"
|
||||
return $_expect_stat
|
||||
fi
|
||||
tcp_expect_lines+=($TCP_LINE)
|
||||
for (( _expect_i = 1; _expect_i <= $#; _expect_i++ )); do
|
||||
if [[ "$TCP_LINE" = ${~argv[_expect_i]} ]]; then
|
||||
[[ -n $_expect_pvar ]] && eval "$_expect_pvar=\$_expect_i"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
done
|
35
Functions/TCP/tcp_fd_handler
Normal file
35
Functions/TCP/tcp_fd_handler
Normal file
|
@ -0,0 +1,35 @@
|
|||
local line name=${tcp_by_fd[$1]}
|
||||
if [[ -n $name ]]
|
||||
then
|
||||
local TCP_INVALIDATE_ZLE
|
||||
if (( $# > 2 )); then
|
||||
zle -I
|
||||
## debugging only
|
||||
# print "Flags on the play:" ${argv[3,-1]}
|
||||
else
|
||||
TCP_INVALIDATE_ZLE=1
|
||||
fi
|
||||
if ! tcp_read -d -u $1; then
|
||||
[[ -n $TCP_INVALIDATE_ZLE ]] && zle -I
|
||||
print "[TCP fd $1 (session $name) gone awol; removing from poll list]" >& 2
|
||||
zle -F $1
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
else
|
||||
zle -I
|
||||
# Handle fds not in the TCP set similarly.
|
||||
# This does the drain thing, to try and get as much data out as possible.
|
||||
if ! read line <&$1; then
|
||||
print "[Reading on $1 failed; removing from poll list]" >& 2
|
||||
zle -F $1
|
||||
return 1
|
||||
fi
|
||||
line="fd$1:$line"
|
||||
local newline
|
||||
while read -t newline <&$1; do
|
||||
line="${line}
|
||||
fd$1:$newline"
|
||||
done
|
||||
fi
|
||||
print -r - $line
|
94
Functions/TCP/tcp_log
Normal file
94
Functions/TCP/tcp_log
Normal file
|
@ -0,0 +1,94 @@
|
|||
# Log TCP output.
|
||||
#
|
||||
# Argument: Output filename.
|
||||
#
|
||||
# Options:
|
||||
# -a Append. Otherwise the existing file is truncated without warning.
|
||||
# (N.B.: even if logging was already active to it!)
|
||||
# -s Per-session logs. Output to <filename>1, <filename>2, etc.
|
||||
# -c Close logging.
|
||||
# -n/-N Turn off or on normal output; output only goes to the logfile, if
|
||||
# any. Otherwise, output also appears interactively. This
|
||||
# can be given with -c (or any other option), then no output
|
||||
# goes anywhere. However, input is still handled by the usual
|
||||
# mechanisms --- $tcp_lines and $TCP_LINE are still set, hence
|
||||
# tcp_expect still works. Equivalent to (un)setting TCP_SILENT.
|
||||
#
|
||||
# With no options and no arguments, print the current configuration.
|
||||
#
|
||||
# Per-session logs are raw output, otherwise $TCP_PROMPT is prepended
|
||||
# to each line.
|
||||
|
||||
emulate -L zsh
|
||||
setopt cbases extendedglob
|
||||
|
||||
local opt append sess close
|
||||
integer activity
|
||||
while getopts "ascnN" opt; do
|
||||
(( activity++ ))
|
||||
case $opt in
|
||||
# append to existing file
|
||||
a) append=1
|
||||
;;
|
||||
# per-session
|
||||
s) sess=1
|
||||
;;
|
||||
# close
|
||||
c) close=1
|
||||
;;
|
||||
# turn off interactive output
|
||||
n) TCP_SILENT=1
|
||||
;;
|
||||
# turn on interactive output
|
||||
N) unset TCP_SILENT
|
||||
;;
|
||||
# incorrect option
|
||||
\?) return 1
|
||||
;;
|
||||
# correct option I forgot about
|
||||
*) print "$0: option -$opt not handled, oops." >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
(( OPTIND > 1 )) && shift $(( OPTIND - 1))
|
||||
|
||||
if [[ -n $close ]]; then
|
||||
if (( $# )); then
|
||||
print "$0: too many arguments for -c" >&2
|
||||
return 1
|
||||
fi
|
||||
unset TCP_LOG TCP_LOG_SESS
|
||||
return 0
|
||||
fi
|
||||
|
||||
if (( $# == 0 && ! activity )); then
|
||||
print "\
|
||||
Per-session log: ${TCP_LOG_SESS:-<none>}
|
||||
Overall log: ${TCP_LOG:-<none>}
|
||||
Silent? ${${TCP_SILENT:+yes}:-no}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if (( $# != 1 )); then
|
||||
print "$0: wrong number of arguments" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -n $sess ]]; then
|
||||
TCP_LOG_SESS=$1
|
||||
if [[ -z $append ]]; then
|
||||
local sesslogs
|
||||
integer i
|
||||
sesslogs=(${TCP_LOG_SESS}*(N))
|
||||
# yes, i know i can do this with multios
|
||||
for (( i = 1; i <= $#sesslogs; i++ )); do
|
||||
: >$sesslogs[$i]
|
||||
done
|
||||
fi
|
||||
else
|
||||
TCP_LOG=$1
|
||||
[[ -z $append ]] && : >$TCP_LOG
|
||||
fi
|
||||
|
||||
return 0
|
197
Functions/TCP/tcp_open
Normal file
197
Functions/TCP/tcp_open
Normal file
|
@ -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
|
65
Functions/TCP/tcp_output
Normal file
65
Functions/TCP/tcp_output
Normal file
|
@ -0,0 +1,65 @@
|
|||
emulate -L zsh
|
||||
setopt extendedglob
|
||||
|
||||
local opt tprompt sess read_fd tpat quiet
|
||||
|
||||
while getopts "F:P:qS:" opt; do
|
||||
case $opt in
|
||||
(F) read_fd=$OPTARG
|
||||
;;
|
||||
(P) tprompt=$OPTARG
|
||||
;;
|
||||
(q) quiet=1
|
||||
;;
|
||||
(S) sess=$OPTARG
|
||||
;;
|
||||
(*) [[ $opt != \? ]] && print -r "Can't handle option $opt" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
|
||||
|
||||
# Per-session logs don't have the session discriminator in front.
|
||||
if [[ -n $TCP_LOG_SESS ]]; then
|
||||
print -r -- "$*" >>${TCP_LOG_SESS}.$sess
|
||||
fi
|
||||
# Always add the TCP prompt. We used only to do this with
|
||||
# multiple sessions, but it seems always to be useful to know
|
||||
# where data is coming from; also, it allows more predictable
|
||||
# behaviour in tcp_expect.
|
||||
if [[ -n $tprompt ]]; then
|
||||
zgprintf -R -%s=$sess -%f=$read_fd -- $tprompt
|
||||
# We will pass this back up.
|
||||
REPLY="$REPLY$*"
|
||||
else
|
||||
REPLY="$*"
|
||||
fi
|
||||
if [[ -n $TCP_LOG ]]; then
|
||||
print -r -- $REPLY >>${TCP_LOG}
|
||||
fi
|
||||
|
||||
if [[ -z $quiet ]]; then
|
||||
local skip=
|
||||
if [[ ${#tcp_filter} -ne 0 ]]; then
|
||||
# Allow tcp_filter to be an associative array, though
|
||||
# it doesn't *need* to be.
|
||||
for tpat in ${(v)tcp_filter}; do
|
||||
[[ $REPLY = ${~tpat} ]] && skip=1 && break
|
||||
done
|
||||
fi
|
||||
if [[ -z $skip ]]; then
|
||||
# Check flag passed down probably from tcp_fd_handler:
|
||||
# if we have output, we are in zle and need to fix the display first.
|
||||
# (The shell is supposed to be smart enough that you can replace
|
||||
# all the following with
|
||||
# [[ -o zle ]] && zle -I
|
||||
# but I haven't dared try it yet.)
|
||||
if [[ -n $TCP_INVALIDATE_ZLE ]]; then
|
||||
zle -I
|
||||
# Only do this the first time.
|
||||
unset TCP_INVALIDATE_ZLE
|
||||
fi
|
||||
print -r -- $REPLY
|
||||
fi
|
||||
fi
|
31
Functions/TCP/tcp_proxy
Normal file
31
Functions/TCP/tcp_proxy
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Listen on the given port and for every connection, start a new
|
||||
# command (defaults to $SHELL) in the background on the accepted fd.
|
||||
# WARNING: this can leave your host open to the universe. For use
|
||||
# in a restricted fashion on a secure network.
|
||||
#
|
||||
# Remote logins are much more efficient...
|
||||
|
||||
local TCP_LISTEN_FD
|
||||
trap '[[ -n $TCP_LISTEN_FD ]] && ztcp -c $TCP_LISTEN_FD; return 1' \
|
||||
HUP INT TERM EXIT PIPE
|
||||
|
||||
if [[ $1 != <-> ]]; then
|
||||
print "Usage: $0 port [cmd args... ]" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
integer port=$1
|
||||
shift
|
||||
ztcp -l $port || return 1
|
||||
TCP_LISTEN_FD=$REPLY
|
||||
|
||||
(( $# )) || set -- ${SHELL:-zsh}
|
||||
local cmd=$1
|
||||
shift
|
||||
|
||||
while ztcp -a $TCP_LISTEN_FD; do
|
||||
# hack to expand aliases without screwing up arguments
|
||||
eval $cmd '$* <&$REPLY >&$REPLY 2>&$REPLY &'
|
||||
# Close the session fd; we don't need it here any more.
|
||||
ztcp -c $REPLY
|
||||
done
|
207
Functions/TCP/tcp_read
Normal file
207
Functions/TCP/tcp_read
Normal file
|
@ -0,0 +1,207 @@
|
|||
# Helper function for reading input from a TCP connection.
|
||||
# Actually, the input doesn't need to be a TCP connection at all, it
|
||||
# is simply an input file descriptor. However, it must be contained
|
||||
# in ${tcp_by_fd[$TCP_SESS]}. This is set set by tcp_open, but may be
|
||||
# set by hand. (Note, however, the blocking/timeout behaviour is usually
|
||||
# not implemented for reading from regular files.)
|
||||
#
|
||||
# The default behaviour is simply to read any single available line from
|
||||
# the input fd and print it. If a line is read, it is stored in the
|
||||
# parameter $TCP_LINE; this always contains the last line successfully
|
||||
# read. Any chunk of lines read in are stored in the array $tcp_lines;
|
||||
# this always contains a complete list of all lines read in by a single
|
||||
# execution of this function and hence may be empty. The fd corresponding
|
||||
# to $TCP_LINE is stored in $TCP_LINE_FD (this can be turned into a
|
||||
# session by looking up in $tcp_by_fd).
|
||||
#
|
||||
# Printed lines are preceded by $TCP_PROMPT. This may contain two
|
||||
# percent escapes: %s for the current session, %f for the current file
|
||||
# descriptor. The default is `T[%s]:'. The prompt is not printed
|
||||
# to per-session logs where the source is unambiguous.
|
||||
#
|
||||
# The function returns 0 if a read succeeded, even if (using -d) a
|
||||
# subsequent read failed.
|
||||
#
|
||||
# The behaviour is modified by the following options.
|
||||
#
|
||||
# -a Read from all fds, not just the one given by TCP_SESS.
|
||||
#
|
||||
# -b The first read blocks until some data is available for reading.
|
||||
#
|
||||
# -d Drain all pending input; loop until no data is available.
|
||||
#
|
||||
# -l sess1,sess2,...
|
||||
# Gives a list of sessions to read on. Equivalent to
|
||||
# -u ${tcp_by_name[sess1]} -u ${tcp_by_name[sess2]} ...
|
||||
# Multiple -l options also work.
|
||||
#
|
||||
# -q Quiet; if $TCP_SESS is not set, just return 1, but don't print
|
||||
# an error message.
|
||||
#
|
||||
# -s sess
|
||||
# Gives a single session; the option may be repeated.
|
||||
#
|
||||
# -t TO On each read (the only read unless -d was also given), time out
|
||||
# if nothing was available after TO seconds (may be floating point).
|
||||
# Otherwise, the function will return immediately when no data is
|
||||
# available.
|
||||
#
|
||||
# If combined with -b, the function will always wait for the
|
||||
# first data to become available; hence this is not useful unless
|
||||
# -d is specified along with -b, in which case the timeout applies
|
||||
# to data after the first line.
|
||||
# -u fd Read from fd instead of the default session; may be repeated for
|
||||
# multiple sessions. Can be a comma-separated list, too.
|
||||
# -T TO This sets an overall timeout, again in seconds.
|
||||
|
||||
emulate -L zsh
|
||||
setopt extendedglob cbases
|
||||
# set -x
|
||||
|
||||
zmodload -i zsh/mathfunc
|
||||
|
||||
local opt drain line quiet block read_fd all sess
|
||||
local -A read_fds
|
||||
read_fds=()
|
||||
float timeout timeout_all endtime
|
||||
integer stat
|
||||
|
||||
while getopts "abdl:qs:t:T:u:" opt; do
|
||||
case $opt in
|
||||
# Read all sessions.
|
||||
(a) all=1
|
||||
;;
|
||||
# Block until we receive something.
|
||||
(b) block=1
|
||||
;;
|
||||
# Drain all pending input.
|
||||
(d) drain=1
|
||||
;;
|
||||
(l) for sess in ${(s.,.)OPTARG}; do
|
||||
read_fd=${tcp_by_name[$sess]}
|
||||
if [[ -z $read_fd ]]; then
|
||||
print "$0: no such session: $sess" >&2
|
||||
return 1
|
||||
fi
|
||||
read_fds[$read_fd]=1
|
||||
done
|
||||
;;
|
||||
|
||||
# Don't print an error mesage if there is no TCP connection,
|
||||
# just return 1.
|
||||
(q) quiet=1
|
||||
;;
|
||||
# Add a single session to the list
|
||||
(s) read_fd=${tcp_by_name[$OPTARG]}
|
||||
if [[ -z $read_fd ]]; then
|
||||
print "$0: no such session: $sess" >&2
|
||||
return 1
|
||||
fi
|
||||
read_fds[$read_fd]=1
|
||||
;;
|
||||
# Per-read timeout: wait this many seconds before
|
||||
# each read.
|
||||
(t) timeout=$OPTARG
|
||||
[[ -n $TCP_READ_DEBUG ]] && print "Timeout per-operations is $timeout" >&2
|
||||
;;
|
||||
# Overall timeout: return after this many seconds.
|
||||
(T) timeout_all=$OPTARG
|
||||
;;
|
||||
# Read from given fd(s).
|
||||
(u) for read_fd in ${(s.,.)OPTARG}; do
|
||||
if [[ $read_fd != (0x[[:xdigit:]]##|[[:digit:]]##) ]]; then
|
||||
print "Bad fd in $OPTARG" >&2
|
||||
return 1
|
||||
fi
|
||||
read_fds[$((read_fd))]=1
|
||||
done
|
||||
;;
|
||||
(*) [[ $opt != \? ]] && print Unhandled option, complain: $opt >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -n $all ]]; then
|
||||
read_fds=(${(kv)tcp_by_fd})
|
||||
elif (( ! $#read_fds )); then
|
||||
if [[ -z $TCP_SESS ]]; then
|
||||
[[ -z $quiet ]] && print "No tcp connection open." >&2
|
||||
return 1
|
||||
elif [[ -z $tcp_by_name[$TCP_SESS] ]]; then
|
||||
print "TCP session $TCP_SESS has gorn!" >&2
|
||||
return 1
|
||||
fi
|
||||
read_fds[$tcp_by_name[$TCP_SESS]]=1
|
||||
fi
|
||||
|
||||
tcp_lines=()
|
||||
local helper_stat=2 skip tpat reply REPLY
|
||||
float newtimeout
|
||||
|
||||
# Get extra accuracy by making SECONDS floating point locally
|
||||
typeset -F SECONDS
|
||||
|
||||
if (( timeout_all )); then
|
||||
(( endtime = SECONDS + timeout_all ))
|
||||
fi
|
||||
|
||||
zmodload -i zsh/zselect
|
||||
|
||||
if [[ -n $block ]]; then
|
||||
if (( timeout_all )); then
|
||||
# zselect -t uses 100ths of a second
|
||||
zselect -t $(( int(100*timeout_all + 0.5) )) ${(k)read_fds} ||
|
||||
return $helper_stat
|
||||
else
|
||||
zselect ${(k)read_fds} || return $helper_stat
|
||||
fi
|
||||
fi
|
||||
|
||||
while (( ${#read_fds} )); do
|
||||
if [[ -n $block ]]; then
|
||||
# We already have data waiting this time through.
|
||||
unset block
|
||||
else
|
||||
if (( timeout_all )); then
|
||||
(( (newtimeout = endtime - SECONDS) <= 0 )) && return 2
|
||||
if (( ! timeout || newtimeout < timeout )); then
|
||||
(( timeout = newtimeout ))
|
||||
fi
|
||||
fi
|
||||
if (( timeout )); then
|
||||
if [[ -n $TCP_READ_DEBUG ]]; then
|
||||
print "[tcp_read: selecting timeout $timeout on ${(k)read_fds}]" >&2
|
||||
fi
|
||||
zselect -t $(( int(timeout*100 + 0.5) )) ${(k)read_fds} ||
|
||||
return $helper_stat
|
||||
else
|
||||
if [[ -n $TCP_READ_DEBUG ]]; then
|
||||
print "[tcp_read: selecting no timeout on ${(k)read_fds}]" >&2
|
||||
fi
|
||||
zselect -t 0 ${(k)read_fds} || return $helper_stat
|
||||
fi
|
||||
fi
|
||||
if [[ -n $TCP_READ_DEBUG ]]; then
|
||||
print "[tcp_read: returned fds ${reply}]" >&2
|
||||
fi
|
||||
for read_fd in ${reply[2,-1]}; do
|
||||
if ! read -r line <&$read_fd; then
|
||||
unset "read_fds[$read_fd]"
|
||||
stat=1
|
||||
continue
|
||||
fi
|
||||
|
||||
helper_stat=0
|
||||
sess=${tcp_by_fd[$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
|
||||
# Only handle one line from one device at a time unless draining.
|
||||
[[ -z $drain ]] && return $stat
|
||||
done
|
||||
done
|
||||
|
||||
return $stat
|
43
Functions/TCP/tcp_rename
Normal file
43
Functions/TCP/tcp_rename
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Rename session OLD (defaults to current session) to session NEW.
|
||||
# Does not handle aliases; use tcp_alias for all alias redefinitions.
|
||||
|
||||
local old new
|
||||
|
||||
if (( $# == 1 )); then
|
||||
old=$TCP_SESS
|
||||
new=$1
|
||||
elif (( $# == 2 )); then
|
||||
old=$1
|
||||
new=$2
|
||||
else
|
||||
print "Usage: $0 OLD NEW" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local fd=$tcp_by_name[$old]
|
||||
if [[ -z $fd ]]; then
|
||||
print "No such session: $old" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -n $tcp_by_name[$new] ]]; then
|
||||
print "Session $new already exists." >&2
|
||||
return 1
|
||||
fi
|
||||
# Can't rename an alias
|
||||
if [[ $tcp_by_fd[$fd] != $old ]]; then
|
||||
print "Use tcp_alias to redefine an alias." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
tcp_by_name[$new]=$fd
|
||||
unset "tcp_by_name[$old]"
|
||||
|
||||
tcp_by_fd[$fd]=$new
|
||||
|
||||
[[ $TCP_SESS = $old ]] && TCP_SESS=$new
|
||||
|
||||
if zmodload -i zsh/parameter; then
|
||||
if (( ${+functions[tcp_on_rename]} )); then
|
||||
tcp_on_rename $new $fd $old
|
||||
fi
|
||||
fi
|
67
Functions/TCP/tcp_send
Normal file
67
Functions/TCP/tcp_send
Normal file
|
@ -0,0 +1,67 @@
|
|||
emulate -L zsh
|
||||
setopt extendedglob cbases
|
||||
|
||||
local opt quiet all sess fd nonewline
|
||||
local -a sessions write_fds
|
||||
|
||||
while getopts "al:nqs:" opt; do
|
||||
case $opt in
|
||||
(a) all=1
|
||||
;;
|
||||
(n) nonewline=-n
|
||||
;;
|
||||
(q) quiet=1
|
||||
;;
|
||||
(l) for sess in ${(s.,.)OPTARG}; do
|
||||
if [[ -z ${tcp_by_name[$sess]} ]]; then
|
||||
print "$0: no such session: $sess" >&2
|
||||
return 1
|
||||
fi
|
||||
sessions+=($sess)
|
||||
done
|
||||
;;
|
||||
(s) if [[ -z $tcp_by_name[$OPTARG] ]]; then
|
||||
print "No such session: $OPTARG" >&2
|
||||
return 1
|
||||
fi
|
||||
sessions+=($OPTARG)
|
||||
;;
|
||||
(*) [[ $opt != '?' ]] && print Unhandled option, complain: $opt >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
|
||||
|
||||
if [[ -n $all ]]; then
|
||||
sessions=(${(k)tcp_by_name})
|
||||
elif (( ! ${#sessions} )); then
|
||||
sessions=($TCP_SESS)
|
||||
fi
|
||||
if (( ! $#sessions )); then
|
||||
if [[ -z $quiet ]]; then
|
||||
print "No current TCP session open." >&2
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Writing on a TCP connection closed by the remote end can cause SIGPIPE.
|
||||
# The following test is reasonably robust, though in principle we can
|
||||
# mistake a SIGPIPE owing to another fd. That doesn't seem like a big worry.
|
||||
# `emulate -L zsh' will already have set localtraps.
|
||||
local TCP_FD_CLOSED
|
||||
trap 'TCP_FD_CLOSED=1' PIPE
|
||||
|
||||
local TCP_SESS
|
||||
|
||||
for TCP_SESS in $sessions; do
|
||||
fd=${tcp_by_name[$TCP_SESS]}
|
||||
print $nonewline -r -- $* >&$fd
|
||||
if [[ $? -ne 0 || -n $TCP_FD_CLOSED ]]; then
|
||||
print "Session ${TCP_SESS}: fd $fd unusable." >&2
|
||||
unset TCP_FD_CLOSED
|
||||
fi
|
||||
if [[ -n $TCP_OUTPUT ]]; then
|
||||
tcp_output -P "$TCP_OUTPUT" -S $TCP_SESS -F $fd -q "${(j. .)*}"
|
||||
fi
|
||||
done
|
39
Functions/TCP/tcp_sess
Normal file
39
Functions/TCP/tcp_sess
Normal file
|
@ -0,0 +1,39 @@
|
|||
# try to disguise parameters from the eval'd command in case it's a function.
|
||||
integer __myfd=1
|
||||
|
||||
if [[ -n $1 ]]; then
|
||||
if [[ -z $tcp_by_name[$1] ]]; then
|
||||
print no such session: $1
|
||||
__myfd=2
|
||||
elif [[ -n $2 ]]; then
|
||||
local TCP_SESS=$1
|
||||
shift
|
||||
# A bit tricky: make sure the first argument gets re-evaluated,
|
||||
# so as to get aliases etc. to work, but make sure the remainder
|
||||
# don't, so as not to bugger up quoting. This ought to work the
|
||||
# vast majority of the time, anyway.
|
||||
local __cmd=$1
|
||||
shift
|
||||
eval $__cmd \$\*
|
||||
return
|
||||
else
|
||||
TCP_SESS=$1
|
||||
return 0;
|
||||
fi
|
||||
fi
|
||||
|
||||
# Print out the list of sessions, first the number, than the corresponding
|
||||
# file descriptor. The current session, if any, is marked with an asterisk.
|
||||
local cur name fd
|
||||
for name in ${(ko)tcp_by_name}; do
|
||||
fd=${tcp_by_name[$name]}
|
||||
# mark current session with an asterisk
|
||||
if [[ ${TCP_SESS} = $name ]]; then
|
||||
cur=" *"
|
||||
else
|
||||
cur=
|
||||
fi
|
||||
print "sess:$name; fd:$fd$cur" >&$__myfd
|
||||
done
|
||||
|
||||
return $(( __myfd - 1 ))
|
97
Functions/TCP/tcp_spam
Normal file
97
Functions/TCP/tcp_spam
Normal file
|
@ -0,0 +1,97 @@
|
|||
# SPAM is a registered trademark of Hormel Foods Corporation.
|
||||
#
|
||||
# -a all connections, override $tcp_spam_list and $tcp_no_spam_list.
|
||||
# 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.
|
||||
# -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).
|
||||
# -t transmit, send data to slave rather than executing command for eac
|
||||
# session.
|
||||
# -v verbose, list session being spammed in turn
|
||||
#
|
||||
# If the function tcp_on_spam is defined, it is called for each link
|
||||
# with the first argument set to the session name, and the remainder the
|
||||
# command line to be executed. If it sets the parameter REPLY to `done',
|
||||
# the command line will not then be executed by tcp_spam, else it will.
|
||||
|
||||
emulate -L zsh
|
||||
setopt extendedglob
|
||||
|
||||
local TCP_SESS cmd opt verbose reverse sesslist transmit all
|
||||
local match mbegin mend REPLY
|
||||
local -a sessions
|
||||
|
||||
while getopts "al:rtv" opt; do
|
||||
case $opt in
|
||||
(a) all=1
|
||||
;;
|
||||
(l) sessions+=(${(s.,.)OPTARG})
|
||||
;;
|
||||
(r) reverse=1
|
||||
;;
|
||||
(s) sessions+=($OPTARG)
|
||||
;;
|
||||
(t) transmit=-t
|
||||
;;
|
||||
(v) verbose=1
|
||||
;;
|
||||
(*) [[ $opt != '?' ]] && print "Option $opt not handled." >&2
|
||||
print "Sorry, spam's off." >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
|
||||
|
||||
local name
|
||||
if [[ -n $all ]]; then
|
||||
sessions=(${(ko)tcp_by_name})
|
||||
elif (( ! ${#sessions} )); then
|
||||
if (( ${#tcp_spam_list} )); then
|
||||
sessions=($tcp_spam_list)
|
||||
else
|
||||
sessions=(${(ko)tcp_by_name})
|
||||
fi
|
||||
if (( ${#tcp_no_spam_list} )); then
|
||||
for name in ${tcp_no_spam_list}; do
|
||||
sessions=(${sessions:#$name})
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n $reverse ]]; then
|
||||
local tmp
|
||||
integer i
|
||||
for (( i = 1; i <= ${#sessions}/2; i++ )); do
|
||||
tmp=${sessions[i]}
|
||||
sessions[i]=${sessions[-i]}
|
||||
sessions[-i]=$tmp
|
||||
done
|
||||
fi
|
||||
|
||||
if (( ! ${#sessions} )); then
|
||||
print "No connections to spam." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -n $transmit ]]; then
|
||||
cmd=tcp_send
|
||||
else
|
||||
cmd=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
: ${TCP_PROMPT:=T[%s]:}
|
||||
|
||||
for TCP_SESS in $sessions; do
|
||||
REPLY=
|
||||
if (( ${+functions[tcp_on_spam]} )); then
|
||||
tcp_on_spam $TCP_SESS $cmd $*
|
||||
[[ $REPLY = done ]] && continue
|
||||
fi
|
||||
[[ -n $verbose ]] && zgprintf -R -%s=$TCP_SESS \
|
||||
-%f=${tcp_by_name[$TCP_SESS]} -- $TCP_PROMPT
|
||||
eval $cmd '$*'
|
||||
done
|
50
Functions/TCP/tcp_talk
Normal file
50
Functions/TCP/tcp_talk
Normal file
|
@ -0,0 +1,50 @@
|
|||
# Make line editor input go straight to the current TCP session.
|
||||
# Returns when the string $TCP_TALK_ESCAPE (default :) is read on its own.
|
||||
# Otherwise, $TCP_TALK_ESCAPE followed by whitespace at the start of a line
|
||||
# is stripped off and the rest of the line passed to the shell.
|
||||
#
|
||||
# History is not currently handled, because this is difficult.
|
||||
|
||||
: ${TCP_TALK_ESCAPE:=:}
|
||||
|
||||
tcp-accept-line-or-exit() {
|
||||
emulate -L zsh
|
||||
setopt extendedglob
|
||||
local match mbegin mend
|
||||
|
||||
if [[ $BUFFER = ${TCP_TALK_ESCAPE}[[:blank:]]#(#b)(*) ]]; then
|
||||
if [[ -z $match[1] ]]; then
|
||||
BUFFER=
|
||||
zle -A .accept-line accept-line
|
||||
PS1=$TCP_SAVE_PS1
|
||||
unset TCP_SAVE_PS1
|
||||
zle -I
|
||||
print '\r[Normal keyboard input restored]' >&2
|
||||
else
|
||||
BUFFER=$match[1]
|
||||
fi
|
||||
zle .accept-line
|
||||
else
|
||||
# BUGS: is deleted from the command line and doesn't appear in
|
||||
# the history.
|
||||
|
||||
# The following attempt to get the BUFFER into the history falls
|
||||
# foul of the fact that we need to accept the current line first.
|
||||
# But we don't actually want to accept the current line at all.
|
||||
# print -s -r - $BUFFER
|
||||
|
||||
# This is my function to send data over a TCP connection; replace
|
||||
# it with something else or nothing.
|
||||
tcp_send $BUFFER
|
||||
BUFFER=
|
||||
fi
|
||||
}
|
||||
|
||||
TCP_SAVE_PS1=${PS1##\[T*\]}
|
||||
if [[ -o prompt_subst ]]; then
|
||||
PS1="T[\$TCP_SESS]$TCP_SAVE_PS1"
|
||||
else
|
||||
PS1="[T]$TCP_SAVE_PS1"
|
||||
fi
|
||||
zle -N tcp-accept-line-or-exit
|
||||
zle -A tcp-accept-line-or-exit accept-line
|
11
Functions/TCP/tcp_wait
Normal file
11
Functions/TCP/tcp_wait
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Wait for given number of seconds, reading any data from
|
||||
# all TCP connections while doing so.
|
||||
|
||||
typeset -F SECONDS to end
|
||||
|
||||
(( to = $1, end = SECONDS + to ))
|
||||
while (( SECONDS < end )); do
|
||||
tcp_read -a -T $to
|
||||
(( to = end - SECONDS ))
|
||||
done
|
||||
return
|
70
Functions/TCP/zgprintf
Normal file
70
Functions/TCP/zgprintf
Normal file
|
@ -0,0 +1,70 @@
|
|||
# Generalised printf.
|
||||
# Arguments of the form -%X=... give the output to be used with
|
||||
# the directive %x.
|
||||
#
|
||||
# -P indicates that any unhandled directives are to be
|
||||
# passed to printf. With this option, any %-escapes passed to printf
|
||||
# are assumed to consume exactly one argument from the command line.
|
||||
# Unused command line arguments are ignored. This is only minimally
|
||||
# implemented.
|
||||
#
|
||||
# -R indicates the value is to be put into REPLY rather than printed.
|
||||
#
|
||||
# -r indicates that print formatting (backslash escapes etc.) should
|
||||
# not be replied to the result. When using -R, no print formatting
|
||||
# is applied in any case.
|
||||
|
||||
emulate -L zsh
|
||||
setopt extendedglob
|
||||
|
||||
local opt printf fmt usereply match mbegin mend raw c
|
||||
typeset -A chars
|
||||
chars=(% %)
|
||||
|
||||
while getopts "%:PrR" opt; do
|
||||
case $opt in
|
||||
(%) if [[ $OPTARG != ?=* ]]; then
|
||||
print -r "Bad % option: should be -%${OPTARG[1]}=..." >&2
|
||||
return 1
|
||||
fi
|
||||
chars[${OPTARG[1]}]=${OPTARG[3,-1]}
|
||||
;;
|
||||
(P) printf=1
|
||||
;;
|
||||
(r) raw=-r
|
||||
;;
|
||||
(R) usereply=1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
|
||||
|
||||
[[ -z $usereply ]] && local REPLY
|
||||
REPLY=
|
||||
|
||||
if (( $# )); then
|
||||
fmt=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
while [[ $fmt = (#b)([^%]#)%([-0-9.*]#?)(*) ]]; do
|
||||
REPLY+=$match[1]
|
||||
c=$match[2]
|
||||
fmt=$match[3]
|
||||
if [[ -n ${chars[$c]} ]]; then
|
||||
REPLY+=${chars[$c]}
|
||||
elif [[ -n $P ]]; then
|
||||
# hmmm, we need sprintf...
|
||||
# TODO: %ld etc.
|
||||
REPLY+=`printf "%$c" $1`
|
||||
(( $? )) && return 1
|
||||
shift
|
||||
else
|
||||
print -r "Format not handled: %$c" >&2
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
REPLY+=$fmt
|
||||
[[ -z $usereply ]] && print -n $raw - $REPLY
|
||||
return 0
|
|
@ -433,7 +433,9 @@ bin_ztcp(char *nam, char **args, Options ops, int func)
|
|||
|
||||
if (bind(sess->fd, (struct sockaddr *)&sess->sock.in, sizeof(struct sockaddr_in)))
|
||||
{
|
||||
zwarnnam(nam, "could not bind to %s: %e", "0.0.0.0", errno);
|
||||
char buf[DIGBUFSIZE];
|
||||
convbase(buf, (zlong)lport, 10);
|
||||
zwarnnam(nam, "could not bind to port %s: %e", buf, errno);
|
||||
tcp_close(sess);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
name=zsh/net/tcp
|
||||
link=dynamic
|
||||
load=no
|
||||
functions='Functions/TCP/*'
|
||||
|
||||
objects="tcp.o"
|
||||
autobins="ztcp"
|
||||
|
|
Loading…
Reference in a new issue