* @const char *var@ = variable name to set
* @void *ctx@ = context pointer
*
- * Returns: Zero on success, @-1@ on failure.
+ * Returns: %$+1$% on success, %$0$% if the variable name was not
+ * recognized, or %$-1$% on any other error.
*
* Use: Set a special variable. The following special variables are
* supported.
#define f_all (f_time | f_timer)
if (STRCMP(var, ==, "@timeout")) {
+ /* Parse a timeout specification. */
+
+ /* If we've already set one then report an error. */
if (tc->f&TVTF_SETTMO) { rc = tvec_dupreg(tv, var); goto end; }
for (;;) {
+ /* Continue until we run out of things. */
+
+ /* Read the next word. */
DRESET(&d); if (tvec_readword(tv, &d, ";", 0)) break;
timeout_primed:
+
+ /* Start parsing the word. */
p = d.buf;
+
+ /* Check for timer tokens. */
if (!(f&f_timer) && STRCMP(p, ==, "REAL"))
{ tmr = ITIMER_REAL; f |= f_timer; }
else if (!(f&f_timer) && STRCMP(p, ==, "VIRTUAL"))
else if (!(f&f_timer) && STRCMP(p, ==, "PROF"))
{ tmr = ITIMER_PROF; f |= f_timer; }
+ /* Otherwise, check for durations. */
else if (!(f&f_time)) {
+
+ /* Skip leading stuff that isn't digits. This is a hedge against
+ * @strtod@ interpreting something unhelpful like a NaN.
+ */
if (*p == '+' || *p == '-') p++;
if (*p == '.') p++;
if (!ISDIGIT(*p)) {
tvec_syntax(tv, *d.buf, "floating-point number");
rc = -1; goto end;
}
+
+ /* Parse the number and check that it's reasonable. */
errno = 0; t = strtod(p, &q); f |= f_time;
if (errno) {
tvec_error(tv, "invalid floating-point number `%s': %s",
d.buf, strerror(errno));
rc = -1; goto end;
}
+ if (t < 0) {
+ tvec_error(tv, "invalid duration `%s': %s",
+ d.buf, strerror(errno));
+ rc = -1; goto end;
+ }
+ /* We're now on the lookout for units. If there's nothing here then
+ * fetch the next word.
+ */
if (!*q) {
tvec_skipspc(tv); pos = d.len;
if (!tvec_readword(tv, &d, ";", 0)) pos++;
q = d.buf + pos;
}
+ /* Match various units. */
if (!*q || STRCMP(q, ==, "s") || STRCMP(q, ==, "sec"))
/* nothing to do */;
else if (STRCMP(q, ==, "d") || STRCMP(q, ==, "dy")) t *= 86400;
else if (STRCMP(q, ==, "y") || STRCMP(q, ==, "yr")) t *= 31557600;
+ /* Not a unit specification after all. If we've already selected a
+ * timer, then this is just junk so report the error. Otherwise, we
+ * snarfed the next token too early, so move it to the start of the
+ * buffer and go round again.
+ */
else {
if (f&f_timer)
{ rc = tvec_syntax(tv, *q, "end-of-line"); goto end; }
pos = q - d.buf; d.len -= pos;
- memmove(d.buf, q, d.len);
+ memmove(d.buf, q, d.len + 1);
goto timeout_primed;
}
}
+ /* If we've read all that we need to, then stop. */
if (!(~f&f_all)) break;
}
+ /* If we didn't get anything, that's a problem. */
if (!f) {
rc = tvec_syntax(tv, fgetc(tv->fp), "timeout or timer keyword");
goto end;
}
+
+ /* Make sure there's nothing else on the line. */
rc = tvec_flushtoeol(tv, 0); if (rc) goto end;
if (f&f_time) tc->t = t;
if (f&f_timer) tc->timer = tmr;
tc->f |= TVTF_SETTMO;
+ rc = 1;
} else if (subenv && subenv->set)
+ /* Not one of ours: pass it on to the sub-environment. */
rc = subenv->set(tv, var, tc->subctx);
else
+ /* No subenvironment. Report the error. */
rc = 0;
+ /* Done. */
end:
dstr_destroy(&d);
return (rc);
if (subenv && subenv->before) subenv->before(tv, tc->subctx);
}
-/* --- @tvec_timeoutafter@ --- *
+/* --- @tvec_timeoutrun@ --- *
*
* Arguments: @struct tvec_state *tv@ = test vector state
- * @void *ctx@ = context pointer
+ * @tvec_testfn *fn@ = test function to run
+ * @void *ctx@ = context pointer for the test function
*
* Returns: ---
*
- * Use: Invoke the subordinate environment's @after@ function to
- * clean up after the test.
+ * Use: Runs a test with a timeout attached.
*/
-void tvec_timeoutafter(struct tvec_state *tv, void *ctx)
+void tvec_timeoutrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
{
struct tvec_timeoutctx *tc = ctx;
const struct tvec_timeoutenv *te = tc->te;
const struct tvec_env *subenv = te->env;
+ struct itimerval itv;
- /* Reset variables. */
- reset(tc);
+ itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0;
+ itv.it_value.tv_sec = tc->t;
+ itv.it_value.tv_usec = (tc->t - itv.it_value.tv_sec)*1e6;
- /* Pass the call on to the subsidiary environment. */
- if (subenv && subenv->after) subenv->after(tv, tc->subctx);
+ if (setitimer(tc->timer, &itv, 0))
+ tvec_skip(tv, "failed to set interval timer: %s", strerror(errno));
+ else {
+ if (subenv && subenv->run) subenv->run(tv, fn, tc->subctx);
+ else fn(tv->in, tv->out, tc->subctx);
+
+ itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0;
+ itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 0;
+ if (setitimer(tc->timer, &itv, 0))
+ tvec_error(tv, "failed to disarm interval timer: %s", strerror(errno));
+ }
}
-/* --- @tvec_timeoutteardown@ --- *
+/* --- @tvec_timeoutafter@ --- *
*
* Arguments: @struct tvec_state *tv@ = test vector state
* @void *ctx@ = context pointer
*
* Returns: ---
*
- * Use: Tear down the timeoutmark environment.
+ * Use: Invoke the subordinate environment's @after@ function to
+ * clean up after the test.
*/
-void tvec_timeoutteardown(struct tvec_state *tv, void *ctx)
+void tvec_timeoutafter(struct tvec_state *tv, void *ctx)
{
struct tvec_timeoutctx *tc = ctx;
const struct tvec_timeoutenv *te = tc->te;
const struct tvec_env *subenv = te->env;
- /* Just call the subsidiary environment. */
- if (subenv && subenv->teardown) subenv->teardown(tv, tc->subctx);
+ /* Reset variables. */
+ reset(tc);
+
+ /* Pass the call on to the subsidiary environment. */
+ if (subenv && subenv->after) subenv->after(tv, tc->subctx);
}
-/* --- @tvec_timeoutrun@ --- *
+/* --- @tvec_timeoutteardown@ --- *
*
* Arguments: @struct tvec_state *tv@ = test vector state
- * @tvec_testfn *fn@ = test function to run
- * @void *ctx@ = context pointer for the test function
+ * @void *ctx@ = context pointer
*
* Returns: ---
*
- * Use: Runs a test with a timeout attached.
+ * Use: Tear down the timeoutmark environment.
*/
-void tvec_timeoutrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
+void tvec_timeoutteardown(struct tvec_state *tv, void *ctx)
{
struct tvec_timeoutctx *tc = ctx;
const struct tvec_timeoutenv *te = tc->te;
const struct tvec_env *subenv = te->env;
- struct itimerval itv;
-
- itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0;
- itv.it_value.tv_sec = tc->t;
- itv.it_value.tv_usec = (tc->t - itv.it_value.tv_sec)*1e6;
-
- if (setitimer(tc->timer, &itv, 0))
- tvec_skip(tv, "failed to set interval timer: %s", strerror(errno));
- else {
- if (subenv && subenv->run) subenv->run(tv, fn, tc->subctx);
- else fn(tv->in, tv->out, tc->subctx);
- itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0;
- itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 0;
- if (setitimer(tc->timer, &itv, 0))
- tvec_error(tv, "failed to disarm interval timer: %s", strerror(errno));
- }
+ /* Just call the subsidiary environment. */
+ if (subenv && subenv->teardown) subenv->teardown(tv, tc->subctx);
}
/*----- That's all, folks -------------------------------------------------*/