Gracias a @JdeBP por la pista, pude crear un pequeño caso de prueba que hace lo mismo 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;
}
}
Cuando se ejecuta en un sistema basado en glibc (escritorio típico de Linux).
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
Cuando se ejecuta en 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
Basado en mi interpretación inexperta del estándar, esto parece un error en glibc (la biblioteca GNU C).
Acerca de fread
:
Para cada objeto, se realizarán llamadas de tamaño a la función fgetc() y los resultados se almacenarán, en el orden de lectura, en una matriz de caracteres sin firmar superpuestos exactamente al objeto.
Acerca de fgetc
:
Si el indicador de fin de archivo para el flujo de entrada al que apunta bystream no está configurado y un siguiente byte está presente, la función fgetc() obtendrá el siguiente byte
Parece que glibc intentará "obtener el siguiente byte" incluso si el indicador eof está activado.
De hecho, realmente lo es un error en la biblioteca GNU C, no presente en las bibliotecas BSD o musl C. Se supo en 2005. Ulrich Drepper cerró el informe de error sin corregir el error en 2007. Se discutió en 2012, donde se señaló que otras bibliotecas C no tenían ni tienen este comportamiento, que el estándar C de 1999 es bastante específico al respecto, y que Solaris incluso tiene un mecanismo especial para esto que se invoca cuando c99
se usa como compilador en lugar de cc
.
Finalmente se solucionó en 2018. La solución se encuentra en la versión 2.28 de la biblioteca GNU C. La versión "estable" actual de Debian, la versión 9, está en la versión 2.24 de la biblioteca GNU C, y este error continúa manifestándose, 14 años después de haber sido informado.
Como se señaló en las discusiones de la biblioteca GNU C, existe la posibilidad de software que se escribieron para requerir las peculiaridades de la biblioteca GNU C sin tener en cuenta otras bibliotecas C como musl o el comportamiento en otras plataformas. Sin embargo, en las discusiones antes mencionadas a lo largo de los años no se identificó tal programa. Mientras que varios programas que están rotos por la antigua biblioteca GNU C, para requerir que los usuarios señalen EOF dos veces seguidas, tienen sido identificado; incluyendo entre otros hexdump
aquí y patch
en StackOverflow en 2018.