160 lines
6.5 KiB
Diff
160 lines
6.5 KiB
Diff
|
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
|
||
|
index 6d3bb4b..127b4c4 100644
|
||
|
--- a/Doc/Zsh/zle.yo
|
||
|
+++ b/Doc/Zsh/zle.yo
|
||
|
@@ -492,26 +492,35 @@ Only available if your system supports one of the `poll' or `select' system
|
||
|
calls; most modern systems do.
|
||
|
|
||
|
Installs var(handler) (the name of a shell function) to handle input from
|
||
|
-file descriptor var(fd). When zle is attempting to read data, it will
|
||
|
-examine both the terminal and the list of handled var(fd)'s. If data
|
||
|
-becomes available on a handled var(fd), zle will call var(handler) with
|
||
|
-the fd which is ready for reading as the only argument. If the handler
|
||
|
-produces output to the terminal, it should call `tt(zle -I)' before doing
|
||
|
-so (see below). The handler should not attempt to read from the terminal.
|
||
|
-Note that zle makes no attempt to check whether this fd is actually
|
||
|
+file descriptor var(fd). Installing a handler for an var(fd) which is
|
||
|
+already handled causes the existing handler to be replaced. Any number of
|
||
|
+handlers for any number of readable file descriptors may be installed.
|
||
|
+Note that zle makes no attempt to check whether this var(fd) is actually
|
||
|
readable when installing the handler. The user must make their own
|
||
|
arrangements for handling the file descriptor when zle is not active.
|
||
|
|
||
|
-If the option tt(-w) is also given, the var(handler) is instead a
|
||
|
-line editor widget, typically a shell function made into a widget using
|
||
|
-tt(zle -N). In that case var(handler) can use all the facilities of
|
||
|
-zle to update the current editing line. Note, however, that as handling
|
||
|
-var(fd) takes place at a low level changes to the display will not
|
||
|
-automatically appear; the widget should call tt(zle -R) to force redisplay.
|
||
|
-
|
||
|
-Any number of handlers for any number of readable file descriptors may be
|
||
|
-installed. Installing a handler for an var(fd) which is already handled
|
||
|
-causes the existing handler to be replaced.
|
||
|
+When zle is attempting to read data, it will examine both the terminal and
|
||
|
+the list of handled var(fd)'s. If data becomes available on a handled
|
||
|
+var(fd), zle calls var(handler) with the fd which is ready for reading
|
||
|
+as the first argument. Under normal circumstances this is the only
|
||
|
+argument, but if an error was detected, a second argument provides
|
||
|
+details: `tt(hup)' for a disconnect, `tt(nval)' for a closed or otherwise
|
||
|
+invalid descriptor, or `tt(err)' for any other condition. Systems that
|
||
|
+support only the `select' system call always use `tt(err)'.
|
||
|
+
|
||
|
+If the option tt(-w) is also given, the var(handler) is instead a line
|
||
|
+editor widget, typically a shell function made into a widget using
|
||
|
+`tt(zle -N)'. In that case var(handler) can use all the facilities of zle
|
||
|
+to update the current editing line. Note, however, that as handling var(fd)
|
||
|
+takes place at a low level changes to the display will not automatically
|
||
|
+appear; the widget should call `tt(zle -R)' to force redisplay. As of this
|
||
|
+writing, widget handlers only support a single argument and thus are never
|
||
|
+passed a string for error state, so widgets must be prepared to test the
|
||
|
+descriptor themselves.
|
||
|
+
|
||
|
+If either type of handler produces output to the terminal, it should call
|
||
|
+`tt(zle -I)' before doing so (see below). Handlers should not attempt to
|
||
|
+read from the terminal.
|
||
|
|
||
|
If no var(handler) is given, but an var(fd) is present, any handler for
|
||
|
that var(fd) is removed. If there is none, an error message is printed
|
||
|
@@ -526,7 +535,8 @@ silently return status 1.
|
||
|
|
||
|
Note that this feature should be used with care. Activity on one of the
|
||
|
var(fd)'s which is not properly handled can cause the terminal to become
|
||
|
-unusable.
|
||
|
+unusable. Removing an var(fd) handler from within a signal trap may cause
|
||
|
+unpredictable behavior.
|
||
|
|
||
|
Here is a simple example of using this feature. A connection to a remote
|
||
|
TCP port is created using the ztcp command; see
|
||
|
@@ -536,6 +546,7 @@ which simply prints out any data which arrives on this connection. Note
|
||
|
that `select' will indicate that the file descriptor needs handling
|
||
|
if the remote side has closed the connection; we handle that by testing
|
||
|
for a failed read.
|
||
|
+
|
||
|
example(if ztcp pwspc 2811; then
|
||
|
tcpfd=$REPLY
|
||
|
handler+LPAR()RPAR() {
|
||
|
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
|
||
|
index b0010fc..442c319 100644
|
||
|
--- a/Src/Zle/zle_main.c
|
||
|
+++ b/Src/Zle/zle_main.c
|
||
|
@@ -525,7 +525,8 @@ raw_getbyte(long do_keytmout, char *cptr)
|
||
|
#endif
|
||
|
#ifndef HAVE_POLL
|
||
|
# ifdef HAVE_SELECT
|
||
|
- fd_set foofd;
|
||
|
+ fd_set foofd, errfd;
|
||
|
+ FD_ZERO(&errfd);
|
||
|
# endif
|
||
|
#endif
|
||
|
|
||
|
@@ -613,11 +614,14 @@ raw_getbyte(long do_keytmout, char *cptr)
|
||
|
if (!errtry) {
|
||
|
for (i = 0; i < nwatch; i++) {
|
||
|
int fd = watch_fds[i].fd;
|
||
|
+ if (FD_ISSET(fd, &errfd))
|
||
|
+ continue;
|
||
|
FD_SET(fd, &foofd);
|
||
|
if (fd > fdmax)
|
||
|
fdmax = fd;
|
||
|
}
|
||
|
}
|
||
|
+ FD_ZERO(&errfd);
|
||
|
|
||
|
if (tmout.tp != ZTM_NONE) {
|
||
|
expire_tv.tv_sec = tmout.exp100ths / 100;
|
||
|
@@ -732,9 +736,10 @@ raw_getbyte(long do_keytmout, char *cptr)
|
||
|
Watch_fd lwatch_fd = lwatch_fds + i;
|
||
|
if (
|
||
|
# ifdef HAVE_POLL
|
||
|
- (fds[i+1].revents & POLLIN)
|
||
|
+ (fds[i+1].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL))
|
||
|
# else
|
||
|
- FD_ISSET(lwatch_fd->fd, &foofd)
|
||
|
+ FD_ISSET(lwatch_fd->fd, &foofd) ||
|
||
|
+ FD_ISSET(lwatch_fd->fd, &errfd)
|
||
|
# endif
|
||
|
) {
|
||
|
/* Handle the fd. */
|
||
|
@@ -765,6 +770,9 @@ raw_getbyte(long do_keytmout, char *cptr)
|
||
|
if (fds[i+1].revents & POLLNVAL)
|
||
|
zaddlinknode(funcargs, ztrdup("nval"));
|
||
|
# endif
|
||
|
+# else
|
||
|
+ if (FD_ISSET(lwatch_fd->fd, &errfd))
|
||
|
+ zaddlinknode(funcargs, ztrdup("err"));
|
||
|
# endif
|
||
|
callhookfunc(lwatch_fd->func, funcargs, 0, NULL);
|
||
|
freelinklist(funcargs, freestr);
|
||
|
@@ -786,6 +794,31 @@ raw_getbyte(long do_keytmout, char *cptr)
|
||
|
for (i = 0; i < lnwatch; i++)
|
||
|
zsfree(lwatch_fds[i].func);
|
||
|
zfree(lwatch_fds, lnwatch*sizeof(struct watch_fd));
|
||
|
+
|
||
|
+# ifdef HAVE_POLL
|
||
|
+ /* Function may have added or removed handlers */
|
||
|
+ nfds = 1 + nwatch;
|
||
|
+ if (nfds > 1) {
|
||
|
+ fds = zrealloc(fds, sizeof(struct pollfd) * nfds);
|
||
|
+ for (i = 0; i < nwatch; i++) {
|
||
|
+ /*
|
||
|
+ * This is imperfect because it assumes fds[] and
|
||
|
+ * watch_fds[] remain in sync, which may be false
|
||
|
+ * if handlers are shuffled. However, it should
|
||
|
+ * be harmless (e.g., produce one extra pass of
|
||
|
+ * the loop) in the event they fall out of sync.
|
||
|
+ */
|
||
|
+ if (fds[i+1].fd == watch_fds[i].fd &&
|
||
|
+ (fds[i+1].revents & (POLLERR|POLLHUP|POLLNVAL))) {
|
||
|
+ fds[i+1].events = 0; /* Don't poll this */
|
||
|
+ } else {
|
||
|
+ fds[i+1].fd = watch_fds[i].fd;
|
||
|
+ fds[i+1].events = POLLIN;
|
||
|
+ }
|
||
|
+ fds[i+1].revents = 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
+# endif
|
||
|
}
|
||
|
}
|
||
|
# ifdef HAVE_POLL
|