changeset 9161:c5cedfcd0db0

Move getline and getdelim into stdio.h, per POSIX 200x. * modules/getline (Files): Remove getline.h. (Depends-on): Add stdio. (configure.ac): Add module indicator. * modules/getdelim (Files): Remove getdelim.h. (Depends-on): Add stdio. (configure.ac): Add module indicator. * modules/stdio (Makefile.am): Work with new indicators. * m4/stdio_h.m4 (gl_STDIO_H_DEFAULTS): Add new defaults. * m4/getdelim.m4 (gl_FUNC_GETDELIM): Work with stdio needs. * m4/getline.m4 (gl_FUNC_GETLINE): Likewise. * lib/getdelim.h: Delete. * lib/getline.h: Delete. * lib/stdio_.h (getdelim, getline): Declare. * modules/getdelim-tests: New module. * modules/getline-tests: Likewise. * tests/test-getdelim.c: New file. * tests/test-getline.c: Likewise. * NEWS: Document the change. * lib/getline.c: Update choice of header. * lib/csharpcomp.c: Likewise. * lib/getpass.c: Likewise. * lib/javacomp.c: Likewise. * lib/javaversion.c: Likewise. * lib/yesno.c: Likewise. * lib/getdelim.c: Likewise. (getdelim): Set errno on failure, and avoid memory leak.
author Eric Blake <ebb9@byu.net>
date Wed, 22 Aug 2007 12:54:21 +0000
parents 3ba41f99309f
children 60a8f78ca96f
files ChangeLog NEWS lib/csharpcomp.c lib/getdelim.c lib/getline.c lib/getpass.c lib/javacomp.c lib/javaversion.c lib/stdio_.h lib/yesno.c m4/getdelim.m4 m4/getline.m4 m4/stdio_h.m4 modules/getdelim modules/getdelim-tests modules/getline modules/getline-tests modules/stdio tests/test-getdelim.c tests/test-getline.c
diffstat 20 files changed, 320 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Aug 20 22:09:08 2007 +0000
+++ b/ChangeLog	Wed Aug 22 12:54:21 2007 +0000
@@ -1,3 +1,33 @@
+2007-08-22  Eric Blake  <ebb9@byu.net>
+
+	Move getline and getdelim into stdio.h, per POSIX 200x.
+	* modules/getline (Files): Remove getline.h.
+	(Depends-on): Add stdio.
+	(configure.ac): Add module indicator.
+	* modules/getdelim (Files): Remove getdelim.h.
+	(Depends-on): Add stdio.
+	(configure.ac): Add module indicator.
+	* modules/stdio (Makefile.am): Work with new indicators.
+	* m4/stdio_h.m4 (gl_STDIO_H_DEFAULTS): Add new defaults.
+	* m4/getdelim.m4 (gl_FUNC_GETDELIM): Work with stdio needs.
+	* m4/getline.m4 (gl_FUNC_GETLINE): Likewise.
+	* lib/getdelim.h: Delete.
+	* lib/getline.h: Delete.
+	* lib/stdio_.h (getdelim, getline): Declare.
+	* modules/getdelim-tests: New module.
+	* modules/getline-tests: Likewise.
+	* tests/test-getdelim.c: New file.
+	* tests/test-getline.c: Likewise.
+	* NEWS: Document the change.
+	* lib/getline.c: Update choice of header.
+	* lib/csharpcomp.c: Likewise.
+	* lib/getpass.c: Likewise.
+	* lib/javacomp.c: Likewise.
+	* lib/javaversion.c: Likewise.
+	* lib/yesno.c: Likewise.
+	* lib/getdelim.c: Likewise.
+	(getdelim): Set errno on failure, and avoid memory leak.
+
 2007-08-19  Bruno Haible  <bruno@clisp.org>
 
 	* modules/closein (Depends-on): Add freadahead.
--- a/NEWS	Mon Aug 20 22:09:08 2007 +0000
+++ b/NEWS	Wed Aug 22 12:54:21 2007 +0000
@@ -6,6 +6,10 @@
 
 Date        Modules         Changes
 
