TL;DR: Why does freopen(NULL, "rb", stdin)
always fail on Windows?
I'm trying to re-implement a base64
encoder in C that takes input from stdin and outputs the encoded equivalent into stdout. I had a problem in my previous post where fread
was signalling EOF prematurely. Here is what my main method looks like:
int main(void)
{
unsigned char buffer[BUFFER_SIZE];
unsigned char base64_buffer[BASE64_BUFFER];
while (1)
{
TRACE_PUTS("Reading in data from stdin...");
size_t read = fread(buffer, 1, sizeof(buffer), stdin); /* Read the data in using fread(3) */
/* Process the buffer */
TRACE_PRINTF("Amount read: %zu\n", read);
TRACE_PUTS("Beginning base64 encode of buffer");
size_t encoded = base64_encode(buffer, read, base64_buffer, sizeof(base64_buffer));
/* Write the data to stdout */
TRACE_PUTS("Writing data to standard output");
...
if (read < sizeof(buffer))
{
break; /* We reached EOF or had an error during the read */
}
}
if (ferror(stdin))
{
/* Handle errors */
fprintf(stderr, "%s\n", "There was a problem reading from the file.");
exit(1);
}
puts(""); /* Output a newline before finishing */
return 0;
}
Essentially, it reads in data from stdin using fread
, encodes to base64, writes it to stdout, then checks if EOF has been reached at the end of the loop.
When I piped the contents of a binary file to this app's stdin, it would only read a fraction of the total bytes in the file. For example:
$ cat /bin/echo | my_base64_program >/dev/null # only view the trace output
TRACE: C:/Users/James/Code/c/base64/main.c:23: Reading in data from stdin...
TRACE: C:/Users/James/Code/c/base64/main.c:28: Amount read: 600
TRACE: C:/Users/James/Code/c/base64/main.c:29: Beginning base64 encode of buffer
TRACE: C:/Users/James/Code/c/base64/main.c:43: Writing data to standard output
TRACE: C:/Users/James/Code/c/base64/main.c:23: Reading in data from stdin...
TRACE: C:/Users/James/Code/c/base64/main.c:28: Amount read: 600
TRACE: C:/Users/James/Code/c/base64/main.c:29: Beginning base64 encode of buffer
TRACE: C:/Users/James/Code/c/base64/main.c:43: Writing data to standard output
TRACE: C:/Users/James/Code/c/base64/main.c:23: Reading in data from stdin...
TRACE: C:/Users/James/Code/c/base64/main.c:28: Amount read: 600
TRACE: C:/Users/James/Code/c/base64/main.c:29: Beginning base64 encode of buffer
TRACE: C:/Users/James/Code/c/base64/main.c:43: Writing data to standard output
TRACE: C:/Users/James/Code/c/base64/main.c:23: Reading in data from stdin...
TRACE: C:/Users/James/Code/c/base64/main.c:28: Amount read: 569
TRACE: C:/Users/James/Code/c/base64/main.c:29: Beginning base64 encode of buffer
TRACE: C:/Users/James/Code/c/base64/main.c:43: Writing data to standard output
$ cat /bin/echo | wc -c
28352
As you can see /bin/echo
is 28352 bytes long, but only ~2400 of them are being processed. I believe the reason is because stdin is not being considered a binary file, so certain control characters (like Control-Z as mentioned in the linked post's answer) were prematurely signalling EOF.
I took a look at the base64 source code and it looks like they're using xfreopen
(which is just a wrapper for freopen
) to tell fread
to interpret stdin as binary. So I went ahead and did that before the while-loop:
if (!freopen(NULL, "rb", stdin))
{
fprintf(stderr, "freopen failed. error: %s\n", strerror(errno));
exit(1);
}
However, now my app always exits at that point with:
$ cat /bin/echo | my_base64_program
freopen failed. error: Invalid argument
So why is freopen
at that point failing, when it works for base64
? I'm using MinGW-w64 with GCC on Windows if that's relevant.
freopen(NULL, ...)
does not work on Windows). – Kristankriste