Hi!

----

Attached (as "astksh_builtin_poll20120728_001.diff.txt") is the
_prototype_ patch for a poll(1) builtin and a demo application
(attached as "shircbot.sh.txt") which shows it's usage.

WARNING for existing users: There has been an API change - see below...

Basic usage example:
-- snip --
$ ksh -c 'compound -a p=( ( fd=0 events=( pollin="true" ) ) ) ; poll
-t10 p ; print -v p'
{press <ENTER key>}
(
        (
                events=(
                        pollin=true
                )
                fd=0
                revents=(
                        pollin=true
                )
        )
)
-- snip --

Changes since the last version:
- The API has been changed in an incompatible way. Previously all
POLL*-flags were stuffed together in a single string variable (e.g.
"events" and "revents"). This has been replaced with individual
variables (making "events" and "revents" compound variables) which
have the values 'true' or 'false'.
- "shircbot.sh" has been updated to use the new API
- |sfpoll()| is now only called _once_ per poll(1) call (poll(2) still
needs to be called to listen for POLLHUP&co. ... this can't be changed
until we get a sfpoll2() function in sfio which supports all flags
provided by the systems poll(2)). Note that the old code which called
|sfpoll()| per pollfd entry is still around but #ifdef'ed out. This is
for regression testing only.
- fd=-1 is now a valid usage
- "revents.pollhup", "revents.pollerr" and "pollevents.pollnval" are
now set and available in the "revents" compound variable even if they
have not been requested in the "events" compound variable. This
reflects the same behaviour for POLLHUP, POLLERR and POLLNVAL mandated
by the POSIX spec for poll(2)
- timeout's are now processed properly if the builtin is interupted
with a signal (e.g. poll(2) returns with EINTR and the code adjusts
the timeout and repeats the poll(2) call)
- $ poll --man # now has more information, including a NOTES and
EXAMPLES section
- The error handling code is still so ugly that little kitten will get
blind and implode.

ToDo:
- POSIX-like shell mode (e.g. add API which makes it possible that
poll(1) can be implemented by dash(1) or bash(1) or other shells which
do not have compound variables):
1. If the input variable is a plain string array (not compound
variable array) it should read those strings and append/replace
"revents='...'" to those strings (this is mainly intended for
simplification and to allow authors to write a bash4/zsh/dash version
of poll(1) without having to implement compound variable support
first... ;-/ )
2. If the input variable is a compound variable array or type variable
array "events" and "revents" should be compound variables themselves

Notes:
- All poll*-variables representing poll(2) flags set in the "events"
compound variable are set (regardless whether they are 'true'/'false'
or processed by poll(2)) in the "revents" variable, too. This has been
implemented that way to ensure the script authors can just do a $ set
-o nounset ; ... if ${myar[foo].revents.pollout} ; then ... # without
getting slapped by the shell that myar[foo].revents.pollout isn't set
or something like that.
- Please check the output of $ poll --man # for proper wording,
spelling and use of english. I'm not a native english speaker and I
guess some formulations are worse then getting teeth pulled... ;-/
- The ksh93 API is considered feature-complete and will be declared
stable with the next release unless there are any objections (there is
still the missing support for string arrays but this is IMO not an API
change... just an addition).

I'll make a proper annoucement once ksh93u+ final is out (assuming
sufficient feedback has been provided (this hint is for
Irek&&Lionel)).

----

Bye,
Roland

-- 
  __ .  . __
 (o.\ \/ /.o) roland.ma...@nrubsig.org
  \__\/\/__/  MPEG specialist, C&&JAVA&&Sun&&Unix programmer
  /O /==\ O\  TEL +49 641 3992797
 (;O/ \/ \O;)
