Paul Eggert wrote:
> @@ -48,7 +48,8 @@ get_locale_dependent_values (struct locale_dependent_values 
> *result)
>    snprintf (result->numeric, sizeof (result->numeric),
>              "%g", 3.5);
>    /* result->numeric is usually "3,5" */
> -  strcpy (result->time, nl_langinfo (MON_1));
> +  strncpy (result->time, nl_langinfo (MON_1), sizeof result->time - 1);
> +  result->time[sizeof result->time - 1] = '\0';
>    /* result->time is usually "janvier" */
>  }
>  

This change has replaced code with 1 drawback
  - The string copy may overrun the buffer.
by code with 3 drawbacks
  - The string copy may be silently truncated.
  - The code needs 2 lines, instead of 1 line.
  - In the common cases, the large result buffer gets needlessly filled
    with NULs.

I think the best way to deal with this situation is the function 'strlcpy':

  ASSERT (strlcpy (result->time, nl_langinfo (MON_1), sizeof result->time) < 
sizeof result->time);

This way,
  - The string copy will not overrun the buffer.
  - The string copy will always be NUL-terminated.
  - Silent truncation does not occur.
  - The code fits in one line.
  - The code is not needlessly inefficient.

Here's a proposal to add 'strlcpy' to gnulib.

Yes, I have read the relevant documentation:
https://www.freebsd.org/cgi/man.cgi?query=strlcpy&sektion=3

and the discussions:
https://www.sourceware.org/ml/libc-alpha/2000-08/msg00052.html
https://lwn.net/Articles/612244/
https://sourceware.org/ml/libc-alpha/2014-09/msg00350.html
https://sourceware.org/glibc/wiki/strlcpy

The major argument against strlcpy is that it is not fool-proof:
If the caller ignores the return value, silent truncation can occur.
To prevent this, the proposed patch declares strlcpy with
__attribute__((__warn_unused_result__)) on all platforms.

Bruno

>From dc4a8d880cb3be138b96b682ae57a10259a67ba4 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Thu, 28 Sep 2017 00:30:05 +0200
Subject: [PATCH 1/2] New module 'strlcpy'.

* lib/string.in.h (_GL_ATTRIBUTE_RETURN_CHECK): New macro.
(strlcpy): New declaration.
* lib/strlcpy.c: New file.
* m4/strlcpy.m4: New file.
* modules/strlcpy: New file.
* m4/string_h.m4 (gl_HEADER_STRING_H_BODY): Test whether strlcpy is
declared.
(gl_HEADER_STRING_H_DEFAULTS): Initialize GNULIB_STRLCPY, HAVE_STRLCPY,
REPLACE_STRLCPY.
* modules/string (Makefile.am): Substitite GNULIB_STRLCPY, HAVE_STRLCPY,
REPLACE_STRLCPY.
* doc/gnulib.texi (BSD Function Substitutes): New chapter.
* doc/bsd-functions/strlcpy.texi: New file.
---
 ChangeLog                      | 17 +++++++++++++
 doc/bsd-functions/strlcpy.texi | 18 +++++++++++++
 doc/gnulib.texi                | 27 ++++++++++++++++++++
 lib/string.in.h                | 46 ++++++++++++++++++++++++++++++++++
 lib/strlcpy.c                  | 57 ++++++++++++++++++++++++++++++++++++++++++
 m4/string_h.m4                 | 17 +++++++------
 m4/strlcpy.m4                  | 22 ++++++++++++++++
 modules/string                 | 15 ++++++-----
 modules/strlcpy                | 26 +++++++++++++++++++
 9 files changed, 232 insertions(+), 13 deletions(-)
 create mode 100644 doc/bsd-functions/strlcpy.texi
 create mode 100644 lib/strlcpy.c
 create mode 100644 m4/strlcpy.m4
 create mode 100644 modules/strlcpy

