diff --git a/src/libc/stpncpy.src b/src/libc/stpncpy.src new file mode 100644 index 000000000..92ac007d7 --- /dev/null +++ b/src/libc/stpncpy.src @@ -0,0 +1,57 @@ + assume adl=1 + + section .text + + public _stpncpy + +_stpncpy: + ld iy, 0 + add iy, sp + ld bc, (iy + 9) ; max_len + + ; inlined strnlen + xor a, a + sbc hl, hl + sbc hl, bc + jr z, .zero_size + add hl, bc + ld de, (iy + 6) ; src + sbc hl, de + ex de, hl + cpir + jr z, .finish_strnlen + inc hl +.finish_strnlen: + xor a, a + adc hl, de +.zero_size: + + ; copy strnlen bytes from src + push hl + ld de, (iy + 3) ; dst + jr z, .zero_byte_copy + ld hl, (iy + 6) ; src + pop bc + push bc + ldir +.zero_byte_copy: + pop bc + + ; zero pad the remainder + ld hl, (iy + 9) ; max_len + scf + sbc hl, bc ; clear_size - 1 = max_len - src_len - 1 + ex de, hl + ret c ; clear_size <= 0 (or max_len <= src_len) + ; HL = dst + src_len + ; DE = clear_size - 1 + add hl, de + ld (hl), a + ret z ; clear_size == 1 + push de + pop bc + push hl + pop de + dec de + lddr + ret diff --git a/src/libc/strlcat.src b/src/libc/strlcat.src new file mode 100644 index 000000000..749c867ca --- /dev/null +++ b/src/libc/strlcat.src @@ -0,0 +1,76 @@ + assume adl=1 + + section .text + + public _strlcat + +_strlcat: + ld iy, 0 + lea bc, iy + add iy, sp + ld hl, (iy + 6) ; src + xor a, a + cpir + sbc hl, hl + dec hl + sbc hl, bc + ; carry is clear + ex de, hl + + ; inlined strnlen + ld bc, (iy + 9) ; max_size + sbc hl, hl + sbc hl, bc + ; Allows dst to be NULL when max_size is zero + jr z, .zero_size ; return src_len + add hl, bc + push de ; src_len + ld de, (iy + 3) ; dst + sbc hl, de + ex de, hl + cpir + add hl, de + jr z, .finish_strnlen + inc hl +.finish_strnlen: + + ex de, hl + ld hl, (iy + 9) ; max_size + ; (copy_size + 1) = max_size - dst_len + xor a, a + sbc hl, de + jr z, .no_room + + pop bc ; src_len + push bc + + ; (copy_size + 1) - src_len - 1 + scf + sbc hl, bc + + jr c, .copy_size_lt_src_len + ; (copy_size + 1 - 1) >= src_len + ; copy_size >= src_len + sbc hl, hl +.copy_size_lt_src_len: + xor a, a + adc hl, bc + jr z, .zero_copy_size + push hl + pop bc + + push de ; dst_len + ld hl, (iy + 3) ; dst + add hl, de ; dst + dst_len + ex de, hl + ld hl, (iy + 6) ; src + ldir + ld (de), a ; null terminate + pop de ; dst_len +.zero_copy_size: +.no_room: + pop hl ; src_len +.zero_size: + ; return src_len + dst_len + add hl, de + ret diff --git a/test/standalone/asprintf_fprintf/src/main.c b/test/standalone/asprintf_fprintf/src/main.c index 3bf27ccef..bd11cc314 100644 --- a/test/standalone/asprintf_fprintf/src/main.c +++ b/test/standalone/asprintf_fprintf/src/main.c @@ -22,6 +22,8 @@ #define C(expr) if (!(expr)) { return __LINE__; } +#define TEST(test) { ret = test; if (ret != 0) { return ret; }} + #define SINK (char*)0xE40000 /* pass NULL into functions without triggering -Wnonnull */ @@ -54,6 +56,12 @@ void *T_memrchr(const void *s, int c, size_t n) char *T_stpcpy(char *__restrict dest, const char *__restrict src) __attribute__((nonnull(1, 2))); +char *T_stpncpy(char *__restrict dest, const char *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); + +size_t T_strlcat(void *__restrict dest, const void *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); + size_t T_strlen(const char *s) __attribute__((nonnull(1))); @@ -75,6 +83,8 @@ void T_bzero(void* s, size_t n); #define T_mempcpy mempcpy #define T_memrchr memrchr #define T_stpcpy stpcpy +#define T_stpncpy stpncpy +#define T_strlcat strlcat #define T_strlen strlen #define T_strcmp strcmp #define T_strncmp strncmp @@ -159,7 +169,7 @@ int boot_sprintf_tests(void) { printf("E: %d != %d\n", len_3, pos_3); return __LINE__; } - + // large string test static char const * const s = "Hello"; int len_4 = boot_snprintf(SINK, 300, @@ -271,7 +281,7 @@ int nano_tests(void) { printf("E: %d != %d\n", len_3s, pos_3); return __LINE__; } - + // https://en.cppreference.com/w/c/io/fprintf static char const * const s = "Hello"; int len_4 = snprintf(SINK, 300, @@ -360,7 +370,7 @@ int memccpy_tests(void) { return __LINE__; } file = fopen(file_name, "wb"); - + // Check if the file was opened successfully if (file == NULL) { perror("Error opening file"); @@ -371,24 +381,24 @@ int memccpy_tests(void) { const char terminal[] = {':', ' ', ',', '.', '!'}; char dest[sizeof src]; const char alt = '@'; - + 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"); - + // if `terminal` character was not found - print the whole `dest` to = to ? to : dest + sizeof dest; - + for (char* from = dest; from != to; ++from) { fputc(isprint(*from) ? *from : alt, file); } - + 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" @@ -396,7 +406,7 @@ int memccpy_tests(void) { char names_only[64]; 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) { if (first) { @@ -597,6 +607,99 @@ int memmove_test(void) { return 0; } +static bool strcmp_exact(const char* x, const char* y) { + if (strlen(x) != strlen(y)) { + return false; + } + if (strcmp(x, y) != 0) { + return false; + } + return true; +} + +int strlcat_test(void) { + const char* src1 = "Foo"; + const char* src2 = "Bar"; + char dst[10]; + + strcpy(dst, src1); C(T_strlcat(dst , src2, 0) == 3); C(strcmp_exact(dst, "Foo")); + strcpy(dst, src1); C(T_strlcat(dst , src2, 1) == 4); C(strcmp_exact(dst, "Foo")); + strcpy(dst, src1); C(T_strlcat(dst , src2, 2) == 5); C(strcmp_exact(dst, "Foo")); + strcpy(dst, src1); C(T_strlcat(dst , src2, 3) == 6); C(strcmp_exact(dst, "Foo")); + strcpy(dst, src1); C(T_strlcat(dst , src2, 4) == 6); C(strcmp_exact(dst, "Foo")); + strcpy(dst, src1); C(T_strlcat(dst , src2, 5) == 6); C(strcmp_exact(dst, "FooB")); + strcpy(dst, src1); C(T_strlcat(dst , src2, 6) == 6); C(strcmp_exact(dst, "FooBa")); + strcpy(dst, src1); C(T_strlcat(dst , src2, 7) == 6); C(strcmp_exact(dst, "FooBar")); + strcpy(dst, src1); C(T_strlcat(dst , src2, 8) == 6); C(strcmp_exact(dst, "FooBar")); + strcpy(dst, src1); C(T_strlcat(dst , src2, 9) == 6); C(strcmp_exact(dst, "FooBar")); + + strcpy(dst, src1); C(T_strlcat(dst , SINK, 0) == 0); C(strcmp_exact(dst, src1)); + strcpy(dst, src1); C(T_strlcat(dst , SINK, 1) == 1); C(strcmp_exact(dst, src1)); + strcpy(dst, src1); C(T_strlcat(dst , SINK, 2) == 2); C(strcmp_exact(dst, src1)); + strcpy(dst, src1); C(T_strlcat(dst , SINK, 3) == 3); C(strcmp_exact(dst, src1)); + strcpy(dst, src1); C(T_strlcat(dst , SINK, 4) == 3); C(strcmp_exact(dst, src1)); + strcpy(dst, src1); C(T_strlcat(dst , SINK, 5) == 3); C(strcmp_exact(dst, src1)); + + C(T_strlcat(NULL_ptr, SINK, 0) == 0); + C(T_strlcat(NULL_ptr, src1, 0) == 3); + + dst[0] = '\0'; C(T_strlcat(dst, SINK, 0) == 0); C(dst[0] == '\0'); + dst[0] = '\0'; C(T_strlcat(dst, SINK, 1) == 0); C(dst[0] == '\0'); + dst[0] = '\0'; C(T_strlcat(dst, SINK, 2) == 0); C(dst[0] == '\0'); + + dst[0] = '\0'; C(T_strlcat(dst, src1, 0) == 3); C(strcmp_exact(dst, "")); + dst[0] = '\0'; C(T_strlcat(dst, src1, 1) == 3); C(strcmp_exact(dst, "")); + dst[0] = '\0'; C(T_strlcat(dst, src1, 2) == 3); C(strcmp_exact(dst, "F")); + dst[0] = '\0'; C(T_strlcat(dst, src1, 3) == 3); C(strcmp_exact(dst, "Fo")); + dst[0] = '\0'; C(T_strlcat(dst, src1, 4) == 3); C(strcmp_exact(dst, "Foo")); + dst[0] = '\0'; C(T_strlcat(dst, src1, 5) == 3); C(strcmp_exact(dst, "Foo")); + + return 0; +} + +int stpncpy_test(void) { + char text[6]; + + C(T_stpncpy(NULL_ptr, "", 0) == NULL_ptr + 0); + C(T_stpncpy(NULL_ptr, "foobar", 0) == NULL_ptr + 0); + + memset(text, '\xee', 6); + + C(T_stpncpy(text, "1", 5) == text + 1); + C(memcmp(text, "1\0\0\0\0\xee", 6) == 0); + + C(T_stpncpy(text, "1234", 5) == text + 4); + C(memcmp(text, "1234\0\xee", 6) == 0); + + C(T_stpncpy(text, "12345", 5) == text + 5); + C(memcmp(text, "12345\xee", 6) == 0); + + C(T_stpncpy(text, "123456", 5) == text + 5); + C(memcmp(text, "12345\xee", 6) == 0); + + memset(text, '\xff', 6); + + C(T_stpncpy(text, "", 0) == text + 0); + C(memcmp(text, "\xff\xff\xff\xff\xff\xff", 6) == 0); + + C(T_stpncpy(text, "123456", 1) == text + 1); + C(memcmp(text, "1\xff\xff\xff\xff\xff", 1) == 0); + + C(T_stpncpy(text, "6", 1) == text + 1); + C(memcmp(text, "6\xff\xff\xff\xff\xff", 1) == 0); + + C(T_stpncpy(text, "", 1) == text + 0); + C(memcmp(text, "\0\xff\xff\xff\xff\xff", 1) == 0); + + C(T_stpncpy(text, "a", 2) == text + 1); + C(memcmp(text, "a\0\xff\xff\xff\xff", 1) == 0); + + C(T_stpncpy(text, "", 5) == text + 0); + C(memcmp(text, "\0\0\0\0\0\xff", 1) == 0); + + return 0; +} + int run_tests(void) { int ret = 0; /* boot_asprintf */ @@ -618,25 +721,13 @@ int run_tests(void) { } if (ret != 0) { return ret; } - /* mempcpy */ - ret = mempcpy_test(); - if (ret != 0) { return ret; } - - /* bzero */ - ret = bzero_test(); - if (ret != 0) { return ret; } - - /* strncmp */ - ret = strncmp_test(); - if (ret != 0) { return ret; } - - /* memrchr */ - ret = memrchr_test(); - if (ret != 0) { return ret; } - - /* memrchr */ - ret = memmove_test(); - if (ret != 0) { return ret; } + TEST(mempcpy_test()); + TEST(bzero_test()); + TEST(strncmp_test()); + TEST(memrchr_test()); + TEST(memmove_test()); + TEST(strlcat_test()); + TEST(stpncpy_test()); return 0; } @@ -675,7 +766,7 @@ int main(void) printf("All tests %s", "passed"); #endif } - + while (!os_GetCSC()); return 0; diff --git a/test/standalone/asprintf_fprintf/src/rename.asm b/test/standalone/asprintf_fprintf/src/rename.asm index a14203fe8..51506be96 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 - public _T_strlen, _T_strcmp, _T_strncmp, _T_stpcpy + public _T_strlen, _T_strcmp, _T_strncmp, _T_stpcpy, _T_stpncpy, _T_strlcat public _T_bzero _T_memset := _memset @@ -18,6 +18,8 @@ _T_strlen := _strlen _T_strcmp := _strcmp _T_strncmp := _strncmp _T_stpcpy := _stpcpy +_T_stpncpy := _stpncpy +_T_strlcat := _strlcat _T_bzero := _bzero @@ -28,5 +30,5 @@ _NULL_ptr: db $00, $00, $00 extern _memset, _memcpy, _memmove, _memcmp, _memccpy, _mempcpy, _memrchr - extern _strlen, _strcmp, _strncmp, _stpcpy + extern _strlen, _strcmp, _strncmp, _stpcpy, _stpncpy, _strlcat extern _bzero