fseek/fgetc puzzle

John-Mark Gurney jmg at funkthat.com
Mon Feb 20 21:03:20 AEDT 2023

Darren Tucker wrote this message on Fri, Feb 17, 2023 at 19:07 +1100:
> I've got a bit of a puzzle and I was wondering if anyone has any insight
> as to what's going on.
> I added some code to ssh's known_hosts handling that checks if the last
> byte in the file is a newline, and if not, it adds one before writing
> the new record.  I also wrote a regression test for this and in most
> cases this works fine.
> On some platforms (Solaris, OpenIndiana and AIX) however, the test fails
> because it adds two newlines instead of the expected one.  Basically if
> I fseek to the end, read a byte and write a byte the first byte will
> be duplicated.  I reduced it to this test case:
> #include <stdio.h>
> int main(void)
> {
>         FILE *f = fopen("testfile", "w");
>         putc('A', f);
>         fclose(f);
>         f = fopen("testfile", "a+");  /* same behaviour for r+ */
>         fseek(f, -1L, SEEK_END);
>         printf("c=%d\n", fgetc(f));
>         /* fseek(f, 0, SEEK_END);  -- with this it behaves as expected */
>         /* fflush(f);  -- this too */
>         fputc('B', f);
> }
> $ gcc test.c && ./a.out; od -x -c testfile
> c=65
> 0000000    4141    0042
>            A   A   B
> 0000003
> I wrote two bytes and read one but somehow ended up with three bytes in
> the file?  (This example is from Solaris 11 but AIX does the same thing).
> On most platforms this behaves as expected:
> $ cc test.c && ./a.out; od -x -c testfile
> c=65
> 0000000     4241
>            A   B
> 0000002
> Now I could just add the fseek, but as far as I can tell I shouldn't
> have to, and I don't understand why.  I've read the specs and the man
> pages and haven't found anything that would explain this behaviour.
> I'm curious if anyone has a) any insight as to what's going on, or b)
> any additional examples of where it fails?

Can you check the return value of fseek to see if it's non-zero?  It
should be zero if successful, but likely non-zero on the failing

I decided to check the C standard for fseek's behavior, and it looks
like SEEK_END is not specified on text streams, only binary streams
(at least in C99 and C2X).

For text streams, this is what it says is supported (C99
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.

Does it work if you open the file in binary mode?  a+b or r+b?

  John-Mark Gurney				Voice: +1 415 225 5579

     "All that I will do, has been done, All that I have, has not."

More information about the openssh-unix-dev mailing list