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