From acda8dc2564ea0099c6a948cbbfaaefde81f8652 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 26 Jun 2000 15:00:27 +0000 Subject: 12073: read -t to test for available input before reading --- ChangeLog | 6 +++ Doc/Zsh/builtins.yo | 18 +++++++- Src/builtin.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++---- Src/utils.c | 33 ++++++++++---- 4 files changed, 164 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2e7faa5bb..8f8882a45 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2000-06-26 Peter Stephenson + + * 12073: Src/builtin.c, Src/utils.c, Doc/Zsh/builtins.yo: first + pass at `read -t' which tests for availability of input before + inputting. + 2000-06-26 Sven Wischnowsky * 3208: Completion/User/_use_lo: default completion even if there diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 70748621c..7a6b50a98 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -751,7 +751,7 @@ contain symbolic links. alias(r)(fc -e -) findex(read) vindex(IFS, use of) -item(tt(read) [ tt(-rzpqAclneE) ] [ tt(-k) [ var(num) ] ] \ +item(tt(read) [ tt(-rzpqAclneEt) ] [ tt(-k) [ var(num) ] ] \ [ tt(-u)var(n) ] [ var(name)[tt(?)var(prompt)] ] [ var(name) ... ])( Read one line and break it into fields using the characters in tt($IFS) as separators, except as noted below. @@ -822,6 +822,22 @@ digit and must em(not) be separated from tt(-u) by any whitespace. item(tt(-p))( Input is read from the coprocess. ) +item(tt(-t))( +Test if input is available before attempting to read; if none is, return +status 1 and do not set any variables. This is not available when reading +from the editor buffer with tt(-z), when called from within completion +with tt(-c) or tt(-l), with tt(-q) which clears the input queue before +reading, or within zle where other mechanisms should be used to test for +input. + +Note that read does not attempt to alter the input processing mode. The +default mode is canonical input, in which an entire line is read at a time, +so usually `tt(read -t)' will not read anything until an entire line has +been typed. However, when reading from the terminal with tt(-k) +this is automatically handled; note that only availablity of the first +character is tested, so that e.g. `tt(read -t -k 2)' can still block on the +second character. +) enditem() If the first argument contains a `tt(?)', the remainder of this diff --git a/Src/builtin.c b/Src/builtin.c index 69dcdd70f..3b38c2674 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -97,7 +97,7 @@ static struct builtin builtins[] = BUILTIN("pushln", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"), BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL), BUILTIN("r", BINF_R, bin_fc, 0, -1, BIN_FC, "nrl", NULL), - BUILTIN("read", 0, bin_read, 0, -1, 0, "rzu0123456789pkqecnAlE", NULL), + BUILTIN("read", 0, bin_read, 0, -1, 0, "ceklnpqrtzuAE0123456789", NULL), BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFHLRTUZafghiltux", "r"), BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "df", "r"), BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL), @@ -3361,6 +3361,94 @@ static int readfd; /* Read a character from readfd, or from the buffer zbuf. Return EOF on end of file/buffer. */ +/**/ +static int +read_poll(int *readchar, int polltty) +{ + int ret = 0; + long mode = -1; + char c; +#ifdef FIONREAD + int val; +#endif +#ifdef HAVE_SELECT + fd_set foofd; + struct timeval expire_tv; +#endif +#ifdef HAS_TIO + struct ttyinfo ti; +#endif + + +#if defined(HAS_TIO) && !defined(__CYGWIN__) + /* + * Under Solaris, at least, reading from the terminal in non-canonical + * mode requires that we use the VMIN mechanism to poll. Any attempt + * to check any other way, or to set the terminal to non-blocking mode + * and poll that way, fails; it will just for canonical mode input. + * We should probably use this mechanism if the user has set non-canonical + * mode, in which case testing here for isatty() and ~ICANON would be + * better than testing whether bin_read() set it, but for now we've got + * enough problems. + * + * Under Cygwin, you won't be surprised to here, this mechanism, + * although present, doesn't work, and we *have* to use ordinary + * non-blocking reads to find out if there is a character present + * in non-canonical mode. + * + * I am assuming Solaris is nearer the UNIX norm. This is not necessarily + * as plausible as it sounds, but it seems the right way to guess. + * pws 2000/06/26 + */ + if (polltty) { + gettyinfo(&ti); + 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(readfd, &foofd); + if (select(readfd+1, (SELECT_ARG_2_T) &foofd, NULL, NULL, &expire_tv) + > 1) + ret = 1; + } +#else +#ifdef FIONREAD + if (!ret) { + ioctl(readfd, FIONREAD, (char *)&val); + if (val) + ret = 1; + } +#endif +#endif + + if (!ret) { + /* + * 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, readfd, &mode)) + && read(readfd, &c, 1) > 0) { + *readchar = STOUC(c); + ret = 1; + } + if (mode != -1) + fcntl(readfd, F_SETFL, mode); + } +#ifdef HAS_TIO + if (polltty) { + ti.tio.c_cc[VMIN] = 1; + settyinfo(&ti); + } +#endif + return ret; +} + /* read: get a line of input, or (for compctl functions) return some * * useful data about the state of the editing line. The -E and -e * * options mean that the result should be sent to stdout. -e means, * @@ -3378,6 +3466,9 @@ bin_read(char *name, char **args, char *ops, int func) char *buf, *bptr, *firstarg, *zbuforig; LinkList readll = newlinklist(); FILE *oshout = NULL; + int readchar = -1, val; + char d; + if ((ops['k'] || ops['b']) && *args && idigit(**args)) { if (!(nchars = atoi(*args))) @@ -3438,6 +3529,17 @@ bin_read(char *name, char **args, char *ops, int func) } else readfd = izle = 0; + if (ops['t'] && !read_poll(&readchar, keys && !zleactive)) { + if (ops['k'] && !zleactive && !isem) + settyinfo(&shttyinfo); + if (haso) { + fclose(shout); + shout = oshout; + SHTTY = -1; + } + return 1; + } + /* handle prompt */ if (firstarg) { for (readpmpt = firstarg; @@ -3453,9 +3555,6 @@ bin_read(char *name, char **args, char *ops, int func) /* option -k means read only a given number of characters (default 1) */ if (ops['k']) { - int val; - char d; - /* allocate buffer space for result */ bptr = buf = (char *)zalloc(nchars+1); @@ -3467,7 +3566,11 @@ bin_read(char *name, char **args, char *ops, int func) nchars--; } else { /* If read returns 0, is end of file */ - if ((val = read(readfd, bptr, nchars)) <= 0) + if (readchar >= 0) { + *bptr = readchar; + val = 1; + readchar = -1; + } else if ((val = read(readfd, bptr, nchars)) <= 0) break; /* decrement number of characters read from number required */ @@ -3544,7 +3647,7 @@ bin_read(char *name, char **args, char *ops, int func) buf = bptr = (char *)zalloc(bsiz = 64); /* get input, a character at a time */ while (!gotnl) { - c = zread(izle); + c = zread(izle, &readchar); /* \ at the end of a line indicates a continuation * * line, except in raw mode (-r option) */ if (bslash && c == '\n') { @@ -3645,7 +3748,7 @@ bin_read(char *name, char **args, char *ops, int func) if (!gotnl) { sigset_t s = child_unblock(); for (;;) { - c = zread(izle); + c = zread(izle, &readchar); /* \ at the end of a line introduces a continuation line, except in raw mode (-r option) */ if (bslash && c == '\n') { @@ -3711,7 +3814,7 @@ bin_read(char *name, char **args, char *ops, int func) /**/ static int -zread(int izle) +zread(int izle, int *readchar) { char cc, retry = 0; @@ -3730,6 +3833,11 @@ zread(int izle) else return (*zbuf) ? STOUC(*zbuf++) : EOF; } + if (*readchar >= 0) { + cc = *readchar; + *readchar = -1; + return STOUC(cc); + } for (;;) { /* read a character from readfd */ switch (read(readfd, &cc, 1)) { diff --git a/Src/utils.c b/Src/utils.c index b4be1c4f1..f423795ab 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1249,7 +1249,7 @@ zstrtol(const char *s, char **t, int base) /**/ int -setblock_stdin(void) +setblock_fd(int turnonblocking, int fd, long *modep) { #ifdef O_NDELAY # ifdef O_NONBLOCK @@ -1267,20 +1267,37 @@ setblock_stdin(void) #if NONBLOCK struct stat st; - long mode; - if (!fstat(0, &st) && !S_ISREG(st.st_mode)) { - mode = fcntl(0, F_GETFL, 0); - if (mode != -1 && (mode & NONBLOCK) && - !fcntl(0, F_SETFL, mode & ~NONBLOCK)) - return 1; - } + if (!fstat(fd, &st) && !S_ISREG(st.st_mode)) { + *modep = fcntl(fd, F_GETFL, 0); + if (*modep != -1) { + if (!turnonblocking) { + /* We want to know if blocking was off */ + if ((*modep & NONBLOCK) || + !fcntl(fd, F_SETFL, *modep | NONBLOCK)) + return 1; + } else if ((*modep & NONBLOCK) && + !fcntl(fd, F_SETFL, *modep & ~NONBLOCK)) { + /* Here we want to know if the state changed */ + return 1; + } + } + } else #endif /* NONBLOCK */ + *modep = -1; return 0; #undef NONBLOCK } +/**/ +int +setblock_stdin(void) +{ + long mode; + return setblock_fd(1, 0, &mode); +} + /**/ int checkrmall(char *s) -- cgit 1.4.1