diff --git a/code/logic/fossil/io/soap.h b/code/logic/fossil/io/soap.h index 6f1a2e7..58339f2 100644 --- a/code/logic/fossil/io/soap.h +++ b/code/logic/fossil/io/soap.h @@ -31,29 +31,93 @@ extern "C" { void fossil_soap_sanitize(char *input); /** - * Check if a word is a curse word or racist phrase. - * This function checks whether a given word is considered offensive. - * It compares the word against a predefined list of offensive words and phrases - * to determine its offensiveness. - * - * @param word The word to be checked for offensiveness. - * @return True if the word is offensive, false otherwise. + * Check if a word is an offensive word or phrase. + * Returns EXIT_FAILURE if the word is considered offensive, EXIT_SUCCESS otherwise. */ int32_t fossil_soap_is_offensive(const char *word); /** - * Get the number of offensive words found in a string. - * This function counts the number of offensive words found in the input string. - * It scans the entire string and checks each word against a list of offensive words - * to determine the count of offensive occurrences. - * - * @param input The input string to be checked for offensive words. - * @return The number of offensive words found. + * Check if a word is meme speak. + * Returns EXIT_FAILURE if the word is considered meme speak, EXIT_SUCCESS otherwise. + */ +int32_t fossil_soap_is_rotbrain(const char *word); + +/** + * Count offensive words in a string. + * Returns the number of offensive words found in the input string. */ int32_t fossil_soap_count_offensive(const char *input); +/** + * Count meme speak words in a string. + * Returns the number of meme speak words found in the input string. + */ +int32_t fossil_soap_count_rotbrain(const char *input); + #ifdef __cplusplus } + +/** + * C++ wrapper for the SOAP API. + */ +namespace fossil { + + /** + * Namespace for I/O operations. + */ + namespace io { + /** + * SOAP API for sanitizing strings. + */ + class Soap { + public: + /** + * Sanitize a string by replacing curse words with asterisks. + * This function scans the input string for offensive words and replaces them with asterisks, + * thereby making the string suitable for use in contexts where offensive language is not allowed. + * The input string is modified in place. + * + * @param input The input string to be sanitized in-place. + */ + static void sanitize(char *input) { + fossil_soap_sanitize(input); + } + + /** + * Check if a word is an offensive word or phrase. + * Returns EXIT_FAILURE if the word is considered offensive, EXIT_SUCCESS otherwise. + */ + static int32_t is_offensive(const char *word) { + return fossil_soap_is_offensive(word); + } + + /** + * Check if a word is meme speak. + * Returns EXIT_FAILURE if the word is considered meme speak, EXIT_SUCCESS otherwise. + */ + static int32_t is_rotbrain(const char *word) { + return fossil_soap_is_rotbrain(word); + } + + /** + * Count offensive words in a string. + * Returns the number of offensive words found in the input string. + */ + static int32_t count_offensive(const char *input) { + return fossil_soap_count_offensive(input); + } + + /** + * Count meme speak words in a string. + * Returns the number of meme speak words found in the input string. + */ + static int32_t count_rotbrain(const char *input) { + return fossil_soap_count_rotbrain(input); + } + }; + } +} + #endif #endif /* FOSSIL_IO_FRAMEWORK_H */ diff --git a/code/logic/soap.c b/code/logic/soap.c index 93df393..e35f2d2 100644 --- a/code/logic/soap.c +++ b/code/logic/soap.c @@ -22,7 +22,7 @@ #include // List of offensive words and phrases (super hard to mainting thisw list as GitHub Copilot doesnt wanna help with this part of the SOAP API) -static const char *offensive_words[] = { +static const char *FOSSIL_SOAP_OFFENSIVE[] = { "curse1", "curse2", "racist_phrase1", @@ -55,7 +55,18 @@ static const char *offensive_words[] = { // Support for other languages can be added via PR to this repository }; -static inline char* _custom_fossil_strdup(const char* str) { +// garbage words and phrases +// (slightly easier to maintain since it's just slang from social media spoken from people who need to touch grass) +static const char *FOSSIL_SOAP_ROTBRAIN[] = { + "rizz", "skibidi", "yeet", "sus", "vibe", "lit", "no cap", "bet", "fam", "bruh", + "flex", "ghost", "goat", "gucci", "hype", "janky", "lowkey", "mood", "salty", "shade", + "slay", "snatched", "stan", "tea", "thirsty", "woke", "yolo", "zaddy", "drip", "fire", + "lol", "omg", "brb", "sus" + + // Support for other terms can be added via PR to this repository +}; + +static inline char* custom_strdup(const char* str) { if (!str) return NULL; // Handle NULL pointer gracefully size_t len = 0; @@ -90,7 +101,7 @@ static char *custom_strcasestr(const char *haystack, const char *needle) { // Function to replace a substring in a string (case-insensitive) static void replace_substring_case_insensitive(char *str, const char *old_substr, const char *new_substr) { char *position = custom_strcasestr(str, old_substr); - if (position != NULL) { + while (position != NULL) { size_t old_len = strlen(old_substr); size_t new_len = strlen(new_substr); size_t tail_len = strlen(position + old_len); @@ -100,8 +111,11 @@ static void replace_substring_case_insensitive(char *str, const char *old_substr memmove(position + new_len, position + old_len, tail_len + 1); } else { memmove(position + new_len, position + old_len, tail_len + 1); - memcpy(position, new_substr, new_len); } + memcpy(position, new_substr, new_len); + + // Find the next occurrence + position = custom_strcasestr(position + new_len, old_substr); } } @@ -109,10 +123,12 @@ void fossil_soap_sanitize(char *input) { if (input == NULL || *input == '\0') return; // Perform single-threaded sanitization - for (size_t i = 0; i < sizeof(offensive_words) / sizeof(offensive_words[0]); ++i) { - while (custom_strcasestr(input, offensive_words[i]) != NULL) { - replace_substring_case_insensitive(input, offensive_words[i], "***"); - } + for (size_t i = 0; i < sizeof(FOSSIL_SOAP_OFFENSIVE) / sizeof(FOSSIL_SOAP_OFFENSIVE[0]); ++i) { + replace_substring_case_insensitive(input, FOSSIL_SOAP_OFFENSIVE[i], "***"); + } + + for (size_t i = 0; i < sizeof(FOSSIL_SOAP_ROTBRAIN) / sizeof(FOSSIL_SOAP_ROTBRAIN[0]); ++i) { + replace_substring_case_insensitive(input, FOSSIL_SOAP_ROTBRAIN[i], "***"); } } @@ -120,8 +136,8 @@ void fossil_soap_sanitize(char *input) { int32_t fossil_soap_is_offensive(const char *word) { if (word == NULL || *word == '\0') return EXIT_SUCCESS; - for (size_t i = 0; i < sizeof(offensive_words) / sizeof(offensive_words[0]); ++i) { - if (strcasecmp(word, offensive_words[i]) == 0) { + for (size_t i = 0; i < sizeof(FOSSIL_SOAP_OFFENSIVE) / sizeof(FOSSIL_SOAP_OFFENSIVE[0]); ++i) { + if (strcasecmp(word, FOSSIL_SOAP_OFFENSIVE[i]) == 0) { return EXIT_FAILURE; } } @@ -133,15 +149,44 @@ int32_t fossil_soap_count_offensive(const char *input) { if (input == NULL || *input == '\0') return 0; int count = 0; - char *copy = _custom_fossil_strdup(input); + char *copy = custom_strdup(input); if (copy == NULL) return EXIT_SUCCESS; - char *token = strtok(copy, " "); // Tokenize the string by space + char *token = strtok(copy, " ,.!?;:"); // Tokenize the string by space and punctuation while (token != NULL) { if (fossil_soap_is_offensive(token)) { count++; } - token = strtok(NULL, " "); + token = strtok(NULL, " ,.!?;:"); + } + free(copy); // Free the memory allocated for the copy + return count; +} + +int32_t fossil_soap_is_rotbrain(const char *word) { + if (word == NULL || *word == '\0') return EXIT_SUCCESS; + + for (size_t i = 0; i < sizeof(FOSSIL_SOAP_ROTBRAIN) / sizeof(FOSSIL_SOAP_ROTBRAIN[0]); ++i) { + if (strcasecmp(word, FOSSIL_SOAP_ROTBRAIN[i]) == 0) { + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + +int32_t fossil_soap_count_rotbrain(const char *input) { + if (input == NULL || *input == '\0') return 0; + + int count = 0; + char *copy = custom_strdup(input); + if (copy == NULL) return EXIT_SUCCESS; + + char *token = strtok(copy, " ,.!?;:"); // Tokenize the string by space and punctuation + while (token != NULL) { + if (fossil_soap_is_rotbrain(token)) { + count++; + } + token = strtok(NULL, " ,.!?;:"); } free(copy); // Free the memory allocated for the copy return count; diff --git a/code/tests/cases/test_soap.c b/code/tests/cases/test_soap.c index 0f166a9..d5126a8 100644 --- a/code/tests/cases/test_soap.c +++ b/code/tests/cases/test_soap.c @@ -63,6 +63,94 @@ FOSSIL_TEST_CASE(c_test_soap_count_offensive) { ASSUME_ITS_EQUAL_I32(2, fossil_soap_count_offensive(input)); } +FOSSIL_TEST_CASE(c_test_soap_is_rotbrain) { + ASSUME_ITS_TRUE(fossil_soap_is_rotbrain("lol")); + ASSUME_ITS_TRUE(fossil_soap_is_rotbrain("brb")); + ASSUME_ITS_FALSE(fossil_soap_is_rotbrain("hello")); +} + +FOSSIL_TEST_CASE(c_test_soap_count_rotbrain) { + char input[] = "This is a test with lol and brb"; + ASSUME_ITS_EQUAL_I32(2, fossil_soap_count_rotbrain(input)); +} + +FOSSIL_TEST_CASE(c_test_soap_sanitize_multiple_offensive) { + char input[] = "curse1 curse2 racist_phrase1 racist_phrase2"; + char expected[] = "*** *** *** ***"; + + fossil_soap_sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + +FOSSIL_TEST_CASE(c_test_soap_sanitize_no_offensive) { + char input[] = "This is a clean sentence."; + char expected[] = "This is a clean sentence."; + + fossil_soap_sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + +FOSSIL_TEST_CASE(c_test_soap_is_offensive_case_insensitive) { + ASSUME_ITS_TRUE(fossil_soap_is_offensive("CuRsE1")); + ASSUME_ITS_TRUE(fossil_soap_is_offensive("RaCiSt_PhrAsE2")); + ASSUME_ITS_FALSE(fossil_soap_is_offensive("Non_Offensive_Word")); +} + +FOSSIL_TEST_CASE(c_test_soap_count_offensive_mixed_case) { + char input[] = "This is a test with CuRsE1 and RaCiSt_PhrAsE1"; + ASSUME_ITS_EQUAL_I32(2, fossil_soap_count_offensive(input)); +} + +FOSSIL_TEST_CASE(c_test_soap_is_rotbrain_case_insensitive) { + ASSUME_ITS_TRUE(fossil_soap_is_rotbrain("LoL")); + ASSUME_ITS_TRUE(fossil_soap_is_rotbrain("BrB")); + ASSUME_ITS_FALSE(fossil_soap_is_rotbrain("Hello")); +} + +FOSSIL_TEST_CASE(c_test_soap_count_rotbrain_mixed_case) { + char input[] = "This is a test with LoL and BrB"; + ASSUME_ITS_EQUAL_I32(2, fossil_soap_count_rotbrain(input)); +} + +FOSSIL_TEST_CASE(c_test_soap_sanitize_synonyms) { + char input[] = "This is a test with rizz and sus."; + char expected[] = "This is a test with *** and ***."; + + fossil_soap_sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + +FOSSIL_TEST_CASE(c_test_soap_sanitize_with_punctuation) { + char input[] = "This is a test with curse1, and racist_phrase1!"; + char expected[] = "This is a test with ***, and ***!"; + + fossil_soap_sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + +FOSSIL_TEST_CASE(c_test_soap_count_offensive_with_punctuation) { + char input[] = "This is a test with curse1, and racist_phrase1!"; + ASSUME_ITS_EQUAL_I32(2, fossil_soap_count_offensive(input)); +} + +FOSSIL_TEST_CASE(c_test_soap_sanitize_rotbrain_with_punctuation) { + char input[] = "This is a test with lol, and brb!"; + char expected[] = "This is a test with ***, and ***!"; + + fossil_soap_sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + +FOSSIL_TEST_CASE(c_test_soap_count_rotbrain_with_punctuation) { + char input[] = "This is a test with lol, and brb!"; + ASSUME_ITS_EQUAL_I32(2, fossil_soap_count_rotbrain(input)); +} + // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Pool // * * * * * * * * * * * * * * * * * * * * * * * * @@ -71,6 +159,19 @@ FOSSIL_TEST_GROUP(c_soap_tests) { FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_sanitize); FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_is_offensive); FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_count_offensive); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_is_rotbrain); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_count_rotbrain); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_sanitize_multiple_offensive); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_sanitize_no_offensive); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_is_offensive_case_insensitive); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_count_offensive_mixed_case); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_is_rotbrain_case_insensitive); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_count_rotbrain_mixed_case); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_sanitize_synonyms); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_sanitize_with_punctuation); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_count_offensive_with_punctuation); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_sanitize_rotbrain_with_punctuation); + FOSSIL_TEST_ADD(c_soap_suite, c_test_soap_count_rotbrain_with_punctuation); FOSSIL_TEST_REGISTER(c_soap_suite); } diff --git a/code/tests/cases/test_soap.cpp b/code/tests/cases/test_soap.cpp index 773fcac..ab3a1bd 100644 --- a/code/tests/cases/test_soap.cpp +++ b/code/tests/cases/test_soap.cpp @@ -15,6 +15,8 @@ #include "fossil/io/framework.h" +using namespace fossil::io; + // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Utilites // * * * * * * * * * * * * * * * * * * * * * * * * @@ -63,6 +65,136 @@ FOSSIL_TEST_CASE(cpp_test_soap_count_offensive) { ASSUME_ITS_EQUAL_I32(2, fossil_soap_count_offensive(input)); } +FOSSIL_TEST_CASE(cpp_test_soap_is_rotbrain) { + ASSUME_ITS_TRUE(fossil_soap_is_rotbrain("lol")); + ASSUME_ITS_TRUE(fossil_soap_is_rotbrain("brb")); + ASSUME_ITS_FALSE(fossil_soap_is_rotbrain("hello")); +} + +FOSSIL_TEST_CASE(cpp_test_soap_count_rotbrain) { + char input[] = "This is a test with lol and brb"; + ASSUME_ITS_EQUAL_I32(2, fossil_soap_count_rotbrain(input)); +} + +FOSSIL_TEST_CASE(cpp_test_soap_sanitize_multiple_offensive) { + char input[] = "curse1 curse2 racist_phrase1 racist_phrase2"; + char expected[] = "*** *** *** ***"; + + fossil_soap_sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + +FOSSIL_TEST_CASE(cpp_test_soap_sanitize_no_offensive) { + char input[] = "This is a clean sentence."; + char expected[] = "This is a clean sentence."; + + fossil_soap_sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + +FOSSIL_TEST_CASE(cpp_test_soap_is_offensive_case_insensitive) { + ASSUME_ITS_TRUE(fossil_soap_is_offensive("CuRsE1")); + ASSUME_ITS_TRUE(fossil_soap_is_offensive("RaCiSt_PhrAsE2")); + ASSUME_ITS_FALSE(fossil_soap_is_offensive("Non_Offensive_Word")); +} + +FOSSIL_TEST_CASE(cpp_test_soap_count_offensive_mixed_case) { + char input[] = "This is a test with CuRsE1 and RaCiSt_PhrAsE1"; + ASSUME_ITS_EQUAL_I32(2, fossil_soap_count_offensive(input)); +} + +FOSSIL_TEST_CASE(cpp_test_soap_is_rotbrain_case_insensitive) { + ASSUME_ITS_TRUE(fossil_soap_is_rotbrain("LoL")); + ASSUME_ITS_TRUE(fossil_soap_is_rotbrain("BrB")); + ASSUME_ITS_FALSE(fossil_soap_is_rotbrain("Hello")); +} + +FOSSIL_TEST_CASE(cpp_test_soap_count_rotbrain_mixed_case) { + char input[] = "This is a test with LoL and BrB"; + ASSUME_ITS_EQUAL_I32(2, fossil_soap_count_rotbrain(input)); +} + +FOSSIL_TEST_CASE(cpp_test_soap_sanitize_synonyms) { + char input[] = "This is a test with rizz and sus."; + char expected[] = "This is a test with *** and ***."; + + fossil_soap_sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + +FOSSIL_TEST_CASE(cpp_test_soap_count_rotbrain_with_punctuation) { + char input[] = "This is a test with lol, and brb!"; + ASSUME_ITS_EQUAL_I32(2, fossil_soap_count_rotbrain(input)); +} + +FOSSIL_TEST_CASE(cpp_test_soap_sanitize_empty_string) { + char input[] = ""; + char expected[] = ""; + + fossil::io::Soap::sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + +FOSSIL_TEST_CASE(cpp_test_soap_sanitize_only_offensive) { + char input[] = "curse1"; + char expected[] = "***"; + + fossil::io::Soap::sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + +FOSSIL_TEST_CASE(cpp_test_soap_is_offensive_empty_string) { + ASSUME_ITS_FALSE(fossil::io::Soap::is_offensive("")); +} + +FOSSIL_TEST_CASE(cpp_test_soap_is_rotbrain_empty_string) { + ASSUME_ITS_FALSE(fossil::io::Soap::is_rotbrain("")); +} + +FOSSIL_TEST_CASE(cpp_test_soap_count_offensive_empty_string) { + char input[] = ""; + ASSUME_ITS_EQUAL_I32(0, fossil::io::Soap::count_offensive(input)); +} + +FOSSIL_TEST_CASE(cpp_test_soap_count_rotbrain_empty_string) { + char input[] = ""; + ASSUME_ITS_EQUAL_I32(0, fossil::io::Soap::count_rotbrain(input)); +} + +FOSSIL_TEST_CASE(cpp_test_soap_sanitize_mixed_content) { + char input[] = "This is a test with curse1, lol, and non_offensive_word."; + char expected[] = "This is a test with ***, ***, and non_offensive_word."; + + fossil::io::Soap::sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + +FOSSIL_TEST_CASE(cpp_test_soap_count_offensive_mixed_content) { + char input[] = "This is a test with curse1, curse2, and non_offensive_word."; + ASSUME_ITS_EQUAL_I32(2, fossil::io::Soap::count_offensive(input)); +} + +FOSSIL_TEST_CASE(cpp_test_soap_count_rotbrain_mixed_content) { + char input[] = "This is a test with lol, brb, and non_offensive_word."; + ASSUME_ITS_EQUAL_I32(2, fossil::io::Soap::count_rotbrain(input)); +} + +FOSSIL_TEST_CASE(cpp_test_soap_sanitize_with_punctuation) { + char input[] = "curse1! curse2? racist_phrase1."; + char expected[] = "***! ***? ***."; + + fossil::io::Soap::sanitize(input); + + ASSUME_ITS_EQUAL_CSTR(expected, input); +} + + // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Pool // * * * * * * * * * * * * * * * * * * * * * * * * @@ -71,6 +203,26 @@ FOSSIL_TEST_GROUP(cpp_soap_tests) { FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_sanitize); FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_is_offensive); FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_count_offensive); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_is_rotbrain); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_count_rotbrain); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_sanitize_multiple_offensive); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_sanitize_no_offensive); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_is_offensive_case_insensitive); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_count_offensive_mixed_case); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_is_rotbrain_case_insensitive); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_count_rotbrain_mixed_case); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_sanitize_synonyms); + + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_sanitize_empty_string); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_sanitize_only_offensive); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_is_offensive_empty_string); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_is_rotbrain_empty_string); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_count_offensive_empty_string); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_count_rotbrain_empty_string); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_sanitize_mixed_content); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_count_offensive_mixed_content); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_count_rotbrain_mixed_content); + FOSSIL_TEST_ADD(cpp_soap_suite, cpp_test_soap_sanitize_with_punctuation); FOSSIL_TEST_REGISTER(cpp_soap_suite); }