1 | /* Tests of fseek and fseeko. |
2 | Copyright (C) 2000-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 <error.h> |
20 | #include <errno.h> |
21 | #include <stdio.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | #include <unistd.h> |
25 | #include <time.h> |
26 | #include <sys/stat.h> |
27 | |
28 | #include <support/support.h> |
29 | |
30 | static int |
31 | do_test (void) |
32 | { |
33 | const char *tmpdir; |
34 | char *fname; |
35 | int fd; |
36 | FILE *fp; |
37 | const char outstr[] = "hello world!\n" ; |
38 | char strbuf[sizeof outstr]; |
39 | char buf[200]; |
40 | struct stat64 st1; |
41 | struct stat64 st2; |
42 | int result = 0; |
43 | |
44 | tmpdir = getenv ("TMPDIR" ); |
45 | if (tmpdir == NULL || tmpdir[0] == '\0') |
46 | tmpdir = "/tmp" ; |
47 | |
48 | fname = xasprintf (format: "%s/tst-fseek.XXXXXX" , tmpdir); |
49 | |
50 | /* Create a temporary file. */ |
51 | fd = mkstemp (template: fname); |
52 | if (fd == -1) |
53 | error (EXIT_FAILURE, errno, format: "cannot open temporary file" ); |
54 | |
55 | fp = fdopen (fd, "w+" ); |
56 | if (fp == NULL) |
57 | error (EXIT_FAILURE, errno, format: "cannot get FILE for temporary file" ); |
58 | |
59 | setbuffer (stream: fp, buf: strbuf, size: sizeof (outstr) -1); |
60 | |
61 | if (fwrite (outstr, sizeof (outstr) - 1, 1, fp) != 1) |
62 | { |
63 | printf (format: "%d: write error\n" , __LINE__); |
64 | result = 1; |
65 | goto out; |
66 | } |
67 | |
68 | /* The EOF flag must be reset. */ |
69 | if (fgetc (stream: fp) != EOF) |
70 | { |
71 | printf (format: "%d: managed to read at end of file\n" , __LINE__); |
72 | result = 1; |
73 | } |
74 | else if (! feof (stream: fp)) |
75 | { |
76 | printf (format: "%d: EOF flag not set\n" , __LINE__); |
77 | result = 1; |
78 | } |
79 | if (fseek (fp, 0, SEEK_CUR) != 0) |
80 | { |
81 | printf (format: "%d: fseek(fp, 0, SEEK_CUR) failed\n" , __LINE__); |
82 | result = 1; |
83 | } |
84 | else if (feof (stream: fp)) |
85 | { |
86 | printf (format: "%d: fseek() didn't reset EOF flag\n" , __LINE__); |
87 | result = 1; |
88 | } |
89 | |
90 | /* Do the same for fseeko(). */ |
91 | if (fgetc (stream: fp) != EOF) |
92 | { |
93 | printf (format: "%d: managed to read at end of file\n" , __LINE__); |
94 | result = 1; |
95 | } |
96 | else if (! feof (stream: fp)) |
97 | { |
98 | printf (format: "%d: EOF flag not set\n" , __LINE__); |
99 | result = 1; |
100 | } |
101 | if (fseeko (stream: fp, off: 0, SEEK_CUR) != 0) |
102 | { |
103 | printf (format: "%d: fseek(fp, 0, SEEK_CUR) failed\n" , __LINE__); |
104 | result = 1; |
105 | } |
106 | else if (feof (stream: fp)) |
107 | { |
108 | printf (format: "%d: fseek() didn't reset EOF flag\n" , __LINE__); |
109 | result = 1; |
110 | } |
111 | |
112 | /* Go back to the beginning of the file: absolute. */ |
113 | if (fseek (fp, 0, SEEK_SET) != 0) |
114 | { |
115 | printf (format: "%d: fseek(fp, 0, SEEK_SET) failed\n" , __LINE__); |
116 | result = 1; |
117 | } |
118 | else if (fflush (fp) != 0) |
119 | { |
120 | printf (format: "%d: fflush() failed\n" , __LINE__); |
121 | result = 1; |
122 | } |
123 | else if (lseek (fd: fd, offset: 0, SEEK_CUR) != 0) |
124 | { |
125 | printf (format: "%d: lseek() returned different position\n" , __LINE__); |
126 | result = 1; |
127 | } |
128 | else if (fread (ptr: buf, size: sizeof (outstr) - 1, n: 1, stream: fp) != 1) |
129 | { |
130 | printf (format: "%d: fread() failed\n" , __LINE__); |
131 | result = 1; |
132 | } |
133 | else if (memcmp (buf, outstr, sizeof (outstr) - 1) != 0) |
134 | { |
135 | printf (format: "%d: content after fseek(,,SEEK_SET) wrong\n" , __LINE__); |
136 | result = 1; |
137 | } |
138 | |
139 | /* Now with fseeko. */ |
140 | if (fseeko (stream: fp, off: 0, SEEK_SET) != 0) |
141 | { |
142 | printf (format: "%d: fseeko(fp, 0, SEEK_SET) failed\n" , __LINE__); |
143 | result = 1; |
144 | } |
145 | else if (fflush (fp) != 0) |
146 | { |
147 | printf (format: "%d: fflush() failed\n" , __LINE__); |
148 | result = 1; |
149 | } |
150 | else if (lseek (fd: fd, offset: 0, SEEK_CUR) != 0) |
151 | { |
152 | printf (format: "%d: lseek() returned different position\n" , __LINE__); |
153 | result = 1; |
154 | } |
155 | else if (fread (ptr: buf, size: sizeof (outstr) - 1, n: 1, stream: fp) != 1) |
156 | { |
157 | printf (format: "%d: fread() failed\n" , __LINE__); |
158 | result = 1; |
159 | } |
160 | else if (memcmp (buf, outstr, sizeof (outstr) - 1) != 0) |
161 | { |
162 | printf (format: "%d: content after fseeko(,,SEEK_SET) wrong\n" , __LINE__); |
163 | result = 1; |
164 | } |
165 | |
166 | /* Go back to the beginning of the file: relative. */ |
167 | if (fseek (fp, -((int) sizeof (outstr) - 1), SEEK_CUR) != 0) |
168 | { |
169 | printf (format: "%d: fseek(fp, 0, SEEK_SET) failed\n" , __LINE__); |
170 | result = 1; |
171 | } |
172 | else if (fflush (fp) != 0) |
173 | { |
174 | printf (format: "%d: fflush() failed\n" , __LINE__); |
175 | result = 1; |
176 | } |
177 | else if (lseek (fd: fd, offset: 0, SEEK_CUR) != 0) |
178 | { |
179 | printf (format: "%d: lseek() returned different position\n" , __LINE__); |
180 | result = 1; |
181 | } |
182 | else if (fread (ptr: buf, size: sizeof (outstr) - 1, n: 1, stream: fp) != 1) |
183 | { |
184 | printf (format: "%d: fread() failed\n" , __LINE__); |
185 | result = 1; |
186 | } |
187 | else if (memcmp (buf, outstr, sizeof (outstr) - 1) != 0) |
188 | { |
189 | printf (format: "%d: content after fseek(,,SEEK_SET) wrong\n" , __LINE__); |
190 | result = 1; |
191 | } |
192 | |
193 | /* Now with fseeko. */ |
194 | if (fseeko (stream: fp, off: -((int) sizeof (outstr) - 1), SEEK_CUR) != 0) |
195 | { |
196 | printf (format: "%d: fseeko(fp, 0, SEEK_SET) failed\n" , __LINE__); |
197 | result = 1; |
198 | } |
199 | else if (fflush (fp) != 0) |
200 | { |
201 | printf (format: "%d: fflush() failed\n" , __LINE__); |
202 | result = 1; |
203 | } |
204 | else if (lseek (fd: fd, offset: 0, SEEK_CUR) != 0) |
205 | { |
206 | printf (format: "%d: lseek() returned different position\n" , __LINE__); |
207 | result = 1; |
208 | } |
209 | else if (fread (ptr: buf, size: sizeof (outstr) - 1, n: 1, stream: fp) != 1) |
210 | { |
211 | printf (format: "%d: fread() failed\n" , __LINE__); |
212 | result = 1; |
213 | } |
214 | else if (memcmp (buf, outstr, sizeof (outstr) - 1) != 0) |
215 | { |
216 | printf (format: "%d: content after fseeko(,,SEEK_SET) wrong\n" , __LINE__); |
217 | result = 1; |
218 | } |
219 | |
220 | /* Go back to the beginning of the file: from the end. */ |
221 | if (fseek (fp, -((int) sizeof (outstr) - 1), SEEK_END) != 0) |
222 | { |
223 | printf (format: "%d: fseek(fp, 0, SEEK_SET) failed\n" , __LINE__); |
224 | result = 1; |
225 | } |
226 | else if (fflush (fp) != 0) |
227 | { |
228 | printf (format: "%d: fflush() failed\n" , __LINE__); |
229 | result = 1; |
230 | } |
231 | else if (lseek (fd: fd, offset: 0, SEEK_CUR) != 0) |
232 | { |
233 | printf (format: "%d: lseek() returned different position\n" , __LINE__); |
234 | result = 1; |
235 | } |
236 | else if (fread (ptr: buf, size: sizeof (outstr) - 1, n: 1, stream: fp) != 1) |
237 | { |
238 | printf (format: "%d: fread() failed\n" , __LINE__); |
239 | result = 1; |
240 | } |
241 | else if (memcmp (buf, outstr, sizeof (outstr) - 1) != 0) |
242 | { |
243 | printf (format: "%d: content after fseek(,,SEEK_SET) wrong\n" , __LINE__); |
244 | result = 1; |
245 | } |
246 | |
247 | /* Now with fseeko. */ |
248 | if (fseeko (stream: fp, off: -((int) sizeof (outstr) - 1), SEEK_END) != 0) |
249 | { |
250 | printf (format: "%d: fseeko(fp, 0, SEEK_SET) failed\n" , __LINE__); |
251 | result = 1; |
252 | } |
253 | else if (fflush (fp) != 0) |
254 | { |
255 | printf (format: "%d: fflush() failed\n" , __LINE__); |
256 | result = 1; |
257 | } |
258 | else if (lseek (fd: fd, offset: 0, SEEK_CUR) != 0) |
259 | { |
260 | printf (format: "%d: lseek() returned different position\n" , __LINE__); |
261 | result = 1; |
262 | } |
263 | else if (fread (ptr: buf, size: sizeof (outstr) - 1, n: 1, stream: fp) != 1) |
264 | { |
265 | printf (format: "%d: fread() failed\n" , __LINE__); |
266 | result = 1; |
267 | } |
268 | else if (memcmp (buf, outstr, sizeof (outstr) - 1) != 0) |
269 | { |
270 | printf (format: "%d: content after fseeko(,,SEEK_SET) wrong\n" , __LINE__); |
271 | result = 1; |
272 | } |
273 | |
274 | if (fwrite (outstr, sizeof (outstr) - 1, 1, fp) != 1) |
275 | { |
276 | printf (format: "%d: write error 2\n" , __LINE__); |
277 | result = 1; |
278 | goto out; |
279 | } |
280 | |
281 | if (fwrite (outstr, sizeof (outstr) - 1, 1, fp) != 1) |
282 | { |
283 | printf (format: "%d: write error 3\n" , __LINE__); |
284 | result = 1; |
285 | goto out; |
286 | } |
287 | |
288 | if (fwrite (outstr, sizeof (outstr) - 1, 1, fp) != 1) |
289 | { |
290 | printf (format: "%d: write error 4\n" , __LINE__); |
291 | result = 1; |
292 | goto out; |
293 | } |
294 | |
295 | if (fwrite (outstr, sizeof (outstr) - 1, 1, fp) != 1) |
296 | { |
297 | printf (format: "%d: write error 5\n" , __LINE__); |
298 | result = 1; |
299 | goto out; |
300 | } |
301 | |
302 | if (fputc (c: '1', stream: fp) == EOF || fputc (c: '2', stream: fp) == EOF) |
303 | { |
304 | printf (format: "%d: cannot add characters at the end\n" , __LINE__); |
305 | result = 1; |
306 | goto out; |
307 | } |
308 | |
309 | /* Check the access time. */ |
310 | if (fstat64 (fd: fd, buf: &st1) < 0) |
311 | { |
312 | printf (format: "%d: fstat64() before fseeko() failed\n\n" , __LINE__); |
313 | result = 1; |
314 | } |
315 | else |
316 | { |
317 | sleep (seconds: 1); |
318 | |
319 | if (fseek (fp, -(2 + 2 * (sizeof (outstr) - 1)), SEEK_CUR) != 0) |
320 | { |
321 | printf (format: "%d: fseek() after write characters failed\n" , __LINE__); |
322 | result = 1; |
323 | goto out; |
324 | } |
325 | else |
326 | { |
327 | |
328 | time_t t; |
329 | /* Make sure the timestamp actually can be different. */ |
330 | sleep (seconds: 1); |
331 | t = time (NULL); |
332 | |
333 | if (fstat64 (fd: fd, buf: &st2) < 0) |
334 | { |
335 | printf (format: "%d: fstat64() after fseeko() failed\n\n" , __LINE__); |
336 | result = 1; |
337 | } |
338 | if (st1.st_ctime >= t) |
339 | { |
340 | printf (format: "%d: st_ctime not updated\n" , __LINE__); |
341 | result = 1; |
342 | } |
343 | if (st1.st_mtime >= t) |
344 | { |
345 | printf (format: "%d: st_mtime not updated\n" , __LINE__); |
346 | result = 1; |
347 | } |
348 | if (st1.st_ctime >= st2.st_ctime) |
349 | { |
350 | printf (format: "%d: st_ctime not changed\n" , __LINE__); |
351 | result = 1; |
352 | } |
353 | if (st1.st_mtime >= st2.st_mtime) |
354 | { |
355 | printf (format: "%d: st_mtime not changed\n" , __LINE__); |
356 | result = 1; |
357 | } |
358 | } |
359 | } |
360 | |
361 | if (fread (ptr: buf, size: 1, n: 2 + 2 * (sizeof (outstr) - 1), stream: fp) |
362 | != 2 + 2 * (sizeof (outstr) - 1)) |
363 | { |
364 | printf (format: "%d: reading 2 records plus bits failed\n" , __LINE__); |
365 | result = 1; |
366 | } |
367 | else if (memcmp (buf, outstr, sizeof (outstr) - 1) != 0 |
368 | || memcmp (&buf[sizeof (outstr) - 1], outstr, |
369 | sizeof (outstr) - 1) != 0 |
370 | || buf[2 * (sizeof (outstr) - 1)] != '1' |
371 | || buf[2 * (sizeof (outstr) - 1) + 1] != '2') |
372 | { |
373 | printf (format: "%d: reading records failed\n" , __LINE__); |
374 | result = 1; |
375 | } |
376 | else if (ungetc ('9', fp) == EOF) |
377 | { |
378 | printf (format: "%d: ungetc() failed\n" , __LINE__); |
379 | result = 1; |
380 | } |
381 | else if (fseek (fp, -(2 + 2 * (sizeof (outstr) - 1)), SEEK_END) != 0) |
382 | { |
383 | printf (format: "%d: fseek after ungetc failed\n" , __LINE__); |
384 | result = 1; |
385 | } |
386 | else if (fread (ptr: buf, size: 1, n: 2 + 2 * (sizeof (outstr) - 1), stream: fp) |
387 | != 2 + 2 * (sizeof (outstr) - 1)) |
388 | { |
389 | printf (format: "%d: reading 2 records plus bits failed\n" , __LINE__); |
390 | result = 1; |
391 | } |
392 | else if (memcmp (buf, outstr, sizeof (outstr) - 1) != 0 |
393 | || memcmp (&buf[sizeof (outstr) - 1], outstr, |
394 | sizeof (outstr) - 1) != 0 |
395 | || buf[2 * (sizeof (outstr) - 1)] != '1') |
396 | { |
397 | printf (format: "%d: reading records for the second time failed\n" , __LINE__); |
398 | result = 1; |
399 | } |
400 | else if (buf[2 * (sizeof (outstr) - 1) + 1] == '9') |
401 | { |
402 | printf (format: "%d: unget character not ignored\n" , __LINE__); |
403 | result = 1; |
404 | } |
405 | else if (buf[2 * (sizeof (outstr) - 1) + 1] != '2') |
406 | { |
407 | printf (format: "%d: unget somehow changed character\n" , __LINE__); |
408 | result = 1; |
409 | } |
410 | |
411 | fclose (fp); |
412 | |
413 | fp = fopen (fname, "r" ); |
414 | if (fp == NULL) |
415 | { |
416 | printf (format: "%d: fopen() failed\n\n" , __LINE__); |
417 | result = 1; |
418 | } |
419 | else if (fstat64 (fd: fileno (fp), buf: &st1) < 0) |
420 | { |
421 | printf (format: "%d: fstat64() before fseeko() failed\n\n" , __LINE__); |
422 | result = 1; |
423 | } |
424 | else if (fseeko (stream: fp, off: 0, SEEK_END) != 0) |
425 | { |
426 | printf (format: "%d: fseeko(fp, 0, SEEK_END) failed\n" , __LINE__); |
427 | result = 1; |
428 | } |
429 | else if (ftello (stream: fp) != st1.st_size) |
430 | { |
431 | printf (format: "%d: fstat64 st_size %zd ftello %zd\n" , __LINE__, |
432 | (size_t) st1.st_size, (size_t) ftello (stream: fp)); |
433 | result = 1; |
434 | } |
435 | else |
436 | printf (format: "%d: SEEK_END works\n" , __LINE__); |
437 | if (fp != NULL) |
438 | fclose (fp); |
439 | |
440 | fp = fopen (fname, "r" ); |
441 | if (fp == NULL) |
442 | { |
443 | printf (format: "%d: fopen() failed\n\n" , __LINE__); |
444 | result = 1; |
445 | } |
446 | else if (fstat64 (fd: fileno (fp), buf: &st1) < 0) |
447 | { |
448 | printf (format: "%d: fstat64() before fgetc() failed\n\n" , __LINE__); |
449 | result = 1; |
450 | } |
451 | else if (fgetc (stream: fp) == EOF) |
452 | { |
453 | printf (format: "%d: fgetc() before fseeko() failed\n\n" , __LINE__); |
454 | result = 1; |
455 | } |
456 | else if (fseeko (stream: fp, off: 0, SEEK_END) != 0) |
457 | { |
458 | printf (format: "%d: fseeko(fp, 0, SEEK_END) failed\n" , __LINE__); |
459 | result = 1; |
460 | } |
461 | else if (ftello (stream: fp) != st1.st_size) |
462 | { |
463 | printf (format: "%d: fstat64 st_size %zd ftello %zd\n" , __LINE__, |
464 | (size_t) st1.st_size, (size_t) ftello (stream: fp)); |
465 | result = 1; |
466 | } |
467 | else |
468 | printf (format: "%d: SEEK_END works\n" , __LINE__); |
469 | if (fp != NULL) |
470 | fclose (fp); |
471 | |
472 | out: |
473 | unlink (name: fname); |
474 | |
475 | return result; |
476 | } |
477 | |
478 | #define TEST_FUNCTION do_test () |
479 | #include "../test-skeleton.c" |
480 | |