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