--- 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 * +* * +***********************************************************************/ +#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 +#include +#include +#include +#include + +#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 ]" +"[-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 # +# # +######################################################################## + +# +# 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))