Grâce à @JdeBP pour l'indice, j'ai pu créer un petit cas de test qui fait la même chose que hexdump
:
#include <stdio.h>
int main(void){
char buf[64]; size_t r;
for(;;){
printf("eof=%d, error=%d\n", feof(stdin), ferror(stdin));
r = fread(buf, 1, sizeof buf, stdin);
printf("read %zd bytes, eof=%d, error=%d\n",
r, feof(stdin), ferror(stdin));
if(!r) return 0;
}
}
Lorsqu'il est exécuté sur un système basé sur glibc (bureau Linux typique).
prompt$ ./fread-test
eof=0, error=0
<control-D>
read 0 bytes, eof=1, error=0
prompt$ ./fread-test
eof=0, error=0
hello
<control-D>
read 6 bytes, eof=1, error=0
eof=1, error=0
<control-D>
read 0 bytes, eof=1, error=0
Lorsqu'il est exécuté sur bsd, solaris, busybox (uclibc), android, etc :
prompt$ ./fread-test
eof=0, error=0
hello
<control-D>
read 6 bytes, eof=1, error=0
eof=1, error=0
read 0 bytes, eof=1, error=0
D'après mon interprétation inexperte de la norme, cela ressemble à un bogue dans glibc (la bibliothèque GNU C).
À propos de fread
:
Pour chaque objet, des appels de taille doivent être effectués à la fonction fgetc() et les résultats stockés, dans l'ordre lu, dans un tableau de caractères non signés recouvrant exactement l'objet.
À propos de fgetc
:
Si l'indicateur de fin de fichier pour le flux d'entrée pointé vers bystream n'est pas défini et qu'un octet suivant est présent, la fonction fgetc() obtiendra l'octet suivant
Il semble que la glibc essaiera "d'obtenir l'octet suivant" même si l'indicateur eof est activé.
En effet, c'est en fait un bogue dans la bibliothèque GNU C, non présent dans les bibliothèques BSD ou musl C. Il était connu en 2005. Ulrich Drepper a fermé le rapport de bogue sans corriger le bogue en 2007. Il a été discuté en 2012, où il a été noté que d'autres bibliothèques C n'avaient pas et n'ont pas ce comportement, que la norme C de 1999 est assez spécifique à ce sujet, et que Solaris a même un mécanisme spécial pour cela qui est invoqué lorsque c99
est utilisé comme compilateur au lieu de cc
.
Il a finalement été corrigé en 2018. Le correctif se trouve dans la version 2.28 de la bibliothèque GNU C. La version "stable" actuelle de Debian, la version 9, est sur la version 2.24 de la bibliothèque GNU C, et ce bug continue donc de se manifester, 14 ans après avoir été signalé.
Comme indiqué dans les discussions sur la bibliothèque GNU C, il y a la possibilité de logiciels qui ont été écrits pour exiger les bizarreries de la bibliothèque GNU C sans tenir compte des autres bibliothèques C telles que musl ou du comportement sur d'autres plates-formes. Cependant, dans les discussions susmentionnées au fil des ans, aucun programme de ce type n'a été identifié. Alors que plusieurs programmes qui sont cassés par l'ancienne bibliothèque GNU C, pour obliger les utilisateurs à signaler EOF deux fois de suite, ont été identifié ; dont entre autres hexdump
ici et patch
sur StackOverflow en 2018.