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