diff options
Diffstat (limited to '')
-rw-r--r-- | strtotimespec.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/strtotimespec.c b/strtotimespec.c new file mode 100644 index 0000000..b8a7489 --- /dev/null +++ b/strtotimespec.c @@ -0,0 +1,144 @@ +/* See LICENSE file for copyright and license details. */ +#include "libsimple.h" + + +int +libsimple_strtotimespec(struct timespec *restrict ts, const char *restrict s, char **restrict end) +{ + int neg = 0, bracket = 0; + time_t sec = 0; + long int nsec = 0; + long int mul = 100000000L; + const char *p; + + if (end) + *end = (void *)s; + + while (isspace(*s)) + s++; + + if (!isdigit(s) && *s != '+' && *s != '-' && *s != '.') { + errno = EINVAL; + return -1; + } + + if (*s == '-') { + neg = 1; + s++; + } else if (*s == '+') { + s++; + } + + if (*s == '.') { + if (s[1] == '.' || s[1] == '(') { + if (!isdigit(s[2])) { + errno = EINVAL; + return -1; + } + } else if (!isdigit(s[1])) { + errno = EINVAL; + return -1; + } + } + + for (; isdigit(*s); s++) { + if (sec < TIME_MIN / 10) + goto overflow; + sec *= 10; + if (sec < TIME_MIN + (*s & 15)) + goto overflow; + sec -= *s & 15; + } + + if (!neg) { + if (TIME_MIN != -TIME_MAX && sec == TIME_MIN) + goto overflow; + sec = -sec; + } + + if (*s != '.') { + ts->tv_sec = sec; + ts->tv_nsec = 0; + if (end) + *end = (void *)s; + return 0; + } + + for (s++; mul && isdigit(*s); s++) { + nsec += (*s & 15) * mul; + mul /= 10; + } + + if (*s == '.' || *s == '(') { + bracket = *s++ == '('; + p = s; + if (!isdigit(*s)) { + errno = EINVAL; + return -1; + } + for (p = s; isdigit(*p); p++); + if (bracket) { + if (*p == ')') { + p++; + } else { + errno = EINVAL; + return -1; + } + } + if (end) + *end = (void *)p; + p = s; + while (mul) { + for (s = p; mul && isdigit(*s); s++) { + nsec += (*s & 15) * mul; + mul /= 10; + } + } + if (!isdigit(*s)) + s = p; + if (*s >= '5') { + nsec += 1; + if (nsec == 1000000000L) { + if (sec == TIME_MAX) + goto overflow; + sec += 1; + nsec = 0; + } + } + } else { + if (isdigit(*s)) { + if (*s >= '5') { + nsec += 1; + if (nsec == 1000000000L) { + if (sec == TIME_MAX) + goto overflow; + sec += 1; + nsec = 0; + } + } + while (isdigit(*s)) + s++; + } + if (end) + *end = (void *)s; + } + + if (neg && nsec) { + if (sec == TIME_MIN) + goto overflow; + nsec = 1000000000L - nsec; + sec -= 1; + } + + return 0; +overflow: + if (neg) { + ts->tv_sec = TIME_MIN; + ts->tv_nsec = 0; + } else { + ts->tv_sec = TIME_MAX; + ts->tv_nsec = 999999999L; + } + errno = ERANGE; + return -1; +} |