+
+
+
+/
+
+- The function is only available as fastcall function, so it may only
+be used in presence of a prototype.
+
- It is up to the caller to free the allocated memory block.
+
+
,
+[
+][
]
diff --git a/include/string.h b/include/string.h
index 3b7ece1d9..cf346894c 100644
--- a/include/string.h
+++ b/include/string.h
@@ -79,6 +79,7 @@ void* __fastcall__ __bzero (void* ptr, size_t n);
#if __CC65_STD__ == __CC65_STD_CC65__
void __fastcall__ bzero (void* ptr, size_t n); /* BSD */
char* __fastcall__ strdup (const char* s); /* SYSV/BSD */
+char* __fastcall__ strndup (const char* s, size_t maxlen); /* SYSV/BSD */
int __fastcall__ stricmp (const char* s1, const char* s2); /* DOS/Windows */
int __fastcall__ strcasecmp (const char* s1, const char* s2); /* Same for Unix */
char* __fastcall__ strcasestr (const char* str, const char* substr);
diff --git a/libsrc/common/strndup.s b/libsrc/common/strndup.s
new file mode 100644
index 000000000..ce81fc1cc
--- /dev/null
+++ b/libsrc/common/strndup.s
@@ -0,0 +1,56 @@
+;
+; Colin Leroy-Mira, 03.07.2025
+;
+; char* __fastcall__ strndup (const char* S, size_t maxlen);
+;
+
+ .importzp ptr4, c_sp
+ .import _strdup, _strlen, pushax, popax, _realloc
+
+ .export _strndup
+
+_strndup:
+ sta maxlen ; Remember maxlen
+ stx maxlen+1
+
+ jsr popax ; Duplicate string
+ jsr _strdup
+ jsr pushax ; Remember result
+
+ jsr _strlen ; Check length,
+ cpx maxlen+1
+ bcc out ; Return directly if < maxlen
+ bne :+
+ cmp maxlen
+ bcc out
+
+: ldy #$00 ; Otherwise, point to end of string,
+ lda maxlen
+ clc
+ adc (c_sp),y
+ sta ptr4
+ lda maxlen+1
+ iny
+ adc (c_sp),y
+ sta ptr4+1
+
+ dey ; Cut it short,
+ tya
+ sta (ptr4),y
+
+ ldx maxlen+1 ; And finally, realloc to maxlen+1
+ ldy maxlen
+ iny
+ tya
+ bne :+
+ inx
+: jsr _realloc ; TOS still contains result
+ ; We consider realloc will not fail,
+ ; as the block shrinks.
+ jsr pushax ; push/pop for size optimisation
+out:
+ jmp popax
+
+ .bss
+
+maxlen: .res 2
diff --git a/test/val/lib_common_strndup.c b/test/val/lib_common_strndup.c
new file mode 100644
index 000000000..5b3859b37
--- /dev/null
+++ b/test/val/lib_common_strndup.c
@@ -0,0 +1,60 @@
+#include
+#include "unittest.h"
+
+#define SHORT_STR "abcdefghijklmnopqrstuvwxyz"
+
+#define MID_STR_LEN 700 /* Two pages and something */
+#define LONG_STR_LEN 40000UL /* Two long to duplicate */
+TEST
+{
+ char *dst;
+ char *src;
+ int i;
+
+ dst = strndup("", 0);
+ ASSERT_IsTrue(dst != NULL, "strndup returned NULL")
+ ASSERT_IsTrue(!strcmp(dst, ""), "strings differ");
+ free(dst);
+
+ for (i = 0; i < 30; i+=10) {
+ dst = strndup(SHORT_STR, i);
+ ASSERT_IsTrue(dst != NULL, "strndup returned NULL");
+ printf("strlen %s = %d (%d expected)\n", dst, strlen(dst), i);
+ ASSERT_IsTrue(strlen(dst) == i, "string lengths differ");
+ ASSERT_IsTrue(!strncmp(dst, SHORT_STR, i), "strings differ");
+ free(dst);
+ }
+
+ dst = strndup(SHORT_STR, 50);
+ ASSERT_IsTrue(dst != NULL, "strndup returned NULL");
+ printf("strlen %s = %d (%d expected)\n", dst, strlen(dst), i);
+ ASSERT_IsTrue(strlen(dst) == 26, "string lengths differ");
+ ASSERT_IsTrue(!strcmp(dst, SHORT_STR), "strings differ");
+ free(dst);
+
+
+ src = malloc(MID_STR_LEN+1);
+ ASSERT_IsTrue(src != NULL, "Could not allocate source string");
+ memset(src, 'a', MID_STR_LEN);
+ src[MID_STR_LEN] = '\0';
+
+ for (i = 0; i < MID_STR_LEN -1; i+=10) {
+ dst = strndup(src, i);
+ ASSERT_IsTrue(dst != NULL, "strndup returned NULL");
+ printf("strlen %s = %d (%d expected)\n", dst, strlen(dst), i);
+ ASSERT_IsTrue(strlen(dst) == i, "string lengths differ");
+ ASSERT_IsTrue(!strncmp(dst, src, i), "strings differ");
+ free(dst);
+ }
+
+ for (i = MID_STR_LEN; i < MID_STR_LEN * 2; i+=10) {
+ dst = strndup(src, i);
+ ASSERT_IsTrue(dst != NULL, "strndup returned NULL");
+ printf("%d, strlen %s = %d (%d expected)\n", i, dst, strlen(dst), strlen(src));
+ ASSERT_IsTrue(strlen(dst) == strlen(src), "string lengths differ");
+ ASSERT_IsTrue(!strcmp(dst, src), "strings differ");
+ free(dst);
+ }
+
+}
+ENDTEST