File astksh_builtin_poll20120806_001.diff of Package ksh

diff -N -r -u 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-07 05:13:40.057935800 +0200
@@ -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);
+}
diff -N -r -u 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-08-06 23:25:47.431435123 +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 -N -r -u 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-08-06 23:25:47.432435170 +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 -N -r -u 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-08-06 23:25:47.432435170 +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 -N -r -u 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-08-06 23:25:47.432435170 +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_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 @@
 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 -DSHOPT_DYNAMIC -D_BLD_shell -DERROR_CONTEXT_T=Error_context_t -D_API_ast=20100309 -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 -N -r -u 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-08-07 03:22:34.753348550 +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.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))
openSUSE Build Service is sponsored by