From dbc0aebcd44a028ed5c5c392fa2b354a1b562955 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 5 Nov 2000 09:27:08 +0000 Subject: Assorted read_poll() and zpty cleanup. --- ChangeLog | 9 +++++ Doc/Zsh/mod_zpty.yo | 85 ++++++++++++++++++++++++++++++--------------- Functions/Misc/nslookup | 2 +- Src/Modules/zpty.c | 91 +++++++++++++++++++++++++++++++++++-------------- Src/utils.c | 34 ++++++++---------- 5 files changed, 147 insertions(+), 74 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2a78b4056..6d8482b3c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2000-11-05 Bart Schaefer + + * 13120: Doc/Zsh/mod_zpty.yo, Functions/Misc/nslookup, + Src/utils.c, Src/Modules/zpty.c: Merge Sven's uncommitted patch + from 13061 with 13116. WARNING: This reverses the meaning of + `zpty -b'; the default is now blocking, -b selects non-blocking. + Fix nslookup accordingly. Clean up read_poll() somewhat. + Document that `zpty -w' and `zpty -r' can stream to/from the pty. + 2000-11-04 Bart Schaefer * 13118: Completion/Core/compinit: Add `bareglobqual' to the diff --git a/Doc/Zsh/mod_zpty.yo b/Doc/Zsh/mod_zpty.yo index 664e2fabd..e90b36d09 100644 --- a/Doc/Zsh/mod_zpty.yo +++ b/Doc/Zsh/mod_zpty.yo @@ -5,42 +5,73 @@ The tt(zsh/zpty) module offers one builtin: startitem() findex(zpty) -xitem(tt(zpty) [ tt(-e) ] [ tt(-b) ] var(name) var(command) [ var(args ...) ]) -xitem(tt(zpty) tt(-d) [ var(names) ... ]) -xitem(tt(zpty) tt(-w) [ tt(-n) ] var(name) var(strings ...)) -xitem(tt(zpty) tt(-r) var(name) [ var(param) [ var(pattern) ] ]) -xitem(tt(zpty) tt(-t) var(name)) -item(tt(zpty) [ tt(-L) ])( +item(tt(zpty) [ tt(-e) ] [ tt(-b) ] var(name) var(command) [ var(args ...) ])( In the first form, the var(command) is started with the var(args) as arguments. The command runs under a newly assigned pseudo-terminal; this is useful for running commands non-interactively which expect an -interactive environment. The var(name) given is used to refer to this -command in later calls to tt(pty). With the tt(-e) option given, the -pseudo-terminal will be set up so that input characters are echoed and -with the tt(-b) option given, input and output from and to the -pseudo-terminal will be blocking. - -The second form with the tt(-d) option is used to delete commands -previously started by supplying a list of their var(name)s. If no +interactive environment. The var(name) is used to refer to this command +in later calls to tt(zpty). + +With the tt(-e) option, the pseudo-terminal is set up so that input +characters are echoed. + +With the tt(-b) option, input to and output from the pseudo-terminal are +made non-blocking. +) +item(tt(zpty) tt(-d) [ var(names) ... ])( +The second form, with the tt(-d) option, is used to delete commands +previously started, by supplying a list of their var(name)s. If no var(names) are given, all commands are deleted. Deleting a command causes the HUP signal to be sent to the corresponding process. - -The tt(-w) option can be used to send the command var(name) the given +) +item(tt(zpty) tt(-w) [ tt(-n) ] var(name) [ var(strings ...) ])( +The tt(-w) option can be used to send the to command var(name) the given var(strings) as input (separated by spaces). If the tt(-n) option is -not given, a newline will be added at the end. +em(not) given, a newline is added at the end. -The tt(-r) option can be used to read the output of the command -var(name). Without a var(param) argument, the string read will be -printed to standard output. With a var(param) argument, the string -read will be put in the parameter named var(param). If the -var(pattern) is also given, output will be read until the whole string -read matches the var(pattern). +If no var(strings) are provided, the standard input is copied to the +pseudo-terminal; this may stop before copying the full input if the +pseudo-terminal is non-blocking. -The tt(-t) option can be used to test whether the command var(name) is -still running. It returns a zero value if the command is running and -a non-zero value otherwise. +Note that the command under the pseudo-terminal sees this input as if it +were typed, so beware when sending special tty driver characters such as +word-erase, line-kill, and end-of-file. +) +item(tt(zpty) tt(-r) [ tt(-t) ] var(name) [ var(param) [ var(pattern) ] ])( +The tt(-r) option can be used to read the output of the command var(name). +With only a var(name) argument, the output read is copied to the standard +output. Unless the pseudo-terminal is non-blocking, copying continues +until the command under the pseudo-terminal exits; when non-blocking, only +as much output as is immediately available is copied. The return value is +zero if any output is copied. -The last form without any arguments is used to list the commands +When also given a var(param) argument, at most one line is read and stored +in the parameter named var(param). Less than a full line may be read if +the pseudo-terminal is non-blocking. The return value is zero if at least +one character is stored in var(param). + +If a var(pattern) is given as well, output is read until the whole string +read matches the var(pattern), even in the non-blocking case. The return +value is zero if the string read matches the pattern, or if the command +has exited but at least one character could still be read. As of this +writing, a maximum of one megabyte of output can be consumed this way; if +a full megabyte is read without matching the pattern, the return value is +non-zero. + +In all cases, the return value is non-zero if nothing could be read, and +is tt(2) if this is because the command has finished. + +If the tt(-r) option is combined with the tt(-t) option, tt(zpty) tests +whether output is available before trying to read. If no output is +available, tt(zpty) immediately returns the value tt(1). +) +item(tt(zpty) tt(-t) var(name))( +The tt(-t) option without the tt(-r) option can be used to test +whether the command var(name) is still running. It returns a zero +value if the command is running and a non-zero value otherwise. +) +item(tt(zpty) [ tt(-L) ])( +The last form, without any arguments, is used to list the commands currently defined. If the tt(-L) option is given, this is done in the form of calls to the tt(zpty) builtin. ) diff --git a/Functions/Misc/nslookup b/Functions/Misc/nslookup index 1751ef68a..d4f7e96b9 100644 --- a/Functions/Misc/nslookup +++ b/Functions/Misc/nslookup @@ -24,7 +24,7 @@ zstyle -s ':nslookup' pager tmp && [[ -z "$pager" ]] && pager="${opager:-more}" (( $#pmpt )) || pmpt=(-p '> ') -zpty -b nslookup nslookup "$@" +zpty nslookup nslookup "$@" zpty -r nslookup line '* > ' diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index b854fe8e3..463fbdf75 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -34,7 +34,6 @@ * upper bound on the number of bytes we read (even if we are give a * pattern). */ -#define READ_LEN 1024 #define READ_MAX (1024 * 1024) typedef struct ptycmd *Ptycmd; @@ -46,9 +45,10 @@ struct ptycmd { int fd; int pid; int echo; - int block; + int nblock; int fin; int read; + char *old; }; static Ptycmd ptycmds; @@ -264,7 +264,7 @@ get_pty(int master, int *retfd) #endif /* __SVR4 */ static int -newptycmd(char *nam, char *pname, char **args, int echo, int block) +newptycmd(char *nam, char *pname, char **args, int echo, int nblock) { Ptycmd p; int master, slave, pid; @@ -380,14 +380,15 @@ newptycmd(char *nam, char *pname, char **args, int echo, int block) p->fd = master; p->pid = pid; p->echo = echo; - p->block = block; + p->nblock = nblock; p->fin = 0; p->read = -1; + p->old = NULL; p->next = ptycmds; ptycmds = p; - if (!block) + if (nblock) ptynonblock(master); return 0; @@ -411,12 +412,12 @@ deleteptycmd(Ptycmd cmd) zsfree(p->name); freearray(p->args); + zclose(cmd->fd); + /* We kill the process group the command put itself in. */ kill(-(p->pid), SIGHUP); - zclose(cmd->fd); - zfree(p, sizeof(*p)); } @@ -438,7 +439,7 @@ checkptycmd(Ptycmd cmd) { if (cmd->read != -1) return; - if (!read_poll(cmd->fd, &cmd->read, !cmd->block) && + if (!read_poll(cmd->fd, &cmd->read, 0) && kill(cmd->pid, 0) < 0) { cmd->fin = 1; zclose(cmd->fd); @@ -448,8 +449,8 @@ checkptycmd(Ptycmd cmd) static int ptyread(char *nam, Ptycmd cmd, char **args) { - int blen = 256, used = 0, seen = 0, ret = 1; - char *buf = (char *) zhalloc((blen = 256) + 1); + int blen, used, seen = 0, ret = 0; + char *buf; Patprog prog = NULL; if (*args && args[1]) { @@ -469,10 +470,20 @@ ptyread(char *nam, Ptycmd cmd, char **args) } else fflush(stdout); + if (cmd->old) { + used = strlen(cmd->old); + buf = (char *) zhalloc((blen = 256 + used) + 1); + strcpy(buf, cmd->old); + zsfree(cmd->old); + cmd->old = NULL; + } else { + used = 0; + buf = (char *) zhalloc((blen = 256) + 1); + } if (cmd->read != -1) { - buf[0] = (char) cmd->read; - buf[1] = '\0'; - used = 1; + buf[used] = (char) cmd->read; + buf[used + 1] = '\0'; + seen = used = 1; cmd->read = -1; } do { @@ -495,36 +506,60 @@ ptyread(char *nam, Ptycmd cmd, char **args) } buf[used] = '\0'; - if (!prog && ret <= 0) + if (!prog && (ret <= 0 || (*args && buf[used - 1] == '\n'))) break; - } while (!errflag && !breaks && !retflag && !contflag && - (prog ? (used < READ_MAX && (!ret || !pattry(prog, buf))) : - (used < READ_LEN))); + } while (!(errflag || breaks || retflag || contflag) && + used < READ_MAX && !(prog && ret && pattry(prog, buf))); + + if (prog && ret < 0 && +#ifdef EWOULDBLOCK + errno == EWOULDBLOCK +#else +#ifdef EAGAIN + errno == EAGAIN +#endif +#endif + ) { + cmd->old = ztrdup(buf); + used = 0; + return 1; + } if (*args) setsparam(*args, ztrdup(metafy(buf, used, META_HREALLOC))); - else + else if (used) write(1, buf, used); - return !seen; + return (seen ? 0 : cmd->fin + 1); } static int ptywritestr(Ptycmd cmd, char *s, int len) { - int written; + int written, all = 0; - for (; len; len -= written, s += written) { - if ((written = write(cmd->fd, s, len)) < 0) - return 1; + for (; !errflag && !breaks && !retflag && !contflag && len; + len -= written, s += written) { + if ((written = write(cmd->fd, s, len)) < 0 && cmd->nblock && +#ifdef EWOULDBLOCK + errno == EWOULDBLOCK +#else +#ifdef EAGAIN + errno == EAGAIN +#endif +#endif + ) + return !all; if (written < 0) { checkptycmd(cmd); if (cmd->fin) break; written = 0; } + if (written > 0) + all += written; } - return 0; + return (all ? 0 : cmd->fin + 1); } static int @@ -559,8 +594,9 @@ static int bin_zpty(char *nam, char **args, char *ops, int func) { if ((ops['r'] && ops['w']) || - ((ops['r'] || ops['w']) && (ops['d'] || ops['e'] || ops['t'] || + ((ops['r'] || ops['w']) && (ops['d'] || ops['e'] || ops['b'] || ops['L'])) || + (ops['w'] && ops['t']) || (ops['n'] && (ops['b'] || ops['e'] || ops['r'] || ops['t'] || ops['d'] || ops['L'])) || (ops['d'] && (ops['b'] || ops['e'] || ops['L'] || ops['t'])) || @@ -579,7 +615,10 @@ bin_zpty(char *nam, char **args, char *ops, int func) return 1; } if (p->fin) + return 2; + if (ops['t'] && p->read == -1 && !read_poll(p->fd, &p->read, 0)) return 1; + return (ops['r'] ? ptyread(nam, p, args + 1) : ptywrite(p, args + 1, ops['n'])); @@ -629,7 +668,7 @@ bin_zpty(char *nam, char **args, char *ops, int func) checkptycmd(p); if (ops['L']) printf("%s %s%s%s ", nam, (p->echo ? "-e " : ""), - (p->block ? "-b " : ""), p->name); + (p->nblock ? "-b " : ""), p->name); else if (p->fin) printf("(finished) %s: ", p->name); else diff --git a/Src/utils.c b/Src/utils.c index 05865cf67..1190347f7 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1315,7 +1315,7 @@ setblock_stdin(void) mod_export int read_poll(int fd, int *readchar, int polltty) { - int ret = 0; + int ret = -1; long mode = -1; char c; #ifdef HAVE_SELECT @@ -1353,38 +1353,32 @@ read_poll(int fd, int *readchar, int polltty) */ if (polltty) { gettyinfo(&ti); - ti.tio.c_cc[VMIN] = 0; - settyinfo(&ti); + if ((polltty = ti.tio.c_cc[VMIN])) { + ti.tio.c_cc[VMIN] = 0; + settyinfo(&ti); + } } #else polltty = 0; #endif #ifdef HAVE_SELECT - if (!ret) { - expire_tv.tv_sec = expire_tv.tv_usec = 0; - FD_ZERO(&foofd); - FD_SET(fd, &foofd); - if (select(fd+1, (SELECT_ARG_2_T) &foofd, NULL, NULL, &expire_tv) - > 1) - ret = 1; - } + expire_tv.tv_sec = expire_tv.tv_usec = 0; + FD_ZERO(&foofd); + FD_SET(fd, &foofd); + ret = select(fd+1, (SELECT_ARG_2_T) &foofd, NULL, NULL, &expire_tv); #else #ifdef FIONREAD - if (!ret) { - ioctl(fd, FIONREAD, (char *)&val); - if (val) - ret = 1; - } + if (ioctl(fd, FIONREAD, (char *) &val) == 0) + ret = (val > 0); #endif #endif - if (!ret) { + if (ret <= 0) { /* * Final attempt: set non-blocking read and try to read a character. * Praise Bill, this works under Cygwin (nothing else seems to). */ - if ((polltty || setblock_fd(0, fd, &mode)) - && read(fd, &c, 1) > 0) { + if ((polltty || setblock_fd(0, fd, &mode)) && read(fd, &c, 1) > 0) { *readchar = STOUC(c); ret = 1; } @@ -1397,7 +1391,7 @@ read_poll(int fd, int *readchar, int polltty) settyinfo(&ti); } #endif - return ret; + return (ret > 0); } /**/ -- cgit 1.4.1