diff --git a/ChangeLog b/ChangeLog
index 8a9bbe6..c005f1c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2017-09-27  Bruno Haible  <br...@clisp.org>
+
+	New module 'strlcpy'.
+	* lib/string.in.h (_GL_ATTRIBUTE_RETURN_CHECK): New macro.
+	(strlcpy): New declaration.
+	* lib/strlcpy.c: New file.
+	* m4/strlcpy.m4: New file.
+	* modules/strlcpy: New file.
+	* m4/string_h.m4 (gl_HEADER_STRING_H_BODY): Test whether strlcpy is
+	declared.
+	(gl_HEADER_STRING_H_DEFAULTS): Initialize GNULIB_STRLCPY, HAVE_STRLCPY,
+	REPLACE_STRLCPY.
+	* modules/string (Makefile.am): Substitite GNULIB_STRLCPY, HAVE_STRLCPY,
+	REPLACE_STRLCPY.
+	* doc/gnulib.texi (BSD Function Substitutes): New chapter.
+	* doc/bsd-functions/strlcpy.texi: New file.
+
 2017-09-26  Bruno Haible  <br...@clisp.org>
 
 	uniname/uniname-tests: Tighten code.
diff --git a/doc/bsd-functions/strlcpy.texi b/doc/bsd-functions/strlcpy.texi
new file mode 100644
index 0000000..5ce05dc
--- /dev/null
+++ b/doc/bsd-functions/strlcpy.texi
@@ -0,0 +1,18 @@
+@node strlcpy
+@subsection @code{strlcpy}
+@findex strlcpy
+
+Gnulib module: strlcpy
+
+Portability problems fixed by Gnulib:
+@itemize
+@item
+This function is missing on some platforms:
+glibc 2.24, AIX 5.1, HP-UX 11, OSF/1 5.1, mingw, MSVC 14, BeOS.
+@item
+Ignoring the return value of this function does not produce a warning.
+@end itemize
+
+Portability problems not fixed by Gnulib:
+@itemize
+@end itemize
diff --git a/doc/gnulib.texi b/doc/gnulib.texi
index 1468c14..6a52787 100644
--- a/doc/gnulib.texi
+++ b/doc/gnulib.texi
@@ -69,6 +69,7 @@ Documentation License''.
 * Legacy Function Substitutes::     Replacing system functions.
 * Glibc Header File Substitutes::   Overriding system headers.
 * Glibc Function Substitutes::      Replacing system functions.
+* BSD Function Substitutes::        Replacing system functions.
 * Native Windows Support::          Support for the native Windows platforms.
 * Particular Modules::              Documentation of individual modules.
 * Regular expressions::             The regex module.
@@ -6306,6 +6307,32 @@ This list of functions is sorted according to the header that declares them.
 @c @section Glibc Extensions to @code{<wordexp.h>}
 
 
+@node BSD Function Substitutes
+@chapter BSD Function Substitutes
+
+This chapter describes which functions and function-like macros provided
+as extensions by the common BSD systems (FreeBSD, NetBSD, OpenBSD) are
+also supported by Gnulib, which portability pitfalls are fixed by Gnulib,
+and which (known) portability problems are not worked around by Gnulib.
+
+@nosuchmodulenote function
+
+This list of functions is sorted according to the header that declares them.
+
+@menu
+* BSD string.h::
+@end menu
+
+@node BSD string.h
+@section BSD Extensions to @code{<string.h>}
+
+@menu
+* strlcpy::
+@end menu
+
+@include bsd-functions/strlcpy.texi
+
+
 @node Native Windows Support
 @chapter Native Windows Support
 
diff --git a/lib/string.in.h b/lib/string.in.h
index eb5be69..e7d6f4b 100644
--- a/lib/string.in.h
+++ b/lib/string.in.h
@@ -60,6 +60,16 @@
 # define _GL_ATTRIBUTE_PURE /* empty */
 #endif
 
+/* Declares that the return value of a function should not be ignored by the
+   caller.  The warn_unused_result attribute appeared first in gcc-3.4.0.  */
+#ifndef _GL_ATTRIBUTE_RETURN_CHECK
+# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#  define _GL_ATTRIBUTE_RETURN_CHECK __attribute__((__warn_unused_result__))
+# else
+#  define _GL_ATTRIBUTE_RETURN_CHECK
+# endif
+#endif
+
 /* NetBSD 5.0 declares strsignal in <unistd.h>, not in <string.h>.  */
 /* But in any case avoid namespace pollution on glibc systems.  */
 #if (@GNULIB_STRSIGNAL@ || defined GNULIB_POSIXCHECK) && defined __NetBSD__ \
