Add remaining _l function stubs.

Bug: http://b/65595804
Test: ran tests
Change-Id: I3bea3af20b354d1f0d3e05fd35421a9045f29020
diff --git a/libc/bionic/wchar_l.cpp b/libc/bionic/wchar_l.cpp
index 6c39e8d..a86961f 100644
--- a/libc/bionic/wchar_l.cpp
+++ b/libc/bionic/wchar_l.cpp
@@ -37,24 +37,42 @@
   return wcsncasecmp(ws1, ws2, n);
 }
 
-int wcscoll_l(const wchar_t *ws1, const wchar_t *ws2, locale_t) {
+int wcscoll_l(const wchar_t* ws1, const wchar_t* ws2, locale_t) {
   return wcscoll(ws1, ws2);
 }
 
-size_t wcsxfrm_l(wchar_t *dest, const wchar_t *src, size_t n, locale_t) {
-  return wcsxfrm(dest, src, n);
+size_t wcsftime_l(wchar_t* buf, size_t n, const wchar_t* fmt, const struct tm* tm, locale_t) {
+  return wcsftime(buf, n, fmt, tm);
 }
 
-long long wcstoll_l(const wchar_t *nptr, wchar_t **endptr, int base,
-                    locale_t) {
-  return wcstoll(nptr, endptr, base);
+size_t wcsxfrm_l(wchar_t* dst, const wchar_t* src, size_t n, locale_t) {
+  return wcsxfrm(dst, src, n);
 }
 
-unsigned long long wcstoull_l(const wchar_t *nptr, wchar_t **endptr,
-                              int base, locale_t) {
-  return wcstoull(nptr, endptr, base);
+double wcstod_l(const wchar_t* s, wchar_t** end_ptr, locale_t) {
+  return wcstod(s, end_ptr);
 }
 
-long double wcstold_l(const wchar_t *nptr, wchar_t **endptr, locale_t) {
-  return wcstold(nptr, endptr);
+float wcstof_l(const wchar_t* s, wchar_t** end_ptr, locale_t) {
+  return wcstof(s, end_ptr);
+}
+
+long wcstol_l(const wchar_t* s, wchar_t** end_ptr, int base, locale_t) {
+  return wcstol(s, end_ptr, base);
+}
+
+long long wcstoll_l(const wchar_t* s, wchar_t** end_ptr, int base, locale_t) {
+  return wcstoll(s, end_ptr, base);
+}
+
+unsigned long wcstoul_l(const wchar_t* s, wchar_t** end_ptr, int base, locale_t) {
+  return wcstoul(s, end_ptr, base);
+}
+
+unsigned long long wcstoull_l(const wchar_t* s, wchar_t** end_ptr, int base, locale_t) {
+  return wcstoull(s, end_ptr, base);
+}
+
+long double wcstold_l(const wchar_t* s, wchar_t** end_ptr, locale_t) {
+  return wcstold(s, end_ptr);
 }
diff --git a/libc/include/sys/cdefs.h b/libc/include/sys/cdefs.h
index 26dd9a2..c270bb5 100644
--- a/libc/include/sys/cdefs.h
+++ b/libc/include/sys/cdefs.h
@@ -120,6 +120,7 @@
 
 #define __printflike(x, y) __attribute__((__format__(printf, x, y)))
 #define __scanflike(x, y) __attribute__((__format__(scanf, x, y)))
+#define __strftimelike(x) __attribute__((__format__(strftime, x, 0)))
 
 /*
  * GNU C version 2.96 added explicit branch prediction so that
diff --git a/libc/include/time.h b/libc/include/time.h
index 8e78949..17751d7 100644
--- a/libc/include/time.h
+++ b/libc/include/time.h
@@ -74,11 +74,12 @@
 struct tm* gmtime(const time_t* __t);
 struct tm* gmtime_r(const time_t* __t, struct tm* __tm);
 
-char* strptime(const char* __s, const char* __fmt, struct tm* __tm);
-size_t strftime(char* __buf, size_t __n, const char* __fmt, const struct tm* __tm);
+char* strptime(const char* __s, const char* __fmt, struct tm* __tm) __strftimelike(2);
+char* strptime_l(const char* __s, const char* __fmt, struct tm* __tm, locale_t __l) __strftimelike(2) __INTRODUCED_IN(28);
 
+size_t strftime(char* __buf, size_t __n, const char* __fmt, const struct tm* __tm) __strftimelike(3);
 #if __ANDROID_API__ >= __ANDROID_API_L__
-size_t strftime_l(char* __buf, size_t __n, const char* __fmt, const struct tm* __tm, locale_t __l) __INTRODUCED_IN(21);
+size_t strftime_l(char* __buf, size_t __n, const char* __fmt, const struct tm* __tm, locale_t __l) __strftimelike(3) __INTRODUCED_IN(21);
 #else
 // Implemented as static inline before 21.
 #endif
diff --git a/libc/include/wchar.h b/libc/include/wchar.h
index 1cf498c83..f33af5f 100644
--- a/libc/include/wchar.h
+++ b/libc/include/wchar.h
@@ -81,6 +81,7 @@
 wchar_t* wcscpy(wchar_t* __dst, const wchar_t* __src);
 size_t wcscspn(const wchar_t* __s, const wchar_t* __accept);
 size_t wcsftime(wchar_t* __buf, size_t __n, const wchar_t* __fmt, const struct tm* __tm);
+size_t wcsftime_l(wchar_t* __buf, size_t __n, const wchar_t* __fmt, const struct tm* __tm, locale_t __l) __INTRODUCED_IN(28);
 size_t wcslen(const wchar_t* __s);
 int wcsncasecmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n);
 int wcsncasecmp_l(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n, locale_t __l) __INTRODUCED_IN(23);
@@ -94,12 +95,16 @@
 size_t wcsspn(const wchar_t* __s, const wchar_t* __accept);
 wchar_t* wcsstr(const wchar_t* __haystack, const wchar_t* __needle);
 double wcstod(const wchar_t* __s, wchar_t** __end_ptr);
+double wcstod_l(const wchar_t* __s, wchar_t** __end_ptr, locale_t __l) __INTRODUCED_IN(28);
 float wcstof(const wchar_t* __s, wchar_t** __end_ptr) __INTRODUCED_IN(21);
+float wcstof_l(const wchar_t* __s, wchar_t** __end_ptr, locale_t __l) __INTRODUCED_IN(28);
 wchar_t* wcstok(wchar_t* __s, const wchar_t* __delimiter, wchar_t** __ptr);
 long wcstol(const wchar_t* __s, wchar_t** __end_ptr, int __base);
+long wcstol_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(28);
 long long wcstoll(const wchar_t* __s, wchar_t** __end_ptr, int __base) __INTRODUCED_IN(21);
 long double wcstold(const wchar_t* __s, wchar_t** __end_ptr) __RENAME_LDBL(wcstod, 3, 21);
 unsigned long wcstoul(const wchar_t* __s, wchar_t** __end_ptr, int __base);
+unsigned long wcstoul_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(28);
 unsigned long long wcstoull(const wchar_t* __s, wchar_t** __end_ptr, int __base) __INTRODUCED_IN(21);
 int wcswidth(const wchar_t* __s, size_t __n);
 size_t wcsxfrm(wchar_t* __dst, const wchar_t* __src, size_t __n);
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index a7977eb..883a259 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1400,8 +1400,14 @@
     sigtimedwait64;
     sigwait64;
     sigwaitinfo64;
+    strptime_l;
     swab;
     syncfs;
+    wcsftime_l;
+    wcstod_l;
+    wcstof_l;
+    wcstol_l;
+    wcstoul_l;
 } LIBC_O;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index 1cae22b..bb5f144 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1320,8 +1320,14 @@
     sigtimedwait64;
     sigwait64;
     sigwaitinfo64;
+    strptime_l;
     swab;
     syncfs;
+    wcsftime_l;
+    wcstod_l;
+    wcstof_l;
+    wcstol_l;
+    wcstoul_l;
 } LIBC_O;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 1188a85..c2c75c0 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1425,8 +1425,14 @@
     sigtimedwait64;
     sigwait64;
     sigwaitinfo64;
+    strptime_l;
     swab;
     syncfs;
+    wcsftime_l;
+    wcstod_l;
+    wcstof_l;
+    wcstol_l;
+    wcstoul_l;
 } LIBC_O;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 3d95079..6a03bab 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1384,8 +1384,14 @@
     sigtimedwait64;
     sigwait64;
     sigwaitinfo64;
+    strptime_l;
     swab;
     syncfs;
+    wcsftime_l;
+    wcstod_l;
+    wcstof_l;
+    wcstol_l;
+    wcstoul_l;
 } LIBC_O;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index 1cae22b..bb5f144 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1320,8 +1320,14 @@
     sigtimedwait64;
     sigwait64;
     sigwaitinfo64;
+    strptime_l;
     swab;
     syncfs;
+    wcsftime_l;
+    wcstod_l;
+    wcstof_l;
+    wcstol_l;
+    wcstoul_l;
 } LIBC_O;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index bcb5feb..519a0f0 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1382,8 +1382,14 @@
     sigtimedwait64;
     sigwait64;
     sigwaitinfo64;
+    strptime_l;
     swab;
     syncfs;
+    wcsftime_l;
+    wcstod_l;
+    wcstof_l;
+    wcstol_l;
+    wcstoul_l;
 } LIBC_O;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index 1cae22b..bb5f144 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1320,8 +1320,14 @@
     sigtimedwait64;
     sigwait64;
     sigwaitinfo64;
+    strptime_l;
     swab;
     syncfs;
+    wcsftime_l;
+    wcstod_l;
+    wcstof_l;
+    wcstol_l;
+    wcstoul_l;
 } LIBC_O;
 
 LIBC_PRIVATE {
diff --git a/libc/tzcode/strptime.c b/libc/tzcode/strptime.c
index 0567aa4..2100630 100644
--- a/libc/tzcode/strptime.c
+++ b/libc/tzcode/strptime.c
@@ -430,3 +430,7 @@
     *dest = result;
     return (1);
 }
+
+char* strptime_l(const char* buf, const char* fmt, struct tm* tm, locale_t l) {
+  return strptime(buf, fmt, tm);
+}
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index fccff67..2b9935a 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -279,6 +279,23 @@
   EXPECT_STREQ("09:41:53", buf);
 }
 
+TEST(time, strptime_l) {
+  setenv("TZ", "UTC", 1);
+
+  struct tm t;
+  char buf[64];
+
+  memset(&t, 0, sizeof(t));
+  strptime_l("11:14", "%R", &t, LC_GLOBAL_LOCALE);
+  strftime_l(buf, sizeof(buf), "%H:%M", &t, LC_GLOBAL_LOCALE);
+  EXPECT_STREQ("11:14", buf);
+
+  memset(&t, 0, sizeof(t));
+  strptime_l("09:41:53", "%T", &t, LC_GLOBAL_LOCALE);
+  strftime_l(buf, sizeof(buf), "%H:%M:%S", &t, LC_GLOBAL_LOCALE);
+  EXPECT_STREQ("09:41:53", buf);
+}
+
 void SetTime(timer_t t, time_t value_s, time_t value_ns, time_t interval_s, time_t interval_ns) {
   itimerspec ts;
   ts.it_value.tv_sec = value_s;
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index 6f90c6c..b42e13c 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -539,7 +539,7 @@
   ASSERT_EQ(EILSEQ, errno);
 }
 
-TEST(wchar, wcsftime) {
+TEST(wchar, wcsftime__wcsftime_l) {
   setenv("TZ", "UTC", 1);
 
   struct tm t;
@@ -552,6 +552,8 @@
 
   EXPECT_EQ(24U, wcsftime(buf, sizeof(buf), L"%c", &t));
   EXPECT_STREQ(L"Sun Mar 10 00:00:00 2100", buf);
+  EXPECT_EQ(24U, wcsftime_l(buf, sizeof(buf), L"%c", &t, LC_GLOBAL_LOCALE));
+  EXPECT_STREQ(L"Sun Mar 10 00:00:00 2100", buf);
 }
 
 TEST(wchar, wmemmove_smoke) {
@@ -878,6 +880,34 @@
   TestWcsToFloatInfNan(wcstold);
 }
 
+TEST(wchar, wcstod_l) {
+  EXPECT_EQ(1.23, wcstod_l(L"1.23", nullptr, LC_GLOBAL_LOCALE));
+}
+
+TEST(wchar, wcstof_l) {
+  EXPECT_EQ(1.23f, wcstof_l(L"1.23", nullptr, LC_GLOBAL_LOCALE));
+}
+
+TEST(wchar, wcstol_l) {
+  EXPECT_EQ(123L, wcstol_l(L"123", nullptr, 10, LC_GLOBAL_LOCALE));
+}
+
+TEST(wchar, wcstold_l) {
+  EXPECT_EQ(1.23L, wcstold_l(L"1.23", nullptr, LC_GLOBAL_LOCALE));
+}
+
+TEST(wchar, wcstoll_l) {
+  EXPECT_EQ(123LL, wcstoll_l(L"123", nullptr, 10, LC_GLOBAL_LOCALE));
+}
+
+TEST(wchar, wcstoul_l) {
+  EXPECT_EQ(123UL, wcstoul_l(L"123", nullptr, 10, LC_GLOBAL_LOCALE));
+}
+
+TEST(wchar, wcstoull_l) {
+  EXPECT_EQ(123ULL, wcstoul_l(L"123", nullptr, 10, LC_GLOBAL_LOCALE));
+}
+
 static void AssertWcwidthRange(wchar_t begin, wchar_t end, int expected) {
   for (wchar_t i = begin; i < end; ++i) {
     EXPECT_EQ(expected, wcwidth(i)) << static_cast<int>(i);