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

Reply via email to