[m-rev.] for review: bugfix for io.set_environment_var/4
Mark Brown
mark at mercurylang.org
Sun Apr 17 05:19:41 AEST 2016
Hi Peter,
Thanks for the review.
On Sat, Apr 16, 2016 at 1:28 PM, Peter Wang <novalazy at gmail.com> wrote:
> Since putenv is utterly broken, I suggest using setenv instead
> (and unsetenv if required).
The attached diff uses setenv on non-Windows platforms.
> Windows has SetEnvironmentVariable. I have not been able to find
> documentation stating whether it duplicates its input, which hopefully
> means it does.
> https://msdn.microsoft.com/en-us/library/windows/desktop/ms686206(v=vs.85).aspx
This doesn't work with _wgetenv. I gather this is because
SetEnvironmentVariable modifies the process environment in the kernel,
whereas _wgetenv/_wputenv uses a copy of the environment in the C
runtime library. But there's a _wputenv_s in the C runtime library
which behaves like setenv and copies the string, so on Windows I have
used that.
I'll commit this soon if there are no objections.
Mark
-------------- next part --------------
commit d4847102b9854e96d183d9480b9083b183e57b7a
Author: Mark Brown <mark at mercurylang.org>
Date: Fri Apr 15 22:11:13 2016 +1000
Fix bug with io.set_environment_var/4.
library/io.m:
Use setenv() and _wputenv_s() to set environment variables
on C backends. Unlike putenv() and _wputenv(), these can be
relied upon to copy their inputs, so the inputs can safely
be garbage collected after the call returns.
tests/hard_coded/Mmakefile:
tests/hard_coded/setenv.{m,exp}:
Test case. Only run in grades for which io.set_environment_var/4
succeeds.
diff --git a/library/io.m b/library/io.m
index 28fab1a..a243012 100644
--- a/library/io.m
+++ b/library/io.m
@@ -10228,7 +10228,7 @@ command_line_argument(_, "") :-
% io.getenv and io.setenv.
:- pragma foreign_decl("C", "
-#include <stdlib.h> /* for getenv() and putenv() */
+#include <stdlib.h> /* for getenv() and setenv() */
").
:- pragma foreign_proc("C",
@@ -10280,24 +10280,16 @@ command_line_argument(_, "") :-
end
").
-io.setenv(Var, Value) :-
- impure io.putenv(Var ++ "=" ++ Value).
-
- % io.putenv(VarString): If VarString is a string of the form "name=value",
- % sets the environment variable name to the specified value. Fails if
- % the operation does not work. This should only be called from io.setenv.
- %
-:- impure pred io.putenv(string::in) is semidet.
-
:- pragma foreign_proc("C",
- io.putenv(VarAndValue::in),
+ io.setenv(Var::in, Value::in),
[will_not_call_mercury, not_thread_safe, tabled_for_io,
does_not_affect_liveness, no_sharing],
"
#ifdef MR_WIN32
- SUCCESS_INDICATOR = (_wputenv(ML_utf8_to_wide(VarAndValue)) == 0);
+ SUCCESS_INDICATOR =
+ (_wputenv_s(ML_utf8_to_wide(Var), ML_utf8_to_wide(Value)) == 0);
#else
- SUCCESS_INDICATOR = (putenv(VarAndValue) == 0);
+ SUCCESS_INDICATOR = (setenv(Var, Value, 1) == 0);
#endif
").
@@ -10313,17 +10305,6 @@ io.setenv(Var, Value) :-
}
").
-:- pragma foreign_proc("C#",
- io.putenv(_VarAndValue::in),
- [will_not_call_mercury, tabled_for_io],
-"
- // This procedure should never be called, as io.setenv/2 has been
- // implemented directly for C#.
- // This implementation is included only to suppress warnings.
-
- throw new System.Exception(""io.putenv/1 not implemented for C#"");
-").
-
:- pragma foreign_proc("Java",
io.setenv(Var::in, Value::in),
[will_not_call_mercury, tabled_for_io, may_not_duplicate],
@@ -10335,18 +10316,6 @@ io.setenv(Var, Value) :-
SUCCESS_INDICATOR = false;
").
-:- pragma foreign_proc("Java",
- io.putenv(VarAndValue::in),
- [will_not_call_mercury, tabled_for_io, may_not_duplicate],
-"
- // This procedure should never be called, as io.setenv/2 has been
- // implemented directly for Java.
- // This implementation is included only to suppress warnings.
-
- io.ML_throw_io_error(
- ""io.putenv/1 not implemented for Java: "" + VarAndValue);
-").
-
:- pragma foreign_proc("Erlang",
io.setenv(Var::in, Value::in),
[will_not_call_mercury, tabled_for_io],
@@ -10357,17 +10326,6 @@ io.setenv(Var, Value) :-
SUCCESS_INDICATOR = true
").
-:- pragma foreign_proc("Erlang",
- io.putenv(VarAndValue::in),
- [will_not_call_mercury, tabled_for_io],
-"
- % This procedure should never be called, as io.setenv/2 has been
- % implemented directly for Erlang.
- % This implementation is included only to suppress warnings.
-
- throw({""io.putenv/1 not implemented for Erlang: "" ++ VarAndValue})
-").
-
%---------------------------------------------------------------------------%
make_temp(Name, !IO) :-
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 1e19c0e..0e6f519 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -707,6 +707,14 @@ else
endif
endif
+# Setting environment variables is not supported in Java grades.
+ifeq "$(filter java%,$(GRADE))" ""
+ SETENV_PROGS = \
+ setenv
+else
+ SETENV_PROGS =
+endif
+
# We currently test only a limited selection in grade java on this directory.
PROGS = \
$(ORDINARY_PROGS) \
@@ -727,7 +735,8 @@ PROGS = \
$(TRACE_GOAL_ENV_PROGS) \
$(CTGC_PROGS) \
$(BIG_DATA_PROGS) \
- $(CONC_PROGS)
+ $(CONC_PROGS) \
+ $(SETENV_PROGS)
#-----------------------------------------------------------------------------#
diff --git a/tests/hard_coded/setenv.exp b/tests/hard_coded/setenv.exp
new file mode 100644
index 0000000..154f429
--- /dev/null
+++ b/tests/hard_coded/setenv.exp
@@ -0,0 +1,3 @@
+Use mem: ok
+Use mem: ok
+Got value: bar
diff --git a/tests/hard_coded/setenv.m b/tests/hard_coded/setenv.m
new file mode 100644
index 0000000..95f0132
--- /dev/null
+++ b/tests/hard_coded/setenv.m
@@ -0,0 +1,44 @@
+:- module setenv.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+:- implementation.
+
+:- import_module gc.
+:- import_module int.
+:- import_module list.
+:- import_module maybe.
+:- import_module string.
+
+main(!IO) :-
+ io.set_environment_var("foo", "bar", !IO),
+
+ % Earlier versions of the Mercury library relied on putenv, which
+ % on many platforms requires that we don't garbage collect the
+ % string we pass it. This code tests we can handle that.
+ use_mem(1000000, !IO),
+ gc.garbage_collect(!IO),
+ use_mem(1000000, !IO),
+
+ io.get_environment_var("foo", Res, !IO),
+ (
+ Res = yes(Value),
+ io.write_string("Got value: " ++ Value ++ "\n", !IO)
+ ;
+ Res = no,
+ io.write_string("Failure!\n", !IO)
+ ).
+
+:- pred use_mem(int::in, io::di, io::uo) is det.
+
+use_mem(N, !IO) :-
+ io.write_string("Use mem: ", !IO),
+ ( if length(1 `..` N) = N then
+ io.write_string("ok\n", !IO)
+ else
+ io.write_string("hmm\n", !IO)
+ ).
+
More information about the reviews
mailing list