Path: ...!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Re: Buffer contents well-defined after fgets() reaches EOF ? Date: Mon, 17 Feb 2025 21:11:29 -0800 Organization: A noiseless patient Spider Lines: 90 Message-ID: <86zfijygzi.fsf@linuxsc.com> References: <87msesnnwa.fsf@nosuchdomain.example.com> <87bjv8nj8g.fsf@nosuchdomain.example.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Date: Tue, 18 Feb 2025 06:11:30 +0100 (CET) Injection-Info: dont-email.me; posting-host="9d1d7b6626b34534d8c8589d87cd0ec0"; logging-data="1657108"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18xWX74iHnrtigETLcxwGRwKygIGMFoWrQ=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:nVk3Qc/XxzrjNcW0s5AkTn3nPBw= sha1:SZz6SHuv6t2jLfzYu5cSBYGYMGQ= Bytes: 4078 Keith Thompson writes: > James Kuyper writes: > >> On 2/11/25 16:59, Keith Thompson wrote: >> >>> James Kuyper writes: >> >> ... >> >>>> I just tried it, using gcc and found that fgets() does set the >>>> first byte of the buffer to a null character. Therefore, it >>>> doesn't conform to the requirements of the standard. That's not >>>> particularly surprising - calling fgets with useless arguments >>>> isn't something that I'd expect to be a high priority on their >>>> pre-delivery tests. >>> >>> As you know, gcc doesn't implement fgets(). Were you using GNU lib >> >> . >> Yes. To be specific, Ubuntu GLIBC 2.35-0ubuntu3.9. >> >> Here's my test code: >> >> #include >> #include >> int main(int argc, char *argv[]) >> { >> char fill = 1; >> char buffer = fill; >> char *retval = NULL; >> FILE *infile; >> if(argc < 2) >> infile = stdin; >> else{ >> infile = fopen(argv[1], "r"); >> if(!infile) >> { >> perror(argv[1]); >> return EXIT_FAILURE; >> } >> } >> >> while((retval = fgets(&buffer, 1, infile)) == &buffer) >> { >> printf("%ld:'%u'\n", ftell(infile), (unsigned)buffer); >> buffer = fill++; >> } >> if(ferror(infile)) >> perror("fgets"); >> >> printf("%p!=%p ferror:%d feof:%d '%c'\n", >> (void*)&buffer, (void*)retval, >> ferror(infile), feof(infile), buffer); >> } >> >> Note that if fgets() works as it should, that's an infinite loop, >> since no data is read in, and therefore there's no movement through >> the input file. I wrote code that executes after the infinite loop >> just to cover the possibility that it doesn't work that way. > > I get an infinite loop with both glibc and musl on Ubuntu, and under > Termux on Android (Bionic library implementation): > > $ ./jk < /dev/null | head -n 3 > 0:'0' > 0:'0' > 0:'0' > $ echo hello | ./jk | head -n 3 > -1:'0' > -1:'0' > -1:'0' > $ > > With newlib on Cygwin, there is no infinite loop: > > $ ./jk.exe < /dev/null > 0x7ffffcc17!=0x0 ferror:0 feof:0 '' > $ echo hello | ./jk.exe > 0x7ffffcc17!=0x0 ferror:0 feof:0 '' > $ I have an amusing footnote to these trials. I wrote a short program to test fgets() under varying length arguments. Compiling with gcc on Ubuntu, I was surprised to discover the behavior of fgets() with a length argument of 1 depended on the the optimization setting of the compiler - using -O0 gave a different result than -O1. Compiling with clang gave the same result under both optimization settings.