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
30static int
31do_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

source code of glibc/stdio-common/tst-fseek.c