From 7d218f93fa7d94614c8906508c92126e74bc08a9 Mon Sep 17 00:00:00 2001 From: ligd Date: Thu, 9 Nov 2023 21:28:38 +0800 Subject: [PATCH] libc: add nx_strdup() & nx_strndup() support Signed-off-by: ligd --- include/stdio.h | 8 +++ include/string.h | 8 +++ libs/libc/stdio/lib_asprintf.c | 33 ++++++++++++ libs/libc/stdio/lib_vasprintf.c | 96 +++++++++++++++++++++++++++++++++ libs/libc/string/lib_strdup.c | 13 +++++ libs/libc/string/lib_strndup.c | 41 ++++++++++++++ 6 files changed, 199 insertions(+) diff --git a/include/stdio.h b/include/stdio.h index 394628a7e9..a7d3115d43 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -266,8 +266,16 @@ int remove(FAR const char *path); #ifndef __KERNEL__ int pclose(FILE *stream); FILE *popen(FAR const char *command, FAR const char *mode) popen_like; +#else +#define asprintf(p, f, ...) nx_asprintf(p, f, ##__VA_ARGS__) +#define vasprintf(p, f, a) nx_vasprintf(p, f, a) #endif +int nx_asprintf(FAR char **ptr, FAR const IPTR char *fmt, ...) + printf_like(2, 3); +int nx_vasprintf(FAR char **ptr, FAR const IPTR char *fmt, va_list ap) + printf_like(2, 0); + #if CONFIG_FORTIFY_SOURCE > 0 fortify_function(fgets) FAR char *fgets(FAR char *s, int n, FAR FILE *stream) { diff --git a/include/string.h b/include/string.h index 8c172aa587..14033cc80a 100644 --- a/include/string.h +++ b/include/string.h @@ -110,6 +110,14 @@ FAR void *memmem(FAR const void *haystack, size_t haystacklen, void explicit_bzero(FAR void *s, size_t n); int timingsafe_bcmp(FAR const void *b1, FAR const void *b2, size_t n); +#ifdef __KERNEL__ +# define strdup(s) nx_strdup(s) +# define strndup(s,sz) nx_strndup(s,sz) +#endif + +FAR char *nx_strdup(FAR const char *s) malloc_like; +FAR char *nx_strndup(FAR const char *s, size_t size) malloc_like; + #if CONFIG_FORTIFY_SOURCE > 0 fortify_function(strcat) FAR char *strcat(FAR char *dest, FAR const char *src) diff --git a/libs/libc/stdio/lib_asprintf.c b/libs/libc/stdio/lib_asprintf.c index 72b6563e4f..566fed504b 100644 --- a/libs/libc/stdio/lib_asprintf.c +++ b/libs/libc/stdio/lib_asprintf.c @@ -30,6 +30,38 @@ * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: nx_asprintf + * + * Description: + * This function is similar to sprintf, except that it dynamically + * allocates a string (as with kmm_malloc) to hold the output, instead of + * putting the output in a buffer you allocate in advance. The ptr + * argument should be the address of a char * object, and a successful + * call to asprintf stores a pointer to the newly allocated string at that + * location. + * + * Returned Value: + * The returned value is the number of characters allocated for the buffer, + * or less than zero if an error occurred. Usually this means that the + * buffer could not be allocated. + * + ****************************************************************************/ + +int nx_asprintf(FAR char **ptr, FAR const IPTR char *fmt, ...) +{ + va_list ap; + int ret; + + /* Let vasprintf do all of the work */ + + va_start(ap, fmt); + ret = nx_vasprintf(ptr, fmt, ap); + va_end(ap); + + return ret; +} + /**************************************************************************** * Name: asprintf * @@ -48,6 +80,7 @@ * ****************************************************************************/ +#undef asprintf int asprintf(FAR char **ptr, FAR const IPTR char *fmt, ...) { va_list ap; diff --git a/libs/libc/stdio/lib_vasprintf.c b/libs/libc/stdio/lib_vasprintf.c index 5f00137a3a..97090ed681 100644 --- a/libs/libc/stdio/lib_vasprintf.c +++ b/libs/libc/stdio/lib_vasprintf.c @@ -34,6 +34,101 @@ * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: nx_vasprintf + * + * Description: + * This function is similar to vsprintf, except that it dynamically + * allocates a string (as with kmm_malloc) to hold the output, instead of + * putting the output in a buffer you allocate in advance. The ptr + * argument should be the address of a char * object, and a successful + * call to vasprintf stores a pointer to the newly allocated string at that + * location. + * + * Returned Value: + * The returned value is the number of characters allocated for the buffer, + * or less than zero if an error occurred. Usually this means that the + * buffer could not be allocated. + * + ****************************************************************************/ + +int nx_vasprintf(FAR char **ptr, FAR const IPTR char *fmt, va_list ap) +{ + struct lib_outstream_s nulloutstream; + struct lib_memoutstream_s memoutstream; + + /* On some architectures, va_list is really a pointer to a structure on + * the stack. And the va_arg builtin will modify that instance of va_list. + * Since vasprintf traverse the parameters in the va_list twice, the + * va_list will be altered in this first cases and the second usage will + * fail. This is a known issue with x86_64. + */ + +#ifdef va_copy + va_list ap2; +#endif + FAR char *buf; + int nbytes; + + DEBUGASSERT(ptr != NULL && fmt != NULL); + +#ifdef va_copy + va_copy(ap2, ap); +#endif + + /* First, use a nullstream to get the size of the buffer. The number + * of bytes returned may or may not include the null terminator. + */ + + lib_nulloutstream(&nulloutstream); + lib_vsprintf(&nulloutstream, fmt, ap); + + /* Then allocate a buffer to hold that number of characters, adding one + * for the null terminator. + */ + + buf = kmm_malloc(nulloutstream.nput + 1); + if (buf == NULL) + { +#ifdef va_copy + va_end(ap2); +#endif + return ERROR; + } + + /* Initialize a memory stream to write into the allocated buffer. The + * memory stream will reserve one byte at the end of the buffer for the + * null terminator and will not report this in the number of output bytes. + */ + + lib_memoutstream(&memoutstream, buf, nulloutstream.nput + 1); + + /* Then let lib_vsprintf do it's real thing */ + +#ifdef va_copy + nbytes = lib_vsprintf(&memoutstream.common, fmt, ap2); + va_end(ap2); +#else + nbytes = lib_vsprintf(&memoutstream.common, fmt, ap); +#endif + + /* Return a pointer to the string to the caller. NOTE: the memstream put() + * method has already added the NUL terminator to the end of the string + * (not included in the nput count). + */ + + DEBUGASSERT(nbytes < 0 || nbytes == nulloutstream.nput); + + if (nbytes < 0) + { + kmm_free(buf); + return ERROR; + } + + *ptr = buf; + return nbytes; +} + /**************************************************************************** * Name: vasprintf * @@ -52,6 +147,7 @@ * ****************************************************************************/ +#undef vasprintf int vasprintf(FAR char **ptr, FAR const IPTR char *fmt, va_list ap) { struct lib_outstream_s nulloutstream; diff --git a/libs/libc/string/lib_strdup.c b/libs/libc/string/lib_strdup.c index e92b4ad589..f876d0b240 100644 --- a/libs/libc/string/lib_strdup.c +++ b/libs/libc/string/lib_strdup.c @@ -47,3 +47,16 @@ FAR char *strdup(FAR const char *s) return news; } + +FAR char *nx_strdup(FAR const char *s) +{ + size_t size = strlen(s) + 1; + FAR char *news = (FAR char *)kmm_malloc(size); + + if (news != NULL) + { + strlcpy(news, s, size); + } + + return news; +} diff --git a/libs/libc/string/lib_strndup.c b/libs/libc/string/lib_strndup.c index 1db4ee9ab1..832ed8132d 100644 --- a/libs/libc/string/lib_strndup.c +++ b/libs/libc/string/lib_strndup.c @@ -34,6 +34,47 @@ * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: nx_strndup + * + * Description: + * The strndup() function is equivalent to the strdup() function, + * duplicating the provided 's' in a new block of memory allocated as + * if by using kmm_malloc(), with the exception being that strndup() copies + * at most 'size' plus one bytes into the newly allocated memory, + * terminating the new string with a NUL character. If the length of 's' + * is larger than 'size', only 'size' bytes will be duplicated. If + * 'size' is larger than the length of 's', all bytes in s will be + * copied into the new memory buffer, including the terminating NUL + * character. The newly created string will always be properly + * terminated. + * + ****************************************************************************/ + +FAR char *nx_strndup(FAR const char *s, size_t size) +{ + FAR char *news; + + /* Get the size of the new string (limited to size) */ + + size_t allocsize = strnlen(s, size); + + /* Allocate the new string, adding 1 for the NUL terminator */ + + news = (FAR char *)kmm_malloc(allocsize + 1); + if (news != NULL) + { + /* Copy the string into the allocated memory and add a NUL + * terminator in any case. + */ + + memcpy(news, s, allocsize); + news[allocsize] = '\0'; + } + + return news; +} + /**************************************************************************** * Name: strndup *