Merge c00d9c034652839745ea119d7a42e8b9718f30d7 on remote branch
Change-Id: I5b97c297bff6d22435d3355f2c577972eaea7d31
diff --git a/docs/status.md b/docs/status.md
index 3d104e4..c2ed944 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -67,6 +67,8 @@
* Support for `%b` and `%B` in the printf/wprintf family, `%b` in the
scanf/wscanf family, and `0b` prefixes with base 0 in the strtol/wcstol
family.
+ * Support for `wN` length modifiers in the printf/wprintf family.
+ * tmpfile() now respects $TMPDIR.
New libc functions in T (API level 33):
* `backtrace`, `backtrace_symbols`, `backtrace_symbols_fd` (`<execinfo.h>`).
diff --git a/libc/bionic/gwp_asan_wrappers.cpp b/libc/bionic/gwp_asan_wrappers.cpp
index fab29ef..251633d 100644
--- a/libc/bionic/gwp_asan_wrappers.cpp
+++ b/libc/bionic/gwp_asan_wrappers.cpp
@@ -307,10 +307,8 @@
sysprop_names[3] = persist_default_sysprop;
}
- // TODO(mitchp): Log overrides using this.
- const char* source;
return get_config_from_env_or_sysprops(env_var, sysprop_names, arraysize(sysprop_names),
- value_out, PROP_VALUE_MAX, &source);
+ value_out, PROP_VALUE_MAX);
}
bool GetGwpAsanIntegerOption(unsigned long long* result,
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 0935cd6..d64d402 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -217,13 +217,16 @@
// Returns true if there's an environment setting (either sysprop or env var)
// that should overwrite the ELF note, and places the equivalent heap tagging
// level into *level.
-static bool get_environment_memtag_setting(const char* basename, HeapTaggingLevel* level) {
+static bool get_environment_memtag_setting(HeapTaggingLevel* level) {
static const char kMemtagPrognameSyspropPrefix[] = "arm64.memtag.process.";
static const char kMemtagGlobalSysprop[] = "persist.arm64.memtag.default";
static const char kMemtagOverrideSyspropPrefix[] =
"persist.device_config.memory_safety_native.mode_override.process.";
- if (basename == nullptr) return false;
+ const char* progname = __libc_shared_globals()->init_progname;
+ if (progname == nullptr) return false;
+
+ const char* basename = __gnu_basename(progname);
char options_str[PROP_VALUE_MAX];
char sysprop_name[512];
@@ -234,9 +237,8 @@
kMemtagOverrideSyspropPrefix, basename);
const char* sys_prop_names[] = {sysprop_name, remote_sysprop_name, kMemtagGlobalSysprop};
- const char* source = nullptr;
if (!get_config_from_env_or_sysprops("MEMTAG_OPTIONS", sys_prop_names, arraysize(sys_prop_names),
- options_str, sizeof(options_str), &source)) {
+ options_str, sizeof(options_str))) {
return false;
}
@@ -247,20 +249,14 @@
} else if (strcmp("off", options_str) == 0) {
*level = M_HEAP_TAGGING_LEVEL_TBI;
} else {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc",
- "%s: unrecognized memtag level in %s: \"%s\" (options are \"sync\", "
- "\"async\", or \"off\").",
- basename, source, options_str);
+ async_safe_format_log(
+ ANDROID_LOG_ERROR, "libc",
+ "unrecognized memtag level: \"%s\" (options are \"sync\", \"async\", or \"off\").",
+ options_str);
return false;
}
- async_safe_format_log(ANDROID_LOG_DEBUG, "libc", "%s: chose memtag level \"%s\" from %s.",
- basename, options_str, source);
- return true;
-}
-static void log_elf_memtag_level(const char* basename, const char* level) {
- async_safe_format_log(ANDROID_LOG_DEBUG, "libc", "%s: chose memtag level \"%s\" from ELF note",
- basename ?: "<unknown>", level);
+ return true;
}
// Returns the initial heap tagging level. Note: This function will never return
@@ -268,14 +264,12 @@
// M_HEAP_TAGGING_LEVEL_TBI.
static HeapTaggingLevel __get_heap_tagging_level(const void* phdr_start, size_t phdr_ct,
uintptr_t load_bias, bool* stack) {
- const char* progname = __libc_shared_globals()->init_progname;
- const char* basename = progname ? __gnu_basename(progname) : nullptr;
unsigned note_val =
__get_memtag_note(reinterpret_cast<const ElfW(Phdr)*>(phdr_start), phdr_ct, load_bias);
*stack = note_val & NT_MEMTAG_STACK;
HeapTaggingLevel level;
- if (get_environment_memtag_setting(basename, &level)) return level;
+ if (get_environment_memtag_setting(&level)) return level;
// Note, previously (in Android 12), any value outside of bits [0..3] resulted
// in a check-fail. In order to be permissive of further extensions, we
@@ -290,17 +284,14 @@
// by anyone, but we note it (heh) here for posterity, in case the zero
// level becomes meaningful, and binaries with this note can be executed
// on Android 12 devices.
- log_elf_memtag_level(basename, "off");
return M_HEAP_TAGGING_LEVEL_TBI;
case NT_MEMTAG_LEVEL_ASYNC:
- log_elf_memtag_level(basename, "async");
return M_HEAP_TAGGING_LEVEL_ASYNC;
case NT_MEMTAG_LEVEL_SYNC:
default:
// We allow future extensions to specify mode 3 (currently unused), with
// the idea that it might be used for ASYMM mode or something else. On
// this version of Android, it falls back to SYNC mode.
- log_elf_memtag_level(basename, "sync");
return M_HEAP_TAGGING_LEVEL_SYNC;
}
}
diff --git a/libc/bionic/sysprop_helpers.cpp b/libc/bionic/sysprop_helpers.cpp
index 2051330..5627034 100644
--- a/libc/bionic/sysprop_helpers.cpp
+++ b/libc/bionic/sysprop_helpers.cpp
@@ -57,22 +57,18 @@
}
bool get_config_from_env_or_sysprops(const char* env_var_name, const char* const* sys_prop_names,
- size_t sys_prop_names_size, char* options, size_t options_size,
- const char** chosen_source) {
+ size_t sys_prop_names_size, char* options,
+ size_t options_size) {
const char* env = getenv(env_var_name);
if (env && *env != '\0') {
strncpy(options, env, options_size);
options[options_size - 1] = '\0'; // Ensure null-termination.
- *chosen_source = env_var_name;
return true;
}
for (size_t i = 0; i < sys_prop_names_size; ++i) {
if (sys_prop_names[i] == nullptr) continue;
- if (get_property_value(sys_prop_names[i], options, options_size)) {
- *chosen_source = sys_prop_names[i];
- return true;
- }
+ if (get_property_value(sys_prop_names[i], options, options_size)) return true;
}
return false;
}
diff --git a/libc/bionic/sysprop_helpers.h b/libc/bionic/sysprop_helpers.h
index 84e7af1..a02c2dc 100644
--- a/libc/bionic/sysprop_helpers.h
+++ b/libc/bionic/sysprop_helpers.h
@@ -36,13 +36,10 @@
// 2. System properties, in the order they're specified in sys_prop_names.
// If neither of these options are specified (or they're both an empty string),
// this function returns false. Otherwise, it returns true, and the presiding
-// options string is written to the `options` buffer of size `size`. It will
-// store a pointer to either env_var_name, or into the relevant entry of
-// sys_prop_names into choicen_source, indiciating which value was used. If
-// this function returns true, `options` is guaranteed to be null-terminated.
+// options string is written to the `options` buffer of size `size`. If this
+// function returns true, `options` is guaranteed to be null-terminated.
// `options_size` should be at least PROP_VALUE_MAX.
__LIBC_HIDDEN__ bool get_config_from_env_or_sysprops(const char* env_var_name,
const char* const* sys_prop_names,
size_t sys_prop_names_size, char* options,
- size_t options_size,
- const char** chosen_source);
+ size_t options_size);
diff --git a/libc/bionic/tmpfile.cpp b/libc/bionic/tmpfile.cpp
index 3d04610..4d6a1fb 100644
--- a/libc/bionic/tmpfile.cpp
+++ b/libc/bionic/tmpfile.cpp
@@ -51,6 +51,9 @@
return nullptr;
}
+// O_TMPFILE isn't available until Linux 3.11, so we fall back to this on
+// older kernels. AOSP was on a new enough kernel in the Lollipop timeframe,
+// so this code should be obsolete by 2025.
static FILE* __tmpfile_dir_legacy(const char* tmp_dir) {
char* path = nullptr;
if (asprintf(&path, "%s/tmp.XXXXXXXXXX", tmp_dir) == -1) {
@@ -79,25 +82,18 @@
return __fd_to_fp(fd);
}
-static FILE* __tmpfile_dir(const char* tmp_dir) {
- int fd = open(tmp_dir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
- if (fd == -1) return __tmpfile_dir_legacy(tmp_dir);
- return __fd_to_fp(fd);
+const char* __get_TMPDIR() {
+ // Use $TMPDIR if set, or fall back to /data/local/tmp otherwise.
+ // Useless for apps, but good enough for the shell.
+ const char* tmpdir = getenv("TMPDIR");
+ return (tmpdir == nullptr) ? "/data/local/tmp" : tmpdir;
}
FILE* tmpfile() {
- // TODO: get this app's temporary directory from the framework ("/data/data/app/cache").
-
- // $EXTERNAL_STORAGE turns out not to be very useful because it doesn't support hard links.
- // This means we can't do the usual trick of calling unlink before handing the file back.
-
- FILE* fp = __tmpfile_dir("/data/local/tmp");
- if (fp == nullptr) {
- // P_tmpdir is "/tmp/", but POSIX explicitly says that tmpdir(3) should try P_tmpdir before
- // giving up. This is potentially useful for bionic on the host anyway.
- fp = __tmpfile_dir(P_tmpdir);
- }
- return fp;
+ const char* tmpdir = __get_TMPDIR();
+ int fd = open(tmpdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
+ if (fd == -1) return __tmpfile_dir_legacy(tmpdir);
+ return __fd_to_fp(fd);
}
__strong_alias(tmpfile64, tmpfile);
@@ -107,7 +103,7 @@
// since we can't easily remove it...
// $TMPDIR overrides any directory passed in.
- char* tmpdir = getenv("TMPDIR");
+ const char* tmpdir = getenv("TMPDIR");
if (tmpdir != nullptr) dir = tmpdir;
// If we still have no directory, we'll give you a default.
@@ -136,12 +132,7 @@
static char buf[L_tmpnam];
if (s == nullptr) s = buf;
- // Use $TMPDIR if set, or fall back to /data/local/tmp otherwise.
- // Useless for apps, but good enough for the shell.
- const char* dir = getenv("TMPDIR");
- if (dir == nullptr) dir = "/data/local/tmp";
-
// Make up a mktemp(3) template and defer to it for the real work.
- snprintf(s, L_tmpnam, "%s/tmpnam.XXXXXXXXXX", dir);
+ snprintf(s, L_tmpnam, "%s/tmpnam.XXXXXXXXXX", __get_TMPDIR());
return mktemp(s);
}
diff --git a/libc/include/sys/select.h b/libc/include/sys/select.h
index 06914a6..8c6c2ff 100644
--- a/libc/include/sys/select.h
+++ b/libc/include/sys/select.h
@@ -71,9 +71,9 @@
} \
} while (0)
-void __FD_CLR_chk(int, fd_set*, size_t) __INTRODUCED_IN(21);
-void __FD_SET_chk(int, fd_set*, size_t) __INTRODUCED_IN(21);
-int __FD_ISSET_chk(int, const fd_set*, size_t) __INTRODUCED_IN(21);
+void __FD_CLR_chk(int, fd_set* _Nonnull , size_t) __INTRODUCED_IN(21);
+void __FD_SET_chk(int, fd_set* _Nonnull, size_t) __INTRODUCED_IN(21);
+int __FD_ISSET_chk(int, const fd_set* _Nonnull, size_t) __INTRODUCED_IN(21);
#define __FD_CLR(fd, set) (__FDS_BITS(fd_set*,set)[__FDELT(fd)] &= ~__FDMASK(fd))
#define __FD_SET(fd, set) (__FDS_BITS(fd_set*,set)[__FDELT(fd)] |= __FDMASK(fd))
@@ -95,7 +95,7 @@
* Returns the number of ready file descriptors on success, 0 for timeout,
* and returns -1 and sets `errno` on failure.
*/
-int select(int __max_fd_plus_one, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, struct timeval* __timeout);
+int select(int __max_fd_plus_one, fd_set* _Nullable __read_fds, fd_set* _Nullable __write_fds, fd_set* _Nullable __exception_fds, struct timeval* _Nullable __timeout);
/**
* [pselect(2)](http://man7.org/linux/man-pages/man2/select.2.html) waits on a
@@ -106,7 +106,7 @@
* Returns the number of ready file descriptors on success, 0 for timeout,
* and returns -1 and sets `errno` on failure.
*/
-int pselect(int __max_fd_plus_one, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, const struct timespec* __timeout, const sigset_t* __mask);
+int pselect(int __max_fd_plus_one, fd_set* _Nullable __read_fds, fd_set* _Nullable __write_fds, fd_set* _Nullable __exception_fds, const struct timespec* _Nullable __timeout, const sigset_t* _Nullable __mask);
/**
* [pselect64(2)](http://man7.org/linux/man-pages/man2/select.2.html) waits on a
@@ -119,6 +119,6 @@
*
* Available since API level 28.
*/
-int pselect64(int __max_fd_plus_one, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, const struct timespec* __timeout, const sigset64_t* __mask) __INTRODUCED_IN(28);
+int pselect64(int __max_fd_plus_one, fd_set* _Nullable __read_fds, fd_set* _Nullable __write_fds, fd_set* _Nullable __exception_fds, const struct timespec* _Nullable __timeout, const sigset64_t* _Nullable __mask) __INTRODUCED_IN(28);
__END_DECLS
diff --git a/libc/include/wchar.h b/libc/include/wchar.h
index add3606..39f9374 100644
--- a/libc/include/wchar.h
+++ b/libc/include/wchar.h
@@ -44,97 +44,96 @@
__BEGIN_DECLS
wint_t btowc(int __ch);
-int fwprintf(FILE* __fp, const wchar_t* __fmt, ...);
-int fwscanf(FILE* __fp, const wchar_t* __fmt, ...);
-wint_t fgetwc(FILE* __fp);
-wchar_t* fgetws(wchar_t* __buf, int __size, FILE* __fp);
-wint_t fputwc(wchar_t __wc, FILE* __fp);
-int fputws(const wchar_t* __s, FILE* __fp);
-int fwide(FILE* __fp, int __mode);
-wint_t getwc(FILE* __fp);
+int fwprintf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, ...);
+int fwscanf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, ...);
+wint_t fgetwc(FILE* _Nonnull __fp);
+wchar_t* _Nullable fgetws(wchar_t* _Nonnull __buf, int __size, FILE* _Nonnull __fp);
+wint_t fputwc(wchar_t __wc, FILE* _Nonnull __fp);
+int fputws(const wchar_t* _Nonnull __s, FILE* _Nonnull __fp);
+int fwide(FILE* _Nonnull __fp, int __mode);
+wint_t getwc(FILE* _Nonnull __fp);
wint_t getwchar(void);
-int mbsinit(const mbstate_t* __ps);
-size_t mbrlen(const char* __s, size_t __n, mbstate_t* __ps);
-size_t mbrtowc(wchar_t* __buf, const char* __s, size_t __n, mbstate_t* __ps);
-size_t mbsrtowcs(wchar_t* __dst, const char** __src, size_t __dst_n, mbstate_t* __ps);
-size_t mbsnrtowcs(wchar_t* __dst, const char** __src, size_t __src_n, size_t __dst_n, mbstate_t* __ps) __INTRODUCED_IN(21);
-wint_t putwc(wchar_t __wc, FILE* __fp);
+int mbsinit(const mbstate_t* _Nullable __ps);
+size_t mbrlen(const char* _Nullable __s, size_t __n, mbstate_t* _Nullable __ps);
+size_t mbrtowc(wchar_t* _Nullable __buf, const char* _Nullable __s, size_t __n, mbstate_t* _Nullable __ps);
+size_t mbsrtowcs(wchar_t* _Nullable __dst, const char* _Nullable * _Nonnull __src, size_t __dst_n, mbstate_t* _Nullable __ps);
+size_t mbsnrtowcs(wchar_t* _Nullable __dst, const char* _Nullable * _Nullable __src, size_t __src_n, size_t __dst_n, mbstate_t* _Nullable __ps) __INTRODUCED_IN(21);
+wint_t putwc(wchar_t __wc, FILE* _Nonnull __fp);
wint_t putwchar(wchar_t __wc);
-int swprintf(wchar_t* __buf, size_t __n, const wchar_t* __fmt, ...);
-int swscanf(const wchar_t* __s, const wchar_t* __fmt, ...);
-wint_t ungetwc(wint_t __wc, FILE* __fp);
-int vfwprintf(FILE* __fp, const wchar_t* __fmt, va_list __args);
-int vfwscanf(FILE* __fp, const wchar_t* __fmt, va_list __args) __INTRODUCED_IN(21);
-int vswprintf(wchar_t* __buf, size_t __n, const wchar_t* __fmt, va_list __args);
-int vswscanf(const wchar_t* __s, const wchar_t* __fmt, va_list __args) __INTRODUCED_IN(21);
-int vwprintf(const wchar_t* __fmt, va_list __args);
-int vwscanf(const wchar_t* __fmt, va_list __args) __INTRODUCED_IN(21);
-wchar_t* wcpcpy(wchar_t* __dst, const wchar_t* __src);
-wchar_t* wcpncpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
-size_t wcrtomb(char* __buf, wchar_t __wc, mbstate_t* __ps);
-int wcscasecmp(const wchar_t* __lhs, const wchar_t* __rhs);
-int wcscasecmp_l(const wchar_t* __lhs, const wchar_t* __rhs, locale_t __l) __INTRODUCED_IN(23);
-wchar_t* wcscat(wchar_t* __dst, const wchar_t* __src);
-wchar_t* wcschr(const wchar_t* __s, wchar_t __wc);
-int wcscmp(const wchar_t* __lhs, const wchar_t* __rhs);
-int wcscoll(const wchar_t* __lhs, const wchar_t* __rhs);
-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);
-wchar_t* wcsncat(wchar_t* __dst, const wchar_t* __src, size_t __n);
-int wcsncmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n);
-wchar_t* wcsncpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
-size_t wcsnrtombs(char* __dst, const wchar_t** __src, size_t __src_n, size_t __dst_n, mbstate_t* __ps) __INTRODUCED_IN(21);
-wchar_t* wcspbrk(const wchar_t* __s, const wchar_t* __accept);
-wchar_t* wcsrchr(const wchar_t* __s, wchar_t __wc);
-size_t wcsrtombs(char* __dst, const wchar_t** __src, size_t __dst_n, mbstate_t* __ps);
-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);
+int swprintf(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nonnull __fmt, ...);
+int swscanf(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __fmt, ...);
+wint_t ungetwc(wint_t __wc, FILE* _Nonnull __fp);
+int vfwprintf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, va_list __args);
+int vfwscanf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, va_list __args) __INTRODUCED_IN(21);
+int vswprintf(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nonnull __fmt, va_list __args);
+int vswscanf(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __fmt, va_list __args) __INTRODUCED_IN(21);
+int vwprintf(const wchar_t* _Nonnull __fmt, va_list __args);
+int vwscanf(const wchar_t* _Nonnull __fmt, va_list __args) __INTRODUCED_IN(21);
+wchar_t* _Nonnull wcpcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src);
+wchar_t* _Nonnull wcpncpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+size_t wcrtomb(char* _Nullable __buf, wchar_t __wc, mbstate_t* _Nullable __ps);
+int wcscasecmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs);
+int wcscasecmp_l(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, locale_t _Nonnull __l) __INTRODUCED_IN(23);
+wchar_t* _Nonnull wcscat(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src);
+wchar_t* _Nullable wcschr(const wchar_t * _Nonnull __s, wchar_t __wc);
+int wcscmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs);
+int wcscoll(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs);
+wchar_t* _Nonnull wcscpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src);
+size_t wcscspn(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __accept);
+size_t wcsftime(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nullable __fmt, const struct tm* _Nonnull __tm);
+size_t wcsftime_l(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nullable __fmt, const struct tm* _Nonnull __tm, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+size_t wcslen(const wchar_t* _Nonnull __s);
+int wcsncasecmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, size_t __n);
+int wcsncasecmp_l(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, size_t __n, locale_t _Nonnull __l) __INTRODUCED_IN(23);
+wchar_t* _Nonnull wcsncat(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+int wcsncmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, size_t __n);
+wchar_t* _Nonnull wcsncpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+size_t wcsnrtombs(char* _Nullable __dst, const wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __src, size_t __src_n, size_t __dst_n, mbstate_t* _Nullable __ps) __INTRODUCED_IN(21);
+wchar_t* _Nullable wcspbrk(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __accept);
+wchar_t* _Nullable wcsrchr(const wchar_t* _Nonnull __s, wchar_t __wc);
+size_t wcsrtombs(char* _Nullable __dst, const wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __src, size_t __dst_n, mbstate_t* _Nullable __ps);
+size_t wcsspn(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __accept);
+wchar_t* _Nullable wcsstr(const wchar_t* _Nonnull __haystack, const wchar_t* _Nonnull __needle);
+double wcstod(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr);
+double wcstod_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+float wcstof(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr) __INTRODUCED_IN(21);
+float wcstof_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+wchar_t* _Nullable wcstok(wchar_t* _Nullable __s, const wchar_t* _Nonnull __delimiter, wchar_t* _Nonnull * _Nonnull __ptr);
+long wcstol(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base);
+long wcstol_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+long long wcstoll(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base) __INTRODUCED_IN(21);
+long double wcstold(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr) __RENAME_LDBL(wcstod, 3, 21);
+unsigned long wcstoul(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base);
+unsigned long wcstoul_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+unsigned long long wcstoull(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base) __INTRODUCED_IN(21);
+int wcswidth(const wchar_t* _Nonnull __s, size_t __n);
+size_t wcsxfrm(wchar_t* __BIONIC_COMPLICATED_NULLNESS __dst, const wchar_t* _Nonnull __src, size_t __n);
int wctob(wint_t __wc);
int wcwidth(wchar_t __wc);
-wchar_t* wmemchr(const wchar_t* __src, wchar_t __wc, size_t __n);
-int wmemcmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n);
-wchar_t* wmemcpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
+wchar_t* _Nullable wmemchr(const wchar_t* _Nonnull __src, wchar_t __wc, size_t __n);
+int wmemcmp(const wchar_t* _Nullable __lhs, const wchar_t* _Nullable __rhs, size_t __n);
+wchar_t* _Nonnull wmemcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
#if defined(__USE_GNU)
-wchar_t* wmempcpy(wchar_t* __dst, const wchar_t* __src, size_t __n) __INTRODUCED_IN(23);
+wchar_t* _Nonnull wmempcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n) __INTRODUCED_IN(23);
#endif
-wchar_t* wmemmove(wchar_t* __dst, const wchar_t* __src, size_t __n);
-wchar_t* wmemset(wchar_t* __dst, wchar_t __wc, size_t __n);
-int wprintf(const wchar_t* __fmt, ...);
-int wscanf(const wchar_t* __fmt, ...);
+wchar_t* _Nonnull wmemmove(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+wchar_t* _Nonnull wmemset(wchar_t* _Nonnull __dst, wchar_t __wc, size_t __n);
+int wprintf(const wchar_t* _Nonnull __fmt, ...);
+int wscanf(const wchar_t* _Nonnull __fmt, ...);
-long long wcstoll_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(21);
-unsigned long long wcstoull_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(21);
-long double wcstold_l(const wchar_t* __s, wchar_t** __end_ptr, locale_t __l) __INTRODUCED_IN(21);
+long long wcstoll_l(const wchar_t* _Nonnull __s, wchar_t* _Nullable * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(21);
+unsigned long long wcstoull_l(const wchar_t* _Nonnull __s, wchar_t* _Nullable * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(21);
+long double wcstold_l(const wchar_t* _Nonnull __s, wchar_t* _Nullable * _Nullable __end_ptr, locale_t _Nonnull __l) __INTRODUCED_IN(21);
-int wcscoll_l(const wchar_t* __lhs, const wchar_t* __rhs, locale_t __l) __attribute_pure__
+int wcscoll_l(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, locale_t _Nonnull __l) __attribute_pure__
__INTRODUCED_IN(21);
-size_t wcsxfrm_l(wchar_t* __dst, const wchar_t* __src, size_t __n, locale_t __l) __INTRODUCED_IN(21);
+size_t wcsxfrm_l(wchar_t* __BIONIC_COMPLICATED_NULLNESS __dst, const wchar_t* _Nonnull __src, size_t __n, locale_t _Nonnull __l) __INTRODUCED_IN(21);
+size_t wcslcat(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+size_t wcslcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
-size_t wcslcat(wchar_t* __dst, const wchar_t* __src, size_t __n);
-size_t wcslcpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
-
-FILE* open_wmemstream(wchar_t** __ptr, size_t* __size_ptr) __INTRODUCED_IN(23);
-wchar_t* wcsdup(const wchar_t* __s);
-size_t wcsnlen(const wchar_t* __s, size_t __n);
+FILE* _Nullable open_wmemstream(wchar_t* _Nonnull * _Nonnull __ptr, size_t* _Nonnull __size_ptr) __INTRODUCED_IN(23);
+wchar_t* _Nullable wcsdup(const wchar_t* _Nonnull __s);
+size_t wcsnlen(const wchar_t* _Nonnull __s, size_t __n);
__END_DECLS
diff --git a/libc/stdio/printf_common.h b/libc/stdio/printf_common.h
index e761835..b0055f0 100644
--- a/libc/stdio/printf_common.h
+++ b/libc/stdio/printf_common.h
@@ -528,6 +528,17 @@
case 'b':
ADDUARG();
break;
+ case 'w':
+ n = 0;
+ ch = *fmt++;
+ while (is_digit(ch)) {
+ APPEND_DIGIT(n, ch);
+ ch = *fmt++;
+ }
+ if (n == 64) {
+ flags |= LLONGINT;
+ }
+ goto reswitch;
default: /* "%?" prints ?, unless ? is NUL */
if (ch == '\0') goto done;
break;
diff --git a/libc/stdio/vfprintf.cpp b/libc/stdio/vfprintf.cpp
index d83a5bf..b7c68dd 100644
--- a/libc/stdio/vfprintf.cpp
+++ b/libc/stdio/vfprintf.cpp
@@ -521,6 +521,34 @@
_umax = UARG();
base = DEC;
goto nosign;
+ case 'w':
+ n = 0;
+ ch = *fmt++;
+ while (is_digit(ch)) {
+ APPEND_DIGIT(n, ch);
+ ch = *fmt++;
+ }
+ switch (n) {
+ case 8: {
+ flags |= CHARINT;
+ goto reswitch;
+ }
+ case 16: {
+ flags |= SHORTINT;
+ goto reswitch;
+ }
+ case 32: {
+ goto reswitch;
+ }
+ case 64: {
+ flags |= LLONGINT;
+ goto reswitch;
+ }
+ default: {
+ __fortify_fatal("%%w%d is unsupported", n);
+ break;
+ }
+ }
case 'X':
xdigs = xdigs_upper;
goto hex;
diff --git a/libc/stdio/vfwprintf.cpp b/libc/stdio/vfwprintf.cpp
index 9819a73..52ae64b 100644
--- a/libc/stdio/vfwprintf.cpp
+++ b/libc/stdio/vfwprintf.cpp
@@ -510,6 +510,34 @@
_umax = UARG();
base = DEC;
goto nosign;
+ case 'w':
+ n = 0;
+ ch = *fmt++;
+ while (is_digit(ch)) {
+ APPEND_DIGIT(n, ch);
+ ch = *fmt++;
+ }
+ switch (n) {
+ case 8: {
+ flags |= CHARINT;
+ goto reswitch;
+ }
+ case 16: {
+ flags |= SHORTINT;
+ goto reswitch;
+ }
+ case 32: {
+ goto reswitch;
+ }
+ case 64: {
+ flags |= LLONGINT;
+ goto reswitch;
+ }
+ default: {
+ __fortify_fatal("%%w%d is unsupported", n);
+ break;
+ }
+ }
case 'X':
xdigs = xdigs_upper;
goto hex;
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 0c71b2a..b68ee7b 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -987,24 +987,25 @@
#endif
Dl_info info;
- void* addr = reinterpret_cast<void*>(puts); // well-known libc function
+ void* addr = reinterpret_cast<void*>(puts); // An arbitrary libc function.
ASSERT_TRUE(dladdr(addr, &info) != 0);
- char libc_realpath[PATH_MAX];
-
// Check if libc is in canonical path or in alternate path.
+ const char* expected_path;
if (strncmp(ALTERNATE_PATH_TO_SYSTEM_LIB,
info.dli_fname,
sizeof(ALTERNATE_PATH_TO_SYSTEM_LIB) - 1) == 0) {
// Platform with emulated architecture. Symlink on ARC++.
- ASSERT_TRUE(realpath(ALTERNATE_PATH_TO_LIBC, libc_realpath) == libc_realpath);
+ expected_path = ALTERNATE_PATH_TO_LIBC;
} else if (strncmp(PATH_TO_BOOTSTRAP_LIBC, info.dli_fname,
sizeof(PATH_TO_BOOTSTRAP_LIBC) - 1) == 0) {
- ASSERT_TRUE(realpath(PATH_TO_BOOTSTRAP_LIBC, libc_realpath) == libc_realpath);
+ expected_path = PATH_TO_BOOTSTRAP_LIBC;
} else {
// /system/lib is symlink when this test is executed on host.
- ASSERT_TRUE(realpath(PATH_TO_LIBC, libc_realpath) == libc_realpath);
+ expected_path = PATH_TO_LIBC;
}
+ char libc_realpath[PATH_MAX];
+ ASSERT_TRUE(realpath(expected_path, libc_realpath) != nullptr) << strerror(errno);
ASSERT_STREQ(libc_realpath, info.dli_fname);
// TODO: add check for dfi_fbase
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 06a0f3d..aad2a4d 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -195,12 +195,12 @@
SKIP_WITH_HWASAN; // TODO(b/148982147): Re-enable when fixed.
size_t stack_size = 640 * 1024;
- std::vector<char> stack_vec(stack_size, '\xff');
- void* stack = stack_vec.data();
+ std::unique_ptr<char[]> stack(new (std::align_val_t(getpagesize())) char[stack_size]);
+ memset(stack.get(), '\xff', stack_size);
pthread_attr_t attr;
ASSERT_EQ(0, pthread_attr_init(&attr));
- ASSERT_EQ(0, pthread_attr_setstack(&attr, stack, stack_size));
+ ASSERT_EQ(0, pthread_attr_setstack(&attr, stack.get(), stack_size));
pthread_t t;
ASSERT_EQ(0, pthread_create(&t, &attr, FnWithStackFrame, nullptr));
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index fb6bce9..0e267c5 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -36,6 +36,7 @@
#include <android-base/file.h>
#include <android-base/silent_death_test.h>
+#include <android-base/strings.h>
#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
@@ -140,6 +141,22 @@
fclose(fp);
}
+TEST(STDIO_TEST, tmpfile_TMPDIR) {
+ TemporaryDir td;
+ setenv("TMPDIR", td.path, 1);
+
+ FILE* fp = tmpfile();
+ ASSERT_TRUE(fp != nullptr);
+
+ std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fileno(fp));
+ char path[PATH_MAX];
+ ASSERT_GT(readlink(fd_path.c_str(), path, sizeof(path)), 0);
+ // $TMPDIR influenced where our temporary file ended up?
+ ASSERT_TRUE(android::base::StartsWith(path, td.path)) << path;
+ // And we used O_TMPFILE, right?
+ ASSERT_TRUE(android::base::EndsWith(path, " (deleted)")) << path;
+}
+
TEST(STDIO_TEST, dprintf) {
TemporaryFile tf;
@@ -2984,59 +3001,153 @@
}
TEST(STDIO_TEST, snprintf_b) {
+#if defined(__BIONIC__)
char buf[BUFSIZ];
- EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%b>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%" PRIb8 ">", b));
EXPECT_STREQ("<101>", buf);
- EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08b>", 5));
+ EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08" PRIb8 ">", b));
EXPECT_STREQ("<00000101>", buf);
- EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%b>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, snprintf(buf, sizeof(buf), "<%" PRIb16 ">", s));
+ EXPECT_STREQ("<1010101010101010>", buf);
+ EXPECT_EQ(20, snprintf(buf, sizeof(buf), "<%#" PRIb16 ">", s));
+ EXPECT_STREQ("<0b1010101010101010>", buf);
+
+ EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%" PRIb32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<10101010101010101010101010101010>", buf);
- EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#b>", 0xaaaaaaaa));
+ EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#" PRIb32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<0b10101010101010101010101010101010>", buf);
+
+ // clang doesn't like "%lb" (https://github.com/llvm/llvm-project/issues/62247)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat"
+ EXPECT_EQ(66, snprintf(buf, sizeof(buf), "<%" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<1010101010101010101010101010101010101010101010101010101010101010>", buf);
+ EXPECT_EQ(68, snprintf(buf, sizeof(buf), "<%#" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<0b1010101010101010101010101010101010101010101010101010101010101010>", buf);
+#pragma clang diagnostic pop
+
EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%#b>", 0));
EXPECT_STREQ("<0>", buf);
+#else
+ GTEST_SKIP() << "no %b in glibc";
+#endif
}
TEST(STDIO_TEST, snprintf_B) {
+#if defined(__BIONIC__)
char buf[BUFSIZ];
- EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%B>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%" PRIB8 ">", b));
EXPECT_STREQ("<101>", buf);
- EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08B>", 5));
+ EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08" PRIB8 ">", b));
EXPECT_STREQ("<00000101>", buf);
- EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%B>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, snprintf(buf, sizeof(buf), "<%" PRIB16 ">", s));
+ EXPECT_STREQ("<1010101010101010>", buf);
+ EXPECT_EQ(20, snprintf(buf, sizeof(buf), "<%#" PRIB16 ">", s));
+ EXPECT_STREQ("<0B1010101010101010>", buf);
+
+ EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%" PRIB32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<10101010101010101010101010101010>", buf);
- EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#B>", 0xaaaaaaaa));
+ EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#" PRIB32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<0B10101010101010101010101010101010>", buf);
- EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%#B>", 0));
+
+ // clang doesn't like "%lB" (https://github.com/llvm/llvm-project/issues/62247)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat"
+ EXPECT_EQ(66, snprintf(buf, sizeof(buf), "<%" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<1010101010101010101010101010101010101010101010101010101010101010>", buf);
+ EXPECT_EQ(68, snprintf(buf, sizeof(buf), "<%#" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<0B1010101010101010101010101010101010101010101010101010101010101010>", buf);
+#pragma clang diagnostic pop
+
+ EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%#b>", 0));
EXPECT_STREQ("<0>", buf);
+#else
+ GTEST_SKIP() << "no %B in glibc";
+#endif
}
TEST(STDIO_TEST, swprintf_b) {
+#if defined(__BIONIC__)
wchar_t buf[BUFSIZ];
- EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%b>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%" PRIb8 ">", b));
EXPECT_EQ(std::wstring(L"<101>"), buf);
- EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08b>", 5));
+ EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08" PRIb8 ">", b));
EXPECT_EQ(std::wstring(L"<00000101>"), buf);
- EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%b>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, swprintf(buf, sizeof(buf), L"<%" PRIb16 ">", s));
+ EXPECT_EQ(std::wstring(L"<1010101010101010>"), buf);
+ EXPECT_EQ(20, swprintf(buf, sizeof(buf), L"<%#" PRIb16 ">", s));
+ EXPECT_EQ(std::wstring(L"<0b1010101010101010>"), buf);
+
+ EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%" PRIb32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010>"), buf);
- EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#b>", 0xaaaaaaaa));
+ EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#" PRIb32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<0b10101010101010101010101010101010>"), buf);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat" // clang doesn't like "%lb"
+ EXPECT_EQ(66, swprintf(buf, sizeof(buf), L"<%" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+ EXPECT_EQ(68, swprintf(buf, sizeof(buf), L"<%#" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<0b1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+#pragma clang diagnostic pop
+
EXPECT_EQ(3, swprintf(buf, sizeof(buf), L"<%#b>", 0));
EXPECT_EQ(std::wstring(L"<0>"), buf);
+#else
+ GTEST_SKIP() << "no %b in glibc";
+#endif
}
TEST(STDIO_TEST, swprintf_B) {
+#if defined(__BIONIC__)
wchar_t buf[BUFSIZ];
- EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%B>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%" PRIB8 ">", b));
EXPECT_EQ(std::wstring(L"<101>"), buf);
- EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08B>", 5));
+ EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08" PRIB8 ">", b));
EXPECT_EQ(std::wstring(L"<00000101>"), buf);
- EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%B>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, swprintf(buf, sizeof(buf), L"<%" PRIB16 ">", s));
+ EXPECT_EQ(std::wstring(L"<1010101010101010>"), buf);
+ EXPECT_EQ(20, swprintf(buf, sizeof(buf), L"<%#" PRIB16 ">", s));
+ EXPECT_EQ(std::wstring(L"<0B1010101010101010>"), buf);
+
+ EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%" PRIB32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010>"), buf);
- EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#B>", 0xaaaaaaaa));
+ EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#" PRIB32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<0B10101010101010101010101010101010>"), buf);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat" // clang doesn't like "%lb"
+ EXPECT_EQ(66, swprintf(buf, sizeof(buf), L"<%" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+ EXPECT_EQ(68, swprintf(buf, sizeof(buf), L"<%#" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<0B1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+#pragma clang diagnostic pop
+
EXPECT_EQ(3, swprintf(buf, sizeof(buf), L"<%#B>", 0));
EXPECT_EQ(std::wstring(L"<0>"), buf);
+#else
+ GTEST_SKIP() << "no %B in glibc";
+#endif
}
TEST(STDIO_TEST, scanf_i_decimal) {
@@ -3164,3 +3275,144 @@
EXPECT_EQ(0, i);
EXPECT_EQ('b', ch);
}
+
+TEST(STDIO_TEST, snprintf_w_base) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wconstant-conversion"
+ char buf[BUFSIZ];
+ int8_t a = 0b101;
+ snprintf(buf, sizeof(buf), "<%w8b>", a);
+ EXPECT_STREQ("<101>", buf);
+ int8_t b1 = 0xFF;
+ snprintf(buf, sizeof(buf), "<%w8d>", b1);
+ EXPECT_STREQ("<-1>", buf);
+ int8_t b2 = 0x1FF;
+ snprintf(buf, sizeof(buf), "<%w8d>", b2);
+ EXPECT_STREQ("<-1>", buf);
+ int16_t c = 0xFFFF;
+ snprintf(buf, sizeof(buf), "<%w16i>", c);
+ EXPECT_STREQ("<-1>", buf);
+ int32_t d = 021;
+ snprintf(buf, sizeof(buf), "<%w32o>", d);
+ EXPECT_STREQ("<21>", buf);
+ uint32_t e = -1;
+ snprintf(buf, sizeof(buf), "<%w32u>", e);
+ EXPECT_STREQ("<4294967295>", buf);
+ int64_t f = 0x3b;
+ snprintf(buf, sizeof(buf), "<%w64x>", f);
+ EXPECT_STREQ("<3b>", buf);
+ snprintf(buf, sizeof(buf), "<%w64X>", f);
+ EXPECT_STREQ("<3B>", buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, snprintf_w_arguments_reordering) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wformat-extra-args"
+ char buf[BUFSIZ];
+ int32_t a = 0xaaaaaaaa;
+ int64_t b = 0x11111111'22222222;
+ int64_t c = 0x33333333'44444444;
+ int64_t d = 0xaaaaaaaa'aaaaaaaa;
+ snprintf(buf, sizeof(buf), "<%2$w32b --- %1$w64x>", c, a);
+ EXPECT_STREQ("<10101010101010101010101010101010 --- 3333333344444444>", buf);
+ snprintf(buf, sizeof(buf), "<%3$w64b --- %1$w64x --- %2$w64x>", b, c, d);
+ EXPECT_STREQ(
+ "<1010101010101010101010101010101010101010101010101010101010101010 --- 1111111122222222 --- "
+ "3333333344444444>",
+ buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, snprintf_invalid_w_width) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+ char buf[BUFSIZ];
+ int32_t a = 100;
+ EXPECT_DEATH(snprintf(buf, sizeof(buf), "%w20d", &a), "%w20 is unsupported");
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_w_base) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wconstant-conversion"
+ wchar_t buf[BUFSIZ];
+ int8_t a = 0b101;
+ swprintf(buf, sizeof(buf), L"<%w8b>", a);
+ EXPECT_EQ(std::wstring(L"<101>"), buf);
+ int8_t b1 = 0xFF;
+ swprintf(buf, sizeof(buf), L"<%w8d>", b1);
+ EXPECT_EQ(std::wstring(L"<-1>"), buf);
+ int8_t b2 = 0x1FF;
+ swprintf(buf, sizeof(buf), L"<%w8d>", b2);
+ EXPECT_EQ(std::wstring(L"<-1>"), buf);
+ int16_t c = 0xFFFF;
+ swprintf(buf, sizeof(buf), L"<%w16i>", c);
+ EXPECT_EQ(std::wstring(L"<-1>"), buf);
+ int32_t d = 021;
+ swprintf(buf, sizeof(buf), L"<%w32o>", d);
+ EXPECT_EQ(std::wstring(L"<21>"), buf);
+ uint32_t e = -1;
+ swprintf(buf, sizeof(buf), L"<%w32u>", e);
+ EXPECT_EQ(std::wstring(L"<4294967295>"), buf);
+ int64_t f = 0x3b;
+ swprintf(buf, sizeof(buf), L"<%w64x>", f);
+ EXPECT_EQ(std::wstring(L"<3b>"), buf);
+ swprintf(buf, sizeof(buf), L"<%w64X>", f);
+ EXPECT_EQ(std::wstring(L"<3B>"), buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_w_arguments_reordering) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wformat-extra-args"
+ wchar_t buf[BUFSIZ];
+ int32_t a = 0xaaaaaaaa;
+ int64_t b = 0x11111111'22222222;
+ int64_t c = 0x33333333'44444444;
+ int64_t d = 0xaaaaaaaa'aaaaaaaa;
+ swprintf(buf, sizeof(buf), L"<%2$w32b --- %1$w64x>", c, a);
+ EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010 --- 3333333344444444>"), buf);
+ swprintf(buf, sizeof(buf), L"<%3$w64b --- %1$w64x --- %2$w64x>", b, c, d);
+ EXPECT_EQ(std::wstring(L"<1010101010101010101010101010101010101010101010101010101010101010 --- "
+ L"1111111122222222 --- 3333333344444444>"),
+ buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_invalid_w_width) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+ wchar_t buf[BUFSIZ];
+ int32_t a = 100;
+ EXPECT_DEATH(swprintf(buf, sizeof(buf), L"%w20d", &a), "%w20 is unsupported");
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
\ No newline at end of file
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index 16d4348..8716810 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -714,7 +714,8 @@
#if defined(__BIONIC__)
wchar_t* p;
size_t size;
-
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
// Invalid buffer.
errno = 0;
ASSERT_EQ(nullptr, open_wmemstream(nullptr, &size));
@@ -724,6 +725,7 @@
errno = 0;
ASSERT_EQ(nullptr, open_wmemstream(&p, nullptr));
ASSERT_EQ(EINVAL, errno);
+#pragma clang diagnostic pop
#else
GTEST_SKIP() << "This test is bionic-specific";
#endif