+2007-08-22  getdelim, getline
+                            The include file is changed from "getdelim.h"
+                            and "getline.h" to the POSIX 200x <stdio.h>.
+
 2007-08-18  idcache         Now provides prototypes in "idcache.h".
 
 2007-08-10  xstrtol         The STRTOL_FATAL_ERROR macro is removed.
--- a/lib/csharpcomp.c	Mon Aug 20 22:09:08 2007 +0000
+++ b/lib/csharpcomp.c	Wed Aug 22 12:54:21 2007 +0000
@@ -30,7 +30,6 @@
 #include "execute.h"
 #include "pipe.h"
 #include "wait-process.h"
-#include "getline.h"
 #include "sh-quote.h"
 #include "safe-read.h"
 #include "xmalloca.h"
--- a/lib/getdelim.c	Mon Aug 20 22:09:08 2007 +0000
+++ b/lib/getdelim.c	Wed Aug 22 12:54:21 2007 +0000
@@ -1,5 +1,5 @@
 /* getdelim.c --- Implementation of replacement getdelim function.
-   Copyright (C) 1994, 1996, 1997, 1998, 2001, 2003, 2005, 2006 Free
+   Copyright (C) 1994, 1996, 1997, 1998, 2001, 2003, 2005, 2006, 2007 Free
    Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
@@ -21,7 +21,7 @@
 
 #include <config.h>
 
-#include "getdelim.h"
+#include <stdio.h>
 
 #include <limits.h>
 #include <stdlib.h>
@@ -42,6 +42,11 @@
 # define funlockfile(x) ((void) 0)
 #endif
 
+/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW.  */
+#ifndef EOVERFLOW
+# define EOVERFLOW E2BIG
+#endif
+
 /* Read up to (and including) a DELIMITER from FP into *LINEPTR (and
    NUL-terminate it).  *LINEPTR is a pointer returned from malloc (or
    NULL), pointing to *N characters of space.  It is realloc'ed as
@@ -62,10 +67,10 @@
 
   flockfile (fp);
 
-  if (*lineptr == NULL || *n == 0)
+  if (*n == 0)
     {
       *n = 120;
-      *lineptr = (char *) malloc (*n);
+      *lineptr = (char *) realloc (*lineptr, 120);
       if (*lineptr == NULL)
 	{
 	  result = -1;
@@ -97,6 +102,7 @@
 	  if (cur_len + 1 >= needed)
 	    {
 	      result = -1;
+	      errno = EOVERFLOW;
 	      goto unlock_return;
 	    }
 
--- a/lib/getline.c	Mon Aug 20 22:09:08 2007 +0000
+++ b/lib/getline.c	Wed Aug 22 12:54:21 2007 +0000
@@ -1,5 +1,5 @@
 /* getline.c --- Implementation of replacement getline function.
-   Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+   Copyright (C) 2005, 2006, 2007 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
@@ -20,8 +20,7 @@
 
 #include <config.h>
 
-#include "getdelim.h"
-#include "getline.h"
+#include <stdio.h>
 
 ssize_t
 getline (char **lineptr, size_t *n, FILE *stream)
--- a/lib/getpass.c	Mon Aug 20 22:09:08 2007 +0000
+++ b/lib/getpass.c	Wed Aug 22 12:54:21 2007 +0000
@@ -41,8 +41,6 @@
 # include <termios.h>
 #endif
 
-#include "getline.h"
-
 #if USE_UNLOCKED_IO
 # include "unlocked-io.h"
 #else
--- a/lib/javacomp.c	Mon Aug 20 22:09:08 2007 +0000
+++ b/lib/javacomp.c	Wed Aug 22 12:54:21 2007 +0000
@@ -42,7 +42,6 @@
 #include "safe-read.h"
 #include "xalloc.h"
 #include "xmalloca.h"
-#include "getline.h"
 #include "filename.h"
 #include "fwriteerror.h"
 #include "clean-temp.h"
--- a/lib/javaversion.c	Mon Aug 20 22:09:08 2007 +0000
+++ b/lib/javaversion.c	Wed Aug 22 12:54:21 2007 +0000
@@ -1,5 +1,5 @@
 /* Determine the Java version supported by javaexec.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2007 Free Software Foundation, Inc.
    Written by Bruno Haible <bruno@clisp.org>, 2006.
 
    This program is free software; you can redistribute it and/or modify
@@ -38,7 +38,6 @@
 #include "pipe.h"
 #include "wait-process.h"
 #include "error.h"
-#include "getline.h"
 #include "gettext.h"
 
 #define _(str) gettext (str)
--- a/lib/stdio_.h	Mon Aug 20 22:09:08 2007 +0000
+++ b/lib/stdio_.h	Wed Aug 22 12:54:21 2007 +0000
@@ -35,8 +35,11 @@
 #include <stdarg.h>
 #include <stddef.h>
 
-#if (@GNULIB_FSEEKO@ && @REPLACE_FSEEKO@) || (@GNULIB_FTELLO@ && @REPLACE_FTELLO@)
-/* Get off_t.  */
+#if (@GNULIB_FSEEKO@ && @REPLACE_FSEEKO@) \
+  || (@GNULIB_FTELLO@ && @REPLACE_FTELLO@) \
+  || (@GNULIB_GETDELIM@ && !@HAVE_DECL_GETDELIM@) \
+  || (@GNULIB_GETLINE@ && (!@HAVE_DECL_GETLINE@ || @REPLACE_GETLINE@))
+/* Get off_t and ssize_t.  */
 # include <sys/types.h>
 #endif
 
