🐙
GNU CoreUtils の sleep は infinity が指定できる
というのを見つけたので、ほんとかよと思いながらソースを調べた。まず調べる前に
$ sleep info
sleep: invalid time interval ‘info’
Try 'sleep --help' for more information.
なるほど意図的ぽい。
$ sleep inf
どうやら inf
と infinity
の両方が指定可能らしい。CoreUtils のソースを適当に検索してみようと GitHub から検索してみたけど、それっぽいものが引っかからない。
こりゃ調べないと分からないなと思って git clone した。こういうの気になりだすと、ちゃんと分かるまで気になって眠れなくなる病気です。
スタート地点は coreutils/src/sleep.c
for (int i = optind; i < argc; i++)
{
double s;
char const *p;
if (! (xstrtod (argv[i], &p, &s, cl_strtod) || errno == ERANGE)
/* Nonnegative interval. */
|| ! (0 <= s)
/* No extra chars after the number and an optional s,m,h,d char. */
|| (*p && *(p+1))
/* Check any suffix char and update S based on the suffix. */
|| ! apply_suffix (&s, *p))
{
error (0, 0, _("invalid time interval %s"), quote (argv[i]));
ok = false;
}
seconds += s;
}
どうやら xstrtod で引数を解析してる。xstrtod は引数に渡した cl_strtod
をコールバック的に渡してるだけ。git submodule の gnulib の中。gnulib/lib/xstrtod.c
DOUBLE
CL_STRTOD (char const *nptr, char **restrict endptr)
{
char *end;
DOUBLE d = STRTOD (nptr, &end);
if (*end)
{
int strtod_errno = errno;
char *c_end;
DOUBLE c = C_STRTOD (nptr, &c_end);
if (end < c_end)
d = c, end = c_end;
else
errno = strtod_errno;
}
if (endptr)
*endptr = end;
return d;
}
という事で STRTOD (strtod) が本体。strtod.c
の中
/* Check for infinities and NaNs. */
else if (c_tolower (*s) == 'i'
&& c_tolower (s[1]) == 'n'
&& c_tolower (s[2]) == 'f')
{
s += 3;
if (c_tolower (*s) == 'i'
&& c_tolower (s[1]) == 'n'
&& c_tolower (s[2]) == 'i'
&& c_tolower (s[3]) == 't'
&& c_tolower (s[4]) == 'y')
s += 5;
num = HUGE_VAL;
errno = saved_errno;
}
else if (c_tolower (*s) == 'n'
&& c_tolower (s[1]) == 'a'
&& c_tolower (s[2]) == 'n')
{
s += 3;
if (*s == '(')
{
const char *p = s + 1;
なるほど。gnulib の strtod
は inf
と infinity
の場合には double の最大の数 HUGE_VAL
が得られると。nan
も NAN
を返す様に判定してるけどこの関数では nan が戻り値として判定され、結局エラーになる。
else if (c_tolower (*s) == 'n'
&& c_tolower (s[1]) == 'a'
&& c_tolower (s[2]) == 'n')
{
s += 3;
if (*s == '(')
{
const char *p = s + 1;
while (c_isalnum (*p))
p++;
if (*p == ')')
s = p + 1;
}
/* If the underlying implementation misparsed the NaN, assume
its result is incorrect, and return a NaN. Normally it's
better to use the underlying implementation's result, since a
nice implementation populates the bits of the NaN according
to interpreting n-char-sequence as a hexadecimal number. */
if (s != end || num == num)
num = NAN;
errno = saved_errno;
}
else
{
という事でまとめ。
Discussion