diff -r -u -N original/src/cmd/ksh93/bltins/poll.c 
build_poll/src/cmd/ksh93/bltins/poll.c
--- src/cmd/ksh93/bltins/poll.c 1970-01-01 01:00:00.000000000 +0100
+++ src/cmd/ksh93/bltins/poll.c 2012-08-01 01:31:42.944973042 +0200
@@ -0,0 +1,758 @@
+/***********************************************************************
+*                                                                      *
+*               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.ma...@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>
+
+#ifndef SH_DICT
+#   define SH_DICT "libshell"
+#endif
+
+#define sh_contexttoshell(context)     ((context)?((context)->shp):(NULL))
+
+#ifdef DEBUG_CMD_POLL
+#define D(x) x
+#else
+#define D(x)
+#endif
+
+static
+const char sh_optpoll[] =
+"[-?\n@(#)$Id: poll (AT&T Labs Research) 2012-07-28 $\n]"
+"[-author?Roland Mainz <roland.ma...@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 |no_open()| but construct 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);
+}
+
+/*
+ * Like |strdup()| but uses a Sfio string stream for storage
+ * instead of |malloc()|
+ */
+static
+const char *mysfstrdup(Sfio_t *str, const char *s)
+{
+        ssize_t pos=stktell(str);
+        sfputr(str, s, 0);
+        return stkptr(str, pos);
+}
+
+#ifdef SFPOLL_ONCE_PER_POLLFD
+/* get |sfpoll()| SF_* flags for a single |Sfio_t*| */
+static
+ssize_t getsfiopollflags(Sfio_t*sfd)
+{
+       ssize_t flags;
+
+       if (sfpoll(&sfd, 1, 0) == 1)
+               flags=sfvalue(sfd);
+       else
+               flags=0;
+
+       return flags;
+}
+#endif
+
+/* Name/value mapping table for POLL*-flags */
+struct pollflagnamemap
+{
+       const int flag;
+       const char *name;
+};
+
+const
+struct pollflagnamemap pfnm[]=
+{
+       { POLLIN,       "pollin"        },
+       { POLLPRI,      "pollpri"       },
+       { 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 *varname, 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", varname, subname);
+       if (!np)
+       {
+               errormsg(SH_DICT, ERROR_warn(1), "could not create pollfd 
%s[%s].revents", varname, 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", varname, 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;
+       const char *subname;
+       char *varname;
+       int n;
+       int fd;
+       nfds_t numpollfd = 0;
+       int i, j;
+       char *s;
+       double timeout = -1.;
+       char buff[PATH_MAX*2+1]; /* fixme: theoretically enough to hold two 
variable names */
+       char *eventarrayname = NULL;
+       bool ttyraw = false;
+       bool pollsfio = true;
+       Sfio_t *strbuff=NULL;
+       int retval=0;
+       int pi;
+       struct pollstat *currps; /* current |pollstat| we're working on */
+       struct pollfd *currpollfd;
+
+       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, 2, "%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));
+
+       varname = argv[0];
+
+       strbuff = sfstropen();
+       if (!strbuff)
+               errormsg(SH_DICT, ERROR_system(1), "out of memory for 
sfstropen()");
+
+       D(if (!pollsfio)        fprintf(stderr, "#poll-debug: sfio polling 
DISABLED\n"));
+       D(if (!ttyraw)          fprintf(stderr, "#poll-debug: ttyraw 
DISABLED\n"));
+
+       array_np = nv_open(varname, shp->var_tree, 
NV_VARNAME|NV_NOFAIL|NV_NOADD);
+       if (!array_np)
+       {
+               sfclose(strbuff);
+               errormsg(SH_DICT, ERROR_system(1), "cannot find array variable 
%s", varname);
+       }
+       if (!nv_isattr(array_np, NV_ARRAY))
+       {
+               nv_close(array_np);
+               sfclose(strbuff);
+               errormsg(SH_DICT, ERROR_system(1), "variable %s is not an 
array", varname);
+       }
+
+       /* 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));
+
+       /* We must allocate one more entry, C99 "variable length
+        * arrays"(="VLA") with zero elements do not work with all
+        * compilers */
+       struct pollfd pollfd[numpollfd+1];      /* data for poll(2) */
+       struct pollstat pollstat[numpollfd+1];  /* context data from shell 
array */
+
+       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=mysfstrdup(strbuff, subname);
+               if (!subname)
+               {
+                       errormsg(SH_DICT, ERROR_warn(1), "out of memory");
+                       goto done_error;
+               }
+
+               np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, 
"%s[%s].fd", varname, subname);
+               if (!np)
+               {
+                       errormsg(SH_DICT, ERROR_warn(1), "missing pollfd 
%s[%s].fd", varname, subname);
+                       goto done_error;
+               }
+               fd = (int)nv_getnum(np);
+               nv_close(np);
+               if ((fd < -1) || (fd > OPEN_MAX))
+               {
+                       errormsg(SH_DICT, ERROR_warn(1), "invalid pollfd pollfd 
%s[%s].fd %d", varname, 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", varname, subname);
+               if (!np)
+               {
+                       errormsg(SH_DICT, ERROR_warn(1), "missing pollfd 
%s[%s].events", varname, 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", varname, 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 (pollsfio)
+       {
+#ifdef SFPOLL_ONCE_PER_POLLFD
+               /*
+                * This algorithm is not very smart (and does not scale
+                * well if many poll entries are used).
+                * Calling |sfpoll()| (via the |getsfiopollflags()|
+                * wrapper) means we |poll()| once per fd to check
+                * whether the matching sfio stream has any data
+                * buffered.
+                * However there is currently no (semi-)public sfio
+                * API which can poll on sfio streams (including
+                * calling SFPOLL disciplines if neccesary), provides
+                * access to all other poll(2) flags/information
+                * and can turn the probing of sfio buffered data on/off
+                * on demand.
+                * 
+                * fixme: Refactor the loop that it builds a |Sfpoll_t|
+                * array, calls |sfpoll() _ONCE_ and then passes the
+                * returned SF_READ/SF|WRITE flags back to
+                * |currpollfd|.
+                */
+               for (i=0 ; i < numpollfd ; i++)
+               {
+                       currps=&pollstat[i];
+                       currpollfd=&pollfd[i];
+                       fd=currpollfd->fd;
+                       
+                       currps->sfio.sfd=(fd>=0)?sh_fd2sfio(fd):NULL;
+                       if (currps->sfio.sfd!=NULL)
+                       {
+                               
currps->sfio.flags=getsfiopollflags(currps->sfio.sfd);
+
+                               if (((currpollfd->events & POLLIN)  && 
(currps->sfio.flags & SF_READ)) ||
+                                   ((currpollfd->events & POLLOUT) && 
(currps->sfio.flags & SF_WRITE)))
+                                       timeout=0;
+                       }
+                       else
+                       {
+                               currps->sfio.flags=0;
+                       }
+               }
+#else
+               Sfio_t  *sfd[numpollfd+1];
+               int     num_sfd=0,
+                       active_sfd=0;
+
+               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);
+               D(fprintf(stderr, "# sfpoll(%p, %d) returned %d\n", (void 
*)sfd, num_sfd, active_sfd));
+               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;
+                                               D(fprintf(stderr, "# 
pollstat[%d].sfio.flags=%d ; sfd[%d]\n", j, (int)sfpoll_flags, i));
+                                       }
+                               }
+                       }
+               }
+#endif
+       }
+
+       if (eventarrayname)
+       {
+               np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_ARRAY|NV_NOFAIL, 
"%s", eventarrayname);
+               if (!np)
+               {
+                       errormsg(SH_DICT, ERROR_warn(1), "could not create poll 
count 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)
+       {
+               errormsg(SH_DICT, ERROR_warn(1), "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, varname, &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 (strbuff)
+               sfclose(strbuff);
+       
+       return(retval);
+}
diff -r -u -N original/src/cmd/ksh93/data/builtins.c 
build_poll/src/cmd/ksh93/data/builtins.c
--- src/cmd/ksh93/data/builtins.c       2012-06-19 10:02:12.000000000 +0200
+++ src/cmd/ksh93/data/builtins.c       2012-07-31 23:04:10.638130082 +0200
@@ -109,6 +109,7 @@
 #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),
diff -r -u -N original/src/cmd/ksh93/include/builtins.h 
build_poll/src/cmd/ksh93/include/builtins.h
--- src/cmd/ksh93/include/builtins.h    2012-01-10 20:11:54.000000000 +0100
+++ src/cmd/ksh93/include/builtins.h    2012-07-31 23:04:10.638925291 +0200
@@ -100,6 +100,7 @@
 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*);
diff -r -u -N original/src/cmd/ksh93/Makefile build_poll/src/cmd/ksh93/Makefile
--- src/cmd/ksh93/Makefile      2012-06-19 09:46:54.000000000 +0200
+++ src/cmd/ksh93/Makefile      2012-07-31 23:04:10.639449150 +0200
@@ -161,6 +161,7 @@
 
 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 \
diff -r -u -N original/src/cmd/ksh93/Mamfile build_poll/src/cmd/ksh93/Mamfile
--- src/cmd/ksh93/Mamfile       2012-07-27 18:05:35.000000000 +0200
+++ src/cmd/ksh93/Mamfile       2012-07-31 23:04:10.641434992 +0200
@@ -548,6 +548,22 @@
 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_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 @@
 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
diff -r -u -N original/src/cmd/ksh93/tests/builtin_poll.sh 
build_poll/src/cmd/ksh93/tests/builtin_poll.sh
--- src/cmd/ksh93/tests/builtin_poll.sh 1970-01-01 01:00:00.000000000 +0100
+++ src/cmd/ksh93/tests/builtin_poll.sh 2012-07-31 23:04:10.642088895 +0200
@@ -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.ma...@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))
_______________________________________________
ast-developers mailing list
ast-developers@research.att.com
https://mailman.research.att.com/mailman/listinfo/ast-developers

Reply via email to