1 | /* fmemopen tests for append and read mode. |
2 | Copyright (C) 2015-2024 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <assert.h> |
20 | #include <stdio.h> |
21 | #include <string.h> |
22 | #include <sys/types.h> |
23 | |
24 | #include <support/xstdio.h> |
25 | |
26 | static void |
27 | print_buffer (const char *s, size_t n) |
28 | { |
29 | size_t i; |
30 | printf (format: "{" ); |
31 | for (i=0; i<n; ++i) |
32 | { |
33 | printf (format: "0x%02X (%c)" , s[i], s[i]); |
34 | if (i != n) |
35 | printf (format: ", " ); |
36 | } |
37 | } |
38 | |
39 | /* This test check append mode initial position (a/a+) based on POSIX definition |
40 | (BZ#6544 and BZ#13151). */ |
41 | static int |
42 | do_test_write_append (const char *mode) |
43 | { |
44 | char buf[32] = "testing buffer" ; |
45 | char exp[32] = "testing bufferXX" ; |
46 | |
47 | FILE *fp = fmemopen (buf, sizeof (buf), mode); |
48 | |
49 | fflush (fp); |
50 | fprintf (fp, "X" ); |
51 | fseek (fp, 0, SEEK_SET); |
52 | fprintf (fp, "X" ); |
53 | fclose (fp); |
54 | |
55 | if (strcmp (buf, exp) != 0) |
56 | { |
57 | printf (format: "%s: check failed: %s != %s\n" , __FUNCTION__, buf, exp); |
58 | return 1; |
59 | } |
60 | |
61 | return 0; |
62 | } |
63 | |
64 | /* This test check append mode initial position (a/a+) based on POSIX definition |
65 | (BZ#6544 and BZ#13151) for buffer without null byte end. */ |
66 | static int |
67 | do_test_write_append_without_null (const char *mode) |
68 | { |
69 | char buf[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }; |
70 | char exp[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }; |
71 | |
72 | /* If '\0' is not found in buffer, POSIX states that SEEK_SET should be |
73 | the size argument. */ |
74 | FILE *fp = fmemopen (buf, sizeof (buf) - 2, "a" ); |
75 | |
76 | fflush (fp); |
77 | fputc (c: 0x70, stream: fp); |
78 | fseek (fp, 0, SEEK_SET); |
79 | fputc (c: 0x70, stream: fp); |
80 | fputc (c: 0x70, stream: fp); |
81 | fclose (fp); |
82 | |
83 | /* POSIX also states that a write operation on the stream shall not advance |
84 | the current buffer size beyond the size given in fmemopen, so the string |
85 | should be same. */ |
86 | if (memcmp (buf, exp, sizeof (buf)) != 0) |
87 | { |
88 | printf (format: "%s: check failed: " , __FUNCTION__); |
89 | print_buffer (s: buf, n: sizeof (buf)); |
90 | printf (format: "!= " ); |
91 | print_buffer (s: exp, n: sizeof (exp)); |
92 | printf (format: "\n" ); |
93 | return 1; |
94 | } |
95 | |
96 | return 0; |
97 | } |
98 | |
99 | /* This test check for initial position and seek value for fmemopen objects |
100 | opened with append mode. */ |
101 | static int |
102 | do_test_read_append (void) |
103 | { |
104 | char buf[32] = "testing buffer" ; |
105 | size_t buflen = strlen (buf); |
106 | long fpos; |
107 | |
108 | /* POSIX defines for 'a+' the initial position is the first null byte. */ |
109 | FILE *fp = fmemopen (buf, sizeof (buf), "a+" ); |
110 | |
111 | fpos = ftell (stream: fp); |
112 | if (fpos != buflen) |
113 | { |
114 | printf (format: "%s: ftell|SEEK_SET (fp) %li != strlen (%s) %zu\n" , |
115 | __FUNCTION__, fpos, buf, buflen); |
116 | fclose (fp); |
117 | return 1; |
118 | } |
119 | |
120 | fseek (fp, 0, SEEK_END); |
121 | |
122 | if (fpos != buflen) |
123 | { |
124 | printf (format: "%s: ftell|SEEK_END (fp) %li != strlen (%s) %zu\n" , |
125 | __FUNCTION__, fpos, buf, buflen); |
126 | fclose (fp); |
127 | return 1; |
128 | } |
129 | fclose (fp); |
130 | |
131 | /* Check if attempting to read past the current size, defined as strlen (buf) |
132 | yield an EOF. */ |
133 | fp = fmemopen (buf, sizeof (buf), "a+" ); |
134 | if (getc(stream: fp) != EOF) |
135 | { |
136 | printf (format: "%s: getc(fp) != EOF\n" , __FUNCTION__); |
137 | fclose (fp); |
138 | return -1; |
139 | } |
140 | |
141 | fclose (fp); |
142 | |
143 | return 0; |
144 | } |
145 | |
146 | /* This test check for fseek (SEEK_END) using negative offsets (BZ#14292). The |
147 | starting position of descriptor is different base on the opening mode. */ |
148 | static int |
149 | do_test_read_seek_neg (const char *mode, const char *expected) |
150 | { |
151 | char buf[] = "abcdefghijklmnopqrstuvxz0123456789" ; |
152 | char tmp[10]; |
153 | size_t tmps = sizeof (tmps); |
154 | long offset = -11; |
155 | |
156 | FILE *fp = fmemopen (buf, sizeof (buf), mode); |
157 | fseek (fp, offset, SEEK_END); |
158 | xfread (ptr: tmp, size: tmps, nmemb: 1, stream: fp); |
159 | |
160 | if (memcmp (tmp, expected, tmps) != 0) |
161 | { |
162 | printf (format: "%s: fmemopen(%s) - fseek (fp, %li, SEEK_END):\n" , |
163 | __FUNCTION__, mode, offset); |
164 | printf (format: " returned: " ); |
165 | print_buffer (s: tmp, n: tmps); |
166 | printf (format: "\n" ); |
167 | printf (format: " expected: " ); |
168 | print_buffer (s: expected, n: tmps); |
169 | printf (format: "\n" ); |
170 | return 1; |
171 | } |
172 | |
173 | fclose (fp); |
174 | |
175 | return 0; |
176 | } |
177 | |
178 | static int |
179 | do_test_read_seek_negative (void) |
180 | { |
181 | int ret = 0; |
182 | |
183 | /* 'r' and 'w' modes defines the initial position at the buffer start and |
184 | seek with SEEK_END shall seek relative to its size give in fmemopen |
185 | call. The expected tmp result is 0 to 9 *without* the ending null */ |
186 | ret += do_test_read_seek_neg (mode: "r" , expected: "0123456789" ); |
187 | /* 'a+' mode sets the initial position at the first null byte in buffer and |
188 | SEEK_END shall seek relative to its size as well. The expected result is |
189 | z012345678, since SEEK_END plus a+ start at '\0', not size. */ |
190 | ret += do_test_read_seek_neg (mode: "a+" , expected: "z012345678" ); |
191 | |
192 | return ret; |
193 | } |
194 | |
195 | static int |
196 | do_test_write_append_2 (const char *str) |
197 | { |
198 | char buf[10]; |
199 | size_t strn = strlen (str); |
200 | strcpy (buf, str); |
201 | |
202 | FILE *fp = fmemopen (buf, sizeof (buf), "a+" ); |
203 | size_t r = ftell (stream: fp); |
204 | size_t e = strlen (buf); |
205 | if (r != e) |
206 | { |
207 | printf (format: "%s: ftell returned %zu, expected %zu\n" , __FUNCTION__, r, e); |
208 | return 1; |
209 | } |
210 | |
211 | if (fseek (fp, 0, SEEK_SET) == -1) |
212 | { |
213 | printf (format: "%s: fseek returned -1\n" , __FUNCTION__); |
214 | return 1; |
215 | } |
216 | |
217 | int gr; |
218 | for (int i=0; i<strn; ++i) |
219 | { |
220 | if ((gr = getc (stream: fp)) != str[i]) |
221 | { |
222 | printf (format: "%s: getc failed returned %d, expected %d\n" , __FUNCTION__, |
223 | gr, str[i]); |
224 | return 1; |
225 | } |
226 | } |
227 | if ((gr = getc (stream: fp)) != EOF) |
228 | { |
229 | printf (format: "%s: getc failed returned %d, expected EOF\n" , __FUNCTION__, |
230 | gr); |
231 | return 1; |
232 | } |
233 | |
234 | if (fseek (fp, e+1, SEEK_SET) == -1) |
235 | { |
236 | printf (format: "%s: fseek returned -1\n" , __FUNCTION__); |
237 | return 1; |
238 | } |
239 | |
240 | if ((r = ftell (stream: fp)) != e+1) |
241 | { |
242 | printf (format: "%s: ftell returned %zu, expected %zu\n" , __FUNCTION__, r, e+1); |
243 | return 1; |
244 | } |
245 | |
246 | if ((gr = getc (stream: fp)) != EOF) |
247 | { |
248 | printf (format: "%s: getc failed returned %i\n" , __FUNCTION__, gr); |
249 | return 1; |
250 | } |
251 | |
252 | /* Check if internal position is not changed with a getc returning EOF. */ |
253 | if ((r = ftell (stream: fp)) != e+1) |
254 | { |
255 | printf (format: "%s: ftell returned %zu, expected %zu\n" , __FUNCTION__, r, e+1); |
256 | return 1; |
257 | } |
258 | |
259 | if (fseek (fp, 0, SEEK_CUR) == -1) |
260 | { |
261 | printf (format: "%s: fseek returned -1\n" , __FUNCTION__); |
262 | return 1; |
263 | } |
264 | |
265 | /* This should be overwritten by fprintf + fflush. */ |
266 | buf[e+2] = 'X'; |
267 | |
268 | if ((r = fprintf (fp, "%d" , 101)) != 3) |
269 | { |
270 | printf (format: "%s: fprintf returned %zu, expected %d\n" , __FUNCTION__, r, 3); |
271 | return 1; |
272 | } |
273 | |
274 | fflush (fp); |
275 | |
276 | /* Check if internal position is changed by 3 (strlen of '101'). */ |
277 | if ((r = ftell (stream: fp)) != e+3) |
278 | { |
279 | printf (format: "%s: ftell returned %zu, expected %zu\n" , __FUNCTION__, r, e+3); |
280 | return 1; |
281 | } |
282 | |
283 | char exp[20]; |
284 | sprintf (exp, "%s%d" , str, 101); |
285 | if (memcmp (buf, exp, strlen (exp)) != 0) |
286 | { |
287 | printf (format: "%s: check failed:" , __FUNCTION__); |
288 | printf (format: "\nexpected: " ); |
289 | print_buffer (s: buf, n: sizeof (buf)); |
290 | printf (format: "\nbuffer: " ); |
291 | print_buffer (s: exp, n: sizeof (exp)); |
292 | printf (format: "\n" ); |
293 | return 1; |
294 | } |
295 | |
296 | fclose(fp); |
297 | |
298 | return 0; |
299 | } |
300 | |
301 | static int |
302 | do_test (void) |
303 | { |
304 | int ret = 0; |
305 | |
306 | ret += do_test_write_append (mode: "a" ); |
307 | ret += do_test_write_append_without_null (mode: "a" ); |
308 | ret += do_test_write_append (mode: "a+" ); |
309 | ret += do_test_write_append_without_null (mode: "a+" ); |
310 | |
311 | ret += do_test_read_append (); |
312 | |
313 | ret += do_test_read_seek_negative (); |
314 | |
315 | /* First test plus addend will fit in the define buffer of size 10. */ |
316 | ret += do_test_write_append_2 (str: "test" ); |
317 | /* The second test will also fit, but not the final '\0'. */ |
318 | ret += do_test_write_append_2 (str: "testing" ); |
319 | |
320 | return ret; |
321 | } |
322 | |
323 | #define TEST_FUNCTION do_test () |
324 | #include "../test-skeleton.c" |
325 | |