changeset 12839:c6d23570567d

Work around getdelim() bug on FreeBSD 8.0.
author Bruno Haible <bruno@clisp.org>
date Sun, 31 Jan 2010 17:43:25 +0100
parents ed6e77e08386
children fec628f2b5af
files ChangeLog doc/posix-functions/getdelim.texi lib/stdio.in.h m4/getdelim.m4 m4/stdio_h.m4 modules/stdio tests/test-getdelim.c
diffstat 7 files changed, 101 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2010-01-31  Bruno Haible  <bruno@clisp.org>
+
+	Work around getdelim() bug on FreeBSD 8.0.
+	* m4/getdelim.m4 (gl_FUNC_GETDELIM): Test whether getdelim supports an
+	initially NULL line. Set REPLACE_GETDELIM if getdelim exists but does
+	not work.
+	* lib/stdio.in.h (getdelim): Define as an alias if REPLACE_GETDELIM
+	is 1.
+	* m4/stdio_h.m4 (gl_STDIO_H_DEFAULTS): Initialize REPLACE_GETDELIM.
+	* modules/stdio (Makefile.am): Also substitute REPLACE_GETDELIM.
+	* tests/test-getdelim.c (main): Also test result for a NULL buffer and
+	a non-zero size.
+	* doc/posix-functions/getdelim.texi: Mention the FreeBSD bug.
+
 2010-01-31  Bruno Haible  <bruno@clisp.org>
 
 	Avoid redundant symbol replacement.
--- a/doc/posix-functions/getdelim.texi
+++ b/doc/posix-functions/getdelim.texi
@@ -14,6 +14,10 @@
 @item
 This function is missing a declaration on some platforms:
 BeOS.
+@item
+This function crashes when passed a pointer to a NULL buffer together with a
+pointer to a non-zero buffer size on some platforms:
+FreeBSD 8.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
--- a/lib/stdio.in.h
+++ b/lib/stdio.in.h
@@ -340,7 +340,11 @@
 #endif
 
 #if @GNULIB_GETDELIM@
-# if !@HAVE_DECL_GETDELIM@
+# if @REPLACE_GETDELIM@
+#  undef getdelim
+#  define getdelim rpl_getdelim
+# endif
+# if !@HAVE_DECL_GETDELIM@ || @REPLACE_GETDELIM@
 /* Read input, up to (and including) the next occurrence of DELIMITER, from
    STREAM, store it in *LINEPTR (and NUL-terminate it).
    *LINEPTR is a pointer returned from malloc (or NULL), pointing to *LINESIZE
--- a/m4/getdelim.m4
+++ b/m4/getdelim.m4
@@ -1,6 +1,6 @@
-# getdelim.m4 serial 5
+# getdelim.m4 serial 6
 
-dnl Copyright (C) 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
+dnl Copyright (C) 2005-2007, 2009-2010 Free Software Foundation, Inc.
 dnl
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -15,16 +15,71 @@
   dnl Persuade glibc <stdio.h> to declare getdelim().
   AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
 
-  AC_REPLACE_FUNCS([getdelim])
   AC_CHECK_DECLS_ONCE([getdelim])
 
-  if test $ac_cv_func_getdelim = no; then
-    gl_PREREQ_GETDELIM
+  AC_CHECK_FUNCS_ONCE([getdelim])
+  if test $ac_cv_func_getdelim = yes; then
+    dnl Found it in some library.  Verify that it works.
+    AC_CACHE_CHECK([for working getdelim function], [gl_cv_func_working_getdelim],
+    [echo fooNbarN | tr -d '\012' | tr N '\012' > conftest.data
+    AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#    include <stdio.h>
+#    include <stdlib.h>
+#    include <string.h>
+    int main ()
+    {
+      FILE *in = fopen ("./conftest.data", "r");
+      if (!in)
+        return 1;
+      {
+        /* Test result for a NULL buffer and a zero size.
+           Based on a test program from Karl Heuer.  */
+        char *line = NULL;
+        size_t siz = 0;
+        int len = getdelim (&line, &siz, '\n', in);
+        if (!(len == 4 && line && strcmp (line, "foo\n") == 0))
+          return 1;
+      }
+      {
+        /* Test result for a NULL buffer and a non-zero size.
+           This crashes on FreeBSD 8.0.  */
+        char *line = NULL;
+        size_t siz = (size_t)(~0) / 4;
+        if (getdelim (&line, &siz, '\n', in) == -1)
+          return 1;
+      }
+      return 0;
+    }
+    ]])], [gl_cv_func_working_getdelim=yes] dnl The library version works.
+    , [gl_cv_func_working_getdelim=no] dnl The library version does NOT work.
+    , dnl We're cross compiling. Assume it works on glibc2 systems.
+      [AC_EGREP_CPP([Lucky GNU user],
+         [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ >= 2)
+  Lucky GNU user
+ #endif
+#endif
+         ],
+         [gl_cv_func_working_getdelim=yes],
+         [gl_cv_func_working_getdelim=no])]
+    )])
+  else
+    gl_cv_func_working_getdelim=no
   fi
 
   if test $ac_cv_have_decl_getdelim = no; then
     HAVE_DECL_GETDELIM=0
   fi
