[m-rev.] for review: binary standard input/output streams on Windows

Peter Wang novalazy at gmail.com
Fri Apr 20 11:33:48 AEST 2012


Branches: main, 11.07

The binary input and output streams were not necessarily put into binary
translation mode when using MinGW, and probably MSVC.

library/io.m:
	Fix the problem by using _setmode() to change the translation
	mode after the file is opened.

tests/hard_coded/Mmakefile:
tests/hard_coded/binary_stdin.exp:
tests/hard_coded/binary_stdin.inp:
tests/hard_coded/binary_stdin.m:
tests/hard_coded/binary_stdout.exp:
tests/hard_coded/binary_stdout.m:
	Add test cases.

diff --git a/library/io.m b/library/io.m
index 9a13c75..5d12eb3 100644
--- a/library/io.m
+++ b/library/io.m
@@ -6300,6 +6300,22 @@ MR_Unsigned mercury_current_text_output_index;
 MR_Unsigned mercury_current_binary_input_index;
 MR_Unsigned mercury_current_binary_output_index;
 
+static void
+mercury_set_binary_mode(FILE *f)
+{
+#if defined(MR_MSVC) || defined(MR_MINGW)
+    /*
+    ** Calling fdopen with 'b' in the mode string does not necessarily put the
+    ** file into binary translation mode on Windows. This is the case with
+    ** MinGW and, reportedly, MSVC. The cause is likely the MSVC CRT.
+    ** The solution is to change the mode on the file after opening.
+    */
+    _setmode(_fileno(f), _O_BINARY);
+#else
+    (void)f;
+#endif
+}
+
 void
 mercury_init_io(void)
 {
@@ -6318,7 +6334,9 @@ mercury_init_io(void)
 #if defined(MR_HAVE_FDOPEN) && (defined(MR_HAVE_FILENO) || defined(fileno)) && \
         defined(MR_HAVE_DUP)
     MR_file(mercury_stdin_binary) = fdopen(dup(fileno(stdin)), ""rb"");
-    if (MR_file(mercury_stdin_binary) == NULL) {
+    if (MR_file(mercury_stdin_binary) != NULL) {
+        mercury_set_binary_mode(MR_file(mercury_stdin_binary));
+    } else {
         /*
         ** The call to fdopen() may fail if stdin is not available.
         ** We don't abort since we still want Mercury programs to be runnable
@@ -6335,7 +6353,9 @@ mercury_init_io(void)
     }
 
     MR_file(mercury_stdout_binary) = fdopen(dup(fileno(stdout)), ""wb"");
-    if (MR_file(mercury_stdout_binary) == NULL) {
+    if (MR_file(mercury_stdout_binary) != NULL) {
+        mercury_set_binary_mode(MR_file(mercury_stdout_binary));
+    } else {
         MR_file(mercury_stdout_binary) = stdout;
     }
 #else
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 01e08f1..46c610a 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -14,6 +14,8 @@ ORDINARY_PROGS=	\
 	backquoted_qualified_ops \
 	bag_various \
 	bidirectional \
+	binary_stdin \
+	binary_stdout \
 	boyer \
 	brace \
 	bug103 \
diff --git a/tests/hard_coded/binary_stdin.exp b/tests/hard_coded/binary_stdin.exp
new file mode 100644
index 0000000..70ff8e5
--- /dev/null
+++ b/tests/hard_coded/binary_stdin.exp
@@ -0,0 +1 @@
+done.
diff --git a/tests/hard_coded/binary_stdin.inp b/tests/hard_coded/binary_stdin.inp
new file mode 100644
index 0000000..553a99f
Binary files /dev/null and b/tests/hard_coded/binary_stdin.inp differ
diff --git a/tests/hard_coded/binary_stdin.m b/tests/hard_coded/binary_stdin.m
new file mode 100644
index 0000000..de5fac8
--- /dev/null
+++ b/tests/hard_coded/binary_stdin.m
@@ -0,0 +1,98 @@
+%-----------------------------------------------------------------------------%
+
+:- module binary_stdin.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module int.
+
+%-----------------------------------------------------------------------------%
+
+main(!IO) :-
+    int.fold_up(read_byte_test, 0, 255, !IO),
+    io.stdin_binary_stream(Stream, !IO),
+    int.fold_up(read_byte_test(Stream), 0, 255, !IO),
+    expect_eof(Stream, !IO),
+    io.write_string("done.\n", !IO).
+
+:- pred read_byte_test(int::in, io::di, io::uo) is det.
+
+read_byte_test(ExpectedByte, !IO) :-
+    io.read_byte(Res, !IO),
+    (
+        Res = ok(ReadByte),
+        ( ExpectedByte = ReadByte ->
+            true
+        ;
+            Stderr = io.stderr_stream,
+            io.write_string(Stderr, "expected ", !IO),
+            io.write_int(Stderr, ExpectedByte, !IO),
+            io.write_string(Stderr, ", got ", !IO),
+            io.write_int(Stderr, ReadByte, !IO),
+            io.nl(Stderr, !IO),
+            io.set_exit_status(1, !IO)
+        )
+    ;
+        Res = eof,
+        io.write_string(io.stderr_stream, "unexpected eof\n", !IO),
+        io.set_exit_status(1, !IO)
+    ;
+        Res = error(Error),
+        io.write_string(io.stderr_stream, io.error_message(Error), !IO),
+        io.set_exit_status(1, !IO)
+    ).
+
+:- pred read_byte_test(io.binary_input_stream::in, int::in, io::di, io::uo)
+    is det.
+
+read_byte_test(Stream, ExpectedByte, !IO) :-
+    io.read_byte(Stream, Res, !IO),
+    (
+        Res = ok(ReadByte),
+        ( ExpectedByte = ReadByte ->
+            true
+        ;
+            Stderr = io.stderr_stream,
+            io.write_string(Stderr, "expected ", !IO),
+            io.write_int(Stderr, ExpectedByte, !IO),
+            io.write_string(Stderr, ", got ", !IO),
+            io.write_int(Stderr, ReadByte, !IO),
+            io.nl(Stderr, !IO),
+            io.set_exit_status(1, !IO)
+        )
+    ;
+        Res = eof,
+        io.write_string(io.stderr_stream, "unexpected eof\n", !IO),
+        io.set_exit_status(1, !IO)
+    ;
+        Res = error(Error),
+        io.write_string(io.stderr_stream, io.error_message(Error), !IO),
+        io.set_exit_status(1, !IO)
+    ).
+
+:- pred expect_eof(io.binary_input_stream::in, io::di, io::uo) is det.
+
+expect_eof(Stream, !IO) :-
+    io.read_byte(Stream, Res, !IO),
+    (
+        Res = ok(_),
+        io.write_string("expected eof\n", !IO),
+        io.set_exit_status(1, !IO)
+    ;
+        Res = eof
+    ;
+        Res = error(Error),
+        io.write_string(io.stderr_stream, io.error_message(Error), !IO),
+        io.set_exit_status(1, !IO)
+    ).
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sts=4 sw=4 et
diff --git a/tests/hard_coded/binary_stdout.exp b/tests/hard_coded/binary_stdout.exp
new file mode 100644
index 0000000..553a99f
Binary files /dev/null and b/tests/hard_coded/binary_stdout.exp differ
diff --git a/tests/hard_coded/binary_stdout.m b/tests/hard_coded/binary_stdout.m
new file mode 100644
index 0000000..8b2bd12
--- /dev/null
+++ b/tests/hard_coded/binary_stdout.m
@@ -0,0 +1,25 @@
+%-----------------------------------------------------------------------------%
+
+:- module binary_stdout.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module int.
+
+%-----------------------------------------------------------------------------%
+
+main(!IO) :-
+    int.fold_up(io.write_byte, 0, 255, !IO),
+    io.stdout_binary_stream(Stream, !IO),
+    int.fold_up(io.write_byte(Stream), 0, 255, !IO).
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sts=4 sw=4 et

--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list