0

A program that runs just fine on my freeBSD system fails when I build it on windows (Visual Studio 15). It goes into an endless loop here:

//...
while (1) {
    if ('@' == fgetc(f)) {
        // we do some stuff here. irrelevant for stackoverflow question
        break;
    }        
    fseek(f, -1, SEEK_CUR);        
    if (0 != fseek(f, -1, SEEK_CUR)) {
        // Beginning of file.
        break;
    }
}
//...

On closer look (by adding a bunch of fgetpos()-calls) I find that fgetc moves the file position indicator backwards. So it misses the beginning of the file and some '@' if they are not in a multiple-of-3 position from the end.

I notice that this only happenes when the file f is opened with

fopen(filename, "a+");
//text mode read/append

When I change it to

fopen(filename, "ab+");
//binary mode read/append

then everything works as expected. I think for my code it is safe just to use binary mode all the time. But two questions remain:

  • Are there reasons that stand against binary mode?
  • What trickery is this with wrong direction in text mode?
Freitags
  • 330
  • 1
  • 10
  • 2
    Can you post an [MCVE](http://stackoverflow.com/help/mcve) please? – Jabberwocky Mar 07 '16 at 16:29
  • 1
    When opening a file in text-mode there's some character translations that can happen, most notably newline in Windows is `"\r\n"` and when reading it is translated to plain `'\n'`. On Unix systems like BSD newlines are only a single `"\n"` so no translation is done on such systems. I don't know if that explains your issue. – Some programmer dude Mar 07 '16 at 16:29
  • "Are there reasons that stand against binary mode?" -- Actually, there is a lot to be said against *text* mode... – DevSolar Mar 07 '16 at 16:32
  • `fseek(f, -1, SEEK_CUR); if (0 != fseek(f, -1, SEEK_CUR)) {` looks like code is trying to move before the beginning of the file. Suspect that is UB. Why **2** seek backwards? – chux - Reinstate Monica Mar 07 '16 at 16:52
  • 1
    Possible duplicate of [Using fseek to backtrack](http://stackoverflow.com/questions/780303/using-fseek-to-backtrack) – Zan Lynx Mar 07 '16 at 17:20
  • @ZanLynx that should probably be made a duplicate of this one, as chqrlie's answer is better than all the answers over there – M.M Mar 07 '16 at 23:42
  • I accepted chqrlies answer because what I observed was really a result of two issues. First the SEEK_CUR on text mode files but also the pseudo-meaningfulness of fgetpos(). The quotation from the standard and also the third paragraph are really helpful related to my question. There is some small overlap with the potential duplicate linked by Zan Lynx but my question was not really answered over there. – Freitags Mar 08 '16 at 16:33

1 Answers1

2

Quoting C11 7.21.9.2 the fseek function:

For a text stream, either offset shall be zero, or offset shall be a value returned by an earlier successful call to the ftell function on a stream associated with the same file and whence shall be SEEK_SET.

Invoking fseek with a whence argument of SEEK_CUR on a stream open in text mode is not covered by the C Standard. Opening the file in binary mode seems a much better option.

The value returned by fgetpos() may not be meaningful as an offset in the file, it is only meant to be passed as an argument to fsetpos().

As a general remark, you should try and change you algorithms to avoid relying on backwards seeks in the stream, especially relying on fseek() errors seems unreliable. Instead save the position before the fgetc() with ftell() or fgetpos() and restore it when needed with fseek(pos, SEEK_SET, fp) or fsetpos().

chqrlie
  • 131,814
  • 10
  • 121
  • 189