diff options
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | Doc/Makefile.in | 57 | ||||
-rw-r--r-- | Doc/Zsh/mod_zselect.yo | 66 | ||||
-rw-r--r-- | Src/Modules/zselect.c | 310 | ||||
-rw-r--r-- | Src/Modules/zselect.mdd | 6 |
5 files changed, 413 insertions, 32 deletions
diff --git a/ChangeLog b/ChangeLog index 21ff75dcc..3d4e96242 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2002-05-08 Peter Stephenson <pws@csr.com> + + * 17081: Src/Modules/zselect.c, Src/Modules/zselect.mdd, + Doc/Zsh/mod_zselect.yo: zsh/zselect module provides zselect + builtin as front-end to select system call. + 2002-05-08 Andrej Borsenkow <bor@zsh.org> * 17080: Doc/Zsh/compsys.yo: clarify tag-order style usage diff --git a/Doc/Makefile.in b/Doc/Makefile.in index 30cde758c..4b67ca875 100644 --- a/Doc/Makefile.in +++ b/Doc/Makefile.in @@ -61,7 +61,8 @@ Zsh/mod_deltochar.yo Zsh/mod_example.yo Zsh/mod_files.yo \ Zsh/mod_mapfile.yo Zsh/mod_mathfunc.yo Zsh/mod_parameter.yo Zsh/mod_pcre.yo \ Zsh/mod_sched.yo Zsh/mod_stat.yo Zsh/mod_termcap.yo Zsh/mod_terminfo.yo \ Zsh/mod_zftp.yo Zsh/mod_zle.yo Zsh/mod_zleparameter.yo \ -Zsh/mod_zprof.yo Zsh/mod_zpty.yo Zsh/mod_zutil.yo Zsh/mod_tcp.yo +Zsh/mod_zprof.yo Zsh/mod_zpty.yo Zsh/mod_zselect.yo \ +Zsh/mod_zutil.yo Zsh/mod_tcp.yo YODLSRC = zmacros.yo zman.yo ztexi.yo Zsh/arith.yo Zsh/builtins.yo \ Zsh/compat.yo Zsh/compctl.yo Zsh/compsys.yo Zsh/compwid.yo Zsh/cond.yo \ @@ -83,31 +84,27 @@ everything: all dvi ps html dvi: zsh.dvi .PHONY: dvi -zsh.dvi: $(sdir)/zsh.texi - $(TEXI2DVI) $(sdir)/zsh.texi +zsh.dvi: zsh.texi + $(TEXI2DVI) zsh.texi -texi: $(sdir)/zsh.texi +texi: zsh.texi .PHONY: texi -$(sdir)/zsh.texi: +zsh.texi: $(YODL) -o $@ -I$(sdir) -w ztexi.yo version.yo zsh.yo; \ test -f $@ info: zsh.info .PHONY: info -zsh.info: $(sdir)/zsh.texi - $(MAKEINFO) $(sdir)/zsh.texi +zsh.info: zsh.texi + $(MAKEINFO) zsh.texi .yo.1: - case $@ in \ - */*) target=$@ ;; \ - *) target=$(sdir)/$@ ;; \ - esac; \ case '$(YODL)' in :*) ;; *) \ - $(YODL) -I$(sdir) -w zman.yo version.yo $< | sed -e '1s/\\-/-/g' -e '/^\.'\''/d' > $$target \ + $(YODL) -I$(sdir) -w zman.yo version.yo $< | sed -e '1s/\\-/-/g' -e '/^\.'\''/d' > $@ \ ;; esac; \ - test -f $$target + test -f $@ ps: us_ps a4_ps .PHONY: ps @@ -127,28 +124,24 @@ zsh_a4.ps: zsh.dvi html: zsh_toc.html .PHONY: html -zsh_toc.html: $(sdir)/zsh.texi - $(TEXI2HTML) $(sdir)/zsh.texi +zsh_toc.html: zsh.texi + $(TEXI2HTML) zsh.texi zshall.1: zsh.yo - case $@ in \ - */*) target=$@ ;; \ - *) target=$(sdir)/$@ ;; \ - esac; \ case '$(YODL)' in :*) ;; *) \ - $(YODL) -I$(sdir) -DZSHALL -w zman.yo version.yo zsh.yo | sed -e '1s/\\-/-/g' -e '/^\.'\''/d' > $$target \ + $(YODL) -I$(sdir) -DZSHALL -w zman.yo version.yo zsh.yo | sed -e '1s/\\-/-/g' -e '/^\.'\''/d' > $@ \ ;; esac; \ - test -f $$target + test -f $@ ../META-FAQ: META-FAQ.yo Zsh/metafaq.yo case '$(YODL)' in :*) ;; *) \ - $(YODL) -I$(sdir) META-FAQ.yo | sed -e '/NEXTLINE/N' -e '/DELLINE/d' -e '/^SECTHEAD$$/{N;s/^SECTHEAD.//;h;s/./-/g;H;g;}' -e 's/ *$$//' > $(sdir_top)/META-FAQ \ + $(YODL) -I$(sdir) META-FAQ.yo | sed -e '/NEXTLINE/N' -e '/DELLINE/d' -e '/^SECTHEAD$$/{N;s/^SECTHEAD.//;h;s/./-/g;H;g;}' -e 's/ *$$//' > $@ \ ;; esac - test -f $(sdir_top)/META-FAQ + test -f $@ $(YODLDOC): version.yo -$(sdir)/zsh.texi: $(YODLSRC) +zsh.texi: $(YODLSRC) man: $(MAN) .PHONY: man @@ -190,7 +183,7 @@ version.yo: $(sdir_top)/Config/version.mk echo 'def(version)(0)('$(VERSION)')'; \ echo 'def(date)(0)('$(VERSION_DATE)')'; \ echo 'ENDDEF()#' | tr '#' '\\'; \ - ) > $(sdir)/version.yo + ) > $@ Zsh/modlist.yo: $(MODDOCSRC) ( \ @@ -218,7 +211,7 @@ Zsh/modlist.yo: $(MODDOCSRC) mod1=$$mod2; \ mod2=$$mod3; \ done \ - ) > $(sdir)/Zsh/modlist.yo + ) > $@ Zsh/modmenu.yo: $(MODDOCSRC) ( \ @@ -228,7 +221,7 @@ Zsh/modmenu.yo: $(MODDOCSRC) < $(sdir)/$$modfile; \ done; \ echo "endmenu()" \ - ) > $(sdir)/Zsh/modmenu.yo + ) > $@ Zsh/manmodmenu.yo: $(MODDOCSRC) ( \ @@ -236,7 +229,7 @@ Zsh/manmodmenu.yo: $(MODDOCSRC) sed -n '1{s|^COMMENT(!MOD!\(.*\)$$|menu(The \1 Module)|;p;q;}' \ < $(sdir)/$$modfile; \ done \ - ) > $(sdir)/Zsh/manmodmenu.yo + ) > $@ # ========== DEPENDENCIES FOR INSTALLING ========== @@ -252,7 +245,7 @@ uninstall: uninstall.man install.man: man ${SHELL} $(sdir_top)/mkinstalldirs $(DESTDIR)$(mandir)/man1 for file in $(MAN); do \ - $(INSTALL_DATA) $(sdir)/$$file $(DESTDIR)$(mandir)/man1/`echo $$file | sed 's|zsh|$(tzsh)|'` || exit 1; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man1/`echo $$file | sed 's|zsh|$(tzsh)|'` || exit 1; \ done .PHONY: install.man @@ -263,7 +256,7 @@ install.info: texi mkdir infodir if ( \ sed '/^@setfilename/s|zsh|$(tzsh)|' \ - < $(sdir)/zsh.texi > infodir/tzsh.texi && \ + < zsh.texi > infodir/tzsh.texi && \ (cd infodir && $(MAKEINFO) tzsh.texi) && \ for file in infodir/$(tzsh).info*; do \ $(INSTALL_DATA) $$file $(DESTDIR)$(infodir) || exit 1; \ @@ -321,8 +314,8 @@ distclean-here: clean-here .PHONY: distclean-here realclean-here: distclean-here - cd $(sdir) && rm -f Zsh/modlist.yo Zsh/modmenu.yo Zsh/manmodmenu.yo - cd $(sdir) && rm -f version.yo ../META-FAQ zsh.texi $(MAN) + rm -f Zsh/modlist.yo Zsh/modmenu.yo Zsh/manmodmenu.yo + rm -f version.yo ../META-FAQ zsh.texi $(MAN) .PHONY: realclean-here @CLEAN_MK@ diff --git a/Doc/Zsh/mod_zselect.yo b/Doc/Zsh/mod_zselect.yo new file mode 100644 index 000000000..506ef033c --- /dev/null +++ b/Doc/Zsh/mod_zselect.yo @@ -0,0 +1,66 @@ +COMMENT(!MOD!zsh/zselect +Block and return when file descriptors are ready. +!MOD!) +The tt(zsh/zselect) module makes available one builtin command: + +startitem() +findex(zselect) +cindex(select, system call) +cindex(file descriptors, waiting for) +item(tt(zselect) [ tt(-rwe) tt(-t) var(timeout) tt(-a) var(array) ] [ var(fd) ... ])( +The tt(zselect) builtin is a front-end to the `select' system call, which +blocks until a file descriptor is ready for reading or writing, or has an +error condition, with an optional timeout. If this is not available on +your system, the command prints an error message and returns status 2 +(normal errors return status 1). For more information, see your systems +documentation for manref(select)(3). Note there is no connection with the +shell builtin of the same name. + +Arguments and options may be intermingled in any order. Non-option +arguments are file descriptors, which must be decimal integers. By +default, file descriptors are to be tested for reading, i.e. tt(zselect) +will return when data is availble to be read from the file descriptor, or +more precisely, when a read operation from the file descriptor will not +block. After a tt(-r), tt(-w) and tt(-e), the given file descriptors are +to be tested for reading, writing, or error conditions. These options and +an arbitrary list of file descriptors may be given in any order. + +(The presence of an `error condition' is not well defined in the +documentation for many implementations of the select system call. +According to recent versions of the POSIX specification, it is really an +em(exception) condition, of which the only standard example is out-of-band +data received on a socket. So zsh users are unlikely to find the tt(-e) +option useful.) + +The option `tt(-t) var(timeout)' specifies a timeout in hundredths of a +second. This may be zero, in which case the file descriptors will simply +be polled and tt(zselect) will return immediately. It is possible to call +zselect with no file descriptors and a non-zero timeout for use as a +finer-grained replacement for `sleep'; not, however, the return status is +always 1 for a timeout. + +The option `tt(-a) var(array)' indicates that tt(array) should be set to +indicate the file descriptor(s) which are ready. If the option is not +given, the array tt(reply) will be used for this purpose. The array will +contain a string similar to the arguments for tt(zselect). For example, + +example(zselect -t 0 -r 0 -w 1) + +might return immediately with status 0 and tt($reply) containing `tt(-r 0 -w +1)' to show that both file descriptors are ready for the requested +operations. + +The option `tt(-A) var(assoc)' indicates that the associative array +tt(assoc) should be set to indicate the file descriptor(s) which are +ready. This option overrides the option tt(-a), nor will tt(reply) be +modified. The keys of tt(assoc) are the file descriptors, and the +corresponding values are any of the characters `tt(rwe)' to indicate the +condition. + +The command returns 0 if some file descriptors are ready for reading. If +the operation timed out, or a timeout of 0 was given and no file +descriptors were ready, or there was an error, it returns status 1 and +the array will not be set (nor modified in any way). If there was an error +in the select operation the appropriate error message is printed. +) +enditem() 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" |