diff options
Diffstat (limited to 'Src')
-rw-r--r-- | Src/Modules/zselect.c | 310 | ||||
-rw-r--r-- | Src/Modules/zselect.mdd | 6 |
2 files changed, 316 insertions, 0 deletions
diff --git a/Src/Modules/zselect.c b/Src/Modules/zselect.c new file mode 100644 index 000000000..827a97797 --- /dev/null +++ b/Src/Modules/zselect.c @@ -0,0 +1,310 @@ +/* + * zselect.c - builtin support for select system call + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1998-2001 Peter Stephenson + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Peter Stephenson or the Zsh Development + * Group be liable to any party for direct, indirect, special, incidental, + * or consequential damages arising out of the use of this software and + * its documentation, even if Peter Stephenson, and the Zsh + * Development Group have been advised of the possibility of such damage. + * + * Peter Stephenson and the Zsh Development Group specifically + * disclaim any warranties, including, but not limited to, the implied + * warranties of merchantability and fitness for a particular purpose. The + * software provided hereunder is on an "as is" basis, and Peter Stephenson + * and the Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zselect.mdh" +#include "zselect.pro" + +/* Helper functions */ + +/* + * Handle an fd by adding it to the current fd_set. + * Return 1 for error (after printing a message), 0 for OK. + */ +static int +handle_digits(char *nam, char *argptr, fd_set *fdset, int *fdcount, + int *fdmax) +{ + int fd; + char *endptr; + + if (!isdigit(STOUC(*argptr))) { + zwarnnam(nam, "expecting file descriptor: %s", argptr, 0); + return 1; + } + fd = (int)zstrtol(argptr, &endptr, 10); + if (*endptr) { + zwarnnam(nam, "garbage after file descriptor: %s", endptr, 0); + return 1; + } + + FD_SET(fd, fdset); + (*fdcount)++; + if (fd+1 > *fdmax) + *fdmax = fd+1; + return 0; +} + +/* The builtin itself */ + +/**/ +static int +bin_zselect(char *nam, char **args, char *ops, int func) +{ +#ifdef HAVE_SELECT + int i, fd, fdsetind = 0, fdcount = 0, fdmax = 0; + fd_set fdset[3]; + const char fdchar[3] = "rwe"; + struct timeval tv, *tvptr = NULL; + char *outarray = "reply", **outdata, **outptr; + char *outhash = NULL; + LinkList fdlist; + + for (i = 0; i < 3; i++) + FD_ZERO(fdset+i); + + for (; *args; args++) { + char *argptr = *args, *endptr; + zlong tempnum; + if (*argptr == '-') { + for (argptr++; *argptr; argptr++) { + switch (*argptr) { + /* + * Array name for reply, if not $reply. + * This gets set to e.g. `-r 0 -w 1' if 0 is ready + * for reading and 1 is ready for writing. + */ + case 'a': + case 'A': + i = *argptr; + if (argptr[1]) + argptr++; + else if (args[1]) { + argptr = *++args; + } else { + zwarnnam(nam, "argument expected after -%c", NULL, + *argptr); + return 1; + } + if (idigit(*argptr) || !isident(argptr)) { + zwarnnam(nam, "invalid array name: %s", argptr, 0); + return 1; + } + if (i == 'a') + outarray = argptr; + else + outhash = argptr; + /* set argptr to next to last char because of increment */ + while (argptr[1]) + argptr++; + break; + + /* Following numbers indicate fd's for reading */ + case 'r': + fdsetind = 0; + break; + + /* Following numbers indicate fd's for writing */ + case 'w': + fdsetind = 1; + break; + + /* Following numbers indicate fd's for errors */ + case 'e': + fdsetind = 2; + break; + + /* + * Get a timeout value in hundredths of a second + * (same units as KEYTIMEOUT). 0 means just poll. + * If not given, blocks indefinitely. + */ + case 't': + if (argptr[1]) + argptr++; + else if (args[1]) { + argptr = *++args; + } else { + zwarnnam(nam, "argument expected after -%c", NULL, + *argptr); + return 1; + } + if (!idigit(*argptr)) { + zwarnnam(nam, "number expected after -t", NULL, 0); + return 1; + } + tempnum = zstrtol(argptr, &endptr, 10); + if (*endptr) { + zwarnnam(nam, "garbage after -t argument: %s", + endptr, 0); + return 1; + } + /* timevalue now active */ + tvptr = &tv; + tv.tv_sec = (long)(tempnum / 100); + tv.tv_usec = (long)(tempnum % 100) * 10000L; + + /* remember argptr is incremented at end of loop */ + argptr = endptr - 1; + break; + + /* Digits following option without arguments are fd's. */ + default: + if (handle_digits(nam, argptr, fdset+fdsetind, + &fdcount, &fdmax)) + return 1; + } + } + } else if (handle_digits(nam, argptr, fdset+fdsetind, &fdcount, + &fdmax)) + return 1; + } + + errno = 0; + do { + i = select(fdmax, (SELECT_ARG_2_T)fdset, (SELECT_ARG_2_T)(fdset+1), + (SELECT_ARG_2_T)(fdset+2), tvptr); + } while (i < 0 && errno == EINTR && !errflag); + + if (i <= 0) { + if (i < 0) + zwarnnam(nam, "error on select: %e", NULL, errno); + /* else no fd's set. Presumably a timeout. */ + return 1; + } + + /* + * Make a linked list of all file descriptors which are ready. + * These go into an array preceded by -r, -w or -e for read, write, + * error as appropriate. Typically there will only be one set + * so this looks rather like overkill. + */ + fdlist = znewlinklist(); + for (i = 0; i < 3; i++) { + int doneit = 0; + for (fd = 0; fd < fdmax; fd++) { + if (FD_ISSET(fd, fdset+i)) { + char buf[BDIGBUFSIZE]; + if (outhash) { + /* + * Key/value pairs; keys are fd's (as strings), + * value is a (possibly improper) subset of "rwe". + */ + LinkNode nptr; + int found = 0; + + convbase(buf, fd, 10); + for (nptr = firstnode(fdlist); nptr; + nptr = nextnode(nextnode(nptr))) { + if (!strcmp((char *)getdata(nptr), buf)) { + /* Already there, add new character. */ + void **dataptr = getaddrdata(nextnode(nptr)); + char *data = (char *)*dataptr, *ptr; + found = 1; + if (!strchr(data, fdchar[i])) { + strcpy(buf, data); + for (ptr = buf; *ptr; ptr++) + ; + *ptr++ = fdchar[1]; + *ptr = '\0'; + zsfree(data); + *dataptr = ztrdup(buf); + } + break; + } + } + if (!found) { + /* Add new key/value pair. */ + zaddlinknode(fdlist, ztrdup(buf)); + buf[0] = fdchar[i]; + buf[1] = '\0'; + zaddlinknode(fdlist, ztrdup(buf)); + } + } else { + /* List of fd's preceeded by -r, -w, -e. */ + if (!doneit) { + buf[0] = '-'; + buf[1] = fdchar[i]; + buf[2] = 0; + zaddlinknode(fdlist, ztrdup(buf)); + doneit = 1; + } + convbase(buf, fd, 10); + zaddlinknode(fdlist, ztrdup(buf)); + } + } + } + } + + /* convert list to array */ + fdcount = countlinknodes(fdlist); + outptr = outdata = (char **)zalloc((fdcount+1)*sizeof(char *)); + while (nonempty(fdlist)) + *outptr++ = getlinknode(fdlist); + *outptr = '\0'; + /* and store in array parameter */ + if (outhash) + sethparam(outhash, outdata); + else + setaparam(outarray, outdata); + freelinklist(fdlist, NULL); + + return 0; +#else + /* TODO: use poll */ + zerrnam(nam, "your system does not implement the select system call.", + NULL, ); + return 2; +#endif +} + +static struct builtin bintab[] = { + BUILTIN("zselect", 0, bin_zselect, 0, -1, 0, NULL, NULL), +}; + +/* The load/unload routines required by the zsh library interface */ + +/**/ +int +setup_(Module m) +{ + return 0; +} + +/**/ +int +boot_(Module m) +{ + return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); +} + + +/**/ +int +cleanup_(Module m) +{ + deletebuiltins("zselect", bintab, sizeof(bintab)/sizeof(*bintab)); + return 0; +} + +/**/ +int +finish_(Module m) +{ + return 0; +} diff --git a/Src/Modules/zselect.mdd b/Src/Modules/zselect.mdd new file mode 100644 index 000000000..b9ee28535 --- /dev/null +++ b/Src/Modules/zselect.mdd @@ -0,0 +1,6 @@ +name=zsh/zselect +link=dynamic +load=no + +objects="zselect.o" +autobins="zselect" |