Add %b and %B support to the scanf/wscanf and strto*/wcsto* families.
Coming to C23 via WG14 N2630.
This one is a little interesting, because it actually changes existing
behavior. Previously "0b101" would be parsed as "0", "b", "101" by these
functions. I'm led to believe that glibc plans to actually have separate
versions of these functions for C23 and pre-C23, so callers can have the
behavior they (implicitly) specify by virtue of which -std= they compile
with. Android has never really done anything like that, and I'm pretty
sure app developers have more than enough to worry about with API levels
without having to deal with the cartesian product of API level and C
standard.
Therefore, my plan A is "if you're running on Android >= U, you get C23
behavior". My plan B in the (I think unlikely) event that that actually
causes trouble for anyone is "if you're _targeting_ Android >= U, you
get C23 behavior". I don't think we'd actually want to have two versions
of each of these functions under any circumstances --- that seems by far
the most confusing option.
Test: treehugger
Change-Id: I0bbb30315d3fabd306905ad1484361f5d8745935
diff --git a/libc/bionic/strtol.cpp b/libc/bionic/strtol.cpp
index 77f1d92..ec72b09 100644
--- a/libc/bionic/strtol.cpp
+++ b/libc/bionic/strtol.cpp
@@ -32,11 +32,13 @@
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
+#include <wchar.h>
-template <typename T, T Min, T Max> T StrToI(const char* nptr, char** endptr, int base) {
+template <typename T, T Min, T Max, typename CharT>
+T StrToI(const CharT* nptr, CharT** endptr, int base) {
// Ensure that base is between 2 and 36 inclusive, or the special value of 0.
if (base < 0 || base == 1 || base > 36) {
- if (endptr != nullptr) *endptr = const_cast<char*>(nptr);
+ if (endptr != nullptr) *endptr = const_cast<CharT*>(nptr);
errno = EINVAL;
return 0;
}
@@ -44,7 +46,7 @@
// Skip white space and pick up leading +/- sign if any.
// If base is 0, allow 0x for hex and 0 for octal, else
// assume decimal; if base is already 16, allow 0x.
- const char* s = nptr;
+ const CharT* s = nptr;
int c;
do {
c = *s++;
@@ -62,6 +64,11 @@
s += 2;
base = 16;
}
+ if ((base == 0 || base == 2) && c == '0' && (*s == 'b' || *s == 'B') && isdigit(s[1])) {
+ c = s[1];
+ s += 2;
+ base = 2;
+ }
if (base == 0) base = (c == '0') ? 8 : 10;
// We always work in the negative space because the most negative value has a
@@ -91,7 +98,7 @@
acc -= c;
}
}
- if (endptr != nullptr) *endptr = const_cast<char*>(any ? s - 1 : nptr);
+ if (endptr != nullptr) *endptr = const_cast<CharT*>(any ? s - 1 : nptr);
if (!neg) {
if (acc == Min) {
errno = ERANGE;
@@ -103,14 +110,15 @@
return acc;
}
-template <typename T, T Max> T StrToU(const char* nptr, char** endptr, int base) {
+template <typename T, T Max, typename CharT>
+T StrToU(const CharT* nptr, CharT** endptr, int base) {
if (base < 0 || base == 1 || base > 36) {
- if (endptr != nullptr) *endptr = const_cast<char*>(nptr);
+ if (endptr != nullptr) *endptr = const_cast<CharT*>(nptr);
errno = EINVAL;
return 0;
}
- const char* s = nptr;
+ const CharT* s = nptr;
int c;
do {
c = *s++;
@@ -128,6 +136,11 @@
s += 2;
base = 16;
}
+ if ((base == 0 || base == 2) && c == '0' && (*s == 'b' || *s == 'B') && isdigit(s[1])) {
+ c = s[1];
+ s += 2;
+ base = 2;
+ }
if (base == 0) base = (c == '0') ? 8 : 10;
T cutoff = Max / static_cast<T>(base);
@@ -155,7 +168,7 @@
}
}
if (neg && any > 0) acc = -acc;
- if (endptr != nullptr) *endptr = const_cast<char*>(any ? s - 1 : nptr);
+ if (endptr != nullptr) *endptr = const_cast<CharT*>(any ? s - 1 : nptr);
return acc;
}
@@ -172,30 +185,54 @@
}
intmax_t strtoimax(const char* s, char** end, int base) {
- return StrToI<intmax_t, INTMAX_MIN, INTMAX_MAX>(s, end, base);
+ return StrToI<intmax_t, INTMAX_MIN, INTMAX_MAX, char>(s, end, base);
+}
+
+intmax_t wcstoimax(const wchar_t* s, wchar_t** end, int base) {
+ return StrToI<intmax_t, INTMAX_MIN, INTMAX_MAX, wchar_t>(s, end, base);
}
long strtol(const char* s, char** end, int base) {
- return StrToI<long, LONG_MIN, LONG_MAX>(s, end, base);
+ return StrToI<long, LONG_MIN, LONG_MAX, char>(s, end, base);
+}
+
+long wcstol(const wchar_t* s, wchar_t** end, int base) {
+ return StrToI<long, LONG_MIN, LONG_MAX, wchar_t>(s, end, base);
}
long long strtoll(const char* s, char** end, int base) {
- return StrToI<long long, LLONG_MIN, LLONG_MAX>(s, end, base);
+ return StrToI<long long, LLONG_MIN, LLONG_MAX, char>(s, end, base);
+}
+
+long long wcstoll(const wchar_t* s, wchar_t** end, int base) {
+ return StrToI<long long, LLONG_MIN, LLONG_MAX, wchar_t>(s, end, base);
}
// Public API since L, but not in any header.
__strong_alias(strtoq, strtoll);
unsigned long strtoul(const char* s, char** end, int base) {
- return StrToU<unsigned long, ULONG_MAX>(s, end, base);
+ return StrToU<unsigned long, ULONG_MAX, char>(s, end, base);
+}
+
+unsigned long wcstoul(const wchar_t* s, wchar_t** end, int base) {
+ return StrToU<unsigned long, ULONG_MAX, wchar_t>(s, end, base);
}
unsigned long long strtoull(const char* s, char** end, int base) {
- return StrToU<unsigned long long, ULLONG_MAX>(s, end, base);
+ return StrToU<unsigned long long, ULLONG_MAX, char>(s, end, base);
+}
+
+unsigned long long wcstoull(const wchar_t* s, wchar_t** end, int base) {
+ return StrToU<unsigned long long, ULLONG_MAX, wchar_t>(s, end, base);
}
uintmax_t strtoumax(const char* s, char** end, int base) {
- return StrToU<uintmax_t, UINTMAX_MAX>(s, end, base);
+ return StrToU<uintmax_t, UINTMAX_MAX, char>(s, end, base);
+}
+
+uintmax_t wcstoumax(const wchar_t* s, wchar_t** end, int base) {
+ return StrToU<uintmax_t, UINTMAX_MAX, wchar_t>(s, end, base);
}
// Public API since L, but not in any header.