ksh/astksh_builtin_poll20120806_001.diff

888 lines
31 KiB
Diff

--- src/cmd/ksh93/Makefile
+++ src/cmd/ksh93/Makefile 2012-08-06 21:25:47.000000000 +0000
@@ -161,6 +161,7 @@ DATAFILES = limits.c msg.c strdata.c tes
shell$(RELEASE) $(VERSION) id=shell :LIBRARY: shell.3 nval.3 alarm.c cd_pwd.c cflow.c deparse.c \
enum.c getopts.c hist.c misc.c print.c read.c sleep.c trap.c test.c \
+ poll.c \
typeset.c ulimit.c umask.c whence.c main.c nvdisc.c nvtype.c \
arith.c args.c array.c completion.c defs.c edit.c expand.c regress.c \
fault.c fcin.c history.c init.c io.c jobs.c lex.c macro.c name.c \
--- src/cmd/ksh93/Mamfile
+++ src/cmd/ksh93/Mamfile 2012-08-06 21:25:47.000000000 +0000
@@ -548,6 +548,22 @@ meta test.o %.c>%.o bltins/test.c test
prev bltins/test.c
exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_API_ast=20100309 -D_PACKAGE_ast -D_BLD_shell -DSHOPT_DYNAMIC -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DSHOPT_STATS -DSHOPT_NAMESPACE -DSHOPT_COSHELL -DSHOPT_HISTEXPAND -DERROR_CONTEXT_T=Error_context_t -DSHOPT_FIXEDARRAY -DSHOPT_ESH -DKSHELL -c bltins/test.c
done test.o generated
+
+make poll.o
+make bltins/poll.c
+prev ${PACKAGE_ast_INCLUDE}/tmx.h implicit
+prev FEATURE/poll implicit
+prev FEATURE/externs implicit
+prev include/builtins.h implicit
+prev include/io.h implicit
+prev ${PACKAGE_ast_INCLUDE}/error.h implicit
+prev include/defs.h implicit
+done bltins/poll.c
+meta poll.o %.c>%.o bltins/poll.c test
+prev bltins/poll.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_API_ast=20100309 -D_PACKAGE_ast -D_BLD_shell -DSHOPT_DYNAMIC -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DSHOPT_STATS -DSHOPT_NAMESPACE -DSHOPT_COSHELL -DSHOPT_DYNAMIC -DSHOPT_HISTEXPAND -DERROR_CONTEXT_T=Error_context_t -DSHOPT_FIXEDARRAY -DSHOPT_ESH -DKSHELL -c bltins/poll.c
+done poll.o generated
+
make typeset.o
make bltins/typeset.c
prev FEATURE/dynamic implicit
@@ -1328,7 +1344,7 @@ meta hexpand.o %.c>%.o edit/hexpand.c he
prev edit/hexpand.c
exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_HISTEXPAND -DSHOPT_EDPREDICT -DSHOPT_MULTIBYTE -DKSHELL -DSHOPT_ESH -DSHOPT_VSH -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_STATS -DSHOPT_NAMESPACE -DSHOPT_COSHELL -D_BLD_shell -D_API_ast=20100309 -DERROR_CONTEXT_T=Error_context_t -DSHOPT_FIXEDARRAY -c edit/hexpand.c
done hexpand.o generated
-exec - ${AR} rc libshell.a alarm.o cd_pwd.o cflow.o deparse.o enum.o getopts.o hist.o misc.o print.o read.o sleep.o trap.o test.o typeset.o ulimit.o umask.o whence.o main.o nvdisc.o nvtype.o arith.o args.o array.o completion.o defs.o edit.o expand.o regress.o fault.o fcin.o
+exec - ${AR} rc libshell.a alarm.o cd_pwd.o cflow.o deparse.o enum.o getopts.o hist.o misc.o poll.o print.o read.o sleep.o trap.o test.o typeset.o ulimit.o umask.o whence.o main.o nvdisc.o nvtype.o arith.o args.o array.o completion.o defs.o edit.o expand.o regress.o fault.o fcin.o
exec - ${AR} rc libshell.a history.o init.o io.o jobs.o lex.o macro.o name.o nvtree.o parse.o path.o string.o streval.o subshell.o tdump.o timers.o trestore.o waitevent.o xec.o env.o limits.o msg.o strdata.o testops.o keywords.o options.o signals.o aliases.o builtins.o variables.o lexstates.o emacs.o vi.o hexpand.o
exec - (ranlib libshell.a) >/dev/null 2>&1 || true
done libshell.a generated
--- src/cmd/ksh93/bltins/poll.c
+++ src/cmd/ksh93/bltins/poll.c 2012-08-07 03:13:40.000000000 +0000
@@ -0,0 +1,717 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 2007-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Roland Mainz <roland.mainz@nrubsig.org> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+#include "defs.h"
+#include "variables.h"
+#include "lexstates.h"
+#include "io.h"
+#include "name.h"
+#include "builtins.h"
+#include "history.h"
+#include "terminal.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <poll.h>
+#include <tmx.h>
+#include <stk.h>
+
+#ifndef SH_DICT
+# define SH_DICT "libshell"
+#endif
+
+#define sh_contexttoshell(context) ((context)?((context)->shp):(NULL))
+
+static
+const char sh_optpoll[] =
+"[-?\n@(#)$Id: poll (AT&T Labs Research) 2012-08-01 $\n]"
+"[-author?Roland Mainz <roland.mainz@nrubsig.org>]"
+"[-license?http://www.eclipse.org/org/documents/epl-v10.html]"
+"[+NAME? poll - input/output multiplexing]"
+"[+DESCRIPTION?The poll command provides applications with a "
+ "mechanism for multiplexing input/output over a set of "
+ "file descriptors. "
+ "For each member of the (optionally sparse) indexed or "
+ "associative compound or type (see \btypeset -T\b) array "
+ "variable \bvar\b, poll examines the given file descriptor "
+ "in the subscript \b.fd\b for the event(s) specified in "
+ "the subscript \b.events\b. "
+ "The poll command identifies those file descriptors on "
+ "which an application can read or write data, or on which "
+ "certain events have occurred.]"
+"[+?The \bvar\b argument specifies an array of file descriptors to "
+ "be examined and the events of interest for each file "
+ "descriptor. "
+ "It is a array of compound variables (or user-defined type) "
+ "with one member for each open file descriptor of interest. "
+ "The array's compound or type variable members contain the "
+ "following subscripts:]{"
+ "[+?\b.fd\b # file descriptor]"
+ "[+?\b.events\b # compound variable of requested event(s)]"
+ "[+?\b.revents\b # compound variable of returned event(s)]"
+ "}"
+"[+?The \bfd\b variable specifies an open file descriptor and the "
+ "\bevents\b and \brevents\b members are compound variables "
+ "constructed from the following boolean member variables:]"
+ "{ "
+ "[+.pollin?('true'|'false') Data other than high priority "
+ "data may be read without blocking. For STREAMS, this "
+ "flag is set in \brevents\b even if the message "
+ "is of zero length.]"
+ "[+.pollrdnorm?('true'|'false') Normal data (priority band "
+ "equals 0) may be read without blocking. For STREAMS, "
+ "this flag is set in \brevents\b even if the message "
+ "is of zero length.]"
+ "[+.pollrdband?('true'|'false') Data from a non-zero "
+ "priority band may be read without blocking. For "
+ "STREAMS, this flag is set in \brevents\b even if the "
+ "message is of zero length.]"
+ "[+.pollpri?('true'|'false') High priority data may be "
+ "received without blocking. For STREAMS, this flag is "
+ "set in \brevents\b even if the message is of zero "
+ "length.]"
+ "[+.pollout?('true'|'false') Normal data (priority band "
+ "equals 0) may be written without blocking.]"
+ "[+.pollwrnorm?('true'|'false') The same as \bpollout\b.]"
+ "[+.pollwrband?('true'|'false') Priority data (priority band "
+ "> 0) may be written. This event only examines bands "
+ "that have been written to at least once.]"
+ "[+.pollerr?('true'|'false') An error has occurred on the "
+ "device or stream. This flag is only valid in the "
+ "\brevents\b compound variable; it is not used in the "
+ "\bevents\b compound variable.]"
+ "[+.pollhup?('true'|'false') A hangup has occurred on the "
+ "stream. This event and \bpollout\b are mutually "
+ "exclusive; a stream can never be writable if a "
+ "hangup has occurred. "
+ "However, this event and \bpollin\b, "
+ "\bpollrdband\b, or \bpollpri\b are not "
+ "mutually exclusive. This flag is only valid "
+ "in the \brevents\b compound variable; it is not "
+ "used in the \bevents\b compound variable.]"
+ "[+.pollnval?('true'|'false') The specified fd value does "
+ "not belong to an open file. "
+ "This flag is only valid in the \brevents\b compound "
+ "variable; it is not used in the \bevents\b "
+ "compound variable.]"
+ "}"
+"]"
+
+"[+?If the value fd is less than 0, events is ignored and "
+ "revents is set to 0 in that entry on return from poll.]"
+
+"[+?The results of the poll query are stored in the \brevents\b "
+ "compound variable members in the \bvar\b structure. "
+ "\bpoll*\b-variables are set in the \brevents\b compound "
+ "variable to indicate which of the requested events are true. "
+ "If none are true, the matching member in the \brevents\b "
+ "compound variable will have the value of 'false' when the "
+ "poll command returns. "
+ "The \brevents\b compound variable members \bpollhup\b, "
+ "\bpollerr\b, and \bpollnval\b are always set to 'true' in "
+ "\brevents\b if the conditions they indicate are true; this "
+ "occurs even though these flags were not present and/or set "
+ "to 'true' in the \bevents\b compound variable.]"
+
+"[+?If none of the defined events have occurred on any selected "
+ "file descriptor, poll waits at least timeout milliseconds "
+ "for an event to occur on any of the selected file "
+ "descriptors. "
+ "On a computer where millisecond timing accuracy is not "
+ "available, timeout is rounded up to the nearest legal value "
+ "available on that system. If the value timeout is 0, poll "
+ "returns immediately. If the value of timeout is -1, poll "
+ "blocks until a requested event occurs or until the call is "
+ "interrupted.]"
+
+"[+?The poll utility supports regular files, terminal and "
+ "pseudo-terminal devices, STREAMS-based files, FIFOs, and "
+ "pipes. The behavior of poll on elements of fds that refer "
+ "to other types of file is unspecified.]"
+
+"[+?The poll utility supports sockets.]"
+#ifdef __SunOS
+"[+?The poll utility may be used on Solaris on directory fds of "
+ "/proc/$pid/ to get a \bpollhup='true'\b when the process quits.]"
+#endif
+"[+?A file descriptor for a socket that is listening for connections "
+ "will indicate that it is ready for reading, once connections "
+ "are available. A file descriptor for a socket that "
+ "is connecting asynchronously will indicate that it is ready "
+ "for writing, once a connection has been established.]"
+
+"[+?Regular files always poll TRUE for reading and writing.]"
+
+"[e:eventarray]:[fdcount?Upon successful completion, an indexed array "
+ "of strings is returned which contains a list of array "
+ "subscripts in the poll array which received events.]"
+"[S!:pollsfio?Look into sfio streams for buffered information and set "
+ "pollin/pollout to reflect sfio stream state.]"
+"[R:pollttyraw?Put tty connections into raw mode when polling. The "
+ "fd is returned to tty cooked mode before poll(1) exits.]"
+"[t:timeout]:[seconds?Timeout in seconds. If the value timeout is 0, "
+ "poll returns immediately. If the value of timeout is -1, "
+ "poll blocks until a requested event occurs or until the "
+ "call is interrupted.]"
+"\n"
+"\nvar\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?Success.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+NOTES?]{"
+ "[+?poll*-variables defined in \bevents\b will always appear "
+ "in \brevents\b. This gives the script author control over "
+ "which poll*-variables he can expect in \brevents\b.]"
+
+ "[+?The \bpollinhup\b, \bpollnval\b and \bpollerr\b variables "
+ "may appear in the \brevents\b compound variable even if "
+ "they were not requested in \bevents\b.]"
+
+ "[+?Using the value of variables in \brevents\b which are "
+ "not set in \bevents\b can be done by putting a '-' suffix "
+ "after the variable name, e.g. use "
+ "\b${ar[x]].revents.pollhup-}\b to get the value of "
+ "\bar[x]].revents.pollhup\b or an empty string if the variable "
+ "was not set.]"
+
+ "[+?Like \bpoll\b(2) it is legal to poll on the same fd in "
+ "multiple entries, for exanple to listen for different events "
+ "or to allow multiple callers to pool their poll lists "
+ "together into one \bpoll\b(1) call.]"
+"}"
+
+/* quoting: ']' must be quoted as "]]" and '?' must be quoted as "//" */
+"[+EXAMPLES?]{"
+ "[+?The following example will wait for 10 seconds for input "
+ "on fd 0, variable \bp[fd0]].revents.pollin\b will be 'true' "
+ "or 'false' depening on whether the stream 0 is ready for "
+ "reading:]{"
+ "[+?compound -A p=( [fd0]]=( fd=0 events=( pollin='true' ) ) ) ; poll -t10 p ; print -v p]"
+ "}"
+
+ "[+?The following example will wait for 2 seconds for input "
+ "on fd 0, and variables \bp[0]].revents.pollin\b and "
+ "\bp[0]].revents.pollhup\b will be 'true' after polling ends "
+ "because there is both input data available and the end of "
+ "the stream was reached:]{"
+ "[+?printf '\\n' | ksh -c 'compound -a p=( ( fd=0 events=( pollin=\"true\" pollhup=\"true\" ) ) ) ; poll -t2 p ; print -v p']"
+ "}"
+"}"
+
+"[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bpoll\b(2)]"
+;
+
+
+/* Like |nv_open()| but constructs variable name on the fly using |sprintf()| format */
+static
+Namval_t *nv_open_fmt(Dt_t *dict, int flags, const char *namefmt, ...)
+{
+ char varnamebuff[PATH_MAX];
+ va_list ap;
+
+ va_start(ap, namefmt);
+ vsnprintf(varnamebuff, sizeof(varnamebuff), namefmt, ap);
+ va_end(ap);
+
+ return nv_open(varnamebuff, dict, flags);
+}
+
+/* Name/value mapping table for POLL*-flags */
+struct pollflagnamemap
+{
+ const int flag;
+ const char *name;
+};
+
+const
+struct pollflagnamemap pfnm[]=
+{
+ { POLLIN, "pollin" },
+#ifdef POLLPRI
+ { POLLPRI, "pollpri" },
+#endif
+ { POLLOUT, "pollout" },
+#ifdef POLLRDNORM
+ { POLLRDNORM, "pollrdnorm" },
+#endif
+#ifdef POLLWRNORM
+ { POLLWRNORM, "pollwrnorm" },
+#endif
+#ifdef POLLRDBAND
+ { POLLRDBAND, "pollrdband" },
+#endif
+#ifdef POLLWRBAND
+ { POLLWRBAND, "pollwrband" },
+#endif
+#ifdef POLLMSG
+ { POLLMSG, "pollmsg" },
+#endif
+#ifdef POLLREMOVE
+ { POLLREMOVE, "pollremove" },
+#endif
+#ifdef POLLRDHUP
+ { POLLRDHUP, "pollrdhup" },
+#endif
+ { POLLERR, "pollerr" },
+ { POLLHUP, "pollhup" },
+ { POLLNVAL, "pollnval" },
+ { 0, NULL },
+};
+
+/* structure to keep track of per array entry data */
+struct pollstat
+{
+ /* name of array subscript */
+ const char *array_subscript;
+
+ /* |sfio| keeps track of sfio information */
+ struct
+ {
+ Sfio_t *sfd;
+ ssize_t flags;
+ } sfio;
+
+ /*
+ * Bits in |eventvar_found| are POLL*-bits, set if matching
+ * ar[i].events.poll* var was found. We use this later to
+ * set the same ar[i].revents.poll* variable, regardless
+ * whether it was polled or not. This was done so the script
+ * author can control which poll* variables in the "revents"
+ * compound appear and which not.
+ */
+ int eventvar_found;
+};
+
+/* poll on given |fds| data and retry after EINTR/EAGAIN while adjusting timeout */
+static
+int poll_loop(Shbltin_t* context, struct pollfd *fds, nfds_t nfds, int timeout)
+{
+/* nanoseconds to milliseconds */
+#define TIME_NS2MS(t) ((t)/(1000UL*1000UL))
+/* milliseconds to nanoseconds */
+#define TIME_MS2NS(t) (((Time_t)(t))*(1000UL*1000UL))
+
+ int n;
+
+ /* We need two codepaths here:
+ * 1. timeout > 0: we have to wait for |timeout| or events.
+ * 2. timeout <= 0: we have to wait forever (-1), return
+ * immediately (0) or an event occurs.
+ */
+ if (timeout > 0)
+ {
+ const Time_t starttime = tmxgettime();
+ Time_t timeout_ns = TIME_MS2NS(timeout);
+
+ do
+ {
+ while(((n = poll(fds, nfds, timeout)) < 0) &&
+ ((errno == EINTR) || (errno == EAGAIN)) &&
+ (!context->sigset))
+ errno=0;
+
+ timeout_ns=timeout_ns-(tmxgettime()-starttime);
+ timeout=TIME_NS2MS(timeout_ns);
+ } while((timeout > 0) && (!context->sigset));
+ }
+ else
+ {
+ while(((n = poll(fds, nfds, timeout)) < 0) &&
+ ((errno == EINTR) || (errno == EAGAIN)) &&
+ (!context->sigset))
+ errno=0;
+ }
+ return n;
+}
+
+/* set ".poll*"-variables in "ar[i].revents" per data in |currpollfd| and |currps| */
+static
+void set_compound_revents(Shell_t *shp, const char *parrayname, struct pollstat *currps, struct pollfd *currpollfd)
+{
+ const char *subname=currps->array_subscript;
+ Namval_t *np;
+ int pi;
+
+ np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_COMVAR, "%s[%s].revents", parrayname, subname);
+ if (!np)
+ {
+ errormsg(SH_DICT, ERROR_ERROR, "could not create pollfd %s[%s].revents", parrayname, subname);
+ return;
+ }
+ nv_setvtree(np); /* make "revents" really a compound variable */
+ nv_close(np);
+
+ for (pi=0 ; pfnm[pi].name != NULL ; pi++)
+ {
+ /*
+ * POLLHUP|POLLNVAL|POLLERR can always appear in |currpollfd->revents|
+ * even if we did not request them in |currpollfd->events|
+ */
+ if ((currps->eventvar_found & pfnm[pi].flag) ||
+ ((currpollfd->revents & (POLLHUP|POLLNVAL|POLLERR)) & pfnm[pi].flag))
+ {
+ np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL, "%s[%s].revents.%s", parrayname, subname, pfnm[pi].name);
+ if (!np)
+ continue;
+
+ nv_putval(np, ((currpollfd->revents & pfnm[pi].flag)?"true":"false"), 0);
+ nv_close(np);
+ }
+ }
+}
+
+/* |main()| for poll(1) builtin */
+extern
+int b_poll(int argc, char *argv[], Shbltin_t* context)
+{
+ Shell_t *shp = sh_contexttoshell(context);
+ Namval_t *np,
+ *array_np,
+ *array_np_sub;
+ Sfio_t *strstk = NULL; /* stk object for memory allocations */
+ const char *parrayname, /* name of array with poll data */
+ *eventarrayname = NULL, /* name of array with indexes to results */
+ *subname, /* current subscript */
+ *s;
+ int n;
+ int fd;
+ nfds_t numpollfd = 0; /* number of entries to poll */
+ int i,
+ j;
+ double timeout = -1.;
+ char buff[PATH_MAX*2+1]; /* fixme: theoretically enough to hold two variable names */
+ bool ttyraw = false;
+ bool pollsfio = true;
+ int pi; /* index for |pfnm| */
+ struct pollfd *pollfd = NULL, /* data for poll(2) */
+ *currpollfd; /* current |pollfd| we are working on */
+ struct pollstat *pollstat = NULL, /* context data from shell array */
+ *currps; /* current |pollstat| we are working on */
+ int retval = 0; /* return value of builtin */
+
+ while (n = optget(argv, sh_optpoll)) switch (n)
+ {
+ case 't':
+ errno = 0;
+ timeout = strtod(opt_info.arg, (char **)NULL);
+ if (errno != 0)
+ errormsg(SH_DICT, ERROR_system(1), "%s: invalid timeout", opt_info.arg);
+
+ /* -t uses seconds */
+ if (timeout >=0)
+ timeout *= 1000.;
+ break;
+ case 'e':
+ eventarrayname = opt_info.arg;
+ break;
+ case 'S':
+ pollsfio=opt_info.num?true:false;
+ break;
+ case 'R':
+ ttyraw=opt_info.num?true:false;
+ break;
+ case ':':
+ errormsg(SH_DICT, ERROR_ERROR, "%s", opt_info.arg);
+ break;
+ case '?':
+ errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ argc -= opt_info.index;
+ argv += opt_info.index;
+ if(argc!=1)
+ errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
+
+ parrayname = argv[0];
+
+ strstk = stkopen(0);
+ if (!strstk)
+ errormsg(SH_DICT, ERROR_system(1), e_nospace);
+
+ array_np = nv_open(parrayname, shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD);
+ if (!array_np)
+ {
+ stkclose(strstk);
+ errormsg(SH_DICT, ERROR_system(1), "cannot find array variable %s", parrayname);
+ }
+ if (!nv_isattr(array_np, NV_ARRAY))
+ {
+ nv_close(array_np);
+ stkclose(strstk);
+ errormsg(SH_DICT, ERROR_system(1), "variable %s is not an array", parrayname);
+ }
+
+ /*
+ * Count number of array elememts. We need to do it "manually"
+ * to handle sparse indexed and associative arrays
+ */
+ nv_putsub(array_np, NULL, ARRAY_SCAN);
+ array_np_sub = array_np;
+ do
+ {
+ if (!(subname=nv_getsub(array_np_sub)))
+ break;
+ numpollfd++;
+ } while(array_np_sub && nv_nextsub(array_np_sub));
+
+ /*
+ * Done with counting, now we need to allocate a work area big enough
+ */
+ pollfd = (struct pollfd *)stkalloc(strstk, (sizeof(struct pollfd) * numpollfd));
+ pollstat = (struct pollstat *)stkalloc(strstk, (sizeof(struct pollstat) * numpollfd));
+ if (!pollfd || !pollstat)
+ {
+ errormsg(SH_DICT, ERROR_ERROR, e_nospace);
+ goto done_error;
+ }
+
+ /*
+ * Walk the array again and fetch the data we need...
+ */
+ nv_putsub(array_np, NULL, ARRAY_SCAN);
+ array_np_sub = array_np;
+ i = 0;
+ do
+ {
+ currps=&pollstat[i];
+ currpollfd=&pollfd[i];
+
+ if (!(subname=nv_getsub(array_np_sub)))
+ break;
+
+ currps->array_subscript=subname=stkcopy(strstk, subname);
+ if (!subname)
+ {
+ errormsg(SH_DICT, ERROR_ERROR, e_nospace);
+ goto done_error;
+ }
+
+ np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%s].fd", parrayname, subname);
+ if (!np)
+ {
+ errormsg(SH_DICT, ERROR_ERROR, "missing pollfd %s[%s].fd", parrayname, subname);
+ goto done_error;
+ }
+ fd = (int)nv_getnum(np);
+ nv_close(np);
+ if ((fd < -1) || (fd > OPEN_MAX))
+ {
+ errormsg(SH_DICT, ERROR_ERROR, "invalid pollfd %s[%s].fd %d", parrayname, subname, fd);
+ goto done_error;
+ }
+ currpollfd->fd = fd;
+
+ np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_COMVAR|NV_NOADD, "%s[%s].events", parrayname, subname);
+ if (!np)
+ {
+ errormsg(SH_DICT, ERROR_ERROR, "missing pollfd %s[%s].events", parrayname, subname);
+ goto done_error;
+ }
+ nv_close(np);
+
+ currpollfd->events=0;
+ currpollfd->revents=0;
+ currps->eventvar_found=0;
+ for (pi=0 ; pfnm[pi].name != NULL ; pi++)
+ {
+ const char *s;
+
+ np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%s].events.%s", parrayname, subname, pfnm[pi].name);
+ if (!np)
+ continue;
+
+ currps->eventvar_found |= pfnm[pi].flag;
+ s=nv_getval(np);
+ if ((s != NULL) && (!strcmp(s, "true")))
+ currpollfd->events |= pfnm[pi].flag;
+ nv_close(np);
+ }
+
+ i++;
+ } while(array_np_sub && nv_nextsub(array_np_sub));
+
+ nv_close(array_np);
+ array_np=NULL;
+
+ /*
+ * If sfio handles fds we need to check whether there are
+ * any data in the sfio buffers and remember this information
+ * so we can set { POLLIN, POLLOUT } on demand to reflect
+ * this information.
+ */
+ if (pollsfio)
+ {
+ Sfio_t **sfd;
+ int num_sfd=0,
+ active_sfd=0;
+
+ sfd = (Sfio_t **)stkalloc(strstk, (sizeof(Sfio_t *) * (numpollfd+1)));
+ if (!sfd)
+ {
+ errormsg(SH_DICT, ERROR_ERROR, e_nospace);
+ goto done_error;
+ }
+
+ for (i=0 ; i < numpollfd ; i++)
+ {
+ currps=&pollstat[i];
+ fd=pollfd[i].fd;
+
+ currps->sfio.sfd=(fd>=0)?sh_fd2sfio(fd):NULL;
+ currps->sfio.flags=0;
+ if (currps->sfio.sfd!=NULL)
+ {
+ /* Only add |currps->sfio.sfd| to the
+ * |sfd| array (list of |Sfio_t*|
+ * passed to |sfpoll()|) if it is not
+ * in that list yet. This prevents
+ * that we call |sfpoll()| on the same
+ * sfio stream multiple times (which
+ * can happen if pollfd contains the
+ * same fd multiple times (which is
+ * valid usage, for example if multiple
+ * consumers pool their pool lists in
+ * one poll call or listen to different
+ * sets of poll event flags)).
+ */
+ for (j=0 ; j < num_sfd ; j++)
+ {
+ if (sfd[j]==currps->sfio.sfd)
+ break;
+ }
+ if (j == num_sfd)
+ sfd[num_sfd++]=currps->sfio.sfd;
+ }
+ }
+
+ active_sfd = sfpoll(&sfd[0], num_sfd, 0);
+ if (active_sfd > 0)
+ {
+ ssize_t sfpoll_flags;
+
+ for (i=0 ; i < active_sfd ; i++)
+ {
+ sfpoll_flags=sfvalue(sfd[i]);
+
+ /*
+ * We have to loop over all entries
+ * because single fd may be polled
+ * multiple times in different pollfd
+ * entries
+ */
+ for (j=0 ; j < numpollfd ; j++)
+ {
+ if (pollstat[j].sfio.sfd == sfd[i])
+ pollstat[j].sfio.flags=sfpoll_flags;
+ }
+ }
+ }
+ }
+
+ /*
+ * Create --eventarray array on demand
+ */
+ if (eventarrayname)
+ {
+ np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_ARRAY|NV_NOFAIL, "%s", eventarrayname);
+ if (!np)
+ {
+ errormsg(SH_DICT, ERROR_ERROR, "could not create eventarray variable %s", eventarrayname);
+ goto done_error;
+ }
+
+ nv_close(np);
+ }
+
+ /*
+ * Make sure we poll on "raw" tty to catch _every_ keystroke...
+ */
+ if (ttyraw)
+ {
+ for (i=0 ; i < numpollfd ; i++)
+ {
+ fd=pollfd[i].fd;
+ if ((fd >=0) && (shp->fdstatus[fd]&IOTTY))
+ tty_raw(fd, 1);
+ }
+ }
+
+ /*
+ * ... then poll for events...
+ */
+ n = poll_loop(context, pollfd, numpollfd, timeout);
+
+ /*
+ * ... and restore the tty's to "cooked" mode
+ */
+ if (ttyraw)
+ {
+ for (i=0 ; i < numpollfd ; i++)
+ {
+ fd=pollfd[i].fd;
+ if ((fd >=0) && (shp->fdstatus[fd]&IOTTY))
+ tty_cooked(fd);
+ }
+ }
+
+ if (n < 0)
+ {
+ /* |ERROR_system(0)| won't quit the builtin */
+ errormsg(SH_DICT, ERROR_system(0), "poll(2) failure");
+ retval=1;
+ }
+
+ /*
+ * Write results back into the array
+ */
+ for (i=0 ; i < numpollfd ; i++)
+ {
+ /* Adjust data in |pollfd[i]| to reflect sfio stream status (if requested) */
+ if (pollsfio)
+ {
+ if ((pollfd[i].events & POLLIN) && (pollstat[i].sfio.flags & SF_READ))
+ pollfd[i].revents |= POLLIN;
+ if ((pollfd[i].events & POLLOUT) && (pollstat[i].sfio.flags & SF_WRITE))
+ pollfd[i].revents |= POLLOUT;
+ }
+
+ set_compound_revents(shp, parrayname, &pollstat[i], &pollfd[i]);
+
+ if (eventarrayname && pollfd[i].revents)
+ {
+ sprintf(buff, "%s+=( '%s' )", eventarrayname, pollstat[i].array_subscript);
+ sh_trap(buff, 0);
+ }
+ }
+
+ goto done;
+
+done_error:
+ retval=1;
+done:
+ if (array_np)
+ nv_close(array_np);
+ if (strstk)
+ stkclose(strstk);
+
+ return(retval);
+}
--- src/cmd/ksh93/data/builtins.c
+++ src/cmd/ksh93/data/builtins.c 2012-08-06 21:25:47.000000000 +0000
@@ -109,6 +109,7 @@ const struct shtable3 shtab_builtins[] =
#endif /* JOBS */
"false", NV_BLTIN|BLT_ENV, bltin(false),
"getopts", NV_BLTIN|BLT_ENV, bltin(getopts),
+ "poll", NV_BLTIN, bltin(poll),
"print", NV_BLTIN|BLT_ENV, bltin(print),
"printf", NV_BLTIN|BLT_ENV, bltin(printf),
"pwd", NV_BLTIN, bltin(pwd),
--- src/cmd/ksh93/include/builtins.h
+++ src/cmd/ksh93/include/builtins.h 2012-08-06 21:25:47.000000000 +0000
@@ -100,6 +100,7 @@ extern int b_wait(int, char*[],Shbltin_t
extern int b_whence(int, char*[],Shbltin_t*);
extern int b_alarm(int, char*[],Shbltin_t*);
+extern int b_poll(int, char*[],Shbltin_t*);
extern int b_print(int, char*[],Shbltin_t*);
extern int b_printf(int, char*[],Shbltin_t*);
extern int b_pwd(int, char*[],Shbltin_t*);
--- src/cmd/ksh93/tests/builtin_poll.sh
+++ src/cmd/ksh93/tests/builtin_poll.sh 2012-08-07 01:22:35.000000000 +0000
@@ -0,0 +1,100 @@
+########################################################################
+# #
+# This software is part of the ast package #
+# Copyright (c) 2009-2012 Roland Mainz #
+# and is licensed under the #
+# Eclipse Public License, Version 1.0 #
+# by AT&T Intellectual Property #
+# #
+# A copy of the License is available at #
+# http://www.eclipse.org/org/documents/epl-v10.html #
+# (with md5 checksum b35adb5213ca9657e911e9befb180842) #
+# #
+# #
+# Roland Mainz <roland.mainz@nrubsig.org> #
+# #
+########################################################################
+
+#
+# Copyright (c) 2009, 2012, Roland Mainz. All rights reserved.
+#
+
+#
+# Test module to check the ksh93 "poll" builtin
+#
+
+# test setup
+function err_exit
+{
+ print -u2 -n '\t'
+ print -u2 -r ${Command}[$1]: "${@:2}"
+ (( Errors++ ))
+}
+alias err_exit='err_exit $LINENO'
+
+set -o nounset
+Command=${0##*/}
+integer Errors=0
+
+typeset ocwd
+typeset tmpdir
+
+# create temporary test directory
+ocwd="${PWD}"
+tmpdir="${ mktemp -t -d 'test_builtin_poll.XXXXXXXX' ; }" || err_exit 'Cannot create temporary directory.'
+
+cd "${tmpdir}" || { err_exit "cd ${tmpdir} failed." ; exit $((Errors<125?Errors:125)) ; }
+
+
+# basic tests
+function test1
+{
+ compound d1=(
+ compound -A u=(
+ [y]=( integer fd=5 ; compound events=( pollin='true' ) revents=() )
+ [x]=( integer fd=5 ; compound events=( pollin='true' ) revents=() )
+ )
+ )
+
+ # test 1:
+ print -C d1 >'testdata.cpv'
+ cat '/dev/zero' | $SHELL -o errexit -c 'builtin poll ; read -C <"testdata.cpv" d1 ; redirect 5<&0 ; poll -e d1.res -t 5. d1.u ; print -C d1 >"testdata.cpv"' >'log.txt' 2>&1 || err_exit "poll returned non-zero exit code $?"
+ unset d1 ; read -C d1 <'testdata.cpv' || err_exit 'Cannot read test data.'
+ [[ "$(< 'log.txt')" == '' ]] || err_exit "Excepted empty stdout/stderr, got $(printf '%q\n' "$(< 'log.txt')")"
+ [[ "${d1.u[x].revents.pollin-}" == 'true' ]] || err_exit "d1.u[x].revents contains '${d1.u[x].revents-}', not 'POLLIN'"
+ [[ "${d1.u[y].revents.pollin-}" == 'true' ]] || err_exit "d1.u[y].revents contains '${d1.u[y].revents-}', not 'POLLIN'"
+ [[ "${d1.res[*]-}" == 'x y' ]] || err_exit "d1.res contains '${d1.res[*]-}', not 'x y'"
+
+ rm 'testdata.cpv' 'log.txt'
+
+ # test 2:
+ unset d1.res
+ d1.u[z]=( integer fd=5 ; compound events=( pollout='true' ) revents=() )
+
+ print -C d1 >'testdata.cpv'
+ $SHELL -o errexit -c 'builtin poll ; read -C <"testdata.cpv" d1 ; { poll -e d1.res -t 5. d1.u ; } 5<"/dev/null" 5>"/dev/null" ; print -C d1 >"testdata.cpv"' >'log.txt' 2>&1 || err_exit "poll returned non-zero exit code $?"
+ unset d1 ; read -C d1 <'testdata.cpv' || err_exit 'Cannot read test data.'
+
+ [[ "$(< 'log.txt')" == '' ]] || err_exit "Excepted empty stdout/stderr, got $(printf '%q\n' "$(< 'log.txt')")"
+ [[ "${d1.u[x].revents.pollin-}" == 'true' ]] || err_exit "d1.u[x].revents contains '${d1.u[x].revents-}', not 'POLLIN'"
+ [[ "${d1.u[y].revents.pollin-}" == 'true' ]] || err_exit "d1.u[y].revents contains '${d1.u[y].revents-}', not 'POLLIN'"
+ [[ "${d1.u[z].revents.pollout-}" == 'true' ]] || err_exit "d1.u[z].revents contains '${d1.u[z].revents-}', not 'POLLOUT,'"
+ [[ "${d1.res[*]-}" == 'x y z' ]] || err_exit "d1.res contains '${d1.res[*]-}', not 'x y z'"
+
+ rm 'testdata.cpv' 'log.txt'
+
+ return 0
+}
+
+# run tests
+builtin 'poll' || err_exit 'poll builtin not found.'
+
+test1
+
+# cleanup
+cd "${ocwd}"
+rmdir "${tmpdir}" || err_exit "Cannot remove temporary directory ${tmpdir}."
+
+
+# tests done
+exit $((Errors<125?Errors:125))