Skip to content

Commit b36f05c

Browse files
authored
[libc] Fix wcstok() "subsequent searches" behavior. (#151589)
POSIX says "If no such wide-character code is found, the current token extends to the end of the wide-character string pointed to by ws1, and subsequent searches for a token shall return a null pointer", but the current implementation only returns nullptr the first time. This failed an existing bionic test when I tried to switch over to llvm-libc wcstok().
1 parent afce932 commit b36f05c

File tree

2 files changed

+17
-4
lines changed

2 files changed

+17
-4
lines changed

libc/src/wchar/wcstok.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,22 @@ LLVM_LIBC_FUNCTION(wchar_t *, wcstok,
2727
wchar_t *tok_start = str;
2828
while (*tok_start != L'\0' && internal::wcschr(delims, *tok_start))
2929
++tok_start;
30+
if (*tok_start == L'\0') {
31+
*context = nullptr;
32+
return nullptr;
33+
}
3034

3135
wchar_t *tok_end = tok_start;
3236
while (*tok_end != L'\0' && !internal::wcschr(delims, *tok_end))
3337
++tok_end;
3438

35-
if (*tok_end != L'\0') {
39+
if (*tok_end == L'\0') {
40+
*context = nullptr;
41+
} else {
3642
*tok_end = L'\0';
37-
++tok_end;
43+
*context = tok_end + 1;
3844
}
39-
*context = tok_end;
40-
return *tok_start == L'\0' ? nullptr : tok_start;
45+
return tok_start;
4146
}
4247

4348
} // namespace LIBC_NAMESPACE_DECL

libc/test/src/wchar/wcstok_test.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ TEST(LlvmLibcWCSTokReentrantTest, NoTokenFound) {
1919
// Another call to ensure that 'reserve' is not in a bad state.
2020
ASSERT_EQ(LIBC_NAMESPACE::wcstok(empty, L"", &reserve), nullptr);
2121
ASSERT_EQ(LIBC_NAMESPACE::wcstok(nullptr, L"", &reserve), nullptr);
22+
// Subsequent searches still return nullptr.
23+
ASSERT_EQ(LIBC_NAMESPACE::wcstok(nullptr, L"", &reserve), nullptr);
2224
}
2325
{ // Empty source and single character delimiter string.
2426
wchar_t empty[] = L"";
@@ -27,6 +29,8 @@ TEST(LlvmLibcWCSTokReentrantTest, NoTokenFound) {
2729
// Another call to ensure that 'reserve' is not in a bad state.
2830
ASSERT_EQ(LIBC_NAMESPACE::wcstok(empty, L"_", &reserve), nullptr);
2931
ASSERT_EQ(LIBC_NAMESPACE::wcstok(nullptr, L"_", &reserve), nullptr);
32+
// Subsequent searches still return nullptr.
33+
ASSERT_EQ(LIBC_NAMESPACE::wcstok(nullptr, L"_", &reserve), nullptr);
3034
}
3135
{ // Same character source and delimiter string.
3236
wchar_t single[] = L"_";
@@ -35,6 +39,8 @@ TEST(LlvmLibcWCSTokReentrantTest, NoTokenFound) {
3539
// Another call to ensure that 'reserve' is not in a bad state.
3640
ASSERT_EQ(LIBC_NAMESPACE::wcstok(single, L"_", &reserve), nullptr);
3741
ASSERT_EQ(LIBC_NAMESPACE::wcstok(nullptr, L"_", &reserve), nullptr);
42+
// Subsequent searches still return nullptr.
43+
ASSERT_EQ(LIBC_NAMESPACE::wcstok(nullptr, L"_", &reserve), nullptr);
3844
}
3945
{ // Multiple character source and single character delimiter string.
4046
wchar_t multiple[] = L"1,2";
@@ -51,6 +57,8 @@ TEST(LlvmLibcWCSTokReentrantTest, NoTokenFound) {
5157
ASSERT_TRUE(tok[2] == L'2');
5258
ASSERT_TRUE(tok[3] == L'\0');
5359
ASSERT_EQ(LIBC_NAMESPACE::wcstok(nullptr, L":", &reserve), nullptr);
60+
// Subsequent searches still return nullptr.
61+
ASSERT_EQ(LIBC_NAMESPACE::wcstok(nullptr, L":", &reserve), nullptr);
5462
}
5563
}
5664

0 commit comments

Comments
 (0)