Mailing List Archive

fseek/fgetc puzzle
Hi all.

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?

Thanks.

--
Darren Tucker (dtucker at dtucker.net)
GPG key 11EAA6FA / A86E 3E07 5B19 5880 E860 37F4 9357 ECEF 11EA A6FA (new)
Good judgement comes with experience. Unfortunately, the experience
usually comes from bad judgement.
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
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
systems.

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 7.19.9.2):
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."
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
On Mon, 20 Feb 2023 at 21:03, John-Mark Gurney <jmg@funkthat.com> wrote:
[...]
> 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
> systems.

The fseek returns zero. The real code[0] checks this and other return
values, but I omitted the check in the test case for brevity (and to
make it easier to truss/strace).

> 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 7.19.9.2):
> 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.

In at least the Solaris man page that language seems to only refer to
use with wide characters:

"""
If the stream is to be used with wide character input/output functions,
offset must either be 0 or a value returned by an earlier call to
ftell(3C) on the same stream and whence must be SEEK_SET.
"""

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

Same behaviour as before with both.

$ cat test.c
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
FILE *f = fopen("testfile", "w");
printf("fputc A=%d\n", fputc('A', f));
fclose(f);

f = fopen("testfile", "a+b");
printf("fseek=%d\n", fseek(f, -1L, SEEK_END));
printf("c=%d\n", fgetc(f));
printf("fputc B = %d\n", fputc('B', f));
}
$ gcc test.c && ./a.out && od -x -c testfile
fputc A=65
fseek=0
c=65
fputc B = 66
0000000 4141 0042
A A B
0000003

[0] https://github.com/openssh/openssh-portable/commit/3c379c9a849a635cc7f05cbe49fe473ccf469ef9

--
Darren Tucker (dtucker at dtucker.net)
GPG key 11EAA6FA / A86E 3E07 5B19 5880 E860 37F4 9357 ECEF 11EA A6FA (new)
Good judgement comes with experience. Unfortunately, the experience
usually comes from bad judgement.
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
On Mon, Feb 20, 2023 at 09:44:41PM +1100, Darren Tucker wrote:
> $ cat test.c
> #include <stdlib.h>
> #include <stdio.h>
> int main(void)
> {
> FILE *f = fopen("testfile", "w");
> printf("fputc A=%d\n", fputc('A', f));

Is fputc() here somehow being evaluated twice? What happens
if you move the fputc() call outside the printf() call?
Or create it separately than this program.

> fclose(f);
>
> f = fopen("testfile", "a+b");
> printf("fseek=%d\n", fseek(f, -1L, SEEK_END));
> printf("c=%d\n", fgetc(f));
> printf("fputc B = %d\n", fputc('B', f));

My suggestion of course fails to explain why there's only one 'B'.
I would create the initial file separately from the test program.

I don't recall, but is there a guaranteed fflush() on open file
descriptors at program exit? Does an explicit call to fflush() do
anything?

> $ gcc test.c && ./a.out && od -x -c testfile
> fputc A=65
> fseek=0
> c=65
> fputc B = 66
> 0000000 4141 0042
> A A B
> 0000003

FWIW, this is the same output I get on both a linux and macos box.

--
nw
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
On Mon, 20 Feb 2023 at 21:57, Nathan Wagner <nw@hydaspes.if.org> wrote:
[...]
> > printf("fputc A=%d\n", fputc('A', f));
>
> Is fputc() here somehow being evaluated twice?

Nope. In the original code it's not inside a printf, and checking it
between the close and reopen confirms there's only one byte in it. I
moved the second call outside of the printf but it didn't make a
difference.

$ cat test.c
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int n;
FILE *f = fopen("testfile", "w");
printf("fputc A=%d\n", fputc('A', f));
fclose(f);
system("od -x -c testfile");

f = fopen("testfile", "r+b");
printf("fseek=%d\n", fseek(f, -1L, SEEK_END));
n = fgetc(f);
printf("c=%d\n", n);
printf("fputc B = %d\n", fputc('B', f));
}
$ gcc test.c && ./a.out && od -x -c testfile
fputc A=65
0000000 0041
A
0000001
fseek=0
c=65
fputc B = 66
0000000 4141 0042
A A B
0000003

