summaryrefslogtreecommitdiffstats
path: root/libc-top-half/musl/src/time
diff options
context:
space:
mode:
Diffstat (limited to 'libc-top-half/musl/src/time')
-rw-r--r--libc-top-half/musl/src/time/__map_file.c19
-rw-r--r--libc-top-half/musl/src/time/__month_to_secs.c10
-rw-r--r--libc-top-half/musl/src/time/__secs_to_tm.c82
-rw-r--r--libc-top-half/musl/src/time/__tm_to_secs.c24
-rw-r--r--libc-top-half/musl/src/time/__tz.c456
-rw-r--r--libc-top-half/musl/src/time/__year_to_secs.c47
-rw-r--r--libc-top-half/musl/src/time/asctime.c7
-rw-r--r--libc-top-half/musl/src/time/asctime_r.c28
-rw-r--r--libc-top-half/musl/src/time/clock.c16
-rw-r--r--libc-top-half/musl/src/time/clock_getcpuclockid.c14
-rw-r--r--libc-top-half/musl/src/time/clock_getres.c21
-rw-r--r--libc-top-half/musl/src/time/clock_gettime.c107
-rw-r--r--libc-top-half/musl/src/time/clock_nanosleep.c38
-rw-r--r--libc-top-half/musl/src/time/clock_settime.c24
-rw-r--r--libc-top-half/musl/src/time/ctime.c8
-rw-r--r--libc-top-half/musl/src/time/ctime_r.c7
-rw-r--r--libc-top-half/musl/src/time/difftime.c6
-rw-r--r--libc-top-half/musl/src/time/ftime.c12
-rw-r--r--libc-top-half/musl/src/time/getdate.c52
-rw-r--r--libc-top-half/musl/src/time/gettimeofday.c13
-rw-r--r--libc-top-half/musl/src/time/gmtime.c8
-rw-r--r--libc-top-half/musl/src/time/gmtime_r.c16
-rw-r--r--libc-top-half/musl/src/time/localtime.c7
-rw-r--r--libc-top-half/musl/src/time/localtime_r.c21
-rw-r--r--libc-top-half/musl/src/time/mktime.c28
-rw-r--r--libc-top-half/musl/src/time/nanosleep.c7
-rw-r--r--libc-top-half/musl/src/time/strftime.c285
-rw-r--r--libc-top-half/musl/src/time/strptime.c206
-rw-r--r--libc-top-half/musl/src/time/time.c10
-rw-r--r--libc-top-half/musl/src/time/time_impl.h15
-rw-r--r--libc-top-half/musl/src/time/timegm.c18
-rw-r--r--libc-top-half/musl/src/time/timer_create.c129
-rw-r--r--libc-top-half/musl/src/time/timer_delete.c14
-rw-r--r--libc-top-half/musl/src/time/timer_getoverrun.c12
-rw-r--r--libc-top-half/musl/src/time/timer_gettime.c28
-rw-r--r--libc-top-half/musl/src/time/timer_settime.c37
-rw-r--r--libc-top-half/musl/src/time/times.c7
-rw-r--r--libc-top-half/musl/src/time/timespec_get.c10
-rw-r--r--libc-top-half/musl/src/time/utime.c11
-rw-r--r--libc-top-half/musl/src/time/wcsftime.c71
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 = &century;
+ 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);