@@ -1041,6 +1051,42 @@ _GL_WARN_ON_USE (strsignal, "strsignal is unportable - "
 # endif
 #endif
 
+/* Copies the string SRC, possibly truncated, into the bounded memory area
+   [DST,...,DST+DSTSIZE-1].  Returns strlen (SRC).
+   The result is truncated if and only if the return value is >= DSTSIZE.
+   The result is truncated without NUL terminator if and only if DSTSIZE == 0.
+ */
+#if @GNULIB_STRLCPY@
+# if @REPLACE_STRLCPY@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef strlcpy
+#   define strlcpy rpl_strlcpy
+#  endif
+_GL_FUNCDECL_RPL (strlcpy, size_t,
+                  (char *__dst, const char *__src, size_t __dstsize)
+                  _GL_ARG_NONNULL ((1, 2))
+                  _GL_ATTRIBUTE_RETURN_CHECK);
+_GL_CXXALIAS_RPL (strlcpy, size_t,
+                  (char *__dst, const char *__src, size_t __dstsize));
+# else
+#  if !@HAVE_STRLCPY@
+_GL_FUNCDECL_SYS (strlcpy, size_t,
+                  (char *__dst, const char *__src, size_t __dstsize)
+                  _GL_ARG_NONNULL ((1, 2))
+                  _GL_ATTRIBUTE_RETURN_CHECK);
+#  endif
+_GL_CXXALIAS_SYS (strlcpy, size_t,
+                  (char *__dst, const char *__src, size_t __dstsize));
+# endif
+_GL_CXXALIASWARN (strlcpy);
+#elif defined GNULIB_POSIXCHECK
+# undef strndup
+# if HAVE_RAW_DECL_STRLCPY
+_GL_WARN_ON_USE (strlcpy, "strlcpy is unportable - "
+                 "use gnulib module strlcpy for portability");
+# endif
+#endif
+
 #if @GNULIB_STRVERSCMP@
 # if !@HAVE_STRVERSCMP@
 _GL_FUNCDECL_SYS (strverscmp, int, (const char *, const char *)
diff --git a/lib/strlcpy.c b/lib/strlcpy.c
new file mode 100644
index 0000000..e6cb133
--- /dev/null
+++ b/lib/strlcpy.c
@@ -0,0 +1,57 @@
+/* Copy a string into a bounded memory area.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <br...@clisp.org>, 2017.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <string.h>
+
+/* Copies the string SRC, possibly truncated, into the bounded memory area
+   [DST,...,DST+DSTSIZE-1].  Returns strlen (SRC).
+   The result is truncated if and only if the return value is >= DSTSIZE.
+   The result is truncated without NUL terminator if and only if DSTSIZE == 0.
+ */
+size_t
+strlcpy (char *dst, const char *src, size_t dstsize)
+#undef strlcpy
+{
+  /* The implementation in dietlibc is broken, says
+     <https://sourceware.org/glibc/wiki/strlcpy>.  */
+#if HAVE_STRLCPY && !defined __dietlibc__
+  return strlcpy (dst, src, dstsize);
+#else
+  if (dstsize > 0)
+    {
+      /* This implementation makes only one pass through SRC, for cache
+         efficiency.  */
+      char *p = dst;
+      while (--dstsize > 0)
+        {
+          char c = *src++;
+          *p = c;
+          if (c == '\0')
+            return p - dst;
+          p++;
+        }
+      *p = '\0';
+      return (p - dst) + strlen (src);
+    }
+  else
+    return strlen (src);
+#endif
+}
diff --git a/m4/string_h.m4 b/m4/string_h.m4
index ac6311f..b8f68ce 100644
--- a/m4/string_h.m4
+++ b/m4/string_h.m4
@@ -5,7 +5,7 @@
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-# serial 21
+# serial 22
 
 # Written by Paul Eggert.
 
@@ -29,7 +29,7 @@ AC_DEFUN([gl_HEADER_STRING_H_BODY],
     ]],
     [ffsl ffsll memmem mempcpy memrchr rawmemchr stpcpy stpncpy strchrnul
      strdup strncat strndup strnlen strpbrk strsep strcasestr strtok_r
-     strerror_r strsignal strverscmp])
+     strerror_r strsignal strlcpy strverscmp])
 ])
 
 AC_DEFUN([gl_STRING_MODULE_INDICATOR],
@@ -80,6 +80,7 @@ AC_DEFUN([gl_HEADER_STRING_H_DEFAULTS],
   GNULIB_STRERROR=0;    AC_SUBST([GNULIB_STRERROR])
   GNULIB_STRERROR_R=0;  AC_SUBST([GNULIB_STRERROR_R])
   GNULIB_STRSIGNAL=0;   AC_SUBST([GNULIB_STRSIGNAL])
+  GNULIB_STRLCPY=0;     AC_SUBST([GNULIB_STRLCPY])
   GNULIB_STRVERSCMP=0;  AC_SUBST([GNULIB_STRVERSCMP])
   HAVE_MBSLEN=0;        AC_SUBST([HAVE_MBSLEN])
   dnl Assume proper GNU behavior unless another module says otherwise.
@@ -103,20 +104,22 @@ AC_DEFUN([gl_HEADER_STRING_H_DEFAULTS],
   HAVE_DECL_STRTOK_R=1;         AC_SUBST([HAVE_DECL_STRTOK_R])
   HAVE_DECL_STRERROR_R=1;       AC_SUBST([HAVE_DECL_STRERROR_R])
   HAVE_DECL_STRSIGNAL=1;        AC_SUBST([HAVE_DECL_STRSIGNAL])
+  HAVE_STRLCPY=1;               AC_SUBST([HAVE_STRLCPY])
   HAVE_STRVERSCMP=1;            AC_SUBST([HAVE_STRVERSCMP])
   REPLACE_MEMCHR=0;             AC_SUBST([REPLACE_MEMCHR])
   REPLACE_MEMMEM=0;             AC_SUBST([REPLACE_MEMMEM])
   REPLACE_STPNCPY=0;            AC_SUBST([REPLACE_STPNCPY])
+  REPLACE_STRCHRNUL=0;          AC_SUBST([REPLACE_STRCHRNUL])
   REPLACE_STRDUP=0;             AC_SUBST([REPLACE_STRDUP])
+  REPLACE_STRNCAT=0;            AC_SUBST([REPLACE_STRNCAT])
+  REPLACE_STRNDUP=0;            AC_SUBST([REPLACE_STRNDUP])
+  REPLACE_STRNLEN=0;            AC_SUBST([REPLACE_STRNLEN])
   REPLACE_STRSTR=0;             AC_SUBST([REPLACE_STRSTR])
   REPLACE_STRCASESTR=0;         AC_SUBST([REPLACE_STRCASESTR])
-  REPLACE_STRCHRNUL=0;          AC_SUBST([REPLACE_STRCHRNUL])
+  REPLACE_STRTOK_R=0;           AC_SUBST([REPLACE_STRTOK_R])
   REPLACE_STRERROR=0;           AC_SUBST([REPLACE_STRERROR])
   REPLACE_STRERROR_R=0;         AC_SUBST([REPLACE_STRERROR_R])
-  REPLACE_STRNCAT=0;            AC_SUBST([REPLACE_STRNCAT])
-  REPLACE_STRNDUP=0;            AC_SUBST([REPLACE_STRNDUP])
-  REPLACE_STRNLEN=0;            AC_SUBST([REPLACE_STRNLEN])
   REPLACE_STRSIGNAL=0;          AC_SUBST([REPLACE_STRSIGNAL])
-  REPLACE_STRTOK_R=0;           AC_SUBST([REPLACE_STRTOK_R])
+  REPLACE_STRLCPY=0;            AC_SUBST([REPLACE_STRLCPY])
   UNDEFINE_STRTOK_R=0;          AC_SUBST([UNDEFINE_STRTOK_R])
 ])
diff --git a/m4/strlcpy.m4 b/m4/strlcpy.m4
new file mode 100644
index 0000000..68eaf48
--- /dev/null
+++ b/m4/strlcpy.m4
@@ -0,0 +1,22 @@
+# strlcpy.m4 serial 1
+dnl Copyright (C) 2017 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_STRLCPY],
+[
+  AC_REQUIRE([gl_HEADER_STRING_H_DEFAULTS])
+
+  AC_CHECK_FUNCS_ONCE([strlcpy])
+  if test $ac_cv_func_strlcpy = yes; then
+    dnl The system's <string.h> declares the function without
+    dnl __attribute__((__warn_unused_result__)). Therefore override it.
+    REPLACE_STRLCPY=1
+  else
+    HAVE_STRLCPY=0
+  fi
+])
+
+# Prerequisites of lib/strlcpy.c.
+AC_DEFUN([gl_PREREQ_STRLCPY], [:])
diff --git a/modules/string b/modules/string
index 8a07da5..daa079b 100644
--- a/modules/string
+++ b/modules/string
@@ -67,6 +67,7 @@ string.h: string.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H
 	      -e 's/@''GNULIB_STRERROR''@/$(GNULIB_STRERROR)/g' \
 	      -e 's/@''GNULIB_STRERROR_R''@/$(GNULIB_STRERROR_R)/g' \
 	      -e 's/@''GNULIB_STRSIGNAL''@/$(GNULIB_STRSIGNAL)/g' \
