xref: /haiku/src/tests/system/libroot/posix/fseek_test.cpp (revision cda7f65b326bed577c56ab9103593d8a9d55cf06)
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