diff --git a/src/libc/include/string.h b/src/libc/include/string.h index 67cc527ec..2fb9fcae0 100644 --- a/src/libc/include/string.h +++ b/src/libc/include/string.h @@ -86,6 +86,9 @@ char *strcasestr(const char *haystack, const char *needle) char *strtok(char *__restrict s, const char *__restrict delim) __attribute__((nonnull(2))); +char *strsep(char **__restrict s, const char *__restrict delim) + __NOEXCEPT __attribute__((nonnull(1, 2))); + char *strdup(const char *s) __attribute__((__malloc__)) __attribute__((nonnull(1))); diff --git a/src/libc/strsep.src b/src/libc/strsep.src new file mode 100644 index 000000000..12c66fc3b --- /dev/null +++ b/src/libc/strsep.src @@ -0,0 +1,46 @@ + assume adl=1 + + section .text + + public _strsep + +; char *strsep(char **__restrict stringp, const char *__restrict delim) +_strsep: + pop bc + pop hl + pop de + push de + push hl + push bc + push hl ; stringp + ld hl, (hl) + add hl, de + or a, a + sbc hl, de + jr z, .ret_null + push de ; delim + push hl ; *stringp + call _strcspn + pop de + pop de + pop iy ; stringp + ld de, (iy) + add hl, de ; *stringp + length + xor a, a + cp a, (hl) ; test for the nul terminator + jr nz, .not_last_token + sbc hl, hl ; last token + jr .finish +.not_last_token: + ld (hl), a ; nul terminate + inc hl ; next token +.finish: + ld (iy), hl ; store the next token + ex de, hl + ; return *stringp + ret +.ret_null: + pop de + ret + + extern _strcspn diff --git a/src/libcxx/include/cstring b/src/libcxx/include/cstring index 7adbf29d1..42883cbeb 100644 --- a/src/libcxx/include/cstring +++ b/src/libcxx/include/cstring @@ -36,6 +36,7 @@ using ::strstr; using ::strrstr; using ::strcasestr; using ::strtok; +using ::strsep; using ::strdup; using ::strndup; using ::strcspn; diff --git a/test/standalone/asprintf_fprintf/src/main.c b/test/standalone/asprintf_fprintf/src/main.c index 181d5f8f7..2150250b6 100644 --- a/test/standalone/asprintf_fprintf/src/main.c +++ b/test/standalone/asprintf_fprintf/src/main.c @@ -8,28 +8,46 @@ #include #include -/** -* @brief Tests the following functions/macros: -* boot_sprintf -* boot_snprintf -* boot_asprintf -* asprintf -* fprintf -* stpcpy -* memccpy -*/ +//------------------------------------------------------------------------------ +// Config +//------------------------------------------------------------------------------ + +// define to 0 or 1 +#define DEBUG_DIAGNOSTICS 0 + +// define to 0 or 1 (prevents Clang from replacing functions with builtins) +#define RENAME_FUNCTIONS 1 + +//------------------------------------------------------------------------------ +// Utilities +//------------------------------------------------------------------------------ #define C(expr) if (!(expr)) { return __LINE__; } -#define TEST(test) { ret = test; if (ret != 0) { return ret; }} +#define TEST(test) { ret = test; if (ret != 0) { return ret; } } #define SINK (char*)0xE40000 +#ifndef DEBUG_DIAGNOSTICS +#error "DEBUG_DIAGNOSTICS needs to be defined to 0 or 1" +#endif + +#if DEBUG_DIAGNOSTICS +#define test_printf printf +#else +#define test_printf(...) +#endif + /* pass NULL into functions without triggering -Wnonnull */ -extern void* NULL_ptr; +extern void * NULL_ptr; +extern size_t ZERO_size; + +//------------------------------------------------------------------------------ +// Functions +//------------------------------------------------------------------------------ // prevents Clang from replacing function calls with builtins -#if 1 +#if RENAME_FUNCTIONS void *T_memcpy(void *__restrict dest, const void *__restrict src, size_t n) __attribute__((nonnull(1, 2))); @@ -82,6 +100,9 @@ char *T_strchrnul(const char *s, int c) char *T_strrstr(const char *haystack, const char *needle) __attribute__((nonnull(1, 2))); +char *T_strsep(char **__restrict s, const char *__restrict delim) + __attribute__((nonnull(1, 2))); + void T_bzero(void* s, size_t n); #else @@ -103,10 +124,15 @@ void T_bzero(void* s, size_t n); #define T_strncmp strncmp #define T_strchrnul strchrnul #define T_strrstr strrstr +#define T_strsep strsep #define T_bzero bzero #endif +//------------------------------------------------------------------------------ +// Globals +//------------------------------------------------------------------------------ + const char gnu_copypasta[] = "I would just like to interject for a moment. What you're referring to "\ "as Linux, is in fact, GNU/Linux, or as I\'ve recently taken to calling "\ @@ -131,6 +157,10 @@ static const int pos_4 = 212; static char* buf = NULL; static FILE* file = NULL; +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + int boot_sprintf_tests(void) { int pos; int len = boot_asprintf( @@ -138,7 +168,7 @@ int boot_sprintf_tests(void) { 123, "asprintf", 076543, 0x9abcd, &pos, 0xFE1, 0 ); if (buf == NULL || len <= 0) { - printf("buf %p len %d\n", buf, len); + test_printf("buf %p len %d\n", buf, len); return __LINE__; } if (buf[len] != '\0') { @@ -146,27 +176,27 @@ int boot_sprintf_tests(void) { } size_t buf_len = T_strlen(buf); if (buf_len != T_strlen(test_1) || buf_len != (size_t)len) { - printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); + test_printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); return __LINE__; } if (pos != pos_1) { - printf("E: %d != %d\n", pos, pos_1); + test_printf("E: %d != %d\n", pos, pos_1); return __LINE__; } int cmp = strcmp(buf, test_1); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } char append[128]; int snprintf_test = boot_snprintf(append, 20, "%s", test_1); if (snprintf_test != (int)T_strlen(test_1)) { - printf("sprintf_test: %d\n", snprintf_test); + test_printf("sprintf_test: %d\n", snprintf_test); return __LINE__; } int len_2 = boot_snprintf(append, sizeof(append), "%s", test_1); if (len_2 != (int)T_strlen(test_1)) { - printf("E: %d != %zu\n", len_2, T_strlen(test_1)); + test_printf("E: %d != %zu\n", len_2, T_strlen(test_1)); return __LINE__; } char str2[128]; @@ -178,18 +208,18 @@ int boot_sprintf_tests(void) { return __LINE__; } if (end != &str2[pos_2]) { - printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); + test_printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); return __LINE__; } int cmp2 = T_strcmp(str2, test_2); if (cmp2 != 0) { - printf("cmp: %d\n", cmp2); + test_printf("cmp: %d\n", cmp2); return __LINE__; } char buf_3[30]; int len_3 = boot_snprintf(buf_3, sizeof(buf_3), test_3); if (len_3 != pos_3) { - printf("E: %d != %d\n", len_3, pos_3); + test_printf("E: %d != %d\n", len_3, pos_3); return __LINE__; } @@ -222,12 +252,12 @@ int boot_sprintf_tests(void) { 5, 10 ); if (len_4 != pos_4) { - printf("E: %d != %d\n", len_4, pos_4); + test_printf("E: %d != %d\n", len_4, pos_4); return __LINE__; } int len_5 = boot_snprintf(SINK, 10, ""); if (len_5 != 0) { - printf("E: %d != 0\n", len_5); + test_printf("E: %d != 0\n", len_5); return __LINE__; } @@ -244,7 +274,7 @@ int nano_tests(void) { 123, "asprintf", 076543, 0x9abcd, &pos, 0xFE1, 0 ); if (buf == NULL || len <= 0) { - printf("buf %p len %d\n", buf, len); + test_printf("buf %p len %d\n", buf, len); return __LINE__; } if (buf[len] != '\0') { @@ -252,27 +282,27 @@ int nano_tests(void) { } size_t buf_len = T_strlen(buf); if (buf_len != T_strlen(test_1) || buf_len != (size_t)len) { - printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); + test_printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); return __LINE__; } if (pos != pos_1) { - printf("E: %d != %d\n", pos, pos_1); + test_printf("E: %d != %d\n", pos, pos_1); return __LINE__; } int cmp = strcmp(buf, test_1); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } char append[128]; int snprintf_test = snprintf(append, 20, "%s", test_1); if (snprintf_test != (int)T_strlen(test_1)) { - printf("sprintf_test: %d\n", snprintf_test); + test_printf("sprintf_test: %d\n", snprintf_test); return __LINE__; } int len_2 = snprintf(append, sizeof(append), "%s", test_1); if (len_2 != (int)T_strlen(test_1)) { - printf("E: %d != %zu\n", len_2, T_strlen(test_1)); + test_printf("E: %d != %zu\n", len_2, T_strlen(test_1)); return __LINE__; } char str2[128]; @@ -284,24 +314,24 @@ int nano_tests(void) { return __LINE__; } if (end != &str2[pos_2]) { - printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); + test_printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); return __LINE__; } int cmp2 = T_strcmp(str2, test_2); if (cmp2 != 0) { - printf("cmp: %d\n", cmp2); + test_printf("cmp: %d\n", cmp2); return __LINE__; } char buf_30[30]; int len_3sn = snprintf(buf_30, sizeof(buf_30), test_3); if (len_3sn != pos_3) { - printf("E: %d != %d\n", len_3sn, pos_3); + test_printf("E: %d != %d\n", len_3sn, pos_3); return __LINE__; } int len_3s = sprintf(buf_30, test_3); if (len_3s != pos_3) { - printf("E: %d != %d\n", len_3s, pos_3); + test_printf("E: %d != %d\n", len_3s, pos_3); return __LINE__; } @@ -334,12 +364,12 @@ int nano_tests(void) { 5, 10 ); if (len_4 != pos_4) { - printf("E: %d != %d\n", len_4, pos_4); + test_printf("E: %d != %d\n", len_4, pos_4); return __LINE__; } int len_5 = snprintf(SINK, 10, ""); if (len_5 != 0) { - printf("E: %d != 0\n", len_5); + test_printf("E: %d != 0\n", len_5); return __LINE__; } @@ -389,7 +419,7 @@ int memccpy_tests(void) { // test zero byte case void* ptr = T_memccpy((void*)0xC0FFEE, (void*)0x123456, 123, 0); if (ptr != NULL) { - printf("%p != NULL\n", ptr); + test_printf("%p != NULL\n", ptr); return __LINE__; } file = fopen(file_name, "wb"); @@ -405,11 +435,10 @@ int memccpy_tests(void) { char dest[sizeof src]; const char alt = '@'; - for (size_t i = 0; i != sizeof terminal; ++i) - { + for (size_t i = 0; i != sizeof terminal; ++i) { void* to = T_memccpy(dest, src, terminal[i], sizeof dest); - fprintf(file,"Terminal '%c' (%s):\t\"", terminal[i], to ? "found" : "absent"); + fprintf(file, "Terminal '%c' (%s):\t\"", terminal[i], to ? "found" : "absent"); // if `terminal` character was not found - print the whole `dest` to = to ? to : dest + sizeof dest; @@ -421,7 +450,6 @@ int memccpy_tests(void) { fputs("\"\n", file); } - fprintf(file, "%c%s", '\n', "Separate star names from distances (ly):\n"); const char *star_distance[] = { "Arcturus : 37", "Vega : 25", "Capella : 43", "Rigel : 860", "Procyon : 11" @@ -430,8 +458,7 @@ int memccpy_tests(void) { char *first = names_only; char *last = names_only + sizeof names_only; - for (size_t t = 0; t != (sizeof star_distance) / (sizeof star_distance[0]); ++t) - { + for (size_t t = 0; t != (sizeof star_distance) / (sizeof star_distance[0]); ++t) { if (first) { first = T_memccpy(first, star_distance[t], ' ', last - first); } else { @@ -490,7 +517,7 @@ int mempcpy_test(void) { // test zero byte case void* ptr = T_mempcpy((void*)0xC0FFEE, (void*)0x123456, 0); if (ptr != (void*)0xC0FFEE) { - printf("%p != %p\n", ptr, (void*)0xC0FFEE); + test_printf("%p != %p\n", ptr, (void*)0xC0FFEE); return __LINE__; } char data[192 + 1]; @@ -501,7 +528,7 @@ int mempcpy_test(void) { T_memset(append, 0x34, 64); char* res = T_mempcpy(&data[64], append, 64); if (res != &data[128]) { - printf("%p != %p\n", res, &data[128]); + test_printf("%p != %p\n", res, &data[128]); return __LINE__; } @@ -512,7 +539,7 @@ int mempcpy_test(void) { int cmp = T_memcmp(data, truth, 192); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } return 0; @@ -535,7 +562,7 @@ int bzero_test(void) { T_memset(&truth[ 2], 0x00, 1); T_memset(&truth[ 8], 0x00, 17); T_memset(&truth[25], 0x8F, 7); - T_bzero(&data[2], 0); + T_bzero(&data[2], ZERO_size); T_bzero(&data[2], 1); if (T_strlen(&data[0]) != 2) { return __LINE__; @@ -546,11 +573,11 @@ int bzero_test(void) { if (T_strlen(&data[2]) != 0) { return __LINE__; } - T_bzero(NULL, 0); + T_bzero(NULL_ptr, ZERO_size); T_bzero(&data[8], 17); int cmp = T_memcmp(data, truth, 32); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } return 0; @@ -994,8 +1021,78 @@ int strchrnul_test(void) { return 0; } +static char *truth_strsep(char **__restrict stringp, const char *__restrict delim) { + char * const begin_str = *stringp; + if (begin_str == NULL) { + return NULL; + } + size_t length = strcspn(begin_str, delim); + char * end_str = begin_str + length; + if (*end_str != '\0') { + *end_str++ = '\0'; + *stringp = end_str; + } else { + *stringp = NULL; + } + return begin_str; +} + +static int strsep_loop_test(char * str_1, char * str_2) { + for (;;) { + char *__restrict prev_1 = T_strsep(&str_1, ", ."); + char *__restrict prev_2 = truth_strsep(&str_2, ", ."); + if (str_1 == NULL || str_2 == NULL) { + break; + } + ptrdiff_t diff_1 = (str_1 - prev_1); + ptrdiff_t diff_2 = (str_2 - prev_2); + if (diff_1 != diff_2) { + test_printf("%td != %td\n%p %p\n1: %.20s\n2: %.20s\n", diff_1, diff_2, prev_1, prev_2, str_1, str_2); + return __LINE__; + } + C(diff_1 == diff_2); + } + C(str_1 == NULL && str_2 == NULL); + return 0; +} + +int strsep_test(char** dup_1, char** dup_2) { + { + char *ptr = NULL_ptr; + C(T_strsep(&ptr, SINK) == NULL); + C(T_strsep(&ptr, NULL_ptr) == NULL); + C(T_strsep((char**)SINK, SINK) == NULL); + C(T_strsep((char**)SINK, NULL_ptr) == NULL); + } + const size_t len = strlen(gnu_copypasta); + const size_t size = len + 1; + *dup_1 = strdup(gnu_copypasta); + *dup_2 = strndup(gnu_copypasta, len); + C(*dup_1 != NULL && *dup_2 != NULL); + C(strcmp(*dup_1, *dup_2) == 0); + { + char *ptr, *prev; + ptr = *dup_2; + prev = T_strsep(&ptr, ""); + C(ptr == NULL); + C(prev == *dup_2); + ptr = *dup_2; + prev = T_strsep(&ptr, "zZ"); + C(ptr == NULL); + C(prev == *dup_2); + C(memcmp(*dup_1, *dup_2, size) == 0); + } + { + int ret; + TEST(strsep_loop_test(*dup_1, *dup_2)); + C(memcmp(*dup_1, *dup_2, size) == 0); + } + return 0; +} + int run_tests(void) { int ret = 0; + /* boot_asprintf */ ret = boot_sprintf_tests(); free(buf); buf = NULL; @@ -1027,6 +1124,17 @@ int run_tests(void) { TEST(strrstr_test()); TEST(strchrnul_test()); + /* strsep */ { + char *dup_1 = SINK, *dup_2 = SINK; + ret = strsep_test(&dup_1, &dup_2); + if (dup_1 == NULL || dup_2 == NULL) { + perror("str(n)dup returned NULL"); + } + free(dup_1); dup_1 = NULL; + free(dup_2); dup_2 = NULL; + if (ret != 0) { return ret; } + } + return 0; } diff --git a/test/standalone/asprintf_fprintf/src/rename.asm b/test/standalone/asprintf_fprintf/src/rename.asm index 14c6bd1b8..b0139b5ad 100644 --- a/test/standalone/asprintf_fprintf/src/rename.asm +++ b/test/standalone/asprintf_fprintf/src/rename.asm @@ -3,7 +3,7 @@ section .text public _T_memset, _T_memcpy, _T_memmove, _T_memcmp, _T_memccpy, _T_mempcpy, _T_memrchr, _T_memmem, _T_memrmem - public _T_strlen, _T_strcmp, _T_strncmp, _T_stpcpy, _T_stpncpy, _T_strlcat, _T_strchrnul, _T_strrstr + public _T_strlen, _T_strcmp, _T_strncmp, _T_stpcpy, _T_stpncpy, _T_strlcat, _T_strchrnul, _T_strrstr, _T_strsep public _T_bzero _T_memset := _memset @@ -24,15 +24,17 @@ _T_stpncpy := _stpncpy _T_strlcat := _strlcat _T_strchrnul := _strchrnul _T_strrstr := _strrstr +_T_strsep := _strsep _T_bzero := _bzero section .rodata - public _NULL_ptr + public _NULL_ptr, _ZERO_size _NULL_ptr: +_ZERO_size: db $00, $00, $00 extern _memset, _memcpy, _memmove, _memcmp, _memccpy, _mempcpy, _memrchr, _memmem, _memrmem - extern _strlen, _strcmp, _strncmp, _stpcpy, _stpncpy, _strlcat, _strchrnul, _strrstr + extern _strlen, _strcmp, _strncmp, _stpcpy, _stpncpy, _strlcat, _strchrnul, _strrstr, _strsep extern _bzero