> My suggestion of course fails to explain why there's only one 'B'.
> I would create the initial file separately from the test program.

The "od" call in the middle of the above example proves there's only
one byte after the first fclose.

> I don't recall, but is there a guaranteed fflush() on open file
> descriptors at program exit?

Yes I think so although I don't have a reference to hand. But even if
it didn't, that would explain a missing "B", not an extra "A".

> Does an explicit call to fflush() do anything?

At the end of the program, no, it misbehaves as described ("AAB").
Before the last fputc, yes, it makes the program behave as expected
("AB").

> > $ gcc test.c && ./a.out && od -x -c testfile
> > fputc A=65
> > fseek=0
> > c=65
> > fputc B = 66
> > 0000000 4141 0042
> > A A B
> > 0000003
>
> FWIW, this is the same output I get on both a linux and macos box.

You get the AAB on Linux and MacOS? Which versions? I get AB on both
OSX High Sierra and Fedora 36. Maybe it's sensitive to locale
settings?

--
Darren Tucker (dtucker at dtucker.net)
GPG key 11EAA6FA / A86E 3E07 5B19 5880 E860 37F4 9357 ECEF 11EA A6FA (new)
Good judgement comes with experience. Unfortunately, the experience
usually comes from bad judgement.
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
On Mon, Feb 20, 2023 at 10:21:31PM +1100, Darren Tucker wrote:

> > My suggestion of course fails to explain why there's only one 'B'.
> > I would create the initial file separately from the test program.
>
> The "od" call in the middle of the above example proves there's only
> one byte after the first fclose.

Ah, so it does. What happens
if you do everything except the last call at the end?
printf("fputc B = %d\n", fputc('B', f));

What happens if you don't read the last byte? I realize
you will need to for the original problem?

What I'm wondering is if somehow reading the byte puts it in
the output buffer.

What happens if you create an initial two byte file, say "AB", and try
to write a 'C'? If you read the B, I guess we expect 'ABBC' at this
point. If you read the 'A', do you get 'ABAC' or something else?

I wish I had a box I could reproduce the problem on (see below).

> You get the AAB on Linux and MacOS? Which versions? I get AB on both
> OSX High Sierra and Fedora 36. Maybe it's sensitive to locale
> settings?

I mis-quoted. I get the expected two byte 'AB' output on both.
Sorry for what must have been a surprising confusion.

--
nw
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
On Mon, 20 Feb 2023 at 22:39, Nathan Wagner <nw@hydaspes.if.org> wrote:
> On Mon, Feb 20, 2023 at 10:21:31PM +1100, Darren Tucker wrote:
[...]
> > The "od" call in the middle of the above example proves there's only
> > one byte after the first fclose.
>
> Ah, so it does.

I added that to answer the question, it wasn't in the original.

> What happens if you do everything except the last call at the end?
> printf("fputc B = %d\n", fputc('B', f));

Omitting the final fputc leaves the file as it was after the first fclose ("A").

> What happen if you don't read the last byte? I realize
> you will need to for the original problem?
>
> What I'm wondering is if somehow reading the byte puts it in
> the output buffer.
>
> What happens if you create an initial two byte file, say "AB", and try
> to write a 'C'? If you read the B, I guess we expect 'ABBC' at this
> point.

Yep that's what we get:

$ cat test.c
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int n;
FILE *f = fopen("testfile", "w");
printf("fputc A=%d\n", fputc('A', f));
printf("fputc B=%d\n", fputc('B', f));
fclose(f);
system("od -x -c testfile");

f = fopen("testfile", "r+b");
printf("fseek=%d\n", fseek(f, -1L, SEEK_END));
n = fgetc(f);
printf("c=%d\n", n);
printf("fputc C = %d\n", fputc('C', f));
}
$ gcc test.c && ./a.out && od -x -c testfile
fputc A=65
fputc B=66
0000000 4241
A B
0000002
fseek=0
c=66
fputc C = 67
0000000 4241 4342
A B B C
0000004

