Setup LOCPATH for tst-ftell-active-handler and tst-ftell-partial-wide in libio
[platform/upstream/glibc.git] / libio / tst-ftell-active-handler.c
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.
5
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.
10
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.
15
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/>.  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <locale.h>
27 #include <wchar.h>
28
29 static int do_test (void);
30
31 #define TEST_FUNCTION do_test ()
32 #include "../test-skeleton.c"
33
34 #define get_handles_fdopen(filename, fd, fp, fd_mode, mode) \
35 ({                                                                            \
36   int ret = 0;                                                                \
37   (fd) = open ((filename), (fd_mode), 0);                                     \
38   if ((fd) == -1)                                                             \
39     {                                                                         \
40       printf ("open failed: %m\n");                                           \
41       ret = 1;                                                                \
42     }                                                                         \
43   else                                                                        \
44     {                                                                         \
45       (fp) = fdopen ((fd), (mode));                                           \
46       if ((fp) == NULL)                                                       \
47         {                                                                     \
48           printf ("fdopen failed: %m\n");                                     \
49           close (fd);                                                         \
50           ret = 1;                                                            \
51         }                                                                     \
52     }                                                                         \
53   ret;                                                                        \
54 })
55
56 #define get_handles_fopen(filename, fd, fp, mode) \
57 ({                                                                            \
58   int ret = 0;                                                                \
59   (fp) = fopen ((filename), (mode));                                          \
60   if ((fp) == NULL)                                                           \
61     {                                                                         \
62       printf ("fopen failed: %m\n");                                          \
63       ret = 1;                                                                \
64     }                                                                         \
65   else                                                                        \
66     {                                                                         \
67       (fd) = fileno (fp);                                                     \
68       if ((fd) == -1)                                                         \
69         {                                                                     \
70           printf ("fileno failed: %m\n");                                     \
71           ret = 1;                                                            \
72         }                                                                     \
73     }                                                                         \
74   ret;                                                                        \
75 })
76
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
81    length.  */
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
88 typedef int (*fputs_func_t) (const void *data, FILE *fp);
89 fputs_func_t fputs_func;
90
91 /* Test that ftell output after a rewind is correct.  */
92 static int
93 do_rewind_test (const char *filename)
94 {
95   int ret = 0;
96   struct test
97     {
98       const char *mode;
99       int fd_mode;
100       size_t old_off;
101       size_t new_off;
102     } test_modes[] = {
103           {"w", O_WRONLY, 0, data_len},
104           {"w+", O_RDWR, 0, data_len},
105           {"r+", O_RDWR, 0, data_len},
106           /* The new offsets for 'a' and 'a+' modes have to factor in the
107              previous writes since they always append to the end of the
108              file.  */
109           {"a", O_WRONLY, 0, 3 * data_len},
110           {"a+", O_RDWR, 0, 4 * data_len},
111     };
112
113   /* Empty the file before the test so that our offsets are simple to
114      calculate.  */
115   FILE *fp = fopen (filename, "w");
116   if (fp == NULL)
117     {
118       printf ("Failed to open file for emptying\n");
119       return 1;
120     }
121   fclose (fp);
122
123   for (int j = 0; j < 2; j++)
124     {
125       for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
126         {
127           FILE *fp;
128           int fd;
129           int fileret;
130
131           printf ("\trewind: %s (file, \"%s\"): ", j == 0 ? "fdopen" : "fopen",
132                   test_modes[i].mode);
133
134           if (j == 0)
135             fileret = get_handles_fdopen (filename, fd, fp,
136                                           test_modes[i].fd_mode,
137                                           test_modes[i].mode);
138           else
139             fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
140
141           if (fileret != 0)
142             return fileret;
143
144           /* Write some content to the file, rewind and ensure that the ftell
145              output after the rewind is 0.  POSIX does not specify what the
146              behavior is when a file is rewound in 'a' mode, so we retain
147              current behavior, which is to keep the 0 offset.  */
148           size_t written = fputs_func (data, fp);
149
150           if (written == EOF)
151             {
152               printf ("fputs[1] failed to write data\n");
153               ret |= 1;
154             }
155
156           rewind (fp);
157           long offset = ftell (fp);
158
159           if (offset != test_modes[i].old_off)
160             {
161               printf ("Incorrect old offset.  Expected %zu, but got %ld, ",
162                       test_modes[i].old_off, offset);
163               ret |= 1;
164             }
165           else
166             printf ("old offset = %ld, ", offset);
167
168           written = fputs_func (data, fp);
169
170           if (written == EOF)
171             {
172               printf ("fputs[1] failed to write data\n");
173               ret |= 1;
174             }
175
176           /* After this write, the offset in append modes should factor in the
177              implicit lseek to the end of file.  */
178           offset = ftell (fp);
179           if (offset != test_modes[i].new_off)
180             {
181               printf ("Incorrect new offset.  Expected %zu, but got %ld\n",
182                       test_modes[i].new_off, offset);
183               ret |= 1;
184             }
185           else
186             printf ("new offset = %ld\n", offset);
187         }
188     }
189   return ret;
190 }
191
192 /* Test that the value of ftell is not cached when the stream handle is not
193    active.  */
194 static int
195 do_ftell_test (const char *filename)
196 {
197   int ret = 0;
198   struct test
199     {
200       const char *mode;
201       int fd_mode;
202       size_t old_off;
203       size_t new_off;
204     } test_modes[] = {
205           /* In w, w+ and r+ modes, the file position should be at the
206              beginning of the file.  After the write, the offset should be
207              updated to data_len.  */
208           {"w", O_WRONLY, 0, data_len},
209           {"w+", O_RDWR, 0, data_len},
210           {"r+", O_RDWR, 0, data_len},
211           /* For the 'a' mode, the initial file position should be the
212              current end of file. After the write, the offset has data_len
213              added to the old value.  For a+ mode however, the initial file
214              position is the file position of the underlying file descriptor,
215              since it is initially assumed to be in read mode.  */
216           {"a", O_WRONLY, data_len, 2 * data_len},
217           {"a+", O_RDWR, 0, 3 * data_len},
218     };
219   for (int j = 0; j < 2; j++)
220     {
221       for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
222         {
223           FILE *fp;
224           int fd;
225           int fileret;
226
227           printf ("\tftell: %s (file, \"%s\"): ", j == 0 ? "fdopen" : "fopen",
228                   test_modes[i].mode);
229
230           if (j == 0)
231             fileret = get_handles_fdopen (filename, fd, fp,
232                                           test_modes[i].fd_mode,
233                                           test_modes[i].mode);
234           else
235             fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
236
237           if (fileret != 0)
238             return fileret;
239
240           long off = ftell (fp);
241           if (off != test_modes[i].old_off)
242             {
243               printf ("Incorrect old offset.  Expected %zu but got %ld, ",
244                       test_modes[i].old_off, off);
245               ret |= 1;
246             }
247           else
248             printf ("old offset = %ld, ", off);
249
250           /* The effect of this write on the offset should be seen in the ftell
251              call that follows it.  */
252           int write_ret = write (fd, data, data_len);
253           if (write_ret != data_len)
254             {
255               printf ("write failed (%m)\n");
256               ret |= 1;
257             }
258           off = ftell (fp);
259
260           if (off != test_modes[i].new_off)
261             {
262               printf ("Incorrect new offset.  Expected %zu but got %ld\n",
263                       test_modes[i].new_off, off);
264               ret |= 1;
265             }
266           else
267             printf ("new offset = %ld\n", off);
268
269           fclose (fp);
270         }
271     }
272
273   return ret;
274 }
275
276 /* This test opens the file for writing, moves the file offset of the
277    underlying file, writes out data and then checks if ftell trips on it.  */
278 static int
279 do_write_test (const char *filename)
280 {
281   FILE *fp = NULL;
282   int fd;
283   int ret = 0;
284   struct test
285     {
286       const char *mode;
287       int fd_mode;
288     } test_modes[] = {
289           {"w", O_WRONLY},
290           {"w+", O_RDWR},
291           {"r+", O_RDWR}
292     };
293
294   for (int j = 0; j < 2; j++)
295     {
296       for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
297         {
298           int fileret;
299           printf ("\twrite: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen",
300                   test_modes[i].mode);
301
302           if (j == 0)
303             fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
304           else
305             fileret = get_handles_fdopen (filename, fd, fp,
306                                           test_modes[i].fd_mode,
307                                           test_modes[i].mode);
308
309           if (fileret != 0)
310             return fileret;
311
312           /* Move offset to just before the end of the file.  */
313           off_t seek_ret = lseek (fd, file_len - 1, SEEK_SET);
314           if (seek_ret == -1)
315             {
316               printf ("lseek failed: %m\n");
317               ret |= 1;
318             }
319
320           /* Write some data.  */
321           size_t written = fputs_func (data, fp);
322
323           if (written == EOF)
324             {
325               printf ("fputs[1] failed to write data\n");
326               ret |= 1;
327             }
328
329           /* Verify that the offset points to the end of the file.  The length
330              of the file would be the original length + the length of data
331              written to it - the amount by which we moved the offset using
332              lseek.  */
333           long offset = ftell (fp);
334           file_len = file_len - 1 + data_len;
335
336           if (offset != file_len)
337             {
338               printf ("Incorrect offset.  Expected %zu, but got %ld\n",
339                       file_len, offset);
340
341               ret |= 1;
342             }
343
344           printf ("offset = %ld\n", offset);
345           fclose (fp);
346         }
347     }
348
349   return ret;
350 }
351
352 /* This test opens a file in append mode, writes some data, and then verifies
353    that ftell does not trip over it.  */
354 static int
355 do_append_test (const char *filename)
356 {
357   FILE *fp = NULL;
358   int ret = 0;
359   int fd;
360
361   struct test
362     {
363       const char *mode;
364       int fd_mode;
365     } test_modes[] = {
366           {"a", O_WRONLY},
367           {"a+", O_RDWR}
368     };
369
370   for (int j = 0; j < 2; j++)
371     {
372       for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
373         {
374           int fileret;
375
376           printf ("\tappend: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen",
377                   test_modes[i].mode);
378
379           if (j == 0)
380             fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
381           else
382             fileret = get_handles_fdopen (filename, fd, fp,
383                                           test_modes[i].fd_mode,
384                                           test_modes[i].mode);
385
386           if (fileret != 0)
387             return fileret;
388
389           /* Write some data.  */
390           size_t written = fputs_func (data, fp);
391
392           if (written == EOF)
393             {
394               printf ("fputs[1] failed to write all data\n");
395               ret |= 1;
396             }
397
398           /* Verify that the offset points to the end of the file.  The file
399              len is maintained by adding data_len each time to reflect the data
400              written to it.  */
401           long offset = ftell (fp);
402           file_len += data_len;
403
404           if (offset != file_len)
405             {
406               printf ("Incorrect offset.  Expected %zu, but got %ld\n",
407                       file_len, offset);
408
409               ret |= 1;
410             }
411
412           printf ("offset = %ld\n", offset);
413           fclose (fp);
414         }
415     }
416
417   /* For fdopen in 'a' mode, the file descriptor should not change if the file
418      is already open with the O_APPEND flag set.  */
419   fd = open (filename, O_WRONLY | O_APPEND, 0);
420   if (fd == -1)
421     {
422       printf ("open(O_APPEND) failed: %m\n");
423       return 1;
424     }
425
426   off_t seek_ret = lseek (fd, file_len - 1, SEEK_SET);
427   if (seek_ret == -1)
428     {
429       printf ("lseek[O_APPEND][0] failed: %m\n");
430       ret |= 1;
431     }
432
433   fp = fdopen (fd, "a");
434   if (fp == NULL)
435     {
436       printf ("fdopen(O_APPEND) failed: %m\n");
437       close (fd);
438       return 1;
439     }
440
441   off_t new_seek_ret = lseek (fd, 0, SEEK_CUR);
442   if (seek_ret == -1)
443     {
444       printf ("lseek[O_APPEND][1] failed: %m\n");
445       ret |= 1;
446     }
447
448   printf ("\tappend: fdopen (file, \"a\"): O_APPEND: ");
449
450   if (seek_ret != new_seek_ret)
451     {
452       printf ("incorrectly modified file offset to %ld, should be %ld",
453               new_seek_ret, seek_ret);
454       ret |= 1;
455     }
456   else
457     printf ("retained current file offset %ld", seek_ret);
458
459   new_seek_ret = ftello (fp);
460
461   if (seek_ret != new_seek_ret)
462     {
463       printf (", ftello reported incorrect offset %ld, should be %ld\n",
464               new_seek_ret, seek_ret);
465       ret |= 1;
466     }
467   else
468     printf (", ftello reported correct offset %ld\n", seek_ret);
469
470   fclose (fp);
471
472   return ret;
473 }
474
475 static int
476 do_one_test (const char *filename)
477 {
478   int ret = 0;
479
480   ret |= do_ftell_test (filename);
481   ret |= do_write_test (filename);
482   ret |= do_append_test (filename);
483   ret |= do_rewind_test (filename);
484
485   return ret;
486 }
487
488 /* Run a set of tests for ftell for regular files and wide mode files.  */
489 static int
490 do_test (void)
491 {
492   int ret = 0;
493   FILE *fp = NULL;
494   char *filename;
495   size_t written;
496   int fd = create_temp_file ("tst-active-handler-tmp.", &filename);
497
498   if (fd == -1)
499     {
500       printf ("create_temp_file: %m\n");
501       return 1;
502     }
503
504   fp = fdopen (fd, "w");
505   if (fp == NULL)
506     {
507       printf ("fdopen[0]: %m\n");
508       close (fd);
509       return 1;
510     }
511
512   data = char_data;
513   data_len = strlen (char_data);
514   file_len = strlen (char_data);
515   written = fputs (data, fp);
516
517   if (written == EOF)
518     {
519       printf ("fputs[1] failed to write data\n");
520       ret = 1;
521     }
522
523   fclose (fp);
524   if (ret)
525     return ret;
526
527   /* Tests for regular files.  */
528   puts ("Regular mode:");
529   fputs_func = (fputs_func_t) fputs;
530   data = char_data;
531   data_len = strlen (char_data);
532   ret |= do_one_test (filename);
533
534   /* Truncate the file before repeating the tests in wide mode.  */
535   fp = fopen (filename, "w");
536   if (fp == NULL)
537     {
538       printf ("fopen failed %m\n");
539       return 1;
540     }
541   fclose (fp);
542
543   /* Tests for wide files.  */
544   puts ("Wide mode:");
545   if (setlocale (LC_ALL, "en_US.UTF-8") == NULL)
546     {
547       printf ("Cannot set en_US.UTF-8 locale.\n");
548       return 1;
549     }
550   fputs_func = (fputs_func_t) fputws;
551   data = wide_data;
552   data_len = wcslen (wide_data);
553   ret |= do_one_test (filename);
554
555   return ret;
556 }