1 /* Verify that ftell returns the correct value after a read and a write on a
2    file opened in a+ mode.
3    Copyright (C) 2014-2022 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    <https://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 <locale.h>
26 #include <wchar.h>
27 
28 /* data points to either char_data or wide_data, depending on whether we're
29    testing regular file mode or wide mode respectively.  Similarly,
30    fputs_func points to either fputs or fputws.  data_len keeps track of the
31    length of the current data and file_len maintains the current file
32    length.  */
33 #define BUF_LEN 4
34 static void *buf;
35 static char char_buf[BUF_LEN];
36 static wchar_t wide_buf[BUF_LEN];
37 static const void *data;
38 static const char *char_data = "abcdefghijklmnopqrstuvwxyz";
39 static const wchar_t *wide_data = L"abcdefghijklmnopqrstuvwxyz";
40 static size_t data_len;
41 static size_t file_len;
42 
43 typedef int (*fputs_func_t) (const void *data, FILE *fp);
44 fputs_func_t fputs_func;
45 
46 typedef void *(*fgets_func_t) (void *s, int size, FILE *stream);
47 fgets_func_t fgets_func;
48 
49 static int do_test (void);
50 
51 #define TEST_FUNCTION do_test ()
52 #include "../test-skeleton.c"
53 
54 static FILE *
init_file(const char * filename)55 init_file (const char *filename)
56 {
57   FILE *fp = fopen (filename, "w");
58   if (fp == NULL)
59     {
60       printf ("fopen: %m\n");
61       return NULL;
62     }
63 
64   int written = fputs_func (data, fp);
65 
66   if (written == EOF)
67     {
68       printf ("fputs failed to write data\n");
69       fclose (fp);
70       return NULL;
71     }
72 
73   file_len = data_len;
74 
75   fclose (fp);
76 
77   fp = fopen (filename, "a+");
78   if (fp == NULL)
79     {
80       printf ("fopen(a+): %m\n");
81       return NULL;
82     }
83 
84   return fp;
85 }
86 
87 static int
do_one_test(const char * filename)88 do_one_test (const char *filename)
89 {
90   FILE *fp = init_file (filename);
91 
92   if (fp == NULL)
93     return 1;
94 
95   void *ret = fgets_func (buf, BUF_LEN, fp);
96 
97   if (ret == NULL)
98     {
99       printf ("read failed: %m\n");
100       fclose (fp);
101       return 1;
102     }
103 
104   int written = fputs_func (data, fp);
105 
106   if (written == EOF)
107     {
108       printf ("fputs failed to write data\n");
109       fclose (fp);
110       return 1;
111     }
112 
113   file_len += data_len;
114 
115   long off = ftell (fp);
116 
117   if (off != file_len)
118     {
119       printf ("Incorrect offset %ld, expected %zu\n", off, file_len);
120       fclose (fp);
121       return 1;
122     }
123   else
124     printf ("Correct offset %ld after write.\n", off);
125 
126   return 0;
127 }
128 
129 /* Run the tests for regular files and wide mode files.  */
130 static int
do_test(void)131 do_test (void)
132 {
133   int ret = 0;
134   char *filename;
135   int fd = create_temp_file ("tst-ftell-append-tmp.", &filename);
136 
137   if (fd == -1)
138     {
139       printf ("create_temp_file: %m\n");
140       return 1;
141     }
142 
143   close (fd);
144 
145   /* Tests for regular files.  */
146   puts ("Regular mode:");
147   fputs_func = (fputs_func_t) fputs;
148   fgets_func = (fgets_func_t) fgets;
149   data = char_data;
150   buf = char_buf;
151   data_len = strlen (char_data);
152   ret |= do_one_test (filename);
153 
154   /* Tests for wide files.  */
155   puts ("Wide mode:");
156   if (setlocale (LC_ALL, "en_US.UTF-8") == NULL)
157     {
158       printf ("Cannot set en_US.UTF-8 locale.\n");
159       return 1;
160     }
161   fputs_func = (fputs_func_t) fputws;
162   fgets_func = (fgets_func_t) fgetws;
163   data = wide_data;
164   buf = wide_buf;
165   data_len = wcslen (wide_data);
166   ret |= do_one_test (filename);
167 
168   return ret;
169 }
170