[m-rev.] for review: Do not use stateful-error style in io.m; minor bug fixes.
Peter Wang
novalazy at gmail.com
Tue Sep 27 17:26:51 AEST 2016
library/io.m:
Make remaining I/O primitives return error codes directly to Mercury
wrapper predicates, instead of saving the error in a global variable
to be looked up separately.
Delete stateful-error predicates and global variables.
Delete unnecessary foreign exported procedures.
Add `no_error' helper function.
Add `result_code' type to indicate whether a primitive succeeded,
reach the end-of-file, or an error occurred.
Make `read_char_code' and `read_byte_val' return the result code
and error separately from the value.
Add efficient implementations of `do_read_bitmap' for C# and Java.
Add Java methods `read_pushback' and `read_non_pushback' for binary
input streams to support `do_read_bitmap'.
Use a purpose-specific type for the return value of
`read_line_as_string_2'.
Fix a bug: the C version of `read_line_as_string_2' returned -2
(null character found) instead of -1 for an I/O error.
Move Mercury value construction for `read_file_as_string' out of
foreign procs.
In C version of `read_char_code', if we hit EOF or an error in the
middle of a multi-byte sequence then stop reading, and report `errno'
or EILSEQ. The old code would try to read the rest of the multi-byte
sequence and decode it, which should fail, then report EILSEQ.
Simplify how `do_open_text' and `do_open_binary' report attempts to
open directories as streams.
Fix a bug: the C version of `read_symlink' passed the wrong buffer
size to readlink(); fortunately it would only truncate very long
symbolic link targets.
Add comments for future changes.
tests/general/read_dir_regression.exp:
Update expected error message.
---
library/io.m | 1531 ++++++++++++++++-----------------
tests/general/read_dir_regression.exp | 2 +-
2 files changed, 755 insertions(+), 778 deletions(-)
diff --git a/library/io.m b/library/io.m
index a5a2d0d..bac48ff 100644
--- a/library/io.m
+++ b/library/io.m
@@ -1966,24 +1966,35 @@ using System.Security.Principal;
:- func get_stream_id(stream) = stream_id.
- % This inter-language stuff is tricky.
- % We communicate via ints rather than via io.result_codes because
- % we don't want the C/Java/etc code to depend on how Mercury stores
- % its discriminated union data types.
+ % We communicate results from foreign_procs as separate simple arguments
+ % so the C/Java/etc code does not depend on how Mercury stores its
+ % discriminated union data types. It also avoids memory allocation in
+ % inner loops.
- % Reads a character (code point) from specified stream, and returns the
- % numerical value for that character (as from char.to_int). This may
+:- type result_code
+ ---> ok
+ ; eof
+ ; error.
+
+:- pragma foreign_export_enum("C", result_code/0,
+ [prefix("ML_RESULT_CODE_"), uppercase]).
+:- pragma foreign_export_enum("C#", result_code/0,
+ [prefix("ML_RESULT_CODE_"), uppercase]).
+:- pragma foreign_export_enum("Java", result_code/0,
+ [prefix("ML_RESULT_CODE_"), uppercase]).
+
+ % Reads a character (code point) from specified stream. This may
% involve converting external character encodings into Mercury's internal
% character representation and (for text streams) converting OS line
% indicators, e.g. CR-LF for Windows, to '\n' characters.
- % Returns -1 if at EOF, -2 if an error occurs.
%
-:- pred read_char_code(input_stream::in, int::out, io::di, io::uo) is det.
+:- pred read_char_code(input_stream::in, result_code::out, char::out,
+ system_error::out, io::di, io::uo) is det.
% Reads a byte from specified stream.
- % Returns -1 if at EOF, -2 if an error occurs.
%
-:- pred read_byte_val(input_stream::in, int::out, io::di, io::uo) is det.
+:- pred read_byte_val(input_stream::in, result_code::out, int::out,
+ system_error::out, io::di, io::uo) is det.
% call_system_code(Command, Status, Success, Message, !IO):
%
@@ -2129,36 +2140,33 @@ read_char(Result, !IO) :-
read_char(Stream, Result, !IO).
read_char(Stream, Result, !IO) :-
- read_char_code(Stream, Code, !IO),
- ( if
- Code >= 0,
- char.to_int(Char, Code)
- then
+ read_char_code(Stream, Result0, Char, Error, !IO),
+ (
+ Result0 = ok,
Result = ok(Char)
- else if Code = -1 then
+ ;
+ Result0 = eof,
Result = eof
- else
- io.make_err_msg("read failed: ", Msg, !IO),
+ ;
+ Result0 = error,
+ make_err_msg(Error, "read failed: ", Msg, !IO),
Result = error(io_error(Msg))
).
:- pragma inline(read_char_unboxed/5).
read_char_unboxed(Stream, Result, Char, !IO) :-
- read_char_code(Stream, Code, !IO),
- ( if
- Code >= 0,
- char.to_int(Char0, Code)
- then
- Result = ok,
- Char = Char0
- else if Code = -1 then
- Result = eof,
- Char = char.det_from_int(0)
- else
- make_err_msg("read failed: ", Msg, !IO),
- Result = error(io_error(Msg)),
- Char = char.det_from_int(0)
+ read_char_code(Stream, Result0, Char, Error, !IO),
+ (
+ Result0 = ok,
+ Result = ok
+ ;
+ Result0 = eof,
+ Result = eof
+ ;
+ Result0 = error,
+ make_err_msg(Error, "read failed: ", Msg, !IO),
+ Result = error(io_error(Msg))
).
% We want to inline these, to allow deforestation.
@@ -2170,13 +2178,16 @@ read_byte(Result, !IO) :-
read_byte(Stream, Result, !IO).
read_byte(binary_input_stream(Stream), Result, !IO) :-
- read_byte_val(input_stream(Stream), Code, !IO),
- ( if Code >= 0 then
- Result = ok(Code)
- else if Code = -1 then
+ read_byte_val(input_stream(Stream), Result0, Byte, Error, !IO),
+ (
+ Result0 = ok,
+ Result = ok(Byte)
+ ;
+ Result0 = eof,
Result = eof
- else
- make_err_msg("read failed: ", Msg, !IO),
+ ;
+ Result0 = error,
+ make_err_msg(Error, "read failed: ", Msg, !IO),
Result = error(io_error(Msg))
).
@@ -2204,12 +2215,11 @@ read_bitmap(binary_input_stream(Stream), Start, NumBytes, !Bitmap,
byte_in_range(!.Bitmap, Start + NumBytes - 1)
then
do_read_bitmap(Stream, Start, NumBytes,
- !Bitmap, 0, BytesRead, !IO),
- ferror(Stream, ErrInt, ErrMsg, !IO),
- ( if ErrInt = 0 then
- Result = ok
+ !Bitmap, 0, BytesRead, Error, !IO),
+ ( if is_error(Error, "read failed: ", Message) then
+ Result = error(io_error(Message))
else
- Result = error(io_error(ErrMsg))
+ Result = ok
)
else if
NumBytes = 0,
@@ -2224,35 +2234,95 @@ read_bitmap(binary_input_stream(Stream), Start, NumBytes, !Bitmap,
:- pred do_read_bitmap(stream::in, byte_index::in, num_bytes::in,
bitmap::bitmap_di, bitmap::bitmap_uo, num_bytes::in, num_bytes::out,
- io::di, io::uo) is det.
+ system_error::out, io::di, io::uo) is det.
- % Default implementation for C# and Java.
-do_read_bitmap(Stream, Start, NumBytes, !Bitmap, !BytesRead, !IO) :-
+ % Default implementation for Erlang.
+do_read_bitmap(Stream, Start, NumBytes, !Bitmap, !BytesRead, Error, !IO) :-
( if NumBytes > 0 then
- read_byte(binary_input_stream(Stream), ByteResult, !IO),
+ read_byte_val(input_stream(Stream), Result0, Byte, Error0, !IO),
(
- ByteResult = ok(Byte),
+ Result0 = ok,
!:Bitmap = !.Bitmap ^ unsafe_byte(Start) := Byte,
!:BytesRead = !.BytesRead + 1,
do_read_bitmap(Stream, Start + 1, NumBytes - 1,
- !Bitmap, !BytesRead, !IO)
+ !Bitmap, !BytesRead, Error, !IO)
;
- ByteResult = eof
+ Result0 = eof,
+ Error = Error0
;
- ByteResult = error(_)
+ Result0 = error,
+ Error = Error0
)
else
- true
+ Error = no_error
).
+
:- pragma foreign_proc("C",
do_read_bitmap(Stream::in, StartByte::in, NumBytes::in,
Bitmap0::bitmap_di, Bitmap::bitmap_uo, BytesRead0::in, BytesRead::out,
- _IO0::di, _IO::uo),
+ Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
"
- Bitmap = Bitmap0,
- BytesRead = BytesRead0 +
- MR_READ(*Stream, Bitmap->elements + StartByte, NumBytes);
+ size_t nread;
+
+ Bitmap = Bitmap0;
+ nread = MR_READ(*Stream, Bitmap->elements + StartByte, NumBytes);
+ BytesRead = BytesRead0 + nread;
+ if (nread < NumBytes && MR_FERROR(*Stream)) {
+ Error = errno;
+ } else {
+ Error = 0;
+ }
+").
+
+:- pragma foreign_proc("C#",
+ do_read_bitmap(Stream::in, StartByte::in, NumBytes::in,
+ Bitmap0::bitmap_di, Bitmap::bitmap_uo, BytesRead0::in, BytesRead::out,
+ Error::out, _IO0::di, _IO::uo),
+ [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+"
+ io.MR_MercuryFileStruct mf = Stream;
+
+ Bitmap = Bitmap0;
+ BytesRead = BytesRead0;
+
+ if (mf.putback != -1) {
+ Bitmap.elements[StartByte] = (byte) mf.putback;
+ BytesRead++;
+ StartByte++;
+ NumBytes--;
+ mf.putback = -1;
+ }
+
+ try {
+ BytesRead += mf.stream.Read(Bitmap.elements, StartByte, NumBytes);
+ Error = null;
+ } catch (System.Exception e) {
+ Error = e;
+ }
+").
+
+:- pragma foreign_proc("Java",
+ do_read_bitmap(Stream::in, StartByte::in, NumBytes::in,
+ Bitmap0::bitmap_di, Bitmap::bitmap_uo, BytesRead0::in, BytesRead::out,
+ Error::out, _IO0::di, _IO::uo),
+ [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+"
+ MR_BinaryInputFile mf = (MR_BinaryInputFile) Stream;
+ Bitmap = Bitmap0;
+ BytesRead = BytesRead0;
+
+ final int nread = mf.read_pushback(Bitmap.elements, StartByte, NumBytes);
+ BytesRead += nread;
+ StartByte += nread;
+ NumBytes -= nread;
+
+ try {
+ BytesRead += mf.read_non_pushback(Bitmap.elements, StartByte, NumBytes);
+ Error = null;
+ } catch (java.lang.Exception e) {
+ Error = e;
+ }
").
read_binary_file_as_bitmap(Result, !IO) :-
@@ -2378,43 +2448,41 @@ read_line(Result, !IO) :-
read_line(Stream, Result, !IO).
read_line(Stream, Result, !IO) :-
- read_char_code(Stream, Code, !IO),
- ( if
- Code >= 0,
- char.to_int(Char, Code)
- then
- ( if Char = '\n' then
- Result = ok([Char])
- else
- read_line_2(Stream, Result0, !IO),
- Result = ok([Char | Result0])
- )
- else if Code = -1 then
+ read_line_2(Stream, Result0, Chars, Error, !IO),
+ (
+ Result0 = ok,
+ Result = ok(Chars)
+ ;
+ Result0 = eof,
Result = eof
- else
- make_err_msg("read failed: ", Msg, !IO),
+ ;
+ Result0 = error,
+ make_err_msg(Error, "read failed: ", Msg, !IO),
Result = error(io_error(Msg))
).
-:- pred read_line_2(input_stream::in, list(char)::out,
- io::di, io::uo) is det.
+:- pred read_line_2(input_stream::in, result_code::out, list(char)::out,
+ system_error::out, io::di, io::uo) is det.
-read_line_2(Stream, Result, !IO) :-
- read_char_code(Stream, Code, !IO),
- ( if
- Code >= 0,
- char.to_int(Char, Code)
- then
+read_line_2(Stream, Result, Chars, Error, !IO) :-
+ read_char_code(Stream, Result0, Char, Error0, !IO),
+ (
+ Result0 = ok,
( if Char = '\n' then
- Result = [Char]
+ Result = ok,
+ Chars = [Char],
+ Error = Error0
else
- read_line_2(Stream, Chars, !IO),
- Result = [Char | Chars]
+ read_line_2(Stream, Result, CharsTail, Error, !IO),
+ Chars = [Char | CharsTail] % lcmc
)
- else if Code = -1 then
- Result = []
- else
- Result = []
+ ;
+ ( Result0 = eof
+ ; Result0 = error
+ ),
+ Result = Result0,
+ Chars = [],
+ Error = Error0
).
read_line_as_string(Result, !IO) :-
@@ -2422,26 +2490,40 @@ read_line_as_string(Result, !IO) :-
read_line_as_string(Stream, Result, !IO).
read_line_as_string(input_stream(Stream), Result, !IO) :-
- read_line_as_string_2(Stream, yes, Res, String, !IO),
- ( if Res < 0 then
- ( if Res = -1 then
- Result = eof
- else if Res = -2 then
- Result = error(io_error("null character in input"))
- else
- make_err_msg("read failed: ", Msg, !IO),
- Result = error(io_error(Msg))
- )
- else
+ read_line_as_string_2(Stream, yes, Res, String, Error, !IO),
+ (
+ Res = ok,
Result = ok(String)
+ ;
+ Res = eof,
+ Result = eof
+ ;
+ Res = null_char,
+ Result = error(io_error("null character in input"))
+ ;
+ Res = error,
+ make_err_msg(Error, "read failed: ", Msg, !IO),
+ Result = error(io_error(Msg))
).
-:- pred read_line_as_string_2(io.stream::in, bool::in, int::out,
- string::out, io::di, io::uo) is det.
+:- type read_line_as_string_result
+ ---> ok
+ ; eof
+ ; null_char
+ ; error.
+
+:- pragma foreign_export_enum("C", read_line_as_string_result/0,
+ [prefix("ML_READ_LINE_AS_STRING_"), uppercase]).
+:- pragma foreign_export_enum("Java", read_line_as_string_result/0,
+ [prefix("ML_READ_LINE_AS_STRING_"), uppercase]).
+
+:- pred read_line_as_string_2(io.stream::in, bool::in,
+ read_line_as_string_result::out, string::out, system_error::out,
+ io::di, io::uo) is det.
:- pragma foreign_proc("C",
read_line_as_string_2(Stream::in, _FirstCall::in, Res::out,
- RetString::out, _IO0::di, _IO::uo),
+ RetString::out, Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness, no_sharing],
"
@@ -2455,21 +2537,23 @@ read_line_as_string(input_stream(Stream), Result, !IO) :-
size_t i;
int char_code = '\\0';
- Res = 0;
+ Res = ML_READ_LINE_AS_STRING_OK;
+ Error = 0;
for (i = 0; char_code != '\\n'; ) {
char_code = mercury_get_byte(Stream);
if (char_code == EOF) {
if (i == 0) {
if (MR_FERROR(*Stream)) {
- Res = -2;
+ Res = ML_READ_LINE_AS_STRING_ERROR;
+ Error = errno;
} else {
- Res = -1;
+ Res = ML_READ_LINE_AS_STRING_EOF;
}
}
break;
}
if (char_code == 0) {
- Res = -2;
+ Res = ML_READ_LINE_AS_STRING_NULL_CHAR;
break;
}
read_buffer[i++] = (char) char_code;
@@ -2487,7 +2571,7 @@ read_line_as_string(input_stream(Stream), Result, !IO) :-
}
}
}
- if (Res == 0) {
+ if (Res == ML_READ_LINE_AS_STRING_OK) {
MR_Word ret_string_word;
MR_offset_incr_hp_atomic_msg(ret_string_word,
0, ML_IO_BYTES_TO_WORDS((i + 1) * sizeof(char)),
@@ -2496,10 +2580,6 @@ read_line_as_string(input_stream(Stream), Result, !IO) :-
MR_memcpy(RetString, read_buffer, i * sizeof(char));
RetString[i] = '\\0';
} else {
- /*
- ** We can't just return NULL here, because otherwise mdb will break
- ** when it tries to print the string.
- */
RetString = MR_make_string_const("""");
}
if (read_buffer != initial_read_buffer) {
@@ -2508,52 +2588,60 @@ read_line_as_string(input_stream(Stream), Result, !IO) :-
").
:- pragma foreign_proc("Java",
- io.read_line_as_string_2(Stream::in, _FirstCall::in, Res::out,
- RetString::out, _IO0::di, _IO::uo),
+ read_line_as_string_2(Stream::in, _FirstCall::in, Res::out,
+ RetString::out, Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness, may_not_duplicate],
"
try {
RetString = ((io.MR_TextInputFile) Stream).read_line();
- Res = (RetString != null) ? 0 : -1;
+ if (RetString != null) {
+ Res = ML_READ_LINE_AS_STRING_OK;
+ } else {
+ Res = ML_READ_LINE_AS_STRING_EOF;
+ }
+ Error = null;
} catch (java.io.IOException e) {
- io.MR_io_exception.set(e);
- Res = -3;
+ Res = ML_READ_LINE_AS_STRING_ERROR;
RetString = """";
+ Error = e;
}
").
-read_line_as_string_2(Stream, FirstCall, Res, String, !IO) :-
+read_line_as_string_2(Stream, FirstCall, Res, String, Error, !IO) :-
% XXX This is terribly inefficient, a better approach would be to
% use a buffer like what is done for io.read_file_as_string.
- read_char(input_stream(Stream), Result, !IO),
+ read_char_code(input_stream(Stream), ReadChar, Char, Error0, !IO),
(
- Result = ok(Char),
+ ReadChar = ok,
( if Char = '\n' then
- Res = 0,
- String = "\n"
+ Res = ok,
+ String = "\n",
+ Error = Error0
else if char.to_int(Char, 0) then
- Res = -2,
- String = ""
+ Res = null_char,
+ String = "",
+ Error = Error0
else
- read_line_as_string_2(Stream, no, Res, String0, !IO),
+ read_line_as_string_2(Stream, no, Res, String0, Error, !IO),
string.first_char(String, Char, String0)
)
;
- Result = eof,
+ ReadChar = eof,
(
FirstCall = yes,
- String = "",
- Res = -1
+ Res = eof
;
FirstCall = no,
- String = "",
- Res = 0
- )
+ Res = ok
+ ),
+ String = "",
+ Error = Error0
;
- Result = error(_),
+ ReadChar = error,
+ Res = error,
String = "",
- Res = -3
+ Error = Error0
).
read_file(Result, !IO) :-
@@ -2585,39 +2673,58 @@ read_file_as_string(Result, !IO) :-
input_stream(Stream, !IO),
read_file_as_string(Stream, Result, !IO).
-:- pragma foreign_proc("Java",
- read_file_as_string(InputStream::in, Result::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
- may_not_duplicate],
-"
- io.MR_TextInputFile File;
- StringBuilder sb;
+read_file_as_string(input_stream(Stream), Result, !IO) :-
+ read_file_as_string_2(Stream, String, Error, NullCharError, !IO),
+ ( if is_error(Error, "read failed: ", Message) then
+ Result = error(String, io_error(Message))
+ else
+ (
+ NullCharError = yes,
+ Result = error("", io_error("null character in input"))
+ ;
+ NullCharError = no,
+ Result = ok(String)
+ )
+ ).
- File = (io.MR_TextInputFile) InputStream.F1;
- sb = new StringBuilder();
+:- pred read_file_as_string_2(stream::in, string::out, system_error::out,
+ bool::out, io::di, io::uo) is det.
+:- pragma foreign_proc("Java",
+ read_file_as_string_2(Stream::in, String::out, Error::out,
+ NullCharError::out, _IO0::di, _IO::uo),
+ [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
+"
+ StringBuilder sb = new StringBuilder();
try {
- File.read_file(sb);
- Result = ML_make_io_maybe_partial_res_1_ok_string(sb.toString());
+ ((io.MR_TextInputFile) Stream).read_file(sb);
+ Error = null;
} catch (java.io.IOException e) {
- Result = ML_make_io_maybe_partial_res_1_error_string(sb.toString(),
- e, ""io.read_file_as_string failed: "");
+ Error = e;
}
+ String = sb.toString();
+ NullCharError = bool.NO;
").
:- pragma foreign_proc("Erlang",
- read_file_as_string(InputStream::in, Result::out, _IO0::di, _IO::uo),
+ read_file_as_string_2(Stream::in, String::out, Error::out,
+ NullCharError::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
- {input_stream, Stream} = InputStream,
- Result = mercury__io:mercury_read_string_to_eof(Stream)
+ case mercury__io:mercury_read_string_to_eof(Stream) of
+ {ok, String} ->
+ Error = ok;
+ {error, String, Reason} ->
+ Error = {error, Reason}
+ end,
+ NullCharError = {no}
").
-read_file_as_string(Stream, Result, !IO) :-
+read_file_as_string_2(Stream, String, Error, NullCharError, !IO) :-
% Check if the stream is a regular file; if so, allocate a buffer
% according to the size of the file. Otherwise, just use a default buffer
% size of 4k minus a bit (to give malloc some room).
- input_stream_file_size(Stream, FileSize, !IO),
+ input_stream_file_size(input_stream(Stream), FileSize, !IO),
( if FileSize >= 0 then
BufferSize0 = FileSize + 1
else
@@ -2627,43 +2734,36 @@ read_file_as_string(Stream, Result, !IO) :-
% Read the file into the buffer (resizing it as we go if necessary),
% convert the buffer into a string, and see if anything went wrong.
- input_clear_err(Stream, !IO),
Pos0 = 0,
- read_file_as_string_2(Stream, Buffer0, Buffer, Pos0, Pos,
- BufferSize0, BufferSize, !IO),
+ read_file_as_string_loop(input_stream(Stream), Buffer0, Buffer, Pos0, Pos,
+ BufferSize0, BufferSize, Error, !IO),
require(Pos < BufferSize, "io.read_file_as_string: overflow"),
- ( if buffer_to_string(Buffer, Pos, String) then
- input_check_err(Stream, Result0, !IO),
- (
- Result0 = ok,
- Result = ok(String)
- ;
- Result0 = error(Error),
- Result = error(String, Error)
- )
+ ( if buffer_to_string(Buffer, Pos, StringPrime) then
+ String = StringPrime,
+ NullCharError = no
else
- Result = error("", io_error("null character in input"))
+ String = "",
+ NullCharError = yes
).
-:- pred read_file_as_string_2(input_stream::in, buffer::buffer_di,
- buffer::buffer_uo, int::in, int::out, int::in, int::out, io::di, io::uo)
- is det.
+:- pred read_file_as_string_loop(input_stream::in, buffer::buffer_di,
+ buffer::buffer_uo, int::in, int::out, int::in, int::out, system_error::out,
+ io::di, io::uo) is det.
-read_file_as_string_2(Stream, !Buffer, !Pos, !Size, !IO) :-
- Pos0 = !.Pos,
+read_file_as_string_loop(Stream, !Buffer, !Pos, !Size, Error, !IO) :-
Size0 = !.Size,
Stream = input_stream(RealStream),
- read_into_buffer(RealStream, !Buffer, !Pos, !.Size, !IO),
- ( if !.Pos =< Pos0 then
- % End-of-file or error.
- true
+ read_into_buffer(RealStream, !Buffer, !Pos, Size0, Error0, !IO),
+ ( if !.Pos < Size0 then
+ % Buffer not full: end-of-file or error.
+ Error = Error0
else if !.Pos = Size0 then
% Full buffer.
!:Size = Size0 * 2,
resize_buffer(Size0, !.Size, !Buffer),
- read_file_as_string_2(Stream, !Buffer, !Pos, !Size, !IO)
+ read_file_as_string_loop(Stream, !Buffer, !Pos, !Size, Error, !IO)
else
- read_file_as_string_2(Stream, !Buffer, !Pos, !Size, !IO)
+ error("io.read_file_as_string: buffer overflow")
).
%---------------------------------------------------------------------------%
@@ -2748,168 +2848,6 @@ input_stream_foldl2_io_maybe_stop(Stream, Pred, T0, Res, !IO) :-
%---------------------------------------------------------------------------%
-% :- pragma obsolete(input_clear_err/3).
-:- pred input_clear_err(io.input_stream::in, io::di, io::uo) is det.
-
-input_clear_err(input_stream(Stream), !IO) :-
- clear_err(Stream, !IO).
-
-% :- pragma obsolete(output_clear_err/3).
-:- pred output_clear_err(output_stream::in, io::di, io::uo) is det.
-
-output_clear_err(output_stream(Stream), !IO) :-
- clear_err(Stream, !IO).
-
- % Same as ANSI C's clearerr().
- %
-:- pred clear_err(stream::in, io::di, io::uo) is det.
-
-:- pragma foreign_proc("C",
- clear_err(Stream::in, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
- does_not_affect_liveness, no_sharing],
-"
- if (MR_IS_FILE_STREAM(*Stream)) {
- clearerr(MR_file(*Stream));
- } else {
- /* Not a file stream so do nothing */
- }
-").
-
-:- pragma foreign_proc("C#",
- clear_err(_Stream::in, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, thread_safe],
-"{
- // XXX no error flag to reset as in .NET an error is thrown
- // directly as an exception (we should create an error indicator
- // in MF_Mercury_file for compatibility)
-}").
-
-:- pragma foreign_proc("Java",
- clear_err(_Stream::in, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
-"
- // XXX as for .NET above
-").
-
-:- pragma foreign_proc("Erlang",
- clear_err(_Stream::in, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, thread_safe],
-"
- % XXX as for .NET above
- void
-").
-
-:- pred io.input_check_err(io.input_stream::in, io.res::out, io::di, io::uo)
- is det.
-
-input_check_err(input_stream(Stream), Result, !IO) :-
- check_err(Stream, Result, !IO).
-
-:- pred check_err(stream::in, io.res::out, io::di, io::uo) is det.
-
-check_err(Stream, Res, !IO) :-
- ferror(Stream, Int, Msg, !IO),
- ( if Int = 0 then
- Res = ok
- else
- Res = error(io_error(Msg))
- ).
-
- % Similar to ANSI C's ferror().
- %
-:- pred ferror(stream::in, int::out, string::out, io::di, io::uo) is det.
-
-:- pragma foreign_proc("C",
- ferror(Stream::in, RetVal::out, RetStr::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
- does_not_affect_liveness, no_sharing],
-"
- if (MR_IS_FILE_STREAM(*Stream)) {
- RetVal = ferror(MR_file(*Stream));
- } else {
- RetVal = -1;
- }
-
- ML_maybe_make_err_msg(RetVal != 0, errno, ""read failed: "",
- MR_ALLOC_ID, RetStr);
-").
-
-:- pragma foreign_proc("C#",
- ferror(_Stream::in, RetVal::out, _RetStr::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, thread_safe],
-"{
- // XXX see clearerr
- RetVal = 0;
-}").
-
-:- pragma foreign_proc("Java",
- ferror(_Stream::in, RetVal::out, _RetStr::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
-"{
- // XXX see clearerr
- RetVal = 0;
-}").
-
-:- pragma foreign_proc("Erlang",
- ferror(_Stream::in, RetVal::out, RetStr::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, thread_safe],
-"
- % XXX see clearerr
- RetVal = 0,
- RetStr = <<>>
-").
-
-% :- pragma obsolete(make_err_msg/4).
-:- pred make_err_msg(string::in, string::out, io::di, io::uo) is det.
-
-make_err_msg(Msg0, Msg, !IO) :-
- get_system_error(Error, !IO),
- make_err_msg(Error, Msg0, Msg, !IO).
-
-% :- pragma obsolete(get_system_error/3).
-:- pred get_system_error(system_error::out, io::di, io::uo) is det.
-
-:- pragma foreign_proc("C",
- get_system_error(Error::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
- does_not_affect_liveness, no_sharing],
-"{
- /*
- ** XXX If the Mercury context that called the failing C function is now
- ** running on a different OS thread, this errno won't be the one
- ** we are looking for. Or, if a different Mercury context was run on
- ** the same thread in the meantime, the errno could have been clobbered.
- */
- Error = errno;
-}").
-
-:- pragma foreign_proc("C#",
- get_system_error(Error::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
-"{
- Error = io.MR_io_exception;
-}").
-
-:- pragma foreign_proc("Java",
- get_system_error(Error::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
-"
- Error = io.MR_io_exception.get();
-").
-
-:- pragma foreign_proc("Erlang",
- get_system_error(Error::out, _IO0::di, _IO::uo),
- [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
-"
- Error = get('MR_io_exception')
-").
-
-:- pragma foreign_export("C", make_err_msg(in, in, out, di, uo),
- "ML_make_err_msg").
-:- pragma foreign_export("C#", make_err_msg(in, in, out, di, uo),
- "ML_make_err_msg").
-
:- pragma foreign_proc("C",
make_err_msg(Error::in, Msg0::in, Msg::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
@@ -2986,11 +2924,6 @@ have_dotnet :-
SUCCESS_INDICATOR = true;
").
-:- pragma foreign_export("C", make_win32_err_msg(in, in, out, di, uo),
- "ML_make_win32_err_msg").
-:- pragma foreign_export("C#", make_win32_err_msg(in, in, out, di, uo),
- "ML_make_win32_err_msg").
-
make_win32_err_msg(_, _, "", !IO) :-
( if semidet_succeed then
error("io.make_win32_err_msg called for non Win32 back-end")
@@ -4255,16 +4188,16 @@ have_file_ids :- semidet_fail.
%---------------------------------------------------------------------------%
-% A `buffer' is just an array of Chars.
-% Buffer sizes are measured in Chars.
+% A `buffer' is an array of chars.
+% For C backends, it is a C array of C chars.
+% For other backends, it is a Mercury array of Mercury chars.
:- type buffer.
:- pragma foreign_type(c, buffer, "char *", [can_pass_as_mercury_type]).
- % XXX It would be better to use a char_array (e.g. defined as char[] in
- % C#) type rather than array(char). This is because on the Java and IL
- % backends indexing into an array whose element type is known
- % statically requires less overhead.
+ % XXX It would be better to use a char_array type rather than array(char).
+ % This is because on the Java and IL backends indexing into an array whose
+ % element type is known statically requires less overhead.
:- type buffer ---> buffer(array(char)).
% XXX Extend the workaround for no `ui' modes in array.m.
@@ -4346,7 +4279,7 @@ resize_buffer(_OldSize, NewSize, buffer(Array0), buffer(Array)) :-
/* Check that the string doesn't contain null characters. */
if (strlen(Str) != Len) {
- SUCCESS_INDICATOR= MR_FALSE;
+ SUCCESS_INDICATOR = MR_FALSE;
} else {
SUCCESS_INDICATOR = MR_TRUE;
}
@@ -4357,46 +4290,58 @@ buffer_to_string(buffer(Array), Len, String) :-
string.semidet_from_char_list(List, String).
:- pred read_into_buffer(stream::in, buffer::buffer_di, buffer::buffer_uo,
- int::in, int::out, int::in, io::di, io::uo) is det.
+ int::in, int::out, int::in, system_error::out, io::di, io::uo) is det.
:- pragma foreign_proc("C",
read_into_buffer(Stream::in, Buffer0::buffer_di, Buffer::buffer_uo,
- Pos0::in, Pos::out, Size::in, _IO0::di, _IO::uo),
+ Pos0::in, Pos::out, Size::in, Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness],
"
- int items_read;
+ size_t bytes_to_read;
+ size_t bytes_read;
MR_CHECK_EXPR_TYPE(Buffer0, char *);
MR_CHECK_EXPR_TYPE(Buffer, char *);
- items_read = MR_READ(*Stream, Buffer0 + Pos0, Size - Pos0);
+ bytes_to_read = Size - Pos0;
+ bytes_read = MR_READ(*Stream, Buffer0 + Pos0, bytes_to_read);
Buffer = Buffer0;
- Pos = Pos0 + items_read;
+ Pos = Pos0 + bytes_read;
+
+ if (bytes_read < bytes_to_read && MR_FERROR(*Stream)) {
+ Error = errno;
+ } else {
+ Error = 0;
+ }
").
-read_into_buffer(Stream, buffer(Array0), buffer(Array), !Pos, Size, !IO) :-
- read_into_array(Stream, Array0, Array, !Pos, Size, !IO).
+read_into_buffer(Stream, buffer(Array0), buffer(Array), Pos0, Pos, Size,
+ Error, !IO) :-
+ read_into_array(input_stream(Stream), Array0, Array, Pos0, Pos, Size,
+ Error, !IO).
-:- pred read_into_array(stream::in,
+:- pred read_into_array(input_stream::in,
array(char)::array_di, array(char)::array_uo, int::in, int::out,
- int::in, io::di, io::uo) is det.
+ int::in, system_error::out, io::di, io::uo) is det.
-read_into_array(Stream, !Array, !Pos, Size, !IO) :-
+read_into_array(Stream, !Array, !Pos, Size, Error, !IO) :-
( if !.Pos >= Size then
- true
+ Error = no_error
else
- io.read_char(input_stream(Stream), CharResult, !IO),
+ read_char_code(Stream, Result, Char, Error0, !IO),
(
- CharResult = ok(Char),
+ Result = ok,
array.set(!.Pos, Char, !Array),
!:Pos = !.Pos + 1,
- io.read_into_array(Stream, !Array, !Pos, Size, !IO)
+ read_into_array(Stream, !Array, !Pos, Size, Error, !IO)
;
- CharResult = error(_)
+ Result = eof,
+ Error = Error0
;
- CharResult = eof
+ Result = error,
+ Error = Error0
)
).
@@ -4809,6 +4754,7 @@ process_read_term(ReadResult, LineNumber, Result) :-
).
read(Stream, Result, !IO) :-
+ % XXX implicit-stream predicate should call explicit-stream predicate
set_input_stream(Stream, OrigStream, !IO),
read(Result, !IO),
set_input_stream(OrigStream, _Stream, !IO).
@@ -5029,11 +4975,13 @@ write_array(Stream, Array, Separator, OutputPred, !IO) :-
%---------------------------------------------------------------------------%
write_binary(Stream, Term, !IO) :-
+ % XXX implicit-stream predicate should call explicit-stream predicate
set_binary_output_stream(Stream, OrigStream, !IO),
write_binary(Term, !IO),
set_binary_output_stream(OrigStream, _Stream, !IO).
read_binary(Stream, Result, !IO) :-
+ % XXX implicit-stream predicate should call explicit-stream predicate
set_binary_input_stream(Stream, OrigStream, !IO),
read_binary(Result, !IO),
set_binary_input_stream(OrigStream, _Stream, !IO).
@@ -5087,99 +5035,63 @@ read_binary(Result, !IO) :-
%
open_input(FileName, Result, !IO) :-
- do_open_text(FileName, "r", Result0, OpenCount, NewStream, !IO),
- (
- Result0 = ok,
+ do_open_text(FileName, "r", OpenCount, NewStream, Error, !IO),
+ ( if is_error(Error, "can't open input file: ", Message) then
+ Result = error(io_error(Message))
+ else
Result = ok(input_stream(NewStream)),
insert_stream_info(NewStream,
stream(OpenCount, input, text, file(FileName)), !IO)
- ;
- Result0 = failed_isdir,
- Result = error(io_error("can't open directory as file"))
- ;
- Result0 = failed_general,
- make_err_msg("can't open input file: ", Msg, !IO),
- Result = error(io_error(Msg))
).
open_output(FileName, Result, !IO) :-
- do_open_text(FileName, "w", Result0, OpenCount, NewStream, !IO),
- (
- Result0 = ok,
+ do_open_text(FileName, "w", OpenCount, NewStream, Error, !IO),
+ ( if is_error(Error, "can't open output file: ", Message) then
+ Result = error(io_error(Message))
+ else
Result = ok(output_stream(NewStream)),
insert_stream_info(NewStream,
stream(OpenCount, output, text, file(FileName)), !IO)
- ;
- Result0 = failed_isdir,
- Result = error(io_error("can't open directory as file"))
- ;
- Result0 = failed_general,
- make_err_msg("can't open output file: ", Msg, !IO),
- Result = error(io_error(Msg))
).
open_append(FileName, Result, !IO) :-
- do_open_text(FileName, "a", Result0, OpenCount, NewStream, !IO),
- (
- Result0 = ok,
+ do_open_text(FileName, "a", OpenCount, NewStream, Error, !IO),
+ ( if is_error(Error, "can't append to file: ", Message) then
+ Result = error(io_error(Message))
+ else
Result = ok(output_stream(NewStream)),
insert_stream_info(NewStream,
stream(OpenCount, append, text, file(FileName)), !IO)
- ;
- Result0 = failed_isdir,
- Result = error(io_error("can't open directory as file"))
- ;
- Result0 = failed_general,
- make_err_msg("can't append to file: ", Msg, !IO),
- Result = error(io_error(Msg))
).
open_binary_input(FileName, Result, !IO) :-
- do_open_binary(FileName, "rb", Result0, OpenCount, NewStream, !IO),
- (
- Result0 = ok,
+ do_open_binary(FileName, "rb", OpenCount, NewStream, Error, !IO),
+ ( if is_error(Error, "can't open input file: ", Message) then
+ Result = error(io_error(Message))
+ else
Result = ok(binary_input_stream(NewStream)),
insert_stream_info(NewStream,
stream(OpenCount, input, binary, file(FileName)), !IO)
- ;
- Result0 = failed_isdir,
- Result = error(io_error("can't open directory as file"))
- ;
- Result0 = failed_general,
- make_err_msg("can't open input file: ", Msg, !IO),
- Result = error(io_error(Msg))
).
open_binary_output(FileName, Result, !IO) :-
- do_open_binary(FileName, "wb", Result0, OpenCount, NewStream, !IO),
- (
- Result0 = ok,
+ do_open_binary(FileName, "wb", OpenCount, NewStream, Error, !IO),
+ ( if is_error(Error, "can't open output file: ", Message) then
+ Result = error(io_error(Message))
+ else
Result = ok(binary_output_stream(NewStream)),
insert_stream_info(NewStream,
stream(OpenCount, output, binary, file(FileName)), !IO)
- ;
- Result0 = failed_isdir,
- Result = error(io_error("can't open directory as file"))
- ;
- Result0 = failed_general,
- make_err_msg("can't open output file: ", Msg, !IO),
- Result = error(io_error(Msg))
).
open_binary_append(FileName, Result, !IO) :-
- do_open_binary(FileName, "ab", Result0, OpenCount, NewStream, !IO),
- (
- Result0 = ok,
+ do_open_binary(FileName, "ab", OpenCount, NewStream, Error, !IO),
+ ( if is_error(Error, "can't append to file: ", Message) then
+ Result = error(io_error(Message))
+ else
Result = ok(binary_output_stream(NewStream)),
insert_stream_info(NewStream,
stream(OpenCount, append, binary, file(FileName)), !IO)
- ;
- Result0 = failed_isdir,
- Result = error(io_error("can't open directory as file"))
- ;
- Result0 = failed_general,
- make_err_msg("can't append to file: ", Msg, !IO),
- Result = error(io_error(Msg))
).
%---------------------------------------------------------------------------%
@@ -5915,7 +5827,7 @@ MercuryFilePtr mercury_current_binary_input(void);
MercuryFilePtr mercury_current_binary_output(void);
int mercury_next_stream_id(void);
MercuryFilePtr mercury_open(const char *filename, const char *openmode,
- MR_bool *is_dir, MR_AllocSiteInfoPtr alloc_id);
+ MR_AllocSiteInfoPtr alloc_id);
int mercury_get_byte(MercuryFilePtr mf);
int mercury_close(MercuryFilePtr mf);
int ML_fprintf(MercuryFilePtr mf, const char *format, ...);
@@ -6338,6 +6250,27 @@ int ML_fprintf(MercuryFilePtr mf, const char *format, ...);
pushback.push(b);
}
+ public int read_pushback(byte[] b, int start, int len)
+ {
+ final int end = start + len;
+ int cur = start;
+ while (cur < end && !pushback.empty()) {
+ b[cur] = pushback.pop();
+ cur++;
+ }
+ return cur - start;
+ }
+
+ public int read_non_pushback(byte[] b, int start, int len)
+ throws java.io.IOException
+ {
+ int n = binary_input.read(b, start, len);
+ if (n < 0) {
+ return 0;
+ }
+ return n;
+ }
+
@Override
public int getOffset()
throws java.io.IOException
@@ -6639,9 +6572,6 @@ public static MR_MercuryFileStruct mercury_current_binary_input =
public static MR_MercuryFileStruct mercury_current_binary_output =
mercury_stdout_binary;
-// XXX not thread-safe!
-public static System.Exception MR_io_exception;
-
").
:- pragma foreign_code("Java",
@@ -6709,9 +6639,6 @@ public static ThreadLocal<MR_BinaryOutputFile> mercury_current_binary_output =
return mercury_stdout_binary;
}
};
-
-public static ThreadLocal<Exception> MR_io_exception =
- new ThreadLocal<Exception>();
").
:- pragma foreign_decl("Erlang", local, "
@@ -6766,6 +6693,7 @@ public static ThreadLocal<Exception> MR_io_exception =
% ensure that two operations from the same process are done in order.
%
mercury_start_file_server(ParentPid, FileName, Mode) ->
+ % XXX This is wrong for binary streams.
Encoding = {encoding, utf8},
case Mode of
[$r | _] ->
@@ -6787,6 +6715,7 @@ mercury_start_file_server(ParentPid, FileName, Mode) ->
end.
mercury_stdio_file_server(IoDevice) ->
+ % XXX This is wrong for binary streams.
io:setopts(IoDevice, [binary, {encoding, utf8}]),
mercury_file_server(IoDevice, 1, []).
@@ -6981,22 +6910,14 @@ mercury_close_stream(Stream) ->
Result
end.
- % Returns char, or -1 on eof, or -2 on error.
+ % Returns <integer> | eof | {error, Reason}
%
mercury_getc(Stream) ->
{'ML_stream', _Id, Pid} = Stream,
Pid ! {self(), read_char},
receive
- {Pid, read_char_ack, Ret} ->
- case Ret of
- C when is_integer(C) ->
- C;
- eof ->
- -1;
- {error, Reason} ->
- put('MR_io_exception', Reason),
- -2
- end
+ {Pid, read_char_ack, Result} ->
+ Result
end.
% Returns {ok, Binary} | {error, Partial, Reason}
@@ -7005,14 +6926,8 @@ mercury_read_string_to_eof(Stream) ->
{'ML_stream', _Id, Pid} = Stream,
Pid ! {self(), read_string_to_eof},
receive
- {Pid, read_string_to_eof_ack, Ret} ->
- case Ret of
- {error, _Partial, Reason} ->
- put('MR_io_exception', Reason);
- _ ->
- void
- end,
- Ret
+ {Pid, read_string_to_eof_ack, Result} ->
+ Result
end.
mercury_putback(Stream, Character) ->
@@ -7124,15 +7039,13 @@ mercury_set_current_binary_input(Stream) ->
mercury_set_current_binary_output(Stream) ->
put('ML_io_current_binary_output', Stream).
-% We also use the key 'MR_io_exception' in the process dictionary.
-
").
:- pragma foreign_code("C", "
MercuryFilePtr
mercury_open(const char *filename, const char *openmode,
- MR_bool *is_dir, MR_AllocSiteInfoPtr alloc_id)
+ MR_AllocSiteInfoPtr alloc_id)
{
MercuryFilePtr mf;
FILE *f;
@@ -7147,7 +7060,6 @@ mercury_open(const char *filename, const char *openmode,
f = fopen(filename, openmode);
#endif
- *is_dir = MR_FALSE;
if (f == NULL) {
return NULL;
}
@@ -7163,8 +7075,8 @@ mercury_open(const char *filename, const char *openmode,
return NULL;
}
if (S_ISDIR(stat_info.st_mode)) {
- *is_dir = MR_TRUE;
fclose(f);
+ errno = EISDIR;
return NULL;
}
#endif
@@ -7187,50 +7099,41 @@ MR_MercuryFileStruct mercury_open(string filename, string openmode,
System.IO.FileShare share;
System.IO.Stream stream = null;
- try {
- if (openmode == ""r"" || openmode == ""rb"") {
- // Like '<' in Bourne shell.
- // Read a file. The file must exist already.
- mode = System.IO.FileMode.Open;
- access = System.IO.FileAccess.Read;
- } else if (openmode == ""w"" || openmode == ""wb"") {
- // Like '>' in Bourne shell.
- // Overwrite an existing file, or create a new file.
- mode = System.IO.FileMode.Create;
- access = System.IO.FileAccess.Write;
- } else if (openmode == ""a"" || openmode == ""ab"") {
- // Like '>>' in Bourne shell.
- // Append to an existing file, or create a new file.
- mode = System.IO.FileMode.Append;
- access = System.IO.FileAccess.Write;
- } else {
- runtime.Errors.SORRY(System.String.Concat(
- ""foreign code for this function, open mode:"",
- openmode));
- // Needed to convince the C# compiler that mode and
- // access are always initialized.
- throw new System.Exception();
- }
-
- // For Unix compatibility, we allow files
- // to be read or written by multiple processes
- // simultaneously. XXX Is this a good idea?
- share = System.IO.FileShare.ReadWrite;
+ if (openmode == ""r"" || openmode == ""rb"") {
+ // Like '<' in Bourne shell.
+ // Read a file. The file must exist already.
+ mode = System.IO.FileMode.Open;
+ access = System.IO.FileAccess.Read;
+ } else if (openmode == ""w"" || openmode == ""wb"") {
+ // Like '>' in Bourne shell.
+ // Overwrite an existing file, or create a new file.
+ mode = System.IO.FileMode.Create;
+ access = System.IO.FileAccess.Write;
+ } else if (openmode == ""a"" || openmode == ""ab"") {
+ // Like '>>' in Bourne shell.
+ // Append to an existing file, or create a new file.
+ mode = System.IO.FileMode.Append;
+ access = System.IO.FileAccess.Write;
+ } else {
+ runtime.Errors.SORRY(System.String.Concat(
+ ""foreign code for this function, open mode:"",
+ openmode));
+ // Needed to convince the C# compiler that mode and
+ // access are always initialized.
+ throw new System.Exception();
+ }
- stream = System.IO.File.Open(filename, mode, access, share);
+ // For Unix compatibility, we allow files
+ // to be read or written by multiple processes
+ // simultaneously. XXX Is this a good idea?
+ share = System.IO.FileShare.ReadWrite;
- } catch (System.Exception e) {
- MR_io_exception = e;
- }
+ stream = System.IO.File.Open(filename, mode, access, share);
- if (stream == null) {
- return null;
- } else {
- // We initialize the `reader' and `writer' fields to null;
- // they will be filled in later if they are needed.
- return mercury_file_init(new System.IO.BufferedStream(stream),
- null, null, line_ending);
- }
+ // We initialize the `reader' and `writer' fields to null;
+ // they will be filled in later if they are needed.
+ return mercury_file_init(new System.IO.BufferedStream(stream),
+ null, null, line_ending);
}
").
@@ -7299,7 +7202,8 @@ mercury_get_byte(MercuryFilePtr mf)
// Read in a character. This means reading in one or more bytes,
// converting the bytes from the system's default encoding to Unicode,
-// and possibly converting CR-LF to newline. Returns -1 on error or EOF.
+// and possibly converting CR-LF to newline. Returns -1 on EOF, and
+// throws an exception on error.
private static readonly string NewLine = System.Environment.NewLine;
@@ -7724,20 +7628,50 @@ throw_on_close_error(Error, !IO) :-
end
").
+:- func no_error = system_error.
+
+:- pragma foreign_proc("C",
+ no_error = (Error::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ Error = 0;
+").
+
+:- pragma foreign_proc("C#",
+ no_error = (Error::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ Error = null;
+").
+
+:- pragma foreign_proc("Java",
+ no_error = (Error::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ Error = null;
+").
+
+:- pragma foreign_proc("Erlang",
+ no_error = (Error::out),
+ [will_not_call_mercury, promise_pure, thread_safe],
+"
+ Error = ok
+").
+
%---------------------------------------------------------------------------%
%
% Input predicates
%
-read_char_code(input_stream(Stream), CharCode, !IO) :-
- read_char_code_2(Stream, CharCode, !IO).
+read_char_code(input_stream(Stream), Result, Char, Error, !IO) :-
+ read_char_code_2(Stream, Result, Char, Error, !IO).
- % XXX return system_error
-:- pred read_char_code_2(stream::in, int::out, io::di, io::uo)
- is det.
+:- pred read_char_code_2(stream::in, result_code::out, char::out,
+ system_error::out, io::di, io::uo) is det.
:- pragma foreign_proc("C",
- read_char_code_2(Stream::in, CharCode::out, _IO0::di, _IO::uo),
+ read_char_code_2(Stream::in, Result::out, Char::out, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io,
does_not_affect_liveness, no_sharing, may_not_duplicate],
"
@@ -7750,13 +7684,18 @@ read_char_code(input_stream(Stream), CharCode, !IO) :-
c = mercury_get_byte(Stream);
uc = c;
if (uc <= 0x7f) {
- CharCode = uc;
+ Result = ML_RESULT_CODE_OK;
+ Char = uc;
+ Error = 0;
} else if (c == EOF) {
if (MR_FERROR(*Stream)) {
- CharCode = -2;
+ Result = ML_RESULT_CODE_ERROR;
+ Error = errno;
} else {
- CharCode = -1;
+ Result = ML_RESULT_CODE_EOF;
+ Error = 0;
}
+ Char = 0;
} else {
if ((uc & 0xE0) == 0xC0) {
nbytes = 2;
@@ -7773,51 +7712,61 @@ read_char_code(input_stream(Stream), CharCode, !IO) :-
c = mercury_get_byte(Stream);
uc = c;
if (c == EOF) {
- /*
- ** If the byte sequence ends early then it is invalid.
- ** The next read attempt will determine if this is EOF or
- ** an IO error.
- */
- errno = EILSEQ;
- CharCode = -2;
+ /* Illegal byte sequence whether EOF or I/O error. */
+ Result = ML_RESULT_CODE_ERROR;
+ Error = MR_FERROR(*Stream) ? errno : EILSEQ;
+ Char = 0;
+ break;
}
buf[i] = uc;
}
- buf[i] = '\\0';
- CharCode = MR_utf8_get(buf, 0);
- if (CharCode < 0) {
- /* Invalid byte sequence. */
- errno = EILSEQ;
- CharCode = -2;
+ if (i == nbytes) {
+ buf[i] = '\\0';
+ c = MR_utf8_get(buf, 0);
+ if (c < 0) {
+ Result = ML_RESULT_CODE_ERROR;
+ Error = EILSEQ;
+ Char = 0;
+ } else {
+ Result = ML_RESULT_CODE_OK;
+ Char = c;
+ Error = 0;
+ }
}
} else {
- /* Invalid byte sequence. */
- errno = EILSEQ;
- CharCode = -2;
+ /* Invalid lead byte. */
+ Result = ML_RESULT_CODE_ERROR;
+ Error = EILSEQ;
+ Char = 0;
}
}
").
-read_byte_val(input_stream(Stream), ByteVal, !IO) :-
- read_byte_val_2(Stream, ByteVal, !IO).
+read_byte_val(input_stream(Stream), Result, ByteVal, Error, !IO) :-
+ read_byte_val_2(Stream, Result, ByteVal, Error, !IO).
- % XXX return system_error
-:- pred read_byte_val_2(stream::in, int::out, io::di, io::uo) is det.
+:- pred read_byte_val_2(stream::in, result_code::out, int::out,
+ system_error::out, io::di, io::uo) is det.
:- pragma foreign_proc("C",
- read_byte_val_2(Stream::in, ByteVal::out, _IO0::di, _IO::uo),
+ read_byte_val_2(Stream::in, Result::out, ByteVal::out, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io,
does_not_affect_liveness, no_sharing],
"
- int c = mercury_get_byte(Stream);
-
- if (c == EOF) {
+ int b = mercury_get_byte(Stream);
+ if (b == EOF) {
if (MR_FERROR(*Stream)) {
- ByteVal = -2;
+ Result = ML_RESULT_CODE_ERROR;
+ Error = errno;
} else {
- ByteVal = -1;
+ Result = ML_RESULT_CODE_EOF;
+ Error = 0;
}
+ ByteVal = 0;
} else {
- ByteVal = c;
+ Result = ML_RESULT_CODE_OK;
+ ByteVal = b;
+ Error = 0;
}
").
@@ -7884,23 +7833,55 @@ putback_byte(binary_input_stream(Stream), Character, !IO) :-
").
:- pragma foreign_proc("C#",
- read_char_code_2(File::in, CharCode::out, _IO0::di, _IO::uo),
+ read_char_code_2(File::in, Result::out, Char::out, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure],
"
io.MR_MercuryFileStruct mf = File;
- CharCode = io.mercury_getc(mf);
+ try {
+ int c = io.mercury_getc(mf);
+ if (c == -1) {
+ Result = io.ML_RESULT_CODE_EOF;
+ Char = 0;
+ } else {
+ Result = io.ML_RESULT_CODE_OK;
+ Char = c;
+ }
+ Error = null;
+ } catch (System.Exception e) {
+ Result = io.ML_RESULT_CODE_ERROR;
+ Char = 0;
+ Error = e;
+ }
").
:- pragma foreign_proc("C#",
- read_byte_val_2(File::in, ByteVal::out, _IO0::di, _IO::uo),
+ read_byte_val_2(File::in, Result::out, ByteVal::out, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure],
"
io.MR_MercuryFileStruct mf = File;
if (mf.putback != -1) {
+ Result = io.ML_RESULT_CODE_OK;
ByteVal = mf.putback;
+ Error = null;
mf.putback = -1;
} else {
- ByteVal = mf.stream.ReadByte();
+ try {
+ int b = mf.stream.ReadByte();
+ if (b == -1) {
+ Result = io.ML_RESULT_CODE_EOF;
+ ByteVal = 0;
+ } else {
+ Result = io.ML_RESULT_CODE_OK;
+ ByteVal = b;
+ }
+ Error = null;
+ } catch (System.Exception e) {
+ Result = io.ML_RESULT_CODE_ERROR;
+ ByteVal = 0;
+ Error = e;
+ }
}
").
@@ -7934,26 +7915,46 @@ putback_byte(binary_input_stream(Stream), Character, !IO) :-
").
:- pragma foreign_proc("Java",
- read_char_code_2(File::in, CharCode::out, _IO0::di, _IO::uo),
+ read_char_code_2(File::in, Result::out, CharCode::out, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
try {
- CharCode = ((io.MR_TextInputFile) File).read_char();
+ int c = ((io.MR_TextInputFile) File).read_char();
+ if (c == -1) {
+ Result = io.ML_RESULT_CODE_EOF;
+ CharCode = 0;
+ } else {
+ Result = io.ML_RESULT_CODE_OK;
+ CharCode = c;
+ }
+ Error = null;
} catch (java.io.IOException e) {
- io.MR_io_exception.set(e);
- CharCode = -2;
+ Result = io.ML_RESULT_CODE_ERROR;
+ CharCode = 0;
+ Error = e;
}
").
:- pragma foreign_proc("Java",
- read_byte_val_2(File::in, ByteVal::out, _IO0::di, _IO::uo),
+ read_byte_val_2(File::in, Result::out, ByteVal::out, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
try {
- ByteVal = ((io.MR_BinaryInputFile) File).read_byte();
+ int b = ((io.MR_BinaryInputFile) File).read_byte();
+ if (b == -1) {
+ Result = io.ML_RESULT_CODE_EOF;
+ ByteVal = 0;
+ } else {
+ Result = io.ML_RESULT_CODE_OK;
+ ByteVal = b;
+ }
+ Error = null;
} catch (java.io.IOException e) {
- io.MR_io_exception.set(e);
- ByteVal = -2;
+ Result = io.ML_RESULT_CODE_ERROR;
+ ByteVal = 0;
+ Error = e;
}
").
@@ -7974,19 +7975,47 @@ putback_byte(binary_input_stream(Stream), Character, !IO) :-
").
:- pragma foreign_proc("Erlang",
- read_char_code_2(Stream::in, CharCode::out, _IO0::di, _IO::uo),
+ read_char_code_2(Stream::in, Result::out, Char::out, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io,
does_not_affect_liveness],
"
- CharCode = mercury__io:mercury_getc(Stream)
+ case mercury__io:mercury_getc(Stream) of
+ C when is_integer(C) ->
+ Result = {ok},
+ Char = C,
+ Error = ok;
+ eof ->
+ Result = {eof},
+ Char = 0,
+ Error = ok;
+ {error, Reason} ->
+ Result = {error},
+ Char = 0,
+ Error = {error, Reason}
+ end
").
:- pragma foreign_proc("Erlang",
- read_byte_val_2(Stream::in, ByteVal::out, _IO0::di, _IO::uo),
+ read_byte_val_2(Stream::in, Result::out, ByteVal::out, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io,
does_not_affect_liveness],
"
- ByteVal = mercury__io:mercury_getc(Stream)
+ case mercury__io:mercury_getc(Stream) of
+ B when is_integer(B) ->
+ Result = {ok},
+ ByteVal = B,
+ Error = ok;
+ eof ->
+ Result = {eof},
+ ByteVal = 0,
+ Error = ok;
+ {error, Reason} ->
+ Result = {error},
+ ByteVal = 0,
+ Error = {error, Reason}
+ end
").
:- pragma foreign_proc("Erlang",
@@ -9525,144 +9554,121 @@ set_binary_output_stream(binary_output_stream(NewStream),
% Stream open/close predicates.
-:- type open_result
- ---> ok
- ; failed_general
-
- % The failed_isdir result is separate because some OSs (Linux and
- % FreeBSD) will willingly open a directory, and FreeBSD will allow
- % you to read from it. This could cause confusion so we use fstat
- % to detect the problem.
- %
- ; failed_isdir.
-
-:- pragma foreign_export_enum("C", open_result/0,
- [prefix("ML_OR_"), uppercase]).
-:- pragma foreign_export_enum("Java", open_result/0,
- [prefix("ML_OR_"), uppercase]).
-:- pragma foreign_export_enum("C#", open_result/0,
- [prefix("ML_OR_"), uppercase]).
-
- % io.do_open_binary(File, Mode, Result, StreamId, Stream, !IO):
- % io.do_open_text(File, Mode, Result, StreamId, Stream, !IO):
+ % do_open_binary(File, Mode, StreamId, Stream, Error, !IO):
+ % do_open_text(File, Mode, StreamId, Stream, Error, !IO):
%
% Attempts to open a file in the specified mode.
- % The Mode is a string suitable for pssing to fopen().
- % Result describes the result of the operation.
+ % The Mode is a string suitable for passing to fopen().
% StreamId is a unique integer identifying the open.
- % Both StreamId and Stream are valid only if Result = ok.
- % make_err_msg should be called iff Result = failed_general.
+ % StreamId and Stream are valid only if Error indicates an error occurred.
%
-:- pred do_open_binary(string::in, string::in, open_result::out, int::out,
- stream::out, io::di, io::uo) is det.
+:- pred do_open_binary(string::in, string::in, int::out, stream::out,
+ system_error::out, io::di, io::uo) is det.
-:- pred do_open_text(string::in, string::in, open_result::out, int::out,
- stream::out, io::di, io::uo) is det.
+:- pred do_open_text(string::in, string::in, int::out, stream::out,
+ system_error::out, io::di, io::uo) is det.
:- pragma foreign_proc("C",
- do_open_text(FileName::in, Mode::in, Result::out,
- StreamId::out, Stream::out, _IO0::di, _IO::uo),
+ do_open_text(FileName::in, Mode::in, StreamId::out, Stream::out,
+ Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness, no_sharing],
"
- MR_bool is_dir;
-
- Stream = mercury_open(FileName, Mode, &is_dir, MR_ALLOC_ID);
+ Stream = mercury_open(FileName, Mode, MR_ALLOC_ID);
if (Stream != NULL) {
- Result = ML_OR_OK;
StreamId = mercury_next_stream_id();
+ Error = 0;
} else {
- Result = (MR_TRUE == is_dir) ?
- ML_OR_FAILED_ISDIR : ML_OR_FAILED_GENERAL;
StreamId = -1;
+ Error = errno;
}
").
:- pragma foreign_proc("C",
- do_open_binary(FileName::in, Mode::in, Result::out,
- StreamId::out, Stream::out, _IO0::di, _IO::uo),
+ do_open_binary(FileName::in, Mode::in, StreamId::out, Stream::out,
+ Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness, no_sharing],
"
- MR_bool is_dir;
-
- Stream = mercury_open(FileName, Mode, &is_dir, MR_ALLOC_ID);
+ Stream = mercury_open(FileName, Mode, MR_ALLOC_ID);
if (Stream != NULL) {
- Result = ML_OR_OK;
StreamId = mercury_next_stream_id();
+ Error = 0;
} else {
- Result = (MR_TRUE == is_dir) ?
- ML_OR_FAILED_ISDIR : ML_OR_FAILED_GENERAL;
StreamId = -1;
+ Error = errno;
}
").
:- pragma foreign_proc("C#",
- do_open_text(FileName::in, Mode::in, Result::out,
- StreamId::out, Stream::out, _IO0::di, _IO::uo),
+ do_open_text(FileName::in, Mode::in, StreamId::out, Stream::out,
+ Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
"
- io.MR_MercuryFileStruct mf = io.mercury_open(FileName, Mode,
- io.ML_default_line_ending);
- Stream = mf;
- if (mf != null) {
- Result = ML_OR_OK;
- StreamId = mf.id;
- } else {
- Result = ML_OR_FAILED_GENERAL;
+ try {
+ Stream = io.mercury_open(FileName, Mode, io.ML_default_line_ending);
+ StreamId = Stream.id;
+ Error = null;
+ } catch (System.Exception e) {
StreamId = -1;
+ Stream = null;
+ Error = e;
}
").
:- pragma foreign_proc("C#",
- do_open_binary(FileName::in, Mode::in, Result::out,
- StreamId::out, Stream::out, _IO0::di, _IO::uo),
+ do_open_binary(FileName::in, Mode::in, StreamId::out, Stream::out,
+ Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
"
- io.MR_MercuryFileStruct mf = io.mercury_open(FileName, Mode,
- io.ML_line_ending_kind.ML_raw_binary);
- Stream = mf;
- if (mf != null) {
- Result = ML_OR_OK;
- StreamId = mf.id;
- } else {
- Result = ML_OR_FAILED_GENERAL;
+ try {
+ Stream = io.mercury_open(FileName, Mode,
+ io.ML_line_ending_kind.ML_raw_binary);
+ StreamId = Stream.id;
+ Error = null;
+ } catch (System.Exception e) {
StreamId = -1;
+ Stream = null;
+ Error = e;
}
").
:- pragma foreign_proc("Java",
- do_open_text(FileName::in, Mode::in, Result::out,
- StreamId::out, Stream::out, _IO0::di, _IO::uo),
+ do_open_text(FileName::in, Mode::in, StreamId::out, Stream::out,
+ Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
may_not_duplicate],
"
try {
- if (Mode.charAt(0) == 'r') {
- Stream = new MR_TextInputFile(
- new java.io.FileInputStream(FileName));
- } else if (Mode.charAt(0) == 'w') {
- Stream = new MR_TextOutputFile(
- new java.io.FileOutputStream(FileName));
- } else if (Mode.charAt(0) == 'a') {
- Stream = new MR_TextOutputFile(
- new java.io.FileOutputStream(FileName, true));
- } else {
- throw new RuntimeException(""Invalid file opening mode: "" + Mode);
+ switch (Mode.charAt(0)) {
+ case 'r':
+ Stream = new MR_TextInputFile(
+ new java.io.FileInputStream(FileName));
+ break;
+ case 'w':
+ Stream = new MR_TextOutputFile(
+ new java.io.FileOutputStream(FileName));
+ break;
+ case 'a':
+ Stream = new MR_TextOutputFile(
+ new java.io.FileOutputStream(FileName, true));
+ break;
+ default:
+ throw new RuntimeException(""Invalid file opening mode: "" +
+ Mode);
}
StreamId = Stream.id;
- Result = ML_OR_OK;
+ Error = null;
} catch (java.lang.Exception e) {
- io.MR_io_exception.set(e);
Stream = null;
StreamId = -1;
- Result = ML_OR_FAILED_GENERAL;
+ Error = e;
}
").
:- pragma foreign_proc("Java",
- do_open_binary(FileName::in, Mode::in, Result::out,
- StreamId::out, Stream::out, _IO0::di, _IO::uo),
+ do_open_binary(FileName::in, Mode::in, StreamId::out, Stream::out,
+ Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
may_not_duplicate],
"
@@ -9685,54 +9691,49 @@ set_binary_output_stream(binary_output_stream(NewStream),
Mode);
}
StreamId = Stream.id;
- Result = ML_OR_OK;
+ Error = null;
} catch (java.lang.Exception e) {
- io.MR_io_exception.set(e);
Stream = null;
StreamId = -1;
- Result = ML_OR_FAILED_GENERAL;
+ Error = e;
}
").
:- pragma foreign_proc("Erlang",
- do_open_text(FileName::in, Mode::in, Result::out,
- StreamId::out, Stream::out, _IO0::di, _IO::uo),
+ do_open_text(FileName::in, Mode::in, StreamId::out, Stream::out,
+ Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
"
FileNameStr = binary_to_list(FileName),
ModeStr = binary_to_list(Mode),
-
- % Text and binary streams are exactly the same so far.
+ % XXX This should probably pass encoding 'utf8'.
case mercury__io:mercury_open_stream(FileNameStr, ModeStr) of
{ok, Stream} ->
{'ML_stream', StreamId, _Pid} = Stream,
- Result = {ok};
+ Error = ok;
{error, Reason} ->
- put('MR_io_exception', Reason),
StreamId = -1,
Stream = null,
- Result = {failed_general}
+ Error = {error, Reason}
end
").
:- pragma foreign_proc("Erlang",
- do_open_binary(FileName::in, Mode::in, Result::out,
- StreamId::out, Stream::out, _IO0::di, _IO::uo),
+ do_open_binary(FileName::in, Mode::in, StreamId::out, Stream::out,
+ Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
"
FileNameStr = binary_to_list(FileName),
ModeStr = binary_to_list(Mode),
-
- % Text and binary streams are exactly the same so far.
+ % XXX This should probably pass encoding 'latin1'.
case mercury__io:mercury_open_stream(FileNameStr, ModeStr) of
{ok, Stream} ->
{'ML_stream', StreamId, _Pid} = Stream,
- Result = {ok};
+ Error = ok;
{error, Reason} ->
- put('MR_io_exception', Reason),
StreamId = -1,
Stream = null,
- Result = {failed_general}
+ Error = {error, Reason}
end
").
@@ -11091,90 +11092,82 @@ system_temp_dir("", 0, !IO).
").
remove_file(FileName, Result, !IO) :-
- remove_file_2(FileName, Res, ResString, !IO),
- ( if Res = 0 then
- Result = ok
+ remove_file_2(FileName, Error, !IO),
+ ( if is_error(Error, "remove failed: ", Message) then
+ Result = error(io_error(Message))
else
- Result = error(io_error(ResString))
+ Result = ok
).
- % XXX return system_error instead
-:- pred remove_file_2(string::in, int::out, string::out, io::di, io::uo)
- is det.
+:- pred remove_file_2(string::in, system_error::out, io::di, io::uo) is det.
:- pragma foreign_proc("C",
- remove_file_2(FileName::in, RetVal::out, RetStr::out,
- _IO0::di, _IO::uo),
+ remove_file_2(FileName::in, Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness, no_sharing],
"
+ int rc;
#ifdef MR_WIN32
- RetVal = _wremove(ML_utf8_to_wide(FileName));
+ rc = _wremove(ML_utf8_to_wide(FileName));
#else
- RetVal = remove(FileName);
+ rc = remove(FileName);
#endif
- ML_maybe_make_err_msg(RetVal != 0, errno, ""remove failed: "",
- MR_ALLOC_ID, RetStr);
+ if (rc == 0) {
+ Error = 0;
+ } else {
+ Error = errno;
+ }
").
:- pragma foreign_proc("C#",
- remove_file_2(FileName::in, RetVal::out, RetStr::out, _IO0::di, _IO::uo),
+ remove_file_2(FileName::in, Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
-"{
+"
try {
if (System.IO.File.Exists(FileName)) {
System.IO.File.Delete(FileName);
- RetVal = 0;
- RetStr = """";
+ Error = null;
} else {
- RetVal = -1;
- RetStr = ""remove failed: No such file or directory"";
+ Error = new System.IO.FileNotFoundException();
}
}
catch (System.Exception e) {
- RetVal = -1;
- RetStr = e.Message;
+ Error = e;
}
-}").
+").
:- pragma foreign_proc("Java",
- remove_file_2(FileName::in, RetVal::out, RetStr::out, _IO0::di, _IO::uo),
+ remove_file_2(FileName::in, Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
may_not_duplicate],
"
+ // Java 7 java.nio.file.Files.delete() provides more detailed information
+ // about failure to delete.
+
try {
java.io.File file = new java.io.File(FileName);
if (file.delete()) {
- RetVal = 0;
- RetStr = """";
+ Error = null;
} else {
- RetVal = -1;
- RetStr = ""remove_file failed"";
+ Error = new java.io.IOException(""remove_file failed"");
}
} catch (java.lang.Exception e) {
- RetVal = -1;
- RetStr = e.getMessage();
- if (RetStr == null) {
- RetStr = ""null"";
- }
+ Error = e;
}
").
:- pragma foreign_proc("Erlang",
- remove_file_2(FileName::in, RetVal::out, RetStr::out, _IO0::di, _IO::uo),
+ remove_file_2(FileName::in, Error::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness],
"
FileNameStr = binary_to_list(FileName),
case file:delete(FileNameStr) of
ok ->
- RetVal = 0,
- RetStr = <<>>;
+ Error = ok;
{error, Reason} ->
- RetVal = -1,
- ReasonStr = file:format_error(Reason),
- RetStr = list_to_binary([""remove failed: "", ReasonStr])
+ Error = {error, Reason}
end
").
@@ -11233,100 +11226,93 @@ remove_directory_entry(DirName, FileName, _FileType, Continue, _, Res, !IO) :-
Continue = no
).
-rename_file(OldFileName, NewFileName, Result, IO0, IO) :-
- rename_file_2(OldFileName, NewFileName, Res, ResString, IO0, IO),
- ( if Res = 0 then
- Result = ok
+rename_file(OldFileName, NewFileName, Result, !IO) :-
+ rename_file_2(OldFileName, NewFileName, Error, !IO),
+ ( if is_error(Error, "rename failed: ", Message) then
+ Result = error(io_error(Message))
else
- Result = error(io_error(ResString))
+ Result = ok
).
- % XXX return system_error instead
-:- pred rename_file_2(string::in, string::in, int::out, string::out,
+:- pred rename_file_2(string::in, string::in, system_error::out,
io::di, io::uo) is det.
:- pragma foreign_proc("C",
- rename_file_2(OldFileName::in, NewFileName::in, RetVal::out,
- RetStr::out, _IO0::di, _IO::uo),
+ rename_file_2(OldFileName::in, NewFileName::in, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness, no_sharing],
"
+ int rc;
#ifdef MR_WIN32
- RetVal = _wrename(ML_utf8_to_wide(OldFileName),
+ rc = _wrename(ML_utf8_to_wide(OldFileName),
ML_utf8_to_wide(NewFileName));
#else
- RetVal = rename(OldFileName, NewFileName);
+ rc = rename(OldFileName, NewFileName);
#endif
- ML_maybe_make_err_msg(RetVal != 0, errno, ""rename failed: "",
- MR_ALLOC_ID, RetStr);
+ if (rc == 0) {
+ Error = 0;
+ } else {
+ Error = errno;
+ }
").
:- pragma foreign_proc("C#",
- rename_file_2(OldFileName::in, NewFileName::in, RetVal::out,
- RetStr::out, _IO0::di, _IO::uo),
+ rename_file_2(OldFileName::in, NewFileName::in, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
-"{
+"
try {
if (System.IO.File.Exists(OldFileName)) {
System.IO.File.Move(OldFileName, NewFileName);
- RetVal = 0;
- RetStr = """";
+ Error = null;
} else {
- RetVal = -1;
- RetStr = ""rename failed: No such file or directory"";
+ Error = new System.IO.FileNotFoundException();
}
}
catch (System.Exception e) {
- RetVal = -1;
- RetStr = e.Message;
+ Error = e;
}
-}").
+").
:- pragma foreign_proc("Java",
- rename_file_2(OldFileName::in, NewFileName::in, RetVal::out,
- RetStr::out, _IO0::di, _IO::uo),
+ rename_file_2(OldFileName::in, NewFileName::in, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
may_not_duplicate],
"
+ // Java 7 java.nio.file.Files.move may provide more detailed information
+ // about failure to rename.
+
try {
java.io.File file = new java.io.File(OldFileName);
if (file.exists()) {
if (file.renameTo(new java.io.File(NewFileName))) {
- RetVal = 0;
- RetStr = """";
+ Error = null;
} else {
- RetVal = -1;
- RetStr = ""rename_file failed"";
+ Error = new java.io.IOException(""rename_file failed"");
}
} else {
- RetVal = -1;
- RetStr = ""rename failed: No such file or directory"";
+ Error = new java.io.IOException(""No such file or directory"");
}
} catch (java.lang.Exception e) {
- RetVal = -1;
- RetStr = e.getMessage();
- if (RetStr == null) {
- RetStr = ""null"";
- }
+ Error = e;
}
").
:- pragma foreign_proc("Erlang",
- rename_file_2(OldFileName::in, NewFileName::in, RetVal::out,
- RetStr::out, _IO0::di, _IO::uo),
+ rename_file_2(OldFileName::in, NewFileName::in, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
"
OldFileNameStr = binary_to_list(OldFileName),
NewFileNameStr = binary_to_list(NewFileName),
case file:rename(OldFileNameStr, NewFileNameStr) of
ok ->
- RetVal = 0,
- RetStr = <<>>;
+ Error = ok;
{error, Reason} ->
- RetVal = -1,
- ReasonStr = file:format_error(Reason),
- RetStr = list_to_binary([""rename_file failed: "", ReasonStr])
+ Error = {error, Reason}
end
").
@@ -11355,10 +11341,9 @@ have_symlinks :- semidet_fail.
make_symlink(FileName, LinkFileName, Result, !IO) :-
( if io.have_symlinks then
- io.make_symlink_2(FileName, LinkFileName, Status, !IO),
- ( if Status = 0 then
- io.make_err_msg("io.make_symlink failed: ", Msg, !IO),
- Result = error(make_io_error(Msg))
+ io.make_symlink_2(FileName, LinkFileName, Error, !IO),
+ ( if is_error(Error, "io.make_symlink failed: ", Message) then
+ Result = error(make_io_error(Message))
else
Result = ok
)
@@ -11367,25 +11352,28 @@ make_symlink(FileName, LinkFileName, Result, !IO) :-
"io.make_symlink not supported on this platform"))
).
- % XXX return system_error instead
-:- pred make_symlink_2(string::in, string::in, int::out, io::di, io::uo)
- is det.
+:- pred make_symlink_2(string::in, string::in, system_error::out,
+ io::di, io::uo) is det.
:- pragma foreign_proc("C",
- make_symlink_2(FileName::in, LinkFileName::in, Status::out,
+ make_symlink_2(FileName::in, LinkFileName::in, Error::out,
_IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness, no_sharing],
"
#ifdef MR_HAVE_SYMLINK
- Status = (symlink(FileName, LinkFileName) == 0);
+ if (symlink(FileName, LinkFileName) == 0) {
+ Error = 0;
+ } else {
+ Error = errno;
+ }
#else
- Status = 0;
+ Error = ENOSYS;
#endif
").
:- pragma foreign_proc("Erlang",
- make_symlink_2(FileName::in, LinkFileName::in, Status::out,
+ make_symlink_2(FileName::in, LinkFileName::in, Error::out,
_IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness],
@@ -11394,19 +11382,17 @@ make_symlink(FileName, LinkFileName, Result, !IO) :-
LinkFileNameStr = binary_to_list(LinkFileName),
case file:make_symlink(FileNameStr, LinkFileNameStr) of
ok ->
- Status = 1;
+ Error = ok;
{error, Reason} ->
- put('MR_io_exception', Reason),
- Status = 0
+ Error = {error, Reason}
end
").
read_symlink(FileName, Result, !IO) :-
( if have_symlinks then
- read_symlink_2(FileName, TargetFileName, Status, Error, !IO),
- ( if Status = 0 then
- make_err_msg(Error, "io.read_symlink failed: ", Msg, !IO),
- Result = error(make_io_error(Msg))
+ read_symlink_2(FileName, TargetFileName, Error, !IO),
+ ( if is_error(Error, "io.read_symlink failed: ", Message) then
+ Result = error(make_io_error(Message))
else
Result = ok(TargetFileName)
)
@@ -11415,13 +11401,12 @@ read_symlink(FileName, Result, !IO) :-
"io.read_symlink not supported on this platform"))
).
- % XXX return system_error only
-:- pred read_symlink_2(string::in, string::out, int::out,
- system_error::out, io::di, io::uo) is det.
+:- pred read_symlink_2(string::in, string::out, system_error::out,
+ io::di, io::uo) is det.
:- pragma foreign_proc("C",
- read_symlink_2(FileName::in, TargetFileName::out, Status::out,
- Error::out, _IO0::di, _IO::uo),
+ read_symlink_2(FileName::in, TargetFileName::out, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness, no_sharing],
"
@@ -11441,82 +11426,74 @@ read_symlink(FileName, Result, !IO) :-
do {
buffer_size2 *= 2;
buffer2 = MR_RESIZE_ARRAY(buffer2, char, buffer_size2);
- num_chars = readlink(FileName, buffer2, PATH_MAX);
+ num_chars = readlink(FileName, buffer2, buffer_size2);
} while (num_chars == buffer_size2);
+ /* Invariant: num_chars < buffer_size2 */
+
if (num_chars == -1) {
- Error = errno;
TargetFileName = MR_make_string_const("""");
- Status = 0;
+ Error = errno;
} else {
buffer2[num_chars] = '\\0';
MR_make_aligned_string_copy_msg(TargetFileName, buffer2,
MR_ALLOC_ID);
- Status = 1;
+ Error = 0;
}
MR_free(buffer2);
} else if (num_chars == -1) {
TargetFileName = MR_make_string_const("""");
Error = errno;
- Status = 0;
} else {
buffer[num_chars] = '\\0';
MR_make_aligned_string_copy_msg(TargetFileName, buffer, MR_ALLOC_ID);
- Status = 1;
+ Error = 0;
}
#else /* !MR_HAVE_READLINK */
- /*
- ** We can't just return NULL here, because otherwise mdb will break
- ** when it tries to print the string.
- */
TargetFileName = MR_make_string_const("""");
- Status = 0;
+ Error = ENOSYS;
#endif
").
:- pragma foreign_proc("Erlang",
- read_symlink_2(FileName::in, TargetFileName::out, Status::out,
- Error::out, _IO0::di, _IO::uo),
+ read_symlink_2(FileName::in, TargetFileName::out, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
does_not_affect_liveness],
"
case file:read_link(binary_to_list(FileName)) of
{ok, TargetFileNameStr} ->
TargetFileName = list_to_binary(TargetFileNameStr),
- Status = 1,
- Error = <<>>;
+ Error = ok;
{error, Reason} ->
- Status = 0,
TargetFileName = <<>>,
- Error = list_to_binary(file:format_error(Reason))
+ Error = {error, Reason}
end
").
% Since io.have_symlinks will fail for Java, these procedures should never be
% called:
+% XXX Java 7 has createSymbolicLink, readSymbolicLink
:- pragma foreign_proc("Java",
- make_symlink_2(_FileName::in, _LinkFileName::in, _Status::out,
+ make_symlink_2(_FileName::in, _LinkFileName::in, Error::out,
_IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
may_not_duplicate],
"
- if (true) {
- throw new java.lang.RuntimeException(
- ""io.make_symlink_2 not implemented"");
- }
+ Error = new java.lang.UnsupportedOperationException(
+ ""io.make_symlink_2 not implemented"");
").
:- pragma foreign_proc("Java",
- read_symlink_2(_FileName::in, _TargetFileName::out, _Status::out,
- _Error::out, _IO0::di, _IO::uo),
+ read_symlink_2(_FileName::in, TargetFileName::out, Error::out,
+ _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
may_not_duplicate],
"
- if (true) {
- throw new java.lang.RuntimeException(
- ""io.read_symlink_2 not implemented"");
- }
+ TargetFileName = """";
+ Error = new java.lang.UnsupportedOperationException(
+ ""io.read_symlink_2 not implemented"");
").
%---------------------------------------------------------------------------%
diff --git a/tests/general/read_dir_regression.exp b/tests/general/read_dir_regression.exp
index d9ab05c..89a9d94 100644
--- a/tests/general/read_dir_regression.exp
+++ b/tests/general/read_dir_regression.exp
@@ -1 +1 @@
-open failed: can't open directory as file
+open failed: can't open input file: Is a directory
--
2.9.0
More information about the reviews
mailing list