mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-17 10:23:46 +00:00
Implement the error handling proposals from February 2022 on the
mercury-users list, and August 2022 on the mercury-reviews list.
We add io.system_error to the public interface of io.m
and document what its foreign representation is for each backend.
We allow io.error to optionally contain an io.system_error value,
and provide predicates to retrieve the io.system_error from an io.error.
The user may then inspect the system error via foreign code.
We also provide a predicate that takes an io.error and returns a name
for the system error it contains (if any). This makes it relatively easy
for Mercury programs to check for specific error conditions.
By returning platform-specific (actually, implementation-dependent)
error names, we are pushing the responsibility of mapping strings to
error conditions onto the application programmer. On the other hand, it
is not practical for us to map all possible system-specific error codes
to some common set of values. We could do it for a small set of common
error codes/exceptions, perhaps.
The standard library will construct io.error values containing
io.system_errors. However, we do not yet provide a facility for user
code to do the same.
library/io.m:
Move io.system_error to the public interface.
Change the internal representation of io.error to support containing
a io.system_error. An io.system_error may originate from an errno
value or a Windows system error code; the constructor distinguishes
those cases.
Add predicates to retrieve a system_error from io.error.
Add predicate to return the name of the system error in an io.error.
Replace make_err_msg with make_io_error_from_system_error.
Replace make_maybe_win32_err_msg with
make_io_error_from_maybe_win32_error.
Delete ML_make_err_msg and ML_make_win32_err_msg macros.
browser/listing.m:
library/bitmap.m:
library/dir.m:
library/io.call_system.m:
library/io.environment.m:
library/io.file.m:
library/io.text_read.m:
mdbcomp/program_representation.m:
Conform to changes.
Leave comments for followup work.
tools/generate_errno_name:
tools/generate_windows_error_name:
Add scripts to generate mercury_errno_name.c and
mercury_windows_error_name.c.
runtime/Mmakefile:
runtime/mercury_errno_name.c:
runtime/mercury_errno_name.h:
runtime/mercury_windows_error_name.c:
runtime/mercury_windows_error_name.h:
Add MR_errno_name() and MR_win32_error_name() functions,
used by io.m to convert error codes to string names.
tests/hard_coded/null_char.exp:
Update expected output.
600 lines
20 KiB
Mathematica
600 lines
20 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2005-2007, 2010-2011 The University of Melbourne.
|
|
% Copyright (C) 2015, 2017-2018, 2020, 2022 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: listing.m.
|
|
% Author: Ralph Becket <rafe@cs.mu.oz.au>
|
|
%
|
|
% Support for providing file listing functionality in the debugger.
|
|
%
|
|
% Unfortunately, scanning large files such as library/io.m byte-by-byte
|
|
% in a debugging grade is likely to exhaust the stack, because debugging
|
|
% grades do not support tail recursion. Instead we have to handle this
|
|
% aspect using a bit of C code.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module mdb.listing.
|
|
:- interface.
|
|
|
|
:- import_module io.
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type search_path.
|
|
:- type line_no == int.
|
|
:- type path_name == string.
|
|
:- type file_name == string.
|
|
:- type c_file_ptr. % For passing `FILE *' arguments.
|
|
|
|
% Construct an empty search_path structure.
|
|
%
|
|
:- func new_list_path = search_path.
|
|
|
|
% Get/set/clear the stack of directories searched for FileName matches by
|
|
% list_file/7.
|
|
%
|
|
:- func get_list_path(search_path::in) = (list(path_name)::out) is det.
|
|
:- pred set_list_path(list(path_name)::in,
|
|
search_path::in, search_path::out) is det.
|
|
:- pred clear_list_path(search_path::in, search_path::out) is det.
|
|
|
|
% push_list_path(Dir, !Path):
|
|
%
|
|
% Push Dir on to the stack of directories searched for FileName
|
|
% matches by list_file/7.
|
|
%
|
|
:- pred push_list_path(path_name::in, search_path::in, search_path::out)
|
|
is det.
|
|
|
|
% pop_list_path(!Path):
|
|
%
|
|
% Pop the last Dir pushed on to the stack of directories.
|
|
% Does nothing if the search path stack is empty.
|
|
%
|
|
:- pred pop_list_path(search_path::in, search_path::out) is det.
|
|
|
|
% list_file(OutStreamC, ErrorStreamC, FileName, FirstLine, LastLine,
|
|
% MarkLine, Path, !IO):
|
|
%
|
|
% Print, on OutStreamC, the lines from FileName with numbers in the range
|
|
% FirstLine..LastLine (the first line is numbered 1). We mark the line
|
|
% numbered MarkLine with a chevron; we indent all other lines
|
|
% appropriately.
|
|
%
|
|
% We search for the file matching FileName by first looking in the current
|
|
% working directory or, failing that, by prepending each Dir on the
|
|
% search path stack in turn until a match is found. If no match is found,
|
|
% we print an error message.
|
|
%
|
|
% We report any errors on ErrorStreamC.
|
|
%
|
|
:- pred list_file(c_file_ptr::in, c_file_ptr::in, file_name::in, line_no::in,
|
|
line_no::in, line_no::in, search_path::in, io::di, io::uo) is det.
|
|
|
|
% As above, but implemented without foreign code. This is used by the
|
|
% source-to-source debugger, which does not enable debugging in the
|
|
% standard library, so does not suffer the problem of excessive
|
|
% stack usage.
|
|
%
|
|
% XXX Stack usage should not be a problem if we implemented this predicate
|
|
% using either read_named_file_as_line, or read_file_as_string and
|
|
% split_into_lines.
|
|
%
|
|
:- pred list_file_portable(io.text_output_stream::in,
|
|
io.text_output_stream::in, file_name::in,
|
|
line_no::in, line_no::in, line_no::in, search_path::in,
|
|
io::di, io::uo) is det.
|
|
|
|
% list_file_with_command(OutStreamC, ErrorStreamC, FileName,
|
|
% FirstLine, LastLine, MarkLine, Path, !IO):
|
|
%
|
|
% Like list_file, but invokes an external command to print the source
|
|
% listing. The command is passed the four arguments:
|
|
%
|
|
% FileName, FirstLine, LastLine, MarkLine
|
|
%
|
|
% The command should print all the lines from the first to the last,
|
|
% both inclusive, with the current line marked (or highlighted) in
|
|
% some fashion to standard output, and report any errors to standard error.
|
|
%
|
|
:- pred list_file_with_command(c_file_ptr::in, c_file_ptr::in, string::in,
|
|
file_name::in, line_no::in, line_no::in, line_no::in, search_path::in,
|
|
io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module bool.
|
|
:- import_module dir.
|
|
:- import_module int.
|
|
:- import_module io.call_system.
|
|
:- import_module io.file.
|
|
:- import_module maybe.
|
|
:- import_module string.
|
|
:- import_module type_desc.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type search_path == list(path_name).
|
|
|
|
:- pragma foreign_type("C", c_file_ptr, "FILE *", [can_pass_as_mercury_type]).
|
|
% stub.
|
|
:- pragma foreign_type("C#", c_file_ptr, "object").
|
|
:- pragma foreign_type("Java", c_file_ptr, "java.lang.Object").
|
|
|
|
% These predicates are called from trace/mercury_trace_internal.c.
|
|
%
|
|
:- pragma foreign_export("C", new_list_path = out,
|
|
"ML_LISTING_new_list_path").
|
|
:- pragma foreign_export("C", get_list_path(in) = out,
|
|
"ML_LISTING_get_list_path").
|
|
:- pragma foreign_export("C", set_list_path(in, in, out),
|
|
"ML_LISTING_set_list_path").
|
|
:- pragma foreign_export("C", clear_list_path(in, out),
|
|
"ML_LISTING_clear_list_path").
|
|
:- pragma foreign_export("C", push_list_path(in, in, out),
|
|
"ML_LISTING_push_list_path").
|
|
:- pragma foreign_export("C", pop_list_path(in, out),
|
|
"ML_LISTING_pop_list_path").
|
|
:- pragma foreign_export("C", list_file(in, in, in, in, in, in, in, di, uo),
|
|
"ML_LISTING_list_file").
|
|
:- pragma foreign_export("C", list_file_with_command(in, in, in, in, in, in,
|
|
in, in, di, uo), "ML_LISTING_list_file_with_command").
|
|
|
|
:- func listing_type = type_desc.
|
|
:- pragma foreign_export("C", listing_type = out, "ML_LISTING_listing_type").
|
|
|
|
listing_type = type_of(Path) :-
|
|
clear_list_path(Path @ [], _).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
new_list_path = [].
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_list_path(Path) = Path.
|
|
|
|
set_list_path(Dirs, _, Dirs).
|
|
|
|
clear_list_path(_, []).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
push_list_path(Dir, Path, [Dir | Path]).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
pop_list_path([], []).
|
|
pop_list_path([_ | Path], Path).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
list_file(OutStreamC, ErrorStreamC, FileName, FirstLine, LastLine, MarkLine,
|
|
Path, !IO) :-
|
|
( if dir.path_name_is_absolute(FileName) then
|
|
io.open_input(FileName, Result0, !IO),
|
|
(
|
|
Result0 = ok(InStream),
|
|
InStreamC = mercury_stream_to_c_file_ptr(InStream),
|
|
print_lines_in_range_c(InStreamC, OutStreamC,
|
|
1, FirstLine, LastLine, MarkLine, !IO),
|
|
io.close_input(InStream, !IO)
|
|
;
|
|
Result0 = error(Error),
|
|
ErrorMsg = io.error_message(Error),
|
|
write_to_c_file(ErrorStreamC,
|
|
string.format("mdb: cannot open file %s: %s\n",
|
|
[s(FileName), s(ErrorMsg)]), !IO)
|
|
)
|
|
else
|
|
find_and_open_file([dir.this_directory | Path], FileName, Result, !IO),
|
|
(
|
|
Result = yes(InStream),
|
|
InStreamC = mercury_stream_to_c_file_ptr(InStream),
|
|
print_lines_in_range_c(InStreamC, OutStreamC,
|
|
1, FirstLine, LastLine, MarkLine, !IO),
|
|
io.close_input(InStream, !IO)
|
|
;
|
|
Result = no,
|
|
string.format("mdb: cannot find file %s\n", [s(FileName)], Msg),
|
|
write_to_c_file(ErrorStreamC, Msg, !IO)
|
|
)
|
|
).
|
|
|
|
:- func mercury_stream_to_c_file_ptr(io.text_input_stream) = c_file_ptr.
|
|
|
|
:- pragma foreign_proc("C",
|
|
mercury_stream_to_c_file_ptr(InStream::in) = (InStreamC::out),
|
|
[promise_pure, thread_safe, will_not_call_mercury],
|
|
"
|
|
InStreamC = MR_file(*(MR_unwrap_input_stream(InStream)));
|
|
").
|
|
|
|
:- pred write_to_c_file(c_file_ptr::in, string::in, io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
write_to_c_file(ErrorStreamC::in, Str::in, _IO0::di, _IO::uo),
|
|
[promise_pure, thread_safe, will_not_call_mercury],
|
|
"
|
|
fputs(Str, (FILE *) ErrorStreamC);
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
list_file_portable(OutStream, ErrorStream, FileName, FirstLine, LastLine,
|
|
MarkLine, Path, !IO) :-
|
|
( if dir.path_name_is_absolute(FileName) then
|
|
io.open_input(FileName, Result0, !IO),
|
|
(
|
|
Result0 = ok(InStream),
|
|
print_lines_in_range_m(InStream, OutStream,
|
|
1, FirstLine, LastLine, MarkLine, !IO),
|
|
io.close_input(InStream, !IO)
|
|
;
|
|
Result0 = error(Error),
|
|
ErrorMsg = io.error_message(Error),
|
|
io.format(ErrorStream, "mdb: cannot open file %s: %s\n",
|
|
[s(FileName), s(ErrorMsg)], !IO)
|
|
)
|
|
else
|
|
find_and_open_file([dir.this_directory | Path], FileName, Result, !IO),
|
|
(
|
|
Result = yes(InStream),
|
|
print_lines_in_range_m(InStream, OutStream,
|
|
1, FirstLine, LastLine, MarkLine, !IO),
|
|
io.close_input(InStream, !IO)
|
|
;
|
|
Result = no,
|
|
io.format(ErrorStream, "mdb: cannot find file %s\n",
|
|
[s(FileName)], !IO)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
list_file_with_command(OutStream, ErrorStream, Command, FileName, FirstLine,
|
|
LastLine, MarkLine, Path, !IO) :-
|
|
LineArgs = [string.from_int(FirstLine), string.from_int(LastLine),
|
|
string.from_int(MarkLine)],
|
|
( if dir.path_name_is_absolute(FileName) then
|
|
FindResult = yes(FileName)
|
|
else
|
|
find_file([dir.this_directory | Path], FileName, FindResult, !IO)
|
|
),
|
|
(
|
|
FindResult = yes(FoundFileName),
|
|
execute_command_with_redirects(Command, [FoundFileName | LineArgs],
|
|
OutStream, ErrorStream, CallResult, !IO),
|
|
(
|
|
CallResult = ok
|
|
;
|
|
CallResult = error(Error),
|
|
write_to_c_file(ErrorStream,
|
|
string.format("mdb: %s: %s\n", [s(Command), s(Error)]), !IO)
|
|
)
|
|
;
|
|
FindResult = no,
|
|
write_to_c_file(ErrorStream,
|
|
string.format("mdb: cannot find file %s\n", [s(FileName)]), !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Search for the first file with the given name on the search path
|
|
% that we can open for reading and return the complete file name
|
|
% (including the path component) and input stream handle.
|
|
%
|
|
:- pred find_and_open_file(search_path::in, file_name::in,
|
|
maybe(io.text_input_stream)::out, io::di, io::uo) is det.
|
|
|
|
find_and_open_file([], _, no, !IO).
|
|
find_and_open_file([Dir | Path], FileName, Result, !IO) :-
|
|
io.open_input(Dir / FileName, Result0, !IO),
|
|
(
|
|
Result0 = ok(InStream),
|
|
Result = yes(InStream)
|
|
;
|
|
Result0 = error(_),
|
|
find_and_open_file(Path, FileName, Result, !IO)
|
|
).
|
|
|
|
:- pred find_file(search_path::in, file_name::in, maybe(file_name)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
find_file([], _, no, !IO).
|
|
find_file([Dir | Path], FileName0, Result, !IO) :-
|
|
FileName = Dir / FileName0,
|
|
FollowSymLinks = yes,
|
|
io.file.file_type(FollowSymLinks, FileName, FileTypeRes, !IO),
|
|
(
|
|
FileTypeRes = ok(FileType),
|
|
(
|
|
( FileType = regular_file
|
|
; FileType = symbolic_link
|
|
; FileType = named_pipe
|
|
; FileType = socket
|
|
; FileType = character_device
|
|
; FileType = block_device
|
|
; FileType = message_queue
|
|
; FileType = semaphore
|
|
; FileType = shared_memory
|
|
; FileType = unknown
|
|
),
|
|
Result = yes(FileName)
|
|
;
|
|
FileType = directory,
|
|
% It is debatable whether we should continue searching.
|
|
find_file(Path, FileName0, Result, !IO)
|
|
)
|
|
;
|
|
FileTypeRes = error(_),
|
|
find_file(Path, FileName0, Result, !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% print_lines_in_range(InStreamC, OutStreamC,
|
|
% ThisLine, FirstLine, LastLine, MarkLine, !IO):
|
|
%
|
|
% Print the lines numbered FirstLine to LastLine from InStreamC
|
|
% on OutStreamC (the current line number is taken as ThisLine).
|
|
% Each line is printed indented with " ", except for the line
|
|
% numbered MarkLine, if it occurs in the range FirstLine .. LastLine,
|
|
% which is indented with "> ".
|
|
%
|
|
:- pred print_lines_in_range_c(c_file_ptr::in, c_file_ptr::in,
|
|
line_no::in, line_no::in, line_no::in, line_no::in, io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
print_lines_in_range_c(InStreamC::in, OutStreamC::in, ThisLine::in,
|
|
FirstLine::in, LastLine::in, MarkLine::in, _IO0::di, _IO::uo),
|
|
[promise_pure, thread_safe, will_not_call_mercury],
|
|
"
|
|
if (FirstLine <= ThisLine && ThisLine <= LastLine) {
|
|
const char *s = (ThisLine == MarkLine) ? \"> \" : \" \";
|
|
fputs(s, (FILE *) OutStreamC);
|
|
}
|
|
while (ThisLine <= LastLine) {
|
|
int c = fgetc((FILE *) InStreamC);
|
|
if (c == EOF) {
|
|
fputc('\\n', (FILE *) OutStreamC);
|
|
break;
|
|
}
|
|
if (FirstLine <= ThisLine) {
|
|
fputc(c, (FILE *) OutStreamC);
|
|
}
|
|
if (c == '\\n') {
|
|
ThisLine++;
|
|
if (FirstLine <= ThisLine && ThisLine <= LastLine) {
|
|
const char *s = (ThisLine == MarkLine) ? \"> \" : \" \";
|
|
fputs(s, (FILE *) OutStreamC);
|
|
}
|
|
}
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred print_lines_in_range_m(io.text_input_stream::in,
|
|
io.text_output_stream::in,
|
|
line_no::in, line_no::in, line_no::in, line_no::in, io::di, io::uo) is det.
|
|
|
|
print_lines_in_range_m(InStream, OutStream, ThisLine, FirstLine, LastLine,
|
|
MarkLine, !IO) :-
|
|
io.read_line_as_string(InStream, Res, !IO),
|
|
(
|
|
Res = ok(Line),
|
|
( if FirstLine =< ThisLine, ThisLine =< LastLine then
|
|
( if ThisLine = MarkLine then
|
|
io.write_string(OutStream, "> ", !IO)
|
|
else
|
|
io.write_string(OutStream, " ", !IO)
|
|
),
|
|
io.write_string(OutStream, Line, !IO)
|
|
else
|
|
true
|
|
),
|
|
print_lines_in_range_m(InStream, OutStream, ThisLine + 1, FirstLine,
|
|
LastLine, MarkLine, !IO)
|
|
;
|
|
Res = eof
|
|
;
|
|
Res = error(Error),
|
|
io.format(OutStream, "Error: %s\n", [s(io.error_message(Error))], !IO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred execute_command_with_redirects(string::in, list(string)::in,
|
|
c_file_ptr::in, c_file_ptr::in, maybe_error::out, io::di, io::uo) is det.
|
|
|
|
execute_command_with_redirects(Prog, Args, OutStreamC, ErrorStreamC,
|
|
Result, !IO) :-
|
|
do_posix_spawnp(Prog, length(Args), Args, OutStreamC, ErrorStreamC, Status,
|
|
Error0, !IO),
|
|
( if Status = -1 then
|
|
io.make_io_error_from_system_error(Error0,
|
|
"error invoking system command: ", IOError, !IO),
|
|
Result = error(io.error_message(IOError))
|
|
else if Status = -2 then
|
|
Result = error("posix_spawn not supported on this platform")
|
|
else
|
|
Result0 = io.call_system.decode_system_command_exit_code(Status),
|
|
(
|
|
Result0 = ok(exited(ExitStatus)),
|
|
( if ExitStatus = 0 then
|
|
Result = ok
|
|
else
|
|
Result = error("exit status " ++ string.from_int(ExitStatus))
|
|
)
|
|
;
|
|
Result0 = ok(signalled(Signal)),
|
|
Result = error("received signal " ++ string.from_int(Signal))
|
|
;
|
|
Result0 = error(Error),
|
|
Result = error(io.error_message(Error))
|
|
)
|
|
).
|
|
|
|
:- pred do_posix_spawnp(string::in, int::in, list(string)::in,
|
|
c_file_ptr::in, c_file_ptr::in, int::out, io.system_error::out,
|
|
io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
do_posix_spawnp(Prog::in, NumArgs::in, Args::in,
|
|
OutStreamC::in, ErrorStreamC::in, Status::out, Error::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
int error;
|
|
|
|
Status = do_posix_spawnp(Prog, NumArgs, Args,
|
|
fileno(OutStreamC), fileno(ErrorStreamC), &error);
|
|
if (Status == -1) {
|
|
Error = error;
|
|
} else {
|
|
Error = 0;
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_decl("C", local, "
|
|
// See library/io.m regarding declaration of the environ global variable.
|
|
#if defined(MR_HAVE_SPAWN_H) && defined(MR_HAVE_ENVIRON)
|
|
#include <spawn.h>
|
|
|
|
#if defined(MR_MAC_OSX)
|
|
#include <crt_externs.h>
|
|
#else
|
|
extern char **environ;
|
|
#endif
|
|
#endif
|
|
|
|
static int do_posix_spawnp(MR_String prog, int num_args, MR_Word args,
|
|
const int outfd, const int errfd, int *ret_errno);
|
|
").
|
|
|
|
:- pragma foreign_code("C", "
|
|
static int
|
|
do_posix_spawnp(MR_String prog, int num_args, MR_Word args,
|
|
const int outfd, const int errfd, int *ret_errno)
|
|
{
|
|
#if defined(MR_HAVE_POSIX_SPAWN) && defined(MR_HAVE_ENVIRON)
|
|
|
|
pid_t pid;
|
|
char **argv;
|
|
posix_spawn_file_actions_t file_actions;
|
|
posix_spawnattr_t attr;
|
|
int rc;
|
|
int status;
|
|
int i;
|
|
|
|
argv = MR_GC_NEW_ARRAY(char *, 1 + num_args + 1);
|
|
argv[0] = prog;
|
|
for (i = 1; i <= num_args; i++) {
|
|
argv[i] = (MR_String) MR_list_head(args);
|
|
args = MR_list_tail(args);
|
|
}
|
|
argv[i] = NULL;
|
|
|
|
rc = posix_spawnattr_init(&attr);
|
|
if (rc == -1) {
|
|
*ret_errno = errno;
|
|
return -1;
|
|
}
|
|
|
|
rc = posix_spawn_file_actions_init(&file_actions);
|
|
if (rc == -1) {
|
|
*ret_errno = errno;
|
|
goto error_cleanup_attr;
|
|
}
|
|
|
|
if (outfd != STDOUT_FILENO) {
|
|
// Redirect standard output in child process.
|
|
rc = posix_spawn_file_actions_adddup2(&file_actions,
|
|
outfd, STDOUT_FILENO);
|
|
if (rc == -1) {
|
|
*ret_errno = errno;
|
|
goto error_cleanup_fa_attr;
|
|
}
|
|
// Close outfd in child process.
|
|
rc = posix_spawn_file_actions_addclose(&file_actions, outfd);
|
|
if (rc == -1) {
|
|
*ret_errno = errno;
|
|
goto error_cleanup_fa_attr;
|
|
}
|
|
}
|
|
|
|
if (errfd != STDERR_FILENO) {
|
|
// Redirect standard error in child process.
|
|
rc = posix_spawn_file_actions_adddup2(&file_actions,
|
|
errfd, STDERR_FILENO);
|
|
if (rc == -1) {
|
|
*ret_errno = errno;
|
|
goto error_cleanup_fa_attr;
|
|
}
|
|
// Close errfd in child process.
|
|
rc = posix_spawn_file_actions_addclose(&file_actions, errfd);
|
|
if (rc == -1) {
|
|
*ret_errno = errno;
|
|
goto error_cleanup_fa_attr;
|
|
}
|
|
}
|
|
|
|
#ifdef MR_MAC_OSX
|
|
rc = posix_spawnp(&pid, prog, &file_actions, &attr, argv,
|
|
*_NSGetEnviron());
|
|
#else
|
|
rc = posix_spawnp(&pid, prog, &file_actions, &attr, argv,
|
|
environ);
|
|
#endif
|
|
|
|
if (rc == -1) {
|
|
// Spawn failed.
|
|
*ret_errno = errno;
|
|
goto error_cleanup_fa_attr;
|
|
}
|
|
|
|
posix_spawnattr_destroy(&attr);
|
|
posix_spawn_file_actions_destroy(&file_actions);
|
|
|
|
// Wait for the spawned process to exit.
|
|
do {
|
|
rc = waitpid(pid, &status, 0);
|
|
} while (rc == -1 && MR_is_eintr(errno));
|
|
if (rc == -1) {
|
|
*ret_errno = errno;
|
|
return -1;
|
|
}
|
|
*ret_errno = 0;
|
|
return status;
|
|
|
|
error_cleanup_fa_attr:
|
|
posix_spawn_file_actions_destroy(&file_actions);
|
|
error_cleanup_attr:
|
|
posix_spawnattr_destroy(&attr);
|
|
return -1;
|
|
|
|
#else // not (defined(MR_HAVE_POSIX_SPAWN) && defined(MR_HAVE_ENVIRON))
|
|
|
|
*ret_errno = ENOEXEC;
|
|
return -2;
|
|
|
|
#endif // not (defined(MR_HAVE_POSIX_SPAWN) && defined(MR_HAVE_ENVIRON))
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module mdb.listing.
|
|
%---------------------------------------------------------------------------%
|