+	      -e 's/@''GNULIB_STRLCPY''@/$(GNULIB_STRLCPY)/g' \
 	      -e 's/@''GNULIB_STRVERSCMP''@/$(GNULIB_STRVERSCMP)/g' \
 	      < $(srcdir)/string.in.h | \
 	  sed -e 's|@''HAVE_EXPLICIT_BZERO''@|$(HAVE_EXPLICIT_BZERO)|g' \
@@ -90,21 +91,23 @@ string.h: string.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H
 	      -e 's|@''HAVE_DECL_STRTOK_R''@|$(HAVE_DECL_STRTOK_R)|g' \
 	      -e 's|@''HAVE_DECL_STRERROR_R''@|$(HAVE_DECL_STRERROR_R)|g' \
 	      -e 's|@''HAVE_DECL_STRSIGNAL''@|$(HAVE_DECL_STRSIGNAL)|g' \
+	      -e 's|@''HAVE_STRLCPY''@|$(HAVE_STRLCPY)|g' \
 	      -e 's|@''HAVE_STRVERSCMP''@|$(HAVE_STRVERSCMP)|g' \
-	      -e 's|@''REPLACE_STPNCPY''@|$(REPLACE_STPNCPY)|g' \
 	      -e 's|@''REPLACE_MEMCHR''@|$(REPLACE_MEMCHR)|g' \
 	      -e 's|@''REPLACE_MEMMEM''@|$(REPLACE_MEMMEM)|g' \
