I just proposed in https://postgr.es/m/0191204143715.GA17312@alvherre.pgsql the addition of strndup() to our src/port.
I think this should be pretty uncontroversial, but wanted to give a heads-up outside that thread. I attach the patch here for completeness. -- Álvaro Herrera http://www.twitter.com/alvherre
>From 9a92076e53a6664ec61c5f475310c5d6edb91d90 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvhe...@alvh.no-ip.org> Date: Wed, 4 Dec 2019 11:02:34 -0300 Subject: [PATCH v3 1/3] Add strndup / pg_strndup The former is now a POSIX function. We already had pnstrdup, but it was not offered to frontend code. --- configure | 23 +++++++++++++++++++++++ configure.in | 3 ++- src/common/fe_memutils.c | 26 ++++++++++++++++++++++++++ src/include/common/fe_memutils.h | 2 ++ src/include/pg_config.h.in | 7 +++++++ src/include/pg_config.h.win32 | 7 +++++++ src/include/port.h | 4 ++++ src/port/strndup.c | 32 ++++++++++++++++++++++++++++++++ 8 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 src/port/strndup.c diff --git a/configure b/configure index 1d88983b34..fd8c759473 100755 --- a/configure +++ b/configure @@ -15482,6 +15482,16 @@ fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_STRLCPY $ac_have_decl _ACEOF +ac_fn_c_check_decl "$LINENO" "strndup" "ac_cv_have_decl_strndup" "$ac_includes_default" +if test "x$ac_cv_have_decl_strndup" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_STRNDUP $ac_have_decl +_ACEOF ac_fn_c_check_decl "$LINENO" "strnlen" "ac_cv_have_decl_strnlen" "$ac_includes_default" if test "x$ac_cv_have_decl_strnlen" = xyes; then : ac_have_decl=1 @@ -15815,6 +15825,19 @@ esac fi +ac_fn_c_check_func "$LINENO" "strndup" "ac_cv_func_strndup" +if test "x$ac_cv_func_strndup" = xyes; then : + $as_echo "#define HAVE_STRNDUP 1" >>confdefs.h + +else + case " $LIBOBJS " in + *" strndup.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS strndup.$ac_objext" + ;; +esac + +fi + ac_fn_c_check_func "$LINENO" "strnlen" "ac_cv_func_strnlen" if test "x$ac_cv_func_strnlen" = xyes; then : $as_echo "#define HAVE_STRNLEN 1" >>confdefs.h diff --git a/configure.in b/configure.in index a2cb20b5e3..11f66d19c3 100644 --- a/configure.in +++ b/configure.in @@ -1679,7 +1679,7 @@ AC_CHECK_DECLS(posix_fadvise, [], [], [#include <fcntl.h>]) ]) # fi AC_CHECK_DECLS(fdatasync, [], [], [#include <unistd.h>]) -AC_CHECK_DECLS([strlcat, strlcpy, strnlen]) +AC_CHECK_DECLS([strlcat, strlcpy, strndup, strnlen]) # This is probably only present on macOS, but may as well check always AC_CHECK_DECLS(F_FULLFSYNC, [], [], [#include <fcntl.h>]) @@ -1738,6 +1738,7 @@ AC_REPLACE_FUNCS(m4_normalize([ srandom strlcat strlcpy + strndup strnlen strtof ])) diff --git a/src/common/fe_memutils.c b/src/common/fe_memutils.c index ce99b4f4da..896e5d46ca 100644 --- a/src/common/fe_memutils.c +++ b/src/common/fe_memutils.c @@ -101,6 +101,26 @@ pg_strdup(const char *in) return tmp; } +char * +pg_strndup(const char *in, size_t size) +{ + char *tmp; + + if (!in) + { + fprintf(stderr, + _("cannot duplicate null pointer (internal error)\n")); + exit(EXIT_FAILURE); + } + tmp = strndup(in, size); + if (!tmp) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + return tmp; +} + void pg_free(void *ptr) { @@ -142,6 +162,12 @@ pstrdup(const char *in) return pg_strdup(in); } +char * +pnstrdup(const char *in, Size size) +{ + return pg_strndup(in, size); +} + void * repalloc(void *pointer, Size size) { diff --git a/src/include/common/fe_memutils.h b/src/include/common/fe_memutils.h index a1e5940d31..c8797ffe38 100644 --- a/src/include/common/fe_memutils.h +++ b/src/include/common/fe_memutils.h @@ -23,6 +23,7 @@ * (except pg_malloc_extended with MCXT_ALLOC_NO_OOM) */ extern char *pg_strdup(const char *in); +extern char *pg_strndup(const char *in, size_t size); extern void *pg_malloc(size_t size); extern void *pg_malloc0(size_t size); extern void *pg_malloc_extended(size_t size, int flags); @@ -31,6 +32,7 @@ extern void pg_free(void *pointer); /* Equivalent functions, deliberately named the same as backend functions */ extern char *pstrdup(const char *in); +extern char *pnstrdup(const char *in, Size size); extern void *palloc(Size size); extern void *palloc0(Size size); extern void *palloc_extended(Size size, int flags); diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index c208dcdfc7..1db3f1b1e8 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -170,6 +170,10 @@ don't. */ #undef HAVE_DECL_STRLCPY +/* Define to 1 if you have the declaration of `strndup', and to 0 if you + don't. */ +#undef HAVE_DECL_STRNDUP + /* Define to 1 if you have the declaration of `strnlen', and to 0 if you don't. */ #undef HAVE_DECL_STRNLEN @@ -545,6 +549,9 @@ /* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY +/* Define to 1 if you have the `strndup' function. */ +#undef HAVE_STRNDUP + /* Define to 1 if you have the `strnlen' function. */ #undef HAVE_STRNLEN diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32 index 6c98ef4916..2ff2c3d4a8 100644 --- a/src/include/pg_config.h.win32 +++ b/src/include/pg_config.h.win32 @@ -129,6 +129,10 @@ don't. */ #define HAVE_DECL_RTLD_NOW 0 +/* Define to 1 if you have the declaration of `strndup', and to 0 if you + don't. */ +#undef HAVE_DECL_STRNDUP + /* Define to 1 if you have the declaration of `strnlen', and to 0 if you don't. */ #define HAVE_DECL_STRNLEN 1 @@ -297,6 +301,9 @@ /* Define to 1 if you have the <pam/pam_appl.h> header file. */ /* #undef HAVE_PAM_PAM_APPL_H */ +/* Define to 1 if you have the `strndup' function. */ +#undef HAVE_STRNDUP + /* Define to 1 if you have the `strnlen' function. */ #define HAVE_STRNLEN 1 diff --git a/src/include/port.h b/src/include/port.h index 10dcb5f0a6..e5d4486eb2 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -430,6 +430,10 @@ extern size_t strlcat(char *dst, const char *src, size_t siz); extern size_t strlcpy(char *dst, const char *src, size_t siz); #endif +#if !HAVE_DECL_STRNDUP +extern char *strndup(char *src, size_t siz); +#endif + #if !HAVE_DECL_STRNLEN extern size_t strnlen(const char *str, size_t maxlen); #endif diff --git a/src/port/strndup.c b/src/port/strndup.c new file mode 100644 index 0000000000..eba41a0498 --- /dev/null +++ b/src/port/strndup.c @@ -0,0 +1,32 @@ +/* + * strndup.c + * Fallback implementation of strndup(). + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/port/strndup.c + */ + +#include "c.h" + +/* + * Implementation of posix' strndup for systems where it's not available. + */ +char * +strndup(const char *str, size_t siz) +{ + char *dst; + int len; + + len = strnlen(str, siz); + dst = malloc(len + 1); + if (dst == NULL) + return NULL; + + memcpy(dst, str, len); + dst[len + 1] = '\0'; + + return dst; +} -- 2.20.1