summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikael Magnusson <mikachu@gmail.com>2026-06-15 18:18:38 +0200
committerMikael Magnusson <mikachu@gmail.com>2026-07-04 19:56:26 +0200
commit4b4ebccaa9666c608f18f73c5b8102cbfc8e8deb (patch)
tree824833e121c1784a0f23770b3d7f9af612c7bc09
parentunposted: complete more media file types (diff)
downloadzsh-4b4ebccaa9666c608f18f73c5b8102cbfc8e8deb.tar
zsh-4b4ebccaa9666c608f18f73c5b8102cbfc8e8deb.tar.gz
zsh-4b4ebccaa9666c608f18f73c5b8102cbfc8e8deb.tar.bz2
zsh-4b4ebccaa9666c608f18f73c5b8102cbfc8e8deb.tar.lz
zsh-4b4ebccaa9666c608f18f73c5b8102cbfc8e8deb.tar.xz
zsh-4b4ebccaa9666c608f18f73c5b8102cbfc8e8deb.tar.zst
zsh-4b4ebccaa9666c608f18f73c5b8102cbfc8e8deb.zip
54783: improve checkptycmd
With the previous (and now fallback) version of checkptycmd(), doing this: zpty foo 'while read; do echo hi; done' zpty (or zpty -t foo) would hang forever, which seems counterintuitive to the purpose of listing processes (or testing if a process is running).
-rw-r--r--ChangeLog5
-rw-r--r--Src/Modules/zpty.c61
-rw-r--r--Test/V08zpty.ztst12
3 files changed, 78 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index e220499de..49dab9d42 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2026-07-04 Mikael Magnusson <mikachu@gmail.com>
+
+ * 54783: Src/Modules/zpty.c, Test/V08zpty.ztst: improve
+ checkptycmd
+
2026-07-03 Oliver Kiddle <opk@zsh.org>
* unposted: Completion/X/Command/_mplayer, Completion/X/Command/_xv:
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index 6e201e76b..2f9b80f40 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -37,6 +37,13 @@
#endif
#endif
+#ifdef HAVE_POLL_H
+# include <poll.h>
+#endif
+#if defined(HAVE_POLL) && !defined(POLLIN)
+# undef HAVE_POLL
+#endif
+
/* The number of bytes we normally read when given no pattern and the
* upper bound on the number of bytes we read (even if we are give a
* pattern). */
@@ -529,6 +536,59 @@ deleteallptycmds(void)
/**** a better process handling would be nice */
+#ifdef HAVE_POLL
+static void
+checkptycmd(Ptycmd cmd)
+{
+ struct pollfd pfd;
+
+ if (cmd->fin)
+ return;
+ pfd.fd = cmd->fd;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ if (poll(&pfd, 1, 0) <= 0)
+ return; /* no events: slave still open, not finished */
+ if (pfd.revents & POLLIN)
+ return; /* data available, not finished */
+ /* POLLHUP without POLLIN: slave closed, no data */
+ cmd->fin = 1;
+ zclose(cmd->fd);
+}
+#elif defined(FIONREAD)
+static void
+checkptycmd(Ptycmd cmd)
+{
+ int val = 0, select_ret = 0;
+
+ if (cmd->fin)
+ return;
+#ifdef HAVE_SELECT
+ {
+ fd_set fds;
+ struct timeval tv;
+ int ret;
+
+ FD_ZERO(&fds);
+ FD_SET(cmd->fd, &fds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ ret = select(cmd->fd + 1, (SELECT_ARG_2_T) &fds, NULL, NULL, &tv);
+ if (ret > 0 && FD_ISSET(cmd->fd, &fds)) {
+ /* either there are bytes, or the process exited */
+ select_ret = 1;
+ }
+ }
+#endif
+ if (ioctl(cmd->fd, FIONREAD, (char *) &val) == 0 && val > 0)
+ return; /* data available, not finished */
+ /* No data (or ioctl failed): check if process is dead */
+ if (select_ret || kill(cmd->pid, 0) < 0) {
+ cmd->fin = 1;
+ zclose(cmd->fd);
+ }
+}
+#else
static void
checkptycmd(Ptycmd cmd)
{
@@ -545,6 +605,7 @@ checkptycmd(Ptycmd cmd)
}
cmd->read = (int) c;
}
+#endif
static int
ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch)
diff --git a/Test/V08zpty.ztst b/Test/V08zpty.ztst
index 057db2e18..cad6bb9bd 100644
--- a/Test/V08zpty.ztst
+++ b/Test/V08zpty.ztst
@@ -25,3 +25,15 @@
zpty -d cat
0:zpty with a process that does not set up the terminal: write via stdin
>a line of text
+
+ zpty loop 'while read; do echo hello; done'
+ zpty -w loop hi
+ zpty -rt loop | tr -d $'\r'
+ zpty -w loop hi
+ zpty -t loop
+ zpty -rt loop | tr -d $'\r'
+ zpty -rt loop
+ zpty -d loop
+0:zpty doesn't duplicate data
+>hello
+>hello