---
.../erlang/0080-otp-update-version-18.3.4.patch | 2964 ++++++++++++++++++++
community/erlang/APKBUILD | 14 +-
2 files changed, 2973 insertions(+), 5 deletions(-)
create mode 100644 community/erlang/0080-otp-update-version-18.3.4.patch
diff --git a/community/erlang/0080-otp-update-version-18.3.4.patch b/community/erlang/0080-otp-update-version-18.3.4.patch
new file mode 100644
index 0000000..cd669f0
--- /dev/null
@@ -0,0 +1,2964 @@
+--- a/OTP_VERSION
++++ b/OTP_VERSION
+@@ -1 +1 @@
+-18.3.2
++18.3.4
+--- a/lib/common_test/doc/src/ct.xml
++++ b/lib/common_test/doc/src/ct.xml
+@@ -601,18 +601,21 @@
+ </func>
+
+ <func>
+- <name>get_timetrap_info() -> {Time, Scale}</name>
++ <name>get_timetrap_info() -> {Time, {Scaling,ScaleVal}}</name>
+ <fsummary>Reads information about the timetrap set for the current
+ test case.</fsummary>
+ <type>
+ <v>Time = integer() | infinity</v>
+- <v>Scale = true | false</v>
++ <v>Scaling = true | false</v>
++ <v>ScaleVal = integer()</v>
+ </type>
+ <desc><marker id="get_timetrap_info-0"/>
+ <p>Reads information about the timetrap set for the current test
+- case. <c>Scale</c> indicates if <c>Common Test</c> will attempt
++ case. <c>Scaling</c> indicates if <c>Common Test</c> will attempt
+ to compensate timetraps automatically for runtime delays
+- introduced by, for example, tools like cover.</p>
++ introduced by, for example, tools like cover. <c>ScaleVal</c> is
++ the value of the current scaling multipler (always 1 if scaling is
++ disabled). Note the <c>Time</c> is not the scaled result.</p>
+ </desc>
+ </func>
+
+@@ -1136,7 +1139,7 @@
+ Opts.</fsummary>
+ <type>
+ <v>Opts = [OptTuples]</v>
+- <v>OptTuples = {dir, TestDirs} | {suite, Suites} | {group, Groups} | {testcase, Cases} | {spec, TestSpecs} | {join_specs, Bool} | {label, Label} | {config, CfgFiles} | {userconfig, UserConfig} | {allow_user_terms, Bool} | {logdir, LogDir} | {silent_connections, Conns} | {stylesheet, CSSFile} | {cover, CoverSpecFile} | {cover_stop, Bool} | {step, StepOpts} | {event_handler, EventHandlers} | {include, InclDirs} | {auto_compile, Bool} | {abort_if_missing_suites, Bool} | {create_priv_dir, CreatePrivDir} | {multiply_timetraps, M} | {scale_timetraps, Bool} | {repeat, N} | {duration, DurTime} | {until, StopTime} | {force_stop, ForceStop} | {decrypt, DecryptKeyOrFile} | {refresh_logs, LogDir} | {logopts, LogOpts} | {verbosity, VLevels} | {basic_html, Bool} | {ct_hooks, CTHs} | {enable_builtin_hooks, Bool} | {release_shell, Bool}</v>
++ <v>OptTuples = {dir, TestDirs} | {suite, Suites} | {group, Groups} | {testcase, Cases} | {spec, TestSpecs} | {join_specs, Bool} | {label, Label} | {config, CfgFiles} | {userconfig, UserConfig} | {allow_user_terms, Bool} | {logdir, LogDir} | {silent_connections, Conns} | {stylesheet, CSSFile} | {cover, CoverSpecFile} | {cover_stop, Bool} | {step, StepOpts} | {event_handler, EventHandlers} | {include, InclDirs} | {auto_compile, Bool} | {abort_if_missing_suites, Bool} | {create_priv_dir, CreatePrivDir} | {multiply_timetraps, M} | {scale_timetraps, Bool} | {repeat, N} | {duration, DurTime} | {until, StopTime} | {force_stop, ForceStop} | {decrypt, DecryptKeyOrFile} | {refresh_logs, LogDir} | {logopts, LogOpts} | {verbosity, VLevels} | {basic_html, Bool} | {esc_chars, Bool} | {ct_hooks, CTHs} | {enable_builtin_hooks, Bool} | {release_shell, Bool}</v>
+ <v>TestDirs = [string()] | string()</v>
+ <v>Suites = [string()] | [atom()] | string() | atom()</v>
+ <v>Cases = [atom()] | atom()</v>
+--- a/lib/common_test/doc/src/ct_run.xml
++++ b/lib/common_test/doc/src/ct_run.xml
+@@ -124,6 +124,7 @@
+ [-duration HHMMSS [-force_stop [skip_rest]]] |
+ [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
+ [-basic_html]
++ [-no_esc_chars]
+ [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
+ CTHModuleN CTHOptsN]
+ [-exit_status ignore_config]
+@@ -162,6 +163,7 @@
+ [-duration HHMMSS [-force_stop [skip_rest]]] |
+ [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
+ [-basic_html]
++ [-no_esc_chars]
+ [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
+ CTHModuleN CTHOptsN]
+ [-exit_status ignore_config]</pre>
+@@ -186,7 +188,8 @@
+ [-muliply_timetraps Multiplier]
+ [-scale_timetraps]
+ [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
+- [-basic_html]</pre>
++ [-basic_html]
++ [-no_esc_chars]</pre>
+ </section>
+
+ <section>
+--- a/lib/common_test/doc/src/ct_telnet.xml
++++ b/lib/common_test/doc/src/ct_telnet.xml
+@@ -64,6 +64,8 @@
+ remaining string terminated) = 0</p></item>
+ <item><p>Polling interval (sleep time between polls) = 1 second</p>
+ </item>
++ <item><p>The TCP_NODELAY option for the telnet socket
++ is disabled (set to <c>false</c>) per default</p></item>
+ </list>
+
+ <p>These parameters can be modified by the user with the following
+@@ -76,7 +78,8 @@
+ {reconnection_interval,Millisec},
+ {keep_alive,Bool},
+ {poll_limit,N},
+- {poll_interval,Millisec}]}.</pre>
++ {poll_interval,Millisec},
++ {tcp_nodelay,Bool}]}.</pre>
+
+ <p><c>Millisec = integer(), N = integer()</c></p>
+
+--- a/lib/common_test/doc/src/notes.xml
++++ b/lib/common_test/doc/src/notes.xml
+@@ -33,6 +33,68 @@
+ <file>notes.xml</file>
+ </header>
+
++<section><title>Common_Test 1.12.1</title>
++
++ <section><title>Fixed Bugs and Malfunctions</title>
++ <list>
++ <item>
++ <p>
++ The <c>nodelay</c> option used to be enabled
++ (<c>true</c>) by default for sockets opened by the Common
++ Test telnet client. This appeared to cause communication
++ problems with telnet servers on some systems, and
++ therefore the option is no longer used. Its value may
++ instead be specified in the telnet connection settings.
++ See the man page for <c>ct_telnet</c> for details. Please
++ note that the interface function <c>connect</c> in
++ <c>unix_telnet</c> has been updated with an extra
++ argument and is now <c>unix_telnet:connect/7</c>.</p>
++ <p>
++ Own Id: OTP-13462 Aux Id: seq13077 </p>
++ </item>
++ <item>
++ <p>
++ Fix bug in cth_surefire: When a pre_init_per_suite hook
++ fails before reaching the
++ cth_surefire:pre_init_per_suite, cth_surefire produced
++ incorrect XML.</p>
++ <p>
++ Own Id: OTP-13513</p>
++ </item>
++ <item>
++ <p>
++ The <c>ct:get_timetrap_info/0</c> function has been
++ updated to return more information about timetrap
++ scaling.</p>
++ <p>
++ Own Id: OTP-13535</p>
++ </item>
++ <item>
++ <p>
++ A problem with stylesheet HTML tags getting incorrectly
++ escaped by Common Test has been corrected.</p>
++ <p>
++ Own Id: OTP-13536</p>
++ </item>
++ <item>
++ <p>
++ The <c>ct_run</c> start flag <c>-no_esc_chars</c> and
++ <c>ct:run_test/1</c> start option <c>{esc_chars,Bool}</c>
++ have been introduced to make it possible to disable
++ automatic escaping of characters. Automatic escaping of
++ special HTML characters printed with <c>io:format/1,2</c>
++ and <c>ct:pal/1,2,3,4</c> was introduced in Common Test
++ 1.12. The new flag/option may be used to disable this
++ feature for backwards compatibility reasons. (The option
++ is also supported in test specifications).</p>
++ <p>
++ Own Id: OTP-13537</p>
++ </item>
++ </list>
++ </section>
++
++</section>
++
+ <section><title>Common_Test 1.12</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+--- a/lib/common_test/doc/src/run_test_chapter.xml
++++ b/lib/common_test/doc/src/run_test_chapter.xml
+@@ -266,6 +266,10 @@
+ <tag><c><![CDATA[-verbosity <levels>]]></c></tag>
+ <item><p>Sets <seealso marker="write_test_chapter#logging">verbosity levels
+ for printouts</seealso>.</p></item>
++
++ <tag><c><![CDATA[-no_esc_chars]]></c></tag>
++ <item><p>Disables automatic escaping of special HTML characters.
++ See the <seealso marker="write_test_chapter#logging">Logging chapter</seealso>.</p></item>
+ </taglist>
+
+ <note><p>Directories passed to <c>Common Test</c> can have either relative or absolute paths.</p></note>
+@@ -802,7 +806,7 @@
+ program</seealso> for an overview of available start flags
+ (as most flags have a corresponding configuration term)</item>
+ <item><seealso marker="write_test_chapter#logging">Logging</seealso>
+- (for terms <c>verbosity</c>, <c>stylesheet</c> and <c>basic_html</c>)</item>
++ (for terms <c>verbosity</c>, <c>stylesheet</c>, <c>basic_html</c> and <c>esc_chars</c>)</item>
+ <item><seealso marker="config_file_chapter#top">External Configuration Data</seealso>
+ (for terms <c>config</c> and <c>userconfig</c>)</item>
+ <item><seealso marker="event_handler_chapter#event_handling">Event
+@@ -887,6 +891,9 @@
+ {basic_html, Bool}.
+ {basic_html, NodeRefs, Bool}.
+
++ {esc_chars, Bool}.
++ {esc_chars, NodeRefs, Bool}.
++
+ {release_shell, Bool}.</pre>
+
+ <p><em>Test terms:</em></p>
+--- a/lib/common_test/doc/src/unix_telnet.xml
++++ b/lib/common_test/doc/src/unix_telnet.xml
+@@ -80,7 +80,7 @@
+
+ <funcs>
+ <func>
+- <name>connect(ConnName, Ip, Port, Timeout, KeepAlive, Extra) -> {ok, Handle} | {error, Reason}</name>
++ <name>connect(ConnName, Ip, Port, Timeout, KeepAlive, TCPNoDelay, Extra) -> {ok, Handle} | {error, Reason}</name>
+ <fsummary>Callback for ct_telnet.erl.</fsummary>
+ <type>
+ <v>ConnName = target_name()</v>
+@@ -88,6 +88,7 @@
+ <v>Port = integer()</v>
+ <v>Timeout = integer()</v>
+ <v>KeepAlive = bool()</v>
++ <v>TCPNoDelay = bool()</v>
+ <v>Extra = target_name() | {Username, Password}</v>
+ <v>Username = string()</v>
+ <v>Password = string()</v>
+--- a/lib/common_test/doc/src/write_test_chapter.xml
++++ b/lib/common_test/doc/src/write_test_chapter.xml
+@@ -1047,9 +1047,15 @@
+
+ <p>Common Test will escape special HTML characters (<, > and &) in printouts
+ to the log file made with <c>ct:pal/4</c> and <c>io:format/2</c>. In order to print
+- strings with HTML tags to the log, use the <c>ct:log/5</c> function. The character escaping
+- feature is per default disabled for <c>ct:log/5</c>, but can be enabled with the
+- <c>esc_chars</c> option.</p>
++ strings with HTML tags to the log, use the <c>ct:log/3,4,5</c> function. The character
++ escaping feature is per default disabled for <c>ct:log/3,4,5</c> but can be enabled with
++ the <c>esc_chars</c> option in the <c>Opts</c> list, see <seealso marker="ct#log-5">
++ <c>ct:log/3,4,5</c></seealso>.</p>
++
++ <p>If the character escaping feature needs to be disabled (typically for backwards
++ compatibility reasons), use the <c>ct_run</c> start flag <c>-no_esc_chars</c>, or the
++ <c>ct:run_test/1</c> start option <c>{esc_chars,Bool}</c> (this start option is also
++ supported in test specifications).</p>
+
+ <p>For more information about log files, see section
+ <seealso marker="run_test_chapter#log_files">Log Files</seealso>
+--- a/lib/common_test/src/ct.erl
++++ b/lib/common_test/src/ct.erl
+@@ -161,9 +161,9 @@ run(TestDirs) ->
+ %%% {repeat,N} | {duration,DurTime} | {until,StopTime} |
+ %%% {force_stop,ForceStop} | {decrypt,DecryptKeyOrFile} |
+ %%% {refresh_logs,LogDir} | {logopts,LogOpts} |
+-%%% {verbosity,VLevels} | {basic_html,Bool} |
+-%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} |
+-%%% {release_shell,Bool}
++%%% {verbosity,VLevels} | {basic_html,Bool} |
++%%% {esc_chars,Bool} | {ct_hooks, CTHs} |
++%%% {enable_builtin_hooks,Bool} | {release_shell,Bool}
+ %%% TestDirs = [string()] | string()
+ %%% Suites = [string()] | [atom()] | string() | atom()
+ %%% Cases = [atom()] | atom()
+--- a/lib/common_test/src/ct_groups.erl
++++ b/lib/common_test/src/ct_groups.erl
+@@ -325,7 +325,7 @@ modify_tc_list1(GrSpecTs, TSCs) ->
+ true ->
+ {[TC|TSCs1],lists:delete(TC,GrSpecTs2)};
+ false ->
+- case lists:keymember(TC, 2, GrSpecTs) of
++ case lists:keysearch(TC, 2, GrSpecTs) of
+ {value,Test} ->
+ {[Test|TSCs1],
+ lists:keydelete(TC, 2, GrSpecTs2)};
+--- a/lib/common_test/src/ct_hooks.erl
++++ b/lib/common_test/src/ct_hooks.erl
+@@ -67,6 +67,8 @@ terminate(Hooks) ->
+ %% tests.
+ -spec init_tc(Mod :: atom(),
+ FuncSpec :: atom() |
++ {ConfigFunc :: init_per_testcase | end_per_testcase,
++ TestCase :: atom()} |
+ {ConfigFunc :: init_per_group | end_per_group,
+ GroupName :: atom(),
+ Properties :: list()},
+@@ -103,7 +105,9 @@ init_tc(_Mod, TC = error_in_suite, Config) ->
+ %% @doc Called as each test case is completed. This includes all configuration
+ %% tests.
+ -spec end_tc(Mod :: atom(),
+- FuncSpec :: atom() |
++ FuncSpec :: atom() |
++ {ConfigFunc :: init_per_testcase | end_per_testcase,
++ TestCase :: atom()} |
+ {ConfigFunc :: init_per_group | end_per_group,
+ GroupName :: atom(),
+ Properties :: list()},
+--- a/lib/common_test/src/ct_logs.erl
++++ b/lib/common_test/src/ct_logs.erl
+@@ -609,7 +609,8 @@ log_timestamp({MS,S,US}) ->
+ ct_log_fd,
+ tc_groupleaders,
+ stylesheet,
+- async_print_jobs}).
++ async_print_jobs,
++ tc_esc_chars}).
+
+ logger(Parent, Mode, Verbosity) ->
+ register(?MODULE,self()),
+@@ -728,14 +729,18 @@ logger(Parent, Mode, Verbosity) ->
+ end
+ end || {Cat,VLvl} <- Verbosity],
+ io:nl(CtLogFd),
+-
++ TcEscChars = case application:get_env(common_test, esc_chars) of
++ {ok,ECBool} -> ECBool;
++ _ -> true
++ end,
+ logger_loop(#logger_state{parent=Parent,
+ log_dir=AbsDir,
+ start_time=Time,
+ orig_GL=group_leader(),
+ ct_log_fd=CtLogFd,
+ tc_groupleaders=[],
+- async_print_jobs=[]}).
++ async_print_jobs=[],
++ tc_esc_chars=TcEscChars}).
+
+ copy_priv_files([SrcF | SrcFs], [DestF | DestFs]) ->
+ case file:copy(SrcF, DestF) of
+@@ -761,20 +766,21 @@ logger_loop(State) ->
+ end,
+ if Importance >= (100-VLvl) ->
+ CtLogFd = State#logger_state.ct_log_fd,
++ DoEscChars = State#logger_state.tc_esc_chars and EscChars,
+ case get_groupleader(Pid, GL, State) of
+ {tc_log,TCGL,TCGLs} ->
+ case erlang:is_process_alive(TCGL) of
+ true ->
+ State1 = print_to_log(SyncOrAsync, Pid,
+ Category, TCGL, Content,
+- EscChars, State),
++ DoEscChars, State),
+ logger_loop(State1#logger_state{
+ tc_groupleaders = TCGLs});
+ false ->
+ %% Group leader is dead, so write to the
+ %% CtLog or unexpected_io log instead
+ unexpected_io(Pid, Category, Importance,
+- Content, CtLogFd, EscChars),
++ Content, CtLogFd, DoEscChars),
+
+ logger_loop(State)
+ end;
+@@ -783,7 +789,7 @@ logger_loop(State) ->
+ %% to ct_log, else write to unexpected_io
+ %% log
+ unexpected_io(Pid, Category, Importance, Content,
+- CtLogFd, EscChars),
++ CtLogFd, DoEscChars),
+ logger_loop(State#logger_state{
+ tc_groupleaders = TCGLs})
+ end;
+@@ -794,7 +800,7 @@ logger_loop(State) ->
+ %% make sure no IO for this test case from the
+ %% CT logger gets rejected
+ test_server:permit_io(GL, self()),
+- print_style(GL, State#logger_state.stylesheet),
++ print_style(GL,GL,State#logger_state.stylesheet),
+ set_evmgr_gl(GL),
+ TCGLs = add_tc_gl(TCPid,GL,State),
+ if not RefreshLog ->
+@@ -882,7 +888,7 @@ create_io_fun(FromPid, CtLogFd, EscChars) ->
+ {_HdOrFt,S,A} -> {false,S,A};
+ {S,A} -> {true,S,A}
+ end,
+- try io_lib:format(Str,Args) of
++ try io_lib:format(Str, Args) of
+ IoStr when Escapable, EscChars, IoList == [] ->
+ escape_chars(IoStr);
+ IoStr when Escapable, EscChars ->
+@@ -925,7 +931,12 @@ print_to_log(sync, FromPid, Category, TCGL, Content, EscChars, State) ->
+ if FromPid /= TCGL ->
+ IoFun = create_io_fun(FromPid, CtLogFd, EscChars),
+ IoList = lists:foldl(IoFun, [], Content),
+- io:format(TCGL,["$tc_html","~ts"], [IoList]);
++ try io:format(TCGL,["$tc_html","~ts"], [IoList]) of
++ ok -> ok
++ catch
++ _:_ ->
++ io:format(TCGL,"~ts", [IoList])
++ end;
+ true ->
+ unexpected_io(FromPid, Category, ?MAX_IMPORTANCE, Content,
+ CtLogFd, EscChars)
+@@ -958,7 +969,10 @@ print_to_log(async, FromPid, Category, TCGL, Content, EscChars, State) ->
+ _:terminated ->
+ unexpected_io(FromPid, Category,
+ ?MAX_IMPORTANCE,
+- Content, CtLogFd, EscChars)
++ Content, CtLogFd, EscChars);
++ _:_ ->
++ io:format(TCGL, "~ts",
++ [lists:foldl(IoFun,[],Content)])
+ end;
+ false ->
+ unexpected_io(FromPid, Category,
+@@ -1099,26 +1113,27 @@ open_ctlog(MiscIoName) ->
+ "View I/O logged after the test run</a></li>\n</ul>\n",
+ [MiscIoName,MiscIoName]),
+
+- print_style(Fd,undefined),
++ print_style(Fd,group_leader(),undefined),
+ io:format(Fd,
+ xhtml("<br><h2>Progress Log</h2>\n<pre>\n",
+ "<br />\n<h4>PROGRESS LOG</h4>\n<pre>\n"), []),
+ Fd.
+
+-print_style(Fd,undefined) ->
++print_style(Fd,GL,undefined) ->
+ case basic_html() of
+ true ->
+- io:format(Fd,
+- "<style>\n"
+- "div.ct_internal { background:lightgrey; color:black; }\n"
+- "div.default { background:lightgreen; color:black; }\n"
+- "</style>\n",
+- []);
++ Style = "<style>\n
++ div.ct_internal { background:lightgrey; color:black; }\n
++ div.default { background:lightgreen; color:black; }\n
++ </style>\n",
++ if Fd == GL -> io:format(["$tc_html",Style], []);
++ true -> io:format(Fd, Style, [])
++ end;
+ _ ->
+ ok
+ end;
+
+-print_style(Fd,StyleSheet) ->
++print_style(Fd,GL,StyleSheet) ->
+ case file:read_file(StyleSheet) of
+ {ok,Bin} ->
+ Str = b2s(Bin,encoding(StyleSheet)),
+@@ -1131,23 +1146,30 @@ print_style(Fd,StyleSheet) ->
+ N1 -> N1
+ end,
+ if (Pos0 == 0) and (Pos1 /= 0) ->
+- print_style_error(Fd,StyleSheet,missing_style_start_tag);
++ print_style_error(Fd,GL,StyleSheet,missing_style_start_tag);
+ (Pos0 /= 0) and (Pos1 == 0) ->
+- print_style_error(Fd,StyleSheet,missing_style_end_tag);
++ print_style_error(Fd,GL,StyleSheet,missing_style_end_tag);
+ Pos0 /= 0 ->
+ Style = string:sub_string(Str,Pos0,Pos1+7),
+- io:format(Fd,"~ts\n",[Style]);
++ if Fd == GL -> io:format(Fd,["$tc_html","~ts\n"],[Style]);
++ true -> io:format(Fd,"~ts\n",[Style])
++ end;
+ Pos0 == 0 ->
+- io:format(Fd,"<style>~ts</style>\n",[Str])
++ if Fd == GL -> io:format(Fd,["$tc_html","<style>\n~ts</style>\n"],[Str]);
++ true -> io:format(Fd,"<style>\n~ts</style>\n",[Str])
++ end
+ end;
+ {error,Reason} ->
+- print_style_error(Fd,StyleSheet,Reason)
++ print_style_error(Fd,GL,StyleSheet,Reason)
+ end.
+
+-print_style_error(Fd,StyleSheet,Reason) ->
+- io:format(Fd,"\n<!-- Failed to load stylesheet ~ts: ~p -->\n",
+- [StyleSheet,Reason]),
+- print_style(Fd,undefined).
++print_style_error(Fd,GL,StyleSheet,Reason) ->
++ IO = io_lib:format("\n<!-- Failed to load stylesheet ~ts: ~p -->\n",
++ [StyleSheet,Reason]),
++ if Fd == GL -> io:format(Fd,["$tc_html",IO],[]);
++ true -> io:format(Fd,IO,[])
++ end,
++ print_style(Fd,GL,undefined).
+
+ close_ctlog(Fd) ->
+ io:format(Fd, "\n</pre>\n", []),
+--- a/lib/common_test/src/ct_master.erl
++++ b/lib/common_test/src/ct_master.erl
+@@ -27,7 +27,7 @@
+ -export([run_on_node/2,run_on_node/3]).
+ -export([run_test/1,run_test/2]).
+ -export([get_event_mgr_ref/0]).
+--export([basic_html/1]).
++-export([basic_html/1,esc_chars/1]).
+
+ -export([abort/0,abort/1,progress/0]).
+
+@@ -317,6 +317,16 @@ basic_html(Bool) ->
+ ok.
+
+ %%%-----------------------------------------------------------------
++%%% @spec esc_chars(Bool) -> ok
++%%% Bool = true | false
++%%%
++%%% @doc If set to false, the ct_master logs will be written without
++%%% special characters being escaped in the HTML logs.
++esc_chars(Bool) ->
++ application:set_env(common_test_master, esc_chars, Bool),
++ ok.
++
++%%%-----------------------------------------------------------------
+ %%% MASTER, runs on central controlling node.
+ %%%-----------------------------------------------------------------
+ start_master(NodeOptsList) ->
+--- a/lib/common_test/src/ct_run.erl
++++ b/lib/common_test/src/ct_run.erl
+@@ -65,6 +65,7 @@
+ logdir,
+ logopts = [],
+ basic_html,
++ esc_chars = true,
+ verbosity = [],
+ config = [],
+ event_handlers = [],
+@@ -346,6 +347,15 @@ script_start1(Parent, Args) ->
+ application:set_env(common_test, basic_html, true),
+ true
+ end,
++ %% esc_chars - used by ct_logs
++ EscChars = case proplists:get_value(no_esc_chars, Args) of
++ undefined ->
++ application:set_env(common_test, esc_chars, true),
++ undefined;
++ _ ->
++ application:set_env(common_test, esc_chars, false),
++ false
++ end,
+ %% disable_log_cache - used by ct_logs
+ case proplists:get_value(disable_log_cache, Args) of
+ undefined ->
+@@ -359,6 +369,7 @@ script_start1(Parent, Args) ->
+ cover = Cover, cover_stop = CoverStop,
+ logdir = LogDir, logopts = LogOpts,
+ basic_html = BasicHtml,
++ esc_chars = EscChars,
+ verbosity = Verbosity,
+ event_handlers = EvHandlers,
+ ct_hooks = CTHooks,
+@@ -587,6 +598,17 @@ combine_test_opts(TS, Specs, Opts) ->
+ BHBool
+ end,
+
++ EscChars =
++ case choose_val(Opts#opts.esc_chars,
++ TSOpts#opts.esc_chars) of
++ undefined ->
++ true;
++ ECBool ->
++ application:set_env(common_test, esc_chars,
++ ECBool),
++ ECBool
++ end,
++
+ Opts#opts{label = Label,
+ profile = Profile,
+ testspec_files = Specs,
+@@ -595,6 +617,7 @@ combine_test_opts(TS, Specs, Opts) ->
+ logdir = which(logdir, LogDir),
+ logopts = AllLogOpts,
+ basic_html = BasicHtml,
++ esc_chars = EscChars,
+ verbosity = AllVerbosity,
+ silent_connections = AllSilentConns,
+ config = TSOpts#opts.config,
+@@ -795,6 +818,7 @@ script_usage() ->
+ "\n\t [-scale_timetraps]"
+ "\n\t [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
+ "\n\t [-basic_html]"
++ "\n\t [-no_esc_chars]"
+ "\n\t [-repeat N] |"
+ "\n\t [-duration HHMMSS [-force_stop [skip_rest]]] |"
+ "\n\t [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]"
+@@ -822,6 +846,7 @@ script_usage() ->
+ "\n\t [-scale_timetraps]"
+ "\n\t [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
+ "\n\t [-basic_html]"
++ "\n\t [-no_esc_chars]"
+ "\n\t [-repeat N] |"
+ "\n\t [-duration HHMMSS [-force_stop [skip_rest]]] |"
+ "\n\t [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]\n\n"),
+@@ -847,7 +872,8 @@ script_usage() ->
+ "\n\t [-multiply_timetraps N]"
+ "\n\t [-scale_timetraps]"
+ "\n\t [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
+- "\n\t [-basic_html]\n\n").
++ "\n\t [-basic_html]"
++ "\n\t [-no_esc_chars]\n\n").
+
+ %%%-----------------------------------------------------------------
+ %%% @hidden
+@@ -1089,7 +1115,17 @@ run_test2(StartOpts) ->
+ application:set_env(common_test, basic_html, BasicHtmlBool),
+ BasicHtmlBool
+ end,
+-
++ %% esc_chars - used by ct_logs
++ EscChars =
++ case proplists:get_value(esc_chars, StartOpts) of
++ undefined ->
++ application:set_env(common_test, esc_chars, true),
++ undefined;
++ EscCharsBool ->
++ application:set_env(common_test, esc_chars, EscCharsBool),
++ EscCharsBool
++ end,
++ %% disable_log_cache - used by ct_logs
+ case proplists:get_value(disable_log_cache, StartOpts) of
+ undefined ->
+ application:set_env(common_test, disable_log_cache, false);
+@@ -1104,6 +1140,7 @@ run_test2(StartOpts) ->
+ cover = Cover, cover_stop = CoverStop,
+ step = Step, logdir = LogDir,
+ logopts = LogOpts, basic_html = BasicHtml,
++ esc_chars = EscChars,
+ config = CfgFiles,
+ verbosity = Verbosity,
+ event_handlers = EvHandlers,
+@@ -1445,6 +1482,7 @@ get_data_for_node(#testspec{label = Labels,
+ logdir = LogDirs,
+ logopts = LogOptsList,
+ basic_html = BHs,
++ esc_chars = EscChs,
+ stylesheet = SSs,
+ verbosity = VLvls,
+ silent_connections = SilentConnsList,
+@@ -1472,6 +1510,7 @@ get_data_for_node(#testspec{label = Labels,
+ LOs -> LOs
+ end,
+ BasicHtml = proplists:get_value(Node, BHs),
++ EscChars = proplists:get_value(Node, EscChs),
+ Stylesheet = proplists:get_value(Node, SSs),
+ Verbosity = case proplists:get_value(Node, VLvls) of
+ undefined -> [];
+@@ -1498,6 +1537,7 @@ get_data_for_node(#testspec{label = Labels,
+ logdir = LogDir,
+ logopts = LogOpts,
+ basic_html = BasicHtml,
++ esc_chars = EscChars,
+ stylesheet = Stylesheet,
+ verbosity = Verbosity,
+ silent_connections = SilentConns,
+@@ -2182,10 +2222,18 @@ do_run_test(Tests, Skip, Opts0) ->
+ %% test_server needs to know the include path too
+ InclPath = case application:get_env(common_test, include) of
+ {ok,Incls} -> Incls;
+- _ -> []
++ _ -> []
+ end,
+ application:set_env(test_server, include, InclPath),
+
++ %% copy the escape characters setting to test_server
++ EscChars =
++ case application:get_env(common_test, esc_chars) of
++ {ok,ECBool} -> ECBool;
++ _ -> true
++ end,
++ application:set_env(test_server, esc_chars, EscChars),
++
+ test_server_ctrl:start_link(local),
+
+ %% let test_server expand the test tuples and count no of cases
+@@ -3071,6 +3119,10 @@ opts2args(EnvStartOpts) ->
+ [{basic_html,[]}];
+ ({basic_html,false}) ->
+ [];
++ ({esc_chars,false}) ->
++ [{no_esc_chars,[]}];
++ ({esc_chars,true}) ->
++ [];
+ ({event_handler,EH}) when is_atom(EH) ->
+ [{event_handler,[atom_to_list(EH)]}];
+ ({event_handler,EHs}) when is_list(EHs) ->
+--- a/lib/common_test/src/ct_telnet.erl
++++ b/lib/common_test/src/ct_telnet.erl
+@@ -42,7 +42,8 @@
+ %% {reconnection_interval,Millisec},
+ %% {keep_alive,Bool},
+ %% {poll_limit,N},
+-%% {poll_interval,Millisec}]}.</pre>
++%% {poll_interval,Millisec},
++%% {tcp_nodelay,Bool}]}.</pre>
+ %% <p><code>Millisec = integer(), N = integer()</code></p>
+ %% <p>Enter the <code>telnet_settings</code> term in a configuration
+ %% file included in the test and ct_telnet will retrieve the information
+@@ -182,7 +183,8 @@
+ conn_to=?DEFAULT_TIMEOUT,
+ com_to=?DEFAULT_TIMEOUT,
+ reconns=?RECONNS,
+- reconn_int=?RECONN_TIMEOUT}).
++ reconn_int=?RECONN_TIMEOUT,
++ tcp_nodelay=false}).
+
+ %%%-----------------------------------------------------------------
+ %%% @spec open(Name) -> {ok,Handle} | {error,Reason}
+@@ -602,8 +604,18 @@ init(Name,{Ip,Port,Type},{TargetMod,KeepAlive,Extra}) ->
+ Settings ->
+ set_telnet_defaults(Settings,#state{})
+ end,
+- case catch TargetMod:connect(Name,Ip,Port,S0#state.conn_to,
+- KeepAlive,Extra) of
++ %% Handle old user versions of TargetMod
++ code:ensure_loaded(TargetMod),
++ try
++ case erlang:function_exported(TargetMod,connect,7) of
++ true ->
++ TargetMod:connect(Name,Ip,Port,S0#state.conn_to,
++ KeepAlive,S0#state.tcp_nodelay,Extra);
++ false ->
++ TargetMod:connect(Name,Ip,Port,S0#state.conn_to,
++ KeepAlive,Extra)
++ end
++ of
+ {ok,TelnPid} ->
+ put({ct_telnet_pid2name,TelnPid},Name),
+ S1 = S0#state{host=Ip,
+@@ -625,15 +637,18 @@ init(Name,{Ip,Port,Type},{TargetMod,KeepAlive,Extra}) ->
+ "Connection timeout: ~p\n"
+ "Keep alive: ~w\n"
+ "Poll limit: ~w\n"
+- "Poll interval: ~w",
++ "Poll interval: ~w\n"
++ "TCP nodelay: ~w",
+ [Ip,Port,S1#state.com_to,S1#state.reconns,
+ S1#state.reconn_int,S1#state.conn_to,KeepAlive,
+- S1#state.poll_limit,S1#state.poll_interval]),
++ S1#state.poll_limit,S1#state.poll_interval,
++ S1#state.tcp_nodelay]),
+ {ok,TelnPid,S1};
+- {'EXIT',Reason} ->
+- {error,Reason};
+ Error ->
+ Error
++ catch
++ _:Reason ->
++ {error,Reason}
+ end.
+
+ type(telnet) -> ip;
+@@ -653,6 +668,8 @@ set_telnet_defaults([{poll_limit,PL}|Ss],S) ->
+ set_telnet_defaults(Ss,S#state{poll_limit=PL});
+ set_telnet_defaults([{poll_interval,PI}|Ss],S) ->
+ set_telnet_defaults(Ss,S#state{poll_interval=PI});
++set_telnet_defaults([{tcp_nodelay,NoDelay}|Ss],S) ->
++ set_telnet_defaults(Ss,S#state{tcp_nodelay=NoDelay});
+ set_telnet_defaults([Unknown|Ss],S) ->
+ force_log(S,error,
+ "Bad element in telnet_settings: ~p",[Unknown]),
+@@ -794,8 +811,17 @@ reconnect(Ip,Port,N,State=#state{name=Name,
+ keep_alive=KeepAlive,
+ extra=Extra,
+ conn_to=ConnTo,
+- reconn_int=ReconnInt}) ->
+- case TargetMod:connect(Name,Ip,Port,ConnTo,KeepAlive,Extra) of
++ reconn_int=ReconnInt,
++ tcp_nodelay=NoDelay}) ->
++ %% Handle old user versions of TargetMod
++ ConnResult =
++ case erlang:function_exported(TargetMod,connect,7) of
++ true ->
++ TargetMod:connect(Name,Ip,Port,ConnTo,KeepAlive,NoDelay,Extra);
++ false ->
++ TargetMod:connect(Name,Ip,Port,ConnTo,KeepAlive,Extra)
++ end,
++ case ConnResult of
+ {ok,NewPid} ->
+ put({ct_telnet_pid2name,NewPid},Name),
+ {ok, NewPid, State#state{teln_pid=NewPid}};
+--- a/lib/common_test/src/ct_telnet_client.erl
++++ b/lib/common_test/src/ct_telnet_client.erl
+@@ -35,7 +35,7 @@
+
+ %%-define(debug, true).
+
+--export([open/2, open/3, open/4, open/5, close/1]).
++-export([open/2, open/3, open/4, open/5, open/6, close/1]).
+ -export([send_data/2, send_data/3, get_data/1]).
+
+ -define(TELNET_PORT, 23).
+@@ -70,19 +70,22 @@
+ -record(state,{conn_name, get_data, keep_alive=true, log_pos=1}).
+
+ open(Server, ConnName) ->
+- open(Server, ?TELNET_PORT, ?OPEN_TIMEOUT, true, ConnName).
++ open(Server, ?TELNET_PORT, ?OPEN_TIMEOUT, true, false, ConnName).
+
+ open(Server, Port, ConnName) ->
+- open(Server, Port, ?OPEN_TIMEOUT, true, ConnName).
++ open(Server, Port, ?OPEN_TIMEOUT, true, false, ConnName).
+
+ open(Server, Port, Timeout, ConnName) ->
+- open(Server, Port, Timeout, true, ConnName).
++ open(Server, Port, Timeout, true, false, ConnName).
+
+ open(Server, Port, Timeout, KeepAlive, ConnName) ->
++ open(Server, Port, Timeout, KeepAlive, false, ConnName).
++
++open(Server, Port, Timeout, KeepAlive, NoDelay, ConnName) ->
+ Self = self(),
+ Pid = spawn(fun() ->
+ init(Self, Server, Port, Timeout,
+- KeepAlive, ConnName)
++ KeepAlive, NoDelay, ConnName)
+ end),
+ receive
+ {open,Pid} ->
+@@ -114,8 +117,8 @@ get_data(Pid) ->
+
+ %%%-----------------------------------------------------------------
+ %%% Internal functions
+-init(Parent, Server, Port, Timeout, KeepAlive, ConnName) ->
+- case gen_tcp:connect(Server, Port, [list,{packet,0},{nodelay,true}], Timeout) of
++init(Parent, Server, Port, Timeout, KeepAlive, NoDelay, ConnName) ->
++ case gen_tcp:connect(Server, Port, [list,{packet,0},{nodelay,NoDelay}], Timeout) of
+ {ok,Sock} ->
+ dbg("~p connected to: ~p (port: ~w, keep_alive: ~w)\n",
+ [ConnName,Server,Port,KeepAlive]),
+--- a/lib/common_test/src/ct_testspec.erl
++++ b/lib/common_test/src/ct_testspec.erl
+@@ -1146,8 +1146,9 @@ should_be_added(Tag,Node,_Data,Spec) ->
+ if
+ %% list terms *without* possible duplicates here
+ Tag == logdir; Tag == logopts;
+- Tag == basic_html; Tag == label;
+- Tag == auto_compile; Tag == abort_if_missing_suites;
++ Tag == basic_html; Tag == esc_chars;
++ Tag == label; Tag == auto_compile;
++ Tag == abort_if_missing_suites;
+ Tag == stylesheet; Tag == verbosity;
+ Tag == silent_connections ->
+ lists:keymember(ref2node(Node,Spec#testspec.nodes),1,
+@@ -1544,6 +1545,8 @@ valid_terms() ->
+ {logopts,3},
+ {basic_html,2},
+ {basic_html,3},
++ {esc_chars,2},
++ {esc_chars,3},
+ {verbosity,2},
+ {verbosity,3},
+ {silent_connections,2},
+--- a/lib/common_test/src/ct_util.erl
++++ b/lib/common_test/src/ct_util.erl
+@@ -459,6 +459,7 @@ loop(Mode,TestData,StartDir) ->
+ error:badarg -> []
+ end,
+ ct_hooks:terminate(Callbacks),
++
+ close_connections(ets:tab2list(?conn_table)),
+ ets:delete(?conn_table),
+ ets:delete(?board_table),
+--- a/lib/common_test/src/ct_util.hrl
++++ b/lib/common_test/src/ct_util.hrl
+@@ -37,6 +37,7 @@
+ logdir=["."],
+ logopts=[],
+ basic_html=[],
++ esc_chars=[],
+ verbosity=[],
+ silent_connections=[],
+ cover=[],
+--- a/lib/common_test/src/cth_log_redirect.erl
++++ b/lib/common_test/src/cth_log_redirect.erl
+@@ -130,7 +130,14 @@ handle_event(Event, #eh_state{log_func = LogFunc} = State) ->
+ tag_event(Event)),
+ if is_list(SReport) ->
+ SaslHeader = format_header(State),
+- ct_logs:LogFunc(sasl, ?STD_IMPORTANCE, SaslHeader, SReport, []);
++ case LogFunc of
++ tc_log ->
++ ct_logs:tc_log(sasl, ?STD_IMPORTANCE,
++ SaslHeader, SReport, [], []);
++ tc_log_async ->
++ ct_logs:tc_log_async(sasl, ?STD_IMPORTANCE,
++ SaslHeader, SReport, [])
++ end;
+ true -> %% Report is an atom if no logging is to be done
+ ignore
+ end
+@@ -139,7 +146,14 @@ handle_event(Event, #eh_state{log_func = LogFunc} = State) ->
+ tag_event(Event),io_lib),
+ if is_list(EReport) ->
+ ErrHeader = format_header(State),
+- ct_logs:LogFunc(error_logger, ?STD_IMPORTANCE, ErrHeader, EReport, []);
++ case LogFunc of
++ tc_log ->
++ ct_logs:tc_log(error_logger, ?STD_IMPORTANCE,
++ ErrHeader, EReport, [], []);
++ tc_log_async ->
++ ct_logs:tc_log_async(error_logger, ?STD_IMPORTANCE,
++ ErrHeader, EReport, [])
++ end;
+ true -> %% Report is an atom if no logging is to be done
+ ignore
+ end,
+--- a/lib/common_test/src/cth_surefire.erl
++++ b/lib/common_test/src/cth_surefire.erl
+@@ -82,7 +82,8 @@ init(Path, Opts) ->
+ url_base = proplists:get_value(url_base,Opts),
+ timer = ?now }.
+
+-pre_init_per_suite(Suite,SkipOrFail,State) when is_tuple(SkipOrFail) ->
++pre_init_per_suite(Suite,SkipOrFail,#state{ test_cases = [] } = State)
++ when is_tuple(SkipOrFail) ->
+ {SkipOrFail, init_tc(State#state{curr_suite = Suite,
+ curr_suite_ts = ?now},
+ SkipOrFail) };
+--- a/lib/common_test/src/unix_telnet.erl
++++ b/lib/common_test/src/unix_telnet.erl
+@@ -27,7 +27,8 @@
+ %%% {port,PortNum}, % optional
+ %%% {username,UserName},
+ %%% {password,Password},
+-%%% {keep_alive,Bool}]}. % optional</pre>
++%%% {keep_alive,Bool}, % optional
++%%% {tcp_nodely,Bool}]} % optional</pre>
+ %%%
+ %%% <p>To communicate via telnet to the host specified by
+ %%% <code>HostNameOrIpAddress</code>, use the interface functions in
+@@ -55,7 +56,7 @@
+ -compile(export_all).
+
+ %% Callbacks for ct_telnet.erl
+--export([connect/6,get_prompt_regexp/0]).
++-export([connect/7,get_prompt_regexp/0]).
+ -import(ct_telnet,[start_gen_log/1,log/4,end_gen_log/0]).
+
+ -define(username,"login: ").
+@@ -82,6 +83,7 @@ get_prompt_regexp() ->
+ %%% Port = integer()
+ %%% Timeout = integer()
+ %%% KeepAlive = bool()
++%%% TCPNoDelay = bool()
+ %%% Extra = ct:target_name() | {Username,Password}
+ %%% Username = string()
+ %%% Password = string()
+@@ -91,25 +93,25 @@ get_prompt_regexp() ->
+ %%% @doc Callback for ct_telnet.erl.
+ %%%
+ %%% <p>Setup telnet connection to a unix host.</p>
+-connect(ConnName,Ip,Port,Timeout,KeepAlive,Extra) ->
++connect(ConnName,Ip,Port,Timeout,KeepAlive,TCPNoDelay,Extra) ->
+ case Extra of
+ {Username,Password} ->
+- connect1(ConnName,Ip,Port,Timeout,KeepAlive,
++ connect1(ConnName,Ip,Port,Timeout,KeepAlive,TCPNoDelay,
+ Username,Password);
+ KeyOrName ->
+ case get_username_and_password(KeyOrName) of
+ {ok,{Username,Password}} ->
+- connect1(ConnName,Ip,Port,Timeout,KeepAlive,
++ connect1(ConnName,Ip,Port,Timeout,KeepAlive,TCPNoDelay,
+ Username,Password);
+ Error ->
+ Error
+ end
+ end.
+
+-connect1(Name,Ip,Port,Timeout,KeepAlive,Username,Password) ->
++connect1(Name,Ip,Port,Timeout,KeepAlive,TCPNoDelay,Username,Password) ->
+ start_gen_log("unix_telnet connect"),
+ Result =
+- case ct_telnet_client:open(Ip,Port,Timeout,KeepAlive,Name) of
++ case ct_telnet_client:open(Ip,Port,Timeout,KeepAlive,TCPNoDelay,Name) of
+ {ok,Pid} ->
+ case ct_telnet:silent_teln_expect(Name,Pid,[],
+ [prompt],?prx,[]) of
+--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl
++++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl
+@@ -1,8 +1,8 @@
+-%%
+-%% %CopyrightBegin%
+-%%
+-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+-%%
++%%
++%% %CopyrightBegin%
++%%
++%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
++%%
+ %% Licensed under the Apache License, Version 2.0 (the "License");
+ %% you may not use this file except in compliance with the License.
+ %% You may obtain a copy of the License at
+@@ -14,46 +14,46 @@
+ %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ %% See the License for the specific language governing permissions and
+ %% limitations under the License.
+-%%
+-%% %CopyrightEnd%
+-%%
+-
+--module(ct_update_config_SUITE).
+-
+--suite_defaults([{timetrap, {minutes, 10}}]).
+-
+-%% Note: This directive should only be used in test suites.
+--compile(export_all).
+-
+--include("ct.hrl").
+-
+--define(now, os:timestamp()).
+-
+-%% Test server callback functions
+-init_per_suite(Config) ->
+- [{init_per_suite,?now}|Config].
+-
+-end_per_suite(_Config) ->
+- ok.
+-
+-init_per_testcase(_TestCase, Config) ->
+- [{init_per_testcase,?now}|Config].
+-
+-end_per_testcase(_TestCase, _Config) ->
+- ok.
+-
+-init_per_group(GroupName, Config) ->
+- [{init_per_group,?now}|Config].
+-
+-end_per_group(GroupName, Config) ->
+- ok.
+-
+-all() ->
+- [{group,group1}].
+-
+-groups() ->
+- [{group1,[],[test_case]}].
+-
+-%% Test cases starts here.
+-test_case(Config) when is_list(Config) ->
+- ok.
++%%
++%% %CopyrightEnd%
++%%
++
++-module(ct_update_config_SUITE).
++
++-suite_defaults([{timetrap, {minutes, 10}}]).
++
++%% Note: This directive should only be used in test suites.
++-compile(export_all).
++
++-include("ct.hrl").
++
++-define(now, ct_test_support:unique_timestamp()).
++
++%% Test server callback functions
++init_per_suite(Config) ->
++ [{init_per_suite,?now}|Config].
++
++end_per_suite(_Config) ->
++ ok.
++
++init_per_testcase(_TestCase, Config) ->
++ [{init_per_testcase,?now}|Config].
++
++end_per_testcase(_TestCase, _Config) ->
++ ok.
++
++init_per_group(GroupName, Config) ->
++ [{init_per_group,?now}|Config].
++
++end_per_group(GroupName, Config) ->
++ ok.
++
++all() ->
++ [{group,group1}].
++
++groups() ->
++ [{group1,[],[test_case]}].
++
++%% Test cases starts here.
++test_case(Config) when is_list(Config) ->
++ ok.
+--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
++++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
+@@ -90,7 +90,7 @@ id(Opts) ->
+ gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, id, [Opts]}}),
+ ct:log("~w:id called", [?MODULE]),
+- os:timestamp().
++ ct_test_support:unique_timestamp().
+
+ %% @doc Called before init_per_suite is called. Note that this callback is
+ %% only called if the CTH is added before init_per_suite is run (eg. in a test
+--- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl
++++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl
+@@ -25,7 +25,7 @@
+ -include_lib("common_test/src/ct_util.hrl").
+ -include_lib("common_test/include/ct_event.hrl").
+
+--define(now, os:timestamp()).
++-define(now, ct_test_support:unique_timestamp()).
+
+ %% CT Hooks
+ -compile(export_all).
+--- a/lib/common_test/test/ct_pre_post_test_io_SUITE.erl
++++ b/lib/common_test/test/ct_pre_post_test_io_SUITE.erl
+@@ -44,13 +44,29 @@
+ %% instance, the tests need to be performed on a separate node (or
+ %% there will be clashes with logging processes etc).
+ %%--------------------------------------------------------------------
++suite() ->
++ [{ct_hooks,[ts_install_cth]},
++ {timetrap,{seconds,120}}].
++
++all() ->
++ [
++ pre_post_io
++ ].
++
+ init_per_suite(Config) ->
+- DataDir = ?config(data_dir, Config),
+- CTH = filename:join(DataDir, "cth_ctrl.erl"),
+- ct:pal("Compiling ~p: ~p",
+- [CTH,compile:file(CTH,[{outdir,DataDir},debug_info])]),
+- ct_test_support:init_per_suite([{path_dirs,[DataDir]},
+- {start_sasl,true} | Config]).
++ TTInfo = {_T,{_Scaled,ScaleVal}} = ct:get_timetrap_info(),
++ ct:pal("Timetrap info = ~w", [TTInfo]),
++ if ScaleVal > 1 ->
++ {skip,"Skip on systems running e.g. cover or debug!"};
++ ScaleVal =< 1 ->
++ DataDir = ?config(data_dir, Config),
++ CTH = filename:join(DataDir, "cth_ctrl.erl"),
++ ct:pal("Compiling ~p: ~p",
++ [CTH,compile:file(CTH,[{outdir,DataDir},
++ debug_info])]),
++ ct_test_support:init_per_suite([{path_dirs,[DataDir]},
++ {start_sasl,true} | Config])
++ end.
+
+ end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+@@ -61,13 +77,6 @@ init_per_testcase(TestCase, Config) ->
+ end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+-suite() -> [{ct_hooks,[ts_install_cth]}].
+-
+-all() ->
+- [
+- pre_post_io
+- ].
+-
+ %%--------------------------------------------------------------------
+ %% TEST CASES
+ %%--------------------------------------------------------------------
+@@ -90,31 +99,50 @@ pre_post_io(Config) ->
+ %%!--------------------------------------------------------------------
+
+ spawn(fun() ->
+- ct:pal("CONTROLLER: Started!", []),
++ ct:pal("CONTROLLER: Starting test run #1...", []),
+ %% --- test run 1 ---
+- ct:sleep(3000),
+- ct:pal("CONTROLLER: Handle remote events = true", []),
+- ok = ct_test_support:ct_rpc({cth_log_redirect,
+- handle_remote_events,
+- [true]}, Config),
+- ct:sleep(2000),
+- ct:pal("CONTROLLER: Proceeding with test run #1!", []),
++ try_loop(ct_test_support, ct_rpc, [{cth_log_redirect,
++ handle_remote_events,
++ [true]}, Config], 3000),
++ CTLoggerPid1 = ct_test_support:ct_rpc({erlang,whereis,
++ [ct_logs]}, Config),
++ ct:pal("CONTROLLER: Logger = ~w~nHandle remote events = true",
++ [CTLoggerPid1]),
++ ct:sleep(5000),
++ ct:pal("CONTROLLER: Proceeding with test run #1...", []),
+ ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
+ ct:sleep(6000),
+- ct:pal("CONTROLLER: Proceeding with shutdown #1!", []),
++ ct:pal("CONTROLLER: Proceeding with shutdown #1...", []),
+ ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
++ try_loop(fun() ->
++ false = ct_test_support:ct_rpc({erlang,
++ is_process_alive,
++ [CTLoggerPid1]},
++ Config)
++ end, 3000),
++ ct:pal("CONTROLLER: Shutdown #1 complete!", []),
++ ct:pal("CONTROLLER: Starting test run #2...", []),
+ %% --- test run 2 ---
+- ct:sleep(3000),
+- ct:pal("CONTROLLER: Handle remote events = true", []),
+- ok = ct_test_support:ct_rpc({cth_log_redirect,
+- handle_remote_events,
+- [true]}, Config),
+- ct:sleep(2000),
+- ct:pal("CONTROLLER: Proceeding with test run #2!", []),
++ try_loop(ct_test_support, ct_rpc, [{cth_log_redirect,
++ handle_remote_events,
++ [true]}, Config], 3000),
++ CTLoggerPid2 = ct_test_support:ct_rpc({erlang,whereis,
++ [ct_logs]}, Config),
++ ct:pal("CONTROLLER: Logger = ~w~nHandle remote events = true",
++ [CTLoggerPid2]),
++ ct:sleep(5000),
++ ct:pal("CONTROLLER: Proceeding with test run #2...", []),
+ ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
+ ct:sleep(6000),
+- ct:pal("CONTROLLER: Proceeding with shutdown #2!", []),
+- ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config)
++ ct:pal("CONTROLLER: Proceeding with shutdown #2...", []),
++ ok = ct_test_support:ct_rpc({cth_ctrl,proceed,[]}, Config),
++ try_loop(fun() ->
++ false = ct_test_support:ct_rpc({erlang,
++ is_process_alive,
++ [CTLoggerPid2]},
++ Config)
++ end, 3000),
++ ct:pal("CONTROLLER: Shutdown #2 complete!", [])
+ end),
+ ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+@@ -157,7 +185,7 @@ pre_post_io(Config) ->
+ Counters
+ end, {pre,0,0,0,0}, Ts),
+ [_|Counters] = tuple_to_list(PrePostIOEntries),
+- ct:log("Entries in the Pre/Post Test IO Log: ~p", [Counters]),
++ ct:pal("Entries in the Pre/Post Test IO Log: ~w", [Counters]),
+ case [C || C <- Counters, C < 2] of
+ [] ->
+ ok;
+@@ -183,7 +211,7 @@ pre_post_io(Config) ->
+ [LogN,ErrN+1];
+ (_, Counters) -> Counters
+ end, [0,0], Ts),
+- ct:log("Entries in the Unexpected IO Log: ~p", [UnexpIOEntries]),
++ ct:log("Entries in the Unexpected IO Log: ~w", [UnexpIOEntries]),
+ case [N || N <- UnexpIOEntries, N < 2] of
+ [] ->
+ ok;
+@@ -208,6 +236,38 @@ setup(Test, Config) ->
+ reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
++try_loop(_Fun, 0) ->
++ ct:pal("WARNING! Fun never succeeded!", []),
++ gave_up;
++try_loop(Fun, N) ->
++ try Fun() of
++ {error,_} ->
++ timer:sleep(10),
++ try_loop(Fun, N-1);
++ Result ->
++ Result
++ catch
++ _:_What ->
++ timer:sleep(10),
++ try_loop(Fun, N-1)
++ end.
++
++try_loop(M, F, _A, 0) ->
++ ct:pal("WARNING! ~w:~w never succeeded!", [M,F]),
++ gave_up;
++try_loop(M, F, A, N) ->
++ try apply(M, F, A) of
++ {error,_} ->
++ timer:sleep(10),
++ try_loop(M, F, A, N-1);
++ Result ->
++ Result
++ catch
++ _:_ ->
++ timer:sleep(10),
++ try_loop(M, F, A, N-1)
++ end.
++
+ %%%-----------------------------------------------------------------
+ %%% TEST EVENTS
+ %%%-----------------------------------------------------------------
+--- a/lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl
++++ b/lib/common_test/test/ct_pre_post_test_io_SUITE_data/cth_ctrl.erl
+@@ -53,8 +53,7 @@ init(_Id, _Opts) ->
+ receive
+ {?MODULE,proceed} -> ok
+ after
+- 10000 ->
+- ok
++ 10000 -> ok
+ end,
+ {ok,[],ct_last}.
+
+@@ -66,8 +65,7 @@ terminate(_State) ->
+ receive
+ {?MODULE,proceed} -> ok
+ after
+- 10000 ->
+- ok
++ 10000 -> ok
+ end,
+ stop_external_logger(cth_logger),
+ stop_dispatcher(),
+@@ -94,7 +92,7 @@ init_logger(Name) ->
+ logger_loop(N) ->
+ ct:log("Logger iteration: ~p", [N]),
+ error_logger:error_report(N),
+- timer:sleep(250),
++ timer:sleep(100),
+ logger_loop(N+1).
+
+ %%%-----------------------------------------------------------------
+--- a/lib/common_test/test/ct_surefire_SUITE.erl
++++ b/lib/common_test/test/ct_surefire_SUITE.erl
+@@ -49,8 +49,11 @@
+ %% there will be clashes with logging processes etc).
+ %%--------------------------------------------------------------------
+ init_per_suite(Config) ->
+- Config1 = ct_test_support:init_per_suite(Config),
+- Config1.
++ DataDir = ?config(data_dir,Config),
++ Hook = "fail_pre_init_per_suite.erl",
++ io:format("Compiling ~p: ~p~n",
++ [Hook, compile:file(Hook,[{outdir,DataDir},debug_info])]),
++ ct_test_support:init_per_suite([{path_dirs,[DataDir]}|Config]).
+
+ end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+@@ -69,7 +72,8 @@ all() ->
+ absolute_path,
+ relative_path,
+ url,
+- logdir
++ logdir,
++ fail_pre_init_per_suite
+ ].
+
+ %%--------------------------------------------------------------------
+@@ -107,6 +111,14 @@ logdir(Config) when is_list(Config) ->
+ Path = "logdir.xml",
+ run(logdir,[{cth_surefire,[{path,Path}]}],Path,Config,[{logdir,MyLogDir}]).
+
++fail_pre_init_per_suite(Config) when is_list(Config) ->
++ DataDir = ?config(data_dir,Config),
++ Suites = [filename:join(DataDir,"pass_SUITE"),
++ filename:join(DataDir,"fail_SUITE")],
++ Path = "fail_pre_init_per_suite.xml",
++ run(fail_pre_init_per_suite,[fail_pre_init_per_suite,
++ {cth_surefire,[{path,Path}]}],Path,Config,[],Suites).
++
+ %%%-----------------------------------------------------------------
+ %%% HELP FUNCTIONS
+ %%%-----------------------------------------------------------------
+@@ -115,6 +127,8 @@ run(Case,CTHs,Report,Config) ->
+ run(Case,CTHs,Report,Config,ExtraOpts) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "surefire_SUITE"),
++ run(Case,CTHs,Report,Config,ExtraOpts,Suite).
++run(Case,CTHs,Report,Config,ExtraOpts,Suite) ->
+ {Opts,ERPid} = setup([{suite,Suite},{ct_hooks,CTHs},{label,Case}|ExtraOpts],
+ Config),
+ ok = execute(Case, Opts, ERPid, Config),
+@@ -142,7 +156,6 @@ setup(Test, Config) ->
+ execute(Name, Opts, ERPid, Config) ->
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+-
+ ct_test_support:log_events(Name,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+@@ -166,10 +179,30 @@ events_to_check(_, 0) ->
+ events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+-test_events(_) ->
+- [{?eh,start_logging,'_'},
+- {?eh,start_info,{1,1,9}},
+- {?eh,tc_start,{surefire_SUITE,init_per_suite}},
++test_suite_events(fail_SUITE, TestStat) ->
++ [{?eh,tc_start,{ct_framework,init_per_suite}},
++ {?eh,tc_done,{ct_framework,init_per_suite,
++ {failed,{error,pre_init_per_suite}}}},
++ {?eh,tc_auto_skip,
++ {fail_SUITE,test_case,
++ {failed,{ct_framework,init_per_suite,{failed,pre_init_per_suite}}}}},
++ {?eh,test_stats,TestStat},
++ {?eh,tc_auto_skip,
++ {ct_framework,end_per_suite,
++ {failed,{ct_framework,init_per_suite,{failed,pre_init_per_suite}}}}}].
++
++test_suite_events(fail_SUITE) ->
++ test_suite_events(fail_SUITE, {0,0,{0,1}});
++test_suite_events(pass_SUITE) ->
++ [{?eh,tc_start,{ct_framework,init_per_suite}},
++ {?eh,tc_done,{ct_framework,init_per_suite,ok}},
++ {?eh,tc_start,{pass_SUITE,test_case}},
++ {?eh,tc_done,{pass_SUITE,test_case,ok}},
++ {?eh,test_stats,{1,0,{0,0}}},
++ {?eh,tc_start,{ct_framework,end_per_suite}},
++ {?eh,tc_done,{ct_framework,end_per_suite,ok}}];
++test_suite_events(_) ->
++ [{?eh,tc_start,{surefire_SUITE,init_per_suite}},
+ {?eh,tc_done,{surefire_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{surefire_SUITE,tc_ok}},
+ {?eh,tc_done,{surefire_SUITE,tc_ok,ok}},
+@@ -216,9 +249,18 @@ test_events(_) ->
+ {surefire_SUITE,init_per_group,
+ {'EXIT',all_cases_should_be_skipped}}}}}],
+ {?eh,tc_start,{surefire_SUITE,end_per_suite}},
+- {?eh,tc_done,{surefire_SUITE,end_per_suite,ok}},
+- {?eh,stop_logging,[]}].
+-
++ {?eh,tc_done,{surefire_SUITE,end_per_suite,ok}}].
++
++test_events(fail_pre_init_per_suite) ->
++ [{?eh,start_logging,{'DEF','RUNDIR'}},
++ {?eh,start_info,{2,2,2}}] ++
++ test_suite_events(pass_SUITE) ++
++ test_suite_events(fail_SUITE, {1,0,{0,1}}) ++
++ [{?eh,stop_logging,[]}];
++test_events(Test) ->
++ [{?eh,start_logging,'_'}, {?eh,start_info,{1,1,9}}] ++
++ test_suite_events(Test) ++
++ [{?eh,stop_logging,[]}].
+
+ %%%-----------------------------------------------------------------
+ %%% Check generated xml log files
+@@ -251,9 +293,9 @@ do_check_xml(Case,[Xml|Xmls]) ->
+ {E,_} = xmerl_scan:file(Xml),
+ Expected = events_to_result(lists:flatten(test_events(Case))),
+ ParseResult = testsuites(Case,E),
+- ct:log("Expecting: ~p~n",[[Expected]]),
++ ct:log("Expecting: ~p~n",[Expected]),
+ ct:log("Actual : ~p~n",[ParseResult]),
+- [Expected] = ParseResult,
++ Expected = ParseResult,
+ do_check_xml(Case,Xmls);
+ do_check_xml(_,[]) ->
+ ok.
+@@ -265,7 +307,8 @@ testsuites(Case,#xmlElement{name=testsuites,content=TS}) ->
+ testsuite(Case,TS).
+
+ testsuite(Case,[#xmlElement{name=testsuite,content=TC,attributes=A}|TS]) ->
+- {ET,EF,ES} = events_to_numbers(lists:flatten(test_events(Case))),
++ TestSuiteEvents = test_suite_events(get_ts_name(A)),
++ {ET,EF,ES} = events_to_numbers(lists:flatten(TestSuiteEvents)),
+ {T,E,F,S} = get_numbers_from_attrs(A,false,false,false,false),
+ ct:log("Expecting total:~p, error:~p, failure:~p, skipped:~p~n",[ET,0,EF,ES]),
+ ct:log("Actual total:~p, error:~p, failure:~p, skipped:~p~n",[T,E,F,S]),
+@@ -318,14 +361,32 @@ failed_or_skipped([]) ->
+ %% Testsuites = [Testsuite]
+ %% Testsuite = [Testcase]
+ %% Testcase = [] | [f] | [s], indicating ok, failed and skipped respectively
+-events_to_result([{?eh,tc_done,{_Suite,_Case,R}}|E]) ->
+- [result(R)|events_to_result(E)];
+-events_to_result([{?eh,tc_auto_skip,_}|E]) ->
+- [[s]|events_to_result(E)];
+-events_to_result([_|E]) ->
+- events_to_result(E);
+-events_to_result([]) ->
+- [].
++events_to_result(E) ->
++ events_to_result(E, []).
++
++events_to_result([{?eh,tc_auto_skip,{_Suite,init_per_suite,_}}|E], Result) ->
++ {Suite,Rest} = events_to_result1(E),
++ events_to_result(Rest, [[[s]|Suite]|Result]);
++events_to_result([{?eh,tc_done,{_Suite,init_per_suite,R}}|E], Result) ->
++ {Suite,Rest} = events_to_result1(E),
++ events_to_result(Rest, [[result(R)|Suite]|Result]);
++events_to_result([_|E], Result) ->
++ events_to_result(E, Result);
++events_to_result([], Result) ->
++ Result.
++
++events_to_result1([{?eh,tc_auto_skip,{_Suite, end_per_suite,_}}|E]) ->
++ {[[s]],E};
++events_to_result1([{?eh,tc_done,{_Suite, end_per_suite,R}}|E]) ->
++ {[result(R)],E};
++events_to_result1([{?eh,tc_done,{_Suite,_Case,R}}|E]) ->
++ {Suite,Rest} = events_to_result1(E),
++ {[result(R)|Suite],Rest};
++events_to_result1([{?eh,tc_auto_skip,_}|E]) ->
++ {Suite,Rest} = events_to_result1(E),
++ {[[s]|Suite],Rest};
++events_to_result1([_|E]) ->
++ events_to_result1(E).
+
+ result(ok) ->[];
+ result({skipped,_}) -> [s];
+@@ -374,3 +435,7 @@ del_files(Dir,[F0|Fs] ) ->
+ end;
+ del_files(_,[]) ->
+ ok.
++
++get_ts_name(Attributes) ->
++ {_,name,_,_,_,_,_,_,Name,_} = lists:keyfind(name, 2, Attributes),
++ list_to_atom(Name).
+--- /dev/null
++++ b/lib/common_test/test/ct_surefire_SUITE_data/fail_SUITE.erl
+@@ -0,0 +1,28 @@
++%% %CopyrightBegin%
++%%
++%% Copyright Ericsson AB 2016. All Rights Reserved.
++%%
++%% Licensed under the Apache License, Version 2.0 (the "License");
++%% you may not use this file except in compliance with the License.
++%% You may obtain a copy of the License at
++%%
++%% http://www.apache.org/licenses/LICENSE-2.0
++%%
++%% Unless required by applicable law or agreed to in writing, software
++%% distributed under the License is distributed on an "AS IS" BASIS,
++%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++%% See the License for the specific language governing permissions and
++%% limitations under the License.
++%%
++%% %CopyrightEnd%
++%%
++-module(fail_SUITE).
++-include_lib("common_test/include/ct.hrl").
++
++-export([all/0, test_case/1]).
++
++all() ->
++ [test_case].
++
++test_case(_Config) ->
++ ok.
+--- /dev/null
++++ b/lib/common_test/test/ct_surefire_SUITE_data/fail_pre_init_per_suite.erl
+@@ -0,0 +1,47 @@
++%%
++%% %CopyrightBegin%
++%%
++%% Copyright Ericsson AB 2016. All Rights Reserved.
++%%
++%% Licensed under the Apache License, Version 2.0 (the "License");
++%% you may not use this file except in compliance with the License.
++%% You may obtain a copy of the License at
++%%
++%% http://www.apache.org/licenses/LICENSE-2.0
++%%
++%% Unless required by applicable law or agreed to in writing, software
++%% distributed under the License is distributed on an "AS IS" BASIS,
++%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++%% See the License for the specific language governing permissions and
++%% limitations under the License.
++%%
++%% %CopyrightEnd%
++%%
++
++%%% This tests that the correct XML is produced when pre_init_per_suite
++%%% fails in a hook
++-module(fail_pre_init_per_suite).
++
++%% CT Hooks
++-export([init/2, pre_init_per_suite/3]).
++
++-type config() :: proplists:proplist().
++-type reason() :: term().
++-type skip_or_fail() :: skip | auto_skip | fail | 'EXIT'.
++
++-record(state, {}).
++
++-spec init(Id :: term(), Opts :: proplists:proplist()) ->
++ {ok, proplists:proplist()}.
++init(_Id, Opts) ->
++ {ok, Opts}.
++
++-spec pre_init_per_suite(Suite :: atom(),
++ Config :: config(),
++ State :: #state{}) ->
++ {config() | {skip_or_fail(), reason()}, NewState :: #state{}}.
++pre_init_per_suite(fail_SUITE, _Config, State) ->
++ {{fail, pre_init_per_suite}, State};
++pre_init_per_suite(_Suite, Config, State) ->
++ {Config, State}.
++
+--- /dev/null
++++ b/lib/common_test/test/ct_surefire_SUITE_data/pass_SUITE.erl
+@@ -0,0 +1,28 @@
++%% %CopyrightBegin%
++%%
++%% Copyright Ericsson AB 2016. All Rights Reserved.
++%%
++%% Licensed under the Apache License, Version 2.0 (the "License");
++%% you may not use this file except in compliance with the License.
++%% You may obtain a copy of the License at
++%%
++%% http://www.apache.org/licenses/LICENSE-2.0
++%%
++%% Unless required by applicable law or agreed to in writing, software
++%% distributed under the License is distributed on an "AS IS" BASIS,
++%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++%% See the License for the specific language governing permissions and
++%% limitations under the License.
++%%
++%% %CopyrightEnd%
++%%
++-module(pass_SUITE).
++-include_lib("common_test/include/ct.hrl").
++
++-export([all/0, test_case/1]).
++
++all() ->
++ [test_case].
++
++test_case(_Config) ->
++ ok.
+--- a/lib/common_test/test/ct_test_support.erl
++++ b/lib/common_test/test/ct_test_support.erl
+@@ -43,6 +43,8 @@
+
+ -export([random_error/1]).
+
++-export([unique_timestamp/0]).
++
+ -include_lib("kernel/include/file.hrl").
+
+ %%%-----------------------------------------------------------------
+@@ -110,7 +112,8 @@ start_slave(NodeName, Config, Level) ->
+ undefined -> [];
+ Ds -> Ds
+ end,
+- PathDirs = [PrivDir,TSDir | AddPathDirs],
++ TestSupDir = filename:dirname(code:which(?MODULE)),
++ PathDirs = [PrivDir,TSDir,TestSupDir | AddPathDirs],
+ [true = rpc:call(CTNode, code, add_patha, [D]) || D <- PathDirs],
+ test_server:format(Level, "Dirs added to code path (on ~w):~n",
+ [CTNode]),
+@@ -1430,7 +1433,21 @@ rm_files([F | Fs]) ->
+ end;
+ rm_files([]) ->
+ ok.
+-
++
++unique_timestamp() ->
++ unique_timestamp(os:timestamp(), 100000).
++
++unique_timestamp(TS, 0) ->
++ TS;
++unique_timestamp(TS0, N) ->
++ case os:timestamp() of
++ TS0 ->
++ timer:sleep(1),
++ unique_timestamp(TS0, N-1);
++ TS1 ->
++ TS1
++ end.
++
+ %%%-----------------------------------------------------------------
+ %%%
+ slave_stop(Node) ->
+--- a/lib/common_test/vsn.mk
++++ b/lib/common_test/vsn.mk
+@@ -1 +1 @@
+-COMMON_TEST_VSN = 1.12
++COMMON_TEST_VSN = 1.12.1
+--- a/lib/inets/doc/src/notes.xml
++++ b/lib/inets/doc/src/notes.xml
+@@ -33,7 +33,40 @@
+ <file>notes.xml</file>
+ </header>
+
+- <section><title>Inets 6.2.2</title>
++ <section><title>Inets 6.2.4</title>
++
++ <section><title>Improvements and New Features</title>
++ <list>
++ <item>
++ <p>
++ Handle multiple \t in mime types file</p>
++ <p>
++ Own Id: OTP-13663 Aux Id: seq13132 </p>
++ </item>
++ </list>
++ </section>
++
++</section>
++
++<section><title>Inets 6.2.3</title>
++
++ <section><title>Improvements and New Features</title>
++ <list>
++ <item>
++ <p>
++ Put back unused module inets_regexp and remove it in OTP
++ 19 instead as it is an incompatibility, although it is an
++ undocumented module and should not affect other
++ applications.</p>
++ <p>
++ Own Id: OTP-13533</p>
++ </item>
++ </list>
++ </section>
++
++</section>
++
++<section><title>Inets 6.2.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+--- a/lib/inets/src/http_server/httpd_conf.erl
++++ b/lib/inets/src/http_server/httpd_conf.erl
+@@ -1004,7 +1004,8 @@ read_config_file(Stream, SoFar) ->
+ %% Ignore commented lines for efficiency later ..
+ read_config_file(Stream, SoFar);
+ Line ->
+- NewLine = re:replace(clean(Line),"[\t\r\f ]"," ", [{return,list}]),
++ NewLine = re:replace(white_space_clean(Line),
++ "[\t\r\f ]"," ", [{return,list}, global]),
+ case NewLine of
+ [] ->
+ %% Also ignore empty lines ..
+@@ -1020,7 +1021,7 @@ parse_mime_types(Stream,MimeTypesList) ->
+ eof ->
+ eof;
+ String ->
+- white_space_clean(String)
++ re:replace(white_space_clean(String), "[\t\r\f ]"," ", [{return,list}, global])
+ end,
+ parse_mime_types(Stream, MimeTypesList, Line).
+ parse_mime_types(Stream, MimeTypesList, eof) ->
+@@ -1042,6 +1043,8 @@ parse_mime_types(Stream, MimeTypesList, Line) ->
+
+ suffixes(_MimeType,[]) ->
+ [];
++suffixes(MimeType,[""|Rest]) ->
++ suffixes(MimeType, Rest);
+ suffixes(MimeType,[Suffix|Rest]) ->
+ [{Suffix,MimeType}|suffixes(MimeType,Rest)].
+
+--- a/lib/inets/src/inets_app/Makefile
++++ b/lib/inets/src/inets_app/Makefile
+@@ -49,7 +49,8 @@ MODULES = \
+ inets_sup \
+ inets_trace \
+ inets_lib \
+- inets_time_compat
++ inets_time_compat \
++ inets_regexp
+
+ INTERNAL_HRL_FILES = inets_internal.hrl
+ EXTERNAL_HRL_FILES = ../../include/httpd.hrl \
+--- a/lib/inets/src/inets_app/inets.app.src
++++ b/lib/inets/src/inets_app/inets.app.src
+@@ -29,6 +29,7 @@
+ inets_trace,
+ inets_lib,
+ inets_time_compat,
++ inets_regexp,
+
+ %% FTP
+ ftp,
+--- a/lib/inets/src/inets_app/inets.appup.src
++++ b/lib/inets/src/inets_app/inets.appup.src
+@@ -18,16 +18,10 @@
+ %% %CopyrightEnd%
+ {"%VSN%",
+ [
+- {<<"6.2.1">>, [{load_module, httpd_script_env, soft_purge, soft_purge, []}]},
+- {<<"6.2">>, [{load_module, httpd_script_env, soft_purge, soft_purge, []},
+- {load_module, httpc, soft_purge, soft_purge, []}]},
+ {<<"6\\..*">>,[{restart_application, inets}]},
+ {<<"5\\..*">>,[{restart_application, inets}]}
+ ],
+ [
+- {<<"6.2.1">>, [{load_module, httpd_script_env, soft_purge, soft_purge, []}]},
+- {<<"6.2">>, [{load_module, httpd_script_env, soft_purge, soft_purge, []},
+- {load_module, httpc, soft_purge, soft_purge, []}]},
+ {<<"6\\..*">>,[{restart_application, inets}]},
+ {<<"5\\..*">>,[{restart_application, inets}]}
+ ]
+--- /dev/null
++++ b/lib/inets/src/inets_app/inets_regexp.erl
+@@ -0,0 +1,414 @@
++%%
++%% %CopyrightBegin%
++%%
++%% Copyright Ericsson AB 2009. All Rights Reserved.
++%%
++%% Licensed under the Apache License, Version 2.0 (the "License");
++%% you may not use this file except in compliance with the License.
++%% You may obtain a copy of the License at
++%%
++%% http://www.apache.org/licenses/LICENSE-2.0
++%%
++%% Unless required by applicable law or agreed to in writing, software
++%% distributed under the License is distributed on an "AS IS" BASIS,
++%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++%% See the License for the specific language governing permissions and
++%% limitations under the License.
++%%
++%% %CopyrightEnd%
++%%
++
++-module(inets_regexp).
++
++-export([parse/1, match/2, first_match/2, split/2, sub/3, gsub/3]).
++
++
++%%%=========================================================================
++%%% API
++%%%=========================================================================
++
++%% parse(RegExp) -> {ok, RE} | {error, E}.
++%% Parse the regexp described in the string RegExp.
++
++parse(S) ->
++ case (catch reg(S)) of
++ {R, []} ->
++ {ok, R};
++ {_R, [C|_]} ->
++ {error, {illegal, [C]}};
++ {error, E} ->
++ {error, E}
++ end.
++
++
++%% Find the longest match of RegExp in String.
++
++match(S, RegExp) when is_list(RegExp) ->
++ case parse(RegExp) of
++ {ok,RE} -> match(S, RE);
++ {error,E} -> {error,E}
++ end;
++match(S, RE) ->
++ case match(RE, S, 1, 0, -1) of
++ {Start,Len} when Len >= 0 ->
++ {match, Start, Len};
++ {_Start,_Len} ->
++ nomatch
++ end.
++
++%% Find the first match of RegExp in String.
++
++first_match(S, RegExp) when is_list(RegExp) ->
++ case parse(RegExp) of
++ {ok, RE} ->
++ first_match(S, RE);
++ {error, E} ->
++ {error, E}
++ end;
++first_match(S, RE) ->
++ case first_match(RE, S, 1) of
++ {Start,Len} when Len >= 0 ->
++ {match, Start,Len};
++ nomatch ->
++ nomatch
++ end.
++
++first_match(RE, S, St) when S =/= [] ->
++ case re_apply(S, St, RE) of
++ {match, P, _Rest} ->
++ {St, P-St};
++ nomatch ->
++ first_match(RE, tl(S), St+1)
++ end;
++first_match(_RE, [], _St) ->
++ nomatch.
++
++
++match(RE, S, St, Pos, L) ->
++ case first_match(RE, S, St) of
++ {St1, L1} ->
++ Nst = St1 + 1,
++ if L1 > L ->
++ match(RE, lists:nthtail(Nst-St, S), Nst, St1, L1);
++ true ->
++ match(RE, lists:nthtail(Nst-St, S), Nst, Pos, L)
++ end;
++ nomatch ->
++ {Pos, L}
++ end.
++
++
++%% Split a string into substrings where the RegExp describes the
++%% field seperator. The RegExp " " is specially treated.
++
++split(String, " ") -> %This is really special
++ {ok, RE} = parse("[ \t]+"),
++ case split_apply(String, RE, true) of
++ [[]|Ss] ->
++ {ok,Ss};
++ Ss ->
++ {ok,Ss}
++ end;
++split(String, RegExp) when is_list(RegExp) ->
++ case parse(RegExp) of
++ {ok, RE} ->
++ {ok, split_apply(String, RE, false)};
++ {error, E} ->
++ {error,E}
++ end;
++split(String, RE) ->
++ {ok, split_apply(String, RE, false)}.
++
++
++%% Substitute the first match of the regular expression RegExp
++%% with the string Replace in String. Accept pre-parsed regular
++%% expressions.
++
++sub(String, RegExp, Rep) when is_list(RegExp) ->
++ case parse(RegExp) of
++ {ok, RE} ->
++ sub(String, RE, Rep);
++ {error, E} ->
++ {error, E}
++ end;
++sub(String, RE, Rep) ->
++ Ss = sub_match(String, RE, 1),
++ {ok, sub_repl(Ss, Rep, String, 1), length(Ss)}.
++
++
++%% Substitute every match of the regular expression RegExp with
++%% the string New in String. Accept pre-parsed regular expressions.
++
++gsub(String, RegExp, Rep) when is_list(RegExp) ->
++ case parse(RegExp) of
++ {ok, RE} ->
++ gsub(String, RE, Rep);
++ {error, E} ->
++ {error, E}
++ end;
++gsub(String, RE, Rep) ->
++ Ss = matches(String, RE, 1),
++ {ok, sub_repl(Ss, Rep, String, 1), length(Ss)}.
++
++
++%%%========================================================================
++%%% Internal functions
++%%%========================================================================
++
++%% This is the regular expression grammar used. It is equivalent to the
++%% one used in AWK, except that we allow ^ $ to be used anywhere and fail
++%% in the matching.
++%%
++%% reg -> reg1 : '$1'.
++%% reg1 -> reg1 "|" reg2 : {'or','$1','$2'}.
++%% reg1 -> reg2 : '$1'.
++%% reg2 -> reg2 reg3 : {concat,'$1','$2'}.
++%% reg2 -> reg3 : '$1'.
++%% reg3 -> reg3 "*" : {kclosure,'$1'}.
++%% reg3 -> reg3 "+" : {pclosure,'$1'}.
++%% reg3 -> reg3 "?" : {optional,'$1'}.
++%% reg3 -> reg4 : '$1'.
++%% reg4 -> "(" reg ")" : '$2'.
++%% reg4 -> "\\" char : '$2'.
++%% reg4 -> "^" : bos.
++%% reg4 -> "$" : eos.
++%% reg4 -> "." : char.
++%% reg4 -> "[" class "]" : {char_class,char_class('$2')}
++%% reg4 -> "[" "^" class "]" : {comp_class,char_class('$3')}
++%% reg4 -> "\"" chars "\"" : char_string('$2')
++%% reg4 -> char : '$1'.
++%% reg4 -> empty : epsilon.
++%% The grammar of the current regular expressions. The actual parser
++%% is a recursive descent implementation of the grammar.
++
++reg(S) -> reg1(S).
++
++%% reg1 -> reg2 reg1'
++%% reg1' -> "|" reg2
++%% reg1' -> empty
++
++reg1(S0) ->
++ {L,S1} = reg2(S0),
++ reg1p(S1, L).
++
++reg1p([$||S0], L) ->
++ {R,S1} = reg2(S0),
++ reg1p(S1, {'or',L,R});
++reg1p(S, L) -> {L,S}.
++
++%% reg2 -> reg3 reg2'
++%% reg2' -> reg3
++%% reg2' -> empty
++
++reg2(S0) ->
++ {L,S1} = reg3(S0),
++ reg2p(S1, L).
++
++reg2p([C|S0], L) when (C =/= $|) andalso (C =/= $)) ->
++ {R,S1} = reg3([C|S0]),
++ reg2p(S1, {concat,L,R});
++reg2p(S, L) -> {L,S}.
++
++%% reg3 -> reg4 reg3'
++%% reg3' -> "*" reg3'
++%% reg3' -> "+" reg3'
++%% reg3' -> "?" reg3'
++%% reg3' -> empty
++
++reg3(S0) ->
++ {L,S1} = reg4(S0),
++ reg3p(S1, L).
++
++reg3p([$*|S], L) -> reg3p(S, {kclosure,L});
++reg3p([$+|S], L) -> reg3p(S, {pclosure,L});
++reg3p([$?|S], L) -> reg3p(S, {optional,L});
++reg3p(S, L) -> {L,S}.
++
++reg4([$(|S0]) ->
++ case reg(S0) of
++ {R,[$)|S1]} -> {R,S1};
++ {_R,_S} -> throw({error,{unterminated,"("}})
++ end;
++reg4([$\\,O1,O2,O3|S])
++ when ((O1 >= $0) andalso
++ (O1 =< $7) andalso
++ (O2 >= $0) andalso
++ (O2 =< $7) andalso
++ (O3 >= $0) andalso
++ (O3 =< $7)) ->
++ {(O1*8 + O2)*8 + O3 - 73*$0,S};
++reg4([$\\,C|S]) ->
++ {escape_char(C),S};
++reg4([$\\]) ->
++ throw({error, {unterminated,"\\"}});
++reg4([$^|S]) ->
++ {bos,S};
++reg4([$$|S]) ->
++ {eos,S};
++reg4([$.|S]) ->
++ {{comp_class,"\n"},S};
++reg4("[^" ++ S0) ->
++ case char_class(S0) of
++ {Cc,[$]|S1]} -> {{comp_class,Cc},S1};
++ {_Cc,_S} -> throw({error,{unterminated,"["}})
++ end;
++reg4([$[|S0]) ->
++ case char_class(S0) of
++ {Cc,[$]|S1]} -> {{char_class,Cc},S1};
++ {_Cc,_S1} -> throw({error,{unterminated,"["}})
++ end;
++reg4([C|S])
++ when (C =/= $*) andalso (C =/= $+) andalso (C =/= $?) andalso (C =/= $]) ->
++ {C, S};
++reg4([C|_S]) ->
++ throw({error,{illegal,[C]}});
++reg4([]) ->
++ {epsilon,[]}.
++
++escape_char($n) -> $\n; %\n = LF
++escape_char($r) -> $\r; %\r = CR
++escape_char($t) -> $\t; %\t = TAB
++escape_char($v) -> $\v; %\v = VT
++escape_char($b) -> $\b; %\b = BS
++escape_char($f) -> $\f; %\f = FF
++escape_char($e) -> $\e; %\e = ESC
++escape_char($s) -> $\s; %\s = SPACE
++escape_char($d) -> $\d; %\d = DEL
++escape_char(C) -> C.
++
++char_class([$]|S]) -> char_class(S, [$]]);
++char_class(S) -> char_class(S, []).
++
++char($\\, [O1,O2,O3|S]) when
++ O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 ->
++ {(O1*8 + O2)*8 + O3 - 73*$0,S};
++char($\\, [C|S]) -> {escape_char(C),S};
++char(C, S) -> {C,S}.
++
++char_class([C1|S0], Cc) when C1 =/= $] ->
++ case char(C1, S0) of
++ {Cf,[$-,C2|S1]} when C2 =/= $] ->
++ case char(C2, S1) of
++ {Cl,S2} when Cf < Cl -> char_class(S2, [{Cf,Cl}|Cc]);
++ {Cl,_S2} -> throw({error,{char_class,[Cf,$-,Cl]}})
++ end;
++ {C,S1} -> char_class(S1, [C|Cc])
++ end;
++char_class(S, Cc) -> {Cc,S}.
++
++
++%% re_apply(String, StartPos, RegExp) -> re_app_res().
++%%
++%% Apply the (parse of the) regular expression RegExp to String. If
++%% there is a match return the position of the remaining string and
++%% the string if else return 'nomatch'. BestMatch specifies if we want
++%% the longest match, or just a match.
++%%
++%% StartPos should be the real start position as it is used to decide
++%% if we ae at the beginning of the string.
++%%
++%% Pass two functions to re_apply_or so it can decide, on the basis
++%% of BestMatch, whether to just any take any match or try both to
++%% find the longest. This is slower but saves duplicatng code.
++
++re_apply(S, St, RE) -> re_apply(RE, [], S, St).
++
++re_apply(epsilon, More, S, P) -> %This always matches
++ re_apply_more(More, S, P);
++re_apply({'or',RE1,RE2}, More, S, P) ->
++ re_apply_or(re_apply(RE1, More, S, P),
++ re_apply(RE2, More, S, P));
++re_apply({concat,RE1,RE2}, More, S0, P) ->
++ re_apply(RE1, [RE2|More], S0, P);
++re_apply({kclosure,CE}, More, S, P) ->
++ %% Be careful with the recursion, explicitly do one call before
++ %% looping.
++ re_apply_or(re_apply_more(More, S, P),
++ re_apply(CE, [{kclosure,CE}|More], S, P));
++re_apply({pclosure,CE}, More, S, P) ->
++ re_apply(CE, [{kclosure,CE}|More], S, P);
++re_apply({optional,CE}, More, S, P) ->
++ re_apply_or(re_apply_more(More, S, P),
++ re_apply(CE, More, S, P));
++re_apply(bos, More, S, 1) -> re_apply_more(More, S, 1);
++re_apply(eos, More, [$\n|S], P) -> re_apply_more(More, S, P);
++re_apply(eos, More, [], P) -> re_apply_more(More, [], P);
++re_apply({char_class,Cc}, More, [C|S], P) ->
++ case in_char_class(C, Cc) of
++ true -> re_apply_more(More, S, P+1);
++ false -> nomatch
++ end;
++re_apply({comp_class,Cc}, More, [C|S], P) ->
++ case in_char_class(C, Cc) of
++ true -> nomatch;
++ false -> re_apply_more(More, S, P+1)
++ end;
++re_apply(C, More, [C|S], P) when is_integer(C) ->
++ re_apply_more(More, S, P+1);
++re_apply(_RE, _More, _S, _P) -> nomatch.
++
++%% re_apply_more([RegExp], String, Length) -> re_app_res().
++
++re_apply_more([RE|More], S, P) -> re_apply(RE, More, S, P);
++re_apply_more([], S, P) -> {match,P,S}.
++
++%% in_char_class(Char, Class) -> bool().
++
++in_char_class(C, [{C1,C2}|_Cc]) when C >= C1, C =< C2 -> true;
++in_char_class(C, [C|_Cc]) -> true;
++in_char_class(C, [_|Cc]) -> in_char_class(C, Cc);
++in_char_class(_C, []) -> false.
++
++%% re_apply_or(Match1, Match2) -> re_app_res().
++%% If we want the best match then choose the longest match, else just
++%% choose one by trying sequentially.
++
++re_apply_or({match,P1,S1}, {match,P2,_S2}) when P1 >= P2 -> {match,P1,S1};
++re_apply_or({match,_P1,_S1}, {match,P2,S2}) -> {match,P2,S2};
++re_apply_or(nomatch, R2) -> R2;
++re_apply_or(R1, nomatch) -> R1.
++
++
++matches(S, RE, St) ->
++ case first_match(RE, S, St) of
++ {St1,0} ->
++ [{St1,0}|matches(string:substr(S, St1+2-St), RE, St1+1)];
++ {St1,L1} ->
++ [{St1,L1}|matches(string:substr(S, St1+L1+1-St), RE, St1+L1)];
++ nomatch ->
++ []
++ end.
++
++sub_match(S, RE, St) ->
++ case first_match(RE, S, St) of
++ {St1,L1} -> [{St1,L1}];
++ nomatch -> []
++ end.
++
++sub_repl([{St,L}|Ss], Rep, S, Pos) ->
++ Rs = sub_repl(Ss, Rep, S, St+L),
++ string:substr(S, Pos, St-Pos) ++
++ sub_repl(Rep, string:substr(S, St, L), Rs);
++sub_repl([], _Rep, S, Pos) ->
++ string:substr(S, Pos).
++
++sub_repl([$&|Rep], M, Rest) -> M ++ sub_repl(Rep, M, Rest);
++sub_repl("\\&" ++ Rep, M, Rest) -> [$&|sub_repl(Rep, M, Rest)];
++sub_repl([C|Rep], M, Rest) -> [C|sub_repl(Rep, M, Rest)];
++sub_repl([], _M, Rest) -> Rest.
++
++split_apply(S, RE, Trim) -> split_apply(S, 1, RE, Trim, []).
++
++split_apply([], _P, _RE, true, []) ->
++ [];
++split_apply([], _P, _RE, _T, Sub) ->
++ [lists:reverse(Sub)];
++split_apply(S, P, RE, T, Sub) ->
++ case re_apply(S, P, RE) of
++ {match,P,_Rest} ->
++ split_apply(tl(S), P+1, RE, T, [hd(S)|Sub]);
++ {match,P1,Rest} ->
++ [lists:reverse(Sub)|split_apply(Rest, P1, RE, T, [])];
++ nomatch ->
++ split_apply(tl(S), P+1, RE, T, [hd(S)|Sub])
++ end.
+--- a/lib/inets/test/httpd_SUITE.erl
++++ b/lib/inets/test/httpd_SUITE.erl
+@@ -70,7 +70,8 @@ all() ->
+ {group, https_security},
+ {group, http_reload},
+ {group, https_reload},
+- {group, http_mime_types}
++ {group, http_mime_types},
++ mime_types_format
+ ].
+
+ groups() ->
+@@ -1291,6 +1292,115 @@ non_disturbing(Config) when is_list(Config)->
+ inets_test_lib:close(Type, Socket),
+ [{server_name, "httpd_non_disturbing_" ++ Version}] = httpd:info(Server, [server_name]).
+
++%%-------------------------------------------------------------------------
++mime_types_format(Config) when is_list(Config) ->
++ DataDir = proplists:get_value(data_dir, Config),
++ MimeTypes = filename:join(DataDir, "mime_types.txt"),
++ {ok,[{"wrl","x-world/x-vrml"},
++ {"vrml","x-world/x-vrml"},
++ {"ice","x-conference/x-cooltalk"},
++ {"movie","video/x-sgi-movie"},
++ {"avi","video/x-msvideo"},
++ {"qt","video/quicktime"},
++ {"mov","video/quicktime"},
++ {"mpeg","video/mpeg"},
++ {"mpg","video/mpeg"},
++ {"mpe","video/mpeg"},
++ {"sgml","text/x-sgml"},
++ {"sgm","text/x-sgml"},
++ {"etx","text/x-setext"},
++ {"tsv","text/tab-separated-values"},
++ {"rtx","text/richtext"},
++ {"txt","text/plain"},
++ {"html","text/html"},
++ {"htm","text/html"},
++ {"css","text/css"},
++ {"xwd","image/x-xwindowdump"},
++ {"xpm","image/x-xpixmap"},
++ {"xbm","image/x-xbitmap"},
++ {"rgb","image/x-rgb"},
++ {"ppm","image/x-portable-pixmap"},
++ {"pgm","image/x-portable-graymap"},
++ {"pbm","image/x-portable-bitmap"},
++ {"pnm","image/x-portable-anymap"},
++ {"ras","image/x-cmu-raster"},
++ {"tiff","image/tiff"},
++ {"tif","image/tiff"},
++ {"png","image/png"},
++ {"jpeg","image/jpeg"},
++ {"jpg","image/jpeg"},
++ {"jpe","image/jpeg"},
++ {"ief","image/ief"},
++ {"gif","image/gif"},
++ {"pdb","chemical/x-pdb"},
++ {"xyz","chemical/x-pdb"},
++ {"wav","audio/x-wav"},
++ {"ra","audio/x-realaudio"},
++ {"rpm","audio/x-pn-realaudio-plugin"},
++ {"ram","audio/x-pn-realaudio"},
++ {"aif","audio/x-aiff"},
++ {"aiff","audio/x-aiff"},
++ {"aifc","audio/x-aiff"},
++ {"mpga","audio/mpeg"},
++ {"mp2","audio/mpeg"},
++ {"au","audio/basic"},
++ {"snd","audio/basic"},
++ {"zip","application/zip"},
++ {"src","application/x-wais-source"},
++ {"ustar","application/x-ustar"},
++ {"ms","application/x-troff-ms"},
++ {"me","application/x-troff-me"},
++ {"man","application/x-troff-man"},
++ {"t","application/x-troff"},
++ {"tr","application/x-troff"},
++ {"roff","application/x-troff"},
++ {"texinfo","application/x-texinfo"},
++ {"texi","application/x-texinfo"},
++ {"tex","application/x-tex"},
++ {"tcl","application/x-tcl"},
++ {"tar","application/x-tar"},
++ {"sv4crc","application/x-sv4crc"},
++ {"sv4cpio","application/x-sv4cpio"},
++ {"sit","application/x-stuffit"},
++ {"shar","application/x-shar"},
++ {"sh","application/x-sh"},
++ {"nc","application/x-netcdf"},
++ {"cdf","application/x-netcdf"},
++ {"mif","application/x-mif"},
++ {"latex","application/x-latex"},
++ {"skp","application/x-koan"},
++ {"skd","application/x-koan"},
++ {"skt","application/x-koan"},
++ {"skm","application/x-koan"},
++ {"cgi","application/x-httpd-cgi"},
++ {"hdf","application/x-hdf"},
++ {"gz","application/x-gzip"},
++ {"gtar","application/x-gtar"},
++ {"dvi","application/x-dvi"},
++ {"dcr","application/x-director"},
++ {"dir","application/x-director"},
++ {"dxr","application/x-director"},
++ {"csh","application/x-csh"},
++ {"cpio","application/x-cpio"},
++ {"Z","application/x-compress"},
++ {"vcd","application/x-cdlink"},
++ {"bcpio","application/x-bcpio"},
++ {"rtf","application/rtf"},
++ {"ppt","application/powerpoint"},
++ {"ai","application/postscript"},
++ {"eps","application/postscript"},
++ {"ps","application/postscript"},
++ {"pdf","application/pdf"},
++ {"oda","application/oda"},
++ {"bin","application/octet-stream"},
++ {"dms","application/octet-stream"},
++ {"lha","application/octet-stream"},
++ {"lzh","application/octet-stream"},
++ {"exe","application/octet-stream"},
++ {"class","application/octet-stream"},
++ {"doc","application/msword"},
++ {"cpt","application/mac-compactpro"},
++ {"hqx","application/mac-binhex40"}]} = httpd_conf:load_mime_types(MimeTypes).
+
+ %%--------------------------------------------------------------------
+ %% Internal functions -----------------------------------
+--- /dev/null
++++ b/lib/inets/test/httpd_SUITE_data/mime_types.txt
+@@ -0,0 +1,100 @@
++# This is a comment. I love comments.
++
++
++application/activemessage
++application/andrew-inset
++application/applefile
++application/atomicmail
++application/dca-rft
++application/dec-dx
++application/mac-binhex40 hqx
++application/mac-compactpro cpt
++application/macwriteii
++application/msword doc
++application/news-message-id
++application/news-transmission
++application/octet-stream bin dms lha lzh exe class
++application/oda oda
++application/pdf pdf
++application/postscript ai eps ps
++application/powerpoint ppt
++application/remote-printing
++application/rtf rtf
++application/slate
++application/wita
++application/wordperfect5.1
++application/x-bcpio bcpio
++application/x-cdlink vcd
++application/x-compress Z
++application/x-cpio cpio
++application/x-csh csh
++application/x-director dcr dir dxr
++application/x-dvi dvi
++application/x-gtar gtar
++application/x-gzip gz
++application/x-hdf hdf
++application/x-httpd-cgi cgi
++application/x-koan skp skd skt skm
++application/x-latex latex
++application/x-mif mif
++application/x-netcdf nc cdf
++application/x-sh sh
++application/x-shar shar
++application/x-stuffit sit
++application/x-sv4cpio sv4cpio
++application/x-sv4crc sv4crc
++application/x-tar tar
++application/x-tcl tcl
++application/x-tex tex
++application/x-texinfo texinfo texi
++application/x-troff t tr roff
++application/x-troff-man man
++application/x-troff-me me
++application/x-troff-ms ms
++application/x-ustar ustar
++application/x-wais-source src
++application/zip zip
++audio/basic au snd
++audio/mpeg mpga mp2
++audio/x-aiff aif aiff aifc
++audio/x-pn-realaudio ram
++audio/x-pn-realaudio-plugin rpm
++audio/x-realaudio ra
++audio/x-wav wav
++chemical/x-pdb pdb xyz
++image/gif gif
++image/ief ief
++image/jpeg jpeg jpg jpe
++image/png png
++image/tiff tiff tif
++image/x-cmu-raster ras
++image/x-portable-anymap pnm
++image/x-portable-bitmap pbm
++image/x-portable-graymap pgm
++image/x-portable-pixmap ppm
++image/x-rgb rgb
++image/x-xbitmap xbm
++image/x-xpixmap xpm
++image/x-xwindowdump xwd
++message/external-body
++message/news
++message/partial
++message/rfc822
++multipart/alternative
++multipart/appledouble
++multipart/digest
++multipart/mixed
++multipart/parallel
++text/css css
++text/html html htm
++text/plain txt
++text/richtext rtx
++text/tab-separated-values tsv
++text/x-setext etx
++text/x-sgml sgml sgm
++video/mpeg mpeg mpg mpe
++video/quicktime qt mov
++video/x-msvideo avi
++video/x-sgi-movie movie
++x-conference/x-cooltalk ice
++x-world/x-vrml wrl vrml
+--- a/lib/inets/vsn.mk
++++ b/lib/inets/vsn.mk
+@@ -19,6 +19,6 @@
+ # %CopyrightEnd%
+
+ APPLICATION = inets
+-INETS_VSN = 6.2.2
++INETS_VSN = 6.2.4
+ PRE_VSN =
+ APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
+--- a/lib/ssl/doc/src/notes.xml
++++ b/lib/ssl/doc/src/notes.xml
+@@ -28,6 +28,68 @@
+ <p>This document describes the changes made to the SSL application.</p>
+
+
++<section><title>SSL 7.3.3</title>
++
++ <section><title>Fixed Bugs and Malfunctions</title>
++ <list>
++ <item>
++ <p>
++ Correct ssl:prf/5 to use the negotiated cipher suite's
++ prf function in ssl:prf/5 instead of the default prf.</p>
++ <p>
++ Own Id: OTP-13546</p>
++ </item>
++ <item>
++ <p>
++ Timeouts may have the value 0, guards have been corrected
++ to allow this</p>
++ <p>
++ Own Id: OTP-13635</p>
++ </item>
++ <item>
++ <p>
++ Change of internal handling of hash sign pairs as the
++ used one enforced to much restrictions making some valid
++ combinations unavailable.</p>
++ <p>
++ Own Id: OTP-13670</p>
++ </item>
++ </list>
++ </section>
++
++
++ <section><title>Improvements and New Features</title>
++ <list>
++ <item>
++ <p>
++ Create a little randomness in sending of session
++ invalidation messages, to mitigate load when whole table
++ is invalidated.</p>
++ <p>
++ Own Id: OTP-13490</p>
++ </item>
++ </list>
++ </section>
++
++</section>
++
++<section><title>SSL 7.3.2</title>
++
++ <section><title>Fixed Bugs and Malfunctions</title>
++ <list>
++ <item>
++ <p>
++ Correct cipher suites conversion and gaurd expression.
++ Caused problems with GCM cipher suites and client side
++ option to set signature_algorithms extention values.</p>
++ <p>
++ Own Id: OTP-13525</p>
++ </item>
++ </list>
++ </section>
++
++</section>
++
+ <section><title>SSL 7.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+--- a/lib/ssl/src/ssl.erl
++++ b/lib/ssl/src/ssl.erl
+@@ -97,7 +97,7 @@ connect(Socket, SslOptions) when is_port(Socket) ->
+ connect(Socket, SslOptions, infinity).
+
+ connect(Socket, SslOptions0, Timeout) when is_port(Socket),
+- (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
++ (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
+ {Transport,_,_,_} = proplists:get_value(cb_info, SslOptions0,
+ {gen_tcp, tcp, tcp_closed, tcp_error}),
+ EmulatedOptions = ssl_socket:emulated_options(),
+@@ -123,7 +123,7 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket),
+ connect(Host, Port, Options) ->
+ connect(Host, Port, Options, infinity).
+
+-connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
++connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
+ try handle_options(Options, client) of
+ {ok, Config} ->
+ do_connect(Host,Port,Config,Timeout)
+@@ -173,7 +173,7 @@ transport_accept(#sslsocket{pid = {ListenSocket,
+ #config{transport_info = {Transport,_,_, _} =CbInfo,
+ connection_cb = ConnectionCb,
+ ssl = SslOpts,
+- emulated = Tracker}}}, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
++ emulated = Tracker}}}, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
+ case Transport:accept(ListenSocket, Timeout) of
+ {ok, Socket} ->
+ {ok, EmOpts} = ssl_socket:get_emulated_opts(Tracker),
+@@ -206,25 +206,25 @@ transport_accept(#sslsocket{pid = {ListenSocket,
+ ssl_accept(ListenSocket) ->
+ ssl_accept(ListenSocket, infinity).
+
+-ssl_accept(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
++ssl_accept(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
+ ssl_connection:handshake(Socket, Timeout);
+-
+-ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
++
++ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
+ ssl_accept(ListenSocket, SslOptions, infinity).
+
+-ssl_accept(#sslsocket{} = Socket, [], Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity)->
++ssl_accept(#sslsocket{} = Socket, [], Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
+ ssl_accept(#sslsocket{} = Socket, Timeout);
+-ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts0, Timeout) when
+- (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity)->
+- try
+- {ok, EmOpts, InheritedSslOpts} = ssl_socket:get_all_opts(Tracker),
++ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts0, Timeout) when
++ (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
++ try
++ {ok, EmOpts, InheritedSslOpts} = ssl_socket:get_all_opts(Tracker),
+ SslOpts = handle_options(SslOpts0, InheritedSslOpts),
+ ssl_connection:handshake(Socket, {SslOpts, emulated_socket_options(EmOpts, #socket_options{})}, Timeout)
+ catch
+ Error = {error, _Reason} -> Error
+ end;
+ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket),
+- (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
++ (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
+ {Transport,_,_,_} =
+ proplists:get_value(cb_info, SslOptions, {gen_tcp, tcp, tcp_closed, tcp_error}),
+ EmulatedOptions = ssl_socket:emulated_options(),
+@@ -252,17 +252,17 @@ close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _}
+ Transport:close(ListenSocket).
+
+ %%--------------------------------------------------------------------
+--spec close(#sslsocket{}, integer() | {pid(), integer()}) -> term().
++-spec close(#sslsocket{}, timeout() | {pid(), integer()}) -> term().
+ %%
+ %% Description: Close an ssl connection
+ %%--------------------------------------------------------------------
+-close(#sslsocket{pid = TLSPid},
+- {Pid, Timeout} = DownGrade) when is_pid(TLSPid),
+- is_pid(Pid),
+- (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
++close(#sslsocket{pid = TLSPid},
++ {Pid, Timeout} = DownGrade) when is_pid(TLSPid),
++ is_pid(Pid),
++ (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
+ ssl_connection:close(TLSPid, {close, DownGrade});
+-close(#sslsocket{pid = TLSPid}, Timeout) when is_pid(TLSPid),
+- (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
++close(#sslsocket{pid = TLSPid}, Timeout) when is_pid(TLSPid),
++ (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
+ ssl_connection:close(TLSPid, {close, Timeout});
+ close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _}}}}, _) ->
+ Transport:close(ListenSocket).
+@@ -286,7 +286,7 @@ send(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport, _, _, _}
+ recv(Socket, Length) ->
+ recv(Socket, Length, infinity).
+ recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid),
+- (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity)->
++ (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
+ ssl_connection:recv(Pid, Length, Timeout);
+ recv(#sslsocket{pid = {Listen,
+ #config{transport_info = {Transport, _, _, _}}}}, _,_) when is_port(Listen)->
+--- a/lib/ssl/src/ssl_cipher.erl
++++ b/lib/ssl/src/ssl_cipher.erl
+@@ -979,21 +979,21 @@ suite({ecdh_rsa, aes_256_cbc, sha384, sha384}) ->
+ %% RFC 5288 AES-GCM Cipher Suites
+ suite({rsa, aes_128_gcm, null, sha256}) ->
+ ?TLS_RSA_WITH_AES_128_GCM_SHA256;
+-suite({rsa, aes_256_gcm, null}) ->
++suite({rsa, aes_256_gcm, null, sha384}) ->
+ ?TLS_RSA_WITH_AES_256_GCM_SHA384;
+-suite({dhe_rsa, aes_128_gcm, null, sha384}) ->
++suite({dhe_rsa, aes_128_gcm, null, sha256}) ->
+ ?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
+-suite({dhe_rsa, aes_256_gcm, null, sha256}) ->
++suite({dhe_rsa, aes_256_gcm, null, sha384}) ->
+ ?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
+-suite({dh_rsa, aes_128_gcm, null, sha384}) ->
++suite({dh_rsa, aes_128_gcm, null, sha256}) ->
+ ?TLS_DH_RSA_WITH_AES_128_GCM_SHA256;
+-suite({dh_rsa, aes_256_gcm, null, sha256}) ->
++suite({dh_rsa, aes_256_gcm, null, sha384}) ->
+ ?TLS_DH_RSA_WITH_AES_256_GCM_SHA384;
+-suite({dhe_dss, aes_128_gcm, null, sha384}) ->
++suite({dhe_dss, aes_128_gcm, null, sha256}) ->
+ ?TLS_DHE_DSS_WITH_AES_128_GCM_SHA256;
+-suite({dhe_dss, aes_256_gcm, null, sha256}) ->
++suite({dhe_dss, aes_256_gcm, null, sha384}) ->
+ ?TLS_DHE_DSS_WITH_AES_256_GCM_SHA384;
+-suite({dh_dss, aes_128_gcm, null, sha384}) ->
++suite({dh_dss, aes_128_gcm, null, sha256}) ->
+ ?TLS_DH_DSS_WITH_AES_128_GCM_SHA256;
+ suite({dh_dss, aes_256_gcm, null, sha384}) ->
+ ?TLS_DH_DSS_WITH_AES_256_GCM_SHA384;
+--- a/lib/ssl/src/ssl_connection.erl
++++ b/lib/ssl/src/ssl_connection.erl
+@@ -821,7 +821,8 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
+ SecParams = ConnectionState#connection_state.security_parameters,
+ #security_parameters{master_secret = MasterSecret,
+ client_random = ClientRandom,
+- server_random = ServerRandom} = SecParams,
++ server_random = ServerRandom,
++ prf_algorithm = PRFAlgorithm} = SecParams,
+ Reply = try
+ SecretToUse = case Secret of
+ _ when is_binary(Secret) -> Secret;
+@@ -832,7 +833,7 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
+ (client_random, Acc) -> [ClientRandom|Acc];
+ (server_random, Acc) -> [ServerRandom|Acc]
+ end, [], Seed)),
+- ssl_handshake:prf(Version, SecretToUse, Label, SeedToUse, WantedLength)
++ ssl_handshake:prf(Version, PRFAlgorithm, SecretToUse, Label, SeedToUse, WantedLength)
+ catch
+ exit:_ -> {error, badarg};
+ error:Reason -> {error, Reason}
+--- a/lib/ssl/src/ssl_handshake.erl
++++ b/lib/ssl/src/ssl_handshake.erl
+@@ -74,7 +74,7 @@
+ ]).
+
+ %% MISC
+--export([select_version/3, prf/5, select_hashsign/5,
++-export([select_version/3, prf/6, select_hashsign/5,
+ select_hashsign_algs/3,
+ premaster_secret/2, premaster_secret/3, premaster_secret/4]).
+
+@@ -564,17 +564,15 @@ server_key_exchange_hash(md5sha, Value) ->
+ server_key_exchange_hash(Hash, Value) ->
+ crypto:hash(Hash, Value).
+ %%--------------------------------------------------------------------
+--spec prf(ssl_record:ssl_version(), binary(), binary(), [binary()], non_neg_integer()) ->
++-spec prf(ssl_record:ssl_version(), non_neg_integer(), binary(), binary(), [binary()], non_neg_integer()) ->
+ {ok, binary()} | {error, undefined}.
+ %%
+ %% Description: use the TLS PRF to generate key material
+ %%--------------------------------------------------------------------
+-prf({3,0}, _, _, _, _) ->
++prf({3,0}, _, _, _, _, _) ->
+ {error, undefined};
+-prf({3,1}, Secret, Label, Seed, WantedLength) ->
+- {ok, tls_v1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)};
+-prf({3,_N}, Secret, Label, Seed, WantedLength) ->
+- {ok, tls_v1:prf(?SHA256, Secret, Label, Seed, WantedLength)}.
++prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) ->
++ {ok, tls_v1:prf(PRFAlgo, Secret, Label, Seed, WantedLength)}.
+
+
+ %%--------------------------------------------------------------------
+--- a/lib/ssl/src/ssl_manager.erl
++++ b/lib/ssl/src/ssl_manager.erl
+@@ -67,6 +67,7 @@
+ -define(CLEAN_SESSION_DB, 60000).
+ -define(CLEAN_CERT_DB, 500).
+ -define(DEFAULT_MAX_SESSION_CACHE, 1000).
++-define(LOAD_MITIGATION, 10).
+
+ %%====================================================================
+ %% API
+@@ -196,10 +197,12 @@ register_session(Port, Session) ->
+ %%--------------------------------------------------------------------
+ -spec invalidate_session(host(), inet:port_number(), #session{}) -> ok.
+ invalidate_session(Host, Port, Session) ->
++ load_mitigation(),
+ cast({invalidate_session, Host, Port, Session}).
+
+ -spec invalidate_session(inet:port_number(), #session{}) -> ok.
+ invalidate_session(Port, Session) ->
++ load_mitigation(),
+ cast({invalidate_session, Port, Session}).
+
+ -spec invalidate_pem(File::binary()) -> ok.
+@@ -719,3 +722,11 @@ invalidate_session_cache(undefined, CacheCb, Cache) ->
+ start_session_validator(Cache, CacheCb, {invalidate_before, erlang:monotonic_time()}, undefined);
+ invalidate_session_cache(Pid, _CacheCb, _Cache) ->
+ Pid.
++
++load_mitigation() ->
++ MSec = rand:uniform(?LOAD_MITIGATION),
++ receive
++ after
++ MSec ->
++ continue
++ end.
+--- a/lib/ssl/src/tls_handshake.erl
++++ b/lib/ssl/src/tls_handshake.erl
+@@ -278,11 +278,13 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
+ {Version, SessionId, ConnectionStates, ProtoExt, Protocol}
+ end.
+
+-available_signature_algs(undefined, SupportedHashSigns, _, {Major, Minor}) when (Major < 3) andalso (Minor < 3) ->
++available_signature_algs(undefined, SupportedHashSigns, _, {Major, Minor}) when
++ (Major >= 3) andalso (Minor >= 3) ->
+ SupportedHashSigns;
+ available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns,
+- _, {Major, Minor}) when (Major < 3) andalso (Minor < 3) ->
+- ordsets:intersection(ClientHashSigns, SupportedHashSigns);
++ _, {Major, Minor}) when (Major >= 3) andalso (Minor >= 3) ->
++ sets:to_list(sets:intersection(sets:from_list(ClientHashSigns),
++ sets:from_list(SupportedHashSigns)));
+ available_signature_algs(_, _, _, _) ->
+ undefined.
+
+--- a/lib/ssl/test/ssl_basic_SUITE.erl
++++ b/lib/ssl/test/ssl_basic_SUITE.erl
+@@ -88,7 +88,8 @@ basic_tests() ->
+ connect_dist,
+ clear_pem_cache,
+ defaults,
+- fallback
++ fallback,
++ cipher_format
+ ].
+
+ options_tests() ->
+@@ -144,7 +145,8 @@ api_tests() ->
+ versions_option,
+ server_name_indication_option,
+ accept_pool,
+- new_options_in_accept
++ new_options_in_accept,
++ prf
+ ].
+
+ session_tests() ->
+@@ -323,6 +325,31 @@ init_per_testcase(rizzo, Config) ->
+ ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
+ ct:timetrap({seconds, 40}),
+ Config;
++init_per_testcase(prf, Config) ->
++ ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
++ ct:timetrap({seconds, 40}),
++ case ?config(tc_group_path, Config) of
++ [] -> Prop = [];
++ [Prop] -> Prop
++ end,
++ case ?config(name, Prop) of
++ undefined -> TlsVersions = [sslv3, tlsv1, 'tlsv1.1', 'tlsv1.2'];
++ TlsVersion when is_atom(TlsVersion) ->
++ TlsVersions = [TlsVersion]
++ end,
++ PRFS=[md5, sha, sha256, sha384, sha512],
++ %All are the result of running tls_v1:prf(PrfAlgo, <<>>, <<>>, <<>>, 16)
++ %with the specified PRF algorithm
++ ExpectedPrfResults=
++ [{md5, <<96,139,180,171,236,210,13,10,28,32,2,23,88,224,235,199>>},
++ {sha, <<95,3,183,114,33,169,197,187,231,243,19,242,220,228,70,151>>},
++ {sha256, <<166,249,145,171,43,95,158,232,6,60,17,90,183,180,0,155>>},
++ {sha384, <<153,182,217,96,186,130,105,85,65,103,123,247,146,91,47,106>>},
++ {sha512, <<145,8,98,38,243,96,42,94,163,33,53,49,241,4,127,28>>},
++ %TLS 1.0 and 1.1 PRF:
++ {md5sha, <<63,136,3,217,205,123,200,177,251,211,17,229,132,4,173,80>>}],
++ TestPlan = prf_create_plan(TlsVersions, PRFS, ExpectedPrfResults),
++ [{prf_test_plan, TestPlan} | Config];
+
+ init_per_testcase(TestCase, Config) when TestCase == ssl_accept_timeout;
+ TestCase == client_closes_socket;
+@@ -428,6 +455,25 @@ new_options_in_accept(Config) when is_list(Config) ->
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+ %%--------------------------------------------------------------------
++prf() ->
++ [{doc,"Test that ssl:prf/5 uses the negotiated PRF."}].
++prf(Config) when is_list(Config) ->
++ TestPlan = ?config(prf_test_plan, Config),
++ case TestPlan of
++ [] -> ct:fail({error, empty_prf_test_plan});
++ _ -> lists:foreach(fun(Suite) ->
++ lists:foreach(
++ fun(Test) ->
++ V = ?config(tls_ver, Test),
++ C = ?config(ciphers, Test),
++ E = ?config(expected, Test),
++ P = ?config(prf, Test),
++ prf_run_test(Config, V, C, E, P)
++ end, Suite)
++ end, TestPlan)
++ end.
++
++%%--------------------------------------------------------------------
+
+ connection_info() ->
+ [{doc,"Test the API function ssl:connection_information/1"}].
+@@ -762,6 +808,14 @@ fallback(Config) when is_list(Config) ->
+ Client, {error,{tls_alert,"inappropriate fallback"}}).
+
+ %%--------------------------------------------------------------------
++cipher_format() ->
++ [{doc, "Test that cipher conversion from tuples to binarys works"}].
++cipher_format(Config) when is_list(Config) ->
++ {ok, Socket} = ssl:listen(0, [{ciphers, ssl:cipher_suites()}]),
++ ssl:close(Socket).
++
++%%--------------------------------------------------------------------
++
+ peername() ->
+ [{doc,"Test API function peername/1"}].
+
+@@ -3653,8 +3707,84 @@ basic_test(Config) ->
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
++prf_create_plan(TlsVersions, PRFs, Results) ->
++ lists:foldl(fun(Ver, Acc) ->
++ A = prf_ciphers_and_expected(Ver, PRFs, Results),
++ [A|Acc]
++ end, [], TlsVersions).
++prf_ciphers_and_expected(TlsVer, PRFs, Results) ->
++ case TlsVer of
++ TlsVer when TlsVer == sslv3 orelse TlsVer == tlsv1
++ orelse TlsVer == 'tlsv1.1' ->
++ Ciphers = ssl:cipher_suites(),
++ {_, Expected} = lists:keyfind(md5sha, 1, Results),
++ [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected}, {prf, md5sha}]];
++ 'tlsv1.2' ->
++ lists:foldl(
++ fun(PRF, Acc) ->
++ Ciphers = prf_get_ciphers(TlsVer, PRF),
++ case Ciphers of
++ [] ->
++ ct:log("No ciphers for PRF algorithm ~p. Skipping.", [PRF]),
++ Acc;
++ Ciphers ->
++ {_, Expected} = lists:keyfind(PRF, 1, Results),
++ [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected},
++ {prf, PRF}] | Acc]
++ end
++ end, [], PRFs)
++ end.
++prf_get_ciphers(TlsVer, PRF) ->
++ case TlsVer of
++ 'tlsv1.2' ->
++ lists:filter(
++ fun(C) when tuple_size(C) == 4 andalso
++ element(4, C) == PRF ->
++ true;
++ (_) -> false
++ end, ssl:cipher_suites())
++ end.
++prf_run_test(_, TlsVer, [], _, Prf) ->
++ ct:fail({error, cipher_list_empty, TlsVer, Prf});
++prf_run_test(Config, TlsVer, Ciphers, Expected, Prf) ->
++ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
++ BaseOpts = [{active, true}, {versions, [TlsVer]}, {ciphers, Ciphers}],
++ ServerOpts = BaseOpts ++ ?config(server_opts, Config),
++ ClientOpts = BaseOpts ++ ?config(client_opts, Config),
++ Server = ssl_test_lib:start_server(
++ [{node, ServerNode}, {port, 0}, {from, self()},
++ {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}},
++ {options, ServerOpts}]),
++ Port = ssl_test_lib:inet_port(Server),
++ Client = ssl_test_lib:start_client(
++ [{node, ClientNode}, {port, Port},
++ {host, Hostname}, {from, self()},
++ {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}},
++ {options, ClientOpts}]),
++ ssl_test_lib:check_result(Server, ok, Client, ok),
++ ssl_test_lib:close(Server),
++ ssl_test_lib:close(Client).
++prf_verify_value(Socket, TlsVer, Expected, Algo) ->
++ Ret = ssl:prf(Socket, <<>>, <<>>, [<<>>], 16),
++ case TlsVer of
++ sslv3 ->
++ case Ret of
++ {error, undefined} -> ok;
++ _ ->
++ {error, {expected, {error, undefined},
++ got, Ret, tls_ver, TlsVer, prf_algorithm, Algo}}
++ end;
++ _ ->
++ case Ret of
++ {ok, Expected} -> ok;
++ {ok, Val} -> {error, {expected, Expected, got, Val, tls_ver, TlsVer,
++ prf_algorithm, Algo}}
++ end
++ end.
++
+ send_recv_result_timeout_client(Socket) ->
+ {error, timeout} = ssl:recv(Socket, 11, 500),
++ {error, timeout} = ssl:recv(Socket, 11, 0),
+ ssl:send(Socket, "Hello world"),
+ receive
+ Msg ->
+--- a/lib/ssl/vsn.mk
++++ b/lib/ssl/vsn.mk
+@@ -1 +1 @@
+-SSL_VSN = 7.3.1
++SSL_VSN = 7.3.3
+--- a/lib/test_server/src/test_server.erl
++++ b/lib/test_server/src/test_server.erl
+@@ -2165,10 +2165,19 @@ get_timetrap_info(TCPid, SendToServer) ->
+ Timers ->
+ case [Info || {Handle,Pid,Info} <- Timers,
+ Pid == TCPid, Handle /= infinity] of
+- [I|_] ->
+- I;
++ [{TVal,true}|_] ->
++ {TVal,{true,test_server:timetrap_scale_factor()}};
++ [{TVal,false}|_] ->
++ {TVal,{false,1}};
+ [] when SendToServer == true ->
+- tc_supervisor_req({get_timetrap_info,TCPid});
++ case tc_supervisor_req({get_timetrap_info,TCPid}) of
++ {TVal,true} ->
++ {TVal,{true,test_server:timetrap_scale_factor()}};
++ {TVal,false} ->
++ {TVal,{false,1}};
++ Error ->
++ Error
++ end;
+ [] ->
+ undefined
+ end
+--- a/lib/test_server/src/test_server_gl.erl
++++ b/lib/test_server/src/test_server_gl.erl
+@@ -131,6 +131,10 @@ set_props(GL, PropList) ->
+ %%% Internal functions.
+
+ init([]) ->
++ EscChars = case application:get_env(test_server, esc_chars) of
++ {ok,ECBool} -> ECBool;
++ _ -> true
++ end,
+ {ok,#st{tc_supervisor=none,
+ minor=none,
+ minor_monitor=none,
+@@ -139,7 +143,7 @@ init([]) ->
+ permit_io=gb_sets:empty(),
+ auto_nl=true,
+ levels={1,19,10},
+- escape_chars=true
++ escape_chars=EscChars
+ }}.
+
+ req(GL, Req) ->
+--- a/otp_versions.table
++++ b/otp_versions.table
+@@ -1,3 +1,5 @@
++OTP-18.3.4 : inets-6.2.4 ssl-7.3.3 # asn1-4.0.2 common_test-1.12.1 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
++OTP-18.3.3 : common_test-1.12.1 inets-6.2.3 ssl-7.3.2 # asn1-4.0.2 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
+ OTP-18.3.2 : inets-6.2.2 ssl-7.3.1 # asn1-4.0.2 common_test-1.12 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3.1 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 mnesia-4.13.4 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
+ OTP-18.3.1 : erts-7.3.1 inets-6.2.1 mnesia-4.13.4 # asn1-4.0.2 common_test-1.12 compiler-6.0.3 cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosNotification-1.2.1 cosProperty-1.2 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 et-1.5.1 eunit-2.2.13 gs-1.6 hipe-3.15 ic-4.4 jinterface-1.6.1 kernel-4.2 megaco-3.18 observer-2.1.2 odbc-2.11.1 orber-3.8.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 public_key-1.1.1 reltool-0.7 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 ssl-7.3 stdlib-2.8 syntax_tools-1.7 test_server-3.10 tools-2.8.3 typer-0.9.10 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 :
+ OTP-18.3 : asn1-4.0.2 common_test-1.12 compiler-6.0.3 cosNotification-1.2.1 cosTime-1.2.1 cosTransactions-1.3.1 crypto-3.6.3 debugger-4.1.2 dialyzer-2.9 diameter-1.11.2 edoc-0.7.18 eldap-1.2.1 erl_docgen-0.4.2 erl_interface-3.8.2 erts-7.3 eunit-2.2.13 hipe-3.15 inets-6.2 kernel-4.2 mnesia-4.13.3 observer-2.1.2 orber-3.8.1 public_key-1.1.1 runtime_tools-1.9.3 sasl-2.7 snmp-5.2.2 ssh-4.2.2 ssl-7.3 stdlib-2.8 test_server-3.10 tools-2.8.3 webtool-0.9.1 wx-1.6.1 xmerl-1.3.10 # cosEvent-2.2 cosEventDomain-1.2 cosFileTransfer-1.2 cosProperty-1.2 et-1.5.1 gs-1.6 ic-4.4 jinterface-1.6.1 megaco-3.18 odbc-2.11.1 os_mon-2.4 ose-1.1 otp_mibs-1.1 parsetools-2.1.1 percept-0.8.11 reltool-0.7 syntax_tools-1.7 typer-0.9.10 :
diff --git a/community/erlang/APKBUILD b/community/erlang/APKBUILD
index 34424f4..57fc384 100644
--- a/community/erlang/APKBUILD
@@ -2,7 +2,7 @@
# Maintainer: Marlus Saraiva <marlus.saraiva@gmail.com>
pkgname=erlang
-pkgver=18.3.2
+pkgver=18.3.4
_srcver=18.3
pkgrel=0
pkgdesc="General-purpose programming language and runtime environment"
@@ -75,7 +75,8 @@ source="http://www.erlang.org/download/otp_src_$_srcver.tar.gz
0007-Split-off-webtool-dependency-from-tools.patch
0010-fix-nteventlog-remove.patch
0060-set-disksup_posix_only-to-true.patch
- 0070-otp-update-version-18.3.2.patch"
+ 0070-otp-update-version-18.3.2.patch
+ 0080-otp-update-version-18.3.4.patch"
_builddir="$srcdir"/otp_src_$_srcver
@@ -219,7 +220,8 @@ a8e9e58c2444ac3994a6b418fd387369 0006-Do-not-install-erlang-sources.patch
b438c37818b5c82443682c3693dd7e53 0007-Split-off-webtool-dependency-from-tools.patch
0dd300003ff68fc46dc3c839c2541d53 0010-fix-nteventlog-remove.patch
d17fbaafa9f7820ade09b239c00aede6 0060-set-disksup_posix_only-to-true.patch
-b0d5cf3bac335964a1c8ffd98d9f105d 0070-otp-update-version-18.3.2.patch"
+b0d5cf3bac335964a1c8ffd98d9f105d 0070-otp-update-version-18.3.2.patch
+96d85db0efbac64be3ca3f9158766e59 0080-otp-update-version-18.3.4.patch"
sha256sums="fdab8129a1cb935db09f1832e3a7d511a4aeb2b9bb3602ca6a7ccb9730d5c9c3 otp_src_18.3.tar.gz
536e78192f915733cbbb264883af6d9b11c9e70c2c4d3d825b58c4dbec36db86 0001-Do-not-format-man-pages-and-do-not-install-miscellan.patch
b2e5844215d7f5f5026a77342dd698d16103cc726d23f8265bcc8399d1a82bb9 0002-Remove-rpath.patch
@@ -230,7 +232,8 @@ fcf2c37b3eaddd3e1bea2880c1b80f010623743ab2f43e144e0ecc7be874d4ff 0006-Do-not-in
9704a53bc4bd6f5624f9ce3f201128204011f4579f19b547df74d92ae22777cf 0007-Split-off-webtool-dependency-from-tools.patch
019c62ea3fee60068caa8c3152d7f96e76591fc5dc096abfcea48ec1593eb758 0010-fix-nteventlog-remove.patch
3ffda0b3acbde755b496c1a974c20e1ca580432c403944c1c16836f48e248429 0060-set-disksup_posix_only-to-true.patch
-ec32540b798d2b3a7b8271cf95c63423dda864b24c8765368835295bf4575c61 0070-otp-update-version-18.3.2.patch"
+ec32540b798d2b3a7b8271cf95c63423dda864b24c8765368835295bf4575c61 0070-otp-update-version-18.3.2.patch
+9f9091b9eaa84e0b07b0f849e6311720b4cf2fed2ea1314e1470e5e65f8959a1 0080-otp-update-version-18.3.4.patch"
sha512sums="f4a69bb14743d9f913d7060cfb6426f7c54693d07ed439506ede5160a0ba5a79a81cf08a56aaa02c68b3377cd22fed66a0fbeb216378330d7ad5c6348ed82014 otp_src_18.3.tar.gz
172f9b0f61748bf2f04737df0451bcc9c0812db0f365f0ef7c7c244fceb991987f79a5274eea578a89cc7d077f84645d1395d9dbcbf14010268b896a080c2ccf 0001-Do-not-format-man-pages-and-do-not-install-miscellan.patch
97c40c185b71b23ffcb924639af390b9a0be897070e396a9413e193e6d43048dfe3b86bcd0c5598b81a66ce1349de9e09f5228a452b51b6612cd947bbafb6377 0002-Remove-rpath.patch
@@ -241,4 +244,5 @@ ef9045ec689e2a6517dba7626c1225a9a265f14675d704effd324529c3d5128260ba64cebfd41185
d65fcfc3a9441941c6292c33d354964a82da11cc7a411dd0440719e490075ed588c1e70690e80650561749010d737394567f12ff73bd460f0d00c792c77cfae3 0007-Split-off-webtool-dependency-from-tools.patch
b7387f92f8c27a0565c7885bba4b357183c62d422616e073bc5ffad338a0e22cb5165dcb3b95bf0b920ba00831599f2216027883f4be255aa6f6150b68b7a37c 0010-fix-nteventlog-remove.patch
f3c50f8610e08173c365a575737079b0259db6fc0dfa5b1be63c281b52358bbce9e2595132a8f6a06772785d7d38d108f8da8515e2f7706cd03b6585aab4d071 0060-set-disksup_posix_only-to-true.patch
-e472bdb4b322d66b9c0ad9627abc469b8c648145ffad1dd7f645b38b2fad43492f475d2fd4d0bab159be6110db5043bb6d792d72e8276b3c3869be46129c6616 0070-otp-update-version-18.3.2.patch"
+e472bdb4b322d66b9c0ad9627abc469b8c648145ffad1dd7f645b38b2fad43492f475d2fd4d0bab159be6110db5043bb6d792d72e8276b3c3869be46129c6616 0070-otp-update-version-18.3.2.patch
+84ee0512b5abad52f102c9696dd9ceec344f50a395078684ba01adba4efb2b287fc0c1f5e982898b0aa29c77a789b06eace156e20e8774ce07e8297f85479cd3 0080-otp-update-version-18.3.4.patch"
--
2.9.0
---
Unsubscribe: alpine-aports+unsubscribe@lists.alpinelinux.org
Help: alpine-aports+help@lists.alpinelinux.org
---