-	      -e 's|@''REPLACE_STRCASESTR''@|$(REPLACE_STRCASESTR)|g' \
+	      -e 's|@''REPLACE_STPNCPY''@|$(REPLACE_STPNCPY)|g' \
 	      -e 's|@''REPLACE_STRCHRNUL''@|$(REPLACE_STRCHRNUL)|g' \
 	      -e 's|@''REPLACE_STRDUP''@|$(REPLACE_STRDUP)|g' \
-	      -e 's|@''REPLACE_STRSTR''@|$(REPLACE_STRSTR)|g' \
-	      -e 's|@''REPLACE_STRERROR''@|$(REPLACE_STRERROR)|g' \
-	      -e 's|@''REPLACE_STRERROR_R''@|$(REPLACE_STRERROR_R)|g' \
 	      -e 's|@''REPLACE_STRNCAT''@|$(REPLACE_STRNCAT)|g' \
 	      -e 's|@''REPLACE_STRNDUP''@|$(REPLACE_STRNDUP)|g' \
 	      -e 's|@''REPLACE_STRNLEN''@|$(REPLACE_STRNLEN)|g' \
-	      -e 's|@''REPLACE_STRSIGNAL''@|$(REPLACE_STRSIGNAL)|g' \
+	      -e 's|@''REPLACE_STRSTR''@|$(REPLACE_STRSTR)|g' \
+	      -e 's|@''REPLACE_STRCASESTR''@|$(REPLACE_STRCASESTR)|g' \
 	      -e 's|@''REPLACE_STRTOK_R''@|$(REPLACE_STRTOK_R)|g' \
