Jim Meyering wrote: > anything that delays finding portability problems > to buildbot runs is a hint that we need better process.
I fully agree; this is one of gnulib's primary goals. The <string.h> substitute now presents an opportunity to improve this. Most of us are developing on sane systems with few broken or missing functions. If, for example, someone uses the 'string' module and stpcpy(), he will not get a warning: /usr/include/string.h declares stpcpy(), and glibc implements it. So the programmer is unaware of the portability problem. Here is a proposed patch, that leads to a link error if someone uses stpcpy() without the 'stpcpy' module (assuming the 'string' module is present - which is quite likely since so much depends on it). $ gcc foo.c -I. -Igllib -Wall foo.c: In function `main': foo.c:3: warning: implicit declaration of function `stpcpy_is_unportable__use_gnulib_module_stpcpy_for_portability' foo.c:3: warning: comparison between pointer and integer /tmp/ccb1jBJh.o(.text+0x1e): In function `main': : undefined reference to `stpcpy_is_unportable__use_gnulib_module_stpcpy_for_portability' collect2: ld returned 1 exit status Objections? Bruno 2007-01-27 Bruno Haible <[EMAIL PROTECTED]> Enforce the use of gnulib modules for unportable <string.h> functions. * m4/string_h.m4 (gl_STRING_MODULE_INDICATOR): New macro. (gl_STRING_MODULE_INDICATOR_DEFAULTS): New macro. (gl_HEADER_STRING_H_BODY): Require it. * lib/string_.h: If the gnulib module XYZ is not present, redefine the symbol XYZ to one that gives a link error. * modules/string (Makefile.am): Also substitute the GNULIB_* variables. * modules/memmem (configure.ac): Invoke gl_STRING_MODULE_INDICATOR. * modules/mempcpy (configure.ac): Likewise. * modules/memrchr (configure.ac): Likewise. * modules/stpcpy (configure.ac): Likewise. * modules/stpncpy (configure.ac): Likewise. * modules/strcase (configure.ac): Likewise. * modules/strcasestr (configure.ac): Likewise. * modules/strchrnul (configure.ac): Likewise. * modules/strdup (configure.ac): Likewise. * modules/strndup (configure.ac): Likewise. * modules/strnlen (configure.ac): Likewise. * modules/strpbrk (configure.ac): Likewise. * modules/strsep (configure.ac): Likewise. * modules/strstr (configure.ac): Likewise. * modules/strtok_r (configure.ac): Likewise. *** m4/string_h.m4 27 Jan 2007 14:43:17 -0000 1.3 --- m4/string_h.m4 27 Jan 2007 15:01:39 -0000 *************** *** 18,23 **** --- 18,24 ---- [ AC_REQUIRE([AC_C_RESTRICT]) AC_REQUIRE([gl_HEADER_STRING_H_DEFAULTS]) + AC_REQUIRE([gl_STRING_MODULE_INDICATOR_DEFAULTS]) gl_ABSOLUTE_HEADER([string.h]) ABSOLUTE_STRING_H=\"$gl_cv_absolute_string_h\" AC_SUBST([ABSOLUTE_STRING_H]) *************** *** 44,46 **** --- 45,73 ---- REPLACE_STRSTR=0; AC_SUBST([REPLACE_STRSTR]) REPLACE_STRCASESTR=0; AC_SUBST([REPLACE_STRCASESTR]) ]) + + AC_DEFUN([gl_STRING_MODULE_INDICATOR], + [ + dnl Use AC_REQUIRE here, so that the default settings are expanded once only. + AC_REQUIRE([gl_STRING_MODULE_INDICATOR_DEFAULTS]) + GNULIB_]translit([$1],[abcdefghijklmnopqrstuvwxyz./-],[ABCDEFGHIJKLMNOPQRSTUVWXYZ___])[=1 + ]) + + AC_DEFUN([gl_STRING_MODULE_INDICATOR_DEFAULTS], + [ + GNULIB_MEMMEM=0; AC_SUBST([GNULIB_MEMMEM]) + GNULIB_MEMPCPY=0; AC_SUBST([GNULIB_MEMPCPY]) + GNULIB_MEMRCHR=0; AC_SUBST([GNULIB_MEMRCHR]) + GNULIB_STPCPY=0; AC_SUBST([GNULIB_STPCPY]) + GNULIB_STPNCPY=0; AC_SUBST([GNULIB_STPNCPY]) + GNULIB_STRCASE=0; AC_SUBST([GNULIB_STRCASE]) + GNULIB_STRCHRNUL=0; AC_SUBST([GNULIB_STRCHRNUL]) + GNULIB_STRDUP=0; AC_SUBST([GNULIB_STRDUP]) + GNULIB_STRNDUP=0; AC_SUBST([GNULIB_STRNDUP]) + GNULIB_STRNLEN=0; AC_SUBST([GNULIB_STRNLEN]) + GNULIB_STRPBRK=0; AC_SUBST([GNULIB_STRPBRK]) + GNULIB_STRSEP=0; AC_SUBST([GNULIB_STRSEP]) + GNULIB_STRSTR=0; AC_SUBST([GNULIB_STRSTR]) + GNULIB_STRCASESTR=0; AC_SUBST([GNULIB_STRCASESTR]) + GNULIB_STRTOK_R=0; AC_SUBST([GNULIB_STRTOK_R]) + ]) *** lib/string_.h 27 Jan 2007 13:17:16 -0000 1.3 --- lib/string_.h 27 Jan 2007 15:01:38 -0000 *************** *** 26,59 **** #endif /* Return the first occurrence of NEEDLE in HAYSTACK. */ ! #if ! @HAVE_DECL_MEMMEM@ extern void *memmem (void const *__haystack, size_t __haystack_len, void const *__needle, size_t __needle_len); #endif /* Copy N bytes of SRC to DEST, return pointer to bytes after the last written byte. */ ! #if ! @HAVE_MEMPCPY@ extern void *mempcpy (void *restrict __dest, void const *restrict __src, size_t __n); #endif /* Search backwards through a block for a byte (specified as an int). */ ! #if ! @HAVE_DECL_MEMRCHR@ extern void *memrchr (void const *, int, size_t); #endif /* Copy SRC to DST, returning the address of the terminating '\0' in DST. */ ! #if ! @HAVE_STPCPY@ extern char *stpcpy (char *restrict __dst, char const *restrict __src); #endif /* Copy no more than N bytes of SRC to DST, returning a pointer past the last non-NUL byte written into DST. */ ! #if ! @HAVE_STPNCPY@ ! # define stpncpy gnu_stpncpy extern char *stpncpy (char *restrict __dst, char const *restrict __src, size_t __n); #endif /* Compare strings S1 and S2, ignoring case, returning less than, equal to or --- 26,79 ---- #endif /* Return the first occurrence of NEEDLE in HAYSTACK. */ ! #if @GNULIB_MEMMEM@ ! # if ! @HAVE_DECL_MEMMEM@ extern void *memmem (void const *__haystack, size_t __haystack_len, void const *__needle, size_t __needle_len); + # endif + #else + # define memmem memmem_is_unportable__use_gnulib_module_memmem_for_portability #endif /* Copy N bytes of SRC to DEST, return pointer to bytes after the last written byte. */ ! #if @GNULIB_MEMPCPY@ ! # if ! @HAVE_MEMPCPY@ extern void *mempcpy (void *restrict __dest, void const *restrict __src, size_t __n); + # endif + #else + # define mempcpy mempcpy_is_unportable__use_gnulib_module_mempcpy_for_portability #endif /* Search backwards through a block for a byte (specified as an int). */ ! #if @GNULIB_MEMRCHR@ ! # if ! @HAVE_DECL_MEMRCHR@ extern void *memrchr (void const *, int, size_t); + # endif + #else + # define memrchr memrchr_is_unportable__use_gnulib_module_memrchr_for_portability #endif /* Copy SRC to DST, returning the address of the terminating '\0' in DST. */ ! #if @GNULIB_STPCPY@ ! # if ! @HAVE_STPCPY@ extern char *stpcpy (char *restrict __dst, char const *restrict __src); + # endif + #else + # define stpcpy stpcpy_is_unportable__use_gnulib_module_stpcpy_for_portability #endif /* Copy no more than N bytes of SRC to DST, returning a pointer past the last non-NUL byte written into DST. */ ! #if @GNULIB_STPNCPY@ ! # if ! @HAVE_STPNCPY@ ! # define stpncpy gnu_stpncpy extern char *stpncpy (char *restrict __dst, char const *restrict __src, size_t __n); + # endif + #else + # define stpncpy stpncpy_is_unportable__use_gnulib_module_stpncpy_for_portability #endif /* Compare strings S1 and S2, ignoring case, returning less than, equal to or *************** *** 64,111 **** No known system has a strcasecmp() function that works correctly in multibyte locales. Therefore use our version always, if the strcase module is available. */ ! #if @REPLACE_STRCASECMP@ ! # define strcasecmp rpl_strcasecmp extern int strcasecmp (char const *__s1, char const *__s2); #endif /* Compare no more than N bytes of strings S1 and S2, ignoring case, returning less than, equal to or greater than zero if S1 is lexicographically less than, equal to or greater than S2. Note: This function cannot work correctly in multibyte locales. */ ! #if ! @HAVE_DECL_STRNCASECMP@ extern int strncasecmp (char const *__s1, char const *__s2, size_t __n); #endif /* Find the first occurrence of C in S or the final NUL byte. */ ! #if ! @HAVE_STRCHRNUL@ extern char *strchrnul (char const *__s, int __c_in); #endif /* Duplicate S, returning an identical malloc'd string. */ ! #if ! @HAVE_DECL_STRDUP@ && ! defined strdup extern char *strdup (char const *__s); #endif /* Return a newly allocated copy of at most N bytes of STRING. */ ! #if ! @HAVE_STRNDUP@ ! # undef strndup ! # define strndup rpl_strndup ! # if ! @HAVE_DECL_STRNDUP@ extern char *strndup (char const *__string, size_t __n); # endif #endif /* Find the length (number of bytes) of STRING, but scan at most MAXLEN bytes. If no '\0' terminator is found in that many bytes, return MAXLEN. */ ! #if ! @HAVE_DECL_STRNLEN@ extern size_t strnlen (char const *__string, size_t __maxlen); #endif /* Find the first occurrence in S of any character in ACCEPT. */ ! #if ! @HAVE_STRPBRK@ extern char *strpbrk (char const *__s, char const *__accept); #endif /* Search the next delimiter (char listed in DELIM) starting at *STRINGP. --- 84,159 ---- No known system has a strcasecmp() function that works correctly in multibyte locales. Therefore use our version always, if the strcase module is available. */ ! #if @GNULIB_STRCASE@ ! # if @REPLACE_STRCASECMP@ ! # define strcasecmp rpl_strcasecmp extern int strcasecmp (char const *__s1, char const *__s2); + # endif + #else + # define strcasecmp strcasecmp_is_unportable__use_gnulib_module_strcase_for_portability #endif /* Compare no more than N bytes of strings S1 and S2, ignoring case, returning less than, equal to or greater than zero if S1 is lexicographically less than, equal to or greater than S2. Note: This function cannot work correctly in multibyte locales. */ ! #if @GNULIB_STRCASE@ ! # if ! @HAVE_DECL_STRNCASECMP@ extern int strncasecmp (char const *__s1, char const *__s2, size_t __n); + # endif + #else + # define strncasecmp strncasecmp_is_unportable__use_gnulib_module_strcase_for_portability #endif /* Find the first occurrence of C in S or the final NUL byte. */ ! #if @GNULIB_STRCHRNUL@ ! # if ! @HAVE_STRCHRNUL@ extern char *strchrnul (char const *__s, int __c_in); + # endif + #else + # define strchrnul strchrnul_is_unportable__use_gnulib_module_strchrnul_for_portability #endif /* Duplicate S, returning an identical malloc'd string. */ ! #if @GNULIB_STRDUP@ ! # if ! @HAVE_DECL_STRDUP@ && ! defined strdup extern char *strdup (char const *__s); + # endif + #else + # define strdup strdup_is_unportable__use_gnulib_module_strdup_for_portability #endif /* Return a newly allocated copy of at most N bytes of STRING. */ ! #if @GNULIB_STRNDUP@ ! # if ! @HAVE_STRNDUP@ ! # undef strndup ! # define strndup rpl_strndup ! # if ! @HAVE_DECL_STRNDUP@ extern char *strndup (char const *__string, size_t __n); + # endif # endif + #else + # define strndup strndup_is_unportable__use_gnulib_module_strndup_for_portability #endif /* Find the length (number of bytes) of STRING, but scan at most MAXLEN bytes. If no '\0' terminator is found in that many bytes, return MAXLEN. */ ! #if @GNULIB_STRNLEN@ ! # if ! @HAVE_DECL_STRNLEN@ extern size_t strnlen (char const *__string, size_t __maxlen); + # endif + #else + # define strnlen strnlen_is_unportable__use_gnulib_module_strnlen_for_portability #endif /* Find the first occurrence in S of any character in ACCEPT. */ ! #if @GNULIB_STRPBRK@ ! # if ! @HAVE_STRPBRK@ extern char *strpbrk (char const *__s, char const *__accept); + # endif + #else + # define strpbrk strpbrk_is_unportable__use_gnulib_module_strpbrk_for_portability #endif /* Search the next delimiter (char listed in DELIM) starting at *STRINGP. *************** *** 124,151 **** characters are ASCII characters < 0x30. See also strtok_r(). */ ! #if ! @HAVE_STRSEP@ extern char *strsep (char **restrict __stringp, char const *restrict __delim); #endif /* Find the first occurrence of NEEDLE in HAYSTACK. No known system has a strstr() function that works correctly in multibyte locales. Therefore use our version always, if the strstr module is available. */ ! #if @REPLACE_STRSTR@ ! # undef strstr ! # define strstr rpl_strstr extern char *strstr (char const *__haystack, char const *__needle); #endif /* Find the first occurrence of NEEDLE in HAYSTACK, using case-insensitive comparison. Note: This function may, in multibyte locales, return success even if strlen (haystack) < strlen (needle) ! */ ! #if @REPLACE_STRCASESTR@ ! # undef strcasestr ! # define strcasestr rpl_strcasestr extern char *strcasestr (const char *haystack, const char *needle); #endif /* Parse S into tokens separated by characters in DELIM. --- 172,211 ---- characters are ASCII characters < 0x30. See also strtok_r(). */ ! #if @GNULIB_STRSEP@ ! # if ! @HAVE_STRSEP@ extern char *strsep (char **restrict __stringp, char const *restrict __delim); + # endif + #else + # define strsep strsep_is_unportable__use_gnulib_module_strsep_for_portability #endif /* Find the first occurrence of NEEDLE in HAYSTACK. No known system has a strstr() function that works correctly in multibyte locales. Therefore use our version always, if the strstr module is available. */ ! #if @GNULIB_STRSTR@ ! # if @REPLACE_STRSTR@ ! # undef strstr ! # define strstr rpl_strstr extern char *strstr (char const *__haystack, char const *__needle); + # endif + #else + # define strstr strstr_is_unportable__use_gnulib_module_strstr_for_portability #endif /* Find the first occurrence of NEEDLE in HAYSTACK, using case-insensitive comparison. Note: This function may, in multibyte locales, return success even if strlen (haystack) < strlen (needle) ! */ ! #if @GNULIB_STRCASESTR@ ! # if @REPLACE_STRCASESTR@ ! # undef strcasestr ! # define strcasestr rpl_strcasestr extern char *strcasestr (const char *haystack, const char *needle); + # endif + #else + # define strcasestr strcasestr_is_unportable__use_gnulib_module_strcasestr_for_portability #endif /* Parse S into tokens separated by characters in DELIM. *************** *** 170,178 **** characters are ASCII characters < 0x30. See also strsep(). */ ! #if ! @HAVE_DECL_STRTOK_R@ extern char *strtok_r (char *restrict __s, char const *restrict __sep, char **restrict __lasts); #endif #ifdef __cplusplus --- 230,242 ---- characters are ASCII characters < 0x30. See also strsep(). */ ! #if @GNULIB_STRTOK_R@ ! # if ! @HAVE_DECL_STRTOK_R@ extern char *strtok_r (char *restrict __s, char const *restrict __sep, char **restrict __lasts); + # endif + #else + # define strtok_r strtok_r_is_unportable__use_gnulib_module_strtok_r_for_portability #endif #ifdef __cplusplus *** modules/string 27 Jan 2007 13:17:16 -0000 1.2 --- modules/string 27 Jan 2007 15:01:40 -0000 *************** *** 21,26 **** --- 21,42 ---- rm -f [EMAIL PROTECTED] $@ { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \ sed -e 's|@''ABSOLUTE_STRING_H''@|$(ABSOLUTE_STRING_H)|g' \ + -e 's|@''GNULIB_MEMMEM''@|$(GNULIB_MEMMEM)|g' \ + -e 's|@''GNULIB_MEMPCPY''@|$(GNULIB_MEMPCPY)|g' \ + -e 's|@''GNULIB_MEMRCHR''@|$(GNULIB_MEMRCHR)|g' \ + -e 's|@''GNULIB_STPCPY''@|$(GNULIB_STPCPY)|g' \ + -e 's|@''GNULIB_STPNCPY''@|$(GNULIB_STPNCPY)|g' \ + -e 's|@''GNULIB_STRCASE''@|$(GNULIB_STRCASE)|g' \ + -e 's|@''GNULIB_STRCASE''@|$(GNULIB_STRCASE)|g' \ + -e 's|@''GNULIB_STRCHRNUL''@|$(GNULIB_STRCHRNUL)|g' \ + -e 's|@''GNULIB_STRDUP''@|$(GNULIB_STRDUP)|g' \ + -e 's|@''GNULIB_STRNDUP''@|$(GNULIB_STRNDUP)|g' \ + -e 's|@''GNULIB_STRNLEN''@|$(GNULIB_STRNLEN)|g' \ + -e 's|@''GNULIB_STRPBRK''@|$(GNULIB_STRPBRK)|g' \ + -e 's|@''GNULIB_STRSEP''@|$(GNULIB_STRSEP)|g' \ + -e 's|@''GNULIB_STRSTR''@|$(GNULIB_STRSTR)|g' \ + -e 's|@''GNULIB_STRCASESTR''@|$(GNULIB_STRCASESTR)|g' \ + -e 's|@''GNULIB_STRTOK_R''@|$(GNULIB_STRTOK_R)|g' \ -e 's|@''HAVE_DECL_MEMMEM''@|$(HAVE_DECL_MEMMEM)|g' \ -e 's|@''HAVE_MEMPCPY''@|$(HAVE_MEMPCPY)|g' \ -e 's|@''HAVE_DECL_MEMRCHR''@|$(HAVE_DECL_MEMRCHR)|g' \ *** modules/memmem 26 Jan 2007 22:16:55 -0000 1.4 --- modules/memmem 27 Jan 2007 15:01:40 -0000 *************** *** 10,15 **** --- 10,16 ---- configure.ac: gl_FUNC_MEMMEM + gl_STRING_MODULE_INDICATOR([memmem]) Makefile.am: *** modules/mempcpy 26 Jan 2007 22:16:55 -0000 1.8 --- modules/mempcpy 27 Jan 2007 15:01:40 -0000 *************** *** 10,15 **** --- 10,16 ---- configure.ac: gl_FUNC_MEMPCPY + gl_STRING_MODULE_INDICATOR([mempcpy]) Makefile.am: *** modules/memrchr 26 Jan 2007 22:16:55 -0000 1.9 --- modules/memrchr 27 Jan 2007 15:01:40 -0000 *************** *** 10,15 **** --- 10,16 ---- configure.ac: gl_FUNC_MEMRCHR + gl_STRING_MODULE_INDICATOR([memrchr]) Makefile.am: *** modules/stpcpy 26 Jan 2007 22:16:55 -0000 1.8 --- modules/stpcpy 27 Jan 2007 15:01:40 -0000 *************** *** 10,15 **** --- 10,16 ---- configure.ac: gl_FUNC_STPCPY + gl_STRING_MODULE_INDICATOR([stpcpy]) Makefile.am: *** modules/stpncpy 26 Jan 2007 22:16:55 -0000 1.5 --- modules/stpncpy 27 Jan 2007 15:01:40 -0000 *************** *** 10,15 **** --- 10,16 ---- configure.ac: gl_FUNC_STPNCPY + gl_STRING_MODULE_INDICATOR([stpncpy]) Makefile.am: *** modules/strcase 26 Jan 2007 22:16:55 -0000 1.10 --- modules/strcase 27 Jan 2007 15:01:40 -0000 *************** *** 13,18 **** --- 13,19 ---- configure.ac: gl_STRCASE + gl_STRING_MODULE_INDICATOR([strcase]) Makefile.am: *** modules/strcasestr 27 Jan 2007 13:17:16 -0000 1.4 --- modules/strcasestr 27 Jan 2007 15:01:40 -0000 *************** *** 12,17 **** --- 12,18 ---- configure.ac: gl_FUNC_STRCASESTR + gl_STRING_MODULE_INDICATOR([strcasestr]) Makefile.am: *** modules/strchrnul 26 Jan 2007 22:16:55 -0000 1.7 --- modules/strchrnul 27 Jan 2007 15:01:40 -0000 *************** *** 10,15 **** --- 10,16 ---- configure.ac: gl_FUNC_STRCHRNUL + gl_STRING_MODULE_INDICATOR([strchrnul]) Makefile.am: *** modules/strdup 26 Jan 2007 22:16:55 -0000 1.10 --- modules/strdup 27 Jan 2007 15:01:40 -0000 *************** *** 10,15 **** --- 10,16 ---- configure.ac: gl_FUNC_STRDUP + gl_STRING_MODULE_INDICATOR([strdup]) Makefile.am: *** modules/strndup 26 Jan 2007 22:16:55 -0000 1.11 --- modules/strndup 27 Jan 2007 15:01:40 -0000 *************** *** 11,16 **** --- 11,17 ---- configure.ac: gl_FUNC_STRNDUP + gl_STRING_MODULE_INDICATOR([strndup]) Makefile.am: *** modules/strnlen 26 Jan 2007 22:16:55 -0000 1.12 --- modules/strnlen 27 Jan 2007 15:01:40 -0000 *************** *** 11,16 **** --- 11,17 ---- configure.ac: gl_FUNC_STRNLEN + gl_STRING_MODULE_INDICATOR([strnlen]) Makefile.am: *** modules/strpbrk 26 Jan 2007 22:16:55 -0000 1.7 --- modules/strpbrk 27 Jan 2007 15:01:40 -0000 *************** *** 10,15 **** --- 10,16 ---- configure.ac: gl_FUNC_STRPBRK + gl_STRING_MODULE_INDICATOR([strpbrk]) Makefile.am: *** modules/strsep 26 Jan 2007 22:16:55 -0000 1.4 --- modules/strsep 27 Jan 2007 15:01:40 -0000 *************** *** 11,16 **** --- 11,17 ---- configure.ac: gl_FUNC_STRSEP + gl_STRING_MODULE_INDICATOR([strsep]) Makefile.am: *** modules/strstr 26 Jan 2007 22:16:55 -0000 1.9 --- modules/strstr 27 Jan 2007 15:01:40 -0000 *************** *** 12,17 **** --- 12,18 ---- configure.ac: gl_FUNC_STRSTR + gl_STRING_MODULE_INDICATOR([strstr]) Makefile.am: *** modules/strtok_r 26 Jan 2007 22:16:55 -0000 1.6 --- modules/strtok_r 27 Jan 2007 15:01:40 -0000 *************** *** 10,15 **** --- 10,16 ---- configure.ac: gl_FUNC_STRTOK_R + gl_STRING_MODULE_INDICATOR([strtok_r]) Makefile.am: