17081: New zselect module and documentation.

This commit is contained in:
Peter Stephenson 2002-05-08 13:13:52 +00:00
parent e484b0f83c
commit c5381b85ed
5 changed files with 413 additions and 32 deletions

View file

@ -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

View file

@ -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@

66
Doc/Zsh/mod_zselect.yo Normal file
View file

@ -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()

310
Src/Modules/zselect.c Normal file
View file

@ -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;
}

6
Src/Modules/zselect.mdd Normal file
View file

@ -0,0 +1,6 @@
name=zsh/zselect
link=dynamic
load=no
objects="zselect.o"
autobins="zselect"