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 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 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 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 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