Add strndup
char* __fastcall__ strndup (const char* S, size_t maxlen);
This commit is contained in:
@@ -785,6 +785,7 @@ communication, see also <tt>testcode/lib/ser-test.c</tt>.
|
|||||||
<item><ref id="strcpy" name="strcpy">
|
<item><ref id="strcpy" name="strcpy">
|
||||||
<item><ref id="strcspn" name="strcspn">
|
<item><ref id="strcspn" name="strcspn">
|
||||||
<item><ref id="strdup" name="strdup">
|
<item><ref id="strdup" name="strdup">
|
||||||
|
<item><ref id="strndup" name="strndup">
|
||||||
<item><ref id="strerror" name="strerror">
|
<item><ref id="strerror" name="strerror">
|
||||||
<item><ref id="stricmp" name="stricmp">
|
<item><ref id="stricmp" name="stricmp">
|
||||||
<item><ref id="strlen" name="strlen">
|
<item><ref id="strlen" name="strlen">
|
||||||
@@ -5621,6 +5622,7 @@ be used in presence of a prototype.
|
|||||||
<ref id="free" name="free">,
|
<ref id="free" name="free">,
|
||||||
<ref id="realloc" name="realloc">,
|
<ref id="realloc" name="realloc">,
|
||||||
<ref id="strdup" name="strdup">
|
<ref id="strdup" name="strdup">
|
||||||
|
<ref id="strndup" name="strndup">
|
||||||
<tag/Example/None.
|
<tag/Example/None.
|
||||||
</descrip>
|
</descrip>
|
||||||
</quote>
|
</quote>
|
||||||
@@ -7774,6 +7776,35 @@ be used in presence of a prototype.
|
|||||||
<tag/See also/
|
<tag/See also/
|
||||||
<ref id="free" name="free">,
|
<ref id="free" name="free">,
|
||||||
<ref id="malloc" name="malloc">
|
<ref id="malloc" name="malloc">
|
||||||
|
<ref id="strndup" name="strndup">
|
||||||
|
<tag/Example/None.
|
||||||
|
</descrip>
|
||||||
|
</quote>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<sect1>strndup<label id="strndup"><p>
|
||||||
|
|
||||||
|
<quote>
|
||||||
|
<descrip>
|
||||||
|
<tag/Function/Allocate a copy of a string on the heap, of a given maximum length.
|
||||||
|
<tag/Header/<tt/<ref id="string.h" name="string.h">/
|
||||||
|
<tag/Declaration/<tt/char* __fastcall__ strndup (const char* s, size_t maxlen);/
|
||||||
|
<tag/Description/<tt/strndup/ allocates a memory block on the heap, big enough
|
||||||
|
to hold a copy of <tt/s/ including the terminating zero. If the allocation
|
||||||
|
fails, <tt/NULL/ is returned, otherwise <tt/s/ is copied into the allocated
|
||||||
|
memory block, maxlen characters are kept, and a pointer to the block is returned.
|
||||||
|
<tag/Notes/<itemize>
|
||||||
|
<item>The function is only available as fastcall function, so it may only
|
||||||
|
be used in presence of a prototype.
|
||||||
|
<item>It is up to the caller to free the allocated memory block.
|
||||||
|
</itemize>
|
||||||
|
<tag/Availability/ISO 9899
|
||||||
|
<tag/See also/
|
||||||
|
<ref id="free" name="free">,
|
||||||
|
<ref id="malloc" name="malloc">
|
||||||
|
<ref id="strndup" name="strndup">
|
||||||
<tag/Example/None.
|
<tag/Example/None.
|
||||||
</descrip>
|
</descrip>
|
||||||
</quote>
|
</quote>
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ void* __fastcall__ __bzero (void* ptr, size_t n);
|
|||||||
#if __CC65_STD__ == __CC65_STD_CC65__
|
#if __CC65_STD__ == __CC65_STD_CC65__
|
||||||
void __fastcall__ bzero (void* ptr, size_t n); /* BSD */
|
void __fastcall__ bzero (void* ptr, size_t n); /* BSD */
|
||||||
char* __fastcall__ strdup (const char* s); /* SYSV/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__ stricmp (const char* s1, const char* s2); /* DOS/Windows */
|
||||||
int __fastcall__ strcasecmp (const char* s1, const char* s2); /* Same for Unix */
|
int __fastcall__ strcasecmp (const char* s1, const char* s2); /* Same for Unix */
|
||||||
char* __fastcall__ strcasestr (const char* str, const char* substr);
|
char* __fastcall__ strcasestr (const char* str, const char* substr);
|
||||||
|
|||||||
56
libsrc/common/strndup.s
Normal file
56
libsrc/common/strndup.s
Normal file
@@ -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
|
||||||
60
test/val/lib_common_strndup.c
Normal file
60
test/val/lib_common_strndup.c
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#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
|
||||||
Reference in New Issue
Block a user