@@ -303,6 +306,44 @@
     fflush (f))
 #endif
 
+#if @GNULIB_GETDELIM@
+# if !@HAVE_DECL_GETDELIM@
+  /* Read up to (and including) a DELIMITER from FP into *LINEPTR (and
+     NUL-terminate it).  *LINEPTR is a pointer returned from malloc (or
+     NULL), pointing to *N characters of space.  It is realloc'ed as
+     necessary.  Returns the number of characters read (not including
+     the null terminator), or -1 on error or EOF.  */
+  extern ssize_t getdelim (char **, size_t *, int delim, FILE *);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef getdelim
+# define getdelim(l, s, d, f)					    \
+  (GL_LINK_WARNING ("getdelim is unportable - "			    \
+		    "use gnulib module getdelim for portability"),  \
+   getdelim (l, s, d, f))
+#endif
+
+#if @GNULIB_GETLINE@
+# if @REPLACE_GETLINE@
+#  undef getline
+#  define getline rpl_getline
+# endif
+# if !@HAVE_DECL_GETLINE@
+  /* Read up to (and including) a newline from FP into *LINEPTR (and
+     NUL-terminate it).  *LINEPTR is a pointer returned from malloc (or
+     NULL), pointing to *N characters of space.  It is realloc'ed as
+     necessary.  Returns the number of characters read (not including
+     the null terminator), or -1 on error or EOF.  */
+  extern ssize_t getline (char **, size_t *, FILE *);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef getline
+# define getline(l, s, f)						\
+  (GL_LINK_WARNING ("getline is unportable - "				\
+		    "use gnulib module getline for portability"),	\
+   getline (l, s, f))
+#endif
+
 #ifdef __cplusplus
 }
 #endif
--- a/lib/yesno.c	Mon Aug 20 22:09:08 2007 +0000
+++ b/lib/yesno.c	Wed Aug 22 12:54:21 2007 +0000
@@ -24,10 +24,6 @@
 #include <stdlib.h>
 #include <stdio.h>
 
