diff options
Diffstat (limited to 'libc-top-half/musl/src/time')
40 files changed, 1931 insertions, 0 deletions
diff --git a/libc-top-half/musl/src/time/__map_file.c b/libc-top-half/musl/src/time/__map_file.c new file mode 100644 index 0000000..d3cefa8 --- /dev/null +++ b/libc-top-half/musl/src/time/__map_file.c @@ -0,0 +1,19 @@ +#include <sys/mman.h> +#include <fcntl.h> +#include <sys/stat.h> +#include "syscall.h" +#include "kstat.h" + +const char unsigned *__map_file(const char *pathname, size_t *size) +{ + struct kstat st; + const unsigned char *map = MAP_FAILED; + int fd = sys_open(pathname, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) return 0; + if (!syscall(SYS_fstat, fd, &st)) { + map = __mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + *size = st.st_size; + } + __syscall(SYS_close, fd); + return map == MAP_FAILED ? 0 : map; +} diff --git a/libc-top-half/musl/src/time/__month_to_secs.c b/libc-top-half/musl/src/time/__month_to_secs.c new file mode 100644 index 0000000..43248fb --- /dev/null +++ b/libc-top-half/musl/src/time/__month_to_secs.c @@ -0,0 +1,10 @@ +int __month_to_secs(int month, int is_leap) +{ + static const int secs_through_month[] = { + 0, 31*86400, 59*86400, 90*86400, + 120*86400, 151*86400, 181*86400, 212*86400, + 243*86400, 273*86400, 304*86400, 334*86400 }; + int t = secs_through_month[month]; + if (is_leap && month >= 2) t+=86400; + return t; +} diff --git a/libc-top-half/musl/src/time/__secs_to_tm.c b/libc-top-half/musl/src/time/__secs_to_tm.c new file mode 100644 index 0000000..093d902 --- /dev/null +++ b/libc-top-half/musl/src/time/__secs_to_tm.c @@ -0,0 +1,82 @@ +#include "time_impl.h" +#include <limits.h> + +/* 2000-03-01 (mod 400 year, immediately after feb29 */ +#define LEAPOCH (946684800LL + 86400*(31+29)) + +#define DAYS_PER_400Y (365*400 + 97) +#define DAYS_PER_100Y (365*100 + 24) +#define DAYS_PER_4Y (365*4 + 1) + +int __secs_to_tm(long long t, struct tm *tm) +{ + long long days, secs, years; + int remdays, remsecs, remyears; + int qc_cycles, c_cycles, q_cycles; + int months; + int wday, yday, leap; + static const char days_in_month[] = {31,30,31,30,31,31,30,31,30,31,31,29}; + + /* Reject time_t values whose year would overflow int */ + if (t < INT_MIN * 31622400LL || t > INT_MAX * 31622400LL) + return -1; + + secs = t - LEAPOCH; + days = secs / 86400; + remsecs = secs % 86400; + if (remsecs < 0) { + remsecs += 86400; + days--; + } + + wday = (3+days)%7; + if (wday < 0) wday += 7; + + qc_cycles = days / DAYS_PER_400Y; + remdays = days % DAYS_PER_400Y; + if (remdays < 0) { + remdays += DAYS_PER_400Y; + qc_cycles--; + } + + c_cycles = remdays / DAYS_PER_100Y; + if (c_cycles == 4) c_cycles--; + remdays -= c_cycles * DAYS_PER_100Y; + + q_cycles = remdays / DAYS_PER_4Y; + if (q_cycles == 25) q_cycles--; + remdays -= q_cycles * DAYS_PER_4Y; + + remyears = remdays / 365; + if (remyears == 4) remyears--; + remdays -= remyears * 365; + + leap = !remyears && (q_cycles || !c_cycles); + yday = remdays + 31 + 28 + leap; + if (yday >= 365+leap) yday -= 365+leap; + + years = remyears + 4*q_cycles + 100*c_cycles + 400LL*qc_cycles; + + for (months=0; days_in_month[months] <= remdays; months++) + remdays -= days_in_month[months]; + + if (months >= 10) { + months -= 12; + years++; + } + + if (years+100 > INT_MAX || years+100 < INT_MIN) + return -1; + + tm->tm_year = years + 100; + tm->tm_mon = months + 2; + tm->tm_mday = remdays + 1; + tm->tm_wday = wday; + tm->tm_yday = yday; + + tm->tm_hour = remsecs / 3600; + tm->tm_min = remsecs / 60 % 60; + tm->tm_sec = remsecs % 60; + + return 0; +} diff --git a/libc-top-half/musl/src/time/__tm_to_secs.c b/libc-top-half/musl/src/time/__tm_to_secs.c new file mode 100644 index 0000000..c29fa98 --- /dev/null +++ b/libc-top-half/musl/src/time/__tm_to_secs.c @@ -0,0 +1,24 @@ +#include "time_impl.h" + +long long __tm_to_secs(const struct tm *tm) +{ + int is_leap; + long long year = tm->tm_year; + int month = tm->tm_mon; + if (month >= 12 || month < 0) { + int adj = month / 12; + month %= 12; + if (month < 0) { + adj--; + month += 12; + } + year += adj; + } + long long t = __year_to_secs(year, &is_leap); + t += __month_to_secs(month, is_leap); + t += 86400LL * (tm->tm_mday-1); + t += 3600LL * tm->tm_hour; + t += 60LL * tm->tm_min; + t += tm->tm_sec; + return t; +} diff --git a/libc-top-half/musl/src/time/__tz.c b/libc-top-half/musl/src/time/__tz.c new file mode 100644 index 0000000..9551503 --- /dev/null +++ b/libc-top-half/musl/src/time/__tz.c @@ -0,0 +1,456 @@ +#include "time_impl.h" +#include <stdint.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#ifdef __wasilibc_unmodified_upstream // timezone data +#include <sys/mman.h> +#include <ctype.h> +#endif +#include "libc.h" +#include "lock.h" +#include "fork_impl.h" + +#define malloc __libc_malloc +#define calloc undef +#define realloc undef +#define free undef + +#ifdef __wasilibc_unmodified_upstream // timezone data +long __timezone = 0; +int __daylight = 0; +char *__tzname[2] = { 0, 0 }; + +weak_alias(__timezone, timezone); +weak_alias(__daylight, daylight); +weak_alias(__tzname, tzname); + +static char std_name[TZNAME_MAX+1]; +static char dst_name[TZNAME_MAX+1]; +#endif +const char __utc[] = "UTC"; + +#ifdef __wasilibc_unmodified_upstream // timezone data +static int dst_off; +static int r0[5], r1[5]; + +static const unsigned char *zi, *trans, *index, *types, *abbrevs, *abbrevs_end; +static size_t map_size; + +static char old_tz_buf[32]; +static char *old_tz = old_tz_buf; +static size_t old_tz_size = sizeof old_tz_buf; + +static volatile int lock[1]; +volatile int *const __timezone_lockptr = lock; + +static int getint(const char **p) +{ + unsigned x; + for (x=0; **p-'0'<10U; (*p)++) x = **p-'0' + 10*x; + return x; +} + +static int getoff(const char **p) +{ + int neg = 0; + if (**p == '-') { + ++*p; + neg = 1; + } else if (**p == '+') { + ++*p; + } + int off = 3600*getint(p); + if (**p == ':') { + ++*p; + off += 60*getint(p); + if (**p == ':') { + ++*p; + off += getint(p); + } + } + return neg ? -off : off; +} + +static void getrule(const char **p, int rule[5]) +{ + int r = rule[0] = **p; + + if (r!='M') { + if (r=='J') ++*p; + else rule[0] = 0; + rule[1] = getint(p); + } else { + ++*p; rule[1] = getint(p); + ++*p; rule[2] = getint(p); + ++*p; rule[3] = getint(p); + } + + if (**p=='/') { + ++*p; + rule[4] = getoff(p); + } else { + rule[4] = 7200; + } +} + +static void getname(char *d, const char **p) +{ + int i; + if (**p == '<') { + ++*p; + for (i=0; (*p)[i] && (*p)[i]!='>'; i++) + if (i<TZNAME_MAX) d[i] = (*p)[i]; + if ((*p)[i]) ++*p; + } else { + for (i=0; ((*p)[i]|32)-'a'<26U; i++) + if (i<TZNAME_MAX) d[i] = (*p)[i]; + } + *p += i; + d[i<TZNAME_MAX?i:TZNAME_MAX] = 0; +} + +#define VEC(...) ((const unsigned char[]){__VA_ARGS__}) + +static uint32_t zi_read32(const unsigned char *z) +{ + return (unsigned)z[0]<<24 | z[1]<<16 | z[2]<<8 | z[3]; +} + +static size_t zi_dotprod(const unsigned char *z, const unsigned char *v, size_t n) +{ + size_t y; + uint32_t x; + for (y=0; n; n--, z+=4, v++) { + x = zi_read32(z); + y += x * *v; + } + return y; +} + +static void do_tzset() +{ + char buf[NAME_MAX+25], *pathname=buf+24; + const char *try, *s, *p; + const unsigned char *map = 0; + size_t i; + static const char search[] = + "/usr/share/zoneinfo/\0/share/zoneinfo/\0/etc/zoneinfo/\0"; + + s = getenv("TZ"); + if (!s) s = "/etc/localtime"; + if (!*s) s = __utc; + + if (old_tz && !strcmp(s, old_tz)) return; + + for (i=0; i<5; i++) r0[i] = r1[i] = 0; + + if (zi) __munmap((void *)zi, map_size); + + /* Cache the old value of TZ to check if it has changed. Avoid + * free so as not to pull it into static programs. Growth + * strategy makes it so free would have minimal benefit anyway. */ + i = strlen(s); + if (i > PATH_MAX+1) s = __utc, i = 3; + if (i >= old_tz_size) { + old_tz_size *= 2; + if (i >= old_tz_size) old_tz_size = i+1; + if (old_tz_size > PATH_MAX+2) old_tz_size = PATH_MAX+2; + old_tz = malloc(old_tz_size); + } + if (old_tz) memcpy(old_tz, s, i+1); + + int posix_form = 0; + if (*s != ':') { + p = s; + char dummy_name[TZNAME_MAX+1]; + getname(dummy_name, &p); + if (p!=s && (*p == '+' || *p == '-' || isdigit(*p) + || !strcmp(dummy_name, "UTC") + || !strcmp(dummy_name, "GMT"))) + posix_form = 1; + } + + /* Non-suid can use an absolute tzfile pathname or a relative + * pathame beginning with "."; in secure mode, only the + * standard path will be searched. */ + if (!posix_form) { + if (*s == ':') s++; + if (*s == '/' || *s == '.') { + if (!libc.secure || !strcmp(s, "/etc/localtime")) + map = __map_file(s, &map_size); + } else { + size_t l = strlen(s); + if (l <= NAME_MAX && !strchr(s, '.')) { + memcpy(pathname, s, l+1); + pathname[l] = 0; + for (try=search; !map && *try; try+=l+1) { + l = strlen(try); + memcpy(pathname-l, try, l); + map = __map_file(pathname-l, &map_size); + } + } + } + if (!map) s = __utc; + } + if (map && (map_size < 44 || memcmp(map, "TZif", 4))) { + __munmap((void *)map, map_size); + map = 0; + s = __utc; + } + + zi = map; + if (map) { + int scale = 2; + if (map[4]!='1') { + size_t skip = zi_dotprod(zi+20, VEC(1,1,8,5,6,1), 6); + trans = zi+skip+44+44; + scale++; + } else { + trans = zi+44; + } + index = trans + (zi_read32(trans-12) << scale); + types = index + zi_read32(trans-12); + abbrevs = types + 6*zi_read32(trans-8); + abbrevs_end = abbrevs + zi_read32(trans-4); + if (zi[map_size-1] == '\n') { + for (s = (const char *)zi+map_size-2; *s!='\n'; s--); + s++; + } else { + const unsigned char *p; + __tzname[0] = __tzname[1] = 0; + __daylight = __timezone = dst_off = 0; + for (p=types; p<abbrevs; p+=6) { + if (!p[4] && !__tzname[0]) { + __tzname[0] = (char *)abbrevs + p[5]; + __timezone = -zi_read32(p); + } + if (p[4] && !__tzname[1]) { + __tzname[1] = (char *)abbrevs + p[5]; + dst_off = -zi_read32(p); + __daylight = 1; + } + } + if (!__tzname[0]) __tzname[0] = __tzname[1]; + if (!__tzname[0]) __tzname[0] = (char *)__utc; + if (!__daylight) { + __tzname[1] = __tzname[0]; + dst_off = __timezone; + } + return; + } + } + + if (!s) s = __utc; + getname(std_name, &s); + __tzname[0] = std_name; + __timezone = getoff(&s); + getname(dst_name, &s); + __tzname[1] = dst_name; + if (dst_name[0]) { + __daylight = 1; + if (*s == '+' || *s=='-' || *s-'0'<10U) + dst_off = getoff(&s); + else + dst_off = __timezone - 3600; + } else { + __daylight = 0; + dst_off = __timezone; + } + + if (*s == ',') s++, getrule(&s, r0); + if (*s == ',') s++, getrule(&s, r1); +} + +/* Search zoneinfo rules to find the one that applies to the given time, + * and determine alternate opposite-DST-status rule that may be needed. */ + +static size_t scan_trans(long long t, int local, size_t *alt) +{ + int scale = 3 - (trans == zi+44); + uint64_t x; + int off = 0; + + size_t a = 0, n = (index-trans)>>scale, m; + + if (!n) { + if (alt) *alt = 0; + return 0; + } + + /* Binary search for 'most-recent rule before t'. */ + while (n > 1) { + m = a + n/2; + x = zi_read32(trans + (m<<scale)); + if (scale == 3) x = x<<32 | zi_read32(trans + (m<<scale) + 4); + else x = (int32_t)x; + if (local) off = (int32_t)zi_read32(types + 6 * index[m-1]); + if (t - off < (int64_t)x) { + n /= 2; + } else { + a = m; + n -= n/2; + } + } + + /* First and last entry are special. First means to use lowest-index + * non-DST type. Last means to apply POSIX-style rule if available. */ + n = (index-trans)>>scale; + if (a == n-1) return -1; + if (a == 0) { + x = zi_read32(trans); + if (scale == 3) x = x<<32 | zi_read32(trans + 4); + else x = (int32_t)x; + /* Find the lowest non-DST type, or 0 if none. */ + size_t j = 0; + for (size_t i=abbrevs-types; i; i-=6) { + if (!types[i-6+4]) j = i-6; + } + if (local) off = (int32_t)zi_read32(types + j); + /* If t is before first transition, use the above-found type + * and the index-zero (after transition) type as the alt. */ + if (t - off < (int64_t)x) { + if (alt) *alt = index[0]; + return j/6; + } + } + + /* Try to find a neighboring opposite-DST-status rule. */ + if (alt) { + if (a && types[6*index[a-1]+4] != types[6*index[a]+4]) + *alt = index[a-1]; + else if (a+1<n && types[6*index[a+1]+4] != types[6*index[a]+4]) + *alt = index[a+1]; + else + *alt = index[a]; + } + + return index[a]; +} + +static int days_in_month(int m, int is_leap) +{ + if (m==2) return 28+is_leap; + else return 30+((0xad5>>(m-1))&1); +} + +/* Convert a POSIX DST rule plus year to seconds since epoch. */ + +static long long rule_to_secs(const int *rule, int year) +{ + int is_leap; + long long t = __year_to_secs(year, &is_leap); + int x, m, n, d; + if (rule[0]!='M') { + x = rule[1]; + if (rule[0]=='J' && (x < 60 || !is_leap)) x--; + t += 86400 * x; + } else { + m = rule[1]; + n = rule[2]; + d = rule[3]; + t += __month_to_secs(m-1, is_leap); + int wday = (int)((t + 4*86400) % (7*86400)) / 86400; + int days = d - wday; + if (days < 0) days += 7; + if (n == 5 && days+28 >= days_in_month(m, is_leap)) n = 4; + t += 86400 * (days + 7*(n-1)); + } + t += rule[4]; + return t; +} + +/* Determine the time zone in effect for a given time in seconds since the + * epoch. It can be given in local or universal time. The results will + * indicate whether DST is in effect at the queried time, and will give both + * the GMT offset for the active zone/DST rule and the opposite DST. This + * enables a caller to efficiently adjust for the case where an explicit + * DST specification mismatches what would be in effect at the time. */ + +void __secs_to_zone(long long t, int local, int *isdst, long *offset, long *oppoff, const char **zonename) +{ + LOCK(lock); + + do_tzset(); + + if (zi) { + size_t alt, i = scan_trans(t, local, &alt); + if (i != -1) { + *isdst = types[6*i+4]; + *offset = (int32_t)zi_read32(types+6*i); + *zonename = (const char *)abbrevs + types[6*i+5]; + if (oppoff) *oppoff = (int32_t)zi_read32(types+6*alt); + UNLOCK(lock); + return; + } + } + + if (!__daylight) goto std; + + /* FIXME: may be broken if DST changes right at year boundary? + * Also, this could be more efficient.*/ + long long y = t / 31556952 + 70; + while (__year_to_secs(y, 0) > t) y--; + while (__year_to_secs(y+1, 0) < t) y++; + + long long t0 = rule_to_secs(r0, y); + long long t1 = rule_to_secs(r1, y); + + if (!local) { + t0 += __timezone; + t1 += dst_off; + } + if (t0 < t1) { + if (t >= t0 && t < t1) goto dst; + goto std; + } else { + if (t >= t1 && t < t0) goto std; + goto dst; + } +std: + *isdst = 0; + *offset = -__timezone; + if (oppoff) *oppoff = -dst_off; + *zonename = __tzname[0]; + UNLOCK(lock); + return; +dst: + *isdst = 1; + *offset = -dst_off; + if (oppoff) *oppoff = -__timezone; + *zonename = __tzname[1]; + UNLOCK(lock); +} + +static void __tzset() +{ + LOCK(lock); + do_tzset(); + UNLOCK(lock); +} + +weak_alias(__tzset, tzset); +#else +void __secs_to_zone(long long t, int local, int *isdst, int *offset, long *oppoff, const char **zonename) +{ + // Minimalist implementation for now. + *isdst = 0; + *offset = 0; + *oppoff = 0; + *zonename = __utc; +} +#endif + +const char *__tm_to_tzname(const struct tm *tm) +{ + const void *p = tm->__tm_zone; +#ifdef __wasilibc_unmodified_upstream // timezone data + LOCK(lock); + do_tzset(); + if (p != __utc && p != __tzname[0] && p != __tzname[1] && + (!zi || (uintptr_t)p-(uintptr_t)abbrevs >= abbrevs_end - abbrevs)) + p = ""; + UNLOCK(lock); +#endif + return p; +} diff --git a/libc-top-half/musl/src/time/__year_to_secs.c b/libc-top-half/musl/src/time/__year_to_secs.c new file mode 100644 index 0000000..2824ec6 --- /dev/null +++ b/libc-top-half/musl/src/time/__year_to_secs.c @@ -0,0 +1,47 @@ +long long __year_to_secs(long long year, int *is_leap) +{ + if (year-2ULL <= 136) { + int y = year; + int leaps = (y-68)>>2; + if (!((y-68)&3)) { + leaps--; + if (is_leap) *is_leap = 1; + } else if (is_leap) *is_leap = 0; + return 31536000*(y-70) + 86400*leaps; + } + + int cycles, centuries, leaps, rem; + + if (!is_leap) is_leap = &(int){0}; + cycles = (year-100) / 400; + rem = (year-100) % 400; + if (rem < 0) { + cycles--; + rem += 400; + } + if (!rem) { + *is_leap = 1; + centuries = 0; + leaps = 0; + } else { + if (rem >= 200) { + if (rem >= 300) centuries = 3, rem -= 300; + else centuries = 2, rem -= 200; + } else { + if (rem >= 100) centuries = 1, rem -= 100; + else centuries = 0; + } + if (!rem) { + *is_leap = 0; + leaps = 0; + } else { + leaps = rem / 4U; + rem %= 4U; + *is_leap = !rem; + } + } + + leaps += 97*cycles + 24*centuries - *is_leap; + + return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400; +} diff --git a/libc-top-half/musl/src/time/asctime.c b/libc-top-half/musl/src/time/asctime.c new file mode 100644 index 0000000..1febe54 --- /dev/null +++ b/libc-top-half/musl/src/time/asctime.c @@ -0,0 +1,7 @@ +#include <time.h> + +char *asctime(const struct tm *tm) +{ + static char buf[26]; + return __asctime_r(tm, buf); +} diff --git a/libc-top-half/musl/src/time/asctime_r.c b/libc-top-half/musl/src/time/asctime_r.c new file mode 100644 index 0000000..26809ca --- /dev/null +++ b/libc-top-half/musl/src/time/asctime_r.c @@ -0,0 +1,28 @@ +#include <time.h> +#include <stdio.h> +#include <langinfo.h> +#include "locale_impl.h" +#include "atomic.h" + +char *__asctime_r(const struct tm *restrict tm, char *restrict buf) +{ + if (snprintf(buf, 26, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", + __nl_langinfo_l(ABDAY_1+tm->tm_wday, C_LOCALE), + __nl_langinfo_l(ABMON_1+tm->tm_mon, C_LOCALE), + tm->tm_mday, tm->tm_hour, + tm->tm_min, tm->tm_sec, + 1900 + tm->tm_year) >= 26) + { + /* ISO C requires us to use the above format string, + * even if it will not fit in the buffer. Thus asctime_r + * is _supposed_ to crash if the fields in tm are too large. + * We follow this behavior and crash "gracefully" to warn + * application developers that they may not be so lucky + * on other implementations (e.g. stack smashing..). + */ + a_crash(); + } + return buf; +} + +weak_alias(__asctime_r, asctime_r); diff --git a/libc-top-half/musl/src/time/clock.c b/libc-top-half/musl/src/time/clock.c new file mode 100644 index 0000000..6724012 --- /dev/null +++ b/libc-top-half/musl/src/time/clock.c @@ -0,0 +1,16 @@ +#include <time.h> +#include <limits.h> + +clock_t clock() +{ + struct timespec ts; + + if (__clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) + return -1; + + if (ts.tv_sec > LONG_MAX/1000000 + || ts.tv_nsec/1000 > LONG_MAX-1000000*ts.tv_sec) + return -1; + + return ts.tv_sec*1000000 + ts.tv_nsec/1000; +} diff --git a/libc-top-half/musl/src/time/clock_getcpuclockid.c b/libc-top-half/musl/src/time/clock_getcpuclockid.c new file mode 100644 index 0000000..8a0e2d4 --- /dev/null +++ b/libc-top-half/musl/src/time/clock_getcpuclockid.c @@ -0,0 +1,14 @@ +#include <time.h> +#include <errno.h> +#include <unistd.h> +#include "syscall.h" + +int clock_getcpuclockid(pid_t pid, clockid_t *clk) +{ + struct timespec ts; + clockid_t id = (-pid-1)*8U + 2; + int ret = __syscall(SYS_clock_getres, id, &ts); + if (ret) return -ret; + *clk = id; + return 0; +} diff --git a/libc-top-half/musl/src/time/clock_getres.c b/libc-top-half/musl/src/time/clock_getres.c new file mode 100644 index 0000000..81c6703 --- /dev/null +++ b/libc-top-half/musl/src/time/clock_getres.c @@ -0,0 +1,21 @@ +#include <time.h> +#include "syscall.h" + +int clock_getres(clockid_t clk, struct timespec *ts) +{ +#ifdef SYS_clock_getres_time64 + /* On a 32-bit arch, use the old syscall if it exists. */ + if (SYS_clock_getres != SYS_clock_getres_time64) { + long ts32[2]; + int r = __syscall(SYS_clock_getres, clk, ts32); + if (!r && ts) { + ts->tv_sec = ts32[0]; + ts->tv_nsec = ts32[1]; + } + return __syscall_ret(r); + } +#endif + /* If reaching this point, it's a 64-bit arch or time64-only + * 32-bit arch and we can get result directly into timespec. */ + return syscall(SYS_clock_getres, clk, ts); +} diff --git a/libc-top-half/musl/src/time/clock_gettime.c b/libc-top-half/musl/src/time/clock_gettime.c new file mode 100644 index 0000000..3e1d097 --- /dev/null +++ b/libc-top-half/musl/src/time/clock_gettime.c @@ -0,0 +1,107 @@ +#include <time.h> +#include <errno.h> +#include <stdint.h> +#include "syscall.h" +#include "atomic.h" + +#ifdef VDSO_CGT_SYM + +static void *volatile vdso_func; + +#ifdef VDSO_CGT32_SYM +static void *volatile vdso_func_32; +static int cgt_time32_wrap(clockid_t clk, struct timespec *ts) +{ + long ts32[2]; + int (*f)(clockid_t, long[2]) = + (int (*)(clockid_t, long[2]))vdso_func_32; + int r = f(clk, ts32); + if (!r) { + /* Fallback to syscalls if time32 overflowed. Maybe + * we lucked out and somehow migrated to a kernel with + * time64 syscalls available. */ + if (ts32[0] < 0) { + a_cas_p(&vdso_func, (void *)cgt_time32_wrap, 0); + return -ENOSYS; + } + ts->tv_sec = ts32[0]; + ts->tv_nsec = ts32[1]; + } + return r; +} +#endif + +static int cgt_init(clockid_t clk, struct timespec *ts) +{ + void *p = __vdsosym(VDSO_CGT_VER, VDSO_CGT_SYM); +#ifdef VDSO_CGT32_SYM + if (!p) { + void *q = __vdsosym(VDSO_CGT32_VER, VDSO_CGT32_SYM); + if (q) { + a_cas_p(&vdso_func_32, 0, q); + p = cgt_time32_wrap; + } + } +#endif + int (*f)(clockid_t, struct timespec *) = + (int (*)(clockid_t, struct timespec *))p; + a_cas_p(&vdso_func, (void *)cgt_init, p); + return f ? f(clk, ts) : -ENOSYS; +} + +static void *volatile vdso_func = (void *)cgt_init; + +#endif + +int __clock_gettime(clockid_t clk, struct timespec *ts) +{ + int r; + +#ifdef VDSO_CGT_SYM + int (*f)(clockid_t, struct timespec *) = + (int (*)(clockid_t, struct timespec *))vdso_func; + if (f) { + r = f(clk, ts); + if (!r) return r; + if (r == -EINVAL) return __syscall_ret(r); + /* Fall through on errors other than EINVAL. Some buggy + * vdso implementations return ENOSYS for clocks they + * can't handle, rather than making the syscall. This + * also handles the case where cgt_init fails to find + * a vdso function to use. */ + } +#endif + +#ifdef SYS_clock_gettime64 + r = -ENOSYS; + if (sizeof(time_t) > 4) + r = __syscall(SYS_clock_gettime64, clk, ts); + if (SYS_clock_gettime == SYS_clock_gettime64 || r!=-ENOSYS) + return __syscall_ret(r); + long ts32[2]; + r = __syscall(SYS_clock_gettime, clk, ts32); + if (r==-ENOSYS && clk==CLOCK_REALTIME) { + r = __syscall(SYS_gettimeofday, ts32, 0); + ts32[1] *= 1000; + } + if (!r) { + ts->tv_sec = ts32[0]; + ts->tv_nsec = ts32[1]; + return r; + } + return __syscall_ret(r); +#else + r = __syscall(SYS_clock_gettime, clk, ts); + if (r == -ENOSYS) { + if (clk == CLOCK_REALTIME) { + __syscall(SYS_gettimeofday, ts, 0); + ts->tv_nsec = (int)ts->tv_nsec * 1000; + return 0; + } + r = -EINVAL; + } + return __syscall_ret(r); +#endif +} + +weak_alias(__clock_gettime, clock_gettime); diff --git a/libc-top-half/musl/src/time/clock_nanosleep.c b/libc-top-half/musl/src/time/clock_nanosleep.c new file mode 100644 index 0000000..e195499 --- /dev/null +++ b/libc-top-half/musl/src/time/clock_nanosleep.c @@ -0,0 +1,38 @@ +#include <time.h> +#include <errno.h> +#include "syscall.h" + +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + +int __clock_nanosleep(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem) +{ + if (clk == CLOCK_THREAD_CPUTIME_ID) return EINVAL; +#ifdef SYS_clock_nanosleep_time64 + time_t s = req->tv_sec; + long ns = req->tv_nsec; + int r = -ENOSYS; + if (SYS_clock_nanosleep == SYS_clock_nanosleep_time64 || !IS32BIT(s)) + r = __syscall_cp(SYS_clock_nanosleep_time64, clk, flags, + ((long long[]){s, ns}), rem); + if (SYS_clock_nanosleep == SYS_clock_nanosleep_time64 || r!=-ENOSYS) + return -r; + long long extra = s - CLAMP(s); + long ts32[2] = { CLAMP(s), ns }; + if (clk == CLOCK_REALTIME && !flags) + r = __syscall_cp(SYS_nanosleep, &ts32, &ts32); + else + r = __syscall_cp(SYS_clock_nanosleep, clk, flags, &ts32, &ts32); + if (r==-EINTR && rem && !(flags & TIMER_ABSTIME)) { + rem->tv_sec = ts32[0] + extra; + rem->tv_nsec = ts32[1]; + } + return -r; +#else + if (clk == CLOCK_REALTIME && !flags) + return -__syscall_cp(SYS_nanosleep, req, rem); + return -__syscall_cp(SYS_clock_nanosleep, clk, flags, req, rem); +#endif +} + +weak_alias(__clock_nanosleep, clock_nanosleep); diff --git a/libc-top-half/musl/src/time/clock_settime.c b/libc-top-half/musl/src/time/clock_settime.c new file mode 100644 index 0000000..1004ed1 --- /dev/null +++ b/libc-top-half/musl/src/time/clock_settime.c @@ -0,0 +1,24 @@ +#include <time.h> +#include <errno.h> +#include "syscall.h" + +#define IS32BIT(x) !((x)+0x80000000ULL>>32) + +int clock_settime(clockid_t clk, const struct timespec *ts) +{ +#ifdef SYS_clock_settime64 + time_t s = ts->tv_sec; + long ns = ts->tv_nsec; + int r = -ENOSYS; + if (SYS_clock_settime == SYS_clock_settime64 || !IS32BIT(s)) + r = __syscall(SYS_clock_settime64, clk, + ((long long[]){s, ns})); + if (SYS_clock_settime == SYS_clock_settime64 || r!=-ENOSYS) + return __syscall_ret(r); + if (!IS32BIT(s)) + return __syscall_ret(-ENOTSUP); + return syscall(SYS_clock_settime, clk, ((long[]){s, ns})); +#else + return syscall(SYS_clock_settime, clk, ts); +#endif +} diff --git a/libc-top-half/musl/src/time/ctime.c b/libc-top-half/musl/src/time/ctime.c new file mode 100644 index 0000000..3602931 --- /dev/null +++ b/libc-top-half/musl/src/time/ctime.c @@ -0,0 +1,8 @@ +#include <time.h> + +char *ctime(const time_t *t) +{ + struct tm *tm = localtime(t); + if (!tm) return 0; + return asctime(tm); +} diff --git a/libc-top-half/musl/src/time/ctime_r.c b/libc-top-half/musl/src/time/ctime_r.c new file mode 100644 index 0000000..3e24aa6 --- /dev/null +++ b/libc-top-half/musl/src/time/ctime_r.c @@ -0,0 +1,7 @@ +#include <time.h> + +char *ctime_r(const time_t *t, char *buf) +{ + struct tm tm, *tm_p = localtime_r(t, &tm); + return tm_p ? asctime_r(tm_p, buf) : 0; +} diff --git a/libc-top-half/musl/src/time/difftime.c b/libc-top-half/musl/src/time/difftime.c new file mode 100644 index 0000000..80a18cc --- /dev/null +++ b/libc-top-half/musl/src/time/difftime.c @@ -0,0 +1,6 @@ +#include <time.h> + +double difftime(time_t t1, time_t t0) +{ + return t1-t0; +} diff --git a/libc-top-half/musl/src/time/ftime.c b/libc-top-half/musl/src/time/ftime.c new file mode 100644 index 0000000..a1734d0 --- /dev/null +++ b/libc-top-half/musl/src/time/ftime.c @@ -0,0 +1,12 @@ +#include <sys/timeb.h> +#include <time.h> + +int ftime(struct timeb *tp) +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + tp->time = ts.tv_sec; + tp->millitm = ts.tv_nsec / 1000000; + tp->timezone = tp->dstflag = 0; + return 0; +} diff --git a/libc-top-half/musl/src/time/getdate.c b/libc-top-half/musl/src/time/getdate.c new file mode 100644 index 0000000..058c48b --- /dev/null +++ b/libc-top-half/musl/src/time/getdate.c @@ -0,0 +1,52 @@ +#include <time.h> +#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) +#include <pthread.h> +#endif +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +int getdate_err; + +struct tm *getdate(const char *s) +{ + static struct tm tmbuf; + struct tm *ret = 0; + char *datemsk = getenv("DATEMSK"); + FILE *f = 0; + char fmt[100], *p; + int cs; + +#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) + pthread_setcancelstate(PTHREAD_CANCEL_DEFERRED, &cs); +#endif + + if (!datemsk) { + getdate_err = 1; + goto out; + } + + f = fopen(datemsk, "rbe"); + if (!f) { + if (errno == ENOMEM) getdate_err = 6; + else getdate_err = 2; + goto out; + } + + while (fgets(fmt, sizeof fmt, f)) { + p = strptime(s, fmt, &tmbuf); + if (p && !*p) { + ret = &tmbuf; + goto out; + } + } + + if (ferror(f)) getdate_err = 5; + else getdate_err = 7; +out: + if (f) fclose(f); +#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) + pthread_setcancelstate(cs, 0); +#endif + return ret; +} diff --git a/libc-top-half/musl/src/time/gettimeofday.c b/libc-top-half/musl/src/time/gettimeofday.c new file mode 100644 index 0000000..691f8e9 --- /dev/null +++ b/libc-top-half/musl/src/time/gettimeofday.c @@ -0,0 +1,13 @@ +#include <time.h> +#include <sys/time.h> +#include "syscall.h" + +int gettimeofday(struct timeval *restrict tv, void *restrict tz) +{ + struct timespec ts; + if (!tv) return 0; + clock_gettime(CLOCK_REALTIME, &ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = (int)ts.tv_nsec / 1000; + return 0; +} diff --git a/libc-top-half/musl/src/time/gmtime.c b/libc-top-half/musl/src/time/gmtime.c new file mode 100644 index 0000000..6320b63 --- /dev/null +++ b/libc-top-half/musl/src/time/gmtime.c @@ -0,0 +1,8 @@ +#include "time_impl.h" +#include <errno.h> + +struct tm *gmtime(const time_t *t) +{ + static struct tm tm; + return __gmtime_r(t, &tm); +} diff --git a/libc-top-half/musl/src/time/gmtime_r.c b/libc-top-half/musl/src/time/gmtime_r.c new file mode 100644 index 0000000..22aec2c --- /dev/null +++ b/libc-top-half/musl/src/time/gmtime_r.c @@ -0,0 +1,16 @@ +#include "time_impl.h" +#include <errno.h> + +struct tm *__gmtime_r(const time_t *restrict t, struct tm *restrict tm) +{ + if (__secs_to_tm(*t, tm) < 0) { + errno = EOVERFLOW; + return 0; + } + tm->tm_isdst = 0; + tm->__tm_gmtoff = 0; + tm->__tm_zone = __utc; + return tm; +} + +weak_alias(__gmtime_r, gmtime_r); diff --git a/libc-top-half/musl/src/time/localtime.c b/libc-top-half/musl/src/time/localtime.c new file mode 100644 index 0000000..5210423 --- /dev/null +++ b/libc-top-half/musl/src/time/localtime.c @@ -0,0 +1,7 @@ +#include "time_impl.h" + +struct tm *localtime(const time_t *t) +{ + static struct tm tm; + return __localtime_r(t, &tm); +} diff --git a/libc-top-half/musl/src/time/localtime_r.c b/libc-top-half/musl/src/time/localtime_r.c new file mode 100644 index 0000000..1a15b31 --- /dev/null +++ b/libc-top-half/musl/src/time/localtime_r.c @@ -0,0 +1,21 @@ +#include "time_impl.h" +#include <errno.h> +#include <limits.h> + +struct tm *__localtime_r(const time_t *restrict t, struct tm *restrict tm) +{ + /* Reject time_t values whose year would overflow int because + * __secs_to_zone cannot safely handle them. */ + if (*t < INT_MIN * 31622400LL || *t > INT_MAX * 31622400LL) { + errno = EOVERFLOW; + return 0; + } + __secs_to_zone(*t, 0, &tm->tm_isdst, &tm->__tm_gmtoff, 0, &tm->__tm_zone); + if (__secs_to_tm((long long)*t + tm->__tm_gmtoff, tm) < 0) { + errno = EOVERFLOW; + return 0; + } + return tm; +} + +weak_alias(__localtime_r, localtime_r); diff --git a/libc-top-half/musl/src/time/mktime.c b/libc-top-half/musl/src/time/mktime.c new file mode 100644 index 0000000..bad3f07 --- /dev/null +++ b/libc-top-half/musl/src/time/mktime.c @@ -0,0 +1,28 @@ +#include "time_impl.h" +#include <errno.h> + +time_t mktime(struct tm *tm) +{ + struct tm new; + long opp; + long long t = __tm_to_secs(tm); + + __secs_to_zone(t, 1, &new.tm_isdst, &new.__tm_gmtoff, &opp, &new.__tm_zone); + + if (tm->tm_isdst>=0 && new.tm_isdst!=tm->tm_isdst) + t -= opp - new.__tm_gmtoff; + + t -= new.__tm_gmtoff; + if ((time_t)t != t) goto error; + + __secs_to_zone(t, 0, &new.tm_isdst, &new.__tm_gmtoff, &opp, &new.__tm_zone); + + if (__secs_to_tm(t + new.__tm_gmtoff, &new) < 0) goto error; + + *tm = new; + return t; + +error: + errno = EOVERFLOW; + return -1; +} diff --git a/libc-top-half/musl/src/time/nanosleep.c b/libc-top-half/musl/src/time/nanosleep.c new file mode 100644 index 0000000..bc9f789 --- /dev/null +++ b/libc-top-half/musl/src/time/nanosleep.c @@ -0,0 +1,7 @@ +#include <time.h> +#include "syscall.h" + +int nanosleep(const struct timespec *req, struct timespec *rem) +{ + return __syscall_ret(-__clock_nanosleep(CLOCK_REALTIME, 0, req, rem)); +} diff --git a/libc-top-half/musl/src/time/strftime.c b/libc-top-half/musl/src/time/strftime.c new file mode 100644 index 0000000..a1db9cb --- /dev/null +++ b/libc-top-half/musl/src/time/strftime.c @@ -0,0 +1,285 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <langinfo.h> +#include <locale.h> +#include <time.h> +#include <limits.h> +#include "locale_impl.h" +#include "time_impl.h" + +static int is_leap(int y) +{ + /* Avoid overflow */ + if (y>INT_MAX-1900) y -= 2000; + y += 1900; + return !(y%4) && ((y%100) || !(y%400)); +} + +static int week_num(const struct tm *tm) +{ + int val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; + /* If 1 Jan is just 1-3 days past Monday, + * the previous week is also in this year. */ + if ((tm->tm_wday + 371U - tm->tm_yday - 2) % 7 <= 2) + val++; + if (!val) { + val = 52; + /* If 31 December of prev year a Thursday, + * or Friday of a leap year, then the + * prev year has 53 weeks. */ + int dec31 = (tm->tm_wday + 7U - tm->tm_yday - 1) % 7; + if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1))) + val++; + } else if (val == 53) { + /* If 1 January is not a Thursday, and not + * a Wednesday of a leap year, then this + * year has only 52 weeks. */ + int jan1 = (tm->tm_wday + 371U - tm->tm_yday) % 7; + if (jan1 != 4 && (jan1 != 3 || !is_leap(tm->tm_year))) + val = 1; + } + return val; +} + +const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc, int pad) +{ + nl_item item; + long long val; + const char *fmt = "-"; + int width = 2, def_pad = '0'; + + switch (f) { + case 'a': + if (tm->tm_wday > 6U) goto string; + item = ABDAY_1 + tm->tm_wday; + goto nl_strcat; + case 'A': + if (tm->tm_wday > 6U) goto string; + item = DAY_1 + tm->tm_wday; + goto nl_strcat; + case 'h': + case 'b': + if (tm->tm_mon > 11U) goto string; + item = ABMON_1 + tm->tm_mon; + goto nl_strcat; + case 'B': + if (tm->tm_mon > 11U) goto string; + item = MON_1 + tm->tm_mon; + goto nl_strcat; + case 'c': + item = D_T_FMT; + goto nl_strftime; + case 'C': + val = (1900LL+tm->tm_year) / 100; + goto number; + case 'e': + def_pad = '_'; + case 'd': + val = tm->tm_mday; + goto number; + case 'D': + fmt = "%m/%d/%y"; + goto recu_strftime; + case 'F': + fmt = "%Y-%m-%d"; + goto recu_strftime; + case 'g': + case 'G': + val = tm->tm_year + 1900LL; + if (tm->tm_yday < 3 && week_num(tm) != 1) val--; + else if (tm->tm_yday > 360 && week_num(tm) == 1) val++; + if (f=='g') val %= 100; + else width = 4; + goto number; + case 'H': + val = tm->tm_hour; + goto number; + case 'I': + val = tm->tm_hour; + if (!val) val = 12; + else if (val > 12) val -= 12; + goto number; + case 'j': + val = tm->tm_yday+1; + width = 3; + goto number; + case 'm': + val = tm->tm_mon+1; + goto number; + case 'M': + val = tm->tm_min; + goto number; + case 'n': + *l = 1; + return "\n"; + case 'p': + item = tm->tm_hour >= 12 ? PM_STR : AM_STR; + goto nl_strcat; + case 'r': + item = T_FMT_AMPM; + goto nl_strftime; + case 'R': + fmt = "%H:%M"; + goto recu_strftime; + case 's': + val = __tm_to_secs(tm) - tm->__tm_gmtoff; + width = 1; + goto number; + case 'S': + val = tm->tm_sec; + goto number; + case 't': + *l = 1; + return "\t"; + case 'T': + fmt = "%H:%M:%S"; + goto recu_strftime; + case 'u': + val = tm->tm_wday ? tm->tm_wday : 7; + width = 1; + goto number; + case 'U': + val = (tm->tm_yday + 7U - tm->tm_wday) / 7; + goto number; + case 'W': + val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; + goto number; + case 'V': + val = week_num(tm); + goto number; + case 'w': + val = tm->tm_wday; + width = 1; + goto number; + case 'x': + item = D_FMT; + goto nl_strftime; + case 'X': + item = T_FMT; + goto nl_strftime; + case 'y': + val = (tm->tm_year + 1900LL) % 100; + if (val < 0) val = -val; + goto number; + case 'Y': + val = tm->tm_year + 1900LL; + if (val >= 10000) { + *l = snprintf(*s, sizeof *s, "+%lld", val); + return *s; + } + width = 4; + goto number; + case 'z': + if (tm->tm_isdst < 0) { + *l = 0; + return ""; + } +#ifdef __wasilibc_unmodified_upstream // wasi-libc's __tm_gmtoff is an int + *l = snprintf(*s, sizeof *s, "%+.4ld", +#else + *l = snprintf(*s, sizeof *s, "%+.4d", +#endif + tm->__tm_gmtoff/3600*100 + tm->__tm_gmtoff%3600/60); + return *s; + case 'Z': + if (tm->tm_isdst < 0) { + *l = 0; + return ""; + } + fmt = __tm_to_tzname(tm); + goto string; + case '%': + *l = 1; + return "%"; + default: + return 0; + } +number: + switch (pad ? pad : def_pad) { + case '-': *l = snprintf(*s, sizeof *s, "%lld", val); break; + case '_': *l = snprintf(*s, sizeof *s, "%*lld", width, val); break; + case '0': + default: *l = snprintf(*s, sizeof *s, "%0*lld", width, val); break; + } + return *s; +nl_strcat: + fmt = __nl_langinfo_l(item, loc); +string: + *l = strlen(fmt); + return fmt; +nl_strftime: + fmt = __nl_langinfo_l(item, loc); +recu_strftime: + *l = __strftime_l(*s, sizeof *s, fmt, tm, loc); + if (!*l) return 0; + return *s; +} + +size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc) +{ + size_t l, k; + char buf[100]; + char *p; + const char *t; + int pad, plus; + unsigned long width; + for (l=0; l<n; f++) { + if (!*f) { + s[l] = 0; + return l; + } + if (*f != '%') { + s[l++] = *f; + continue; + } + f++; + pad = 0; + if (*f == '-' || *f == '_' || *f == '0') pad = *f++; + if ((plus = (*f == '+'))) f++; + width = strtoul(f, &p, 10); + if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') { + if (!width && p!=f) width = 1; + } else { + width = 0; + } + f = p; + if (*f == 'E' || *f == 'O') f++; + t = __strftime_fmt_1(&buf, &k, *f, tm, loc, pad); + if (!t) break; + if (width) { + /* Trim off any sign and leading zeros, then + * count remaining digits to determine behavior + * for the + flag. */ + if (*t=='+' || *t=='-') t++, k--; + for (; *t=='0' && t[1]-'0'<10U; t++, k--); + if (width < k) width = k; + size_t d; + for (d=0; t[d]-'0'<10U; d++); + if (tm->tm_year < -1900) { + s[l++] = '-'; + width--; + } else if (plus && d+(width-k) >= (*p=='C'?3:5)) { + s[l++] = '+'; + width--; + } + for (; width > k && l < n; width--) + s[l++] = '0'; + } + if (k > n-l) k = n-l; + memcpy(s+l, t, k); + l += k; + } + if (n) { + if (l==n) l=n-1; + s[l] = 0; + } + return 0; +} + +size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm) +{ + return __strftime_l(s, n, f, tm, CURRENT_LOCALE); +} + +weak_alias(__strftime_l, strftime_l); diff --git a/libc-top-half/musl/src/time/strptime.c b/libc-top-half/musl/src/time/strptime.c new file mode 100644 index 0000000..c54a0d8 --- /dev/null +++ b/libc-top-half/musl/src/time/strptime.c @@ -0,0 +1,206 @@ +#include <stdlib.h> +#include <langinfo.h> +#include <time.h> +#include <ctype.h> +#include <stddef.h> +#include <string.h> +#include <strings.h> + +char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm) +{ + int i, w, neg, adj, min, range, *dest, dummy; + const char *ex; + size_t len; + int want_century = 0, century = 0, relyear = 0; + while (*f) { + if (*f != '%') { + if (isspace(*f)) for (; *s && isspace(*s); s++); + else if (*s != *f) return 0; + else s++; + f++; + continue; + } + f++; + if (*f == '+') f++; + if (isdigit(*f)) { + char *new_f; + w=strtoul(f, &new_f, 10); + f = new_f; + } else { + w=-1; + } + adj=0; + switch (*f++) { + case 'a': case 'A': + dest = &tm->tm_wday; + min = ABDAY_1; + range = 7; + goto symbolic_range; + case 'b': case 'B': case 'h': + dest = &tm->tm_mon; + min = ABMON_1; + range = 12; + goto symbolic_range; + case 'c': + s = strptime(s, nl_langinfo(D_T_FMT), tm); + if (!s) return 0; + break; + case 'C': + dest = ¢ury; + if (w<0) w=2; + want_century |= 2; + goto numeric_digits; + case 'd': case 'e': + dest = &tm->tm_mday; + min = 1; + range = 31; + goto numeric_range; + case 'D': + s = strptime(s, "%m/%d/%y", tm); + if (!s) return 0; + break; + case 'H': + dest = &tm->tm_hour; + min = 0; + range = 24; + goto numeric_range; + case 'I': + dest = &tm->tm_hour; + min = 1; + range = 12; + goto numeric_range; + case 'j': + dest = &tm->tm_yday; + min = 1; + range = 366; + adj = 1; + goto numeric_range; + case 'm': + dest = &tm->tm_mon; + min = 1; + range = 12; + adj = 1; + goto numeric_range; + case 'M': + dest = &tm->tm_min; + min = 0; + range = 60; + goto numeric_range; + case 'n': case 't': + for (; *s && isspace(*s); s++); + break; + case 'p': + ex = nl_langinfo(AM_STR); + len = strlen(ex); + if (!strncasecmp(s, ex, len)) { + tm->tm_hour %= 12; + s += len; + break; + } + ex = nl_langinfo(PM_STR); + len = strlen(ex); + if (!strncasecmp(s, ex, len)) { + tm->tm_hour %= 12; + tm->tm_hour += 12; + s += len; + break; + } + return 0; + case 'r': + s = strptime(s, nl_langinfo(T_FMT_AMPM), tm); + if (!s) return 0; + break; + case 'R': + s = strptime(s, "%H:%M", tm); + if (!s) return 0; + break; + case 'S': + dest = &tm->tm_sec; + min = 0; + range = 61; + goto numeric_range; + case 'T': + s = strptime(s, "%H:%M:%S", tm); + if (!s) return 0; + break; + case 'U': + case 'W': + /* Throw away result, for now. (FIXME?) */ + dest = &dummy; + min = 0; + range = 54; + goto numeric_range; + case 'w': + dest = &tm->tm_wday; + min = 0; + range = 7; + goto numeric_range; + case 'x': + s = strptime(s, nl_langinfo(D_FMT), tm); + if (!s) return 0; + break; + case 'X': + s = strptime(s, nl_langinfo(T_FMT), tm); + if (!s) return 0; + break; + case 'y': + dest = &relyear; + w = 2; + want_century |= 1; + goto numeric_digits; + case 'Y': + dest = &tm->tm_year; + if (w<0) w=4; + adj = 1900; + want_century = 0; + goto numeric_digits; + case '%': + if (*s++ != '%') return 0; + break; + default: + return 0; + numeric_range: + if (!isdigit(*s)) return 0; + *dest = 0; + for (i=1; i<=min+range && isdigit(*s); i*=10) + *dest = *dest * 10 + *s++ - '0'; + if (*dest - min >= (unsigned)range) return 0; + *dest -= adj; + switch((char *)dest - (char *)tm) { + case offsetof(struct tm, tm_yday): + ; + } + goto update; + numeric_digits: + neg = 0; + if (*s == '+') s++; + else if (*s == '-') neg=1, s++; + if (!isdigit(*s)) return 0; + for (*dest=i=0; i<w && isdigit(*s); i++) + *dest = *dest * 10 + *s++ - '0'; + if (neg) *dest = -*dest; + *dest -= adj; + goto update; + symbolic_range: + for (i=2*range-1; i>=0; i--) { + ex = nl_langinfo(min+i); + len = strlen(ex); + if (strncasecmp(s, ex, len)) continue; + s += len; + *dest = i % range; + break; + } + if (i<0) return 0; + goto update; + update: + //FIXME + ; + } + } + if (want_century) { + tm->tm_year = relyear; + if (want_century & 2) tm->tm_year += century * 100 - 1900; + else if (tm->tm_year <= 68) tm->tm_year += 100; + } + return (char *)s; +} diff --git a/libc-top-half/musl/src/time/time.c b/libc-top-half/musl/src/time/time.c new file mode 100644 index 0000000..ad0480f --- /dev/null +++ b/libc-top-half/musl/src/time/time.c @@ -0,0 +1,10 @@ +#include <time.h> +#include "syscall.h" + +time_t time(time_t *t) +{ + struct timespec ts; + __clock_gettime(CLOCK_REALTIME, &ts); + if (t) *t = ts.tv_sec; + return ts.tv_sec; +} diff --git a/libc-top-half/musl/src/time/time_impl.h b/libc-top-half/musl/src/time/time_impl.h new file mode 100644 index 0000000..1a378c2 --- /dev/null +++ b/libc-top-half/musl/src/time/time_impl.h @@ -0,0 +1,15 @@ +#include <time.h> + +hidden int __days_in_month(int, int); +hidden int __month_to_secs(int, int); +hidden long long __year_to_secs(long long, int *); +hidden long long __tm_to_secs(const struct tm *); +hidden const char *__tm_to_tzname(const struct tm *); +hidden int __secs_to_tm(long long, struct tm *); +#ifdef __wasilibc_unmodified_upstream // type of __tm_gmtoff +hidden void __secs_to_zone(long long, int, int *, long *, long *, const char **); +#else +hidden void __secs_to_zone(long long, int, int *, int *, long *, const char **); +#endif +hidden const char *__strftime_fmt_1(char (*)[100], size_t *, int, const struct tm *, locale_t, int); +extern hidden const char __utc[]; diff --git a/libc-top-half/musl/src/time/timegm.c b/libc-top-half/musl/src/time/timegm.c new file mode 100644 index 0000000..4e5907d --- /dev/null +++ b/libc-top-half/musl/src/time/timegm.c @@ -0,0 +1,18 @@ +#define _GNU_SOURCE +#include "time_impl.h" +#include <errno.h> + +time_t timegm(struct tm *tm) +{ + struct tm new; + long long t = __tm_to_secs(tm); + if (__secs_to_tm(t, &new) < 0) { + errno = EOVERFLOW; + return -1; + } + *tm = new; + tm->tm_isdst = 0; + tm->__tm_gmtoff = 0; + tm->__tm_zone = __utc; + return t; +} diff --git a/libc-top-half/musl/src/time/timer_create.c b/libc-top-half/musl/src/time/timer_create.c new file mode 100644 index 0000000..4bef239 --- /dev/null +++ b/libc-top-half/musl/src/time/timer_create.c @@ -0,0 +1,129 @@ +#include <time.h> +#include <setjmp.h> +#include <limits.h> +#include "pthread_impl.h" +#include "atomic.h" + +struct ksigevent { + union sigval sigev_value; + int sigev_signo; + int sigev_notify; + int sigev_tid; +}; + +struct start_args { + pthread_barrier_t b; + struct sigevent *sev; +}; + +static void dummy_0() +{ +} +weak_alias(dummy_0, __pthread_tsd_run_dtors); + +static void cleanup_fromsig(void *p) +{ + pthread_t self = __pthread_self(); + __pthread_tsd_run_dtors(); + self->cancel = 0; + self->cancelbuf = 0; + self->canceldisable = 0; + self->cancelasync = 0; + __reset_tls(); + longjmp(p, 1); +} + +static void *start(void *arg) +{ + pthread_t self = __pthread_self(); + struct start_args *args = arg; + jmp_buf jb; + + void (*notify)(union sigval) = args->sev->sigev_notify_function; + union sigval val = args->sev->sigev_value; + + pthread_barrier_wait(&args->b); + for (;;) { + siginfo_t si; + while (sigwaitinfo(SIGTIMER_SET, &si) < 0); + if (si.si_code == SI_TIMER && !setjmp(jb)) { + pthread_cleanup_push(cleanup_fromsig, jb); + notify(val); + pthread_cleanup_pop(1); + } + if (self->timer_id < 0) break; + } + __syscall(SYS_timer_delete, self->timer_id & INT_MAX); + return 0; +} + +int timer_create(clockid_t clk, struct sigevent *restrict evp, timer_t *restrict res) +{ + volatile static int init = 0; + pthread_t td; + pthread_attr_t attr; + int r; + struct start_args args; + struct ksigevent ksev, *ksevp=0; + int timerid; + sigset_t set; + + switch (evp ? evp->sigev_notify : SIGEV_SIGNAL) { + case SIGEV_NONE: + case SIGEV_SIGNAL: + case SIGEV_THREAD_ID: + if (evp) { + ksev.sigev_value = evp->sigev_value; + ksev.sigev_signo = evp->sigev_signo; + ksev.sigev_notify = evp->sigev_notify; + if (evp->sigev_notify == SIGEV_THREAD_ID) + ksev.sigev_tid = evp->sigev_notify_thread_id; + else + ksev.sigev_tid = 0; + ksevp = &ksev; + } + if (syscall(SYS_timer_create, clk, ksevp, &timerid) < 0) + return -1; + *res = (void *)(intptr_t)timerid; + break; + case SIGEV_THREAD: + if (!init) { + struct sigaction sa = { .sa_handler = SIG_DFL }; + __libc_sigaction(SIGTIMER, &sa, 0); + a_store(&init, 1); + } + if (evp->sigev_notify_attributes) + attr = *evp->sigev_notify_attributes; + else + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_barrier_init(&args.b, 0, 2); + args.sev = evp; + + __block_app_sigs(&set); + __syscall(SYS_rt_sigprocmask, SIG_BLOCK, SIGTIMER_SET, 0, _NSIG/8); + r = pthread_create(&td, &attr, start, &args); + __restore_sigs(&set); + if (r) { + errno = r; + return -1; + } + + ksev.sigev_value.sival_ptr = 0; + ksev.sigev_signo = SIGTIMER; + ksev.sigev_notify = SIGEV_THREAD_ID; + ksev.sigev_tid = td->tid; + if (syscall(SYS_timer_create, clk, &ksev, &timerid) < 0) + timerid = -1; + td->timer_id = timerid; + pthread_barrier_wait(&args.b); + if (timerid < 0) return -1; + *res = (void *)(INTPTR_MIN | (uintptr_t)td>>1); + break; + default: + errno = EINVAL; + return -1; + } + + return 0; +} diff --git a/libc-top-half/musl/src/time/timer_delete.c b/libc-top-half/musl/src/time/timer_delete.c new file mode 100644 index 0000000..b0bfac0 --- /dev/null +++ b/libc-top-half/musl/src/time/timer_delete.c @@ -0,0 +1,14 @@ +#include <time.h> +#include <limits.h> +#include "pthread_impl.h" + +int timer_delete(timer_t t) +{ + if ((intptr_t)t < 0) { + pthread_t td = (void *)((uintptr_t)t << 1); + a_store(&td->timer_id, td->timer_id | INT_MIN); + __syscall(SYS_tkill, td->tid, SIGTIMER); + return 0; + } + return __syscall(SYS_timer_delete, t); +} diff --git a/libc-top-half/musl/src/time/timer_getoverrun.c b/libc-top-half/musl/src/time/timer_getoverrun.c new file mode 100644 index 0000000..e7f891e --- /dev/null +++ b/libc-top-half/musl/src/time/timer_getoverrun.c @@ -0,0 +1,12 @@ +#include <time.h> +#include <limits.h> +#include "pthread_impl.h" + +int timer_getoverrun(timer_t t) +{ + if ((intptr_t)t < 0) { + pthread_t td = (void *)((uintptr_t)t << 1); + t = (void *)(uintptr_t)(td->timer_id & INT_MAX); + } + return syscall(SYS_timer_getoverrun, t); +} diff --git a/libc-top-half/musl/src/time/timer_gettime.c b/libc-top-half/musl/src/time/timer_gettime.c new file mode 100644 index 0000000..21c9d32 --- /dev/null +++ b/libc-top-half/musl/src/time/timer_gettime.c @@ -0,0 +1,28 @@ +#include <time.h> +#include <limits.h> +#include "pthread_impl.h" + +int timer_gettime(timer_t t, struct itimerspec *val) +{ + if ((intptr_t)t < 0) { + pthread_t td = (void *)((uintptr_t)t << 1); + t = (void *)(uintptr_t)(td->timer_id & INT_MAX); + } +#ifdef SYS_timer_gettime64 + int r = -ENOSYS; + if (sizeof(time_t) > 4) + r = __syscall(SYS_timer_gettime64, t, val); + if (SYS_timer_gettime == SYS_timer_gettime64 || r!=-ENOSYS) + return __syscall_ret(r); + long val32[4]; + r = __syscall(SYS_timer_gettime, t, val32); + if (!r) { + val->it_interval.tv_sec = val32[0]; + val->it_interval.tv_nsec = val32[1]; + val->it_value.tv_sec = val32[2]; + val->it_value.tv_nsec = val32[3]; + } + return __syscall_ret(r); +#endif + return syscall(SYS_timer_gettime, t, val); +} diff --git a/libc-top-half/musl/src/time/timer_settime.c b/libc-top-half/musl/src/time/timer_settime.c new file mode 100644 index 0000000..373f00c --- /dev/null +++ b/libc-top-half/musl/src/time/timer_settime.c @@ -0,0 +1,37 @@ +#include <time.h> +#include <limits.h> +#include "pthread_impl.h" + +#define IS32BIT(x) !((x)+0x80000000ULL>>32) + +int timer_settime(timer_t t, int flags, const struct itimerspec *restrict val, struct itimerspec *restrict old) +{ + if ((intptr_t)t < 0) { + pthread_t td = (void *)((uintptr_t)t << 1); + t = (void *)(uintptr_t)(td->timer_id & INT_MAX); + } +#ifdef SYS_timer_settime64 + time_t is = val->it_interval.tv_sec, vs = val->it_value.tv_sec; + long ins = val->it_interval.tv_nsec, vns = val->it_value.tv_nsec; + int r = -ENOSYS; + if (SYS_timer_settime == SYS_timer_settime64 + || !IS32BIT(is) || !IS32BIT(vs) || (sizeof(time_t)>4 && old)) + r = __syscall(SYS_timer_settime64, t, flags, + ((long long[]){is, ins, vs, vns}), old); + if (SYS_timer_settime == SYS_timer_settime64 || r!=-ENOSYS) + return __syscall_ret(r); + if (!IS32BIT(is) || !IS32BIT(vs)) + return __syscall_ret(-ENOTSUP); + long old32[4]; + r = __syscall(SYS_timer_settime, t, flags, + ((long[]){is, ins, vs, vns}), old32); + if (!r && old) { + old->it_interval.tv_sec = old32[0]; + old->it_interval.tv_nsec = old32[1]; + old->it_value.tv_sec = old32[2]; + old->it_value.tv_nsec = old32[3]; + } + return __syscall_ret(r); +#endif + return syscall(SYS_timer_settime, t, flags, val, old); +} diff --git a/libc-top-half/musl/src/time/times.c b/libc-top-half/musl/src/time/times.c new file mode 100644 index 0000000..c4a100f --- /dev/null +++ b/libc-top-half/musl/src/time/times.c @@ -0,0 +1,7 @@ +#include <sys/times.h> +#include "syscall.h" + +clock_t times(struct tms *tms) +{ + return __syscall(SYS_times, tms); +} diff --git a/libc-top-half/musl/src/time/timespec_get.c b/libc-top-half/musl/src/time/timespec_get.c new file mode 100644 index 0000000..40ea9c1 --- /dev/null +++ b/libc-top-half/musl/src/time/timespec_get.c @@ -0,0 +1,10 @@ +#include <time.h> + +/* There is no other implemented value than TIME_UTC; all other values + * are considered erroneous. */ +int timespec_get(struct timespec * ts, int base) +{ + if (base != TIME_UTC) return 0; + int ret = __clock_gettime(CLOCK_REALTIME, ts); + return ret < 0 ? 0 : base; +} diff --git a/libc-top-half/musl/src/time/utime.c b/libc-top-half/musl/src/time/utime.c new file mode 100644 index 0000000..e7592b2 --- /dev/null +++ b/libc-top-half/musl/src/time/utime.c @@ -0,0 +1,11 @@ +#include <utime.h> +#include <sys/stat.h> +#include <time.h> +#include <fcntl.h> + +int utime(const char *path, const struct utimbuf *times) +{ + return utimensat(AT_FDCWD, path, times ? ((struct timespec [2]){ + { .tv_sec = times->actime }, { .tv_sec = times->modtime }}) + : 0, 0); +} diff --git a/libc-top-half/musl/src/time/wcsftime.c b/libc-top-half/musl/src/time/wcsftime.c new file mode 100644 index 0000000..8e1437b --- /dev/null +++ b/libc-top-half/musl/src/time/wcsftime.c @@ -0,0 +1,71 @@ +#include <wchar.h> +#include <time.h> +#include <locale.h> +#include "locale_impl.h" +#include "time_impl.h" + +size_t __wcsftime_l(wchar_t *restrict s, size_t n, const wchar_t *restrict f, const struct tm *restrict tm, locale_t loc) +{ + size_t l, k; + char buf[100]; + wchar_t wbuf[100]; + wchar_t *p; + const char *t_mb; + const wchar_t *t; + int pad, plus; + unsigned long width; + for (l=0; l<n; f++) { + if (!*f) { + s[l] = 0; + return l; + } + if (*f != '%') { + s[l++] = *f; + continue; + } + f++; + pad = 0; + if (*f == '-' || *f == '_' || *f == '0') pad = *f++; + if ((plus = (*f == '+'))) f++; + width = wcstoul(f, &p, 10); + if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') { + if (!width && p!=f) width = 1; + } else { + width = 0; + } + f = p; + if (*f == 'E' || *f == 'O') f++; + t_mb = __strftime_fmt_1(&buf, &k, *f, tm, loc, pad); + if (!t_mb) break; + k = mbstowcs(wbuf, t_mb, sizeof wbuf / sizeof *wbuf); + if (k == (size_t)-1) return 0; + t = wbuf; + if (width) { + for (; *t=='+' || *t=='-' || (*t=='0'&&t[1]); t++, k--); + width--; + if (plus && tm->tm_year >= 10000-1900) + s[l++] = '+'; + else if (tm->tm_year < -1900) + s[l++] = '-'; + else + width++; + for (; width > k && l < n; width--) + s[l++] = '0'; + } + if (k >= n-l) k = n-l; + wmemcpy(s+l, t, k); + l += k; + } + if (n) { + if (l==n) l=n-1; + s[l] = 0; + } + return 0; +} + +size_t wcsftime(wchar_t *restrict wcs, size_t n, const wchar_t *restrict f, const struct tm *restrict tm) +{ + return __wcsftime_l(wcs, n, f, tm, CURRENT_LOCALE); +} + +weak_alias(__wcsftime_l, wcsftime_l); |