+	      -e 's|@''REPLACE_STRERROR''@|$(REPLACE_STRERROR)|g' \
+	      -e 's|@''REPLACE_STRERROR_R''@|$(REPLACE_STRERROR_R)|g' \
+	      -e 's|@''REPLACE_STRSIGNAL''@|$(REPLACE_STRSIGNAL)|g' \
+	      -e 's|@''REPLACE_STRLCPY''@|$(REPLACE_STRLCPY)|g' \
 	      -e 's|@''UNDEFINE_STRTOK_R''@|$(UNDEFINE_STRTOK_R)|g' \
 	      -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
 	      -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
diff --git a/modules/strlcpy b/modules/strlcpy
new file mode 100644
index 0000000..f12d8d6
--- /dev/null
+++ b/modules/strlcpy
@@ -0,0 +1,26 @@
+Description:
+strlcpy() function: copy a string into a bounded memory area.
+
+Files:
+lib/strlcpy.c
+m4/strlcpy.m4
+
+Depends-on:
+string
+
+configure.ac:
+gl_FUNC_STRLCPY
+gl_PREREQ_STRLCPY
+gl_STRING_MODULE_INDICATOR([strlcpy])
+
+Makefile.am:
+lib_SOURCES += strlcpy.c
+
+Include:
+<string.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4

From 8e0c10ee607cfea17167fbcd540b18d7f23d9fec Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Thu, 28 Sep 2017 00:31:04 +0200
Subject: [PATCH 2/2] Tests for module 'strlcpy'.

* tests/test-strlcpy.c: New file, based on tests/unistr/test-strncpy.h.
* modules/strlcpy-tests: New file.
* tests/test-string-c++.cc: Check the signature of strlcpy.
---
 ChangeLog                |   5 ++
 modules/strlcpy-tests    |  19 ++++++++
 tests/test-string-c++.cc |   5 ++
 tests/test-strlcpy.c     | 117 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 146 insertions(+)
 create mode 100644 modules/strlcpy-tests
 create mode 100644 tests/test-strlcpy.c

diff --git a/ChangeLog b/ChangeLog
index c005f1c..8fa21e8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2017-09-27  Bruno Haible  <br...@clisp.org>
 
+	Tests for module 'strlcpy'.
+	* tests/test-strlcpy.c: New file, based on tests/unistr/test-strncpy.h.
+	* modules/strlcpy-tests: New file.
+	* tests/test-string-c++.cc: Check the signature of strlcpy.
+
 	New module 'strlcpy'.
 	* lib/string.in.h (_GL_ATTRIBUTE_RETURN_CHECK): New macro.
 	(strlcpy): New declaration.
diff --git a/modules/strlcpy-tests b/modules/strlcpy-tests
new file mode 100644
index 0000000..fab82e2
--- /dev/null
+++ b/modules/strlcpy-tests
@@ -0,0 +1,19 @@
+Files:
+tests/test-strlcpy.c
+tests/zerosize-ptr.h
+tests/macros.h
+m4/mmap-anon.m4
+
+Depends-on:
+extensions
+getpagesize
+
+configure.ac:
+gl_FUNC_MMAP_ANON
+AC_CHECK_HEADERS_ONCE([sys/mman.h])
+AC_CHECK_FUNCS_ONCE([mprotect])
+
+Makefile.am:
+TESTS += test-strlcpy
+check_PROGRAMS += test-strlcpy
+test_strlcpy_SOURCES = test-strlcpy.c
diff --git a/tests/test-string-c++.cc b/tests/test-string-c++.cc
index 877cf68..c3df903 100644
--- a/tests/test-string-c++.cc
+++ b/tests/test-string-c++.cc
@@ -142,6 +142,11 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::strerror_r, int, (int, char *, size_t));
 SIGNATURE_CHECK (GNULIB_NAMESPACE::strsignal, char *, (int));
 #endif
 