-#if ENABLE_NLS
-# include "getline.h"
-#endif
-
 extern int rpmatch (char const *response);
 
 /* Return true if we read an affirmative line from standard input.
--- a/m4/getdelim.m4	Mon Aug 20 22:09:08 2007 +0000
+++ b/m4/getdelim.m4	Wed Aug 22 12:54:21 2007 +0000
@@ -1,6 +1,6 @@
-# getdelim.m4 serial 2
+# getdelim.m4 serial 3
 
-dnl Copyright (C) 2005, 2006 Free Software dnl Foundation, Inc.
+dnl Copyright (C) 2005, 2006, 2007 Free Software dnl Foundation, Inc.
 dnl
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -10,6 +10,7 @@
 
 AC_DEFUN([gl_FUNC_GETDELIM],
 [
+  AC_REQUIRE([gl_STDIO_H_DEFAULTS])
 
   dnl Persuade glibc <stdio.h> to declare getdelim().
   AC_REQUIRE([AC_GNU_SOURCE])
@@ -19,6 +20,7 @@
 
   if test $ac_cv_func_getdelim = no; then
     gl_PREREQ_GETDELIM
+    HAVE_DECL_GETDELIM=0
   fi
 ])
 
--- a/m4/getline.m4	Mon Aug 20 22:09:08 2007 +0000
+++ b/m4/getline.m4	Wed Aug 22 12:54:21 2007 +0000
@@ -1,7 +1,7 @@
-# getline.m4 serial 15
+# getline.m4 serial 16
 
-dnl Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006 Free Software
-dnl Foundation, Inc.
+dnl Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007 Free
+dnl 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,6 +15,8 @@
 dnl to do with the function we need.
 AC_DEFUN([gl_FUNC_GETLINE],
 [
+  AC_REQUIRE([gl_STDIO_H_DEFAULTS])
+
   dnl Persuade glibc <stdio.h> to declare getline().
   AC_REQUIRE([AC_GNU_SOURCE])
 
@@ -60,12 +62,12 @@
     )])
   fi
 
+  if test $ac_cv_func_getline = no; then
+    HAVE_DECL_GETLINE=0
+  fi
+
   if test $am_cv_func_working_getline = no; then
-    dnl We must choose a different name for our function, since on ELF systems
-    dnl a broken getline() in libc.so would override our getline() in
-    dnl libgettextlib.so.
-    AC_DEFINE([getline], [gnu_getline],
-      [Define to a replacement function name for getline().])
+    REPLACE_GETLINE=1
     AC_LIBOBJ(getline)
 
     gl_PREREQ_GETLINE
--- a/m4/stdio_h.m4	Mon Aug 20 22:09:08 2007 +0000
+++ b/m4/stdio_h.m4	Wed Aug 22 12:54:21 2007 +0000
@@ -33,6 +33,8 @@
   GNULIB_FTELL=0;          AC_SUBST([GNULIB_FTELL])
   GNULIB_FTELLO=0;         AC_SUBST([GNULIB_FTELLO])
   GNULIB_FFLUSH=0;         AC_SUBST([GNULIB_FFLUSH])
+  GNULIB_GETDELIM=0;       AC_SUBST([GNULIB_GETDELIM])
+  GNULIB_GETLINE=0;        AC_SUBST([GNULIB_GETLINE])
   dnl Assume proper GNU behavior unless another module says otherwise.
   REPLACE_FPRINTF=0;       AC_SUBST([REPLACE_FPRINTF])
   REPLACE_VFPRINTF=0;      AC_SUBST([REPLACE_VFPRINTF])
@@ -53,6 +55,9 @@
   REPLACE_FTELLO=0;        AC_SUBST([REPLACE_FTELLO])
   REPLACE_FTELL=0;         AC_SUBST([REPLACE_FTELL])
   REPLACE_FFLUSH=0;        AC_SUBST([REPLACE_FFLUSH])
+  HAVE_DECL_GETDELIM=1;    AC_SUBST([HAVE_DECL_GETDELIM])
+  HAVE_DECL_GETLINE=1;     AC_SUBST([HAVE_DECL_GETLINE])
+  REPLACE_GETLINE=0;       AC_SUBST([REPLACE_GETLINE])
 ])
 
 dnl Code shared by fseeko and ftello.  Determine if large files are supported,
--- a/modules/getdelim	Mon Aug 20 22:09:08 2007 +0000
+++ b/modules/getdelim	Wed Aug 22 12:54:21 2007 +0000
@@ -2,19 +2,20 @@
 Read character delimited data from a stream.
 
 Files:
-lib/getdelim.h
 lib/getdelim.c
 m4/getdelim.m4
 
 Depends-on:
+stdio
 
 configure.ac:
 gl_FUNC_GETDELIM
+gl_STDIO_MODULE_INDICATOR([getdelim])
 
 Makefile.am:
 
 Include:
-"getdelim.h"
+<stdio.h>
 
 License:
 LGPLv2+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/getdelim-tests	Wed Aug 22 12:54:21 2007 +0000
@@ -0,0 +1,11 @@
+Files:
+tests/test-getdelim.c
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-getdelim
+check_PROGRAMS += test-getdelim
+MOSTLYCLEANFILES += test-getdelim.txt
--- a/modules/getline	Mon Aug 20 22:09:08 2007 +0000
+++ b/modules/getline	Wed Aug 22 12:54:21 2007 +0000
@@ -2,20 +2,21 @@
 Read a line from a stream.
 
 Files:
-lib/getline.h
 lib/getline.c
 m4/getline.m4
 
 Depends-on:
 getdelim
+stdio
 
 configure.ac:
 gl_FUNC_GETLINE
+gl_STDIO_MODULE_INDICATOR([getline])
 
 Makefile.am:
 
 Include:
-"getline.h"
+<stdio.h>
 
 License:
 LGPLv2+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/getline-tests	Wed Aug 22 12:54:21 2007 +0000
@@ -0,0 +1,11 @@
+Files:
+tests/test-getline.c
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-getline
+check_PROGRAMS += test-getline
+MOSTLYCLEANFILES += test-getline.txt
--- a/modules/stdio	Mon Aug 20 22:09:08 2007 +0000
+++ b/modules/stdio	Wed Aug 22 12:54:21 2007 +0000
@@ -36,6 +36,8 @@
 	      -e 's|@''GNULIB_FTELL''@|$(GNULIB_FTELL)|g' \
 	      -e 's|@''GNULIB_FTELLO''@|$(GNULIB_FTELLO)|g' \
 	      -e 's|@''GNULIB_FFLUSH''@|$(GNULIB_FFLUSH)|g' \
+	      -e 's|@''GNULIB_GETDELIM''@|$(GNULIB_GETDELIM)|g' \
+	      -e 's|@''GNULIB_GETLINE''@|$(GNULIB_GETLINE)|g' \
 	      -e 's|@''REPLACE_FPRINTF''@|$(REPLACE_FPRINTF)|g' \
 	      -e 's|@''REPLACE_VFPRINTF''@|$(REPLACE_VFPRINTF)|g' \
 	      -e 's|@''REPLACE_PRINTF''@|$(REPLACE_PRINTF)|g' \
@@ -53,6 +55,9 @@
 	      -e 's|@''REPLACE_FTELLO''@|$(REPLACE_FTELLO)|g' \
 	      -e 's|@''REPLACE_FTELL''@|$(REPLACE_FTELL)|g' \
 	      -e 's|@''REPLACE_FFLUSH''@|$(REPLACE_FFLUSH)|g' \
+	      -e 's|@''HAVE_DECL_GETDELIM''@|$(HAVE_DECL_GETDELIM)|g' \
+	      -e 's|@''HAVE_DECL_GETLINE''@|$(HAVE_DECL_GETLINE)|g' \
+	      -e 's|@''REPLACE_GETLINE''@|$(REPLACE_GETLINE)|g' \
 	      -e '/definition of GL_LINK_WARNING/r $(LINK_WARNING_H)' \
 	      < $(srcdir)/stdio_.h; \
 	} > $@-t
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-getdelim.c	Wed Aug 22 12:54:21 2007 +0000
@@ -0,0 +1,89 @@
+/* Test of getdelim() function.
+   Copyright (C) 2007 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, 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, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Written by Eric Blake <ebb9@byu.net>, 2007.  */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ASSERT(expr) \
+  do									     \
+    {									     \
+      if (!(expr))							     \
+        {								     \
+          fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
+          abort ();							     \
+        }								     \
+    }									     \
+  while (0)
+
+int
+main (int argc, char **argv)
+{
+  FILE *f;
+  char *line = NULL;
+  size_t len = 0;
+  ssize_t result;
+
+  /* Create test file.  */
+  f = fopen ("test-getdelim.txt", "wb");
+  if (!f || fwrite ("anbcnd\0f", 1, 8, f) != 8 || fclose (f) != 0)
+    {
+      fputs ("Failed to create sample file.\n", stderr);
+      unlink ("test-getdelim.txt");
+      return 1;
+    }
+  f = fopen ("test-getdelim.txt", "rb");
+  if (!f)
+    {
+      fputs ("Failed to reopen sample file.\n", stderr);
+      unlink ("test-getdelim.txt");
+      return 1;
+    }
+
+  /* Test initial allocation, which must include trailing NUL.  */
+  result = getdelim (&line, &len, 'n', f);
+  ASSERT (result == 2);
+  ASSERT (strcmp (line, "an") == 0);
+  ASSERT (2 < len);
+
+  /* Test growth of buffer.  */
+  free (line);
+  line = malloc (1);
+  len = 1;
+  result = getdelim (&line, &len, 'n', f);
+  ASSERT (result == 3);
+  ASSERT (strcmp (line, "bcn") == 0);
+  ASSERT (3 < len);
+
+  /* Test embedded NULs and EOF behavior.  */
+  result = getdelim (&line, &len, 'n', f);
+  ASSERT (result == 3);
+  ASSERT (memcmp (line, "d\0f", 4) == 0);
+  ASSERT (3 < len);
+
+  result = getdelim (&line, &len, 'n', f);
+  ASSERT (result == -1);
+
+  free (line);
+  fclose (f);
+  unlink ("test-getdelim.txt");
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-getline.c	Wed Aug 22 12:54:21 2007 +0000
@@ -0,0 +1,89 @@
+/* Test of getline() function.
+   Copyright (C) 2007 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, 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, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Written by Eric Blake <ebb9@byu.net>, 2007.  */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ASSERT(expr) \
+  do									     \
+    {									     \
+      if (!(expr))							     \
+        {								     \
+          fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
+          abort ();							     \
+        }								     \
+    }									     \
+  while (0)
+
+int
+main (int argc, char **argv)
+{
+  FILE *f;
+  char *line = NULL;
+  size_t len = 0;
+  ssize_t result;
+
+  /* Create test file.  */
+  f = fopen ("test-getline.txt", "wb");
+  if (!f || fwrite ("a\nbc\nd\0f", 1, 8, f) != 8 || fclose (f) != 0)
+    {
+      fputs ("Failed to create sample file.\n", stderr);
+      unlink ("test-getline.txt");
+      return 1;
+    }
+  f = fopen ("test-getline.txt", "rb");
+  if (!f)
+    {
+      fputs ("Failed to reopen sample file.\n", stderr);
+      unlink ("test-getline.txt");
+      return 1;
+    }
+
+  /* Test initial allocation, which must include trailing NUL.  */
+  result = getline (&line, &len, f);
+  ASSERT (result == 2);
+  ASSERT (strcmp (line, "a\n") == 0);
+  ASSERT (2 < len);
+
+  /* Test growth of buffer, must not leak.  */
+  free (line);
+  line = malloc (1);
+  len = 0;
+  result = getline (&line, &len, f);
+  ASSERT (result == 3);
+  ASSERT (strcmp (line, "bc\n") == 0);
+  ASSERT (3 < len);
+
+  /* Test embedded NULs and EOF behavior.  */
+  result = getline (&line, &len, f);
+  ASSERT (result == 3);
+  ASSERT (memcmp (line, "d\0f", 4) == 0);
+  ASSERT (3 < len);
+
+  result = getline (&line, &len, f);
+  ASSERT (result == -1);
+
+  free (line);
+  fclose (f);
+  unlink ("test-getline.txt");
+  return 0;
+}