1 /* Verify that ftell returns the correct value at various points before and
2 after the handler on which it is called becomes active.
3 Copyright (C) 2014 Free Software Foundation, Inc.
4 This file is part of the GNU C Library.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <http://www.gnu.org/licenses/>. */
29 static int do_test (void);
31 #define TEST_FUNCTION do_test ()
32 #include "../test-skeleton.c"
34 #define get_handles_fdopen(filename, fd, fp, fd_mode, mode) \
37 (fd) = open ((filename), (fd_mode), 0); \
40 printf ("open failed: %m\n"); \
45 (fp) = fdopen ((fd), (mode)); \
48 printf ("fdopen failed: %m\n"); \
56 #define get_handles_fopen(filename, fd, fp, mode) \
59 (fp) = fopen ((filename), (mode)); \
62 printf ("fopen failed: %m\n"); \
70 printf ("fileno failed: %m\n"); \
77 /* data points to either char_data or wide_data, depending on whether we're
78 testing regular file mode or wide mode respectively. Similarly,
79 fputs_func points to either fputs or fputws. data_len keeps track of the
80 length of the current data and file_len maintains the current file
82 static const void *data;
83 static const char *char_data = "abcdef";
84 static const wchar_t *wide_data = L"abcdef";
85 static size_t data_len;
86 static size_t file_len;
87 static size_t char_len;
89 typedef int (*fputs_func_t) (const void *data, FILE *fp);
90 typedef void *(*fgets_func_t) (void *ws, int n, FILE *fp);
91 fputs_func_t fputs_func;
92 fgets_func_t fgets_func;
94 /* This test verifies that the offset reported by ftell is correct after the
95 file is truncated using ftruncate. ftruncate does not change the file
96 offset on truncation and hence, SEEK_CUR should continue to point to the
97 old offset and not be changed to the new offset. */
99 do_ftruncate_test (const char *filename)
110 {"w", O_WRONLY | O_TRUNC},
111 {"w+", O_RDWR | O_TRUNC},
116 for (int j = 0; j < 2; j++)
118 for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
121 printf ("\tftruncate: %s (file, \"%s\"): ",
122 j == 0 ? "fopen" : "fdopen",
126 fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
128 fileret = get_handles_fdopen (filename, fd, fp,
129 test_modes[i].fd_mode,
135 /* Write some data. */
136 size_t written = fputs_func (data, fp);
140 printf ("fputs[1] failed to write data\n");
144 /* Record the offset. */
145 long offset = ftell (fp);
147 /* Flush data to allow switching active handles. */
150 printf ("Flush failed: %m\n");
154 /* Now truncate the file. */
155 if (ftruncate (fd, 0) != 0)
157 printf ("Failed to truncate file: %m\n");
161 /* ftruncate does not change the offset, so there is no need to call
162 anything to be able to switch active handles. */
163 long new_offset = ftell (fp);
165 /* The offset should remain unchanged since ftruncate does not update
167 if (offset != new_offset)
169 printf ("Incorrect offset. Expected %zu, but got %ld\n",
175 printf ("offset = %ld\n", offset);
183 /* Test that ftell output after a rewind is correct. */
185 do_rewind_test (const char *filename)
195 {"w", O_WRONLY | O_TRUNC, 0, data_len},
196 {"w+", O_RDWR | O_TRUNC, 0, data_len},
197 {"r+", O_RDWR, 0, data_len},
198 /* The new offsets for 'a' and 'a+' modes have to factor in the
199 previous writes since they always append to the end of the
201 {"a", O_WRONLY, 0, 3 * data_len},
202 {"a+", O_RDWR, 0, 4 * data_len},
205 /* Empty the file before the test so that our offsets are simple to
207 FILE *fp = fopen (filename, "w");
210 printf ("Failed to open file for emptying\n");
215 for (int j = 0; j < 2; j++)
217 for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
223 printf ("\trewind: %s (file, \"%s\"): ", j == 0 ? "fdopen" : "fopen",
227 fileret = get_handles_fdopen (filename, fd, fp,
228 test_modes[i].fd_mode,
231 fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
236 /* Write some content to the file, rewind and ensure that the ftell
237 output after the rewind is 0. POSIX does not specify what the
238 behavior is when a file is rewound in 'a' mode, so we retain
239 current behavior, which is to keep the 0 offset. */
240 size_t written = fputs_func (data, fp);
244 printf ("fputs[1] failed to write data\n");
249 long offset = ftell (fp);
251 if (offset != test_modes[i].old_off)
253 printf ("Incorrect old offset. Expected %zu, but got %ld, ",
254 test_modes[i].old_off, offset);
258 printf ("old offset = %ld, ", offset);
260 written = fputs_func (data, fp);
264 printf ("fputs[1] failed to write data\n");
268 /* After this write, the offset in append modes should factor in the
269 implicit lseek to the end of file. */
271 if (offset != test_modes[i].new_off)
273 printf ("Incorrect new offset. Expected %zu, but got %ld\n",
274 test_modes[i].new_off, offset);
278 printf ("new offset = %ld\n", offset);
284 /* Test that the value of ftell is not cached when the stream handle is not
287 do_ftell_test (const char *filename)
298 /* In w, w+ and r+ modes, the file position should be at the
299 beginning of the file. After the write, the offset should be
300 updated to data_len. We don't use eof_off in w and a modes since
301 they don't allow reading. */
302 {"w", O_WRONLY | O_TRUNC, 0, data_len, 0},
303 {"w+", O_RDWR | O_TRUNC, 0, data_len, 2 * data_len},
304 {"r+", O_RDWR, 0, data_len, 3 * data_len},
305 /* For the 'a' mode, the initial file position should be the
306 current end of file. After the write, the offset has data_len
307 added to the old value. For a+ mode however, the initial file
308 position is the file position of the underlying file descriptor,
309 since it is initially assumed to be in read mode. */
310 {"a", O_WRONLY, 3 * data_len, 4 * data_len, 5 * data_len},
311 {"a+", O_RDWR, 0, 5 * data_len, 6 * data_len},
313 for (int j = 0; j < 2; j++)
315 for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
321 printf ("\tftell: %s (file, \"%s\"): ", j == 0 ? "fdopen" : "fopen",
325 fileret = get_handles_fdopen (filename, fd, fp,
326 test_modes[i].fd_mode,
329 fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
334 long off = ftell (fp);
335 if (off != test_modes[i].old_off)
337 printf ("Incorrect old offset. Expected %zu but got %ld, ",
338 test_modes[i].old_off, off);
342 printf ("old offset = %ld, ", off);
344 /* The effect of this write on the offset should be seen in the ftell
345 call that follows it. */
346 int write_ret = write (fd, data, data_len);
347 if (write_ret != data_len)
349 printf ("write failed (%m)\n");
354 if (off != test_modes[i].new_off)
356 printf ("Incorrect new offset. Expected %zu but got %ld",
357 test_modes[i].new_off, off);
361 printf ("new offset = %ld", off);
363 /* Read to the end, write some data to the fd and check if ftell can
364 see the new ofset. Do this test only for files that allow
366 if (test_modes[i].fd_mode != O_WRONLY)
368 char tmpbuf[data_len * char_len];
372 while (fgets_func (tmpbuf, data_len, fp) && !feof (fp));
374 write_ret = write (fd, data, data_len);
375 if (write_ret != data_len)
377 printf ("write failed (%m)\n");
382 if (off != test_modes[i].eof_off)
384 printf (", Incorrect offset after read EOF. "
385 "Expected %zu but got %ld\n",
386 test_modes[i].eof_off, off);
390 printf (", offset after EOF = %ld\n", off);
402 /* This test opens the file for writing, moves the file offset of the
403 underlying file, writes out data and then checks if ftell trips on it. */
405 do_write_test (const char *filename)
415 {"w", O_WRONLY | O_TRUNC},
416 {"w+", O_RDWR | O_TRUNC},
420 for (int j = 0; j < 2; j++)
422 for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
425 printf ("\twrite: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen",
429 fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
431 fileret = get_handles_fdopen (filename, fd, fp,
432 test_modes[i].fd_mode,
438 /* Move offset to just before the end of the file. */
439 off_t seek_ret = lseek (fd, file_len - 1, SEEK_SET);
442 printf ("lseek failed: %m\n");
446 /* Write some data. */
447 size_t written = fputs_func (data, fp);
451 printf ("fputs[1] failed to write data\n");
455 /* Verify that the offset points to the end of the file. The length
456 of the file would be the original length + the length of data
457 written to it - the amount by which we moved the offset using
459 long offset = ftell (fp);
460 file_len = file_len - 1 + data_len;
462 if (offset != file_len)
464 printf ("Incorrect offset. Expected %zu, but got %ld\n",
470 printf ("offset = %ld\n", offset);
478 /* This test opens a file in append mode, writes some data, and then verifies
479 that ftell does not trip over it. */
481 do_append_test (const char *filename)
496 for (int j = 0; j < 2; j++)
498 for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
502 printf ("\tappend: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen",
506 fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
508 fileret = get_handles_fdopen (filename, fd, fp,
509 test_modes[i].fd_mode,
515 /* Write some data. */
516 size_t written = fputs_func (data, fp);
520 printf ("fputs[1] failed to write all data\n");
524 /* Verify that the offset points to the end of the file. The file
525 len is maintained by adding data_len each time to reflect the data
527 long offset = ftell (fp);
528 file_len += data_len;
530 if (offset != file_len)
532 printf ("Incorrect offset. Expected %zu, but got %ld\n",
538 printf ("offset = %ld\n", offset);
543 /* For fdopen in 'a' mode, the file descriptor should not change if the file
544 is already open with the O_APPEND flag set. */
545 fd = open (filename, O_WRONLY | O_APPEND, 0);
548 printf ("open(O_APPEND) failed: %m\n");
552 off_t seek_ret = lseek (fd, file_len - 1, SEEK_SET);
555 printf ("lseek[O_APPEND][0] failed: %m\n");
559 fp = fdopen (fd, "a");
562 printf ("fdopen(O_APPEND) failed: %m\n");
567 off_t new_seek_ret = lseek (fd, 0, SEEK_CUR);
570 printf ("lseek[O_APPEND][1] failed: %m\n");
574 printf ("\tappend: fdopen (file, \"a\"): O_APPEND: ");
576 if (seek_ret != new_seek_ret)
578 printf ("incorrectly modified file offset to %ld, should be %ld",
579 new_seek_ret, seek_ret);
583 printf ("retained current file offset %ld", seek_ret);
585 new_seek_ret = ftello (fp);
587 if (seek_ret != new_seek_ret)
589 printf (", ftello reported incorrect offset %ld, should be %ld\n",
590 new_seek_ret, seek_ret);
594 printf (", ftello reported correct offset %ld\n", seek_ret);
602 do_one_test (const char *filename)
606 ret |= do_ftell_test (filename);
607 ret |= do_write_test (filename);
608 ret |= do_append_test (filename);
609 ret |= do_rewind_test (filename);
610 ret |= do_ftruncate_test (filename);
615 /* Run a set of tests for ftell for regular files and wide mode files. */
623 int fd = create_temp_file ("tst-active-handler-tmp.", &filename);
627 printf ("create_temp_file: %m\n");
631 fp = fdopen (fd, "w");
634 printf ("fdopen[0]: %m\n");
640 data_len = strlen (char_data);
641 file_len = strlen (char_data);
642 written = fputs (data, fp);
646 printf ("fputs[1] failed to write data\n");
654 /* Tests for regular files. */
655 puts ("Regular mode:");
656 fputs_func = (fputs_func_t) fputs;
657 fgets_func = (fgets_func_t) fgets;
659 data_len = strlen (char_data);
660 char_len = sizeof (char);
661 ret |= do_one_test (filename);
663 /* Truncate the file before repeating the tests in wide mode. */
664 fp = fopen (filename, "w");
667 printf ("fopen failed %m\n");
672 /* Tests for wide files. */
674 if (setlocale (LC_ALL, "en_US.UTF-8") == NULL)
676 printf ("Cannot set en_US.UTF-8 locale.\n");
679 fputs_func = (fputs_func_t) fputws;
680 fgets_func = (fgets_func_t) fgetws;
682 data_len = wcslen (wide_data);
683 char_len = sizeof (wchar_t);
684 ret |= do_one_test (filename);