Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/808732/?format=api
{ "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": [] }