@@ -254,6 +254,11 @@ tests := \
tst-scanf-round \
tst-scanf-to_inpunct \
tst-setvbuf1 \
+ tst-setvbuf2 \
+ tst-setvbuf3 \
+ tst-setvbuf4 \
+ tst-setvbuf5 \
+ tst-setvbuf6 \
tst-sprintf \
tst-sprintf-errno \
tst-sprintf2 \
@@ -316,6 +321,8 @@ tests-special += \
$(objpfx)tst-printf.out \
$(objpfx)tst-printfsz-islongdouble.out \
$(objpfx)tst-setvbuf1-cmp.out \
+ $(objpfx)tst-setvbuf2-cmp.out \
+ $(objpfx)tst-setvbuf3-cmp.out \
$(objpfx)tst-unbputc.out \
# tests-special
@@ -583,5 +590,21 @@ $(objpfx)tst-setvbuf1-cmp.out: tst-setvbuf1.expect $(objpfx)tst-setvbuf1.out
cmp $^ > $@; \
$(evaluate-test)
+$(objpfx)tst-setvbuf2.out: /dev/null $(objpfx)tst-setvbuf2
+ $(test-program-cmd) > $@ 2>&1; \
+ $(evaluate-test)
+
+$(objpfx)tst-setvbuf2-cmp.out: tst-setvbuf2.expect $(objpfx)tst-setvbuf2.out
+ cmp $^ > $@; \
+ $(evaluate-test)
+
+$(objpfx)tst-setvbuf3.out: /dev/null $(objpfx)tst-setvbuf3
+ $(test-program-cmd) > $@ 2>&1; \
+ $(evaluate-test)
+
+$(objpfx)tst-setvbuf3-cmp.out: tst-setvbuf3.expect $(objpfx)tst-setvbuf3.out
+ cmp $^ > $@; \
+ $(evaluate-test)
+
$(objpfx)tst-printf-round: $(libm)
$(objpfx)tst-scanf-round: $(libm)
new file mode 100644
@@ -0,0 +1,85 @@
+/* Test using setvbuf to set unbuffered mode on standard streams.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+
+/* Check that the standard streams (stdout and stderr) can be set to
+ unbuffered. Note - the POSIX standard indicates that setting the
+ buffering on a stream might not work. It states:
+
+ The setvbuf( ) function may be used after the stream
+ pointed to by stream is associated with an open file
+ but before any other operation (other than an unsuccessful
+ call to setvbuf( )) is performed on the stream.
+
+ Hence if messages have already been written to stdout and/or stderr
+ before this code is executed then we may not be able to change
+ the buffering. The standard does not provide a way to detect if
+ this has happened, so we have to hope for the best. */
+
+/* By default the test harness code will call setvbuf on stdout.
+ Since we want to do this ourselves, we disable the harness'
+ behaviour here. */
+#define TEST_NO_SETVBUF 1
+
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+ TEST_VERIFY_EXIT (setvbuf (stderr, NULL, _IONBF, 0) == 0);
+ TEST_VERIFY_EXIT (setvbuf (stdout, NULL, _IONBF, 0) == 0);
+
+ /* Emit some text to both streams with newlines in the middle. */
+ fprintf (stderr, "setvbuf2: Output #1 ");
+ printf ("setvbuf2: Output #2 ");
+ fprintf (stderr, "<stderr>.\nAny day now ");
+ fprintf (stdout, "<stdout>.\nWe should discover ");
+
+ /* Flush the output buffers. */
+ fflush (stderr);
+ fflush (stdout);
+
+ /* Assuming that test is attached to a terminal and the shell mixes stdout
+ and stderr output, then...
+
+ If the streams are unbuffered then the output should look like this:
+
+ setvbuf2: Output #1 setvbuf2: Output #2 <stderr>.
+ Any day now <stdout>.
+ We should discover
+
+ However if the streams are line buffered then it should look like this:
+
+ setvbuf2: Output #1 <stderr>.
+ setvbuf2: Output #2 <stdout>.
+ Any day now We should discover
+
+ And if they are fully buffered then it should look like this:
+
+ setvbuf2: Output #1 <stderr>.
+ Any day now
+ setvbuf2: Output #2 <stdout>.
+ We should discover
+
+ This is checked by comparing the output to the tst-setvbuf2.expect file. */
+
+ return 0;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,3 @@
+setvbuf2: Output #1 setvbuf2: Output #2 <stderr>.
+Any day now <stdout>.
+We should discover
new file mode 100644
@@ -0,0 +1,111 @@
+/* Test using setvbuf to set line buffered mode on standard streams.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+
+/* Check that the standard streams (stdout and stderr) can be set to
+ line buffered. Note - the POSIX standard indicates that setting
+ the buffering on a file might not work. It states:
+
+ The setvbuf( ) function may be used after the stream
+ pointed to by stream is associated with an open file
+ but before any other operation (other than an unsuccessful
+ call to setvbuf( )) is performed on the stream.
+
+ Hence if messages have already been written to stdout and/or stderr,
+ eg by the test harness code, but before this code is executed then
+ the calls to setvbuf might not actually change anything. */
+
+/* By default the test harness code will call setvbuf on stdout.
+ Since we do not want that to happen we use the define below. */
+#define TEST_NO_SETVBUF 1
+
+#include <support/check.h>
+
+#define LOCAL_BUF_SIZE 128
+
+static int
+do_test (void)
+{
+ /* Note - the POSIX standard indicates that setting the buffering on a
+ stream might not give the results expected. It states:
+
+ If BUF is not a null pointer, the array it points to *may*
+ be used instead of a buffer allocated by setvbuf( ) and the
+ argument SIZE specifies the size of the array; otherwise,
+ SIZE *may* determine the size of a buffer allocated by the
+ setvbuf( ) function.
+
+ So whilst we can set the buffering mode, we cannot be certain of the size
+ of the buffer that will be used, or where that buffer will be held. */
+
+ /* Note - the POSIX standard also indicates that line buffering might only
+ work on input files:
+
+ Applications should note that many implementations
+ only provide line buffering on input from terminal
+ devices.
+
+ For the purposes of this test we assume that this restriction is not
+ enforced. */
+
+ /* Use a library allocated line buffer for stderr. */
+ TEST_VERIFY_EXIT (setvbuf (stderr, NULL, _IOLBF, LOCAL_BUF_SIZE) == 0);
+
+ static char local_buf [LOCAL_BUF_SIZE];
+
+ /* Use a program allocated line buffer for stdout. */
+ TEST_VERIFY_EXIT (setvbuf (stdout, local_buf, _IOLBF, sizeof local_buf) == 0);
+
+ /* Emit some text to both streams with newlines in the middle. */
+ fprintf (stderr, "setvbuf3: Output #1 ");
+ printf ("setvbuf3: Output #2 ");
+ fprintf (stderr, "<stderr>.\nAny day now ");
+ printf ("<stdout>.\nWe should discover ");
+
+ /* Flush the output buffers. */
+ fflush (stderr);
+ fflush (stdout);
+
+ /* Assuming that test is attached to a terminal and the shell mixes stdout
+ and stderr output, then...
+
+ If the streams are unbuffered then the output should look like this:
+
+ setvbuf3: Output #1 setvbuf3: Output #2 <stderr>.
+ Any day now <stdout>.
+ We should discover
+
+ However if the streams a line buffered then it should look like this:
+
+ setvbuf3: Output #1 <stderr>.
+ setvbuf3: Output #2 <stdout>.
+ Any day now We should discover
+
+ And if they are fully buffered then it should look like this:
+
+ setvbuf3: Output #1 <stderr>.
+ Any day now setvbuf3: Output #2 <stdout>.
+ We should discover
+
+ This is checked by comparing the output to the tst-setvbuf3.expect file. */
+
+ return 0;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,3 @@
+setvbuf3: Output #1 <stderr>.
+setvbuf3: Output #2 <stdout>.
+Any day now We should discover
new file mode 100644
@@ -0,0 +1,154 @@
+/* Test using setvbuf to set full buffered mode on a non-terminal file.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <support/check.h>
+#include <support/temp_file.h>
+
+/* Check the behaviour of enabling full buffering on a non-terminal stream.
+
+ Note - the POSIX standard indicates that setting the buffering on a file
+ might not produce the expected results. It states:
+
+ If BUF is not a null pointer, the array it points to *may*
+ be used instead of a buffer allocated by setvbuf() and the
+ argument SIZE specifies the size of the array; otherwise,
+ SIZE *may* determine the size of a buffer allocated by the
+ setvbuf() function.
+
+ So whilst we can set the buffering mode, we cannot be certain of the size of
+ the buffer that will be used, or where that buffer will be held. For now we
+ proceed with the assumption that if the calls to setvbuf succeed then the
+ buffers are the size we expect. But we do not test to see if the buffer we
+ have supplied are the ones actually being used.
+
+ Note - because of the POSIX rules on the interactions of multiple handles
+ on the same stream (see section 2.5.1 "Interaction of File Descriptors
+ and Standard I/O Streams" in the POSIX specification) we cannot just open a
+ file twice, once for reading and once for writing and then check that writes
+ to the file do not happen until the buffer is full. Nor can we open a
+ single stream for both reading and writing and test that way because any
+ time we reposition the file pointer (ie by calling fseek) the buffer is
+ flushed.
+
+ In theory we could use fmemopen() to create a memory backed stream and
+ then check the buffering behaviour that way. But it turns out the glibc's
+ implementation does not support buffering, so that does not work.
+
+ Another alternative is open_memstream() - which does use glibc's default
+ I/O code. But it turns out that the function is not suitable for this test
+ as it specifically does not support having the memory buffer examined after
+ a write has completed but before a flush has been performed.
+
+ So we resort to opening an ordinary file and using mmap to provide us with
+ a memory page that we can examine. */
+
+#define BIG_BUF_SIZE 128
+#define SMALL_BUF_SIZE 12
+
+_Static_assert (SMALL_BUF_SIZE < BIG_BUF_SIZE,
+ "test assumes that its small buffer is shorter than its large buffer");
+_Static_assert (SMALL_BUF_SIZE > 1,
+ "test assumes that its small buffer is more than one byte long");
+
+static char file_buf [SMALL_BUF_SIZE];
+static char small_buf [SMALL_BUF_SIZE];
+static char big_buf [BIG_BUF_SIZE];
+
+static int
+do_test (void)
+{
+ FILE * file;
+ char * mem_page;
+ size_t page_size;
+ int fd;
+ int val;
+
+ TEST_VERIFY_EXIT ((page_size = sysconf (_SC_PAGE_SIZE)) != 0);
+ TEST_VERIFY_EXIT (page_size > SMALL_BUF_SIZE);
+
+ /* Create a temporay file. */
+ TEST_VERIFY_EXIT ((fd = create_temp_file ("tst-setvbuf4", NULL)) != -1);
+
+ /* Create a stream attached to the file. */
+ TEST_VERIFY_EXIT ((file = fdopen (fd, "r+")) != NULL);
+
+ /* Set full buffering on the file, using our (small) file buffer.
+
+ Note - this has to be done now, right after opening the file. The POSIX
+ standard states:
+
+ The setvbuf() function may be used after the stream
+ pointed to by stream is associated with an open file
+ but before any other operation (other than an unsuccessful
+ call to setvbuf( )) is performed on the stream. */
+ TEST_VERIFY_EXIT (setvbuf (file, file_buf, _IOFBF, sizeof file_buf) == 0);
+
+ /* Map the file into memory.
+ FIXME: Using xmmap would simplify this statement, but it would be
+ inconsistent with how we test all of the other library function calls. */
+ TEST_VERIFY_EXIT ((mem_page = mmap (NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0)) != MAP_FAILED);
+
+ /* The page backing the file has not been initialised yet, so attempts
+ to read from it will produce a SIGBUS error. We fix that by setting
+ the file to the size of the page. */
+ TEST_VERIFY (ftruncate (fd, page_size) == 0);
+
+ /* Create our test data using a value that should not
+ be the same as the contents of the memory page. */
+ val = mem_page[0] + 1;
+ memset (small_buf, val, sizeof small_buf);
+
+ /* Write one byte (which is less than our file buffer size) to the file. */
+ TEST_VERIFY (fseek (file, 0L, SEEK_SET) == 0);
+ TEST_VERIFY (fwrite (small_buf, 1, 1, file) == 1);
+
+ /* Check that the byte has not made it into the file. */
+ TEST_VERIFY (mem_page[0] != val);
+
+ /* Try reading the byte. This would fail, except for the
+ fact that we call fseek() first, which flushes the buffer. */
+ TEST_VERIFY (fseek (file, 0L, SEEK_SET) == 0);
+ TEST_VERIFY (fread (big_buf, 1, 1, file) == 1);
+ TEST_VERIFY (big_buf[0] == small_buf[0]);
+
+ /* Write a whole buffer full. */
+ TEST_VERIFY (fseek (file, 0L, SEEK_SET) == 0);
+ TEST_VERIFY (fwrite (small_buf, 1, sizeof small_buf, file) == sizeof small_buf);
+
+ /* Check that the in-memory buffer now contains these bytes. */
+ TEST_VERIFY (memcmp (small_buf, mem_page, sizeof small_buf) == 0);
+
+ /* Try reading lots of data. */
+ TEST_VERIFY (fseek (file, 0L, SEEK_SET) == 0);
+ TEST_VERIFY (fread (big_buf, 1, sizeof big_buf, file) == sizeof big_buf);
+
+ /* Verify that what we have read the expected bytes. */
+ TEST_VERIFY (memcmp (big_buf, small_buf, sizeof small_buf) == 0);
+
+ fclose (file);
+ return 0;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,171 @@
+/* Test using setvbuf to set line buffered mode on a non-terminal file.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <support/check.h>
+#include <support/temp_file.h>
+
+/* Check the behaviour of enabling line buffering mode on a non-terminal stream.
+
+ Note - the POSIX standard indicates that setting the buffering on a file
+ might not produce the expected results. It states:
+
+ If BUF is not a null pointer, the array it points to *may*
+ be used instead of a buffer allocated by setvbuf( ) and the
+ argument SIZE specifies the size of the array; otherwise,
+ SIZE *may* determine the size of a buffer allocated by the
+ setvbuf( ) function.
+
+ So whilst we can set the buffering mode, we cannot be certain of the size of
+ the buffer that will be used, or where that buffer will be held. For now we
+ proceed with the assumption that if the calls to setvbuf succeed then the
+ buffers are the size we expect. But we do not test to see if the buffer we
+ have supplied are the ones actually being used.
+
+ Note - the POSIX standard also indicates that line buffering mode might not
+ be supported:
+
+ Applications should note that many implementations only
+ provide line buffering on input from terminal devices.
+
+ So the test first tries to write less than a line of characters. If this
+ shows up in the memory buffer, then we know that line buffering is not
+ supported and the test exits. Otherwise it continues and tests that
+ writing a full line does cause the buffer to be flushed to memory.
+
+ Note - because of the POSIX rules on the interactions of multiple handles
+ on the same stream (see section 2.5.1 "Interaction of File Descriptors
+ and Standard I/O Streams" in the POSIX specification) we cannot just open a
+ file twice, once for reading and once for writing and then check that writes
+ to the file do not happen until the buffer is full. Nor can we open a
+ single stream for both reading and writing and test that way because any
+ time we reposition the file pointer (ie by calling fseek) the buffer is
+ flushed.
+
+ In theory we could use fmemopen() to create a memory backed stream and
+ then check the buffering behaviour that way. But it turns out the glibc's
+ implementation does not support buffering, so that does not work.
+
+ Another alternative is open_memstream() - which does use glibc's default
+ I/O code. But it turns out that the function is not suitable for this test
+ as it specifically does not support having the memory buffer examined after
+ a write has completed but before a flush has been performed.
+
+ So we resort to opening an ordinary file and using mmap to provide us with
+ a memory page that we can examine. */
+
+#define BIG_BUF_SIZE 128
+#define SMALL_BUF_SIZE 12
+
+_Static_assert (SMALL_BUF_SIZE < BIG_BUF_SIZE,
+ "test assumes that its small buffer is shorter than its large buffer");
+_Static_assert (SMALL_BUF_SIZE > 1,
+ "test assumes that its small buffer is more than one byte long");
+
+static char line_buf [SMALL_BUF_SIZE];
+static char in_buf [BIG_BUF_SIZE];
+
+const char string_without_newline[] = "test";
+
+static int
+do_test (void)
+{
+ FILE * file;
+ char * mem_page;
+ size_t page_size;
+ int fd;
+ int len;
+
+ TEST_VERIFY_EXIT ((page_size = sysconf (_SC_PAGE_SIZE)) != 0);
+ TEST_VERIFY_EXIT (page_size > SMALL_BUF_SIZE);
+
+ /* Create a temporay file. */
+ TEST_VERIFY_EXIT ((fd = create_temp_file ("tst-setvbuf5", NULL)) != -1);
+
+ /* Create a stream attached to the file. */
+ TEST_VERIFY_EXIT ((file = fdopen (fd, "r+")) != NULL);
+
+ /* Set line buffering on the file, using our (small) file buffer.
+
+ Note - this has to be done now, right after opening the file. The POSIX
+ standard states:
+
+ The setvbuf() function may be used after the stream
+ pointed to by stream is associated with an open file
+ but before any other operation (other than an unsuccessful
+ call to setvbuf( )) is performed on the stream. */
+ TEST_VERIFY_EXIT (setvbuf (file, line_buf, _IOLBF, sizeof line_buf) == 0);
+
+ /* Map the file into memory.
+ FIXME: Using xmmap would simplify this statement, but it would be
+ inconsistent with how we test all of the other library function calls. */
+ TEST_VERIFY_EXIT ((mem_page = mmap (NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0)) != MAP_FAILED);
+
+ /* The page backing the file has not been initialised yet, so attempts
+ to read from it will produce a SIGBUS error. We fix that by setting
+ the file to the size of the page. */
+ TEST_VERIFY (ftruncate (fd, page_size) == 0);
+
+ /* Write one byte to the file. */
+ TEST_VERIFY (fwrite (string_without_newline, 1, 1, file) == 1);
+
+ /* Check that the byte has not made it into the file. */
+ if (mem_page[0] == string_without_newline[0])
+ {
+ printf ("info: tst-setvbuf5.c: line buffering is not supported on non-terminal I/O streams\n");
+ printf ("info: tst-setvbuf5.c: this is allowed by the POSIX standard\n");
+ close (fd);
+ return 0;
+ }
+
+ /* Try reading the byte. This would fail, except for the
+ fact that we call fseek() first, which flushes the buffer. */
+ TEST_VERIFY (fseek (file, 0L, SEEK_SET) == 0);
+ TEST_VERIFY (fread (in_buf, 1, 1, file) == 1);
+ TEST_VERIFY (in_buf[0] == string_without_newline[0]);
+
+ /* Write one and half lines to the file. */
+ len = strlen (string_without_newline);
+ TEST_VERIFY (fseek (file, 0L, SEEK_SET) == 0);
+ TEST_VERIFY (fprintf (file, "%s\n%s", string_without_newline,
+ string_without_newline) == len * 2 + 1);
+
+ /* Check that the in-memory buffer now contains the first line. */
+ TEST_VERIFY (strncmp (mem_page, string_without_newline, len) == 0);
+
+ /* And that it does not contain the second line. */
+ TEST_VERIFY (strncmp (mem_page + len + 1, string_without_newline, len) != 0);
+
+ /* Read back what we have written. */
+ TEST_VERIFY (fseek (file, 0L, SEEK_SET) == 0);
+ TEST_VERIFY (fread (in_buf, 1, len * 2 + 1, file) == len * 2 + 1);
+
+ /* Check that we read in the second, not-newline-terminated string. */
+ TEST_VERIFY (strncmp (in_buf + len + 1, string_without_newline, len) == 0);
+
+ fclose (file);
+ return 0;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,136 @@
+/* Test using setvbuf to set unbuffered mode on a non-terminal file.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <support/check.h>
+#include <support/temp_file.h>
+
+/* Check the behaviour of setting unbuffered mode on a non-terminal stream.
+
+ Note - because of the POSIX rules on the interactions of multiple handles
+ on the same stream (see section 2.5.1 "Interaction of File Descriptors
+ and Standard I/O Streams" in the POSIX specification) we cannot just open a
+ file twice, once for reading and once for writing and then check that writes
+ to the file happen immediately. Nor can we open a single stream for both
+ reading and writing and test that way because any time we reposition the
+ file pointer (ie by calling fseek) the buffer is flushed.
+
+ In theory we can use fmemopen() to create a memory backed stream and
+ then check the buffering behaviour that way. But it turns out the glibc's
+ implementation does not support buffering, which would not matter for this
+ test except that we want to be sure that buffering is not enabled because
+ of our actions, rather than the library's internal code.
+
+ Another alternative is open_memstream() - which does use glibc's default
+ I/O code. But it turns out that the function is not suitable for this test
+ as it specifically does not support having the memory buffer examined after
+ a write has completed but before a flush has been performed.
+
+ So we resort to opening an ordinary file and using mmap to provide us with
+ a memory page that we can examine. */
+
+#define BIG_BUF_SIZE 128
+#define SMALL_BUF_SIZE 12
+
+_Static_assert (SMALL_BUF_SIZE < BIG_BUF_SIZE,
+ "test assumes that its small buffer is shorter than its large buffer");
+
+static char small_buf [SMALL_BUF_SIZE];
+static char big_buf [BIG_BUF_SIZE];
+
+static int
+do_test (void)
+{
+ FILE * file;
+ char * mem_page;
+ size_t page_size;
+ int fd;
+ int val;
+
+ TEST_VERIFY_EXIT ((page_size = sysconf (_SC_PAGE_SIZE)) != 0);
+ TEST_VERIFY_EXIT (page_size > SMALL_BUF_SIZE);
+
+ /* Create a temporay file. */
+ TEST_VERIFY_EXIT ((fd = create_temp_file ("tst-setvbuf6", NULL)) != -1);
+
+ /* Create a stream attached to the file. */
+ TEST_VERIFY_EXIT ((file = fdopen (fd, "r+")) != NULL);
+
+ /* Disable buffering on the file.
+
+ Note - this has to be done now, right after opening the file. The POSIX
+ standard states:
+
+ The setvbuf() function may be used after the stream
+ pointed to by stream is associated with an open file
+ but before any other operation (other than an unsuccessful
+ call to setvbuf( )) is performed on the stream. */
+ TEST_VERIFY_EXIT (setvbuf (file, NULL, _IONBF, 0) == 0);
+
+ /* Map the file into memory.
+ FIXME: Using xmmap would simplify this statement, but it would be
+ inconsistent with how we test all of the other library function calls. */
+ TEST_VERIFY_EXIT ((mem_page = mmap (NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0)) != MAP_FAILED);
+
+ /* The page backing the file has not been initialised yet, so attempts
+ to read from it will produce a SIGBUS error. We fix that by setting
+ the file to the size of the page. */
+ TEST_VERIFY (ftruncate (fd, page_size) == 0);
+
+ /* Create our test data using a value that should not
+ be the same as the contents of the memory page. */
+ val = mem_page[0] + 1;
+ memset (small_buf, val, sizeof small_buf);
+
+ /* Write one byte to the file. */
+ TEST_VERIFY (fseek (file, 0L, SEEK_SET) == 0);
+ TEST_VERIFY (fwrite (small_buf, 1, 1, file) == 1);
+
+ /* Check that the byte has made it into the file. */
+ TEST_VERIFY (mem_page[0] == val);
+
+ /* Try reading the byte. */
+ TEST_VERIFY (fseek (file, 0L, SEEK_SET) == 0);
+ TEST_VERIFY (fread (big_buf, 1, 1, file) == 1);
+ TEST_VERIFY (big_buf[0] == small_buf[0]);
+
+ /* Write a whole buffer full. */
+ TEST_VERIFY (fseek (file, 0L, SEEK_SET) == 0);
+ TEST_VERIFY (fwrite (small_buf, 1, sizeof small_buf, file) == sizeof small_buf);
+
+ /* Check that the in-memory buffer now contains these bytes. */
+ TEST_VERIFY (memcmp (small_buf, mem_page, sizeof small_buf) == 0);
+
+ /* Try reading lots of data. */
+ TEST_VERIFY (fseek (file, 0L, SEEK_SET) == 0);
+ TEST_VERIFY (fread (big_buf, 1, sizeof big_buf, file) == sizeof big_buf);
+
+ /* Verify that what we have read the expected bytes. */
+ TEST_VERIFY (memcmp (big_buf, small_buf, sizeof small_buf) == 0);
+
+ fclose (file);
+ return 0;
+}
+
+#include <support/test-driver.c>