aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bootstrap/lib/kernel/ebin/inet_dns.beambin19772 -> 19768 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_sets.beambin8284 -> 8432 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_trees.beambin4988 -> 5176 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/maps.beambin2304 -> 2628 bytes
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c16
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml1
-rw-r--r--lib/dialyzer/src/dialyzer.erl39
-rw-r--r--lib/dialyzer/test/plt_SUITE.erl85
-rw-r--r--lib/eldap/doc/src/eldap.xml4
-rw-r--r--lib/erl_docgen/vsn.mk2
-rw-r--r--lib/eunit/doc/overview.edoc15
-rw-r--r--lib/eunit/include/eunit.hrl20
-rw-r--r--lib/inets/doc/src/notes.xml17
-rw-r--r--lib/inets/src/http_server/httpd_request.erl8
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java3
-rw-r--r--lib/kernel/src/inet_tcp_dist.erl2
-rw-r--r--lib/orber/src/orber.app.src2
-rw-r--r--lib/ssh/doc/src/notes.xml19
-rw-r--r--lib/ssh/doc/src/ssh.xml62
-rw-r--r--lib/ssh/doc/src/ssh_channel.xml32
-rw-r--r--lib/ssh/doc/src/ssh_client_key_api.xml20
-rw-r--r--lib/ssh/doc/src/ssh_connection.xml42
-rw-r--r--lib/ssh/doc/src/ssh_server_key_api.xml24
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml4
-rw-r--r--lib/ssh/doc/src/ssh_sftpd.xml16
-rw-r--r--lib/ssh/src/ssh.erl4
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl12
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl90
-rw-r--r--lib/ssl/doc/src/ssl.xml12
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache_api.xml8
-rw-r--r--lib/ssl/doc/src/ssl_session_cache_api.xml20
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl2
-rw-r--r--lib/stdlib/doc/src/c.xml8
-rw-r--r--lib/stdlib/doc/src/gb_sets.xml13
-rw-r--r--lib/stdlib/doc/src/gb_trees.xml13
-rw-r--r--lib/stdlib/doc/src/maps.xml22
-rw-r--r--lib/stdlib/src/c.erl23
-rw-r--r--lib/stdlib/src/gb_sets.erl24
-rw-r--r--lib/stdlib/src/gb_trees.erl31
-rw-r--r--lib/stdlib/src/maps.erl21
-rw-r--r--lib/stdlib/src/shell_default.erl3
-rw-r--r--lib/stdlib/test/dict_SUITE.erl50
-rw-r--r--lib/stdlib/test/dict_test_lib.erl5
-rw-r--r--lib/stdlib/test/maps_SUITE.erl14
-rw-r--r--lib/stdlib/test/sets_SUITE.erl44
-rw-r--r--lib/stdlib/test/sets_test_lib.erl5
-rw-r--r--lib/test_server/src/erl2html2.erl18
-rw-r--r--lib/test_server/test/erl2html2_SUITE.erl66
-rw-r--r--lib/test_server/test/erl2html2_SUITE_data/include/header3.hrl1
-rw-r--r--lib/test_server/test/erl2html2_SUITE_data/m1.erl6
-rw-r--r--lib/tools/doc/src/cprof.xml2
-rw-r--r--otp_versions.table1
53 files changed, 726 insertions, 227 deletions
diff --git a/bootstrap/lib/kernel/ebin/inet_dns.beam b/bootstrap/lib/kernel/ebin/inet_dns.beam
index 6d04559d9a..bd625968a2 100644
--- a/bootstrap/lib/kernel/ebin/inet_dns.beam
+++ b/bootstrap/lib/kernel/ebin/inet_dns.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gb_sets.beam b/bootstrap/lib/stdlib/ebin/gb_sets.beam
index cf763f51a0..71da2376ba 100644
--- a/bootstrap/lib/stdlib/ebin/gb_sets.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_sets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gb_trees.beam b/bootstrap/lib/stdlib/ebin/gb_trees.beam
index 6660dcb787..db59d5af19 100644
--- a/bootstrap/lib/stdlib/ebin/gb_trees.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_trees.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/maps.beam b/bootstrap/lib/stdlib/ebin/maps.beam
index 5209c7cfd8..d1aa8bb9dd 100644
--- a/bootstrap/lib/stdlib/ebin/maps.beam
+++ b/bootstrap/lib/stdlib/ebin/maps.beam
Binary files differ
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
index 5ad92dad02..d050748703 100644
--- a/erts/emulator/sys/unix/erl_child_setup.c
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -55,7 +55,7 @@ void sys_sigrelease(int sig)
#endif /* !SIG_SIGSET */
#if defined(__ANDROID__)
-int __system_properties_fd(void);
+static int system_properties_fd(void);
#endif /* __ANDROID__ */
#if defined(__ANDROID__)
@@ -104,9 +104,12 @@ main(int argc, char *argv[])
#if defined(HAVE_CLOSEFROM)
closefrom(from);
#elif defined(__ANDROID__)
- for (i = from; i <= to; i++) {
- if (i!=__system_properties_fd)
- (void) close(i);
+ if (from <= to) {
+ int spfd = system_properties_fd();
+ for (i = from; i <= to; i++) {
+ if (i != spfd)
+ (void) close(i);
+ }
}
#else
for (i = from; i <= to; i++)
@@ -143,9 +146,9 @@ main(int argc, char *argv[])
}
#if defined(__ANDROID__)
-int __system_properties_fd(void)
+static int system_properties_fd(void)
{
- int s, fd;
+ int fd;
char *env;
env = getenv("ANDROID_PROPERTY_WORKSPACE");
@@ -156,4 +159,3 @@ int __system_properties_fd(void)
return fd;
}
#endif /* __ANDROID__ */
-
diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index 2a8bf6edcc..5f52906625 100644
--- a/lib/dialyzer/doc/src/dialyzer.xml
+++ b/lib/dialyzer/doc/src/dialyzer.xml
@@ -368,6 +368,7 @@ Option :: {files, [Filename :: string()]}
| {include_dirs, [DirName :: string()]}
| {output_file, FileName :: string()}
| {output_plt, FileName :: string()}
+ | {check_plt, boolean()},
| {analysis_type, 'succ_typings' |
'plt_add' |
'plt_build' |
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index c9e7da9ef0..c8537e3bd8 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -162,14 +162,7 @@ run(Opts) ->
{error, Msg} ->
throw({dialyzer_error, Msg});
OptsRecord ->
- case OptsRecord#options.check_plt of
- true ->
- case cl_check_init(OptsRecord) of
- {ok, ?RET_NOTHING_SUSPICIOUS} -> ok;
- {error, ErrorMsg1} -> throw({dialyzer_error, ErrorMsg1})
- end;
- false -> ok
- end,
+ ok = check_init(OptsRecord),
case dialyzer_cl:start(OptsRecord) of
{?RET_DISCREPANCIES, Warnings} -> Warnings;
{?RET_NOTHING_SUSPICIOUS, _} -> []
@@ -179,6 +172,16 @@ run(Opts) ->
erlang:error({dialyzer_error, lists:flatten(ErrorMsg)})
end.
+check_init(#options{analysis_type = plt_check}) ->
+ ok;
+check_init(#options{check_plt = true} = OptsRecord) ->
+ case cl_check_init(OptsRecord) of
+ {ok, _} -> ok;
+ {error, Msg} -> throw({dialyzer_error, Msg})
+ end;
+check_init(#options{check_plt = false}) ->
+ ok.
+
internal_gui(OptsRecord) ->
F = fun() ->
dialyzer_gui_wx:start(OptsRecord),
@@ -199,17 +202,13 @@ gui(Opts) ->
throw({dialyzer_error, Msg});
OptsRecord ->
ok = check_gui_options(OptsRecord),
- case cl_check_init(OptsRecord) of
- {ok, ?RET_NOTHING_SUSPICIOUS} ->
- F = fun() ->
- dialyzer_gui_wx:start(OptsRecord)
- end,
- case doit(F) of
- {ok, _} -> ok;
- {error, Msg} -> throw({dialyzer_error, Msg})
- end;
- {error, ErrorMsg1} ->
- throw({dialyzer_error, ErrorMsg1})
+ ok = check_init(OptsRecord),
+ F = fun() ->
+ dialyzer_gui_wx:start(OptsRecord)
+ end,
+ case doit(F) of
+ {ok, _} -> ok;
+ {error, Msg} -> throw({dialyzer_error, Msg})
end
catch
throw:{dialyzer_error, ErrorMsg} ->
diff --git a/lib/dialyzer/test/plt_SUITE.erl b/lib/dialyzer/test/plt_SUITE.erl
index ef4cdc57f0..ecbac14e5d 100644
--- a/lib/dialyzer/test/plt_SUITE.erl
+++ b/lib/dialyzer/test/plt_SUITE.erl
@@ -6,12 +6,13 @@
-include_lib("common_test/include/ct.hrl").
-include("dialyzer_test_constants.hrl").
--export([suite/0, all/0, build_plt/1, beam_tests/1, update_plt/1]).
+-export([suite/0, all/0, build_plt/1, beam_tests/1, update_plt/1,
+ run_plt_check/1, run_succ_typings/1]).
suite() ->
[{timetrap, ?plt_timeout}].
-all() -> [build_plt, beam_tests, update_plt].
+all() -> [build_plt, beam_tests, update_plt, run_plt_check, run_succ_typings].
build_plt(Config) ->
OutDir = ?config(priv_dir, Config),
@@ -37,14 +38,76 @@ beam_tests(Config) when is_list(Config) ->
">>,
Opts = [no_auto_import],
{ok, BeamFile} = compile(Config, Prog, no_auto_import, Opts),
- [] = run_dialyzer([BeamFile]),
+ [] = run_dialyzer(plt_build, [BeamFile], []),
ok.
-run_dialyzer(Files) ->
- dialyzer:run([{analysis_type, plt_build},
- {files, Files},
- {from, byte_code},
- {check_plt, false}]).
+run_plt_check(Config) when is_list(Config) ->
+ Mod1 = <<"
+ -module(run_plt_check1).
+ ">>,
+
+ Mod2A = <<"
+ -module(run_plt_check2).
+ ">>,
+
+ {ok, BeamFile1} = compile(Config, Mod1, run_plt_check1, []),
+ {ok, BeamFile2} = compile(Config, Mod2A, run_plt_check2, []),
+ [] = run_dialyzer(plt_build, [BeamFile1, BeamFile2], []),
+
+ Mod2B = <<"
+ -module(run_plt_check2).
+
+ -export([call/1]).
+
+ call(X) -> run_plt_check1:call(X).
+ ">>,
+
+ {ok, BeamFile2} = compile(Config, Mod2B, run_plt_check2, []),
+
+ % callgraph warning as run_plt_check2:call/1 makes a call to unexported
+ % function run_plt_check1:call/1.
+ [_] = run_dialyzer(plt_check, [], []),
+
+ ok.
+
+run_succ_typings(Config) when is_list(Config) ->
+ Mod1A = <<"
+ -module(run_succ_typings1).
+
+ -export([call/0]).
+
+ call() -> a.
+ ">>,
+
+ {ok, BeamFile1} = compile(Config, Mod1A, run_succ_typings1, []),
+ [] = run_dialyzer(plt_build, [BeamFile1], []),
+
+ Mod1B = <<"
+ -module(run_succ_typings1).
+
+ -export([call/0]).
+
+ call() -> b.
+ ">>,
+
+ Mod2 = <<"
+ -module(run_succ_typings2).
+
+ -export([call/0]).
+
+ -spec call() -> b.
+ call() -> run_succ_typings1:call().
+ ">>,
+
+ {ok, BeamFile1} = compile(Config, Mod1B, run_succ_typings1, []),
+ {ok, BeamFile2} = compile(Config, Mod2, run_succ_typings2, []),
+ % contract types warning as run_succ_typings2:call/0 makes a call to
+ % run_succ_typings1:call/0, which returns a (not b) in the PLT.
+ [_] = run_dialyzer(succ_typings, [BeamFile2], [{check_plt, false}]),
+ % warning not returned as run_succ_typings1 is updated in the PLT.
+ [] = run_dialyzer(succ_typings, [BeamFile2], [{check_plt, true}]),
+
+ ok.
%%% [James Fish:]
%%% If a function is removed from a module and the module has previously
@@ -103,3 +166,9 @@ compile(Config, Prog, Module, CompileOpts) ->
Opts = [{outdir, PrivDir}, debug_info | CompileOpts],
{ok, Module} = compile:file(Filename, Opts),
{ok, filename:join([PrivDir, lists:concat([Module, ".beam"])])}.
+
+run_dialyzer(Analysis, Files, Opts) ->
+ dialyzer:run([{analysis_type, Analysis},
+ {files, Files},
+ {from, byte_code} |
+ Opts]).
diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml
index a6fad8a857..253ba7c2ff 100644
--- a/lib/eldap/doc/src/eldap.xml
+++ b/lib/eldap/doc/src/eldap.xml
@@ -121,7 +121,7 @@ filter() See present/1, substrings/2,
<item>Any error responded from ssl:connect/3</item>
</taglist>
<p>The <c>Timeout</c> parameter is for the actual tls upgrade (phase 2) while the timeout in
- <seealso marker="#open/2">erl_tar:open/2</seealso> is used for the initial negotiation about
+ <seealso marker="#open/2">eldap:open/2</seealso> is used for the initial negotiation about
upgrade (phase 1).
</p>
</desc>
@@ -298,7 +298,7 @@ filter() See present/1, substrings/2,
search(Handle, [{base, "dc=example, dc=com"}, {filter, Filter}, {attributes, ["cn"]}]),
</pre>
<p>The <c>timeout</c> option in the <c>SearchOptions</c> is for the ldap server, while
- the timeout in <seealso marker="#open/2">erl_tar:open/2</seealso> is used for each
+ the timeout in <seealso marker="#open/2">eldap:open/2</seealso> is used for each
individual request in the search operation.
</p>
</desc>
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index 5823c96253..2abd3d2b7e 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1 +1 @@
-ERL_DOCGEN_VSN = 0.3.8
+ERL_DOCGEN_VSN = 0.4
diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc
index df716cdeea..eb60f673ef 100644
--- a/lib/eunit/doc/overview.edoc
+++ b/lib/eunit/doc/overview.edoc
@@ -572,6 +572,21 @@ Examples:
<dt>`assertNotMatch(GuardedPattern, Expr)'</dt>
<dd>The inverse case of assertMatch, for convenience.
</dd>
+<dt>`assertReceive(GuardedPattern, Timeout)'</dt>
+<dd>Waits for up to the `Timeout' milliseconds for a message to arrive
+in the mailbox of the current process that matches against the
+`GuardedPattern' if testing is enabled.
+If no message matching the `GuardedPattern' is received in the specified
+`Timeout' interval, the assertion fails and an informative exception will
+be generated; see the `assert' macro for further details. `GuardedPattern'
+can be anything that you can write on the left hand side of the `->'
+symbol in a case-clause, except that it cannot contain comma-separated
+guard tests.
+
+Examples:
+```?assertReceive(done, 1000)'''
+```?assertReceive(Bin when byte_size(Bin) > 10, 1000)'''
+</dd>
<dt>`assertEqual(Expect, Expr)'</dt>
<dd>Evaluates the expressions `Expect' and `Expr' and compares the
results for equality, if testing is enabled. If the values are not
diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl
index 53d291430d..8a829396ec 100644
--- a/lib/eunit/include/eunit.hrl
+++ b/lib/eunit/include/eunit.hrl
@@ -166,6 +166,26 @@
%% This is mostly a convenience which gives more detailed reports.
%% Note: Guard is a guarded pattern, and can not be used for value.
-ifdef(NOASSERT).
+-define(assertReceive(Guard, Timeout), ok).
+-else.
+-define(assertReceive(Guard, Timeout),
+ begin
+ ((fun () ->
+ receive (Guard) -> ok
+ after Timeout -> erlang:error({assertReceive_timedout,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {pattern, (??Guard)},
+ {timeout, __V}]})
+ end
+ end)())
+ end).
+-endif.
+-define(_assertReceive(Guard, Timeout), ?_test(?assertReceive(Guard, Timeout))).
+
+%% This is mostly a convenience which gives more detailed reports.
+%% Note: Guard is a guarded pattern, and can not be used for value.
+-ifdef(NOASSERT).
-define(assertMatch(Guard, Expr), ok).
-else.
-define(assertMatch(Guard, Expr),
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 12bbc2b736..bae8e327a3 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -32,7 +32,22 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 5.10.7</title>
+ <section><title>Inets 5.10.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Reject messages with a Content-Length less than 0</p>
+ <p>
+ Own Id: OTP-12739 Aux Id: seq12860 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.7</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl
index 6985065c3e..3ff07616f9 100644
--- a/lib/inets/src/http_server/httpd_request.erl
+++ b/lib/inets/src/http_server/httpd_request.erl
@@ -417,8 +417,12 @@ check_header({"content-length", Value}, Maxsizes) ->
case length(Value) =< MaxLen of
true ->
try
- _ = list_to_integer(Value),
- ok
+ list_to_integer(Value)
+ of
+ I when I>= 0 ->
+ ok;
+ _ ->
+ {error, {size_error, Max, 411, "negative content-length"}}
catch _:_ ->
{error, {size_error, Max, 411, "content-length not an integer"}}
end;
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index e9ecb2632a..ecb84e447c 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 5.10.7
+INETS_VSN = 5.10.8
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java
index 5b9d13ad81..74afbbcca6 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java
@@ -153,9 +153,6 @@ public class OtpSelf extends OtpLocalNode {
* the port number you wish to use for incoming connections.
* Specifying 0 lets the system choose an available port.
*
- * @param transportFactory
- * the transport factory to use when creating connections.
- *
* @exception IOException
* in case of server transport failure
*/
diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl
index 835dcf2705..fb60a14afb 100644
--- a/lib/kernel/src/inet_tcp_dist.erl
+++ b/lib/kernel/src/inet_tcp_dist.erl
@@ -112,7 +112,6 @@ listen_options(Opts0) ->
end,
case application:get_env(kernel, inet_dist_listen_options) of
{ok,ListenOpts} ->
- erlang:display({inet_dist_listen_options, ListenOpts}),
ListenOpts ++ Opts1;
_ ->
Opts1
@@ -340,7 +339,6 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) ->
connect_options(Opts) ->
case application:get_env(kernel, inet_dist_connect_options) of
{ok,ConnectOpts} ->
- erlang:display({inet_dist_listen_options, ConnectOpts}),
ConnectOpts ++ Opts;
_ ->
Opts
diff --git a/lib/orber/src/orber.app.src b/lib/orber/src/orber.app.src
index 5dda63982f..217c1b247f 100644
--- a/lib/orber/src/orber.app.src
+++ b/lib/orber/src/orber.app.src
@@ -104,7 +104,7 @@
{applications, [stdlib, kernel, mnesia]},
{env, []},
{mod, {orber, []}},
- {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","mnesia-4.12","kernel-3.0",
+ {runtime_dependencies, ["stdlib-2.5","ssl-5.3.4","mnesia-4.12","kernel-3.0",
"inets-5.10","erts-7.0"]}
]}.
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 41885c684c..579a3ae4a8 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -29,6 +29,25 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 3.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A new option for handling the SSH_MSG_DEBUG message's
+ printouts. A fun could be given in the options that will
+ be called whenever the SSH_MSG_DEBUG message arrives.
+ This enables the user to format the printout or just
+ discard it.</p>
+ <p>
+ Own Id: OTP-12738 Aux Id: seq12860 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 3.2.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index d49d3ac2a7..df13442fc6 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -57,29 +57,28 @@
this module, or abstractions to indicate the intended use of the data
type, or both:</p>
<taglist>
- <tag><c>boolean()</c></tag>
- <item><p>= <c>true | false</c></p></item>
- <tag><c>string()</c></tag>
- <item><p>= <c>[byte()]</c></p></item>
- <tag><c>ssh_daemon_ref()</c></tag>
- <item><p>Opaque to the user,
- returned by <c>ssh:daemon/[1,2,3]</c></p></item>
- <tag><c>ssh_connection_ref()</c></tag>
- <item><p>Opaque to the user,
- returned by <c>ssh:connect/3</c></p></item>
- <tag><c>ip_address()</c></tag>
+ <tag><c>boolean() =</c></tag>
+ <item><p><c>true | false</c></p></item>
+ <tag><c>string() =</c></tag>
+ <item><p><c>[byte()]</c></p></item>
+ <tag><c>ssh_daemon_ref() =</c></tag>
+ <item><p>opaque() -
+ as returned by <c>ssh:daemon/[1,2,3]</c></p></item>
+ <tag><c>ssh_connection_ref() =</c></tag>
+ <item><p>opaque() - as returned by <c>ssh:connect/3</c></p></item>
+ <tag><c>ip_address() =</c></tag>
<item><p><c>inet::ip_address</c></p></item>
- <tag><c>subsystem_spec()</c></tag>
- <item><p>= <c>{subsystem_name(),
- {channel_callback(), channel_init_args()}}</c></p></item>
- <tag><c>subsystem_name()</c></tag>
- <item><p>= <c>string()</c></p></item>
- <tag><c>channel_callback()</c></tag>
- <item><p>= <c>atom()</c> - Name of the Erlang module
- implementing the subsystem using the <c>ssh_channel</c> behavior, see
- <seealso marker="ssh_channel">ssh_channel(3)</seealso></p></item>
- <tag><c>channel_init_args()</c></tag>
- <item><p>= <c>list()</c></p></item>
+ <tag><c>subsystem_spec() =</c></tag>
+ <item><p><c>{subsystem_name(),
+ {channel_callback(), channel_init_args()}}</c></p></item>
+ <tag><c>subsystem_name() =</c></tag>
+ <item><p><c>string()</c></p></item>
+ <tag><c>channel_callback() =</c></tag>
+ <item><p><c>atom()</c> - Name of the Erlang module
+ implementing the subsystem using the <c>ssh_channel</c> behavior, see
+ <seealso marker="ssh_channel">ssh_channel(3)</seealso></p></item>
+ <tag><c>channel_init_args() =</c></tag>
+ <item><p><c>list()</c></p></item>
</taglist>
</section>
@@ -227,6 +226,13 @@
<item>
<p>Sets a time-out on a connection when no channels are active.
Defaults to <c>infinity</c>.</p></item>
+ <tag><c><![CDATA[{ssh_msg_debug_fun, fun(ConnectionRef::ssh_connection_ref(), AlwaysDisplay::boolean(), Msg::binary(), LanguageTag::binary()) -> _}]]></c></tag>
+ <item>
+ <p>Provide a fun to implement your own logging of the SSH message SSH_MSG_DEBUG. The last three parameters are from the message, see RFC4253, section 11.3. The <c>ConnectionRef</c> is the reference to the connection on which the message arrived. The return value from the fun is not checked.</p>
+ <p>The default behaviour is ignore the message.
+ To get a printout for each message with <c>AlwaysDisplay = true</c>, use for example <c>{ssh_msg_debug_fun, fun(_,true,M,_)-> io:format("DEBUG: ~p~n", [M]) end}</c></p>
+ </item>
+
</taglist>
</desc>
</func>
@@ -427,8 +433,16 @@
<item>
<p>Provides a fun to implement your own logging when a user disconnects from the server.</p>
</item>
- </taglist>
- </desc>
+
+ <tag><c><![CDATA[{ssh_msg_debug_fun, fun(ConnectionRef::ssh_connection_ref(), AlwaysDisplay::boolean(), Msg::binary(), LanguageTag::binary()) -> _}]]></c></tag>
+ <item>
+ <p>Provide a fun to implement your own logging of the SSH message SSH_MSG_DEBUG. The last three parameters are from the message, see RFC4253, section 11.3. The <c>ConnectionRef</c> is the reference to the connection on which the message arrived. The return value from the fun is not checked.</p>
+ <p>The default behaviour is ignore the message.
+ To get a printout for each message with <c>AlwaysDisplay = true</c>, use for example <c>{ssh_msg_debug_fun, fun(_,true,M,_)-> io:format("DEBUG: ~p~n", [M]) end}</c></p>
+ </item>
+
+ </taglist>
+ </desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_channel.xml b/lib/ssh/doc/src/ssh_channel.xml
index b8a03c350a..2fdecf9072 100644
--- a/lib/ssh/doc/src/ssh_channel.xml
+++ b/lib/ssh/doc/src/ssh_channel.xml
@@ -62,22 +62,22 @@
type, or both:</p>
<taglist>
- <tag><c>boolean()</c></tag>
- <item><p>= <c>true | false</c></p></item>
- <tag><c>string()</c></tag>
- <item><p>= list of ASCII characters</p></item>
- <tag><c>timeout()</c></tag>
- <item><p>= <c>infinity | integer()</c> in milliseconds</p></item>
- <tag><c>ssh_connection_ref()</c></tag>
- <item><p>Opaque to the user, returned by
- <c>ssh:connect/3</c> or sent to an SSH channel process</p></item>
- <tag><c>ssh_channel_id()</c></tag>
- <item><p>= <c>integer()</c></p></item>
- <tag><c>ssh_data_type_code()</c></tag>
- <item><p>= <c>1</c> ("stderr") | <c>0</c> ("normal") are
- the valid values,
- see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url>
- Section 5.2</p></item>
+ <tag><c>boolean() =</c></tag>
+ <item><p><c>true | false</c></p></item>
+ <tag><c>string() =</c></tag>
+ <item><p>list of ASCII characters</p></item>
+ <tag><c>timeout() =</c></tag>
+ <item><p><c>infinity | integer()</c> in milliseconds</p></item>
+ <tag><c>ssh_connection_ref() =</c></tag>
+ <item><p>opaque() -as returned by
+ <c>ssh:connect/3</c> or sent to an SSH channel process</p></item>
+ <tag><c>ssh_channel_id() =</c></tag>
+ <item><p><c>integer()</c></p></item>
+ <tag><c>ssh_data_type_code() =</c></tag>
+ <item><p><c>1</c> ("stderr") | <c>0</c> ("normal") are
+ the valid values,
+ see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url>
+ Section 5.2</p></item>
</taglist>
</section>
diff --git a/lib/ssh/doc/src/ssh_client_key_api.xml b/lib/ssh/doc/src/ssh_client_key_api.xml
index a8dda042c9..9a892d71fd 100644
--- a/lib/ssh/doc/src/ssh_client_key_api.xml
+++ b/lib/ssh/doc/src/ssh_client_key_api.xml
@@ -50,16 +50,16 @@
<seealso marker="public_key:public_key_records"> public_key user's guide:</seealso>
</p>
<taglist>
- <tag><c>boolean()</c></tag>
- <item><p>= <c>true | false</c></p></item>
- <tag><c>string()</c></tag>
- <item><p>= <c>[byte()]</c></p></item>
- <tag><c>public_key()</c></tag>
- <item><p>= <c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item>
- <tag><c>private_key()</c></tag>
- <item><p>= <c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item>
- <tag><c>public_key_algorithm()</c></tag>
- <item><p>= <c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item>
+ <tag><c>boolean() =</c></tag>
+ <item><p><c>true | false</c></p></item>
+ <tag><c>string() =</c></tag>
+ <item><p><c>[byte()]</c></p></item>
+ <tag><c>public_key() =</c></tag>
+ <item><p><c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item>
+ <tag><c>private_key() =</c></tag>
+ <item><p><c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item>
+ <tag><c>public_key_algorithm() =</c></tag>
+ <item><p><c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item>
</taglist>
</section>
diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml
index 669a361db9..5422633dc3 100644
--- a/lib/ssh/doc/src/ssh_connection.xml
+++ b/lib/ssh/doc/src/ssh_connection.xml
@@ -56,29 +56,29 @@
type, or both:</p>
<taglist>
- <tag><c>boolean()</c></tag>
- <item><p>= <c>true | false </c></p></item>
- <tag><c>string()</c></tag>
- <item><p>= list of ASCII characters</p></item>
- <tag><c>timeout()</c></tag>
- <item><p>= <c>infinity | integer()</c> in milliseconds</p></item>
- <tag><c>ssh_connection_ref()</c></tag>
- <item><p>Opaque to the user, returned by
- <c>ssh:connect/3</c> or sent to an SSH channel processes</p></item>
- <tag><c>ssh_channel_id()</c></tag>
- <item><p>= <c>integer()</c></p></item>
- <tag><c>ssh_data_type_code()</c></tag>
- <item><p>= <c>1</c> ("stderr") | <c>0</c> ("normal") are
+ <tag><c>boolean() =</c></tag>
+ <item><p><c>true | false </c></p></item>
+ <tag><c>string() =</c></tag>
+ <item><p>list of ASCII characters</p></item>
+ <tag><c>timeout() =</c></tag>
+ <item><p><c>infinity | integer()</c> in milliseconds</p></item>
+ <tag><c>ssh_connection_ref() =</c></tag>
+ <item><p>opaque() -as returned by
+ <c>ssh:connect/3</c> or sent to an SSH channel processes</p></item>
+ <tag><c>ssh_channel_id() =</c></tag>
+ <item><p><c>integer()</c></p></item>
+ <tag><c>ssh_data_type_code() =</c></tag>
+ <item><p><c>1</c> ("stderr") | <c>0</c> ("normal") are
valid values, see
<url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> Section 5.2.</p></item>
- <tag><c>ssh_request_status() ssh_request_status()</c></tag>
- <item><p>= <c>success | failure</c></p></item>
- <tag><c>event()</c></tag>
- <item><p>= <c>{ssh_cm, ssh_connection_ref(), ssh_event_msg()}</c></p></item>
- <tag><c>ssh_event_msg()</c></tag>
- <item><p>= <c>data_events() | status_events() | terminal_events()</c></p></item>
- <tag><c>reason()</c></tag>
- <item><p>= <c>timeout | closed</c></p></item>
+ <tag><c>ssh_request_status() =</c></tag>
+ <item><p> <c>success | failure</c></p></item>
+ <tag><c>event() =</c></tag>
+ <item><p><c>{ssh_cm, ssh_connection_ref(), ssh_event_msg()}</c></p></item>
+ <tag><c>ssh_event_msg() =</c></tag>
+ <item><p><c>data_events() | status_events() | terminal_events()</c></p></item>
+ <tag><c>reason() =</c></tag>
+ <item><p><c>timeout | closed</c></p></item>
</taglist>
<taglist>
diff --git a/lib/ssh/doc/src/ssh_server_key_api.xml b/lib/ssh/doc/src/ssh_server_key_api.xml
index 34ce7f7660..73dd90c962 100644
--- a/lib/ssh/doc/src/ssh_server_key_api.xml
+++ b/lib/ssh/doc/src/ssh_server_key_api.xml
@@ -50,20 +50,20 @@
<seealso marker="public_key:public_key_records"> public_key user's guide</seealso>.
</p>
-<taglist>
- <tag><c>boolean()</c></tag>
- <item><p>= <c>true | false</c></p></item>
- <tag><c>string()</c></tag>
- <item><p>= <c>[byte()]</c></p></item>
- <tag><c>public_key()</c></tag>
- <item><p>= <c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item>
- <tag><c>private_key()</c></tag>
- <item><p>= <c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item>
- <tag><c>public_key_algorithm()</c></tag>
- <item><p>= <c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item>
+ <taglist>
+ <tag><c>boolean() =</c></tag>
+ <item><p><c>true | false</c></p></item>
+ <tag><c>string() =</c></tag>
+ <item><p><c>[byte()]</c></p></item>
+ <tag><c>public_key() =</c></tag>
+ <item><p><c>#'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</c></p></item>
+ <tag><c>private_key() =</c></tag>
+ <item><p><c>#'RSAPrivateKey'{} | #'DSAPrivateKey'{} | term()</c></p></item>
+ <tag><c>public_key_algorithm() =</c></tag>
+ <item><p><c>'ssh-rsa'| 'ssh-dss' | atom()</c></p></item>
</taglist>
</section>
-
+
<funcs>
<func>
<name>Module:host_key(Algorithm, DaemonOptions) ->
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index 643130fe6b..fc418bc934 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -43,8 +43,8 @@
</p>
<taglist>
- <tag><c>ssh_connection_ref()</c></tag>
- <item><p>Opaque to the user, returned by <c>ssh:connect/3</c></p></item>
+ <tag><c>ssh_connection_ref() =</c></tag>
+ <item><p>opaque() - as returned by <c>ssh:connect/3</c></p></item>
<tag><c>timeout()</c></tag>
<item><p>= <c>infinity | integer() in milliseconds. Default infinity.</c></p></item>
</taglist>
diff --git a/lib/ssh/doc/src/ssh_sftpd.xml b/lib/ssh/doc/src/ssh_sftpd.xml
index bc2660f595..8b2497e6a3 100644
--- a/lib/ssh/doc/src/ssh_sftpd.xml
+++ b/lib/ssh/doc/src/ssh_sftpd.xml
@@ -37,16 +37,16 @@
<section>
<title>DATA TYPES</title>
<taglist>
- <tag><c>subsystem_spec()</c></tag>
- <item><p>= <c>{subsystem_name(), {channel_callback(), channel_init_args()}}</c></p></item>
- <tag><c>subsystem_name()</c></tag>
- <item><p>= <c>"sftp"</c></p></item>
- <tag><c>channel_callback()</c></tag>
- <item><p>= <c>atom()</c> - Name of the Erlang module implementing the subsystem using the
+ <tag><c>subsystem_spec() =</c></tag>
+ <item><p><c>{subsystem_name(), {channel_callback(), channel_init_args()}}</c></p></item>
+ <tag><c>subsystem_name() =</c></tag>
+ <item><p><c>"sftp"</c></p></item>
+ <tag><c>channel_callback() =</c></tag>
+ <item><p><c>atom()</c> - Name of the Erlang module implementing the subsystem using the
<c>ssh_channel</c> behavior, see the
<seealso marker="ssh_channel">ssh_channel(3)</seealso> manual page.</p></item>
- <tag><c>channel_init_args()</c></tag>
- <item><p>= <c>list()</c> - The one given as argument to function <c>subsystem_spec/1</c>.</p></item>
+ <tag><c>channel_init_args() =</c></tag>
+ <item><p><c>list()</c> - The one given as argument to function <c>subsystem_spec/1</c>.</p></item>
</taglist>
</section>
<funcs>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index d4b02a024e..71e7d77475 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -312,6 +312,8 @@ handle_option([{disconnectfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{failfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{ssh_msg_debug_fun, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
%%Backwards compatibility should not be underscore between ip and v6 in API
handle_option([{ip_v6_disabled, Value} | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option({ipv6_disabled, Value}) | SshOptions]);
@@ -417,6 +419,8 @@ handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({failfun, Value} = Opt) when is_function(Value) ->
Opt;
+handle_ssh_option({ssh_msg_debug_fun, Value} = Opt) when is_function(Value,4) ->
+ Opt;
handle_ssh_option({ipv6_disabled, Value} = Opt) when is_boolean(Value) ->
throw({error, {{ipv6_disabled, Opt}, option_no_longer_valid_use_inet_option_instead}});
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 4dea284071..2c7f132916 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -581,12 +581,12 @@ handle_event(#ssh_msg_disconnect{description = Desc} = DisconnectMsg, _StateName
handle_event(#ssh_msg_ignore{}, StateName, State) ->
{next_state, StateName, next_packet(State)};
-handle_event(#ssh_msg_debug{always_display = true, message = DbgMsg},
- StateName, State) ->
- io:format("DEBUG: ~p\n", [DbgMsg]),
- {next_state, StateName, next_packet(State)};
-
-handle_event(#ssh_msg_debug{}, StateName, State) ->
+handle_event(#ssh_msg_debug{always_display = Display, message = DbgMsg, language=Lang},
+ StateName, #state{opts = Opts} = State) ->
+ F = proplists:get_value(ssh_msg_debug_fun, Opts,
+ fun(_ConnRef, _AlwaysDisplay, _Msg, _Language) -> ok end
+ ),
+ catch F(self(), Display, DbgMsg, Lang),
{next_state, StateName, next_packet(State)};
handle_event(#ssh_msg_unimplemented{}, StateName, State) ->
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index d55d09f2a2..242c9a3bd9 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -52,6 +52,8 @@ all() ->
ssh_connect_arg4_timeout,
packet_size_zero,
ssh_daemon_minimal_remote_max_packet_size_option,
+ ssh_msg_debug_fun_option_client,
+ ssh_msg_debug_fun_option_server,
id_string_no_opt_client,
id_string_own_string_client,
id_string_random_client,
@@ -494,6 +496,94 @@ server_userpassword_option(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+ssh_msg_debug_fun_option_client() ->
+ [{doc, "validate client that uses the 'ssh_msg_debug_fun' option"}].
+ssh_msg_debug_fun_option_client(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = ?config(data_dir, Config),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ Parent = self(),
+ DbgFun = fun(ConnRef,Displ,Msg,Lang) -> Parent ! {msg_dbg,{ConnRef,Displ,Msg,Lang}} end,
+
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_dir, UserDir},
+ {user_interaction, false},
+ {ssh_msg_debug_fun,DbgFun}]),
+ %% Beware, implementation knowledge:
+ gen_fsm:send_all_state_event(ConnectionRef,{ssh_msg_debug,false,<<"Hello">>,<<>>}),
+ receive
+ {msg_dbg,X={ConnectionRef,false,<<"Hello">>,<<>>}} ->
+ ct:log("Got expected dbg msg ~p",[X]),
+ ssh:stop_daemon(Pid);
+ {msg_dbg,X={_,false,<<"Hello">>,<<>>}} ->
+ ct:log("Got dbg msg but bad ConnectionRef (~p expected) ~p",[ConnectionRef,X]),
+ ssh:stop_daemon(Pid),
+ {fail, "Bad ConnectionRef received"};
+ {msg_dbg,X} ->
+ ct:log("Got bad dbg msg ~p",[X]),
+ ssh:stop_daemon(Pid),
+ {fail,"Bad msg received"}
+ after 1000 ->
+ ssh:stop_daemon(Pid),
+ {fail,timeout}
+ end.
+
+%%--------------------------------------------------------------------
+ssh_msg_debug_fun_option_server() ->
+ [{doc, "validate client that uses the 'ssh_msg_debug_fun' option"}].
+ssh_msg_debug_fun_option_server(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = ?config(data_dir, Config),
+
+ Parent = self(),
+ DbgFun = fun(ConnRef,Displ,Msg,Lang) -> Parent ! {msg_dbg,{ConnRef,Displ,Msg,Lang}} end,
+ ConnFun = fun(_,_,_) -> Parent ! {connection_pid,self()} end,
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {failfun, fun ssh_test_lib:failfun/2},
+ {connectfun, ConnFun},
+ {ssh_msg_debug_fun, DbgFun}]),
+ _ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_dir, UserDir},
+ {user_interaction, false}]),
+ receive
+ {connection_pid,Server} ->
+ %% Beware, implementation knowledge:
+ gen_fsm:send_all_state_event(Server,{ssh_msg_debug,false,<<"Hello">>,<<>>}),
+ receive
+ {msg_dbg,X={_,false,<<"Hello">>,<<>>}} ->
+ ct:log("Got expected dbg msg ~p",[X]),
+ ssh:stop_daemon(Pid);
+ {msg_dbg,X} ->
+ ct:log("Got bad dbg msg ~p",[X]),
+ ssh:stop_daemon(Pid),
+ {fail,"Bad msg received"}
+ after 3000 ->
+ ssh:stop_daemon(Pid),
+ {fail,timeout2}
+ end
+ after 3000 ->
+ ssh:stop_daemon(Pid),
+ {fail,timeout1}
+ end.
+
+%%--------------------------------------------------------------------
known_hosts() ->
[{doc, "check that known_hosts is updated correctly"}].
known_hosts(Config) when is_list(Config) ->
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 8a0bf69be4..18d98e5efb 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -123,13 +123,13 @@
The callback <c>gen_tcp</c> is treated specially and calls <c>inet</c>
directly.</p>
<taglist>
- <tag><c>CallbackModule</c></tag>
- <item><p><c>= atom()</c></p></item>
- <tag><c>DataTag</c></tag>
- <item><p><c>= atom()</c></p>
+ <tag><c>CallbackModule =</c></tag>
+ <item><p><c>atom()</c></p></item>
+ <tag><c>DataTag =</c></tag>
+ <item><p><c>atom()</c></p>
<p>Used in socket data message.</p></item>
- <tag><c>ClosedTag</c></tag>
- <item><p><c>= atom()</c></p>
+ <tag><c>ClosedTag =</c></tag>
+ <item><p><c>atom()</c></p>
<p>Used in socket close message.</p></item>
</taglist>
</item>
diff --git a/lib/ssl/doc/src/ssl_crl_cache_api.xml b/lib/ssl/doc/src/ssl_crl_cache_api.xml
index 90aa895aff..9230442ae0 100644
--- a/lib/ssl/doc/src/ssl_crl_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_crl_cache_api.xml
@@ -47,10 +47,10 @@
<taglist>
- <tag><c>cache_ref()</c></tag>
- <item> = opaque()</item>
- <tag><c>dist_point()</c></tag>
- <item><p> = #'DistributionPoint'{} see <seealso
+ <tag><c>cache_ref() =</c></tag>
+ <item>opaque()</item>
+ <tag><c>dist_point() =</c></tag>
+ <item><p>#'DistributionPoint'{} see <seealso
marker="public_key:public_key_records"> X509 certificates records</seealso></p></item>
</taglist>
diff --git a/lib/ssl/doc/src/ssl_session_cache_api.xml b/lib/ssl/doc/src/ssl_session_cache_api.xml
index c89d3874a1..28b5f4ce23 100644
--- a/lib/ssl/doc/src/ssl_session_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_session_cache_api.xml
@@ -40,20 +40,20 @@
<c>ssl_session_cache_api</c>:</p>
<taglist>
- <tag><c>cache_ref()</c></tag>
- <item><p>= <c>opaque()</c></p></item>
+ <tag><c>cache_ref() =</c></tag>
+ <item><p><c>opaque()</c></p></item>
- <tag><c>key()</c></tag>
- <item><p>= <c>{partialkey(), session_id()}</c></p></item>
+ <tag><c>key() =</c></tag>
+ <item><p><c>{partialkey(), session_id()}</c></p></item>
- <tag><c>partialkey()</c></tag>
- <item><p>= <c>opaque()</c></p></item>
+ <tag><c>partialkey() =</c></tag>
+ <item><p><c>opaque()</c></p></item>
- <tag><c>session_id()</c></tag>
- <item><p>= <c>binary()</c></p></item>
+ <tag><c>session_id() =</c></tag>
+ <item><p><c>binary()</c></p></item>
- <tag><c>session()</c></tag>
- <item><p>= <c>opaque()</c></p></item>
+ <tag><c>session()</c> =</tag>
+ <item><p><c>opaque()</c></p></item>
</taglist>
</section>
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 50d5fb411f..3495b978b7 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -3445,7 +3445,7 @@ renegotiate_immediately(Socket) ->
end,
ok = ssl:renegotiate(Socket),
{error, renegotiation_rejected} = ssl:renegotiate(Socket),
- ct:sleep(?RENEGOTIATION_DISABLE_TIME +1),
+ ct:sleep(?RENEGOTIATION_DISABLE_TIME + ?SLEEP),
ok = ssl:renegotiate(Socket),
ct:log("Renegotiated again"),
ssl:send(Socket, "Hello world"),
diff --git a/lib/stdlib/doc/src/c.xml b/lib/stdlib/doc/src/c.xml
index b49fa6ad67..b43d4786ae 100644
--- a/lib/stdlib/doc/src/c.xml
+++ b/lib/stdlib/doc/src/c.xml
@@ -232,6 +232,14 @@ compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_w
</desc>
</func>
<func>
+ <name name="uptime" arity="0"/>
+ <fsummary>Print node uptime</fsummary>
+ <desc>
+ <p>Prints the node uptime (as given by
+ <c>erlang:statistics(wall_clock)</c>), in human-readable form.</p>
+ </desc>
+ </func>
+ <func>
<name>xm(ModSpec) -> void()</name>
<fsummary>Cross reference check a module</fsummary>
<type>
diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml
index ea96c14472..405bae5698 100644
--- a/lib/stdlib/doc/src/gb_sets.xml
+++ b/lib/stdlib/doc/src/gb_sets.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2001</year><year>2014</year>
+ <year>2001</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -306,6 +306,17 @@
</desc>
</func>
<func>
+ <name name="iterator_from" arity="2"/>
+ <fsummary>Return an iterator for a set starting from a specified element</fsummary>
+ <desc>
+ <p>Returns an iterator that can be used for traversing the
+ entries of <c><anno>Set</anno></c>; see <c>next/1</c>.
+ The difference as compared to the iterator returned by
+ <c>iterator/1</c> is that the first element greater than
+ or equal to <c><anno>Element</anno></c> is returned.</p>
+ </desc>
+ </func>
+ <func>
<name name="largest" arity="1"/>
<fsummary>Return largest element</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/gb_trees.xml b/lib/stdlib/doc/src/gb_trees.xml
index b2f237e1d7..82167e1083 100644
--- a/lib/stdlib/doc/src/gb_trees.xml
+++ b/lib/stdlib/doc/src/gb_trees.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2001</year><year>2014</year>
+ <year>2001</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -183,6 +183,17 @@
</desc>
</func>
<func>
+ <name name="iterator_from" arity="2"/>
+ <fsummary>Return an iterator for a tree starting from specified key</fsummary>
+ <desc>
+ <p>Returns an iterator that can be used for traversing the
+ entries of <c><anno>Tree</anno></c>; see <c>next/1</c>.
+ The difference as compared to the iterator returned by
+ <c>iterator/1</c> is that the first key greater than
+ or equal to <c><anno>Key</anno></c> is returned.</p>
+ </desc>
+ </func>
+ <func>
<name name="keys" arity="1"/>
<fsummary>Return a list of the keys in a tree</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml
index e46068230a..7345a9357a 100644
--- a/lib/stdlib/doc/src/maps.xml
+++ b/lib/stdlib/doc/src/maps.xml
@@ -33,6 +33,28 @@
<funcs>
<func>
+ <name name="filter" arity="2"/>
+ <fsummary>Choose pairs which satisfy a predicate</fsummary>
+ <desc>
+ <p>
+ Returns a map <c><anno>Map2</anno></c> for which predicate
+ <c><anno>Pred</anno></c> holds true in <c><anno>Map1</anno></c>.
+ </p>
+ <p>
+ The call will fail with a <c>{badmap,Map}</c> exception if
+ <c><anno>Map1</anno></c> is not a map or with <c>badarg</c> if
+ <c><anno>Pred</anno></c> is not a function of arity 2.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> M = #{a => 2, b => 3, c=> 4, "a" => 1, "b" => 2, "c" => 4},
+ Pred = fun(K,V) -> is_atom(K) andalso (V rem 2) =:= 0 end,
+ maps:filter(Pred,M).
+#{a => 2,c => 4} </code>
+ </desc>
+ </func>
+
+ <func>
<name name="find" arity="2"/>
<fsummary></fsummary>
<desc>
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index 9860adf04d..d5b24d3c32 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.erl
@@ -27,7 +27,7 @@
lc_batch/0, lc_batch/1,
i/3,pid/3,m/0,m/1,
bt/1, q/0,
- erlangrc/0,erlangrc/1,bi/1, flush/0, regs/0,
+ erlangrc/0,erlangrc/1,bi/1, flush/0, regs/0, uptime/0,
nregs/0,pwd/0,ls/0,ls/1,cd/1,memory/1,memory/0, xm/1]).
-export([display_info/1]).
@@ -65,6 +65,7 @@ help() ->
"q() -- quit - shorthand for init:stop()\n"
"regs() -- information about registered processes\n"
"nregs() -- information about all registered processes\n"
+ "uptime() -- print node uptime\n"
"xm(M) -- cross reference check a module\n"
"y(File) -- generate a Yecc parser\n">>).
@@ -774,6 +775,26 @@ memory() -> erlang:memory().
memory(TypeSpec) -> erlang:memory(TypeSpec).
%%
+%% uptime/0
+%%
+
+-spec uptime() -> 'ok'.
+
+uptime() ->
+ io:format("~s~n", [uptime(get_uptime())]).
+
+uptime({D, {H, M, S}}) ->
+ lists:flatten(
+ [[ io_lib:format("~p days, ", [D]) || D > 0 ],
+ [ io_lib:format("~p hours, ", [H]) || D+H > 0 ],
+ [ io_lib:format("~p minutes and ", [M]) || D+H+M > 0 ],
+ io_lib:format("~p seconds", [S])]).
+
+get_uptime() ->
+ {UpTime, _} = erlang:statistics(wall_clock),
+ calendar:seconds_to_daystime(UpTime div 1000).
+
+%%
%% Cross Reference Check
%%
%%-spec xm(module() | file:filename()) -> xref:m/1 return
diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl
index 393fb07229..d3fbd542f7 100644
--- a/lib/stdlib/src/gb_sets.erl
+++ b/lib/stdlib/src/gb_sets.erl
@@ -137,6 +137,10 @@
%% approach is that it does not require the complete list of all
%% elements to be built in memory at one time.
%%
+%% - iterator_from(X, S): returns an iterator that can be used for
+%% traversing the elements of set S greater than or equal to X;
+%% see `next'.
+%%
%% - next(T): returns {X, T1} where X is the smallest element referred
%% to by the iterator T, and T1 is the new iterator to be used for
%% traversing the remaining elements, or the atom `none' if no
@@ -157,8 +161,8 @@
insert/2, add/2, delete/2, delete_any/2, balance/1, union/2,
union/1, intersection/2, intersection/1, is_disjoint/2, difference/2,
is_subset/2, to_list/1, from_list/1, from_ordset/1, smallest/1,
- largest/1, take_smallest/1, take_largest/1, iterator/1, next/1,
- filter/2, fold/3, is_set/1]).
+ largest/1, take_smallest/1, take_largest/1, iterator/1,
+ iterator_from/2, next/1, filter/2, fold/3, is_set/1]).
%% `sets' compatibility aliases:
@@ -500,6 +504,22 @@ iterator({_, L, _} = T, As) ->
iterator(nil, As) ->
As.
+-spec iterator_from(Element, Set) -> Iter when
+ Set :: set(Element),
+ Iter :: iter(Element).
+
+iterator_from(S, {_, T}) ->
+ iterator_from(S, T, []).
+
+iterator_from(S, {K, _, T}, As) when K < S ->
+ iterator_from(S, T, As);
+iterator_from(_, {_, nil, _} = T, As) ->
+ [T | As];
+iterator_from(S, {_, L, _} = T, As) ->
+ iterator_from(S, L, [T | As]);
+iterator_from(_, nil, As) ->
+ As.
+
-spec next(Iter1) -> {Element, Iter2} | 'none' when
Iter1 :: iter(Element),
Iter2 :: iter(Element).
diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl
index 7069b61873..259e8f718b 100644
--- a/lib/stdlib/src/gb_trees.erl
+++ b/lib/stdlib/src/gb_trees.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -102,6 +102,10 @@
%% approach is that it does not require the complete list of all
%% elements to be built in memory at one time.
%%
+%% - iterator_from(K, T): returns an iterator that can be used for
+%% traversing the entries of tree T with key greater than or
+%% equal to K; see `next'.
+%%
%% - next(S): returns {X, V, S1} where X is the smallest key referred to
%% by the iterator S, and S1 is the new iterator to be used for
%% traversing the remaining entries, or the atom `none' if no entries
@@ -117,7 +121,7 @@
update/3, enter/3, delete/2, delete_any/2, balance/1,
is_defined/2, keys/1, values/1, to_list/1, from_orddict/1,
smallest/1, largest/1, take_smallest/1, take_largest/1,
- iterator/1, next/1, map/2]).
+ iterator/1, iterator_from/2, next/1, map/2]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -529,6 +533,29 @@ iterator({_, _, L, _} = T, As) ->
iterator(nil, As) ->
As.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-spec iterator_from(Key, Tree) -> Iter when
+ Tree :: tree(Key, Value),
+ Iter :: iter(Key, Value).
+
+iterator_from(S, {_, T}) ->
+ iterator_1_from(S, T).
+
+iterator_1_from(S, T) ->
+ iterator_from(S, T, []).
+
+iterator_from(S, {K, _, _, T}, As) when K < S ->
+ iterator_from(S, T, As);
+iterator_from(_, {_, _, nil, _} = T, As) ->
+ [T | As];
+iterator_from(S, {_, _, L, _} = T, As) ->
+ iterator_from(S, L, [T | As]);
+iterator_from(_, nil, As) ->
+ As.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
-spec next(Iter1) -> 'none' | {Key, Value, Iter2} when
Iter1 :: iter(Key, Value),
Iter2 :: iter(Key, Value).
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 3877c150ec..533ff08726 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -19,7 +19,8 @@
-module(maps).
--export([get/3,fold/3, map/2, size/1,
+-export([get/3,filter/2,fold/3, map/2,
+ size/1,
without/2, with/2]).
@@ -145,6 +146,19 @@ get(Key,Map,Default) ->
erlang:error({badmap,Map},[Key,Map,Default]).
+-spec filter(Pred,Map1) -> Map2 when
+ Pred :: fun((Key, Value) -> boolean()),
+ Key :: term(),
+ Value :: term(),
+ Map1 :: map(),
+ Map2 :: map().
+
+filter(Pred,Map) when is_function(Pred,2), is_map(Map) ->
+ maps:from_list([{K,V}||{K,V}<-maps:to_list(Map),Pred(K,V)]);
+filter(Pred,Map) ->
+ erlang:error(error_type(Map),[Pred,Map]).
+
+
-spec fold(Fun,Init,Map) -> Acc when
Fun :: fun((K, V, AccIn) -> AccOut),
Init :: term(),
@@ -169,10 +183,7 @@ fold(Fun,Init,Map) ->
V2 :: term().
map(Fun,Map) when is_function(Fun, 2), is_map(Map) ->
- maps:from_list(lists:map(fun
- ({K,V}) ->
- {K,Fun(K,V)}
- end,maps:to_list(Map)));
+ maps:from_list([{K,Fun(K,V)}||{K,V}<-maps:to_list(Map)]);
map(Fun,Map) ->
erlang:error(error_type(Map),[Fun,Map]).
diff --git a/lib/stdlib/src/shell_default.erl b/lib/stdlib/src/shell_default.erl
index 3fe359af0e..0fca7ff8c7 100644
--- a/lib/stdlib/src/shell_default.erl
+++ b/lib/stdlib/src/shell_default.erl
@@ -23,7 +23,7 @@
-module(shell_default).
-export([help/0,lc/1,c/1,c/2,nc/1,nl/1,l/1,i/0,pid/3,i/3,m/0,m/1,
- memory/0,memory/1,
+ memory/0,memory/1,uptime/0,
erlangrc/1,bi/1, regs/0, flush/0,pwd/0,ls/0,ls/1,cd/1,
y/1, y/2,
xm/1, bt/1, q/0,
@@ -92,6 +92,7 @@ pid(X,Y,Z) -> c:pid(X,Y,Z).
pwd() -> c:pwd().
q() -> c:q().
regs() -> c:regs().
+uptime() -> c:uptime().
xm(Mod) -> c:xm(Mod).
y(File) -> c:y(File).
y(File, Opts) -> c:y(File, Opts).
diff --git a/lib/stdlib/test/dict_SUITE.erl b/lib/stdlib/test/dict_SUITE.erl
index 69814e12ce..ab624e8dd2 100644
--- a/lib/stdlib/test/dict_SUITE.erl
+++ b/lib/stdlib/test/dict_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,16 +25,16 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
- create/1,store/1]).
+ create/1,store/1,iterate/1]).
-include_lib("test_server/include/test_server.hrl").
--import(lists, [foldl/3,reverse/1]).
+-import(lists, [foldl/3]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [create, store].
+ [create, store, iterate].
groups() ->
[].
@@ -93,6 +93,48 @@ store_1(List, M) ->
D0.
%%%
+%%% Test specifics for gb_trees.
+%%%
+
+iterate(Config) when is_list(Config) ->
+ test_all(fun iterate_1/1).
+
+iterate_1(M) ->
+ case M(module, []) of
+ gb_trees -> iterate_2(M);
+ _ -> ok
+ end,
+ M(empty, []).
+
+iterate_2(M) ->
+ random:seed(1, 2, 42),
+ iter_tree(M, 1000).
+
+iter_tree(_M, 0) ->
+ ok;
+iter_tree(M, N) ->
+ L = [{I, I} || I <- lists:seq(1, N)],
+ T = M(from_list, L),
+ L = lists:reverse(iterate_tree(M, T)),
+ R = random:uniform(N),
+ KV = lists:reverse(iterate_tree_from(M, R, T)),
+ KV = [P || P={K,_} <- L, K >= R],
+ iter_tree(M, N-1).
+
+iterate_tree(M, Tree) ->
+ I = M(iterator, Tree),
+ iterate_tree_1(M, M(next, I), []).
+
+iterate_tree_from(M, Start, Tree) ->
+ I = M(iterator_from, {Start, Tree}),
+ iterate_tree_1(M, M(next, I), []).
+
+iterate_tree_1(_, none, R) ->
+ R;
+iterate_tree_1(M, {K, V, I}, R) ->
+ iterate_tree_1(M, M(next, I), [{K, V} | R]).
+
+%%%
%%% Helper functions.
%%%
diff --git a/lib/stdlib/test/dict_test_lib.erl b/lib/stdlib/test/dict_test_lib.erl
index 4fdb4fa0bd..81d26ce5f8 100644
--- a/lib/stdlib/test/dict_test_lib.erl
+++ b/lib/stdlib/test/dict_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,6 +29,9 @@ new(Mod, Eq) ->
(module, []) -> Mod;
(size, D) -> Mod:size(D);
(is_empty, D) -> Mod:is_empty(D);
+ (iterator, S) -> Mod:iterator(S);
+ (iterator_from, {Start, S}) -> Mod:iterator_from(Start, S);
+ (next, I) -> Mod:next(I);
(to_list, D) -> to_list(Mod, D)
end.
diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl
index 1d9c041a74..21e146ae3d 100644
--- a/lib/stdlib/test/maps_SUITE.erl
+++ b/lib/stdlib/test/maps_SUITE.erl
@@ -34,7 +34,7 @@
-export([init_per_testcase/2]).
-export([end_per_testcase/2]).
--export([t_get_3/1,
+-export([t_get_3/1, t_filter_2/1,
t_fold_3/1,t_map_2/1,t_size_1/1,
t_with_2/1,t_without_2/1]).
@@ -45,7 +45,7 @@ suite() ->
[{ct_hooks, [ts_install_cth]}].
all() ->
- [t_get_3,
+ [t_get_3,t_filter_2,
t_fold_3,t_map_2,t_size_1,
t_with_2,t_without_2].
@@ -99,6 +99,16 @@ t_with_2(_Config) ->
?badarg(with,[a,#{}]) = (catch maps:with(a,#{})),
ok.
+t_filter_2(Config) when is_list(Config) ->
+ M = #{a => 2, b => 3, c=> 4, "a" => 1, "b" => 2, "c" => 4},
+ Pred1 = fun(K,V) -> is_atom(K) andalso (V rem 2) =:= 0 end,
+ Pred2 = fun(K,V) -> is_list(K) andalso (V rem 2) =:= 0 end,
+ #{a := 2,c := 4} = maps:filter(Pred1,M),
+ #{"b" := 2,"c" := 4} = maps:filter(Pred2,M),
+ %% error case
+ ?badmap(a,filter,[_,a]) = (catch maps:filter(fun(_,_) -> ok end,id(a))),
+ ?badarg(filter,[<<>>,#{}]) = (catch maps:filter(id(<<>>),#{})),
+ ok.
t_fold_3(Config) when is_list(Config) ->
Vs = lists:seq(1,200),
diff --git a/lib/stdlib/test/sets_SUITE.erl b/lib/stdlib/test/sets_SUITE.erl
index c0cf1fc7e8..24f5d65f82 100644
--- a/lib/stdlib/test/sets_SUITE.erl
+++ b/lib/stdlib/test/sets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
create/1,add_element/1,del_element/1,
subtract/1,intersection/1,union/1,is_subset/1,
is_set/1,fold/1,filter/1,
- take_smallest/1,take_largest/1]).
+ take_smallest/1,take_largest/1, iterate/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -48,7 +48,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[create, add_element, del_element, subtract,
intersection, union, is_subset, is_set, fold, filter,
- take_smallest, take_largest].
+ take_smallest, take_largest, iterate].
groups() ->
[].
@@ -426,6 +426,44 @@ take_largest_3(S0, List0, M) ->
take_largest_3(S, List, M)
end.
+iterate(Config) when is_list(Config) ->
+ test_all(fun iterate_1/1).
+
+iterate_1(M) ->
+ case M(module, []) of
+ gb_sets -> iterate_2(M);
+ _ -> ok
+ end,
+ M(empty, []).
+
+iterate_2(M) ->
+ random:seed(1, 2, 42),
+ iter_set(M, 1000).
+
+iter_set(_M, 0) ->
+ ok;
+iter_set(M, N) ->
+ L = [I || I <- lists:seq(1, N)],
+ T = M(from_list, L),
+ L = lists:reverse(iterate_set(M, T)),
+ R = random:uniform(N),
+ S = lists:reverse(iterate_set(M, R, T)),
+ S = [E || E <- L, E >= R],
+ iter_set(M, N-1).
+
+iterate_set(M, Set) ->
+ I = M(iterator, Set),
+ iterate_set_1(M, M(next, I), []).
+
+iterate_set(M, Start, Set) ->
+ I = M(iterator_from, {Start, Set}),
+ iterate_set_1(M, M(next, I), []).
+
+iterate_set_1(_, none, R) ->
+ R;
+iterate_set_1(M, {E, I}, R) ->
+ iterate_set_1(M, M(next, I), [E | R]).
+
%%%
%%% Helper functions.
%%%
diff --git a/lib/stdlib/test/sets_test_lib.erl b/lib/stdlib/test/sets_test_lib.erl
index 86f009a8f9..772139406d 100644
--- a/lib/stdlib/test/sets_test_lib.erl
+++ b/lib/stdlib/test/sets_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,7 +34,10 @@ new(Mod, Eq) ->
(is_empty, S) -> is_empty(Mod, S);
(is_set, S) -> Mod:is_set(S);
(is_subset, {S,Set}) -> is_subset(Mod, Eq, S, Set);
+ (iterator, S) -> Mod:iterator(S);
+ (iterator_from, {Start, S}) -> Mod:iterator_from(Start, S);
(module, []) -> Mod;
+ (next, I) -> Mod:next(I);
(singleton, E) -> singleton(Mod, E);
(size, S) -> Mod:size(S);
(subtract, {S1,S2}) -> subtract(Mod, S1, S2);
diff --git a/lib/test_server/src/erl2html2.erl b/lib/test_server/src/erl2html2.erl
index 9101212852..b0b5c40965 100644
--- a/lib/test_server/src/erl2html2.erl
+++ b/lib/test_server/src/erl2html2.erl
@@ -170,7 +170,11 @@ get_line(Anno) ->
%%% Find the line number of the last expression in the function
find_clause_lines([{clause,CL,_Params,_Op,Exprs}], CLs) -> % last clause
try tuple_to_list(lists:last(Exprs)) of
- [_Type,ExprLine | _] ->
+ [_Type,ExprLine | _] when is_integer(ExprLine) ->
+ {lists:reverse([{clause,get_line(CL)}|CLs]), get_line(ExprLine)};
+ [tree,_ | Exprs1] ->
+ find_clause_lines([{clause,CL,undefined,undefined,Exprs1}], CLs);
+ [macro,{_var,ExprLine,_MACRO} | _] when is_integer(ExprLine) ->
{lists:reverse([{clause,get_line(CL)}|CLs]), get_line(ExprLine)};
_ ->
{lists:reverse([{clause,get_line(CL)}|CLs]), get_line(CL)}
@@ -188,18 +192,18 @@ build_html(SFd,DFd,Encoding,FuncsAndCs) ->
build_html(SFd,DFd,Encoding,file:read_line(SFd),1,FuncsAndCs,
false,undefined).
-%% function start line found
-build_html(SFd,DFd,Enc,{ok,Str},L0,[{F,A,L0,LastL}|FuncsAndCs],
- _IsFuncDef,_FAndLastL) ->
- FALink = test_server_ctrl:uri_encode(F++"-"++integer_to_list(A),utf8),
- file:write(DFd,["<a name=\"",to_raw_list(FALink,Enc),"\"/>"]),
- build_html(SFd,DFd,Enc,{ok,Str},L0,FuncsAndCs,true,{F,LastL});
%% line of last expression in function found
build_html(SFd,DFd,Enc,{ok,Str},LastL,FuncsAndCs,_IsFuncDef,{F,LastL}) ->
LastLineLink = test_server_ctrl:uri_encode(F++"-last_expr",utf8),
file:write(DFd,["<a name=\"",
to_raw_list(LastLineLink,Enc),"\"/>"]),
build_html(SFd,DFd,Enc,{ok,Str},LastL,FuncsAndCs,true,undefined);
+%% function start line found
+build_html(SFd,DFd,Enc,{ok,Str},L0,[{F,A,L0,LastL}|FuncsAndCs],
+ _IsFuncDef,_FAndLastL) ->
+ FALink = test_server_ctrl:uri_encode(F++"-"++integer_to_list(A),utf8),
+ file:write(DFd,["<a name=\"",to_raw_list(FALink,Enc),"\"/>"]),
+ build_html(SFd,DFd,Enc,{ok,Str},L0,FuncsAndCs,true,{F,LastL});
build_html(SFd,DFd,Enc,{ok,Str},L,[{clause,L}|FuncsAndCs],
_IsFuncDef,FAndLastL) ->
build_html(SFd,DFd,Enc,{ok,Str},L,FuncsAndCs,true,FAndLastL);
diff --git a/lib/test_server/test/erl2html2_SUITE.erl b/lib/test_server/test/erl2html2_SUITE.erl
index 908985c879..796b84dedd 100644
--- a/lib/test_server/test/erl2html2_SUITE.erl
+++ b/lib/test_server/test/erl2html2_SUITE.erl
@@ -130,15 +130,7 @@ groups() ->
%% @end
%%--------------------------------------------------------------------
all() ->
- [m1].
-
-%%--------------------------------------------------------------------
-%% @spec TestCase() -> Info
-%% Info = [tuple()]
-%% @end
-%%--------------------------------------------------------------------
-m1() ->
- [].
+ [macros_defined, macros_undefined].
%%--------------------------------------------------------------------
%% @spec TestCase(Config0) ->
@@ -149,19 +141,29 @@ m1() ->
%% Comment = term()
%% @end
%%--------------------------------------------------------------------
-m1(Config) ->
- {Src,Dst} = convert_module("m1",Config),
+macros_defined(Config) ->
+ %% let erl2html2 use epp as parser
+ DataDir = ?config(data_dir,Config),
+ InclDir = filename:join(DataDir, "include"),
+ {Src,Dst} = convert_module("m1",[InclDir],Config),
{true,L} = check_line_numbers(Src,Dst),
- ok = check_link_targets(Src,Dst,L,[{baz,0}]),
+ ok = check_link_targets(Src,Dst,L,[{baz,0}],[]),
ok.
-convert_module(Mod,Config) ->
+macros_undefined(Config) ->
+ %% let erl2html2 use epp_dodger as parser
+ {Src,Dst} = convert_module("m1",[],Config),
+ {true,L} = check_line_numbers(Src,Dst),
+ ok = check_link_targets(Src,Dst,L,[{baz,0}],[{quux,0}]),
+ ok.
+
+convert_module(Mod,InclDirs,Config) ->
DataDir = ?config(data_dir,Config),
PrivDir = ?config(priv_dir,Config),
Src = filename:join(DataDir,Mod++".erl"),
Dst = filename:join(PrivDir,Mod++".erl.html"),
io:format("<a href=\"~s\">~s</a>\n",[Src,filename:basename(Src)]),
- ok = erl2html2:convert(Src, Dst, [], "<html><body>"),
+ ok = erl2html2:convert(Src, Dst, InclDirs, "<html><body>"),
io:format("<a href=\"~s\">~s</a>\n",[Dst,filename:basename(Dst)]),
{Src,Dst}.
@@ -229,36 +231,46 @@ check_line_number(Last,Line,OrigLine) ->
%% function.
%% The test module has -compile(export_all), so all functions are
%% found by listing the exported ones.
-check_link_targets(Src,Dst,L,RmFncs) ->
+check_link_targets(Src,Dst,L,RmFncs,ShouldRemain) ->
Mod = list_to_atom(filename:basename(filename:rootname(Src))),
Exports = Mod:module_info(exports)--[{module_info,0},{module_info,1}|RmFncs],
- {ok,{[],L},_} = xmerl_sax_parser:file(Dst,
- [{event_fun,fun sax_event/3},
- {event_state,{Exports,0}}]),
+ LastExprFuncs = [Func || {Func,_A} <- Exports],
+ {ok,{FAs,Fs,L},_} =
+ xmerl_sax_parser:file(Dst,
+ [{event_fun,fun sax_event/3},
+ {event_state,{Exports,LastExprFuncs,0}}]),
+ true = (length(FAs) == length(ShouldRemain)),
+ [] = [FA || FA <- FAs, not lists:member(FA,ShouldRemain)],
+ [] = [F || F <- Fs, not lists:keymember(F,1,ShouldRemain)],
ok.
sax_event(Event,_Loc,State) ->
sax_event(Event,State).
-sax_event({startElement,_Uri,"a",_QN,Attrs},{Exports,PrevLine}) ->
+sax_event({startElement,_Uri,"a",_QN,Attrs},{Exports,LastExprFuncs,PrevLine}) ->
{_,_,"name",Name} = lists:keyfind("name",3,Attrs),
case catch list_to_integer(Name) of
Line when is_integer(Line) ->
case PrevLine + 1 of
Line ->
-% erlang:display({found_line,Line}),
- {Exports,Line};
+ {Exports,LastExprFuncs,Line};
Other ->
ct:fail({unexpected_line_number_target,Other})
end;
{'EXIT',_} ->
- {match,[FStr,AStr]} =
- re:run(Name,"^(.*)-([0-9]+)$",[{capture,all_but_first,list}]),
+ {match,[FStr,EndStr]} =
+ re:run(Name,"^(.*)-(last_expr|[0-9]+)$",
+ [{capture,all_but_first,list}]),
F = list_to_atom(http_uri:decode(FStr)),
- A = list_to_integer(AStr),
-% erlang:display({found_fnc,F,A}),
- A = proplists:get_value(F,Exports),
- {lists:delete({F,A},Exports),PrevLine}
+ case EndStr of
+ "last_expr" ->
+ true = lists:member(F,LastExprFuncs),
+ {Exports,lists:delete(F,LastExprFuncs),PrevLine};
+ _ ->
+ A = list_to_integer(EndStr),
+ A = proplists:get_value(F,Exports),
+ {lists:delete({F,A},Exports),LastExprFuncs,PrevLine}
+ end
end;
sax_event(_,State) ->
State.
diff --git a/lib/test_server/test/erl2html2_SUITE_data/include/header3.hrl b/lib/test_server/test/erl2html2_SUITE_data/include/header3.hrl
new file mode 100644
index 0000000000..2a20850a3a
--- /dev/null
+++ b/lib/test_server/test/erl2html2_SUITE_data/include/header3.hrl
@@ -0,0 +1 @@
+-define(EPP_SWITCH, on).
diff --git a/lib/test_server/test/erl2html2_SUITE_data/m1.erl b/lib/test_server/test/erl2html2_SUITE_data/m1.erl
index 156f1d0a51..1d405963a5 100644
--- a/lib/test_server/test/erl2html2_SUITE_data/m1.erl
+++ b/lib/test_server/test/erl2html2_SUITE_data/m1.erl
@@ -7,9 +7,15 @@
-include("header1.hrl").
-include("header2.hrl").
+-include("header3.hrl").
-define(MACRO1,value).
+%% This macro is used to select parser in erl2html2.
+%% If EPP_SWITCH is defined epp is used, else epp_dodger.
+epp_switch() ->
+ ?EPP_SWITCH.
+
%%% Comment
foo(x) ->
%% Comment
diff --git a/lib/tools/doc/src/cprof.xml b/lib/tools/doc/src/cprof.xml
index 553597837e..bfddb9f5a8 100644
--- a/lib/tools/doc/src/cprof.xml
+++ b/lib/tools/doc/src/cprof.xml
@@ -66,7 +66,7 @@
<func>
<name>analyse() -> {AllCallCount, ModAnalysisList}</name>
<name>analyse(Limit) -> {AllCallCount, ModAnalysisList}</name>
- <name>analyse(Mod) -> ModAnlysis</name>
+ <name>analyse(Mod) -> ModAnalysis</name>
<name>analyse(Mod, Limit) -> ModAnalysis</name>
<fsummary>Collect and analyse call counters.</fsummary>
<type>
diff --git a/otp_versions.table b/otp_versions.table
index 12790c88a8..fbed2ce427 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,4 @@
+OTP-17.5.4 : inets-5.10.8 ssh-3.2.3 # asn1-3.0.4 common_test-1.10.1 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9.1 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4.1 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.2 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8.1 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 :
OTP-17.5.3 : common_test-1.10.1 diameter-1.9.1 erts-6.4.1 snmp-5.1.2 test_server-3.8.1 # asn1-3.0.4 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 inets-5.10.7 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 ssh-3.2.2 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 :
OTP-17.5.2 : inets-5.10.7 ssh-3.2.2 # asn1-3.0.4 common_test-1.10 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.1 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 :
OTP-17.5.1 : ssh-3.2.1 # asn1-3.0.4 common_test-1.10 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 inets-5.10.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.1 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 :