+#if GNULIB_TEST_STRLCPY
+SIGNATURE_CHECK (GNULIB_NAMESPACE::strlcpy, size_t,
+                 (char *, const char *, size_t));
+#endif
+
 #if GNULIB_TEST_STRVERSCMP
 SIGNATURE_CHECK (GNULIB_NAMESPACE::strverscmp, int,
                  (const char *, const char *));
diff --git a/tests/test-strlcpy.c b/tests/test-strlcpy.c
new file mode 100644
index 0000000..9d5eee9
--- /dev/null
+++ b/tests/test-strlcpy.c
@@ -0,0 +1,117 @@
+/* Tests of strlcpy().
+   Copyright (C) 2010-2017 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <br...@clisp.org>.  */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (strlcpy, size_t, (char *, const char *, size_t));
+
+#include <stdlib.h>
+
+#include "zerosize-ptr.h"
+#include "macros.h"
+
+#define MAGIC (char)0xBA
+
+static void
+check_single (const char *input, size_t length, size_t n)
+{
+  char *dest;
+  size_t result;
+  size_t i;
+
+  dest = (char *) malloc (1 + n + 1);
+  ASSERT (dest != NULL);
+
+  for (i = 0; i < 1 + n + 1; i++)
+    dest[i] = MAGIC;
+
+  result = strlcpy (dest + 1, input, n);
+  ASSERT (result == length);
+
+  ASSERT (dest[0] == MAGIC);
+  i = 0;
+  if (n > 0)
+    {
+      for (; i < (n <= length ? n - 1 : length); i++)
+        ASSERT (dest[1 + i] == input[i]);
+      ASSERT (dest[1 + i] == '\0');
+      i++;
+    }
+  for (; i < n + 1; i++)
+    ASSERT (dest[1 + i] == MAGIC);
+
+  free (dest);
+}
+
+static void
+check (const char *input, size_t input_length)
+{
+  size_t length;
+  size_t n;
+
+  ASSERT (input_length > 0);
+  ASSERT (input[input_length - 1] == 0);
+  length = input_length - 1; /* = strlen (input) */
+
+  for (n = 0; n <= 2 * length + 2; n++)
+    check_single (input, length, n);
+
+  /* Check that strlcpy (D, S, N) does not look at more than
+     strlen (S) + 1 bytes.  */
+  {
+    char *page_boundary = (char *) zerosize_ptr ();
+
+    if (page_boundary != NULL)
+      {
+        size_t bytes_to_copy = length + 1;
+        char *copy;
+        size_t i;
+
+        copy = (char *) page_boundary - bytes_to_copy;
+        for (i = 0; i < bytes_to_copy; i++)
+          copy[i] = input[i];
+        
+        for (n = 0; n <= 2 * length + 2; n++)
+          check_single (copy, length, n);
+      }
+  }
+}
+
+int
+main ()
+{
+  /* Simple string.  */
+  { /* "Grüß Gott. Здравствуйте! x=(-b±sqrt(b²-4ac))/(2a)  日本語,中文,한글" */
+    static const char input[] =
+      { 'G', 'r', 0xC3, 0xBC, 0xC3, 0x9F, ' ', 'G', 'o', 't', 't', '.', ' ',
+        0xD0, 0x97, 0xD0, 0xB4, 0xD1, 0x80, 0xD0, 0xB0, 0xD0, 0xB2, 0xD1, 0x81,
+        0xD1, 0x82, 0xD0, 0xB2, 0xD1, 0x83, 0xD0, 0xB9, 0xD1, 0x82, 0xD0, 0xB5,
+        '!', ' ', 'x', '=', '(', '-', 'b', 0xC2, 0xB1, 's', 'q', 'r', 't', '(',
+        'b', 0xC2, 0xB2, '-', '4', 'a', 'c', ')', ')', '/', '(', '2', 'a', ')',
+        ' ', ' ', 0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC, 0xE8, 0xAA, 0x9E, ',',
+        0xE4, 0xB8, 0xAD, 0xE6, 0x96, 0x87, ',',
+        0xED, 0x95, 0x9C, 0xEA, 0xB8, 0x80, '\0'
+      };
+    check (input, SIZEOF (input));
+  }
+
+  return 0;
+}
-- 
2.7.4

Reply via email to