> If you read the 'A', do you get 'ABAC' or something else?

Yep. change the fseek to -2 and you get "ABAC".

> I wish I had a box I could reproduce the problem on (see below).

If you're willing to install OpenIndiana (an OpenSolaris fork) it
reproduces on that:

$ uname -a
SunOS openindiana 5.11 illumos-a5a9a6bb70 i86pc i386 i86pc

--
Darren Tucker (dtucker at dtucker.net)
GPG key 11EAA6FA / A86E 3E07 5B19 5880 E860 37F4 9357 ECEF 11EA A6FA (new)
Good judgement comes with experience. Unfortunately, the experience
usually comes from bad judgement.
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
Hi,

On Fri, Feb 17, 2023 at 07:07:12PM +1100, Darren Tucker wrote:
> 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:

Having read the thread, the only explanation I can come up is that
mixing fgetc()/fputc() will somehow confuse the stdio buffering.

Googling suggests that this might be a "SysV" thing (Wikipedia lists
"four System V variants: IBM's AIX, ... HP-UX, and Oracle's Solaris,
plus ... illumos").

Of course this made me curious so I ran your (latest) test program
on an ancient SCO OpenServer 5 system I have access to, which is SysVR3
with some R4 bits. No gcc there, but "stdio surprises" are more a libc
thing anyway.

And lo and behold...

bigbox:/usr/gert$ cc fgetc-test.c
bigbox:/usr/gert$ ./a.out
fputc A=65
0000000 0041
A
0000001
fseek=0
c=65
fputc B = 66
bigbox:/usr/gert$ od -x -c testfile
0000000 4141 0042
A A B
0000003


Adding a fflush(f) call after the fgetc(f) will fix the problem
the same way as you reported for Solaris/AIX.

bigbox:/usr/gert$ od -x -c testfile
0000000 4241
A B
0000002


... most interesting.

gert

--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress

Gert Doering - Munich, Germany gert@greenie.muc.de
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
Darren Tucker wrote in
<CALDDTe39k4UFJWBvts5HWbbhHO+Vw9OAP0zBhu-Hje-2aR9+xA@mail.gmail.com>:

Jörg Schilling convinced me in 2017

Readd removed fflush()/fseek() in between read and write..

The behave:record_a_resend-2 test failed on Solaris 5.10 and 5.11,
messages in *record* where separated by four \n / U+000A instead
of two. It turns out that the effective sequence

if((fo = Zopen(name, "a+")) == NULL){
if((fo = Zopen(name, "wx")) == NULL){

...

if(fseek(fo, -2L, SEEK_END) == 0){
switch(fread(buf, sizeof *buf, 2, fo)){

...
if(emptyline){
putc('\n', fo);
fflush(fo);
}

on a O_APPEND|O_RDWR file requires an intermediate

fseek(fout, 0, SEEK_END))
or
fflush()

which i consider a bug in Solaris stdio.
Jörg Schilling however convinced me that the standard requires
this behaviour, and it is also in the C FAQ. Nonetheless...

--steffen
|
|Der Kragenbaer, The moon bear,
|der holt sich munter he cheerfully and one by one
|einen nach dem anderen runter wa.ks himself off
|(By Robert Gernhardt)
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
Hi,

On Mon, Feb 20, 2023 at 06:29:22PM +0100, Gert Doering wrote:
> Having read the thread, the only explanation I can come up is that
> mixing fgetc()/fputc() will somehow confuse the stdio buffering.

Job nerd-sniped me into reading OpenIllumos source code, and from
my reading of

https://github.com/illumos/illumos-gate/blob/86d949f9497332fe19be6b5d711d265eb957439f/usr/src/lib/libc/port/stdio/getc.c
https://github.com/illumos/illumos-gate/blob/86d949f9497332fe19be6b5d711d265eb957439f/usr/src/lib/libc/port/stdio/_filbuf.c

the sequence

fseek(-1)
fgetc()

will leave "iop->_cnt" at 0 ("one byte read from file, one byte returned,
0 byte left in buffer"), and then

https://github.com/illumos/illumos-gate/blob/86d949f9497332fe19be6b5d711d265eb957439f/usr/src/lib/libc/port/stdio/putc.c

putc_unlocked() will add the new byte to the buffer

return (*iop->_ptr++ = (unsigned char)ch);

... but nothing actually *resets* the buffer in between.


At "now it's time to write the buffer out" time

https://github.com/illumos/illumos-gate/blob/86d949f9497332fe19be6b5d711d265eb957439f/usr/src/lib/libc/port/stdio/flush.c

fflush() -> _fflush_u() -> _xflsbuf() will do "n = iop->_ptr - iop->_base"
(which is now *2*) and will write out 2 bytes.


Having the fflush() in between seems to reset "iop->_ptr = iop->_base",
so the next fflush() after fputc() only has n = 1.


I haven't actually written a test program that prints out the various
states of iop->_ptr, _base, etc. to verify that assumption.

gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress

Gert Doering - Munich, Germany gert@greenie.muc.de
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
Hi,

On Mon, Feb 20, 2023 at 08:00:24PM +0100, Steffen Nurpmeso wrote:
> J?rg Schilling however convinced me that the standard requires
> this behaviour, and it is also in the C FAQ. Nonetheless...

Do you have references for this?

Curious...

(Especially since "if the standard requires this" all the BSDs would
be in error here)

gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress

Gert Doering - Munich, Germany gert@greenie.muc.de
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
Gert Doering wrote in
<Y/PHnwAkzq29I9G4@greenie.muc.de>:
|Hi,
|
|On Mon, Feb 20, 2023 at 08:00:24PM +0100, Steffen Nurpmeso wrote:
|> J?rg Schilling however convinced me that the standard requires
|> this behaviour, and it is also in the C FAQ. Nonetheless...
|
|Do you have references for this?

Hm, mail in between me and J?rg from 2017-05-02.

| |Davor kann ich nur warnen. Das stdio in Solaris geht auf die
| |Originalimplementierung aus der Zeit um 1976 zur?ck. Es ist also 40 \
| |Jahre alter
| |und auch st?ndig verifizierter Code.
|
|Oh, mein C++ ist auch ziemlich gut.
|
| |>|Au?erdem: Nach einem read() mu? man fflush() aufrufen bevor man schreiben \
| |>|\
| |>|darf
| |>|und umgekehrt auch.
| |>
| |> Wo steht das? Wirklich? Ich habe stdio nie benutzt, bis ich nail
| |
| |Das steht leider nicht im POSIX standard (das halte ich f?r einen Bug) \
| |aber es
| |ist im C-Standard.....
|
|Du hast mich ?berzeugt, in der C FAQ steht es auch.

Maybe i was a bit over-forgiving as the C FAQ how i have it
locally says

12.30: I'm trying to update a file in place, by using fopen mode "r+",
reading a certain string, and writing back a modified string,
but it's not working.

A: Be sure to call fseek before you write, both to seek back to the
beginning of the string you're trying to overwrite, and because
an fseek or fflush is always required between reading and
writing in the read/write "+" modes. Also, remember that you

From: scs@eskimo.com (Steve Summit)
Newsgroups: comp.lang.c,comp.lang.c.moderated,comp.answers,news.answers
Subject: comp.lang.c Answers to Frequently Asked Questions (FAQ List)
Date: 3 Jul 2004 23:27:33 GMT
Organization: better late than never
Message-ID: <2004Jul03.1927.scs.0003@eskimo.com>

...
| |> Die Test-Suite scheint mir wirklich l?chrig zu sein.
| |> Sag, ist das ein Fehler, oder nicht? Und wenn ja, was mach ich
| |> damit? Br?uchte das gar eine Klarstellung im Standard?
| |
| |Wie gesagt: im C-Standard steht, da? zwischen dem ?ndern der I/O Richtung
| |fflush() zu rufen ist.
...
| |In der Funktion, die Du erw?hnst ist es aber falsch, denn da wird eben \
| |nicht
| |immer zwischen einem read und einem write fflush() gerufen.
| |Zwischen einem write und einem read mu? es nat?rlich auch geschehen.J?rg

I personally stand to my opinion quoted from my commit message
"which i consider a bug in Solaris stdio", but i was talking to
J?rg, and doing it the way he said fixed the problem. (I have no
idea of stdio and would only fflush after writing, say.)
Anyhow it was no later but 2021-11-11 that i once again had to fix
something in J?rg's spirit

commit 7b8ac94bf7cc6a97cc2c6d799cab07218e693377
Author: Steffen Nurpmeso <steffen@sdaoden.eu>
AuthorDate: 2021-11-11 21:27:04 +0100
...
n_folder_mbox_prepare_append(): fix it on SunOS (J?rg Schilling, RIP!)
...
- if((needsep && putc('\n', fout) == EOF) || fflush(fout) == EOF)
+ if((needsep && (fseek(fout, 0L, SEEK_END) || putc('\n', fout) == EOF)) ||
+ fflush(fout) == EOF)
...

--steffen
|
|Der Kragenbaer, The moon bear,
|der holt sich munter he cheerfully and one by one
|einen nach dem anderen runter wa.ks himself off
|(By Robert Gernhardt)
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
I get a variety of hits searching for "Be sure to call fseek before
you write", especially here: https://c-faq.com/stdio/fupdate.html ,
which might be drawn from the book referenced on the c-faq.com index
page, "C Programming FAQs: Frequently Asked Questions"
(Addison-Wesley, 1995, ISBN 0-201-84519-9).

The link mentions "ISO Sec. 7.9.5.3" presumably refers to the ISO/IEC
9899:1990 version of the C standard, which is the section, "The fopen
function". I think this section is the most pertinent:

When a file is opened with update mode (‘+’ as the second or third
character in the above list of mode argument values), both input and
output may be performed on the associated stream. However, output may
not be directly followed by input without an intervening call to the
flush function or to a file positioning function (fseek. fsetpos. or
rewind), and input may not be directly followed by output without an
intervening call to a file positioning function. unless the input
operation encounters end-of-file. Opening (or creating) a text file
with update mode may instead open (or create) a binary stream in some
implementations.

(The above excerpt may contain typos courtesy of OCR. I fixed the ones
I noticed.)


On Mon, Feb 20, 2023 at 2:53 PM Steffen Nurpmeso <steffen@sdaoden.eu> wrote:
>
> Gert Doering wrote in
> <Y/PHnwAkzq29I9G4@greenie.muc.de>:
> |Hi,
> |
> |On Mon, Feb 20, 2023 at 08:00:24PM +0100, Steffen Nurpmeso wrote:
> |> Jörg Schilling however convinced me that the standard requires
> |> this behaviour, and it is also in the C FAQ. Nonetheless...
> |
> |Do you have references for this?
>
> Hm, mail in between me and Jörg from 2017-05-02.
>
> | |Davor kann ich nur warnen. Das stdio in Solaris geht auf die
> | |Originalimplementierung aus der Zeit um 1976 zurück. Es ist also 40 \
> | |Jahre alter
> | |und auch ständig verifizierter Code.
> |
> |Oh, mein C++ ist auch ziemlich gut.
> |
> | |>|Außerdem: Nach einem read() muß man fflush() aufrufen bevor man schreiben \
> | |>|\
> | |>|darf
> | |>|und umgekehrt auch.
> | |>
> | |> Wo steht das? Wirklich? Ich habe stdio nie benutzt, bis ich nail
> | |
> | |Das steht leider nicht im POSIX standard (das halte ich für einen Bug) \
> | |aber es
> | |ist im C-Standard.....
> |
> |Du hast mich überzeugt, in der C FAQ steht es auch.
>
> Maybe i was a bit over-forgiving as the C FAQ how i have it
> locally says
>
> 12.30: I'm trying to update a file in place, by using fopen mode "r+",
> reading a certain string, and writing back a modified string,
> but it's not working.
>
> A: Be sure to call fseek before you write, both to seek back to the
> beginning of the string you're trying to overwrite, and because
> an fseek or fflush is always required between reading and
> writing in the read/write "+" modes. Also, remember that you
>
> From: scs@eskimo.com (Steve Summit)
> Newsgroups: comp.lang.c,comp.lang.c.moderated,comp.answers,news.answers
> Subject: comp.lang.c Answers to Frequently Asked Questions (FAQ List)
> Date: 3 Jul 2004 23:27:33 GMT
> Organization: better late than never
> Message-ID: <2004Jul03.1927.scs.0003@eskimo.com>
>
> ...
> | |> Die Test-Suite scheint mir wirklich löchrig zu sein.
> | |> Sag, ist das ein Fehler, oder nicht? Und wenn ja, was mach ich
> | |> damit? Bräuchte das gar eine Klarstellung im Standard?
> | |
> | |Wie gesagt: im C-Standard steht, daß zwischen dem Ändern der I/O Richtung
> | |fflush() zu rufen ist.
> ...
> | |In der Funktion, die Du erwähnst ist es aber falsch, denn da wird eben \
> | |nicht
> | |immer zwischen einem read und einem write fflush() gerufen.
> | |Zwischen einem write und einem read muß es natürlich auch geschehen.Jörg
>
> I personally stand to my opinion quoted from my commit message
> "which i consider a bug in Solaris stdio", but i was talking to
> Jörg, and doing it the way he said fixed the problem. (I have no
> idea of stdio and would only fflush after writing, say.)
> Anyhow it was no later but 2021-11-11 that i once again had to fix
> something in Jörg's spirit
>
> commit 7b8ac94bf7cc6a97cc2c6d799cab07218e693377
> Author: Steffen Nurpmeso <steffen@sdaoden.eu>
> AuthorDate: 2021-11-11 21:27:04 +0100
> ...
> n_folder_mbox_prepare_append(): fix it on SunOS (Jörg Schilling, RIP!)
> ...
> - if((needsep && putc('\n', fout) == EOF) || fflush(fout) == EOF)
> + if((needsep && (fseek(fout, 0L, SEEK_END) || putc('\n', fout) == EOF)) ||
> + fflush(fout) == EOF)
> ...
>
> --steffen
> |
> |Der Kragenbaer, The moon bear,
> |der holt sich munter he cheerfully and one by one
> |einen nach dem anderen runter wa.ks himself off
> |(By Robert Gernhardt)
> _______________________________________________
> openssh-unix-dev mailing list
> openssh-unix-dev@mindrot.org
> https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: fseek/fgetc puzzle [ In reply to ]
On Mon, 20 Feb 2023, Mitchell Dorrell wrote:

>The link mentions "ISO Sec. 7.9.5.3" presumably refers to the ISO/IEC
>9899:1990 version of the C standard, which is the section, "The fopen

Still present in C99 and the mid-January C23 draft.
N?n-OCR quote from the latter (§7.23.5.3(7) fopen):

| When a file is opened with update mode (’+’ as the second or third
| character in the above list of mode argument values), both input and
| output may be performed on the associated stream. However, output
| shall not be directly followed by input without an intervening call to
| the fflush function or to a file positioning function (fseek, fsetpos,
| or rewind), and input shall not be directly followed by output without
| an intervening call to a file positioning function, unless the input
| operation encounters end-of-file. Opening (or creating) a text file
| with update mode may instead open (or create) a binary stream in some
| implementations.

This sadly rules out fflush() for this case, even if it works here.

bye,
//mirabilos
--
Infrastrukturexperte • tarent solutions GmbH
Am Dickobskreuz 10, D-53121 Bonn • http://www.tarent.de/
Telephon +49 228 54881-393 • Fax: +49 228 54881-235
HRB AG Bonn 5168 • USt-ID (VAT): DE122264941
Geschäftsführer: Dr. Stefan Barth, Kai Ebenrett, Boris Esser, Alexander Steeg

****************************************************
/?\ The UTF-8 Ribbon
? ? Campaign against Mit dem tarent-Newsletter nichts mehr verpassen:
 ?  HTML eMail! Also, https://www.tarent.de/newsletter
? ? header encryption!
****************************************************
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev