Problems using fread() on stdin under win32
Asked Answered
G

2

3

I'm trying to parse data from stdin in binary mode under Win32. The first thing my code does is to check for a 4byte header at the beginning:

int riff_header;
fread(&riff_header, sizeof(riff_header), 1, ifp);
//  'RIFF' = little-endian
if (riff_header != 0x46464952) {
    fprintf(stderr, "wav2msu: Incorrect header: Invalid format or endianness\n");
    fprintf(stderr, "         Value was: 0x%x\n", riff_header);
    return -1;
}

stdin has been switched to binary mode before reading from it:

if (*argv[argc-1] == '-') {
    fprintf(stderr, "Reading from stdin.\n");
    infile = stdin;
    // We need to switch stdin to binary mode, or else we run
    // into problems under Windows
    freopen(NULL, "rb", stdin);
}

This code works fine under Linux, however on Win32 (specifically Windows XP), the fread only seems to read a single byte and thus cause the evaluation to fail. Example:

> ffmeg.exe -i ..\test.mp3 -f wav pipe:1 2> nul |..\foo.exe -o test.bin -
Reading from stdin.
foo: Incorrect header: Invalid format or endianness
     Value was: 0x4

What am I doing wrong?

Gravitt answered 13/8, 2011 at 19:54 Comment(4)
What happens if you don't do the freopen?Foraminifer
The header checks fine, but I get nonsensical results with other checks I run later, due to windows handling newlines differently :-/Gravitt
Ok. I just wanted to double-check that this problem is specific to reopened stdin, not just Windows in general.Foraminifer
Would it be an option to filter the input and translate the \r\n sequences to \n?Krummhorn
K
5

At http://pubs.opengroup.org/onlinepubs/009695399/functions/freopen.html I have found the following:

If filename is a null pointer, the freopen() function shall attempt to change the mode of the stream to that specified by mode, as if the name of the file currently associated with the stream had been used. In this case, the file descriptor associated with the stream need not be closed if the call to freopen() succeeds. It is implementation-defined which changes of mode are permitted (if any), and under what circumstances.

Maybe you should check if the change of mode (from text to binary) is allowed by the compiler and libraries you are using. Which compiler are you using?

Update / summary

Using MinGW you can call setmode() to switch the mode of the stdin stream. You should set the mode to _O_BINARY, which is defined in fcntl.h. For more information see e.g. http://gnuwin32.sourceforge.net/compile.html

Krummhorn answered 13/8, 2011 at 20:14 Comment(1)
I'm using GCC 4.5.2 under MinGW.Gravitt
C
6

According to the MSDN documentation, it's not permitted to pass NULL for the path parameter of freopen, so the call to freopen is almost certainly failing; have you checked the return value and the value of errno? C89 does not specify the behavior of freopen when path is NULL; C99 does, but the Microsoft C runtime is not (and does not claim to be) C99-compliant.

If you really need to read binary info from stdin, you might have to use platform-specific code and read the raw binary data directly with ReadFile on the file GetStdHandle(STD_INPUT_HANDLE).

Cyd answered 13/8, 2011 at 20:21 Comment(7)
Or setmode(fileno(stdin), O_BINARY); MSDNPlatus
@user786653: Ah, good call, I was not aware of that function. You should add that as an answer.Cyd
setmode() should also work with MinGW. See gnuwin32.sourceforge.net/compile.html, section "Text files and binary files".Krummhorn
@user786653: When I try that, the compiler complains that it cannot find the declaration of O_BINARY. I included io.h, though :-/Gravitt
Under MinGW it should be #include <fcntl.h>, and the constant is _O_BINARY (see the link above).Krummhorn
@Giorgio, thanks for this link, it mentioned fcntl.h and including that did the trick. If you post it as an answer, I'll mark it :-)Gravitt
Thanks. I could add it to my answer, but setmode() was user786653's suggestion. I will wait in case he wants to add it as an answer. It's his call. If he doesn't, I will add it to my answer.Krummhorn
K
5

At http://pubs.opengroup.org/onlinepubs/009695399/functions/freopen.html I have found the following:

If filename is a null pointer, the freopen() function shall attempt to change the mode of the stream to that specified by mode, as if the name of the file currently associated with the stream had been used. In this case, the file descriptor associated with the stream need not be closed if the call to freopen() succeeds. It is implementation-defined which changes of mode are permitted (if any), and under what circumstances.

Maybe you should check if the change of mode (from text to binary) is allowed by the compiler and libraries you are using. Which compiler are you using?

Update / summary

Using MinGW you can call setmode() to switch the mode of the stdin stream. You should set the mode to _O_BINARY, which is defined in fcntl.h. For more information see e.g. http://gnuwin32.sourceforge.net/compile.html

Krummhorn answered 13/8, 2011 at 20:14 Comment(1)
I'm using GCC 4.5.2 under MinGW.Gravitt

© 2022 - 2024 — McMap. All rights reserved.