aboutsummaryrefslogblamecommitdiffstats
path: root/strtotimespec.c
blob: b8a7489e1c74e7d896dfbf177fef52c9c686c626 (plain) (tree)















































































































































                                                                                                  
/* 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;
}