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
22void
23error(int status, int errorNum, const char* errorText)
24{
25	fprintf(stderr, "%s (errno: %x)\n", errorText, errorNum);
26	exit(status);
27}
28
29
30int
31glibc_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
355out:
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
372int
373gnulib_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
468int
469main(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