diff --git a/libc/config/baremetal/entrypoints.txt b/libc/config/baremetal/entrypoints.txt index db645c06fe265..5dc233ef8dd0b 100644 --- a/libc/config/baremetal/entrypoints.txt +++ b/libc/config/baremetal/entrypoints.txt @@ -32,6 +32,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.stpcpy libc.src.string.stpncpy libc.src.string.strcasecmp + libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt index cf2931963cfa8..a1ad122e13c7f 100644 --- a/libc/config/darwin/arm/entrypoints.txt +++ b/libc/config/darwin/arm/entrypoints.txt @@ -32,6 +32,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.stpcpy libc.src.string.stpncpy libc.src.string.strcasecmp + libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt index 57b314f3d231f..ee39c6426a366 100644 --- a/libc/config/gpu/entrypoints.txt +++ b/libc/config/gpu/entrypoints.txt @@ -31,6 +31,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.stpcpy libc.src.string.stpncpy libc.src.string.strcasecmp + libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 7223975eb8355..b9460acaa77f5 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -41,6 +41,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.stpcpy libc.src.string.stpncpy libc.src.string.strcasecmp + libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt index f4228f1201460..98d00aa5c9c93 100644 --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -32,6 +32,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.stpcpy libc.src.string.stpncpy libc.src.string.strcasecmp + libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 2c1c938c5fff5..7b12ec710100b 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -41,6 +41,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.stpcpy libc.src.string.stpncpy libc.src.string.strcasecmp + libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt index 01c33f9d68d46..61cf5a15c6ab6 100644 --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -32,6 +32,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.string.stpcpy libc.src.string.stpncpy libc.src.string.strcasecmp + libc.src.string.strcasestr libc.src.string.strcat libc.src.string.strchr libc.src.string.strcmp diff --git a/libc/spec/gnu_ext.td b/libc/spec/gnu_ext.td index 239790bb9bdc5..f3311e3a782de 100644 --- a/libc/spec/gnu_ext.td +++ b/libc/spec/gnu_ext.td @@ -68,6 +68,11 @@ def GnuExtensions : StandardSpec<"GNUExtensions"> { RetValSpec, [ArgSpec, ArgSpec, ArgSpec] >, + FunctionSpec< + "strcasestr", + RetValSpec, + [ArgSpec, ArgSpec] + >, ] >; diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt index 9c580dcaae1aa..944889a86bde6 100644 --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -127,6 +127,17 @@ add_entrypoint_object( libc.src.__support.ctype_utils ) +add_entrypoint_object( + strcasestr + SRCS + strcasestr.cpp + HDRS + strcasestr.h + DEPENDS + .memory_utils.strstr_implementation + libc.src.__support.ctype_utils +) + add_entrypoint_object( strcoll SRCS diff --git a/libc/src/string/strcasestr.cpp b/libc/src/string/strcasestr.cpp new file mode 100644 index 0000000000000..a1272422f294d --- /dev/null +++ b/libc/src/string/strcasestr.cpp @@ -0,0 +1,28 @@ +//===-- Implementation of strcasestr --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/string/strcasestr.h" + +#include "src/__support/common.h" +#include "src/__support/ctype_utils.h" +#include "src/string/memory_utils/strstr_implementations.h" + +namespace __llvm_libc { + +// TODO: This is a simple brute force implementation. This can be +// improved upon using well known string matching algorithms. +LLVM_LIBC_FUNCTION(char *, strcasestr, + (const char *haystack, const char *needle)) { + auto case_cmp = [](char a, char b) { + return __llvm_libc::internal::tolower(a) - + __llvm_libc::internal::tolower(b); + }; + return strstr_implementation(haystack, needle, case_cmp); +} + +} // namespace __llvm_libc diff --git a/libc/src/string/strcasestr.h b/libc/src/string/strcasestr.h new file mode 100644 index 0000000000000..8c4bc51d38a28 --- /dev/null +++ b/libc/src/string/strcasestr.h @@ -0,0 +1,18 @@ +//===-- Implementation header for strcasestr --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STRING_STRCASECMP_H +#define LLVM_LIBC_SRC_STRING_STRCASECMP_H + +namespace __llvm_libc { + +char *strcasestr(const char *needle, const char *haystack); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRCASECMP_H diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt index 99dfabbd93a39..4c9ab58bd3846 100644 --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -114,6 +114,16 @@ add_libc_unittest( libc.src.string.strcasecmp ) +add_libc_unittest( + strcasestr_test + SUITE + libc_string_unittests + SRCS + strcasestr_test.cpp + DEPENDS + libc.src.string.strcasestr +) + add_libc_unittest( strcoll_test SUITE diff --git a/libc/test/src/string/strcasestr_test.cpp b/libc/test/src/string/strcasestr_test.cpp new file mode 100644 index 0000000000000..313ad8ebee665 --- /dev/null +++ b/libc/test/src/string/strcasestr_test.cpp @@ -0,0 +1,24 @@ +//===-- Unittests for strcasestr ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/string/strcasestr.h" +#include "utils/UnitTest/Test.h" + +TEST(LlvmLibcStrCaseStrTest, NeedleNotInHaystack) { + EXPECT_STREQ(__llvm_libc::strcasestr("abcd", "e"), nullptr); + EXPECT_STREQ(__llvm_libc::strcasestr("ABCD", "e"), nullptr); + EXPECT_STREQ(__llvm_libc::strcasestr("abcd", "E"), nullptr); + EXPECT_STREQ(__llvm_libc::strcasestr("ABCD", "E"), nullptr); +} + +TEST(LlvmLibcStrCaseStrTest, NeedleInMiddle) { + EXPECT_STREQ(__llvm_libc::strcasestr("abcdefghi", "def"), "defghi"); + EXPECT_STREQ(__llvm_libc::strcasestr("ABCDEFGHI", "def"), "DEFGHI"); + EXPECT_STREQ(__llvm_libc::strcasestr("abcdefghi", "DEF"), "defghi"); + EXPECT_STREQ(__llvm_libc::strcasestr("ABCDEFGHI", "DEF"), "DEFGHI"); +}