diff --git a/funcs.c b/funcs.c index 3638ae1..b1c20c2 100644 --- a/funcs.c +++ b/funcs.c @@ -26,7 +26,19 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.168 2011/01/30 01:35:58 tg Exp $"); +#if HAVE_SELECT +#if HAVE_SYS_BSDTYPES_H +#include +#endif +#if HAVE_SYS_SELECT_H +#include +#endif +#if HAVE_BSTRING_H +#include +#endif +#endif + +__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.169 2011/02/11 00:41:34 tg Exp $"); #if HAVE_KILLPG /* @@ -112,6 +124,9 @@ const struct builtin mkshbuiltins[] = { #endif {"realpath", c_realpath}, {"rename", c_rename}, +#if HAVE_SELECT + {"sleep", c_sleep}, +#endif {NULL, (int (*)(const char **))NULL} }; @@ -3647,3 +3662,43 @@ c_cat(const char **wp) free(buf); return (rv); } + +#if HAVE_SELECT +int +c_sleep(const char **wp) +{ + struct timeval tv; + int rv = 1; + + /* skip argv[0] */ + ++wp; + if (wp[0] && !strcmp(wp[0], "--")) + /* skip "--" (options separator) */ + ++wp; + + if (!wp[0] || wp[1]) + bi_errorf(T_synerr); + else if (parse_usec(wp[0], &tv)) + bi_errorf("%s: %s '%s'", T_synerr, strerror(errno), wp[0]); + else { +#ifndef MKSH_NOPROSPECTOFWORK + sigset_t omask; + + /* block SIGCHLD from interrupting us, though */ + sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); +#endif + if (select(0, NULL, NULL, NULL, &tv) == 0 || errno == EINTR) + /* + * strictly speaking only for SIGALRM, but the + * execution may be interrupted by other signals + */ + rv = 0; + else + bi_errorf("%s: %s", T_select, strerror(errno)); +#ifndef MKSH_NOPROSPECTOFWORK + sigprocmask(SIG_BLOCK, &omask, NULL); +#endif + } + return (rv); +} +#endif diff --git a/mksh.1 b/mksh.1 index d83cc1d..ac34602 100644 --- a/mksh.1 +++ b/mksh.1 @@ -1,4 +1,4 @@ -.\" $MirOS: src/bin/mksh/mksh.1,v 1.248 2011/02/09 13:08:18 tg Exp $ +.\" $MirOS: src/bin/mksh/mksh.1,v 1.249 2011/02/11 00:41:35 tg Exp $ .\" $OpenBSD: ksh.1,v 1.138 2010/09/20 07:41:17 jmc Exp $ .\"- .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, @@ -72,7 +72,7 @@ .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" -.Dd $Mdocdate: February 9 2011 $ +.Dd $Mdocdate: February 11 2011 $ .\" .\" Check which macro package we use .\" @@ -2805,7 +2805,7 @@ regular commands .Ic \&[ , chdir , bind , cat , .Ic echo , let , mknod , print , .Ic printf , pwd , realpath , rename , -.Ic test , ulimit , whence +.Ic sleep , test , ulimit , whence .Pp In the future, the additional .Nm @@ -4047,6 +4047,11 @@ etc. .Ar number defaults to 1. .Pp +.It Ic sleep Ar seconds +Suspends execution for a minimum of the +.Ar seconds +specified as positive decimal value with an optional fractional part. +Signal delivery may continue execution earlier. .It Ic source Ar file Op Ar arg ... Like .Ic \&. Po Do dot Dc Pc , diff --git a/sh.h b/sh.h index 6fd7503..514ca94 100644 --- a/sh.h +++ b/sh.h @@ -154,7 +154,7 @@ #endif #ifdef EXTERN -__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.428 2011/02/09 13:08:27 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.429 2011/02/11 00:41:37 tg Exp $"); #endif #define MKSH_VERSION "R39 2011/02/09" @@ -626,6 +626,7 @@ EXTERN const char T_oomem[] I__("can't allocate %lu data bytes"); #else EXTERN const char T_synerr[] I__("syntax error"); #endif +EXTERN const char T_select[] I__("select"); EXTERN const char T_r_fc_e_[] I__("r=fc -e -"); #define T_fc_e_ (T_r_fc_e_ + 2) /* "fc -e -" */ #define Tn_fc_e_ 7 /* strlen(T_fc_e_) */ @@ -794,7 +795,7 @@ struct coproc { EXTERN struct coproc coproc; #ifndef MKSH_NOPROSPECTOFWORK -/* Used in jobs.c and by coprocess stuff in exec.c */ +/* used in jobs.c and by coprocess stuff in exec.c and select() calls */ EXTERN sigset_t sm_default, sm_sigchld; #endif @@ -1526,6 +1527,7 @@ int c_mknod(const char **); int c_realpath(const char **); int c_rename(const char **); int c_cat(const char **); +int c_sleep(const char **); /* histrap.c */ void init_histvec(void); void hist_init(Source *); @@ -1703,6 +1705,7 @@ int shf_vfprintf(struct shf *, const char *, va_list) /* syn.c */ void initkeywords(void); struct op *compile(Source *); +bool parse_usec(const char *, struct timeval *); /* tree.c */ int fptreef(struct shf *, int, const char *, ...); char *snptreef(char *, int, const char *, ...); diff --git a/syn.c b/syn.c index 2ed737b..67b9999 100644 --- a/syn.c +++ b/syn.c @@ -1,7 +1,7 @@ /* $OpenBSD: syn.c,v 1.28 2008/07/23 16:34:38 jaredy Exp $ */ /*- - * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009 + * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011 * Thorsten Glaser * * Provided that these terms and disclaimer and all copyright notices @@ -22,7 +22,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.52 2010/09/14 21:26:18 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.53 2011/02/11 00:41:38 tg Exp $"); struct nesting_state { int start_token; /* token than began nesting (eg, FOR) */ @@ -405,7 +405,7 @@ get_command(int cf) t = newtp((c == FOR) ? TFOR : TSELECT); musthave(LWORD, ARRAYVAR); if (!is_wdvarname(yylval.cp, true)) - yyerror("%s: %s\n", c == FOR ? "for" : "select", + yyerror("%s: %s\n", c == FOR ? "for" : T_select, "bad identifier"); strdupx(t->str, ident, ATEMP); nesting_push(&old_nesting, c); @@ -736,7 +736,7 @@ const struct tokeninfo { { "case", CASE, true }, { "esac", ESAC, true }, { "for", FOR, true }, - { "select", SELECT, true }, + { T_select, SELECT, true }, { "while", WHILE, true }, { "until", UNTIL, true }, { "do", DO, true }, @@ -1005,3 +1005,61 @@ dbtestp_error(Test_env *te, int offset, const char *msg) } syntaxerr(msg); } + +#if HAVE_SELECT + +#ifndef EOVERFLOW +#ifdef ERANGE +#define EOVERFLOW ERANGE +#else +#define EOVERFLOW EINVAL +#endif +#endif + +bool +parse_usec(const char *s, struct timeval *tv) +{ + struct timeval tt; + int i; + + tv->tv_sec = 0; + /* parse integral part */ + while (ksh_isdigit(*s)) { + tt.tv_sec = tv->tv_sec * 10 + (*s++ - '0'); + if (tt.tv_sec / 10 != tv->tv_sec) { + errno = EOVERFLOW; + return (true); + } + tv->tv_sec = tt.tv_sec; + } + + tv->tv_usec = 0; + if (!*s) + /* no decimal fraction */ + return (false); + else if (*s++ != '.') { + /* junk after integral part */ + errno = EINVAL; + return (true); + } + + /* parse decimal fraction */ + i = 100000; + while (ksh_isdigit(*s)) { + tv->tv_usec += i * (*s++ - '0'); + if (i == 1) + break; + i /= 10; + } + /* check for junk after fractional part */ + while (ksh_isdigit(*s)) + ++s; + if (*s) { + errno = EINVAL; + return (true); + } + + /* end of input string reached, no errors */ + return (false); +} +#endif