+
+  if test $gl_cv_func_working_getdelim = no; then
+    if test $ac_cv_func_getdelim = yes; then
+      REPLACE_GETDELIM=1
+    fi
+    AC_LIBOBJ([getdelim])
+    gl_PREREQ_GETDELIM
+  fi
 ])
 
 # Prerequisites of lib/getdelim.c.
--- a/m4/stdio_h.m4
+++ b/m4/stdio_h.m4
@@ -1,4 +1,4 @@
-# stdio_h.m4 serial 24
+# stdio_h.m4 serial 25
 dnl Copyright (C) 2007-2010 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -111,6 +111,7 @@
   REPLACE_FSEEKO=0;              AC_SUBST([REPLACE_FSEEKO])
   REPLACE_FTELL=0;               AC_SUBST([REPLACE_FTELL])
   REPLACE_FTELLO=0;              AC_SUBST([REPLACE_FTELLO])
+  REPLACE_GETDELIM=0;            AC_SUBST([REPLACE_GETDELIM])
   REPLACE_GETLINE=0;             AC_SUBST([REPLACE_GETLINE])
   REPLACE_OBSTACK_PRINTF=0;      AC_SUBST([REPLACE_OBSTACK_PRINTF])
   REPLACE_PERROR=0;              AC_SUBST([REPLACE_PERROR])
--- a/modules/stdio
+++ b/modules/stdio
@@ -89,6 +89,7 @@
 	      -e 's|@''REPLACE_FSEEKO''@|$(REPLACE_FSEEKO)|g' \
 	      -e 's|@''REPLACE_FTELL''@|$(REPLACE_FTELL)|g' \
 	      -e 's|@''REPLACE_FTELLO''@|$(REPLACE_FTELLO)|g' \
+	      -e 's|@''REPLACE_GETDELIM''@|$(REPLACE_GETDELIM)|g' \
 	      -e 's|@''REPLACE_GETLINE''@|$(REPLACE_GETLINE)|g' \
 	      -e 's|@''REPLACE_OBSTACK_PRINTF''@|$(REPLACE_OBSTACK_PRINTF)|g' \
 	      -e 's|@''REPLACE_PERROR''@|$(REPLACE_PERROR)|g' \
--- a/tests/test-getdelim.c
+++ b/tests/test-getdelim.c
@@ -33,13 +33,13 @@
 main (void)
 {
   FILE *f;
-  char *line = NULL;
-  size_t len = 0;
+  char *line;
+  size_t len;
   ssize_t result;
 
   /* Create test file.  */
   f = fopen ("test-getdelim.txt", "wb");
-  if (!f || fwrite ("anbcnd\0f", 1, 8, f) != 8 || fclose (f) != 0)
+  if (!f || fwrite ("anAnbcnd\0f", 1, 10, f) != 10 || fclose (f) != 0)
     {
       fputs ("Failed to create sample file.\n", stderr);
       remove ("test-getdelim.txt");
@@ -54,13 +54,24 @@
     }
 
   /* Test initial allocation, which must include trailing NUL.  */
+  line = NULL;
+  len = 0;
   result = getdelim (&line, &len, 'n', f);
   ASSERT (result == 2);
   ASSERT (strcmp (line, "an") == 0);
   ASSERT (2 < len);
+  free (line);
+
+  /* Test initial allocation again, with line = NULL and len != 0.  */
+  line = NULL;
+  len = (size_t)(~0) / 4;
+  result = getdelim (&line, &len, 'n', f);
+  ASSERT (result == 2);
+  ASSERT (strcmp (line, "An") == 0);
+  ASSERT (2 < len);
+  free (line);
 
   /* Test growth of buffer.  */
-  free (line);
   line = malloc (1);
   len = 1;
   result = getdelim (&line, &len, 'n', f);