get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/808732/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 808732,
    "url": "http://patchwork.ozlabs.org/api/patches/808732/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/glibc/patch/20170901154335.E5819439942E3@oldenburg.str.redhat.com/",
    "project": {
        "id": 41,
        "url": "http://patchwork.ozlabs.org/api/projects/41/?format=api",
        "name": "GNU C Library",
        "link_name": "glibc",
        "list_id": "libc-alpha.sourceware.org",
        "list_email": "libc-alpha@sourceware.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20170901154335.E5819439942E3@oldenburg.str.redhat.com>",
    "list_archive_url": null,
    "date": "2017-09-01T15:43:35",
    "name": "libio: Implement internal function __libc_readline_unlocked",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "a2c839cc8369b9aadfe485a2c5b0d08c91e2674e",
    "submitter": {
        "id": 14312,
        "url": "http://patchwork.ozlabs.org/api/people/14312/?format=api",
        "name": "Florian Weimer",
        "email": "fweimer@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/glibc/patch/20170901154335.E5819439942E3@oldenburg.str.redhat.com/mbox/",
    "series": [
        {
            "id": 1051,
            "url": "http://patchwork.ozlabs.org/api/series/1051/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/glibc/list/?series=1051",
            "date": "2017-09-01T15:43:35",
            "name": "libio: Implement internal function __libc_readline_unlocked",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/1051/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/808732/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/808732/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<libc-alpha-return-84025-incoming=patchwork.ozlabs.org@sourceware.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": [
            "patchwork-incoming@bilbo.ozlabs.org",
            "mailing list libc-alpha@sourceware.org"
        ],
        "Authentication-Results": [
            "ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=sourceware.org\n\t(client-ip=209.132.180.131; helo=sourceware.org;\n\tenvelope-from=libc-alpha-return-84025-incoming=patchwork.ozlabs.org@sourceware.org;\n\treceiver=<UNKNOWN>)",
            "ozlabs.org; dkim=pass (1024-bit key;\n\tsecure) header.d=sourceware.org header.i=@sourceware.org\n\theader.b=\"h7I2OCAs\"; dkim-atps=neutral",
            "sourceware.org; auth=none",
            "ext-mx05.extmail.prod.ext.phx2.redhat.com;\n\tdmarc=none (p=none dis=none) header.from=redhat.com",
            "ext-mx05.extmail.prod.ext.phx2.redhat.com;\n\tspf=fail smtp.mailfrom=fweimer@redhat.com"
        ],
        "Received": [
            "from sourceware.org (server1.sourceware.org [209.132.180.131])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xkNnk1ZvJz9t3P\n\tfor <incoming@patchwork.ozlabs.org>;\n\tSat,  2 Sep 2017 01:43:53 +1000 (AEST)",
            "(qmail 115794 invoked by alias); 1 Sep 2017 15:43:46 -0000",
            "(qmail 115784 invoked by uid 89); 1 Sep 2017 15:43:45 -0000"
        ],
        "DomainKey-Signature": "a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id\n\t:list-unsubscribe:list-subscribe:list-archive:list-post\n\t:list-help:sender:date:to:subject:mime-version:content-type\n\t:content-transfer-encoding:message-id:from; q=dns; s=default; b=\n\tt25a69Ma6Vfq/EKjpAVYkHYLawd9Nj5hdzCVW/G99pqPKvgdxSRNNLc79svnVvgi\n\tYNUZbNgfDxtLX0qGeiGmcUyJvOojjTUcnSGfhOIQfWwbElTh0rrBATjBGEF6gQPc\n\tyZtHtsxSRCz8ozQtdv6Jy+SKR11cXYuC8Fgce4vgOAI=",
        "DKIM-Signature": "v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id\n\t:list-unsubscribe:list-subscribe:list-archive:list-post\n\t:list-help:sender:date:to:subject:mime-version:content-type\n\t:content-transfer-encoding:message-id:from; s=default; bh=pfVzH6\n\tjDhxzToSIkCYN/ufUXR2c=; b=h7I2OCAsCeHRI4JeH/NL+X1kUemOcG977f7Znh\n\t97Bcp8JKBrsc7tZuarx7U24IGteT0LZWn3FWp4rQv2yLVacNv2m/H2xQm8D5T6ja\n\tkwdbOpm8ilpte36U3fAW8tEpnG/co/hqOE1ql0Upo5a6XCX302aT7mpRAPrh/4aC\n\ttJsTk=",
        "Mailing-List": "contact libc-alpha-help@sourceware.org; run by ezmlm",
        "Precedence": "bulk",
        "List-Id": "<libc-alpha.sourceware.org>",
        "List-Unsubscribe": "<mailto:libc-alpha-unsubscribe-incoming=patchwork.ozlabs.org@sourceware.org>",
        "List-Subscribe": "<mailto:libc-alpha-subscribe@sourceware.org>",
        "List-Archive": "<http://sourceware.org/ml/libc-alpha/>",
        "List-Post": "<mailto:libc-alpha@sourceware.org>",
        "List-Help": "<mailto:libc-alpha-help@sourceware.org>,\n\t<http://sourceware.org/ml/#faqs>",
        "Sender": "libc-alpha-owner@sourceware.org",
        "X-Virus-Found": "No",
        "X-Spam-SWARE-Status": "No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0,\n\tGIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RP_MATCHES_RCVD,\n\tSPF_HELO_PASS autolearn=ham version=3.3.2 spammy=",
        "X-HELO": "mx1.redhat.com",
        "DMARC-Filter": "OpenDMARC Filter v1.3.2 mx1.redhat.com 94101BDEF",
        "Date": "Fri, 01 Sep 2017 17:43:35 +0200",
        "To": "libc-alpha@sourceware.org",
        "Subject": "[PATCH] libio: Implement internal function __libc_readline_unlocked",
        "User-Agent": "Heirloom mailx 12.5 7/5/10",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=us-ascii",
        "Content-Transfer-Encoding": "7bit",
        "Message-Id": "<20170901154335.E5819439942E3@oldenburg.str.redhat.com>",
        "From": "fweimer@redhat.com (Florian Weimer)"
    },
    "content": "This is a variant of fgets which fails with ERANGE if the\nbuffer is too small, and the buffer length is given as an\nargument of type size_t.\n\nThis function will be useful for implementing NSS file reading\noperations.  Compared to a direct implementation using the public API,\nit avoids an lseek system call in case the line terminator can be\nfound in the internal read buffer.\n\n2017-09-01  Florian Weimer  <fweimer@redhat.com>\n\n\t* include/stdio.h (__libc_readline_unlocked): Declare.\n\t(__ftello64, __fseeko64): Declare aliases.\n\t* libio/readline.c: New file.\n\t* libio/tst-readline.c: Likewise.\n\t(routines): Add readline.\n\t(tests-internal): Add tst-readlime.\n\t* libio/Versions (GLIBC_PRIVATE): Export __libc_readline_unlocked.\n\t* libio/fseeko.c (__fseeko): Rename from fseeko.\n\t(fseeko): Add alias.\n\t[__OFF_T_MATCHES_OFF64_T] (fseeko64, __fseeko64): Likewise.\n\t* libio/fseeko64.c (__fseeko64): Rename from fseeko64.\n\t(fseeko64): Add alias.\n\t* libio/ftello.c [__OFF_T_MATCHES_OFF64_T] (__ftello64): Add alias.\n\t* libio/ftello64.c (__ftello64): Rename from ftello64.\n\t(ftello64): Add alias.",
    "diff": "diff --git a/include/stdio.h b/include/stdio.h\nindex 509447c528..cae99ea5ea 100644\n--- a/include/stdio.h\n+++ b/include/stdio.h\n@@ -122,6 +122,18 @@ extern int __fxprintf (FILE *__fp, const char *__fmt, ...)\n extern int __fxprintf_nocancel (FILE *__fp, const char *__fmt, ...)\n      __attribute__ ((__format__ (__printf__, 2, 3)));\n \n+/* Read the next line from FP into BUFFER, of LENGTH bytes.  LINE will\n+   include the line terminator and a NUL terminator.  On success,\n+   return the length of the line, including the line terminator, but\n+   excluding the NUL termintor.  On EOF, return zero and write a NUL\n+   terminator.  On error, return -1 and set errno.  If the total byte\n+   count (line and both terminators) exceeds LENGTH, return -1 and set\n+   errno to ERANGE (but do not mark the stream as failed).\n+\n+   The behavior is undefined if FP is not seekable.  */\n+ssize_t __libc_readline_unlocked (_IO_FILE *fp, char *buffer, size_t length);\n+libc_hidden_proto (__libc_readline_unlocked);\n+\n extern const char *const _sys_errlist_internal[] attribute_hidden;\n extern int _sys_nerr_internal attribute_hidden;\n \n@@ -161,6 +173,8 @@ libc_hidden_proto (fwrite)\n libc_hidden_proto (fseek)\n extern __typeof (ftello) __ftello;\n libc_hidden_proto (__ftello)\n+extern __typeof (fseeko64) __fseeko64 attribute_hidden;\n+extern __typeof (ftello64) __ftello64 attribute_hidden;\n libc_hidden_proto (fflush)\n libc_hidden_proto (fflush_unlocked)\n extern __typeof (fflush_unlocked) __fflush_unlocked;\ndiff --git a/libio/Makefile b/libio/Makefile\nindex 9d09bd8b6a..85da7dc42f 100644\n--- a/libio/Makefile\n+++ b/libio/Makefile\n@@ -47,7 +47,7 @@ routines\t:=\t\t\t\t\t\t\t      \\\n \t__fbufsize __freading __fwriting __freadable __fwritable __flbf\t      \\\n \t__fpurge __fpending __fsetlocking\t\t\t\t      \\\n \t\t\t\t\t\t\t\t\t      \\\n-\tlibc_fatal fmemopen oldfmemopen vtables\n+\tlibc_fatal fmemopen oldfmemopen vtables readline\n \n tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \\\n \ttst_wprintf2 tst-widetext test-fmemopen tst-ext tst-ext2 \\\n@@ -63,6 +63,9 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \\\n \ttst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \\\n \ttst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \\\n \ttst-ftell-append tst-fputws\n+\n+tests-internal = tst-readline\n+\n ifeq (yes,$(build-shared))\n # Add test-fopenloc only if shared library is enabled since it depends on\n # shared localedata objects.\ndiff --git a/libio/Versions b/libio/Versions\nindex 77123347e3..af24d4180c 100644\n--- a/libio/Versions\n+++ b/libio/Versions\n@@ -158,5 +158,8 @@ libc {\n \n     # Used by NPTL\n     _IO_enable_locks;\n+\n+    # Used by NSS.\n+    __libc_readline_unlocked;\n   }\n }\ndiff --git a/libio/fseeko.c b/libio/fseeko.c\nindex b77d4be18f..a25e130aef 100644\n--- a/libio/fseeko.c\n+++ b/libio/fseeko.c\n@@ -28,7 +28,7 @@\n #include \"stdio.h\"\n \n int\n-fseeko (_IO_FILE *fp, off_t offset, int whence)\n+__fseeko (_IO_FILE *fp, off_t offset, int whence)\n {\n   int result;\n   CHECK_FILE (fp, -1);\n@@ -38,6 +38,9 @@ fseeko (_IO_FILE *fp, off_t offset, int whence)\n   return result;\n }\n \n+weak_alias (__fseeko, fseeko)\n+\n #ifdef __OFF_T_MATCHES_OFF64_T\n-weak_alias (fseeko, fseeko64)\n+weak_alias (__fseeko, fseeko64)\n+strong_alias (__fseeko, __fseeko64)\n #endif\ndiff --git a/libio/fseeko64.c b/libio/fseeko64.c\nindex 12221d585e..4207b18e86 100644\n--- a/libio/fseeko64.c\n+++ b/libio/fseeko64.c\n@@ -32,7 +32,7 @@\n #ifndef __OFF_T_MATCHES_OFF64_T\n \n int\n-fseeko64 (_IO_FILE *fp, __off64_t offset, int whence)\n+__fseeko64 (_IO_FILE *fp, __off64_t offset, int whence)\n {\n   int result;\n   CHECK_FILE (fp, -1);\n@@ -42,4 +42,6 @@ fseeko64 (_IO_FILE *fp, __off64_t offset, int whence)\n   return result;\n }\n \n+weak_alias (__fseeko64, fseeko64)\n+\n #endif\ndiff --git a/libio/ftello.c b/libio/ftello.c\nindex 9791e835c6..8c522bd614 100644\n--- a/libio/ftello.c\n+++ b/libio/ftello.c\n@@ -61,4 +61,5 @@ weak_alias (__ftello, ftello)\n \n #ifdef __OFF_T_MATCHES_OFF64_T\n weak_alias (__ftello, ftello64)\n+strong_alias (__ftello, __ftello64)\n #endif\ndiff --git a/libio/ftello64.c b/libio/ftello64.c\nindex 15518af7a6..16647bde7a 100644\n--- a/libio/ftello64.c\n+++ b/libio/ftello64.c\n@@ -32,7 +32,7 @@\n #ifndef __OFF_T_MATCHES_OFF64_T\n \n off64_t\n-ftello64 (_IO_FILE *fp)\n+__ftello64 (_IO_FILE *fp)\n {\n   _IO_off64_t pos;\n   CHECK_FILE (fp, -1L);\n@@ -53,4 +53,6 @@ ftello64 (_IO_FILE *fp)\n   return pos;\n }\n \n+weak_alias (__ftello64, ftello64)\n+\n #endif\ndiff --git a/libio/readline.c b/libio/readline.c\nnew file mode 100644\nindex 0000000000..64e426e6aa\n--- /dev/null\n+++ b/libio/readline.c\n@@ -0,0 +1,186 @@\n+/* fgets with ERANGE error reporting and size_t buffer length.\n+   Copyright (C) 2017 Free Software Foundation, Inc.\n+   This file is part of the GNU C Library.\n+\n+   The GNU C Library is free software; you can redistribute it and/or\n+   modify it under the terms of the GNU Lesser General Public\n+   License as published by the Free Software Foundation; either\n+   version 2.1 of the License, or (at your option) any later version.\n+\n+   The GNU C Library is distributed in the hope that it will be useful,\n+   but WITHOUT ANY WARRANTY; without even the implied warranty of\n+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+   Lesser General Public License for more details.\n+\n+   You should have received a copy of the GNU Lesser General Public\n+   License along with the GNU C Library; if not, see\n+   <http://www.gnu.org/licenses/>.  */\n+\n+#include <errno.h>\n+#include <stdio.h>\n+#include <string.h>\n+\n+#include \"libioP.h\"\n+\n+/* Return -1 and set errno to EINVAL if it is ERANGE.  */\n+static ssize_t\n+fail_no_erange (void)\n+{\n+  if (errno == ERANGE)\n+    __set_errno (EINVAL);\n+  return -1;\n+}\n+\n+/* Slow path for reading the line.  Called with no data in the stream\n+   read buffer.  Write data to [BUFFER, BUFFER_END).  */\n+static ssize_t\n+readline_slow (_IO_FILE *fp, char *buffer, char *buffer_end)\n+{\n+  char *start = buffer;\n+\n+  while (buffer < buffer_end)\n+    {\n+      int ch = __uflow (fp);\n+      if (ch == EOF)\n+        {\n+          if (_IO_ferror_unlocked (fp))\n+            /* If the EOF was caused by a read error, report it.  */\n+            return fail_no_erange ();\n+          *buffer = '\\0';\n+          /* Do not include the NULL terminator.  */\n+          return buffer - start;\n+        }\n+\n+      /* Save the character just read.  */\n+      *buffer = ch;\n+      ++buffer;\n+\n+      if (ch == '\\n')\n+        {\n+          if (buffer == buffer_end)\n+            /* Not enough room in the caller-supplied buffer.  */\n+            break;\n+          *buffer = '\\0';\n+          /* Do not include the NULL terminator.  */\n+          return buffer - start;\n+        }\n+\n+      /* __uflow may have filled the buffer.  */\n+      char *readptr = fp->_IO_read_ptr;\n+      ssize_t readlen = fp->_IO_read_end - readptr;\n+      if (readlen > 0)\n+        {\n+          char *pnl = memchr (readptr, '\\n', readlen);\n+          if (pnl != NULL)\n+            {\n+              /* We found the terminator.  */\n+              size_t line_length = pnl - readptr;\n+              if (line_length + 2 > buffer_end - buffer)\n+                /* Not enough room in the caller-supplied buffer.  */\n+                break;\n+              memcpy (buffer, readptr, line_length + 1);\n+              buffer[line_length + 1] = '\\0';\n+              fp->_IO_read_ptr = pnl + 1;\n+              /* Do not include the NULL terminator.  */\n+              return buffer - start + line_length + 1;\n+            }\n+\n+          if (readlen >= buffer_end - buffer)\n+            /* Not enough room in the caller-supplied buffer.  */\n+            break;\n+\n+          /* Save and consume the stream buffer.  */\n+          memcpy (buffer, readptr, readlen);\n+          fp->_IO_read_ptr = fp->_IO_read_end;\n+          buffer += readlen;\n+        }\n+    }\n+\n+  /* The line does not fit into the buffer.  */\n+  __set_errno (ERANGE);\n+  return -1;\n+}\n+\n+ssize_t\n+__libc_readline_unlocked (_IO_FILE *fp, char *buffer, size_t buffer_length)\n+{\n+  char *buffer_end = buffer + buffer_length;\n+\n+  /* Orient the stream.  */\n+  if (__builtin_expect (fp->_mode, -1) == 0)\n+    _IO_fwide (fp, -1);\n+\n+  /* Fast path: The line terminator is found in the buffer.  */\n+  char *readptr = fp->_IO_read_ptr;\n+  ssize_t readlen = fp->_IO_read_end - readptr;\n+  off64_t start_offset;         /* File offset before reading anything.  */\n+  if (readlen > 0)\n+    {\n+      char *pnl = memchr (readptr, '\\n', readlen);\n+      if (pnl != NULL)\n+        {\n+          size_t line_length = pnl - readptr;\n+          /* Account for line and NULL terminators.  */\n+          if (line_length + 2 > buffer_length)\n+            {\n+              __set_errno (ERANGE);\n+              return -1;\n+            }\n+          memcpy (buffer, readptr, line_length + 1);\n+          buffer[line_length + 1] = '\\0';\n+          /* Consume the entire line.  */\n+          fp->_IO_read_ptr = pnl + 1;\n+          return line_length + 1;\n+        }\n+\n+      /* If the buffer does not have enough space for what is pending\n+         in the stream (plus a NUL terminator), the buffer is too\n+         small.  */\n+      if (readlen + 1 > buffer_length)\n+        {\n+          __set_errno (ERANGE);\n+          return -1;\n+        }\n+\n+      /* End of line not found.  We need all the buffered data.  Fall\n+         through to the slow path.  */\n+      memcpy (buffer, readptr, readlen);\n+      buffer += readlen;\n+      /* The original length is invalid after this point.  Use\n+         buffer_end instead.  */\n+#pragma GCC poison buffer_length\n+      /* Read the old offset before updating the read pointer.  */\n+      start_offset = __ftello64 (fp);\n+      fp->_IO_read_ptr = fp->_IO_read_end;\n+    }\n+  else\n+    {\n+      readlen = 0;\n+      start_offset = __ftello64 (fp);\n+    }\n+\n+\n+  /* Slow path: Read more data from the underlying file.  We need to\n+     restore the file pointer if the buffer is too small.  First,\n+     check if the __ftello64 call above failed.  */\n+  if (start_offset < 0)\n+    return fail_no_erange ();\n+\n+  ssize_t result = readline_slow (fp, buffer, buffer_end);\n+  if (result < 0)\n+    {\n+      if (errno == ERANGE)\n+        {\n+          /* Restore the file pointer so that the caller may read the\n+             same line again.  */\n+          if (__fseeko64 (fp, start_offset, SEEK_SET) < 0)\n+            return fail_no_erange ();\n+          __set_errno (ERANGE);\n+        }\n+      /* Do not restore the file position on other errors; it is\n+         likely that the __fseeko64 call would fail, too.  */\n+      return -1;\n+    }\n+  return readlen + result;\n+}\n+libc_hidden_def (__libc_readline_unlocked)\ndiff --git a/libio/tst-readline.c b/libio/tst-readline.c\nnew file mode 100644\nindex 0000000000..79ed6ea699\n--- /dev/null\n+++ b/libio/tst-readline.c\n@@ -0,0 +1,235 @@\n+/* Test the __libc_readline_unlocked function.\n+   Copyright (C) 2017 Free Software Foundation, Inc.\n+   This file is part of the GNU C Library.\n+\n+   The GNU C Library is free software; you can redistribute it and/or\n+   modify it under the terms of the GNU Lesser General Public\n+   License as published by the Free Software Foundation; either\n+   version 2.1 of the License, or (at your option) any later version.\n+\n+   The GNU C Library is distributed in the hope that it will be useful,\n+   but WITHOUT ANY WARRANTY; without even the implied warranty of\n+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+   Lesser General Public License for more details.\n+\n+   You should have received a copy of the GNU Lesser General Public\n+   License along with the GNU C Library; if not, see\n+   <http://www.gnu.org/licenses/>.  */\n+\n+/* Exercise __libc_readline_unlocked with various combinations of line\n+   lengths, stdio buffer sizes, and line read buffer sizes.  */\n+\n+#include <errno.h>\n+#include <stdbool.h>\n+#include <stdio.h>\n+#include <support/check.h>\n+#include <support/test-driver.h>\n+#include <support/support.h>\n+#include <support/xstdio.h>\n+#include <support/xmemstream.h>\n+#include <support/temp_file.h>\n+#include <support/xunistd.h>\n+\n+enum\n+  {\n+    maximum_line_length = 7,\n+    number_of_lines = 3,\n+  };\n+\n+/* -1: Do not set buffer size.  0: unbuffered.  Otherwise, use this as\n+   the size of the buffer.  */\n+static int buffer_size;\n+\n+/* These size of the buffer used for reading.  Must be at least 2.  */\n+static int read_size;\n+\n+/* If a read files with ERANGE, increase the buffer size by this\n+   amount.  Must be positive.  */\n+static int read_size_increment;\n+\n+/* If non-zero, do not reset the read size after an ERANGE error.  */\n+static int read_size_preserve;\n+\n+/* If non-zero, no '\\n' at the end of the file.  */\n+static int no_newline_at_eof;\n+\n+/* Length of the line, or -1 if the line is not present.  */\n+static int line_lengths[number_of_lines];\n+\n+/* The name of the test file.  */\n+static char *test_file_path;\n+\n+/* The contents of the test file.  */\n+static char expected_contents[(maximum_line_length + 2) * number_of_lines + 1];\n+static size_t expected_length;\n+\n+/* Returns a random byte which is not zero or the line terminator.  */\n+static char\n+random_char (void)\n+{\n+  static unsigned int rand_state = 1;\n+  while (true)\n+    {\n+      char result = rand_r (&rand_state) >> 16;\n+      if (result != 0 && result != '\\n')\n+        return result;\n+    }\n+}\n+\n+/* Create the test file.  */\n+static void\n+prepare (int argc, char **argv)\n+{\n+  int fd = create_temp_file (\"tst-readline-\", &test_file_path);\n+  TEST_VERIFY_EXIT (fd >= 0);\n+  xclose (fd);\n+}\n+\n+/* Prepare the test file.  Return false if the test parameters are\n+   incongruent and the test should be skipped.  */\n+static bool\n+write_test_file (void)\n+{\n+  expected_length = 0;\n+  char *p = expected_contents;\n+  for (int lineno = 0; lineno < number_of_lines; ++lineno)\n+    for (int i = 0; i < line_lengths[lineno]; ++i)\n+      *p++ = random_char ();\n+  expected_length = p - &expected_contents[0];\n+  if (no_newline_at_eof)\n+    {\n+      if (expected_length == 0)\n+        return false;\n+      --expected_length;\n+      --p;\n+    }\n+  if (test_verbose > 0)\n+    {\n+      printf (\"info: writing test file of %zu bytes:\\n\", expected_length);\n+      for (int i = 0; i < number_of_lines; ++i)\n+        printf (\" line %d: %d\\n\", i, line_lengths[i]);\n+      if (no_newline_at_eof)\n+        puts (\"  (no newline at EOF)\");\n+    }\n+  TEST_VERIFY_EXIT (expected_length < sizeof (expected_contents));\n+  *p++ = '\\0';\n+  support_write_file_string (test_file_path, expected_contents);\n+  return true;\n+}\n+\n+/* Run a single test (a combination of a test file and read\n+   parameters).  */\n+static void\n+run_test (void)\n+{\n+  TEST_VERIFY_EXIT (read_size_increment > 0);\n+  if (test_verbose > 0)\n+    {\n+      printf (\"info: running test: buffer_size=%d read_size=%d\\n\"\n+              \"  read_size_increment=%d read_size_preserve=%d\\n\",\n+              buffer_size, read_size, read_size_increment, read_size_preserve);\n+    }\n+\n+  struct xmemstream result;\n+  xopen_memstream (&result);\n+\n+  FILE *fp = xfopen (test_file_path, \"rce\");\n+  char *fp_buffer = NULL;\n+  if (buffer_size == 0)\n+    TEST_VERIFY_EXIT (setvbuf (fp, NULL, _IONBF, 0) == 0);\n+  if (buffer_size > 0)\n+    {\n+      fp_buffer = xmalloc (buffer_size);\n+      TEST_VERIFY_EXIT (setvbuf (fp, fp_buffer, _IOFBF, buffer_size) == 0);\n+    }\n+\n+  char *line_buffer = xmalloc (read_size);\n+  size_t line_buffer_size = read_size;\n+\n+  while (true)\n+    {\n+      ssize_t ret = __libc_readline_unlocked\n+        (fp, line_buffer, line_buffer_size);\n+      if (ret < 0)\n+        {\n+          TEST_VERIFY (ret == -1);\n+          if (errno != ERANGE)\n+            FAIL_EXIT1 (\"__libc_readline_unlocked: %m\");\n+          line_buffer_size += read_size_increment;\n+          free (line_buffer);\n+          line_buffer = xmalloc (line_buffer_size);\n+          /* Try reading this line again.  */\n+        }\n+      else if (ret == 0)\n+        break;\n+      else\n+        {\n+          /* A line has been read.  Save it.  */\n+          TEST_VERIFY (ret == strlen (line_buffer));\n+          const char *pnl = strchr (line_buffer, '\\n');\n+          /* If there is a \\n, it must be at the end.  */\n+          TEST_VERIFY (pnl == NULL || pnl == line_buffer + ret - 1);\n+          fputs (line_buffer, result.out);\n+\n+          /* Restore the original read size if required.  */\n+          if (line_buffer_size > read_size && !read_size_preserve)\n+            {\n+              line_buffer_size = read_size;\n+              free (line_buffer);\n+              line_buffer = xmalloc (line_buffer_size);\n+            }\n+        }\n+    }\n+\n+  xfclose (fp);\n+  free (fp_buffer);\n+  free (line_buffer);\n+\n+  xfclose_memstream (&result);\n+  TEST_VERIFY (result.length == expected_length);\n+  TEST_VERIFY (strcmp (result.buffer, expected_contents) == 0);\n+  if (test_verbose > 0)\n+    {\n+      printf (\"info: expected (%zu): [[%s]]\\n\",\n+              expected_length, expected_contents);\n+      printf (\"info:   actual (%zu): [[%s]]\\n\", result.length, result.buffer);\n+    }\n+  free (result.buffer);\n+}\n+\n+/* Test one test file with multiple read parameters.  */\n+static void\n+test_one_file (void)\n+{\n+  for (buffer_size = -1; buffer_size <= maximum_line_length + 1; ++buffer_size)\n+    for (read_size = 2; read_size <= maximum_line_length + 2; ++read_size)\n+      for (read_size_increment = 1; read_size_increment <= 4;\n+           ++read_size_increment)\n+        for (read_size_preserve = 0; read_size_preserve < 2;\n+             ++read_size_preserve)\n+          run_test ();\n+}\n+\n+\n+static int\n+do_test (void)\n+{\n+  /* Set up the test file contents.  */\n+  for (line_lengths[0] = -1; line_lengths[0] <= maximum_line_length;\n+       ++line_lengths[0])\n+    for (line_lengths[1] = -1; line_lengths[1] <= maximum_line_length;\n+         ++line_lengths[1])\n+      for (line_lengths[2] = -1; line_lengths[2] <= maximum_line_length;\n+           ++line_lengths[2])\n+        for (no_newline_at_eof = 0; no_newline_at_eof < 2; ++no_newline_at_eof)\n+          {\n+            if (!write_test_file ())\n+              continue;\n+            test_one_file ();\n+          }\n+  free (test_file_path);\n+  return 0;\n+}\n+\n+#define PREPARE prepare\n+#include <support/test-driver.c>\n",
    "prefixes": []
}