about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2002-05-08 13:13:52 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2002-05-08 13:13:52 +0000
commitc5381b85ed6e5466a5eec780480b429878bdec5d (patch)
tree5322600ae33fe9d351a74f19ff7ea94cc3f9f405
parente484b0f83ca9a795429caee90550955ac5544926 (diff)
downloadzsh-c5381b85ed6e5466a5eec780480b429878bdec5d.tar.gz
zsh-c5381b85ed6e5466a5eec780480b429878bdec5d.tar.xz
zsh-c5381b85ed6e5466a5eec780480b429878bdec5d.zip
17081: New zselect module and documentation.
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Makefile.in57
-rw-r--r--Doc/Zsh/mod_zselect.yo66
-rw-r--r--Src/Modules/zselect.c310
-rw-r--r--Src/Modules/zselect.mdd6
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"