I discovered the following surprising behavior recently when adding a few more gnulib:: namespace tags to the Octave sources. Eventually I was able to generate the relatively small example attached below that shows the same behavior and is independent of Octave.
The problem seems to be that fseek is not changing the file position at all unless I call fflush before calling fseek. This is happening on a newly opened file that is opened for reading only. Perhaps I'm missing something fundamental, but I don't see why it should be necessary at all, but especially not for a file opened for reading only. If I compile the attached program on my system (relatively recent Debian testing, AMD64, GCC 4.6.1), without gnulib it works as I would expect. But with gnulib, the file position is not updated without calls to fflush. I'd be grateful if someone could help me understand whether this is a bug in my code (fflush is really needed?) or if the code should be working without having to call fflush. I'm not sure what would be needed for someone to reproduce the problem, so please ask for more info if it is needed. I can send a tarball for this minimal example made with "make dist" if that would help. Compiled with gnulib, the program prints the following for me when run: WITHOUT flush after seek, desired position = original position = 0 eof position = 0 after seek to desired position = 0 final pos after do_seek = 0 status = 0 WITH flush after seek, desired position = original position = 0 eof position = 1750 after seek to desired position = 100 final pos after do_seek = 100 status = 0 My bootstrap.conf, configure.ac, Makefile.am, and libgnu/Makefile.am files are also attached below. jwe
#ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> long do_tell (FILE *fptr, const char *msg) { long pos = ftell (fptr); if (msg) fprintf (stderr, "%s = %ld\n", msg, pos); return pos; } int do_seek (FILE *fptr, long offset, int origin, int flush_before_seek) { if (flush_before_seek) fflush (fptr); int status = fseek (fptr, offset, origin); return status; } int seek_within_file (FILE *fptr, long offset, int origin, int flush_before_seek) { int status = -1; // Find current position so we can return to it if needed. long orig_pos = do_tell (fptr, "original position"); // Move to end of file. If successful, find the offset of the end. status = do_seek (fptr, 0, SEEK_END, flush_before_seek); if (status == 0) { long eof_pos = do_tell (fptr, "eof position"); // Attempt to move to desired position; may be outside bounds of // existing file. status = do_seek (fptr, offset, origin, flush_before_seek); if (status == 0) { // Where are we after moving to desired position? long desired_pos = do_tell (fptr, "after seek to desired position"); if (desired_pos > eof_pos || desired_pos < 0) { // The desired position is outside the bounds of the // existing file. Move back to original position and // return failure status. do_seek (fptr, orig_pos, SEEK_SET, flush_before_seek); status = -1; } } else { // Seeking to the desired position failed. Move back to // original position and return failure status. do_seek (fptr, orig_pos, SEEK_SET, flush_before_seek); status = -1; } } return status; } void doit (const char *fname, long pos, int flush_before_seek, const char *msg) { FILE *fptr = fopen (fname, "rb"); fprintf (stderr, "%s, desired position = ", msg, pos); int status = seek_within_file (fptr, pos, SEEK_SET, flush_before_seek); do_tell (fptr, "final pos after do_seek"); fprintf (stderr, "status = %d\n\n", status); fclose (fptr); } int main (void) { doit ("/etc/passwd", 100, 0, "WITHOUT flush after seek"); doit ("/etc/passwd", 100, 1, "WITH flush after seek"); return 0; }
# Bootstrap configuration. # Copyright (C) 2006-2007, 2009-2011 Free Software Foundation, Inc. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # gnulib modules used by this package. gnulib_modules=" fclose fflush fopen fseek ftell stdio " # Additional xgettext options to use. Use "\\\newline" to break lines. XGETTEXT_OPTIONS=$XGETTEXT_OPTIONS'\\\ --from-code=UTF-8\\\ --flag=asprintf:2:c-format --flag=vasprintf:2:c-format\\\ --flag=asnprintf:3:c-format --flag=vasnprintf:3:c-format\\\ --flag=wrapf:1:c-format\\\ ' # If "AM_GNU_GETTEXT(external" or "AM_GNU_GETTEXT([external]" # appears in configure.ac, exclude some unnecessary files. # Without grep's -E option (not portable enough, pre-configure), # the following test is ugly. Also, this depends on the existence # of configure.ac, not the obsolescent-named configure.in. But if # you're using this infrastructure, you should care about such things. gettext_external=0 grep '^[ ]*AM_GNU_GETTEXT(external\>' configure.ac > /dev/null && gettext_external=1 grep '^[ ]*AM_GNU_GETTEXT(\[external\]' configure.ac > /dev/null && gettext_external=1 if test $gettext_external = 1; then # Gettext supplies these files, but we don't need them since # we don't have an intl subdirectory. excluded_files=' m4/glibc2.m4 m4/intdiv0.m4 m4/lcmessage.m4 m4/lock.m4 m4/printf-posix.m4 m4/size_max.m4 m4/uintmax_t.m4 m4/ulonglong.m4 m4/visibility.m4 m4/xsize.m4 ' fi # Build prerequisites buildreq="\ autoconf 2.59 automake 1.9.6 git 1.5.5 tar - " checkout_only_file=HACKING gnulib_tool_option_extras="--libtool" gnulib_name="libgnu" source_base="libgnu"
AC_INIT([foobar], [0.0.0]) AC_PREREQ([2.62]) AC_CONFIG_SRCDIR([foobar.c]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE([1.11 tar-ustar]) AC_PROG_CC AC_PROG_CPP gl_EARLY gl_INIT AC_PROG_LIBTOOL AC_CONFIG_FILES([Makefile libgnu/Makefile]) AC_OUTPUT
EXTRA_DIST = m4/gnulib-cache.m4 HACKING AUTHORS SUBDIRS = libgnu AM_CPPFLAGS = -I$(top_builddir)/libgnu -I$(top_srcdir)/libgnu -I. ACLOCAL_AMFLAGS = -I m4 foobar_SOURCES = foobar.c foobar_LDADD = libgnu/libgnu.la bin_PROGRAMS = foobar
include gnulib.mk