Returns the Value of the environment variable
++ VarName, or DefaultValue if the environment variable
++ is undefined.
++
If Unicode file name encoding is in effect (see the erl manual
++ page), the strings (both VarName and
++ Value) may contain characters with codepoints > 255.
++
++
++
+
+ Return the process identifier of the emulator process
+
+diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
+index 9415593..d5ef994 100644
+--- a/lib/kernel/src/os.erl
++++ b/lib/kernel/src/os.erl
+@@ -26,7 +26,7 @@
+
+ %%% BIFs
+
+--export([getenv/0, getenv/1, getpid/0, putenv/2, timestamp/0, unsetenv/1]).
++-export([getenv/0, getenv/1, getenv/2, getpid/0, putenv/2, timestamp/0, unsetenv/1]).
+
+ -spec getenv() -> [string()].
+
+@@ -39,6 +39,19 @@ getenv() -> erlang:nif_error(undef).
+ getenv(_) ->
+ erlang:nif_error(undef).
+
++-spec getenv(VarName, DefaultValue) -> Value when
++ VarName :: string(),
++ DefaultValue :: string(),
++ Value :: string().
++
++getenv(VarName, DefaultValue) ->
++ case os:getenv(VarName) of
++ false ->
++ DefaultValue;
++ Value ->
++ Value
++ end.
++
+ -spec getpid() -> Value when
+ Value :: string().
+
diff --git a/otp-0019-Patch-removes-support-for-SSLv3-protocol-because-it-.patch b/otp-0019-Patch-removes-support-for-SSLv3-protocol-because-it-.patch
new file mode 100644
index 0000000..359c011
--- /dev/null
+++ b/otp-0019-Patch-removes-support-for-SSLv3-protocol-because-it-.patch
@@ -0,0 +1,99 @@
+From: Sergei Golovan
+Date: Sun, 30 Nov 2014 20:20:41 +0300
+Subject: [PATCH] Patch removes support for SSLv3 protocol because it is proved
+ to be insecure and nobody should use it anymore.
+
+
+diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
+index 1d74faf..912acc2 100644
+--- a/lib/ssl/doc/src/ssl.xml
++++ b/lib/ssl/doc/src/ssl.xml
+@@ -123,7 +123,7 @@
+
+
Protocol that will be supported by started clients and
+ servers. If this option is not set it will default to all
+@@ -58,6 +58,9 @@
+ Note that this option may be overridden by the version option
+ to ssl:connect/[2,3] and ssl:listen/2.
+
++
For Debian GNU/Linux distribution the sslv3 protocol was
++ disabled due to its security issues.
++
This option only affects TLS-1.0 connections.
++ If set to false it disables the block cipher padding check
++ to be able to interoperate with legacy software.
++
++
++
Using this option makes TLS vulnerable to
++ the Poodle attack
-diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd.c otp_src_R16B03-1/erts/epmd/src/epmd.c
---- otp_src_R16B03-1.orig/erts/epmd/src/epmd.c 2016-01-16 14:06:42.823537438 -0500
-+++ otp_src_R16B03-1/erts/epmd/src/epmd.c 2016-01-16 14:07:02.565424309 -0500
-@@ -342,7 +342,7 @@
+diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c
+index 1757fa9..ebae0a5 100644
+--- a/erts/epmd/src/epmd.c
++++ b/erts/epmd/src/epmd.c
+@@ -342,7 +342,7 @@ static void run_daemon(EpmdVars *g)
for (fd = 0; fd < g->max_conn ; fd++) /* close all files ... */
close(fd);
/* Syslog on linux will try to write to whatever if we dont
@@ -54,10 +100,11 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd.c otp_src_R16B03-1/erts/epmd
closelog();
/* These chouldn't be needed but for safety... */
-diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_cli.c otp_src_R16B03-1/erts/epmd/src/epmd_cli.c
---- otp_src_R16B03-1.orig/erts/epmd/src/epmd_cli.c 2016-01-16 14:06:42.823537438 -0500
-+++ otp_src_R16B03-1/erts/epmd/src/epmd_cli.c 2016-01-16 14:07:02.565424309 -0500
-@@ -135,19 +135,33 @@
+diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c
+index 8817bde..c0b3729 100644
+--- a/erts/epmd/src/epmd_cli.c
++++ b/erts/epmd/src/epmd_cli.c
+@@ -135,19 +135,33 @@ void epmd_call(EpmdVars *g,int what)
static int conn_to_epmd(EpmdVars *g)
{
struct EPMD_SOCKADDR_IN address;
@@ -91,17 +138,18 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_cli.c otp_src_R16B03-1/erts/
- if (connect(connect_sock, (struct sockaddr*)&address, sizeof address) < 0)
+ connect_sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (connect_sock<0)
-+ goto error;
+ goto error;
+
+ if (connect(connect_sock, (struct sockaddr*)&address, salen) < 0)
- goto error;
++ goto error;
+
return connect_sock;
error:
-diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_int.h otp_src_R16B03-1/erts/epmd/src/epmd_int.h
---- otp_src_R16B03-1.orig/erts/epmd/src/epmd_int.h 2016-01-16 14:06:42.823537438 -0500
-+++ otp_src_R16B03-1/erts/epmd/src/epmd_int.h 2016-01-16 14:07:02.565424309 -0500
+diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
+index 363923e..e028449 100644
+--- a/erts/epmd/src/epmd_int.h
++++ b/erts/epmd/src/epmd_int.h
@@ -44,9 +44,14 @@
#include
@@ -198,10 +246,11 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_int.h otp_src_R16B03-1/erts/
#endif /* Not IP v6 */
/* ************************************************************************ */
-diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/epmd/src/epmd_srv.c
---- otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c 2016-01-16 14:06:42.823537438 -0500
-+++ otp_src_R16B03-1/erts/epmd/src/epmd_srv.c 2016-01-16 14:09:00.945745937 -0500
-@@ -70,6 +70,7 @@
+diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
+index 78524a6..7b4d774 100644
+--- a/erts/epmd/src/epmd_srv.c
++++ b/erts/epmd/src/epmd_srv.c
+@@ -70,6 +70,7 @@ static time_t current_time(EpmdVars*);
static Connection *conn_init(EpmdVars*);
static int conn_open(EpmdVars*,int);
@@ -209,7 +258,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
static int conn_close_fd(EpmdVars*,int);
static void node_init(EpmdVars*);
-@@ -200,10 +201,11 @@
+@@ -200,10 +201,11 @@ void run(EpmdVars *g)
{
struct EPMD_SOCKADDR_IN iserv_addr[MAX_LISTEN_SOCKETS];
int listensock[MAX_LISTEN_SOCKETS];
@@ -222,7 +271,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
node_init(g);
g->conn = conn_init(g);
-@@ -246,64 +248,82 @@
+@@ -246,64 +248,82 @@ void run(EpmdVars *g)
if (g->addresses != NULL && /* String contains non-separator characters if: */
g->addresses[strspn(g->addresses," ,")] != '\000')
{
@@ -231,8 +280,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
- int loopback_ok = 0;
+ char *tmp = NULL;
+ char *token = NULL;
-
-- if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL)
++
+ /* Always listen on the loopback. */
+ SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_LOOPBACK),sport);
+ num_sockets++;
@@ -240,7 +288,8 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
+ SET_ADDR6(iserv_addr[num_sockets],in6addr_loopback,sport);
+ num_sockets++;
+#endif
-+
+
+- if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL)
+ if ((tmp = strdup(g->addresses)) == NULL)
{
dbg_perror(g,"cannot allocate memory");
@@ -297,10 +346,10 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
if (IS_ADDR_LOOPBACK(addr))
- loopback_ok = 1;
+ continue;
++
++ num_sockets++;
- if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1)
-+ num_sockets++;
-+
+ if (num_sockets >= MAX_LISTEN_SOCKETS)
{
dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses",
@@ -332,7 +381,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
}
#ifdef HAVE_SYSTEMD_SD_DAEMON_H
}
-@@ -334,13 +354,39 @@
+@@ -334,13 +354,39 @@ void run(EpmdVars *g)
#endif
for (i = 0; i < num_sockets; i++)
{
@@ -376,7 +425,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
/*
* Note that we must not enable the SO_REUSEADDR on Windows,
* because addresses will be reused even if they are still in use.
-@@ -372,8 +418,7 @@
+@@ -372,8 +418,7 @@ void run(EpmdVars *g)
dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
listensock[i]);
@@ -386,7 +435,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
{
if (errno == EADDRINUSE)
{
-@@ -388,12 +433,20 @@
+@@ -388,12 +433,20 @@ void run(EpmdVars *g)
}
}
@@ -407,7 +456,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
#ifdef HAVE_SYSTEMD_SD_DAEMON_H
}
sd_notifyf(0, "READY=1\n"
-@@ -1001,15 +1054,6 @@
+@@ -1001,15 +1054,6 @@ static int conn_open(EpmdVars *g,int fd)
for (i = 0; i < g->max_conn; i++) {
if (g->conn[i].open == EPMD_FALSE) {
@@ -423,7 +472,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
g->active_conn++;
s = &g->conn[i];
-@@ -1020,20 +1064,7 @@
+@@ -1020,20 +1064,7 @@ static int conn_open(EpmdVars *g,int fd)
s->open = EPMD_TRUE;
s->keep = EPMD_FALSE;
@@ -445,7 +494,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" :
"Non-local peer connected");
-@@ -1041,7 +1072,7 @@
+@@ -1041,7 +1072,7 @@ static int conn_open(EpmdVars *g,int fd)
s->got = 0;
s->mod_time = current_time(g); /* Note activity */
@@ -454,7 +503,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
if (s->buf == NULL) {
dbg_printf(g,0,"epmd: Insufficient memory");
-@@ -1059,6 +1090,60 @@
+@@ -1059,6 +1090,60 @@ static int conn_open(EpmdVars *g,int fd)
return EPMD_FALSE;
}
@@ -515,9 +564,10 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/
static int conn_close_fd(EpmdVars *g,int fd)
{
int i;
-diff -Naur otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl otp_src_R16B03-1/erts/epmd/test/epmd_SUITE.erl
---- otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl 2016-01-16 14:06:42.823537438 -0500
-+++ otp_src_R16B03-1/erts/epmd/test/epmd_SUITE.erl 2016-01-16 14:07:02.566424303 -0500
+diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl
+index cc24a55..ddfe5ab 100644
+--- a/erts/epmd/test/epmd_SUITE.erl
++++ b/erts/epmd/test/epmd_SUITE.erl
@@ -42,6 +42,7 @@
-export(
[
@@ -536,7 +586,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl otp_src_R16B03-1/
register_duplicate_name, unicode_name, long_unicode_name,
get_port_nr, slow_get_port_nr,
unregister_others_name_1, unregister_others_name_2,
-@@ -165,6 +167,24 @@
+@@ -165,6 +167,24 @@ register_name(Config) when is_list(Config) ->
?line ok = close(Sock), % Unregister
ok.
@@ -561,7 +611,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl otp_src_R16B03-1/
register_names_1(doc) ->
["Register and unregister two nodes"];
register_names_1(suite) ->
-@@ -238,13 +258,18 @@
+@@ -238,13 +258,18 @@ register_node(Name) ->
register_node(Name,Port) ->
register_node_v2(Port,$M,0,5,5,Name,"").
@@ -581,7 +631,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl otp_src_R16B03-1/
{ok,Sock} ->
case recv(Sock,4) of
{ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} ->
-@@ -1129,7 +1154,9 @@
+@@ -1129,7 +1154,9 @@ send_direct(Sock, Bytes) ->
end.
send_req(Req) ->
@@ -592,9 +642,10 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl otp_src_R16B03-1/
{ok,Sock} ->
case send(Sock, [size16(Req), Req]) of
ok ->
-diff -Naur otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl otp_src_R16B03-1/lib/kernel/src/erl_epmd.erl
---- otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl 2016-01-16 14:06:43.005536396 -0500
-+++ otp_src_R16B03-1/lib/kernel/src/erl_epmd.erl 2016-01-16 14:07:02.566424303 -0500
+diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
+index 91af49f..21a3dec 100644
+--- a/lib/kernel/src/erl_epmd.erl
++++ b/lib/kernel/src/erl_epmd.erl
@@ -31,7 +31,7 @@
%% External exports
-export([start/0, start_link/0, stop/0, port_please/2,
@@ -604,7 +655,7 @@ diff -Naur otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl otp_src_R16B03-1/li
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
-@@ -106,7 +106,9 @@
+@@ -106,7 +106,9 @@ names1(HostName) ->
register_node(Name, PortNo) ->
@@ -615,7 +666,7 @@ diff -Naur otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl otp_src_R16B03-1/li
%%%----------------------------------------------------------------------
%%% Callback functions from gen_server
-@@ -124,10 +126,10 @@
+@@ -124,10 +126,10 @@ init(_) ->
-spec handle_call(calls(), term(), state()) ->
{'reply', term(), state()} | {'stop', 'shutdown', 'ok', state()}.
@@ -628,7 +679,7 @@ diff -Naur otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl otp_src_R16B03-1/li
{alive, Socket, Creation} ->
S = State#state{socket = Socket,
port_no = PortNo,
-@@ -210,8 +212,12 @@
+@@ -210,8 +212,12 @@ open({A,B,C,D,E,F,G,H}=EpmdAddr, Timeout) when ?ip6(A,B,C,D,E,F,G,H) ->
close(Socket) ->
gen_tcp:close(Socket).
diff --git a/otp-0024-Remove-unused-code-in-error-logger-handlers.patch b/otp-0024-Remove-unused-code-in-error-logger-handlers.patch
new file mode 100644
index 0000000..a3ea0ac
--- /dev/null
+++ b/otp-0024-Remove-unused-code-in-error-logger-handlers.patch
@@ -0,0 +1,1028 @@
+From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
+Date: Thu, 20 Aug 2015 09:44:43 +0200
+Subject: [PATCH] Remove unused code in error logger handlers
+
+A long time ago, errors from the emulator itself was sent as
+messages that would end up in the handle_info/2 function.
+Those clauses in handle_info/2 can be removed.
+
+The code for handling events tagged 'info' instead of 'info_msg'
+can also be taken out.
+
+error_logger_file_h: Refactor and modernize code
+
+Refactor, simplify, and modernize the code to facilitate future
+improvements in the following commits.
+
+Teach error_logger_file_h to truncate big messages
+
+Add the possibility to truncate big messages to avoid running out
+of memory.
+
+error_logger_tty_h: Refactor and modernize code
+
+Refactor, simplify, and modernize the code to facilitate future
+improvements in the following commits.
+
+Teach error_logger_tty_h to truncate big messages
+
+Add the possibility to truncate big messages to avoid running out
+of memory.
+
+stdlib: Fix leaking files after error_logger:logfile(close)
+
+Introduced when changing state from tuple to record.
+
+Make the scanned form of the io_lib format strings available for processing
+
+This adds three new functions to io_lib - scan_format/2, unscan_format/1,
+and build_text/1 - which expose the parsed form of the format control
+sequences to make it possible to easily modify or filter the input to
+io_lib:format/2. This can e.g. be used in order to replace unbounded-size
+control sequences like ~w or ~p with corresponding depth-limited ~W and ~P
+before doing the actual formatting.
+
+diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml
+index 90f24c4..6613dcd 100644
+--- a/lib/stdlib/doc/src/io.xml
++++ b/lib/stdlib/doc/src/io.xml
+@@ -505,7 +505,8 @@ ok
+
Writes the data with standard syntax in the same way as
+ ~w, but breaks terms whose printed representation
+ is longer than one line into many lines and indents each
+- line sensibly. It also tries to detect lists of
++ line sensibly. Left justification is not supported.
++ It also tries to detect lists of
+ printable characters and to output these as strings. The
+ Unicode translation modifier is used for determining
+ what characters are printable. For example:
+diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml
+index 68352ff..0c1c84d 100644
+--- a/lib/stdlib/doc/src/io_lib.xml
++++ b/lib/stdlib/doc/src/io_lib.xml
+@@ -4,7 +4,7 @@
+
+
+
+- 19962013
++ 19962014
+ Ericsson AB. All Rights Reserved.
+
+
+@@ -59,6 +59,35 @@
+
+
+
++
++
++
Description:
++
++
control_char is the type of control
++ sequence: $P, $w, and so on;
++
++
args is a list of the arguments used by the
++ control sequence, or an empty list if the control sequence
++ does not take any arguments;
++
++
width is the field width;
++
++
adjust is the adjustment;
++
++
precision is the precision of the printed
++ argument;
++
++
pad_char is the padding character;
++
++
encoding is set to true if the translation
++ modifier t is present;
++
++
strings is set to false if the modifier
++ l is present.
++
++
++
++
+
+
+
+@@ -260,6 +289,45 @@
+
+
+
++
++ Parse all control sequences in the format string
++
++
Returns a list corresponding to the given format string,
++ where control sequences have been replaced with
++ corresponding tuples. This list can be passed to io_lib:build_text/1 to have
++ the same effect as io_lib:format(Format, Args), or to
++ io_lib:unscan_format/1
++ in order to get the corresponding pair of Format and
++ Args (with every * and corresponding argument
++ expanded to numeric values).
++
A typical use of this function is to replace unbounded-size
++ control sequences like ~w and ~p with the
++ depth-limited variants ~W and ~P before
++ formatting to text, e.g. in a logger.
++
++
++
++
++ Revert a pre-parsed format list to a plain character list
++ and a list of arguments
++
++
See io_lib:scan_format/2 for
++ details.
++
++
++
++
++ Build the output text for a pre-parsed format list
++
++
See io_lib:scan_format/2 for
++ details.
++
++
++
+
+ Indentation after printing string
+
+diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl
+index e9364ed..ca44d01 100644
+--- a/lib/stdlib/src/error_logger_file_h.erl
++++ b/lib/stdlib/src/error_logger_file_h.erl
+@@ -23,24 +23,28 @@
+
+ %%%
+ %%% A handler that can be connected to the error_logger
+-%%% event handler.
+-%%% Writes all events formatted to file.
+-%%% Handles events tagged error, emulator and info.
++%%% event handler. Writes all events formatted to file.
+ %%%
+ %%% It can only be started from error_logger:swap_handler({logfile, File})
+-%%% or error_logger:logfile(File)
++%%% or error_logger:logfile(File).
+ %%%
+
+ -export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2, code_change/3]).
+
++-record(st,
++ {fd,
++ filename,
++ prev_handler,
++ depth=unlimited :: 'unlimited' | non_neg_integer()}).
++
+ %% This one is used when we takeover from the simple error_logger.
+ init({File, {error_logger, Buf}}) ->
+ case init(File, error_logger) of
+- {ok, {Fd, File, PrevHandler}} ->
+- write_events(Fd, Buf),
+- {ok, {Fd, File, PrevHandler}};
++ {ok, State} ->
++ write_events(State, Buf),
++ {ok, State};
+ Error ->
+ Error
+ end;
+@@ -52,49 +56,45 @@ init(File, PrevHandler) ->
+ process_flag(trap_exit, true),
+ case file:open(File, [write]) of
+ {ok,Fd} ->
+- {ok, {Fd, File, PrevHandler}};
++ Depth = get_depth(),
++ State = #st{fd=Fd,filename=File,prev_handler=PrevHandler,
++ depth=Depth},
++ {ok, State};
+ Error ->
+ Error
+ end.
+-
++
++get_depth() ->
++ case application:get_env(kernel, error_logger_format_depth) of
++ {ok, Depth} when is_integer(Depth) ->
++ max(10, Depth);
++ undefined ->
++ unlimited
++ end.
++
+ handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
+ {ok, State};
+-handle_event(Event, {Fd, File, PrevHandler}) ->
+- write_event(Fd, tag_event(Event)),
+- {ok, {Fd, File, PrevHandler}};
+-handle_event(_, State) ->
++handle_event(Event, State) ->
++ write_event(State, Event),
+ {ok, State}.
+
+-handle_info({'EXIT', Fd, _Reason}, {Fd, _File, PrevHandler}) ->
++handle_info({'EXIT', Fd, _Reason}, #st{fd=Fd,prev_handler=PrevHandler}) ->
+ case PrevHandler of
+ [] ->
+ remove_handler;
+ _ ->
+ {swap_handler, install_prev, [], PrevHandler, go_back}
+ end;
+-handle_info({emulator, GL, Chars}, {Fd, File, PrevHandler})
+- when node(GL) == node() ->
+- write_event(Fd, tag_event({emulator, GL, Chars})),
+- {ok, {Fd, File, PrevHandler}};
+-handle_info({emulator, noproc, Chars}, {Fd, File, PrevHandler}) ->
+- write_event(Fd, tag_event({emulator, noproc, Chars})),
+- {ok, {Fd, File, PrevHandler}};
+ handle_info(_, State) ->
+ {ok, State}.
+
+-handle_call(filename, {Fd, File, Prev}) ->
+- {ok, File, {Fd, File, Prev}};
++handle_call(filename, #st{filename=File}=State) ->
++ {ok, File, State};
+ handle_call(_Query, State) ->
+ {ok, {error, bad_query}, State}.
+
+-terminate(_Reason, State) ->
+- case State of
+- {Fd, _File, _Prev} ->
+- ok = file:close(Fd);
+- _ ->
+- ok
+- end,
+- [].
++terminate(_Reason, #st{fd=Fd}) ->
++ file:close(Fd).
+
+ code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+@@ -103,69 +103,71 @@ code_change(_OldVsn, State, _Extra) ->
+ %%% Misc. functions.
+ %%% ------------------------------------------------------
+
+-tag_event(Event) ->
+- {erlang:universaltime(), Event}.
++write_events(State, [Ev|Es]) ->
++ %% Write the events in reversed order.
++ write_events(State, Es),
++ write_event(State, Ev);
++write_events(_State, []) ->
++ ok.
+
+-write_events(Fd, Events) -> write_events1(Fd, lists:reverse(Events)).
++write_event(#st{fd=Fd}=State, Event) ->
++ case parse_event(Event) of
++ ignore ->
++ ok;
++ {Head,Pid,FormatList} ->
++ Time = maybe_utc(erlang:universaltime()),
++ Header = write_time(Time, Head),
++ Body = format_body(State, FormatList),
++ AtNode = if
++ node(Pid) =/= node() ->
++ ["** at node ",atom_to_list(node(Pid))," **\n"];
++ true ->
++ []
++ end,
++ io:put_chars(Fd, [Header,Body,AtNode])
++ end.
+
+-write_events1(Fd, [Event|Es]) ->
+- write_event(Fd, Event),
+- write_events1(Fd, Es);
+-write_events1(_, []) ->
+- ok.
++format_body(State, [{Format,Args}|T]) ->
++ S = try format(State, Format, Args) of
++ S0 ->
++ S0
++ catch
++ _:_ ->
++ format(State, "ERROR: ~p - ~p\n", [Format,Args])
++ end,
++ [S|format_body(State, T)];
++format_body(_State, []) ->
++ [].
+
+-write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) ->
+- T = write_time(maybe_utc(Time)),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- io:format(Fd, T ++ S, []);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- io:format(Fd, T ++ F, [Format,Args])
+- end;
+-write_event(Fd, {Time, {emulator, _GL, Chars}}) ->
+- T = write_time(maybe_utc(Time)),
+- case catch io_lib:format(Chars, []) of
+- S when is_list(S) ->
+- io:format(Fd, T ++ S, []);
+- _ ->
+- io:format(Fd, T ++ "ERROR: ~p ~n", [Chars])
+- end;
+-write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) ->
+- T = write_time(maybe_utc(Time)),
+- io:format(Fd, T ++ add_node("~p~n",Pid),[Info]);
+-write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) ->
+- T = write_time(maybe_utc(Time)),
+- S = format_report(Rep),
+- io:format(Fd, T ++ S ++ add_node("", Pid), []);
+-write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) ->
+- T = write_time(maybe_utc(Time), "INFO REPORT"),
+- S = format_report(Rep),
+- io:format(Fd, T ++ S ++ add_node("", Pid), []);
+-write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) ->
+- T = write_time(maybe_utc(Time), "INFO REPORT"),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- io:format(Fd, T ++ S, []);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- io:format(Fd, T ++ F, [Format,Args])
+- end;
+-write_event(Fd, {Time, {warning_report, _GL, {Pid, std_warning, Rep}}}) ->
+- T = write_time(maybe_utc(Time), "WARNING REPORT"),
+- S = format_report(Rep),
+- io:format(Fd, T ++ S ++ add_node("", Pid), []);
+-write_event(Fd, {Time, {warning_msg, _GL, {Pid, Format, Args}}}) ->
+- T = write_time(maybe_utc(Time), "WARNING REPORT"),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- io:format(Fd, T ++ S, []);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- io:format(Fd, T ++ F, [Format,Args])
+- end;
+-write_event(_, _) ->
+- ok.
++format(#st{depth=unlimited}, Format, Args) ->
++ io_lib:format(Format, Args);
++format(#st{depth=Depth}, Format0, Args) ->
++ Format1 = io_lib:scan_format(Format0, Args),
++ Format = limit_format(Format1, Depth),
++ io_lib:build_text(Format).
++
++limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p;
++ C0 =:= $w ->
++ C = C0 - ($a - $A), %To uppercase.
++ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}||limit_format(T, Depth)];
++limit_format([H|T], Depth) ->
++ [H|limit_format(T, Depth)];
++limit_format([], _) ->
++ [].
++
++parse_event({error, _GL, {Pid, Format, Args}}) ->
++ {"ERROR REPORT",Pid,[{Format,Args}]};
++parse_event({info_msg, _GL, {Pid, Format, Args}}) ->
++ {"INFO REPORT",Pid,[{Format, Args}]};
++parse_event({warning_msg, _GL, {Pid, Format, Args}}) ->
++ {"WARNING REPORT",Pid,[{Format,Args}]};
++parse_event({error_report, _GL, {Pid, std_error, Args}}) ->
++ {"ERROR REPORT",Pid,format_term(Args)};
++parse_event({info_report, _GL, {Pid, std_info, Args}}) ->
++ {"INFO REPORT",Pid,format_term(Args)};
++parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
++ {"WARNING REPORT",Pid,format_term(Args)};
++parse_event(_) -> ignore.
+
+ maybe_utc(Time) ->
+ UTC = case application:get_env(sasl, utc_log) of
+@@ -182,30 +184,27 @@ maybe_utc(Time) ->
+ maybe_utc(Time, true) -> {utc, Time};
+ maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}.
+
+-format_report(Rep) when is_list(Rep) ->
+- case string_p(Rep) of
++format_term(Term) when is_list(Term) ->
++ case string_p(Term) of
+ true ->
+- io_lib:format("~s~n",[Rep]);
+- _ ->
+- format_rep(Rep)
++ [{"~s\n",[Term]}];
++ false ->
++ format_term_list(Term)
+ end;
+-format_report(Rep) ->
+- io_lib:format("~p~n",[Rep]).
+-
+-format_rep([{Tag,Data}|Rep]) ->
+- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
+-format_rep([Other|Rep]) ->
+- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep);
+-format_rep(_) ->
++format_term(Term) ->
++ [{"~p\n",[Term]}].
++
++format_term_list([{Tag,Data}|T]) ->
++ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
++format_term_list([Data|T]) ->
++ [{" ~p\n",[Data]}|format_term_list(T)];
++format_term_list([]) ->
++ [];
++format_term_list(_) ->
++ %% Continue to allow non-proper lists for now.
++ %% FIXME: Remove this clause in OTP 19.
+ [].
+
+-add_node(X, Pid) when is_atom(X) ->
+- add_node(atom_to_list(X), Pid);
+-add_node(X, Pid) when node(Pid) =/= node() ->
+- lists:concat([X,"** at node ",node(Pid)," **~n"]);
+-add_node(X, _) ->
+- X.
+-
+ string_p([]) ->
+ false;
+ string_p(Term) ->
+@@ -221,15 +220,10 @@ string_p1([$\b|T]) -> string_p1(T);
+ string_p1([$\f|T]) -> string_p1(T);
+ string_p1([$\e|T]) -> string_p1(T);
+ string_p1([H|T]) when is_list(H) ->
+- case string_p1(H) of
+- true -> string_p1(T);
+- _ -> false
+- end;
++ string_p1(H) andalso string_p1(T);
+ string_p1([]) -> true;
+ string_p1(_) -> false.
+
+-write_time(Time) -> write_time(Time, "ERROR REPORT").
+-
+ write_time({utc,{{Y,Mo,D},{H,Mi,S}}}, Type) ->
+ io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
+ [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
+diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl
+index ad5891f..72a052f 100644
+--- a/lib/stdlib/src/error_logger_tty_h.erl
++++ b/lib/stdlib/src/error_logger_tty_h.erl
+@@ -22,144 +22,178 @@
+
+ %%%
+ %%% A handler that can be connected to the error_logger
+-%%% event handler.
+-%%% Writes all events formatted to stdout.
+-%%% Handles events tagged error, emulator and info.
++%%% event handler. Writes all events formatted to stdout.
+ %%%
+ %%% It can only be started from error_logger:swap_handler(tty)
+-%%% or error_logger:tty(true)
++%%% or error_logger:tty(true).
+ %%%
+
+ -export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+--export([write_event/2]).
++-export([write_event/2,write_event/3]).
++
++-record(st,
++ {user,
++ prev_handler,
++ io_mod=io,
++ depth=unlimited}).
+
+ %% This one is used when we takeover from the simple error_logger.
+ init({[], {error_logger, Buf}}) ->
+ User = set_group_leader(),
+- write_events(Buf,io),
+- {ok, {User, error_logger}};
++ Depth = get_depth(),
++ State = #st{user=User,prev_handler=error_logger,depth=Depth},
++ write_events(State, Buf),
++ {ok, State};
+ %% This one is used if someone took over from us, and now wants to
+ %% go back.
+ init({[], {error_logger_tty_h, PrevHandler}}) ->
+ User = set_group_leader(),
+- {ok, {User, PrevHandler}};
++ {ok, #st{user=User,prev_handler=PrevHandler}};
+ %% This one is used when we are started directly.
+ init([]) ->
+ User = set_group_leader(),
+- {ok, {User, []}}.
++ Depth = get_depth(),
++ {ok, #st{user=User,prev_handler=[],depth=Depth}}.
++
++get_depth() ->
++ case application:get_env(kernel, error_logger_format_depth) of
++ {ok, Depth} when is_integer(Depth) ->
++ max(10, Depth);
++ undefined ->
++ unlimited
++ end.
+
+ handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
+ {ok, State};
+ handle_event(Event, State) ->
+- write_event(tag_event(Event),io),
++ ok = do_write_event(State, tag_event(Event)),
+ {ok, State}.
+
+-handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) ->
++handle_info({'EXIT', User, _Reason},
++ #st{user=User,prev_handler=PrevHandler}=State) ->
+ case PrevHandler of
+ [] ->
+ remove_handler;
+ _ ->
+- {swap_handler, install_prev, {User, PrevHandler},
++ {swap_handler, install_prev, State,
+ PrevHandler, go_back}
+ end;
+-handle_info({emulator, GL, Chars}, State) when node(GL) == node() ->
+- write_event(tag_event({emulator, GL, Chars}),io),
+- {ok, State};
+-handle_info({emulator, noproc, Chars}, State) ->
+- write_event(tag_event({emulator, noproc, Chars}),io),
+- {ok, State};
+ handle_info(_, State) ->
+ {ok, State}.
+
+ handle_call(_Query, State) -> {ok, {error, bad_query}, State}.
+
+-% unfortunately, we can't unlink from User - links are not counted!
+-% if pid(User) -> unlink(User); true -> ok end,
+ terminate(install_prev, _State) ->
+ [];
+-terminate(_Reason, {_User, PrevHandler}) ->
++terminate(_Reason, #st{prev_handler=PrevHandler}) ->
+ {error_logger_tty_h, PrevHandler}.
+
+ code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
++%% Exported (but unoffical) API.
++write_event(Event, IoMod) ->
++ do_write_event(#st{io_mod=IoMod}, Event).
++
++write_event(Event, IoMod, Depth) ->
++ do_write_event(#st{io_mod=IoMod,depth=Depth}, Event).
++
++
+ %%% ------------------------------------------------------
+ %%% Misc. functions.
+ %%% ------------------------------------------------------
+
+ set_group_leader() ->
+ case whereis(user) of
+- User when is_pid(User) -> link(User), group_leader(User,self()), User;
+- _ -> false
++ User when is_pid(User) ->
++ link(User),
++ group_leader(User,self()),
++ User;
++ _ ->
++ false
+ end.
+
+ tag_event(Event) ->
+ {erlang:universaltime(), Event}.
+
+-write_events(Events,IOMod) -> write_events1(lists:reverse(Events),IOMod).
+-
+-write_events1([Event|Es],IOMod) ->
+- write_event(Event,IOMod),
+- write_events1(Es,IOMod);
+-write_events1([],_IOMod) ->
++write_events(State, [Ev|Es]) ->
++ %% Write the events in reverse order.
++ _ = write_events(State, Es),
++ _ = do_write_event(State, Ev),
++ ok;
++write_events(_State, []) ->
+ ok.
+
+-write_event({Time, {error, _GL, {Pid, Format, Args}}},IOMod) ->
+- T = write_time(maybe_utc(Time)),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- format(IOMod, T ++ S);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- format(IOMod, T ++ F, [Format,Args])
++do_write_event(State, {Time0, Event}) ->
++ case parse_event(Event) of
++ ignore ->
++ ok;
++ {Head,Pid,FormatList} ->
++ Time = maybe_utc(Time0),
++ Header = write_time(Time, Head),
++ Body = format_body(State, FormatList),
++ AtNode = if
++ node(Pid) =/= node() ->
++ ["** at node ",atom_to_list(node(Pid))," **\n"];
++ true ->
++ []
++ end,
++ Str = [Header,Body,AtNode],
++ case State#st.io_mod of
++ io_lib ->
++ Str;
++ io ->
++ io:put_chars(user, Str)
++ end
+ end;
+-write_event({Time, {emulator, _GL, Chars}},IOMod) ->
+- T = write_time(maybe_utc(Time)),
+- case catch io_lib:format(Chars, []) of
+- S when is_list(S) ->
+- format(IOMod, T ++ S);
+- _ ->
+- format(IOMod, T ++ "ERROR: ~p ~n", [Chars])
+- end;
+-write_event({Time, {info, _GL, {Pid, Info, _}}},IOMod) ->
+- T = write_time(maybe_utc(Time)),
+- format(IOMod, T ++ add_node("~p~n",Pid),[Info]);
+-write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}},IOMod) ->
+- T = write_time(maybe_utc(Time)),
+- S = format_report(Rep),
+- format(IOMod, T ++ S ++ add_node("", Pid));
+-write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}},IOMod) ->
+- T = write_time(maybe_utc(Time), "INFO REPORT"),
+- S = format_report(Rep),
+- format(IOMod, T ++ S ++ add_node("", Pid));
+-write_event({Time, {info_msg, _GL, {Pid, Format, Args}}},IOMod) ->
+- T = write_time(maybe_utc(Time), "INFO REPORT"),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- format(IOMod, T ++ S);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- format(IOMod, T ++ F, [Format,Args])
+- end;
+-write_event({Time, {warning_report, _GL, {Pid, std_warning, Rep}}},IOMod) ->
+- T = write_time(maybe_utc(Time), "WARNING REPORT"),
+- S = format_report(Rep),
+- format(IOMod, T ++ S ++ add_node("", Pid));
+-write_event({Time, {warning_msg, _GL, {Pid, Format, Args}}},IOMod) ->
+- T = write_time(maybe_utc(Time), "WARNING REPORT"),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- format(IOMod, T ++ S);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- format(IOMod, T ++ F, [Format,Args])
+- end;
+-write_event({_Time, _Error},_IOMod) ->
++do_write_event(_, _) ->
+ ok.
+
++format_body(State, [{Format,Args}|T]) ->
++ S = try format(State, Format, Args) of
++ S0 ->
++ S0
++ catch
++ _:_ ->
++ format(State, "ERROR: ~p - ~p\n", [Format,Args])
++ end,
++ [S|format_body(State, T)];
++format_body(_State, []) ->
++ [].
++
++format(#st{depth=unlimited}, Format, Args) ->
++ io_lib:format(Format, Args);
++format(#st{depth=Depth}, Format0, Args) ->
++ Format1 = io_lib:scan_format(Format0, Args),
++ Format = limit_format(Format1, Depth),
++ io_lib:build_text(Format).
++
++limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p;
++ C0 =:= $w ->
++ C = C0 - ($a - $A), %To uppercase.
++ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}|limit_format(T, Depth)];
++limit_format([H|T], Depth) ->
++ [H|limit_format(T, Depth)];
++limit_format([], _) ->
++ [].
++
++parse_event({error, _GL, {Pid, Format, Args}}) ->
++ {"ERROR REPORT",Pid,[{Format,Args}]};
++parse_event({info_msg, _GL, {Pid, Format, Args}}) ->
++ {"INFO REPORT",Pid,[{Format, Args}]};
++parse_event({warning_msg, _GL, {Pid, Format, Args}}) ->
++ {"WARNING REPORT",Pid,[{Format,Args}]};
++parse_event({error_report, _GL, {Pid, std_error, Args}}) ->
++ {"ERROR REPORT",Pid,format_term(Args)};
++parse_event({info_report, _GL, {Pid, std_info, Args}}) ->
++ {"INFO REPORT",Pid,format_term(Args)};
++parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
++ {"WARNING REPORT",Pid,format_term(Args)};
++parse_event(_) -> ignore.
++
+ maybe_utc(Time) ->
+ UTC = case application:get_env(sasl, utc_log) of
+ {ok, Val} -> Val;
+@@ -175,33 +209,26 @@ maybe_utc(Time) ->
+ maybe_utc(Time, true) -> {utc, Time};
+ maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}.
+
+-format(IOMod, String) -> format(IOMod, String, []).
+-format(io_lib, String, Args) -> io_lib:format(String, Args);
+-format(io, String, Args) -> io:format(user, String, Args).
+-
+-format_report(Rep) when is_list(Rep) ->
+- case string_p(Rep) of
++format_term(Term) when is_list(Term) ->
++ case string_p(Term) of
+ true ->
+- io_lib:format("~s~n",[Rep]);
+- _ ->
+- format_rep(Rep)
++ [{"~s\n",[Term]}];
++ false ->
++ format_term_list(Term)
+ end;
+-format_report(Rep) ->
+- io_lib:format("~p~n",[Rep]).
+-
+-format_rep([{Tag,Data}|Rep]) ->
+- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
+-format_rep([Other|Rep]) ->
+- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep);
+-format_rep(_) ->
+- [].
++format_term(Term) ->
++ [{"~p\n",[Term]}].
+
+-add_node(X, Pid) when is_atom(X) ->
+- add_node(atom_to_list(X), Pid);
+-add_node(X, Pid) when node(Pid) =/= node() ->
+- lists:concat([X,"** at node ",node(Pid)," **~n"]);
+-add_node(X, _) ->
+- X.
++format_term_list([{Tag,Data}|T]) ->
++ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
++format_term_list([Data|T]) ->
++ [{" ~p\n",[Data]}|format_term_list(T)];
++format_term_list([]) ->
++ [];
++format_term_list(_) ->
++ %% Continue to allow non-proper lists for now.
++ %% FIXME: Remove this clause in OTP 19.
++ [].
+
+ string_p([]) ->
+ false;
+@@ -225,7 +252,6 @@ string_p1([H|T]) when is_list(H) ->
+ string_p1([]) -> true;
+ string_p1(_) -> false.
+
+-write_time(Time) -> write_time(Time, "ERROR REPORT").
+ write_time({utc,{{Y,Mo,D},{H,Mi,S}}},Type) ->
+ io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
+ [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
+diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
+index 9e69601..3c173dc 100644
+--- a/lib/stdlib/src/io_lib.erl
++++ b/lib/stdlib/src/io_lib.erl
+@@ -2,7 +2,7 @@
+ %%
+ %% %CopyrightBegin%
+ %%
+-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
++%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+ %%
+ %% The contents of this file are subject to the Erlang Public License,
+ %% Version 1.1, (the "License"); you may not use this file except in
+@@ -61,6 +61,7 @@
+ -module(io_lib).
+
+ -export([fwrite/2,fread/2,fread/3,format/2]).
++-export([scan_format/2,unscan_format/1,build_text/1]).
+ -export([print/1,print/4,indentation/2]).
+
+ -export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]).
+@@ -84,7 +85,7 @@
+ deep_unicode_char_list/1]).
+
+ -export_type([chars/0, latin1_string/0, continuation/0,
+- fread_error/0, fread_item/0]).
++ fread_error/0, fread_item/0, format_spec/0]).
+
+ %%----------------------------------------------------------------------
+
+@@ -109,6 +110,18 @@
+
+ -type fread_item() :: string() | atom() | integer() | float().
+
++-type format_spec() ::
++ {
++ ControlChar :: char(),
++ Args :: [any()],
++ Width :: 'none' | integer(),
++ Adjust :: 'left' | 'right',
++ Precision :: 'none' | integer(),
++ PadChar :: char(),
++ Encoding :: 'unicode' | 'latin1',
++ Strings :: boolean()
++ }.
++
+ %%----------------------------------------------------------------------
+
+ %% Interface calls to sub-modules.
+@@ -157,6 +170,31 @@ format(Format, Args) ->
+ Other
+ end.
+
++-spec scan_format(Format, Data) -> FormatList when
++ Format :: io:format(),
++ Data :: [term()],
++ FormatList :: [char() | format_spec()].
++
++scan_format(Format, Args) ->
++ try io_lib_format:scan(Format, Args)
++ catch
++ _:_ -> erlang:error(badarg, [Format, Args])
++ end.
++
++-spec unscan_format(FormatList) -> {Format, Data} when
++ FormatList :: [char() | format_spec()],
++ Format :: io:format(),
++ Data :: [term()].
++
++unscan_format(FormatList) ->
++ io_lib_format:unscan(FormatList).
++
++-spec build_text(FormatList) -> chars() when
++ FormatList :: [char() | format_spec()].
++
++build_text(FormatList) ->
++ io_lib_format:build(FormatList).
++
+ -spec print(Term) -> chars() when
+ Term :: term().
+
+diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
+index 56e15a1..37b47d7 100644
+--- a/lib/stdlib/src/io_lib_format.erl
++++ b/lib/stdlib/src/io_lib_format.erl
+@@ -1,7 +1,7 @@
+ %%
+ %% %CopyrightBegin%
+ %%
+-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
++%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+ %%
+ %% The contents of this file are subject to the Erlang Public License,
+ %% Version 1.1, (the "License"); you may not use this file except in
+@@ -20,10 +20,9 @@
+
+ %% Formatting functions of io library.
+
+--export([fwrite/2,fwrite_g/1,indentation/2]).
++-export([fwrite/2,fwrite_g/1,indentation/2,scan/2,unscan/1,build/1]).
+
+-%% fwrite(Format, ArgList) -> string().
+-%% Format the arguments in ArgList after string Format. Just generate
++%% Format the arguments in Args after string Format. Just generate
+ %% an error if there is an error in the arguments.
+ %%
+ %% To do the printing command correctly we need to calculate the
+@@ -37,15 +36,83 @@
+ %% and it also splits the handling of the control characters into two
+ %% parts.
+
+-fwrite(Format, Args) when is_atom(Format) ->
+- fwrite(atom_to_list(Format), Args);
+-fwrite(Format, Args) when is_binary(Format) ->
+- fwrite(binary_to_list(Format), Args);
++-spec fwrite(Format, Data) -> FormatList when
++ Format :: io:format(),
++ Data :: [term()],
++ FormatList :: [char() | io_lib:format_spec()].
++
+ fwrite(Format, Args) ->
+- Cs = collect(Format, Args),
++ build(scan(Format, Args)).
++
++%% Build the output text for a pre-parsed format list.
++
++-spec build(FormatList) -> io_lib:chars() when
++ FormatList :: [char() | io_lib:format_spec()].
++
++build(Cs) ->
+ Pc = pcount(Cs),
+ build(Cs, Pc, 0).
+
++%% Parse all control sequences in the format string.
++
++-spec scan(Format, Data) -> FormatList when
++ Format :: io:format(),
++ Data :: [term()],
++ FormatList :: [char() | io_lib:format_spec()].
++
++scan(Format, Args) when is_atom(Format) ->
++ scan(atom_to_list(Format), Args);
++scan(Format, Args) when is_binary(Format) ->
++ scan(binary_to_list(Format), Args);
++scan(Format, Args) ->
++ collect(Format, Args).
++
++%% Revert a pre-parsed format list to a plain character list and a
++%% list of arguments.
++
++-spec unscan(FormatList) -> {Format, Data} when
++ FormatList :: [char() | io_lib:format_spec()],
++ Format :: io:format(),
++ Data :: [term()].
++
++unscan(Cs) ->
++ {print(Cs), args(Cs)}.
++
++args([{_C,As,_F,_Ad,_P,_Pad,_Enc,_Str} | Cs]) ->
++ As ++ args(Cs);
++args([_C | Cs]) ->
++ args(Cs);
++args([]) ->
++ [].
++
++print([{C,_As,F,Ad,P,Pad,Enc,Str} | Cs ]) ->
++ print(C, F, Ad, P, Pad, Enc, Str) ++ print(Cs);
++print([C | Cs]) ->
++ [C | print(Cs)];
++print([]) ->
++ [].
++
++print(C, F, Ad, P, Pad, Encoding, Strings) ->
++ [$~] ++ print_field_width(F, Ad) ++ print_precision(P) ++
++ print_pad_char(Pad) ++ print_encoding(Encoding) ++
++ print_strings(Strings) ++ [C].
++
++print_field_width(none, _Ad) -> "";
++print_field_width(F, left) -> integer_to_list(-F);
++print_field_width(F, right) -> integer_to_list(F).
++
++print_precision(none) -> "";
++print_precision(P) -> [$. | integer_to_list(P)].
++
++print_pad_char($\s) -> ""; % default, no need to make explicit
++print_pad_char(Pad) -> [$., Pad].
++
++print_encoding(unicode) -> "t";
++print_encoding(latin1) -> "".
++
++print_strings(false) -> "l";
++print_strings(true) -> "".
++
+ collect([$~|Fmt0], Args0) ->
+ {C,Fmt1,Args1} = collect_cseq(Fmt0, Args0),
+ [C|collect(Fmt1, Args1)];
+@@ -141,7 +208,7 @@ pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1);
+ pcount([_|Cs], Acc) -> pcount(Cs, Acc);
+ pcount([], Acc) -> Acc.
+
+-%% build([Control], Pc, Indentation) -> string().
++%% build([Control], Pc, Indentation) -> io_lib:chars().
+ %% Interpret the control structures. Count the number of print
+ %% remaining and only calculate indentation when necessary. Must also
+ %% be smart when calculating indentation for characters in format.
+@@ -162,10 +229,14 @@ decr_pc($p, Pc) -> Pc - 1;
+ decr_pc($P, Pc) -> Pc - 1;
+ decr_pc(_, Pc) -> Pc.
+
+-%% indentation(String, Indentation) -> Indentation.
++
+ %% Calculate the indentation of the end of a string given its start
+ %% indentation. We assume tabs at 8 cols.
+
++-spec indentation(String, StartIndent) -> integer() when
++ String :: io_lib:chars(),
++ StartIndent :: integer().
++
+ indentation([$\n|Cs], _I) -> indentation(Cs, 0);
+ indentation([$\t|Cs], I) -> indentation(Cs, ((I + 8) div 8) * 8);
+ indentation([C|Cs], I) when is_integer(C) ->
+@@ -366,7 +437,6 @@ float_data([D|Cs], Ds) when D >= $0, D =< $9 ->
+ float_data([_|Cs], Ds) ->
+ float_data(Cs, Ds).
+
+-%% fwrite_g(Float)
+ %% Writes the shortest, correctly rounded string that converts
+ %% to Float when read back with list_to_float/1.
+ %%
+@@ -374,6 +444,8 @@ float_data([_|Cs], Ds) ->
+ %% in Proceedings of the SIGPLAN '96 Conference on Programming
+ %% Language Design and Implementation.
+
++-spec fwrite_g(float()) -> string().
++
+ fwrite_g(0.0) ->
+ "0.0";
+ fwrite_g(Float) when is_float(Float) ->
+@@ -642,7 +714,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase)
+ term([Prefix|S], F, Adj, none, Pad)
+ end.
+
+-%% char(Char, Field, Adjust, Precision, PadChar) -> string().
++%% char(Char, Field, Adjust, Precision, PadChar) -> chars().
+
+ char(C, none, _Adj, none, _Pad) -> [C];
+ char(C, F, _Adj, none, _Pad) -> chars(C, F);
diff --git a/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch b/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch
new file mode 100644
index 0000000..e9e6450
--- /dev/null
+++ b/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch
@@ -0,0 +1,74 @@
+From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
+Date: Tue, 25 Aug 2015 15:20:23 +0200
+Subject: [PATCH] Teach sasl_report to limit crash reports
+
+
+diff --git a/lib/sasl/src/sasl_report.erl b/lib/sasl/src/sasl_report.erl
+index c3e6fed..aa84e4f 100644
+--- a/lib/sasl/src/sasl_report.erl
++++ b/lib/sasl/src/sasl_report.erl
+@@ -61,27 +61,53 @@ write_report2(IO, Fd, Head, supervisor_report, Report) ->
+ Context = sup_get(errorContext, Report),
+ Reason = sup_get(reason, Report),
+ Offender = sup_get(offender, Report),
+- FmtString = " Supervisor: ~p~n Context: ~p~n Reason: "
+- "~80.18p~n Offender: ~80.18p~n~n",
+- write_report_action(IO, Fd, Head ++ FmtString,
+- [Name,Context,Reason,Offender]);
++ {FmtString,Args} = supervisor_format([Name,Context,Reason,Offender]),
++ write_report_action(IO, Fd, Head, FmtString, Args);
+ write_report2(IO, Fd, Head, progress, Report) ->
+ Format = format_key_val(Report),
+- write_report_action(IO, Fd, Head ++ "~s", [Format]);
++ write_report_action(IO, Fd, Head, "~s", [Format]);
+ write_report2(IO, Fd, Head, crash_report, Report) ->
+- Format = proc_lib:format(Report),
+- write_report_action(IO, Fd, Head ++ "~s", [Format]).
++ Depth = get_depth(),
++ Format = proc_lib:format(Report, latin1, Depth),
++ write_report_action(IO, Fd, Head, "~s", [Format]).
++
++supervisor_format(Args0) ->
++ case get_depth() of
++ unlimited ->
++ {" Supervisor: ~p~n"
++ " Context: ~p~n"
++ " Reason: ~80.18p~n"
++ " Offender: ~80.18p~n~n",
++ Args0};
++ Depth ->
++ [A,B,C,D] = Args0,
++ Args = [A,Depth,B,Depth,C,Depth,D,Depth],
++ {" Supervisor: ~P~n"
++ " Context: ~P~n"
++ " Reason: ~80.18P~n"
++ " Offender: ~80.18P~n~n",
++ Args}
++ end.
+
+-write_report_action(io, Fd, Format, Args) ->
+- io:format(Fd, Format, Args);
+-write_report_action(io_lib, _Fd, Format, Args) ->
+- io_lib:format(Format, Args).
++write_report_action(IO, Fd, Head, Format, Args) ->
++ S = [Head|io_lib:format(Format, Args)],
++ case IO of
++ io -> io:put_chars(Fd, S);
++ io_lib -> S
++ end.
+
+ format_key_val([{Tag,Data}|Rep]) ->
+ io_lib:format(" ~16w: ~p~n",[Tag,Data]) ++ format_key_val(Rep);
+ format_key_val(_) ->
+ [].
+
++get_depth() ->
++ case application:get_env(kernel, error_logger_format_depth) of
++ {ok, Depth} when is_integer(Depth) ->
++ max(10, Depth);
++ undefined ->
++ unlimited
++ end.
+
+ sup_get(Tag, Report) ->
+ case lists:keysearch(Tag, 1, Report) of
From db107ef77c6f466dd4154135b93a8c730733d941 Mon Sep 17 00:00:00 2001
From: Peter Lemenkov
Date: Fri, 19 Feb 2016 18:42:20 +0300
Subject: [PATCH 09/13] Add missing dependency
Signed-off-by: Peter Lemenkov
---
erlang.spec | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/erlang.spec b/erlang.spec
index d0d47ec..872dc21 100644
--- a/erlang.spec
+++ b/erlang.spec
@@ -25,7 +25,7 @@
Name: erlang
Version: %{upstream_ver}
-Release: %{upstream_rel_for_rpm}.14%{?dist}
+Release: %{upstream_rel_for_rpm}.15%{?dist}
Summary: General-purpose programming language and runtime environment
Group: Development/Languages
@@ -483,6 +483,8 @@ Low level interface to C.
%package erts
Summary: Functionality necessary to run the Erlang System itself
Group: Development/Languages
+# epmd user, epmd group
+Requires(pre): shadow-utils
Requires: %{name}-kernel%{?_isa} = %{version}-%{release}
Requires: %{name}-stdlib%{?_isa} = %{version}-%{release}
# This library is dlopened so it can't be picked up automatically by the RPM
@@ -2352,6 +2354,9 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \
%changelog
+* Fri Feb 19 2016 Peter Lemenkov - R16B-03.15
+- Add missing dependency
+
* Wed Jan 27 2016 Peter Lemenkov - R16B-03.14
- Enable error_logger depth fine tuning
From 9f08c84c22b5fe96acf2d4163157e93b955ce401 Mon Sep 17 00:00:00 2001
From: Peter Lemenkov
Date: Sun, 28 Feb 2016 00:05:49 +0300
Subject: [PATCH 10/13] Fixed issue with nodes registration over IPv6
Signed-off-by: Peter Lemenkov
---
erlang.spec | 5 +-
...-epmd-support-IPv6-node-registration.patch | 205 +++++++++++-------
2 files changed, 127 insertions(+), 83 deletions(-)
diff --git a/erlang.spec b/erlang.spec
index 872dc21..af332c7 100644
--- a/erlang.spec
+++ b/erlang.spec
@@ -25,7 +25,7 @@
Name: erlang
Version: %{upstream_ver}
-Release: %{upstream_rel_for_rpm}.15%{?dist}
+Release: %{upstream_rel_for_rpm}.16%{?dist}
Summary: General-purpose programming language and runtime environment
Group: Development/Languages
@@ -2354,6 +2354,9 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \
%changelog
+* Sun Feb 28 2016 Peter Lemenkov - R16B-03.16
+- Fixed issue with nodes registration over IPv6
+
* Fri Feb 19 2016 Peter Lemenkov - R16B-03.15
- Add missing dependency
diff --git a/otp-0023-epmd-support-IPv6-node-registration.patch b/otp-0023-epmd-support-IPv6-node-registration.patch
index da81020..7d46bae 100644
--- a/otp-0023-epmd-support-IPv6-node-registration.patch
+++ b/otp-0023-epmd-support-IPv6-node-registration.patch
@@ -17,30 +17,22 @@ epmd ignores errors opening the socket if the protocol is not
supported. Similarly, the epmd client will fall back to IPv4 if the IPv6
socket is not available.
-The interaction between IPv4 and IPv6 sockets depends on the platform:
-
-* FreeBSD allows multiple "specific" sockets to bind the same port (such
- as 2 sockets listening to the same port on ANY and the loopback).
- Binding port 4369 to IPv4 and IPv6 sockets simulataneously is allowed.
-
-* Linux does not allow the same port to be bound by different sockets.
- Setting the IPV6_V6ONLY socket option is required.
-
-* Windows
-
- The behaviour differs depending on the version of Windows:
-
- http://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx
-
- According to the site, sockets on Windows XP with Service Pack 1 (SP1)
- and Windows Server 2003 will only listen on either IPv4 or IPv6, so
- creating two sockets is required to service IPv4 and IPv6 traffic on
- the same port. The IPV6_V6ONLY socket option is not supported.
-
- For Windows Vista and later, a single socket can handle IPv4 and IPv6
- traffic for the same port. The IPV6_V6ONLY socket option is supported
- and is enabled for IPv6 sockets by default.
+Update the minimum supported version of Windows to Windows Vista to
+support IPv6.
+diff --git a/erts/configure.in b/erts/configure.in
+index d9bc1ec..756f3cb 100644
+--- a/erts/configure.in
++++ b/erts/configure.in
+@@ -424,7 +424,7 @@ case $host_os in
+ win32)
+ # The ethread library requires _WIN32_WINNT of at least 0x0403.
+ # -D_WIN32_WINNT=* from CPPFLAGS is saved in ETHR_DEFS.
+- CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0501 -DWINVER=0x0501"
++ CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600 -DWINVER=0x0600"
+ ;;
+ darwin*)
+ CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE"
diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml
index 3e70054..3c9313e 100644
--- a/erts/doc/src/epmd.xml
@@ -55,13 +47,18 @@ index 3e70054..3c9313e 100644
Starts the port mapper daemon
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
-index 528a2d9..dada3a1 100644
+index 528a2d9..0b8f821 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
-@@ -381,6 +381,28 @@
+@@ -381,6 +381,33 @@
similar to . See
code(3).
++
++
++
Replaces the path specified in the boot script. See
++ script(4).
++
+
+
+
Specify a protocol for Erlang distribution.
@@ -70,9 +67,9 @@ index 528a2d9..dada3a1 100644
+
+
TCP over IPv4 (the default)
+
-+ inet_ssl
++ inet_tls
+
-+
distribution over SSL
++
distribution over TLS/SSL
+
+ inet6_tcp
+
@@ -101,7 +98,7 @@ index 1757fa9..ebae0a5 100644
/* These chouldn't be needed but for safety... */
diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c
-index 8817bde..c0b3729 100644
+index 8817bde..ea7dac7 100644
--- a/erts/epmd/src/epmd_cli.c
+++ b/erts/epmd/src/epmd_cli.c
@@ -135,19 +135,33 @@ void epmd_call(EpmdVars *g,int what)
@@ -116,7 +113,7 @@ index 8817bde..c0b3729 100644
- goto error;
+ unsigned short sport = g->port;
+
-+#if defined(HAVE_IN6) && defined(AF_INET6)
++#if defined(EPMD6)
+ SET_ADDR6(address, in6addr_loopback, sport);
+ salen = sizeof(struct sockaddr_in6);
+
@@ -147,17 +144,10 @@ index 8817bde..c0b3729 100644
error:
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
-index 363923e..e028449 100644
+index 363923e..c6d9173 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
-@@ -44,9 +44,14 @@
- #include
-
- #ifdef __WIN32__
-+# ifdef _WIN32_WINVER
-+# undef _WIN32_WINVER
-+# endif
-+# define _WIN32_WINVER 0x0501
+@@ -47,6 +47,7 @@
# ifndef WINDOWS_H_INCLUDES_WINSOCK2_H
# include
# endif
@@ -165,6 +155,17 @@ index 363923e..e028449 100644
# include
# include
#endif
+@@ -114,6 +115,10 @@
+ # include
+ #endif
+
++#if defined(HAVE_IN6) && defined(AF_INET6) && defined(HAVE_INET_PTON)
++# define EPMD6
++#endif
++
+ /* ************************************************************************ */
+ /* Replace some functions by others by making the function name a macro */
+
@@ -167,33 +172,53 @@
/* ************************************************************************ */
/* Macros that let us use IPv6 */
@@ -193,7 +194,7 @@ index 363923e..e028449 100644
+
+#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK))
+
-+#if defined(HAVE_IN6) && defined(AF_INET6)
++#if defined(EPMD6)
-#define EPMD_SOCKADDR_IN sockaddr_in6
-#define EPMD_IN_ADDR in6_addr
@@ -247,7 +248,7 @@ index 363923e..e028449 100644
/* ************************************************************************ */
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
-index 78524a6..7b4d774 100644
+index 78524a6..28e30d4 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -70,6 +70,7 @@ static time_t current_time(EpmdVars*);
@@ -284,7 +285,7 @@ index 78524a6..7b4d774 100644
+ /* Always listen on the loopback. */
+ SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_LOOPBACK),sport);
+ num_sockets++;
-+#if defined(HAVE_IN6) && defined(AF_INET6)
++#if defined(EPMD6)
+ SET_ADDR6(iserv_addr[num_sockets],in6addr_loopback,sport);
+ num_sockets++;
+#endif
@@ -304,9 +305,10 @@ index 78524a6..7b4d774 100644
+ token = strtok(NULL,", "))
{
- struct EPMD_IN_ADDR addr;
-+ struct in_addr addr;
- #ifdef HAVE_INET_PTON
+-#ifdef HAVE_INET_PTON
- int ret;
++ struct in_addr addr;
++#if defined(EPMD6)
+ struct in6_addr addr6;
+ struct sockaddr_storage *sa = &iserv_addr[num_sockets];
@@ -337,7 +339,7 @@ index 78524a6..7b4d774 100644
epmd_cleanup_exit(g,1);
}
-+#if defined(HAVE_IN6) && defined(AF_INET6)
++#if defined(EPMD6)
+ if (sa->ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&addr6))
+ continue;
+
@@ -346,10 +348,10 @@ index 78524a6..7b4d774 100644
if (IS_ADDR_LOOPBACK(addr))
- loopback_ok = 1;
+ continue;
-+
-+ num_sockets++;
- if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1)
++ num_sockets++;
++
+ if (num_sockets >= MAX_LISTEN_SOCKETS)
{
dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses",
@@ -374,7 +376,7 @@ index 78524a6..7b4d774 100644
- num_sockets = 1;
+ SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_ANY),sport);
+ num_sockets++;
-+#if defined(HAVE_IN6) && defined(AF_INET6)
++#if defined(EPMD6)
+ SET_ADDR6(iserv_addr[num_sockets],in6addr_any,sport);
+ num_sockets++;
+#endif
@@ -387,7 +389,7 @@ index 78524a6..7b4d774 100644
{
- if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0)
+ struct sockaddr *sa = (struct sockaddr *)&iserv_addr[i];
-+#if defined(HAVE_IN6) && defined(AF_INET6)
++#if defined(EPMD6)
+ size_t salen = (sa->sa_family == AF_INET6 ?
+ sizeof(struct sockaddr_in6) :
+ sizeof(struct sockaddr_in));
@@ -398,7 +400,6 @@ index 78524a6..7b4d774 100644
+ if ((listensock[i] = socket(sa->sa_family,SOCK_STREAM,0)) < 0)
{
- dbg_perror(g,"error opening stream socket");
-- epmd_cleanup_exit(g,1);
+ switch (errno) {
+ case EAFNOSUPPORT:
+ case EPROTONOSUPPORT:
@@ -407,9 +408,8 @@ index 78524a6..7b4d774 100644
+ dbg_perror(g,"error opening stream socket");
+ epmd_cleanup_exit(g,1);
+ }
- }
- g->listenfd[i] = listensock[i];
--
++ }
++ g->listenfd[bound++] = listensock[i];
+
+#if HAVE_DECL_IPV6_V6ONLY
+ opt = 1;
@@ -418,8 +418,10 @@ index 78524a6..7b4d774 100644
+ sizeof(opt)) <0)
+ {
+ dbg_perror(g,"can't set IPv6 only socket option");
-+ epmd_cleanup_exit(g,1);
-+ }
+ epmd_cleanup_exit(g,1);
+ }
+- g->listenfd[i] = listensock[i];
+-
+#endif
+
/*
@@ -435,28 +437,30 @@ index 78524a6..7b4d774 100644
{
if (errno == EADDRINUSE)
{
-@@ -388,12 +433,20 @@ void run(EpmdVars *g)
- }
- }
-
-+ bound++;
-+
- if(listen(listensock[i], SOMAXCONN) < 0) {
- dbg_perror(g,"failed to listen on socket");
- epmd_cleanup_exit(g,1);
+@@ -394,6 +439,11 @@ void run(EpmdVars *g)
}
select_fd_set(g, listensock[i]);
}
-+
+ if (bound == 0) {
+ dbg_perror(g,"unable to bind any address");
+ epmd_cleanup_exit(g,1);
+ }
-+
++ num_sockets = bound;
#ifdef HAVE_SYSTEMD_SD_DAEMON_H
}
sd_notifyf(0, "READY=1\n"
-@@ -1001,15 +1054,6 @@ static int conn_open(EpmdVars *g,int fd)
+@@ -438,8 +488,8 @@ void run(EpmdVars *g)
+ }
+
+ for (i = 0; i < num_sockets; i++)
+- if (FD_ISSET(listensock[i],&read_mask)) {
+- if (do_accept(g, listensock[i]) && g->active_conn < g->max_conn) {
++ if (FD_ISSET(g->listenfd[i],&read_mask)) {
++ if (do_accept(g, g->listenfd[i]) && g->active_conn < g->max_conn) {
+ /*
+ * The accept() succeeded, and we have at least one file
+ * descriptor still free, which means that another accept()
+@@ -1001,15 +1051,6 @@ static int conn_open(EpmdVars *g,int fd)
for (i = 0; i < g->max_conn; i++) {
if (g->conn[i].open == EPMD_FALSE) {
@@ -472,7 +476,7 @@ index 78524a6..7b4d774 100644
g->active_conn++;
s = &g->conn[i];
-@@ -1020,20 +1064,7 @@ static int conn_open(EpmdVars *g,int fd)
+@@ -1020,20 +1061,7 @@ static int conn_open(EpmdVars *g,int fd)
s->open = EPMD_TRUE;
s->keep = EPMD_FALSE;
@@ -494,7 +498,7 @@ index 78524a6..7b4d774 100644
dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" :
"Non-local peer connected");
-@@ -1041,7 +1072,7 @@ static int conn_open(EpmdVars *g,int fd)
+@@ -1041,7 +1069,7 @@ static int conn_open(EpmdVars *g,int fd)
s->got = 0;
s->mod_time = current_time(g); /* Note activity */
@@ -503,7 +507,7 @@ index 78524a6..7b4d774 100644
if (s->buf == NULL) {
dbg_printf(g,0,"epmd: Insufficient memory");
-@@ -1059,6 +1090,60 @@ static int conn_open(EpmdVars *g,int fd)
+@@ -1059,6 +1087,60 @@ static int conn_open(EpmdVars *g,int fd)
return EPMD_FALSE;
}
@@ -515,7 +519,7 @@ index 78524a6..7b4d774 100644
+ struct sockaddr_in *si4 = (struct sockaddr_in *)&si;
+ struct sockaddr_in *di4 = (struct sockaddr_in *)&di;
+
-+#if defined(HAVE_IN6) && defined(AF_INET6)
++#if defined(EPMD6)
+ struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&si;
+ struct sockaddr_in6 *di6 = (struct sockaddr_in6 *)&di;
+#endif
@@ -537,7 +541,7 @@ index 78524a6..7b4d774 100644
+
+ /* Only 127.x.x.x and connections from the host's IP address
+ allowed, no false positives */
-+#if defined(HAVE_IN6) && defined(AF_INET6)
++#if defined(EPMD6)
+ if (si.ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&(si6->sin6_addr)))
+ return EPMD_TRUE;
+
@@ -550,13 +554,13 @@ index 78524a6..7b4d774 100644
+ if (getsockname(fd,(struct sockaddr*) &di,&st))
+ return EPMD_FALSE;
+
-+#if defined(HAVE_IN6) && defined(AF_INET6)
++#if defined(EPMD6)
+ if (si.ss_family == AF_INET6)
+ return IN6_ARE_ADDR_EQUAL( &(si6->sin6_addr), &(di6->sin6_addr));
+ if (si.ss_family == AF_INET)
+#endif
+ return si4->sin_addr.s_addr == di4->sin_addr.s_addr;
-+#if defined(HAVE_IN6) && defined(AF_INET6)
++#if defined(EPMD6)
+ return EPMD_FALSE;
+#endif
+}
@@ -565,7 +569,7 @@ index 78524a6..7b4d774 100644
{
int i;
diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl
-index cc24a55..ddfe5ab 100644
+index cc24a55..8dfc21f 100644
--- a/erts/epmd/test/epmd_SUITE.erl
+++ b/erts/epmd/test/epmd_SUITE.erl
@@ -42,6 +42,7 @@
@@ -611,7 +615,7 @@ index cc24a55..ddfe5ab 100644
register_names_1(doc) ->
["Register and unregister two nodes"];
register_names_1(suite) ->
-@@ -238,13 +258,18 @@ register_node(Name) ->
+@@ -238,13 +258,14 @@ register_node(Name) ->
register_node(Name,Port) ->
register_node_v2(Port,$M,0,5,5,Name,"").
@@ -619,19 +623,20 @@ index cc24a55..ddfe5ab 100644
+ register_node_v2({0,0,0,0,0,0,0,1},?DUMMY_PORT,$M,0,5,5,Name,"").
+
register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) ->
+- Utf8Name = unicode:characters_to_binary(Name),
+- Req = [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot,
+- put16(HVsn), put16(LVsn),
+- put16(size(Utf8Name)), binary_to_list(Utf8Name),
+- size16(Extra), Extra],
+- case send_req(Req) of
+ register_node_v2("localhost", Port, NodeType, Prot, HVsn, LVsn, Name, Extra).
+register_node_v2(Addr, Port, NodeType, Prot, HVsn, LVsn, Name, Extra) ->
- Utf8Name = unicode:characters_to_binary(Name),
- Req = [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot,
- put16(HVsn), put16(LVsn),
- put16(size(Utf8Name)), binary_to_list(Utf8Name),
- size16(Extra), Extra],
-- case send_req(Req) of
++ Req = alive2_req(Port, NodeType, Prot, HVsn, LVsn, Name, Extra),
+ case send_req(Req, Addr) of
{ok,Sock} ->
case recv(Sock,4) of
{ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} ->
-@@ -1129,7 +1154,9 @@ send_direct(Sock, Bytes) ->
+@@ -1129,7 +1150,9 @@ send_direct(Sock, Bytes) ->
end.
send_req(Req) ->
@@ -642,6 +647,19 @@ index cc24a55..ddfe5ab 100644
{ok,Sock} ->
case send(Sock, [size16(Req), Req]) of
ok ->
+diff --git a/lib/erl_interface/configure.in b/lib/erl_interface/configure.in
+index d511f2e..99ee635 100644
+--- a/lib/erl_interface/configure.in
++++ b/lib/erl_interface/configure.in
+@@ -250,7 +250,7 @@ case "$threads_disabled" in
+ ;;
+ win32_threads)
+ EI_THREADS="true"
+- THR_DEFS="$THR_DEFS -D_WIN32_WINNT=0x0500 -DWINVER=0x0500"
++ THR_DEFS="$THR_DEFS -D_WIN32_WINNT=0x0600 -DWINVER=0x0600"
+ ;;
+ pthread)
+ EI_THREADS="true"
diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
index 91af49f..21a3dec 100644
--- a/lib/kernel/src/erl_epmd.erl
@@ -694,3 +712,26 @@ index 91af49f..21a3dec 100644
{ok, Socket} ->
Name = to_string(NodeName),
Extra = "",
+diff --git a/lib/wx/configure.in b/lib/wx/configure.in
+index 3756786..be73888 100755
+--- a/lib/wx/configure.in
++++ b/lib/wx/configure.in
+@@ -163,14 +163,14 @@ case $host_os in
+ CPPFLAGS="$CPPFLAGS -D_MACOSX $PTHR_CFLAGS"
+ ;;
+ mingw32)
+- CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0500 -D_WINDOWS -D_UNICODE -DUNICODE"
+- CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0500"
++ CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0600 -D_WINDOWS -D_UNICODE -DUNICODE"
++ CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600"
+ AC_MSG_WARN([Reverting to 32-bit time_t])
+ CPPFLAGS="$CPPFLAGS -D_USE_32BIT_TIME_T"
+ ;;
+ win32)
+- CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0500 -D_WINDOWS -D_UNICODE -DUNICODE"
+- CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0500"
++ CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0600 -D_WINDOWS -D_UNICODE -DUNICODE"
++ CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600"
+ ;;
+ *)
+ CFLAGS="$CFLAGS -Wno-deprecated-declarations"
From 6515854c294bc6be60987407a54d9680fd8faf65 Mon Sep 17 00:00:00 2001
From: John Eckersberg
Date: Thu, 7 Apr 2016 11:17:41 -0400
Subject: [PATCH 11/13] Revert "Enable error_logger depth fine tuning"
(rhbz#1324922)
This reverts commit 77046292336ce368b7f3310fdced075118075f61.
---
erlang.spec | 13 +-
...unused-code-in-error-logger-handlers.patch | 1028 -----------------
...h-sasl_report-to-limit-crash-reports.patch | 74 --
3 files changed, 4 insertions(+), 1111 deletions(-)
delete mode 100644 otp-0024-Remove-unused-code-in-error-logger-handlers.patch
delete mode 100644 otp-0025-Teach-sasl_report-to-limit-crash-reports.patch
diff --git a/erlang.spec b/erlang.spec
index af332c7..83d9e27 100644
--- a/erlang.spec
+++ b/erlang.spec
@@ -25,7 +25,7 @@
Name: erlang
Version: %{upstream_ver}
-Release: %{upstream_rel_for_rpm}.16%{?dist}
+Release: %{upstream_rel_for_rpm}.17%{?dist}
Summary: General-purpose programming language and runtime environment
Group: Development/Languages
@@ -125,12 +125,6 @@ Patch22: otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch
# Fedora specific patch
# epmd: support IPv6 node registration
Patch23: otp-0023-epmd-support-IPv6-node-registration.patch
-# Fedora specific patch
-# Remove unused code in error logger handlers
-Patch24: otp-0024-Remove-unused-code-in-error-logger-handlers.patch
-# Fedora specific patch
-# Teach sasl_report to limit crash reports
-Patch25: otp-0025-Teach-sasl_report-to-limit-crash-reports.patch
# end of autogenerated patch tag list
BuildRequires: lksctp-tools-devel
@@ -1021,8 +1015,6 @@ Erlang mode for XEmacs (source lisp files).
%patch21 -p1 -b .Add_patch_to_crash_dump_on_large_distribution
%patch22 -p1 -b .kernel_inet6_tcp_dist_reuse_inet_tcp_dist_code
%patch23 -p1 -b .epmd_support_IPv6_node_registration
-%patch24 -p1 -b .Remove_unused_code_in_error_logger_handlers
-%patch25 -p1 -b .Teach_sasl_report_to_limit_crash_reports
# end of autogenerated prep patch list
# FIXME we should come up with a better solution
@@ -2354,6 +2346,9 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \
%changelog
+* Thu Apr 7 2016 John Eckersberg - R16B-03.17
+- Revert "Enable error_logger depth fine tuning" (rhbz#1324922)
+
* Sun Feb 28 2016 Peter Lemenkov - R16B-03.16
- Fixed issue with nodes registration over IPv6
diff --git a/otp-0024-Remove-unused-code-in-error-logger-handlers.patch b/otp-0024-Remove-unused-code-in-error-logger-handlers.patch
deleted file mode 100644
index a3ea0ac..0000000
--- a/otp-0024-Remove-unused-code-in-error-logger-handlers.patch
+++ /dev/null
@@ -1,1028 +0,0 @@
-From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
-Date: Thu, 20 Aug 2015 09:44:43 +0200
-Subject: [PATCH] Remove unused code in error logger handlers
-
-A long time ago, errors from the emulator itself was sent as
-messages that would end up in the handle_info/2 function.
-Those clauses in handle_info/2 can be removed.
-
-The code for handling events tagged 'info' instead of 'info_msg'
-can also be taken out.
-
-error_logger_file_h: Refactor and modernize code
-
-Refactor, simplify, and modernize the code to facilitate future
-improvements in the following commits.
-
-Teach error_logger_file_h to truncate big messages
-
-Add the possibility to truncate big messages to avoid running out
-of memory.
-
-error_logger_tty_h: Refactor and modernize code
-
-Refactor, simplify, and modernize the code to facilitate future
-improvements in the following commits.
-
-Teach error_logger_tty_h to truncate big messages
-
-Add the possibility to truncate big messages to avoid running out
-of memory.
-
-stdlib: Fix leaking files after error_logger:logfile(close)
-
-Introduced when changing state from tuple to record.
-
-Make the scanned form of the io_lib format strings available for processing
-
-This adds three new functions to io_lib - scan_format/2, unscan_format/1,
-and build_text/1 - which expose the parsed form of the format control
-sequences to make it possible to easily modify or filter the input to
-io_lib:format/2. This can e.g. be used in order to replace unbounded-size
-control sequences like ~w or ~p with corresponding depth-limited ~W and ~P
-before doing the actual formatting.
-
-diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml
-index 90f24c4..6613dcd 100644
---- a/lib/stdlib/doc/src/io.xml
-+++ b/lib/stdlib/doc/src/io.xml
-@@ -505,7 +505,8 @@ ok
-
Writes the data with standard syntax in the same way as
- ~w, but breaks terms whose printed representation
- is longer than one line into many lines and indents each
-- line sensibly. It also tries to detect lists of
-+ line sensibly. Left justification is not supported.
-+ It also tries to detect lists of
- printable characters and to output these as strings. The
- Unicode translation modifier is used for determining
- what characters are printable. For example:
-diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml
-index 68352ff..0c1c84d 100644
---- a/lib/stdlib/doc/src/io_lib.xml
-+++ b/lib/stdlib/doc/src/io_lib.xml
-@@ -4,7 +4,7 @@
-
-
-
-- 19962013
-+ 19962014
- Ericsson AB. All Rights Reserved.
-
-
-@@ -59,6 +59,35 @@
-
-
-
-+
-+
-+
Description:
-+
-+
control_char is the type of control
-+ sequence: $P, $w, and so on;
-+
-+
args is a list of the arguments used by the
-+ control sequence, or an empty list if the control sequence
-+ does not take any arguments;
-+
-+
width is the field width;
-+
-+
adjust is the adjustment;
-+
-+
precision is the precision of the printed
-+ argument;
-+
-+
pad_char is the padding character;
-+
-+
encoding is set to true if the translation
-+ modifier t is present;
-+
-+
strings is set to false if the modifier
-+ l is present.
-+
-+
-+
-+
-
-
-
-@@ -260,6 +289,45 @@
-
-
-
-+
-+ Parse all control sequences in the format string
-+
-+
Returns a list corresponding to the given format string,
-+ where control sequences have been replaced with
-+ corresponding tuples. This list can be passed to io_lib:build_text/1 to have
-+ the same effect as io_lib:format(Format, Args), or to
-+ io_lib:unscan_format/1
-+ in order to get the corresponding pair of Format and
-+ Args (with every * and corresponding argument
-+ expanded to numeric values).
-+
A typical use of this function is to replace unbounded-size
-+ control sequences like ~w and ~p with the
-+ depth-limited variants ~W and ~P before
-+ formatting to text, e.g. in a logger.
-+
-+
-+
-+
-+ Revert a pre-parsed format list to a plain character list
-+ and a list of arguments
-+
-+
See io_lib:scan_format/2 for
-+ details.
-+
-+
-+
-+
-+ Build the output text for a pre-parsed format list
-+
-+
See io_lib:scan_format/2 for
-+ details.
-+
-+
-+
-
- Indentation after printing string
-
-diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl
-index e9364ed..ca44d01 100644
---- a/lib/stdlib/src/error_logger_file_h.erl
-+++ b/lib/stdlib/src/error_logger_file_h.erl
-@@ -23,24 +23,28 @@
-
- %%%
- %%% A handler that can be connected to the error_logger
--%%% event handler.
--%%% Writes all events formatted to file.
--%%% Handles events tagged error, emulator and info.
-+%%% event handler. Writes all events formatted to file.
- %%%
- %%% It can only be started from error_logger:swap_handler({logfile, File})
--%%% or error_logger:logfile(File)
-+%%% or error_logger:logfile(File).
- %%%
-
- -export([init/1,
- handle_event/2, handle_call/2, handle_info/2,
- terminate/2, code_change/3]).
-
-+-record(st,
-+ {fd,
-+ filename,
-+ prev_handler,
-+ depth=unlimited :: 'unlimited' | non_neg_integer()}).
-+
- %% This one is used when we takeover from the simple error_logger.
- init({File, {error_logger, Buf}}) ->
- case init(File, error_logger) of
-- {ok, {Fd, File, PrevHandler}} ->
-- write_events(Fd, Buf),
-- {ok, {Fd, File, PrevHandler}};
-+ {ok, State} ->
-+ write_events(State, Buf),
-+ {ok, State};
- Error ->
- Error
- end;
-@@ -52,49 +56,45 @@ init(File, PrevHandler) ->
- process_flag(trap_exit, true),
- case file:open(File, [write]) of
- {ok,Fd} ->
-- {ok, {Fd, File, PrevHandler}};
-+ Depth = get_depth(),
-+ State = #st{fd=Fd,filename=File,prev_handler=PrevHandler,
-+ depth=Depth},
-+ {ok, State};
- Error ->
- Error
- end.
--
-+
-+get_depth() ->
-+ case application:get_env(kernel, error_logger_format_depth) of
-+ {ok, Depth} when is_integer(Depth) ->
-+ max(10, Depth);
-+ undefined ->
-+ unlimited
-+ end.
-+
- handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
- {ok, State};
--handle_event(Event, {Fd, File, PrevHandler}) ->
-- write_event(Fd, tag_event(Event)),
-- {ok, {Fd, File, PrevHandler}};
--handle_event(_, State) ->
-+handle_event(Event, State) ->
-+ write_event(State, Event),
- {ok, State}.
-
--handle_info({'EXIT', Fd, _Reason}, {Fd, _File, PrevHandler}) ->
-+handle_info({'EXIT', Fd, _Reason}, #st{fd=Fd,prev_handler=PrevHandler}) ->
- case PrevHandler of
- [] ->
- remove_handler;
- _ ->
- {swap_handler, install_prev, [], PrevHandler, go_back}
- end;
--handle_info({emulator, GL, Chars}, {Fd, File, PrevHandler})
-- when node(GL) == node() ->
-- write_event(Fd, tag_event({emulator, GL, Chars})),
-- {ok, {Fd, File, PrevHandler}};
--handle_info({emulator, noproc, Chars}, {Fd, File, PrevHandler}) ->
-- write_event(Fd, tag_event({emulator, noproc, Chars})),
-- {ok, {Fd, File, PrevHandler}};
- handle_info(_, State) ->
- {ok, State}.
-
--handle_call(filename, {Fd, File, Prev}) ->
-- {ok, File, {Fd, File, Prev}};
-+handle_call(filename, #st{filename=File}=State) ->
-+ {ok, File, State};
- handle_call(_Query, State) ->
- {ok, {error, bad_query}, State}.
-
--terminate(_Reason, State) ->
-- case State of
-- {Fd, _File, _Prev} ->
-- ok = file:close(Fd);
-- _ ->
-- ok
-- end,
-- [].
-+terminate(_Reason, #st{fd=Fd}) ->
-+ file:close(Fd).
-
- code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-@@ -103,69 +103,71 @@ code_change(_OldVsn, State, _Extra) ->
- %%% Misc. functions.
- %%% ------------------------------------------------------
-
--tag_event(Event) ->
-- {erlang:universaltime(), Event}.
-+write_events(State, [Ev|Es]) ->
-+ %% Write the events in reversed order.
-+ write_events(State, Es),
-+ write_event(State, Ev);
-+write_events(_State, []) ->
-+ ok.
-
--write_events(Fd, Events) -> write_events1(Fd, lists:reverse(Events)).
-+write_event(#st{fd=Fd}=State, Event) ->
-+ case parse_event(Event) of
-+ ignore ->
-+ ok;
-+ {Head,Pid,FormatList} ->
-+ Time = maybe_utc(erlang:universaltime()),
-+ Header = write_time(Time, Head),
-+ Body = format_body(State, FormatList),
-+ AtNode = if
-+ node(Pid) =/= node() ->
-+ ["** at node ",atom_to_list(node(Pid))," **\n"];
-+ true ->
-+ []
-+ end,
-+ io:put_chars(Fd, [Header,Body,AtNode])
-+ end.
-
--write_events1(Fd, [Event|Es]) ->
-- write_event(Fd, Event),
-- write_events1(Fd, Es);
--write_events1(_, []) ->
-- ok.
-+format_body(State, [{Format,Args}|T]) ->
-+ S = try format(State, Format, Args) of
-+ S0 ->
-+ S0
-+ catch
-+ _:_ ->
-+ format(State, "ERROR: ~p - ~p\n", [Format,Args])
-+ end,
-+ [S|format_body(State, T)];
-+format_body(_State, []) ->
-+ [].
-
--write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) ->
-- T = write_time(maybe_utc(Time)),
-- case catch io_lib:format(add_node(Format,Pid), Args) of
-- S when is_list(S) ->
-- io:format(Fd, T ++ S, []);
-- _ ->
-- F = add_node("ERROR: ~p - ~p~n", Pid),
-- io:format(Fd, T ++ F, [Format,Args])
-- end;
--write_event(Fd, {Time, {emulator, _GL, Chars}}) ->
-- T = write_time(maybe_utc(Time)),
-- case catch io_lib:format(Chars, []) of
-- S when is_list(S) ->
-- io:format(Fd, T ++ S, []);
-- _ ->
-- io:format(Fd, T ++ "ERROR: ~p ~n", [Chars])
-- end;
--write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) ->
-- T = write_time(maybe_utc(Time)),
-- io:format(Fd, T ++ add_node("~p~n",Pid),[Info]);
--write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) ->
-- T = write_time(maybe_utc(Time)),
-- S = format_report(Rep),
-- io:format(Fd, T ++ S ++ add_node("", Pid), []);
--write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) ->
-- T = write_time(maybe_utc(Time), "INFO REPORT"),
-- S = format_report(Rep),
-- io:format(Fd, T ++ S ++ add_node("", Pid), []);
--write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) ->
-- T = write_time(maybe_utc(Time), "INFO REPORT"),
-- case catch io_lib:format(add_node(Format,Pid), Args) of
-- S when is_list(S) ->
-- io:format(Fd, T ++ S, []);
-- _ ->
-- F = add_node("ERROR: ~p - ~p~n", Pid),
-- io:format(Fd, T ++ F, [Format,Args])
-- end;
--write_event(Fd, {Time, {warning_report, _GL, {Pid, std_warning, Rep}}}) ->
-- T = write_time(maybe_utc(Time), "WARNING REPORT"),
-- S = format_report(Rep),
-- io:format(Fd, T ++ S ++ add_node("", Pid), []);
--write_event(Fd, {Time, {warning_msg, _GL, {Pid, Format, Args}}}) ->
-- T = write_time(maybe_utc(Time), "WARNING REPORT"),
-- case catch io_lib:format(add_node(Format,Pid), Args) of
-- S when is_list(S) ->
-- io:format(Fd, T ++ S, []);
-- _ ->
-- F = add_node("ERROR: ~p - ~p~n", Pid),
-- io:format(Fd, T ++ F, [Format,Args])
-- end;
--write_event(_, _) ->
-- ok.
-+format(#st{depth=unlimited}, Format, Args) ->
-+ io_lib:format(Format, Args);
-+format(#st{depth=Depth}, Format0, Args) ->
-+ Format1 = io_lib:scan_format(Format0, Args),
-+ Format = limit_format(Format1, Depth),
-+ io_lib:build_text(Format).
-+
-+limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p;
-+ C0 =:= $w ->
-+ C = C0 - ($a - $A), %To uppercase.
-+ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}||limit_format(T, Depth)];
-+limit_format([H|T], Depth) ->
-+ [H|limit_format(T, Depth)];
-+limit_format([], _) ->
-+ [].
-+
-+parse_event({error, _GL, {Pid, Format, Args}}) ->
-+ {"ERROR REPORT",Pid,[{Format,Args}]};
-+parse_event({info_msg, _GL, {Pid, Format, Args}}) ->
-+ {"INFO REPORT",Pid,[{Format, Args}]};
-+parse_event({warning_msg, _GL, {Pid, Format, Args}}) ->
-+ {"WARNING REPORT",Pid,[{Format,Args}]};
-+parse_event({error_report, _GL, {Pid, std_error, Args}}) ->
-+ {"ERROR REPORT",Pid,format_term(Args)};
-+parse_event({info_report, _GL, {Pid, std_info, Args}}) ->
-+ {"INFO REPORT",Pid,format_term(Args)};
-+parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
-+ {"WARNING REPORT",Pid,format_term(Args)};
-+parse_event(_) -> ignore.
-
- maybe_utc(Time) ->
- UTC = case application:get_env(sasl, utc_log) of
-@@ -182,30 +184,27 @@ maybe_utc(Time) ->
- maybe_utc(Time, true) -> {utc, Time};
- maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}.
-
--format_report(Rep) when is_list(Rep) ->
-- case string_p(Rep) of
-+format_term(Term) when is_list(Term) ->
-+ case string_p(Term) of
- true ->
-- io_lib:format("~s~n",[Rep]);
-- _ ->
-- format_rep(Rep)
-+ [{"~s\n",[Term]}];
-+ false ->
-+ format_term_list(Term)
- end;
--format_report(Rep) ->
-- io_lib:format("~p~n",[Rep]).
--
--format_rep([{Tag,Data}|Rep]) ->
-- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
--format_rep([Other|Rep]) ->
-- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep);
--format_rep(_) ->
-+format_term(Term) ->
-+ [{"~p\n",[Term]}].
-+
-+format_term_list([{Tag,Data}|T]) ->
-+ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
-+format_term_list([Data|T]) ->
-+ [{" ~p\n",[Data]}|format_term_list(T)];
-+format_term_list([]) ->
-+ [];
-+format_term_list(_) ->
-+ %% Continue to allow non-proper lists for now.
-+ %% FIXME: Remove this clause in OTP 19.
- [].
-
--add_node(X, Pid) when is_atom(X) ->
-- add_node(atom_to_list(X), Pid);
--add_node(X, Pid) when node(Pid) =/= node() ->
-- lists:concat([X,"** at node ",node(Pid)," **~n"]);
--add_node(X, _) ->
-- X.
--
- string_p([]) ->
- false;
- string_p(Term) ->
-@@ -221,15 +220,10 @@ string_p1([$\b|T]) -> string_p1(T);
- string_p1([$\f|T]) -> string_p1(T);
- string_p1([$\e|T]) -> string_p1(T);
- string_p1([H|T]) when is_list(H) ->
-- case string_p1(H) of
-- true -> string_p1(T);
-- _ -> false
-- end;
-+ string_p1(H) andalso string_p1(T);
- string_p1([]) -> true;
- string_p1(_) -> false.
-
--write_time(Time) -> write_time(Time, "ERROR REPORT").
--
- write_time({utc,{{Y,Mo,D},{H,Mi,S}}}, Type) ->
- io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
- [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
-diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl
-index ad5891f..72a052f 100644
---- a/lib/stdlib/src/error_logger_tty_h.erl
-+++ b/lib/stdlib/src/error_logger_tty_h.erl
-@@ -22,144 +22,178 @@
-
- %%%
- %%% A handler that can be connected to the error_logger
--%%% event handler.
--%%% Writes all events formatted to stdout.
--%%% Handles events tagged error, emulator and info.
-+%%% event handler. Writes all events formatted to stdout.
- %%%
- %%% It can only be started from error_logger:swap_handler(tty)
--%%% or error_logger:tty(true)
-+%%% or error_logger:tty(true).
- %%%
-
- -export([init/1,
- handle_event/2, handle_call/2, handle_info/2,
- terminate/2, code_change/3]).
-
---export([write_event/2]).
-+-export([write_event/2,write_event/3]).
-+
-+-record(st,
-+ {user,
-+ prev_handler,
-+ io_mod=io,
-+ depth=unlimited}).
-
- %% This one is used when we takeover from the simple error_logger.
- init({[], {error_logger, Buf}}) ->
- User = set_group_leader(),
-- write_events(Buf,io),
-- {ok, {User, error_logger}};
-+ Depth = get_depth(),
-+ State = #st{user=User,prev_handler=error_logger,depth=Depth},
-+ write_events(State, Buf),
-+ {ok, State};
- %% This one is used if someone took over from us, and now wants to
- %% go back.
- init({[], {error_logger_tty_h, PrevHandler}}) ->
- User = set_group_leader(),
-- {ok, {User, PrevHandler}};
-+ {ok, #st{user=User,prev_handler=PrevHandler}};
- %% This one is used when we are started directly.
- init([]) ->
- User = set_group_leader(),
-- {ok, {User, []}}.
-+ Depth = get_depth(),
-+ {ok, #st{user=User,prev_handler=[],depth=Depth}}.
-+
-+get_depth() ->
-+ case application:get_env(kernel, error_logger_format_depth) of
-+ {ok, Depth} when is_integer(Depth) ->
-+ max(10, Depth);
-+ undefined ->
-+ unlimited
-+ end.
-
- handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
- {ok, State};
- handle_event(Event, State) ->
-- write_event(tag_event(Event),io),
-+ ok = do_write_event(State, tag_event(Event)),
- {ok, State}.
-
--handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) ->
-+handle_info({'EXIT', User, _Reason},
-+ #st{user=User,prev_handler=PrevHandler}=State) ->
- case PrevHandler of
- [] ->
- remove_handler;
- _ ->
-- {swap_handler, install_prev, {User, PrevHandler},
-+ {swap_handler, install_prev, State,
- PrevHandler, go_back}
- end;
--handle_info({emulator, GL, Chars}, State) when node(GL) == node() ->
-- write_event(tag_event({emulator, GL, Chars}),io),
-- {ok, State};
--handle_info({emulator, noproc, Chars}, State) ->
-- write_event(tag_event({emulator, noproc, Chars}),io),
-- {ok, State};
- handle_info(_, State) ->
- {ok, State}.
-
- handle_call(_Query, State) -> {ok, {error, bad_query}, State}.
-
--% unfortunately, we can't unlink from User - links are not counted!
--% if pid(User) -> unlink(User); true -> ok end,
- terminate(install_prev, _State) ->
- [];
--terminate(_Reason, {_User, PrevHandler}) ->
-+terminate(_Reason, #st{prev_handler=PrevHandler}) ->
- {error_logger_tty_h, PrevHandler}.
-
- code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-+%% Exported (but unoffical) API.
-+write_event(Event, IoMod) ->
-+ do_write_event(#st{io_mod=IoMod}, Event).
-+
-+write_event(Event, IoMod, Depth) ->
-+ do_write_event(#st{io_mod=IoMod,depth=Depth}, Event).
-+
-+
- %%% ------------------------------------------------------
- %%% Misc. functions.
- %%% ------------------------------------------------------
-
- set_group_leader() ->
- case whereis(user) of
-- User when is_pid(User) -> link(User), group_leader(User,self()), User;
-- _ -> false
-+ User when is_pid(User) ->
-+ link(User),
-+ group_leader(User,self()),
-+ User;
-+ _ ->
-+ false
- end.
-
- tag_event(Event) ->
- {erlang:universaltime(), Event}.
-
--write_events(Events,IOMod) -> write_events1(lists:reverse(Events),IOMod).
--
--write_events1([Event|Es],IOMod) ->
-- write_event(Event,IOMod),
-- write_events1(Es,IOMod);
--write_events1([],_IOMod) ->
-+write_events(State, [Ev|Es]) ->
-+ %% Write the events in reverse order.
-+ _ = write_events(State, Es),
-+ _ = do_write_event(State, Ev),
-+ ok;
-+write_events(_State, []) ->
- ok.
-
--write_event({Time, {error, _GL, {Pid, Format, Args}}},IOMod) ->
-- T = write_time(maybe_utc(Time)),
-- case catch io_lib:format(add_node(Format,Pid), Args) of
-- S when is_list(S) ->
-- format(IOMod, T ++ S);
-- _ ->
-- F = add_node("ERROR: ~p - ~p~n", Pid),
-- format(IOMod, T ++ F, [Format,Args])
-+do_write_event(State, {Time0, Event}) ->
-+ case parse_event(Event) of
-+ ignore ->
-+ ok;
-+ {Head,Pid,FormatList} ->
-+ Time = maybe_utc(Time0),
-+ Header = write_time(Time, Head),
-+ Body = format_body(State, FormatList),
-+ AtNode = if
-+ node(Pid) =/= node() ->
-+ ["** at node ",atom_to_list(node(Pid))," **\n"];
-+ true ->
-+ []
-+ end,
-+ Str = [Header,Body,AtNode],
-+ case State#st.io_mod of
-+ io_lib ->
-+ Str;
-+ io ->
-+ io:put_chars(user, Str)
-+ end
- end;
--write_event({Time, {emulator, _GL, Chars}},IOMod) ->
-- T = write_time(maybe_utc(Time)),
-- case catch io_lib:format(Chars, []) of
-- S when is_list(S) ->
-- format(IOMod, T ++ S);
-- _ ->
-- format(IOMod, T ++ "ERROR: ~p ~n", [Chars])
-- end;
--write_event({Time, {info, _GL, {Pid, Info, _}}},IOMod) ->
-- T = write_time(maybe_utc(Time)),
-- format(IOMod, T ++ add_node("~p~n",Pid),[Info]);
--write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}},IOMod) ->
-- T = write_time(maybe_utc(Time)),
-- S = format_report(Rep),
-- format(IOMod, T ++ S ++ add_node("", Pid));
--write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}},IOMod) ->
-- T = write_time(maybe_utc(Time), "INFO REPORT"),
-- S = format_report(Rep),
-- format(IOMod, T ++ S ++ add_node("", Pid));
--write_event({Time, {info_msg, _GL, {Pid, Format, Args}}},IOMod) ->
-- T = write_time(maybe_utc(Time), "INFO REPORT"),
-- case catch io_lib:format(add_node(Format,Pid), Args) of
-- S when is_list(S) ->
-- format(IOMod, T ++ S);
-- _ ->
-- F = add_node("ERROR: ~p - ~p~n", Pid),
-- format(IOMod, T ++ F, [Format,Args])
-- end;
--write_event({Time, {warning_report, _GL, {Pid, std_warning, Rep}}},IOMod) ->
-- T = write_time(maybe_utc(Time), "WARNING REPORT"),
-- S = format_report(Rep),
-- format(IOMod, T ++ S ++ add_node("", Pid));
--write_event({Time, {warning_msg, _GL, {Pid, Format, Args}}},IOMod) ->
-- T = write_time(maybe_utc(Time), "WARNING REPORT"),
-- case catch io_lib:format(add_node(Format,Pid), Args) of
-- S when is_list(S) ->
-- format(IOMod, T ++ S);
-- _ ->
-- F = add_node("ERROR: ~p - ~p~n", Pid),
-- format(IOMod, T ++ F, [Format,Args])
-- end;
--write_event({_Time, _Error},_IOMod) ->
-+do_write_event(_, _) ->
- ok.
-
-+format_body(State, [{Format,Args}|T]) ->
-+ S = try format(State, Format, Args) of
-+ S0 ->
-+ S0
-+ catch
-+ _:_ ->
-+ format(State, "ERROR: ~p - ~p\n", [Format,Args])
-+ end,
-+ [S|format_body(State, T)];
-+format_body(_State, []) ->
-+ [].
-+
-+format(#st{depth=unlimited}, Format, Args) ->
-+ io_lib:format(Format, Args);
-+format(#st{depth=Depth}, Format0, Args) ->
-+ Format1 = io_lib:scan_format(Format0, Args),
-+ Format = limit_format(Format1, Depth),
-+ io_lib:build_text(Format).
-+
-+limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p;
-+ C0 =:= $w ->
-+ C = C0 - ($a - $A), %To uppercase.
-+ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}|limit_format(T, Depth)];
-+limit_format([H|T], Depth) ->
-+ [H|limit_format(T, Depth)];
-+limit_format([], _) ->
-+ [].
-+
-+parse_event({error, _GL, {Pid, Format, Args}}) ->
-+ {"ERROR REPORT",Pid,[{Format,Args}]};
-+parse_event({info_msg, _GL, {Pid, Format, Args}}) ->
-+ {"INFO REPORT",Pid,[{Format, Args}]};
-+parse_event({warning_msg, _GL, {Pid, Format, Args}}) ->
-+ {"WARNING REPORT",Pid,[{Format,Args}]};
-+parse_event({error_report, _GL, {Pid, std_error, Args}}) ->
-+ {"ERROR REPORT",Pid,format_term(Args)};
-+parse_event({info_report, _GL, {Pid, std_info, Args}}) ->
-+ {"INFO REPORT",Pid,format_term(Args)};
-+parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
-+ {"WARNING REPORT",Pid,format_term(Args)};
-+parse_event(_) -> ignore.
-+
- maybe_utc(Time) ->
- UTC = case application:get_env(sasl, utc_log) of
- {ok, Val} -> Val;
-@@ -175,33 +209,26 @@ maybe_utc(Time) ->
- maybe_utc(Time, true) -> {utc, Time};
- maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}.
-
--format(IOMod, String) -> format(IOMod, String, []).
--format(io_lib, String, Args) -> io_lib:format(String, Args);
--format(io, String, Args) -> io:format(user, String, Args).
--
--format_report(Rep) when is_list(Rep) ->
-- case string_p(Rep) of
-+format_term(Term) when is_list(Term) ->
-+ case string_p(Term) of
- true ->
-- io_lib:format("~s~n",[Rep]);
-- _ ->
-- format_rep(Rep)
-+ [{"~s\n",[Term]}];
-+ false ->
-+ format_term_list(Term)
- end;
--format_report(Rep) ->
-- io_lib:format("~p~n",[Rep]).
--
--format_rep([{Tag,Data}|Rep]) ->
-- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
--format_rep([Other|Rep]) ->
-- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep);
--format_rep(_) ->
-- [].
-+format_term(Term) ->
-+ [{"~p\n",[Term]}].
-
--add_node(X, Pid) when is_atom(X) ->
-- add_node(atom_to_list(X), Pid);
--add_node(X, Pid) when node(Pid) =/= node() ->
-- lists:concat([X,"** at node ",node(Pid)," **~n"]);
--add_node(X, _) ->
-- X.
-+format_term_list([{Tag,Data}|T]) ->
-+ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
-+format_term_list([Data|T]) ->
-+ [{" ~p\n",[Data]}|format_term_list(T)];
-+format_term_list([]) ->
-+ [];
-+format_term_list(_) ->
-+ %% Continue to allow non-proper lists for now.
-+ %% FIXME: Remove this clause in OTP 19.
-+ [].
-
- string_p([]) ->
- false;
-@@ -225,7 +252,6 @@ string_p1([H|T]) when is_list(H) ->
- string_p1([]) -> true;
- string_p1(_) -> false.
-
--write_time(Time) -> write_time(Time, "ERROR REPORT").
- write_time({utc,{{Y,Mo,D},{H,Mi,S}}},Type) ->
- io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
- [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
-diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
-index 9e69601..3c173dc 100644
---- a/lib/stdlib/src/io_lib.erl
-+++ b/lib/stdlib/src/io_lib.erl
-@@ -2,7 +2,7 @@
- %%
- %% %CopyrightBegin%
- %%
--%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
-+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
- %%
- %% The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
-@@ -61,6 +61,7 @@
- -module(io_lib).
-
- -export([fwrite/2,fread/2,fread/3,format/2]).
-+-export([scan_format/2,unscan_format/1,build_text/1]).
- -export([print/1,print/4,indentation/2]).
-
- -export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]).
-@@ -84,7 +85,7 @@
- deep_unicode_char_list/1]).
-
- -export_type([chars/0, latin1_string/0, continuation/0,
-- fread_error/0, fread_item/0]).
-+ fread_error/0, fread_item/0, format_spec/0]).
-
- %%----------------------------------------------------------------------
-
-@@ -109,6 +110,18 @@
-
- -type fread_item() :: string() | atom() | integer() | float().
-
-+-type format_spec() ::
-+ {
-+ ControlChar :: char(),
-+ Args :: [any()],
-+ Width :: 'none' | integer(),
-+ Adjust :: 'left' | 'right',
-+ Precision :: 'none' | integer(),
-+ PadChar :: char(),
-+ Encoding :: 'unicode' | 'latin1',
-+ Strings :: boolean()
-+ }.
-+
- %%----------------------------------------------------------------------
-
- %% Interface calls to sub-modules.
-@@ -157,6 +170,31 @@ format(Format, Args) ->
- Other
- end.
-
-+-spec scan_format(Format, Data) -> FormatList when
-+ Format :: io:format(),
-+ Data :: [term()],
-+ FormatList :: [char() | format_spec()].
-+
-+scan_format(Format, Args) ->
-+ try io_lib_format:scan(Format, Args)
-+ catch
-+ _:_ -> erlang:error(badarg, [Format, Args])
-+ end.
-+
-+-spec unscan_format(FormatList) -> {Format, Data} when
-+ FormatList :: [char() | format_spec()],
-+ Format :: io:format(),
-+ Data :: [term()].
-+
-+unscan_format(FormatList) ->
-+ io_lib_format:unscan(FormatList).
-+
-+-spec build_text(FormatList) -> chars() when
-+ FormatList :: [char() | format_spec()].
-+
-+build_text(FormatList) ->
-+ io_lib_format:build(FormatList).
-+
- -spec print(Term) -> chars() when
- Term :: term().
-
-diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
-index 56e15a1..37b47d7 100644
---- a/lib/stdlib/src/io_lib_format.erl
-+++ b/lib/stdlib/src/io_lib_format.erl
-@@ -1,7 +1,7 @@
- %%
- %% %CopyrightBegin%
- %%
--%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
-+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
- %%
- %% The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
-@@ -20,10 +20,9 @@
-
- %% Formatting functions of io library.
-
---export([fwrite/2,fwrite_g/1,indentation/2]).
-+-export([fwrite/2,fwrite_g/1,indentation/2,scan/2,unscan/1,build/1]).
-
--%% fwrite(Format, ArgList) -> string().
--%% Format the arguments in ArgList after string Format. Just generate
-+%% Format the arguments in Args after string Format. Just generate
- %% an error if there is an error in the arguments.
- %%
- %% To do the printing command correctly we need to calculate the
-@@ -37,15 +36,83 @@
- %% and it also splits the handling of the control characters into two
- %% parts.
-
--fwrite(Format, Args) when is_atom(Format) ->
-- fwrite(atom_to_list(Format), Args);
--fwrite(Format, Args) when is_binary(Format) ->
-- fwrite(binary_to_list(Format), Args);
-+-spec fwrite(Format, Data) -> FormatList when
-+ Format :: io:format(),
-+ Data :: [term()],
-+ FormatList :: [char() | io_lib:format_spec()].
-+
- fwrite(Format, Args) ->
-- Cs = collect(Format, Args),
-+ build(scan(Format, Args)).
-+
-+%% Build the output text for a pre-parsed format list.
-+
-+-spec build(FormatList) -> io_lib:chars() when
-+ FormatList :: [char() | io_lib:format_spec()].
-+
-+build(Cs) ->
- Pc = pcount(Cs),
- build(Cs, Pc, 0).
-
-+%% Parse all control sequences in the format string.
-+
-+-spec scan(Format, Data) -> FormatList when
-+ Format :: io:format(),
-+ Data :: [term()],
-+ FormatList :: [char() | io_lib:format_spec()].
-+
-+scan(Format, Args) when is_atom(Format) ->
-+ scan(atom_to_list(Format), Args);
-+scan(Format, Args) when is_binary(Format) ->
-+ scan(binary_to_list(Format), Args);
-+scan(Format, Args) ->
-+ collect(Format, Args).
-+
-+%% Revert a pre-parsed format list to a plain character list and a
-+%% list of arguments.
-+
-+-spec unscan(FormatList) -> {Format, Data} when
-+ FormatList :: [char() | io_lib:format_spec()],
-+ Format :: io:format(),
-+ Data :: [term()].
-+
-+unscan(Cs) ->
-+ {print(Cs), args(Cs)}.
-+
-+args([{_C,As,_F,_Ad,_P,_Pad,_Enc,_Str} | Cs]) ->
-+ As ++ args(Cs);
-+args([_C | Cs]) ->
-+ args(Cs);
-+args([]) ->
-+ [].
-+
-+print([{C,_As,F,Ad,P,Pad,Enc,Str} | Cs ]) ->
-+ print(C, F, Ad, P, Pad, Enc, Str) ++ print(Cs);
-+print([C | Cs]) ->
-+ [C | print(Cs)];
-+print([]) ->
-+ [].
-+
-+print(C, F, Ad, P, Pad, Encoding, Strings) ->
-+ [$~] ++ print_field_width(F, Ad) ++ print_precision(P) ++
-+ print_pad_char(Pad) ++ print_encoding(Encoding) ++
-+ print_strings(Strings) ++ [C].
-+
-+print_field_width(none, _Ad) -> "";
-+print_field_width(F, left) -> integer_to_list(-F);
-+print_field_width(F, right) -> integer_to_list(F).
-+
-+print_precision(none) -> "";
-+print_precision(P) -> [$. | integer_to_list(P)].
-+
-+print_pad_char($\s) -> ""; % default, no need to make explicit
-+print_pad_char(Pad) -> [$., Pad].
-+
-+print_encoding(unicode) -> "t";
-+print_encoding(latin1) -> "".
-+
-+print_strings(false) -> "l";
-+print_strings(true) -> "".
-+
- collect([$~|Fmt0], Args0) ->
- {C,Fmt1,Args1} = collect_cseq(Fmt0, Args0),
- [C|collect(Fmt1, Args1)];
-@@ -141,7 +208,7 @@ pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1);
- pcount([_|Cs], Acc) -> pcount(Cs, Acc);
- pcount([], Acc) -> Acc.
-
--%% build([Control], Pc, Indentation) -> string().
-+%% build([Control], Pc, Indentation) -> io_lib:chars().
- %% Interpret the control structures. Count the number of print
- %% remaining and only calculate indentation when necessary. Must also
- %% be smart when calculating indentation for characters in format.
-@@ -162,10 +229,14 @@ decr_pc($p, Pc) -> Pc - 1;
- decr_pc($P, Pc) -> Pc - 1;
- decr_pc(_, Pc) -> Pc.
-
--%% indentation(String, Indentation) -> Indentation.
-+
- %% Calculate the indentation of the end of a string given its start
- %% indentation. We assume tabs at 8 cols.
-
-+-spec indentation(String, StartIndent) -> integer() when
-+ String :: io_lib:chars(),
-+ StartIndent :: integer().
-+
- indentation([$\n|Cs], _I) -> indentation(Cs, 0);
- indentation([$\t|Cs], I) -> indentation(Cs, ((I + 8) div 8) * 8);
- indentation([C|Cs], I) when is_integer(C) ->
-@@ -366,7 +437,6 @@ float_data([D|Cs], Ds) when D >= $0, D =< $9 ->
- float_data([_|Cs], Ds) ->
- float_data(Cs, Ds).
-
--%% fwrite_g(Float)
- %% Writes the shortest, correctly rounded string that converts
- %% to Float when read back with list_to_float/1.
- %%
-@@ -374,6 +444,8 @@ float_data([_|Cs], Ds) ->
- %% in Proceedings of the SIGPLAN '96 Conference on Programming
- %% Language Design and Implementation.
-
-+-spec fwrite_g(float()) -> string().
-+
- fwrite_g(0.0) ->
- "0.0";
- fwrite_g(Float) when is_float(Float) ->
-@@ -642,7 +714,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase)
- term([Prefix|S], F, Adj, none, Pad)
- end.
-
--%% char(Char, Field, Adjust, Precision, PadChar) -> string().
-+%% char(Char, Field, Adjust, Precision, PadChar) -> chars().
-
- char(C, none, _Adj, none, _Pad) -> [C];
- char(C, F, _Adj, none, _Pad) -> chars(C, F);
diff --git a/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch b/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch
deleted file mode 100644
index e9e6450..0000000
--- a/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch
+++ /dev/null
@@ -1,74 +0,0 @@
-From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
-Date: Tue, 25 Aug 2015 15:20:23 +0200
-Subject: [PATCH] Teach sasl_report to limit crash reports
-
-
-diff --git a/lib/sasl/src/sasl_report.erl b/lib/sasl/src/sasl_report.erl
-index c3e6fed..aa84e4f 100644
---- a/lib/sasl/src/sasl_report.erl
-+++ b/lib/sasl/src/sasl_report.erl
-@@ -61,27 +61,53 @@ write_report2(IO, Fd, Head, supervisor_report, Report) ->
- Context = sup_get(errorContext, Report),
- Reason = sup_get(reason, Report),
- Offender = sup_get(offender, Report),
-- FmtString = " Supervisor: ~p~n Context: ~p~n Reason: "
-- "~80.18p~n Offender: ~80.18p~n~n",
-- write_report_action(IO, Fd, Head ++ FmtString,
-- [Name,Context,Reason,Offender]);
-+ {FmtString,Args} = supervisor_format([Name,Context,Reason,Offender]),
-+ write_report_action(IO, Fd, Head, FmtString, Args);
- write_report2(IO, Fd, Head, progress, Report) ->
- Format = format_key_val(Report),
-- write_report_action(IO, Fd, Head ++ "~s", [Format]);
-+ write_report_action(IO, Fd, Head, "~s", [Format]);
- write_report2(IO, Fd, Head, crash_report, Report) ->
-- Format = proc_lib:format(Report),
-- write_report_action(IO, Fd, Head ++ "~s", [Format]).
-+ Depth = get_depth(),
-+ Format = proc_lib:format(Report, latin1, Depth),
-+ write_report_action(IO, Fd, Head, "~s", [Format]).
-+
-+supervisor_format(Args0) ->
-+ case get_depth() of
-+ unlimited ->
-+ {" Supervisor: ~p~n"
-+ " Context: ~p~n"
-+ " Reason: ~80.18p~n"
-+ " Offender: ~80.18p~n~n",
-+ Args0};
-+ Depth ->
-+ [A,B,C,D] = Args0,
-+ Args = [A,Depth,B,Depth,C,Depth,D,Depth],
-+ {" Supervisor: ~P~n"
-+ " Context: ~P~n"
-+ " Reason: ~80.18P~n"
-+ " Offender: ~80.18P~n~n",
-+ Args}
-+ end.
-
--write_report_action(io, Fd, Format, Args) ->
-- io:format(Fd, Format, Args);
--write_report_action(io_lib, _Fd, Format, Args) ->
-- io_lib:format(Format, Args).
-+write_report_action(IO, Fd, Head, Format, Args) ->
-+ S = [Head|io_lib:format(Format, Args)],
-+ case IO of
-+ io -> io:put_chars(Fd, S);
-+ io_lib -> S
-+ end.
-
- format_key_val([{Tag,Data}|Rep]) ->
- io_lib:format(" ~16w: ~p~n",[Tag,Data]) ++ format_key_val(Rep);
- format_key_val(_) ->
- [].
-
-+get_depth() ->
-+ case application:get_env(kernel, error_logger_format_depth) of
-+ {ok, Depth} when is_integer(Depth) ->
-+ max(10, Depth);
-+ undefined ->
-+ unlimited
-+ end.
-
- sup_get(Tag, Report) ->
- case lists:keysearch(Tag, 1, Report) of
From 4627cfdff14a120761c13d63c39371c082aa71f0 Mon Sep 17 00:00:00 2001
From: Peter Lemenkov
Date: Wed, 9 Nov 2016 17:40:36 +0300
Subject: [PATCH 12/13] Backports for mnesia, and epmd in ipv6-only env
- Respect -proto_dist switch while connecting to EPMD
- Cherry-picked fix for OTP-13423. Mnesia transactions could hang while
waiting on a response from a node who had stopped.
Signed-off-by: Peter Lemenkov
---
erlang.spec | 27 +-
...unused-code-in-error-logger-handlers.patch | 1028 ++++++++++++++++
...h-sasl_report-to-limit-crash-reports.patch | 74 ++
...unused-code-in-error-logger-handlers.patch | 1064 +++++++++++++++++
...dist-switch-while-connection-to-EPMD.patch | 34 +
...ia_down-messages-to-waiting-transact.patch | 38 +
6 files changed, 2264 insertions(+), 1 deletion(-)
create mode 100644 otp-0024-Remove-unused-code-in-error-logger-handlers.patch
create mode 100644 otp-0025-Teach-sasl_report-to-limit-crash-reports.patch
create mode 100644 otp-0026-Revert-Remove-unused-code-in-error-logger-handlers.patch
create mode 100644 otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch
create mode 100644 otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch
diff --git a/erlang.spec b/erlang.spec
index 83d9e27..e117c20 100644
--- a/erlang.spec
+++ b/erlang.spec
@@ -25,7 +25,7 @@
Name: erlang
Version: %{upstream_ver}
-Release: %{upstream_rel_for_rpm}.17%{?dist}
+Release: %{upstream_rel_for_rpm}.18%{?dist}
Summary: General-purpose programming language and runtime environment
Group: Development/Languages
@@ -125,6 +125,21 @@ Patch22: otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch
# Fedora specific patch
# epmd: support IPv6 node registration
Patch23: otp-0023-epmd-support-IPv6-node-registration.patch
+# Fedora specific patch
+# Remove unused code in error logger handlers
+Patch24: otp-0024-Remove-unused-code-in-error-logger-handlers.patch
+# Fedora specific patch
+# Teach sasl_report to limit crash reports
+Patch25: otp-0025-Teach-sasl_report-to-limit-crash-reports.patch
+# Fedora specific patch
+# Revert "Remove unused code in error logger handlers"
+Patch26: otp-0026-Revert-Remove-unused-code-in-error-logger-handlers.patch
+# Fedora specific patch
+# Respect -proto_dist switch while connection to EPMD
+Patch27: otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch
+# Fedora specific patch
+# mnesia: Send mnesia_down messages to waiting transactions
+Patch28: otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch
# end of autogenerated patch tag list
BuildRequires: lksctp-tools-devel
@@ -1015,6 +1030,11 @@ Erlang mode for XEmacs (source lisp files).
%patch21 -p1 -b .Add_patch_to_crash_dump_on_large_distribution
%patch22 -p1 -b .kernel_inet6_tcp_dist_reuse_inet_tcp_dist_code
%patch23 -p1 -b .epmd_support_IPv6_node_registration
+%patch24 -p1 -b .Remove_unused_code_in_error_logger_handlers
+%patch25 -p1 -b .Teach_sasl_report_to_limit_crash_reports
+%patch26 -p1 -b .Revert_Remove_unused_code_in_error_logger_handlers
+%patch27 -p1 -b .Respect_proto_dist_switch_while_connection_to_EPMD
+%patch28 -p1 -b .mnesia_Send_mnesia_down_messages_to_waiting_transact
# end of autogenerated prep patch list
# FIXME we should come up with a better solution
@@ -2346,6 +2366,11 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \
%changelog
+* Wed Nov 9 2016 Peter Lemenkov - R16B-03.18
+- Respect -proto_dist switch while connecting to EPMD
+- Cherry-picked fix for OTP-13423. Mnesia transactions could hang while waiting
+ on a response from a node who had stopped.
+
* Thu Apr 7 2016 John Eckersberg - R16B-03.17
- Revert "Enable error_logger depth fine tuning" (rhbz#1324922)
diff --git a/otp-0024-Remove-unused-code-in-error-logger-handlers.patch b/otp-0024-Remove-unused-code-in-error-logger-handlers.patch
new file mode 100644
index 0000000..a3ea0ac
--- /dev/null
+++ b/otp-0024-Remove-unused-code-in-error-logger-handlers.patch
@@ -0,0 +1,1028 @@
+From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
+Date: Thu, 20 Aug 2015 09:44:43 +0200
+Subject: [PATCH] Remove unused code in error logger handlers
+
+A long time ago, errors from the emulator itself was sent as
+messages that would end up in the handle_info/2 function.
+Those clauses in handle_info/2 can be removed.
+
+The code for handling events tagged 'info' instead of 'info_msg'
+can also be taken out.
+
+error_logger_file_h: Refactor and modernize code
+
+Refactor, simplify, and modernize the code to facilitate future
+improvements in the following commits.
+
+Teach error_logger_file_h to truncate big messages
+
+Add the possibility to truncate big messages to avoid running out
+of memory.
+
+error_logger_tty_h: Refactor and modernize code
+
+Refactor, simplify, and modernize the code to facilitate future
+improvements in the following commits.
+
+Teach error_logger_tty_h to truncate big messages
+
+Add the possibility to truncate big messages to avoid running out
+of memory.
+
+stdlib: Fix leaking files after error_logger:logfile(close)
+
+Introduced when changing state from tuple to record.
+
+Make the scanned form of the io_lib format strings available for processing
+
+This adds three new functions to io_lib - scan_format/2, unscan_format/1,
+and build_text/1 - which expose the parsed form of the format control
+sequences to make it possible to easily modify or filter the input to
+io_lib:format/2. This can e.g. be used in order to replace unbounded-size
+control sequences like ~w or ~p with corresponding depth-limited ~W and ~P
+before doing the actual formatting.
+
+diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml
+index 90f24c4..6613dcd 100644
+--- a/lib/stdlib/doc/src/io.xml
++++ b/lib/stdlib/doc/src/io.xml
+@@ -505,7 +505,8 @@ ok
+
Writes the data with standard syntax in the same way as
+ ~w, but breaks terms whose printed representation
+ is longer than one line into many lines and indents each
+- line sensibly. It also tries to detect lists of
++ line sensibly. Left justification is not supported.
++ It also tries to detect lists of
+ printable characters and to output these as strings. The
+ Unicode translation modifier is used for determining
+ what characters are printable. For example:
+diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml
+index 68352ff..0c1c84d 100644
+--- a/lib/stdlib/doc/src/io_lib.xml
++++ b/lib/stdlib/doc/src/io_lib.xml
+@@ -4,7 +4,7 @@
+
+
+
+- 19962013
++ 19962014
+ Ericsson AB. All Rights Reserved.
+
+
+@@ -59,6 +59,35 @@
+
+
+
++
++
++
Description:
++
++
control_char is the type of control
++ sequence: $P, $w, and so on;
++
++
args is a list of the arguments used by the
++ control sequence, or an empty list if the control sequence
++ does not take any arguments;
++
++
width is the field width;
++
++
adjust is the adjustment;
++
++
precision is the precision of the printed
++ argument;
++
++
pad_char is the padding character;
++
++
encoding is set to true if the translation
++ modifier t is present;
++
++
strings is set to false if the modifier
++ l is present.
++
++
++
++
+
+
+
+@@ -260,6 +289,45 @@
+
+
+
++
++ Parse all control sequences in the format string
++
++
Returns a list corresponding to the given format string,
++ where control sequences have been replaced with
++ corresponding tuples. This list can be passed to io_lib:build_text/1 to have
++ the same effect as io_lib:format(Format, Args), or to
++ io_lib:unscan_format/1
++ in order to get the corresponding pair of Format and
++ Args (with every * and corresponding argument
++ expanded to numeric values).
++
A typical use of this function is to replace unbounded-size
++ control sequences like ~w and ~p with the
++ depth-limited variants ~W and ~P before
++ formatting to text, e.g. in a logger.
++
++
++
++
++ Revert a pre-parsed format list to a plain character list
++ and a list of arguments
++
++
See io_lib:scan_format/2 for
++ details.
++
++
++
++
++ Build the output text for a pre-parsed format list
++
++
See io_lib:scan_format/2 for
++ details.
++
++
++
+
+ Indentation after printing string
+
+diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl
+index e9364ed..ca44d01 100644
+--- a/lib/stdlib/src/error_logger_file_h.erl
++++ b/lib/stdlib/src/error_logger_file_h.erl
+@@ -23,24 +23,28 @@
+
+ %%%
+ %%% A handler that can be connected to the error_logger
+-%%% event handler.
+-%%% Writes all events formatted to file.
+-%%% Handles events tagged error, emulator and info.
++%%% event handler. Writes all events formatted to file.
+ %%%
+ %%% It can only be started from error_logger:swap_handler({logfile, File})
+-%%% or error_logger:logfile(File)
++%%% or error_logger:logfile(File).
+ %%%
+
+ -export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2, code_change/3]).
+
++-record(st,
++ {fd,
++ filename,
++ prev_handler,
++ depth=unlimited :: 'unlimited' | non_neg_integer()}).
++
+ %% This one is used when we takeover from the simple error_logger.
+ init({File, {error_logger, Buf}}) ->
+ case init(File, error_logger) of
+- {ok, {Fd, File, PrevHandler}} ->
+- write_events(Fd, Buf),
+- {ok, {Fd, File, PrevHandler}};
++ {ok, State} ->
++ write_events(State, Buf),
++ {ok, State};
+ Error ->
+ Error
+ end;
+@@ -52,49 +56,45 @@ init(File, PrevHandler) ->
+ process_flag(trap_exit, true),
+ case file:open(File, [write]) of
+ {ok,Fd} ->
+- {ok, {Fd, File, PrevHandler}};
++ Depth = get_depth(),
++ State = #st{fd=Fd,filename=File,prev_handler=PrevHandler,
++ depth=Depth},
++ {ok, State};
+ Error ->
+ Error
+ end.
+-
++
++get_depth() ->
++ case application:get_env(kernel, error_logger_format_depth) of
++ {ok, Depth} when is_integer(Depth) ->
++ max(10, Depth);
++ undefined ->
++ unlimited
++ end.
++
+ handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
+ {ok, State};
+-handle_event(Event, {Fd, File, PrevHandler}) ->
+- write_event(Fd, tag_event(Event)),
+- {ok, {Fd, File, PrevHandler}};
+-handle_event(_, State) ->
++handle_event(Event, State) ->
++ write_event(State, Event),
+ {ok, State}.
+
+-handle_info({'EXIT', Fd, _Reason}, {Fd, _File, PrevHandler}) ->
++handle_info({'EXIT', Fd, _Reason}, #st{fd=Fd,prev_handler=PrevHandler}) ->
+ case PrevHandler of
+ [] ->
+ remove_handler;
+ _ ->
+ {swap_handler, install_prev, [], PrevHandler, go_back}
+ end;
+-handle_info({emulator, GL, Chars}, {Fd, File, PrevHandler})
+- when node(GL) == node() ->
+- write_event(Fd, tag_event({emulator, GL, Chars})),
+- {ok, {Fd, File, PrevHandler}};
+-handle_info({emulator, noproc, Chars}, {Fd, File, PrevHandler}) ->
+- write_event(Fd, tag_event({emulator, noproc, Chars})),
+- {ok, {Fd, File, PrevHandler}};
+ handle_info(_, State) ->
+ {ok, State}.
+
+-handle_call(filename, {Fd, File, Prev}) ->
+- {ok, File, {Fd, File, Prev}};
++handle_call(filename, #st{filename=File}=State) ->
++ {ok, File, State};
+ handle_call(_Query, State) ->
+ {ok, {error, bad_query}, State}.
+
+-terminate(_Reason, State) ->
+- case State of
+- {Fd, _File, _Prev} ->
+- ok = file:close(Fd);
+- _ ->
+- ok
+- end,
+- [].
++terminate(_Reason, #st{fd=Fd}) ->
++ file:close(Fd).
+
+ code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+@@ -103,69 +103,71 @@ code_change(_OldVsn, State, _Extra) ->
+ %%% Misc. functions.
+ %%% ------------------------------------------------------
+
+-tag_event(Event) ->
+- {erlang:universaltime(), Event}.
++write_events(State, [Ev|Es]) ->
++ %% Write the events in reversed order.
++ write_events(State, Es),
++ write_event(State, Ev);
++write_events(_State, []) ->
++ ok.
+
+-write_events(Fd, Events) -> write_events1(Fd, lists:reverse(Events)).
++write_event(#st{fd=Fd}=State, Event) ->
++ case parse_event(Event) of
++ ignore ->
++ ok;
++ {Head,Pid,FormatList} ->
++ Time = maybe_utc(erlang:universaltime()),
++ Header = write_time(Time, Head),
++ Body = format_body(State, FormatList),
++ AtNode = if
++ node(Pid) =/= node() ->
++ ["** at node ",atom_to_list(node(Pid))," **\n"];
++ true ->
++ []
++ end,
++ io:put_chars(Fd, [Header,Body,AtNode])
++ end.
+
+-write_events1(Fd, [Event|Es]) ->
+- write_event(Fd, Event),
+- write_events1(Fd, Es);
+-write_events1(_, []) ->
+- ok.
++format_body(State, [{Format,Args}|T]) ->
++ S = try format(State, Format, Args) of
++ S0 ->
++ S0
++ catch
++ _:_ ->
++ format(State, "ERROR: ~p - ~p\n", [Format,Args])
++ end,
++ [S|format_body(State, T)];
++format_body(_State, []) ->
++ [].
+
+-write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) ->
+- T = write_time(maybe_utc(Time)),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- io:format(Fd, T ++ S, []);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- io:format(Fd, T ++ F, [Format,Args])
+- end;
+-write_event(Fd, {Time, {emulator, _GL, Chars}}) ->
+- T = write_time(maybe_utc(Time)),
+- case catch io_lib:format(Chars, []) of
+- S when is_list(S) ->
+- io:format(Fd, T ++ S, []);
+- _ ->
+- io:format(Fd, T ++ "ERROR: ~p ~n", [Chars])
+- end;
+-write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) ->
+- T = write_time(maybe_utc(Time)),
+- io:format(Fd, T ++ add_node("~p~n",Pid),[Info]);
+-write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) ->
+- T = write_time(maybe_utc(Time)),
+- S = format_report(Rep),
+- io:format(Fd, T ++ S ++ add_node("", Pid), []);
+-write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) ->
+- T = write_time(maybe_utc(Time), "INFO REPORT"),
+- S = format_report(Rep),
+- io:format(Fd, T ++ S ++ add_node("", Pid), []);
+-write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) ->
+- T = write_time(maybe_utc(Time), "INFO REPORT"),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- io:format(Fd, T ++ S, []);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- io:format(Fd, T ++ F, [Format,Args])
+- end;
+-write_event(Fd, {Time, {warning_report, _GL, {Pid, std_warning, Rep}}}) ->
+- T = write_time(maybe_utc(Time), "WARNING REPORT"),
+- S = format_report(Rep),
+- io:format(Fd, T ++ S ++ add_node("", Pid), []);
+-write_event(Fd, {Time, {warning_msg, _GL, {Pid, Format, Args}}}) ->
+- T = write_time(maybe_utc(Time), "WARNING REPORT"),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- io:format(Fd, T ++ S, []);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- io:format(Fd, T ++ F, [Format,Args])
+- end;
+-write_event(_, _) ->
+- ok.
++format(#st{depth=unlimited}, Format, Args) ->
++ io_lib:format(Format, Args);
++format(#st{depth=Depth}, Format0, Args) ->
++ Format1 = io_lib:scan_format(Format0, Args),
++ Format = limit_format(Format1, Depth),
++ io_lib:build_text(Format).
++
++limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p;
++ C0 =:= $w ->
++ C = C0 - ($a - $A), %To uppercase.
++ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}||limit_format(T, Depth)];
++limit_format([H|T], Depth) ->
++ [H|limit_format(T, Depth)];
++limit_format([], _) ->
++ [].
++
++parse_event({error, _GL, {Pid, Format, Args}}) ->
++ {"ERROR REPORT",Pid,[{Format,Args}]};
++parse_event({info_msg, _GL, {Pid, Format, Args}}) ->
++ {"INFO REPORT",Pid,[{Format, Args}]};
++parse_event({warning_msg, _GL, {Pid, Format, Args}}) ->
++ {"WARNING REPORT",Pid,[{Format,Args}]};
++parse_event({error_report, _GL, {Pid, std_error, Args}}) ->
++ {"ERROR REPORT",Pid,format_term(Args)};
++parse_event({info_report, _GL, {Pid, std_info, Args}}) ->
++ {"INFO REPORT",Pid,format_term(Args)};
++parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
++ {"WARNING REPORT",Pid,format_term(Args)};
++parse_event(_) -> ignore.
+
+ maybe_utc(Time) ->
+ UTC = case application:get_env(sasl, utc_log) of
+@@ -182,30 +184,27 @@ maybe_utc(Time) ->
+ maybe_utc(Time, true) -> {utc, Time};
+ maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}.
+
+-format_report(Rep) when is_list(Rep) ->
+- case string_p(Rep) of
++format_term(Term) when is_list(Term) ->
++ case string_p(Term) of
+ true ->
+- io_lib:format("~s~n",[Rep]);
+- _ ->
+- format_rep(Rep)
++ [{"~s\n",[Term]}];
++ false ->
++ format_term_list(Term)
+ end;
+-format_report(Rep) ->
+- io_lib:format("~p~n",[Rep]).
+-
+-format_rep([{Tag,Data}|Rep]) ->
+- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
+-format_rep([Other|Rep]) ->
+- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep);
+-format_rep(_) ->
++format_term(Term) ->
++ [{"~p\n",[Term]}].
++
++format_term_list([{Tag,Data}|T]) ->
++ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
++format_term_list([Data|T]) ->
++ [{" ~p\n",[Data]}|format_term_list(T)];
++format_term_list([]) ->
++ [];
++format_term_list(_) ->
++ %% Continue to allow non-proper lists for now.
++ %% FIXME: Remove this clause in OTP 19.
+ [].
+
+-add_node(X, Pid) when is_atom(X) ->
+- add_node(atom_to_list(X), Pid);
+-add_node(X, Pid) when node(Pid) =/= node() ->
+- lists:concat([X,"** at node ",node(Pid)," **~n"]);
+-add_node(X, _) ->
+- X.
+-
+ string_p([]) ->
+ false;
+ string_p(Term) ->
+@@ -221,15 +220,10 @@ string_p1([$\b|T]) -> string_p1(T);
+ string_p1([$\f|T]) -> string_p1(T);
+ string_p1([$\e|T]) -> string_p1(T);
+ string_p1([H|T]) when is_list(H) ->
+- case string_p1(H) of
+- true -> string_p1(T);
+- _ -> false
+- end;
++ string_p1(H) andalso string_p1(T);
+ string_p1([]) -> true;
+ string_p1(_) -> false.
+
+-write_time(Time) -> write_time(Time, "ERROR REPORT").
+-
+ write_time({utc,{{Y,Mo,D},{H,Mi,S}}}, Type) ->
+ io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
+ [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
+diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl
+index ad5891f..72a052f 100644
+--- a/lib/stdlib/src/error_logger_tty_h.erl
++++ b/lib/stdlib/src/error_logger_tty_h.erl
+@@ -22,144 +22,178 @@
+
+ %%%
+ %%% A handler that can be connected to the error_logger
+-%%% event handler.
+-%%% Writes all events formatted to stdout.
+-%%% Handles events tagged error, emulator and info.
++%%% event handler. Writes all events formatted to stdout.
+ %%%
+ %%% It can only be started from error_logger:swap_handler(tty)
+-%%% or error_logger:tty(true)
++%%% or error_logger:tty(true).
+ %%%
+
+ -export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+--export([write_event/2]).
++-export([write_event/2,write_event/3]).
++
++-record(st,
++ {user,
++ prev_handler,
++ io_mod=io,
++ depth=unlimited}).
+
+ %% This one is used when we takeover from the simple error_logger.
+ init({[], {error_logger, Buf}}) ->
+ User = set_group_leader(),
+- write_events(Buf,io),
+- {ok, {User, error_logger}};
++ Depth = get_depth(),
++ State = #st{user=User,prev_handler=error_logger,depth=Depth},
++ write_events(State, Buf),
++ {ok, State};
+ %% This one is used if someone took over from us, and now wants to
+ %% go back.
+ init({[], {error_logger_tty_h, PrevHandler}}) ->
+ User = set_group_leader(),
+- {ok, {User, PrevHandler}};
++ {ok, #st{user=User,prev_handler=PrevHandler}};
+ %% This one is used when we are started directly.
+ init([]) ->
+ User = set_group_leader(),
+- {ok, {User, []}}.
++ Depth = get_depth(),
++ {ok, #st{user=User,prev_handler=[],depth=Depth}}.
++
++get_depth() ->
++ case application:get_env(kernel, error_logger_format_depth) of
++ {ok, Depth} when is_integer(Depth) ->
++ max(10, Depth);
++ undefined ->
++ unlimited
++ end.
+
+ handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
+ {ok, State};
+ handle_event(Event, State) ->
+- write_event(tag_event(Event),io),
++ ok = do_write_event(State, tag_event(Event)),
+ {ok, State}.
+
+-handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) ->
++handle_info({'EXIT', User, _Reason},
++ #st{user=User,prev_handler=PrevHandler}=State) ->
+ case PrevHandler of
+ [] ->
+ remove_handler;
+ _ ->
+- {swap_handler, install_prev, {User, PrevHandler},
++ {swap_handler, install_prev, State,
+ PrevHandler, go_back}
+ end;
+-handle_info({emulator, GL, Chars}, State) when node(GL) == node() ->
+- write_event(tag_event({emulator, GL, Chars}),io),
+- {ok, State};
+-handle_info({emulator, noproc, Chars}, State) ->
+- write_event(tag_event({emulator, noproc, Chars}),io),
+- {ok, State};
+ handle_info(_, State) ->
+ {ok, State}.
+
+ handle_call(_Query, State) -> {ok, {error, bad_query}, State}.
+
+-% unfortunately, we can't unlink from User - links are not counted!
+-% if pid(User) -> unlink(User); true -> ok end,
+ terminate(install_prev, _State) ->
+ [];
+-terminate(_Reason, {_User, PrevHandler}) ->
++terminate(_Reason, #st{prev_handler=PrevHandler}) ->
+ {error_logger_tty_h, PrevHandler}.
+
+ code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
++%% Exported (but unoffical) API.
++write_event(Event, IoMod) ->
++ do_write_event(#st{io_mod=IoMod}, Event).
++
++write_event(Event, IoMod, Depth) ->
++ do_write_event(#st{io_mod=IoMod,depth=Depth}, Event).
++
++
+ %%% ------------------------------------------------------
+ %%% Misc. functions.
+ %%% ------------------------------------------------------
+
+ set_group_leader() ->
+ case whereis(user) of
+- User when is_pid(User) -> link(User), group_leader(User,self()), User;
+- _ -> false
++ User when is_pid(User) ->
++ link(User),
++ group_leader(User,self()),
++ User;
++ _ ->
++ false
+ end.
+
+ tag_event(Event) ->
+ {erlang:universaltime(), Event}.
+
+-write_events(Events,IOMod) -> write_events1(lists:reverse(Events),IOMod).
+-
+-write_events1([Event|Es],IOMod) ->
+- write_event(Event,IOMod),
+- write_events1(Es,IOMod);
+-write_events1([],_IOMod) ->
++write_events(State, [Ev|Es]) ->
++ %% Write the events in reverse order.
++ _ = write_events(State, Es),
++ _ = do_write_event(State, Ev),
++ ok;
++write_events(_State, []) ->
+ ok.
+
+-write_event({Time, {error, _GL, {Pid, Format, Args}}},IOMod) ->
+- T = write_time(maybe_utc(Time)),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- format(IOMod, T ++ S);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- format(IOMod, T ++ F, [Format,Args])
++do_write_event(State, {Time0, Event}) ->
++ case parse_event(Event) of
++ ignore ->
++ ok;
++ {Head,Pid,FormatList} ->
++ Time = maybe_utc(Time0),
++ Header = write_time(Time, Head),
++ Body = format_body(State, FormatList),
++ AtNode = if
++ node(Pid) =/= node() ->
++ ["** at node ",atom_to_list(node(Pid))," **\n"];
++ true ->
++ []
++ end,
++ Str = [Header,Body,AtNode],
++ case State#st.io_mod of
++ io_lib ->
++ Str;
++ io ->
++ io:put_chars(user, Str)
++ end
+ end;
+-write_event({Time, {emulator, _GL, Chars}},IOMod) ->
+- T = write_time(maybe_utc(Time)),
+- case catch io_lib:format(Chars, []) of
+- S when is_list(S) ->
+- format(IOMod, T ++ S);
+- _ ->
+- format(IOMod, T ++ "ERROR: ~p ~n", [Chars])
+- end;
+-write_event({Time, {info, _GL, {Pid, Info, _}}},IOMod) ->
+- T = write_time(maybe_utc(Time)),
+- format(IOMod, T ++ add_node("~p~n",Pid),[Info]);
+-write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}},IOMod) ->
+- T = write_time(maybe_utc(Time)),
+- S = format_report(Rep),
+- format(IOMod, T ++ S ++ add_node("", Pid));
+-write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}},IOMod) ->
+- T = write_time(maybe_utc(Time), "INFO REPORT"),
+- S = format_report(Rep),
+- format(IOMod, T ++ S ++ add_node("", Pid));
+-write_event({Time, {info_msg, _GL, {Pid, Format, Args}}},IOMod) ->
+- T = write_time(maybe_utc(Time), "INFO REPORT"),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- format(IOMod, T ++ S);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- format(IOMod, T ++ F, [Format,Args])
+- end;
+-write_event({Time, {warning_report, _GL, {Pid, std_warning, Rep}}},IOMod) ->
+- T = write_time(maybe_utc(Time), "WARNING REPORT"),
+- S = format_report(Rep),
+- format(IOMod, T ++ S ++ add_node("", Pid));
+-write_event({Time, {warning_msg, _GL, {Pid, Format, Args}}},IOMod) ->
+- T = write_time(maybe_utc(Time), "WARNING REPORT"),
+- case catch io_lib:format(add_node(Format,Pid), Args) of
+- S when is_list(S) ->
+- format(IOMod, T ++ S);
+- _ ->
+- F = add_node("ERROR: ~p - ~p~n", Pid),
+- format(IOMod, T ++ F, [Format,Args])
+- end;
+-write_event({_Time, _Error},_IOMod) ->
++do_write_event(_, _) ->
+ ok.
+
++format_body(State, [{Format,Args}|T]) ->
++ S = try format(State, Format, Args) of
++ S0 ->
++ S0
++ catch
++ _:_ ->
++ format(State, "ERROR: ~p - ~p\n", [Format,Args])
++ end,
++ [S|format_body(State, T)];
++format_body(_State, []) ->
++ [].
++
++format(#st{depth=unlimited}, Format, Args) ->
++ io_lib:format(Format, Args);
++format(#st{depth=Depth}, Format0, Args) ->
++ Format1 = io_lib:scan_format(Format0, Args),
++ Format = limit_format(Format1, Depth),
++ io_lib:build_text(Format).
++
++limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p;
++ C0 =:= $w ->
++ C = C0 - ($a - $A), %To uppercase.
++ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}|limit_format(T, Depth)];
++limit_format([H|T], Depth) ->
++ [H|limit_format(T, Depth)];
++limit_format([], _) ->
++ [].
++
++parse_event({error, _GL, {Pid, Format, Args}}) ->
++ {"ERROR REPORT",Pid,[{Format,Args}]};
++parse_event({info_msg, _GL, {Pid, Format, Args}}) ->
++ {"INFO REPORT",Pid,[{Format, Args}]};
++parse_event({warning_msg, _GL, {Pid, Format, Args}}) ->
++ {"WARNING REPORT",Pid,[{Format,Args}]};
++parse_event({error_report, _GL, {Pid, std_error, Args}}) ->
++ {"ERROR REPORT",Pid,format_term(Args)};
++parse_event({info_report, _GL, {Pid, std_info, Args}}) ->
++ {"INFO REPORT",Pid,format_term(Args)};
++parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
++ {"WARNING REPORT",Pid,format_term(Args)};
++parse_event(_) -> ignore.
++
+ maybe_utc(Time) ->
+ UTC = case application:get_env(sasl, utc_log) of
+ {ok, Val} -> Val;
+@@ -175,33 +209,26 @@ maybe_utc(Time) ->
+ maybe_utc(Time, true) -> {utc, Time};
+ maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}.
+
+-format(IOMod, String) -> format(IOMod, String, []).
+-format(io_lib, String, Args) -> io_lib:format(String, Args);
+-format(io, String, Args) -> io:format(user, String, Args).
+-
+-format_report(Rep) when is_list(Rep) ->
+- case string_p(Rep) of
++format_term(Term) when is_list(Term) ->
++ case string_p(Term) of
+ true ->
+- io_lib:format("~s~n",[Rep]);
+- _ ->
+- format_rep(Rep)
++ [{"~s\n",[Term]}];
++ false ->
++ format_term_list(Term)
+ end;
+-format_report(Rep) ->
+- io_lib:format("~p~n",[Rep]).
+-
+-format_rep([{Tag,Data}|Rep]) ->
+- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
+-format_rep([Other|Rep]) ->
+- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep);
+-format_rep(_) ->
+- [].
++format_term(Term) ->
++ [{"~p\n",[Term]}].
+
+-add_node(X, Pid) when is_atom(X) ->
+- add_node(atom_to_list(X), Pid);
+-add_node(X, Pid) when node(Pid) =/= node() ->
+- lists:concat([X,"** at node ",node(Pid)," **~n"]);
+-add_node(X, _) ->
+- X.
++format_term_list([{Tag,Data}|T]) ->
++ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
++format_term_list([Data|T]) ->
++ [{" ~p\n",[Data]}|format_term_list(T)];
++format_term_list([]) ->
++ [];
++format_term_list(_) ->
++ %% Continue to allow non-proper lists for now.
++ %% FIXME: Remove this clause in OTP 19.
++ [].
+
+ string_p([]) ->
+ false;
+@@ -225,7 +252,6 @@ string_p1([H|T]) when is_list(H) ->
+ string_p1([]) -> true;
+ string_p1(_) -> false.
+
+-write_time(Time) -> write_time(Time, "ERROR REPORT").
+ write_time({utc,{{Y,Mo,D},{H,Mi,S}}},Type) ->
+ io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
+ [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
+diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
+index 9e69601..3c173dc 100644
+--- a/lib/stdlib/src/io_lib.erl
++++ b/lib/stdlib/src/io_lib.erl
+@@ -2,7 +2,7 @@
+ %%
+ %% %CopyrightBegin%
+ %%
+-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
++%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+ %%
+ %% The contents of this file are subject to the Erlang Public License,
+ %% Version 1.1, (the "License"); you may not use this file except in
+@@ -61,6 +61,7 @@
+ -module(io_lib).
+
+ -export([fwrite/2,fread/2,fread/3,format/2]).
++-export([scan_format/2,unscan_format/1,build_text/1]).
+ -export([print/1,print/4,indentation/2]).
+
+ -export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]).
+@@ -84,7 +85,7 @@
+ deep_unicode_char_list/1]).
+
+ -export_type([chars/0, latin1_string/0, continuation/0,
+- fread_error/0, fread_item/0]).
++ fread_error/0, fread_item/0, format_spec/0]).
+
+ %%----------------------------------------------------------------------
+
+@@ -109,6 +110,18 @@
+
+ -type fread_item() :: string() | atom() | integer() | float().
+
++-type format_spec() ::
++ {
++ ControlChar :: char(),
++ Args :: [any()],
++ Width :: 'none' | integer(),
++ Adjust :: 'left' | 'right',
++ Precision :: 'none' | integer(),
++ PadChar :: char(),
++ Encoding :: 'unicode' | 'latin1',
++ Strings :: boolean()
++ }.
++
+ %%----------------------------------------------------------------------
+
+ %% Interface calls to sub-modules.
+@@ -157,6 +170,31 @@ format(Format, Args) ->
+ Other
+ end.
+
++-spec scan_format(Format, Data) -> FormatList when
++ Format :: io:format(),
++ Data :: [term()],
++ FormatList :: [char() | format_spec()].
++
++scan_format(Format, Args) ->
++ try io_lib_format:scan(Format, Args)
++ catch
++ _:_ -> erlang:error(badarg, [Format, Args])
++ end.
++
++-spec unscan_format(FormatList) -> {Format, Data} when
++ FormatList :: [char() | format_spec()],
++ Format :: io:format(),
++ Data :: [term()].
++
++unscan_format(FormatList) ->
++ io_lib_format:unscan(FormatList).
++
++-spec build_text(FormatList) -> chars() when
++ FormatList :: [char() | format_spec()].
++
++build_text(FormatList) ->
++ io_lib_format:build(FormatList).
++
+ -spec print(Term) -> chars() when
+ Term :: term().
+
+diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
+index 56e15a1..37b47d7 100644
+--- a/lib/stdlib/src/io_lib_format.erl
++++ b/lib/stdlib/src/io_lib_format.erl
+@@ -1,7 +1,7 @@
+ %%
+ %% %CopyrightBegin%
+ %%
+-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
++%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
+ %%
+ %% The contents of this file are subject to the Erlang Public License,
+ %% Version 1.1, (the "License"); you may not use this file except in
+@@ -20,10 +20,9 @@
+
+ %% Formatting functions of io library.
+
+--export([fwrite/2,fwrite_g/1,indentation/2]).
++-export([fwrite/2,fwrite_g/1,indentation/2,scan/2,unscan/1,build/1]).
+
+-%% fwrite(Format, ArgList) -> string().
+-%% Format the arguments in ArgList after string Format. Just generate
++%% Format the arguments in Args after string Format. Just generate
+ %% an error if there is an error in the arguments.
+ %%
+ %% To do the printing command correctly we need to calculate the
+@@ -37,15 +36,83 @@
+ %% and it also splits the handling of the control characters into two
+ %% parts.
+
+-fwrite(Format, Args) when is_atom(Format) ->
+- fwrite(atom_to_list(Format), Args);
+-fwrite(Format, Args) when is_binary(Format) ->
+- fwrite(binary_to_list(Format), Args);
++-spec fwrite(Format, Data) -> FormatList when
++ Format :: io:format(),
++ Data :: [term()],
++ FormatList :: [char() | io_lib:format_spec()].
++
+ fwrite(Format, Args) ->
+- Cs = collect(Format, Args),
++ build(scan(Format, Args)).
++
++%% Build the output text for a pre-parsed format list.
++
++-spec build(FormatList) -> io_lib:chars() when
++ FormatList :: [char() | io_lib:format_spec()].
++
++build(Cs) ->
+ Pc = pcount(Cs),
+ build(Cs, Pc, 0).
+
++%% Parse all control sequences in the format string.
++
++-spec scan(Format, Data) -> FormatList when
++ Format :: io:format(),
++ Data :: [term()],
++ FormatList :: [char() | io_lib:format_spec()].
++
++scan(Format, Args) when is_atom(Format) ->
++ scan(atom_to_list(Format), Args);
++scan(Format, Args) when is_binary(Format) ->
++ scan(binary_to_list(Format), Args);
++scan(Format, Args) ->
++ collect(Format, Args).
++
++%% Revert a pre-parsed format list to a plain character list and a
++%% list of arguments.
++
++-spec unscan(FormatList) -> {Format, Data} when
++ FormatList :: [char() | io_lib:format_spec()],
++ Format :: io:format(),
++ Data :: [term()].
++
++unscan(Cs) ->
++ {print(Cs), args(Cs)}.
++
++args([{_C,As,_F,_Ad,_P,_Pad,_Enc,_Str} | Cs]) ->
++ As ++ args(Cs);
++args([_C | Cs]) ->
++ args(Cs);
++args([]) ->
++ [].
++
++print([{C,_As,F,Ad,P,Pad,Enc,Str} | Cs ]) ->
++ print(C, F, Ad, P, Pad, Enc, Str) ++ print(Cs);
++print([C | Cs]) ->
++ [C | print(Cs)];
++print([]) ->
++ [].
++
++print(C, F, Ad, P, Pad, Encoding, Strings) ->
++ [$~] ++ print_field_width(F, Ad) ++ print_precision(P) ++
++ print_pad_char(Pad) ++ print_encoding(Encoding) ++
++ print_strings(Strings) ++ [C].
++
++print_field_width(none, _Ad) -> "";
++print_field_width(F, left) -> integer_to_list(-F);
++print_field_width(F, right) -> integer_to_list(F).
++
++print_precision(none) -> "";
++print_precision(P) -> [$. | integer_to_list(P)].
++
++print_pad_char($\s) -> ""; % default, no need to make explicit
++print_pad_char(Pad) -> [$., Pad].
++
++print_encoding(unicode) -> "t";
++print_encoding(latin1) -> "".
++
++print_strings(false) -> "l";
++print_strings(true) -> "".
++
+ collect([$~|Fmt0], Args0) ->
+ {C,Fmt1,Args1} = collect_cseq(Fmt0, Args0),
+ [C|collect(Fmt1, Args1)];
+@@ -141,7 +208,7 @@ pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1);
+ pcount([_|Cs], Acc) -> pcount(Cs, Acc);
+ pcount([], Acc) -> Acc.
+
+-%% build([Control], Pc, Indentation) -> string().
++%% build([Control], Pc, Indentation) -> io_lib:chars().
+ %% Interpret the control structures. Count the number of print
+ %% remaining and only calculate indentation when necessary. Must also
+ %% be smart when calculating indentation for characters in format.
+@@ -162,10 +229,14 @@ decr_pc($p, Pc) -> Pc - 1;
+ decr_pc($P, Pc) -> Pc - 1;
+ decr_pc(_, Pc) -> Pc.
+
+-%% indentation(String, Indentation) -> Indentation.
++
+ %% Calculate the indentation of the end of a string given its start
+ %% indentation. We assume tabs at 8 cols.
+
++-spec indentation(String, StartIndent) -> integer() when
++ String :: io_lib:chars(),
++ StartIndent :: integer().
++
+ indentation([$\n|Cs], _I) -> indentation(Cs, 0);
+ indentation([$\t|Cs], I) -> indentation(Cs, ((I + 8) div 8) * 8);
+ indentation([C|Cs], I) when is_integer(C) ->
+@@ -366,7 +437,6 @@ float_data([D|Cs], Ds) when D >= $0, D =< $9 ->
+ float_data([_|Cs], Ds) ->
+ float_data(Cs, Ds).
+
+-%% fwrite_g(Float)
+ %% Writes the shortest, correctly rounded string that converts
+ %% to Float when read back with list_to_float/1.
+ %%
+@@ -374,6 +444,8 @@ float_data([_|Cs], Ds) ->
+ %% in Proceedings of the SIGPLAN '96 Conference on Programming
+ %% Language Design and Implementation.
+
++-spec fwrite_g(float()) -> string().
++
+ fwrite_g(0.0) ->
+ "0.0";
+ fwrite_g(Float) when is_float(Float) ->
+@@ -642,7 +714,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase)
+ term([Prefix|S], F, Adj, none, Pad)
+ end.
+
+-%% char(Char, Field, Adjust, Precision, PadChar) -> string().
++%% char(Char, Field, Adjust, Precision, PadChar) -> chars().
+
+ char(C, none, _Adj, none, _Pad) -> [C];
+ char(C, F, _Adj, none, _Pad) -> chars(C, F);
diff --git a/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch b/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch
new file mode 100644
index 0000000..e9e6450
--- /dev/null
+++ b/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch
@@ -0,0 +1,74 @@
+From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?=
+Date: Tue, 25 Aug 2015 15:20:23 +0200
+Subject: [PATCH] Teach sasl_report to limit crash reports
+
+
+diff --git a/lib/sasl/src/sasl_report.erl b/lib/sasl/src/sasl_report.erl
+index c3e6fed..aa84e4f 100644
+--- a/lib/sasl/src/sasl_report.erl
++++ b/lib/sasl/src/sasl_report.erl
+@@ -61,27 +61,53 @@ write_report2(IO, Fd, Head, supervisor_report, Report) ->
+ Context = sup_get(errorContext, Report),
+ Reason = sup_get(reason, Report),
+ Offender = sup_get(offender, Report),
+- FmtString = " Supervisor: ~p~n Context: ~p~n Reason: "
+- "~80.18p~n Offender: ~80.18p~n~n",
+- write_report_action(IO, Fd, Head ++ FmtString,
+- [Name,Context,Reason,Offender]);
++ {FmtString,Args} = supervisor_format([Name,Context,Reason,Offender]),
++ write_report_action(IO, Fd, Head, FmtString, Args);
+ write_report2(IO, Fd, Head, progress, Report) ->
+ Format = format_key_val(Report),
+- write_report_action(IO, Fd, Head ++ "~s", [Format]);
++ write_report_action(IO, Fd, Head, "~s", [Format]);
+ write_report2(IO, Fd, Head, crash_report, Report) ->
+- Format = proc_lib:format(Report),
+- write_report_action(IO, Fd, Head ++ "~s", [Format]).
++ Depth = get_depth(),
++ Format = proc_lib:format(Report, latin1, Depth),
++ write_report_action(IO, Fd, Head, "~s", [Format]).
++
++supervisor_format(Args0) ->
++ case get_depth() of
++ unlimited ->
++ {" Supervisor: ~p~n"
++ " Context: ~p~n"
++ " Reason: ~80.18p~n"
++ " Offender: ~80.18p~n~n",
++ Args0};
++ Depth ->
++ [A,B,C,D] = Args0,
++ Args = [A,Depth,B,Depth,C,Depth,D,Depth],
++ {" Supervisor: ~P~n"
++ " Context: ~P~n"
++ " Reason: ~80.18P~n"
++ " Offender: ~80.18P~n~n",
++ Args}
++ end.
+
+-write_report_action(io, Fd, Format, Args) ->
+- io:format(Fd, Format, Args);
+-write_report_action(io_lib, _Fd, Format, Args) ->
+- io_lib:format(Format, Args).
++write_report_action(IO, Fd, Head, Format, Args) ->
++ S = [Head|io_lib:format(Format, Args)],
++ case IO of
++ io -> io:put_chars(Fd, S);
++ io_lib -> S
++ end.
+
+ format_key_val([{Tag,Data}|Rep]) ->
+ io_lib:format(" ~16w: ~p~n",[Tag,Data]) ++ format_key_val(Rep);
+ format_key_val(_) ->
+ [].
+
++get_depth() ->
++ case application:get_env(kernel, error_logger_format_depth) of
++ {ok, Depth} when is_integer(Depth) ->
++ max(10, Depth);
++ undefined ->
++ unlimited
++ end.
+
+ sup_get(Tag, Report) ->
+ case lists:keysearch(Tag, 1, Report) of
diff --git a/otp-0026-Revert-Remove-unused-code-in-error-logger-handlers.patch b/otp-0026-Revert-Remove-unused-code-in-error-logger-handlers.patch
new file mode 100644
index 0000000..dda297d
--- /dev/null
+++ b/otp-0026-Revert-Remove-unused-code-in-error-logger-handlers.patch
@@ -0,0 +1,1064 @@
+From: Peter Lemenkov
+Date: Wed, 9 Nov 2016 17:28:28 +0300
+Subject: [PATCH] Revert "Remove unused code in error logger handlers"
+
+This reverts commit f022259a41dbf5b078bea38fd3a7b98f0d4ed587.
+
+Revert "Teach sasl_report to limit crash reports"
+
+This reverts commit 33254666c90aedb190c6cffe36a027053918d92c.
+
+diff --git a/lib/sasl/src/sasl_report.erl b/lib/sasl/src/sasl_report.erl
+index aa84e4f..c3e6fed 100644
+--- a/lib/sasl/src/sasl_report.erl
++++ b/lib/sasl/src/sasl_report.erl
+@@ -61,53 +61,27 @@ write_report2(IO, Fd, Head, supervisor_report, Report) ->
+ Context = sup_get(errorContext, Report),
+ Reason = sup_get(reason, Report),
+ Offender = sup_get(offender, Report),
+- {FmtString,Args} = supervisor_format([Name,Context,Reason,Offender]),
+- write_report_action(IO, Fd, Head, FmtString, Args);
++ FmtString = " Supervisor: ~p~n Context: ~p~n Reason: "
++ "~80.18p~n Offender: ~80.18p~n~n",
++ write_report_action(IO, Fd, Head ++ FmtString,
++ [Name,Context,Reason,Offender]);
+ write_report2(IO, Fd, Head, progress, Report) ->
+ Format = format_key_val(Report),
+- write_report_action(IO, Fd, Head, "~s", [Format]);
++ write_report_action(IO, Fd, Head ++ "~s", [Format]);
+ write_report2(IO, Fd, Head, crash_report, Report) ->
+- Depth = get_depth(),
+- Format = proc_lib:format(Report, latin1, Depth),
+- write_report_action(IO, Fd, Head, "~s", [Format]).
+-
+-supervisor_format(Args0) ->
+- case get_depth() of
+- unlimited ->
+- {" Supervisor: ~p~n"
+- " Context: ~p~n"
+- " Reason: ~80.18p~n"
+- " Offender: ~80.18p~n~n",
+- Args0};
+- Depth ->
+- [A,B,C,D] = Args0,
+- Args = [A,Depth,B,Depth,C,Depth,D,Depth],
+- {" Supervisor: ~P~n"
+- " Context: ~P~n"
+- " Reason: ~80.18P~n"
+- " Offender: ~80.18P~n~n",
+- Args}
+- end.
++ Format = proc_lib:format(Report),
++ write_report_action(IO, Fd, Head ++ "~s", [Format]).
+
+-write_report_action(IO, Fd, Head, Format, Args) ->
+- S = [Head|io_lib:format(Format, Args)],
+- case IO of
+- io -> io:put_chars(Fd, S);
+- io_lib -> S
+- end.
++write_report_action(io, Fd, Format, Args) ->
++ io:format(Fd, Format, Args);
++write_report_action(io_lib, _Fd, Format, Args) ->
++ io_lib:format(Format, Args).
+
+ format_key_val([{Tag,Data}|Rep]) ->
+ io_lib:format(" ~16w: ~p~n",[Tag,Data]) ++ format_key_val(Rep);
+ format_key_val(_) ->
+ [].
+
+-get_depth() ->
+- case application:get_env(kernel, error_logger_format_depth) of
+- {ok, Depth} when is_integer(Depth) ->
+- max(10, Depth);
+- undefined ->
+- unlimited
+- end.
+
+ sup_get(Tag, Report) ->
+ case lists:keysearch(Tag, 1, Report) of
+diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml
+index 6613dcd..90f24c4 100644
+--- a/lib/stdlib/doc/src/io.xml
++++ b/lib/stdlib/doc/src/io.xml
+@@ -505,8 +505,7 @@ ok
+
Writes the data with standard syntax in the same way as
+ ~w, but breaks terms whose printed representation
+ is longer than one line into many lines and indents each
+- line sensibly. Left justification is not supported.
+- It also tries to detect lists of
++ line sensibly. It also tries to detect lists of
+ printable characters and to output these as strings. The
+ Unicode translation modifier is used for determining
+ what characters are printable. For example:
+diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml
+index 0c1c84d..68352ff 100644
+--- a/lib/stdlib/doc/src/io_lib.xml
++++ b/lib/stdlib/doc/src/io_lib.xml
+@@ -4,7 +4,7 @@
+
+
+
+- 19962014
++ 19962013
+ Ericsson AB. All Rights Reserved.
+
+
+@@ -59,35 +59,6 @@
+
+
+
+-
+-
+-
Description:
+-
+-
control_char is the type of control
+- sequence: $P, $w, and so on;
+-
+-
args is a list of the arguments used by the
+- control sequence, or an empty list if the control sequence
+- does not take any arguments;
+-
+-
width is the field width;
+-
+-
adjust is the adjustment;
+-
+-
precision is the precision of the printed
+- argument;
+-
+-
pad_char is the padding character;
+-
+-
encoding is set to true if the translation
+- modifier t is present;
+-
+-
strings is set to false if the modifier
+- l is present.
+-
+-
+-
+-
+
+
+
+@@ -289,45 +260,6 @@
+
+
+
+-
+- Parse all control sequences in the format string
+-
+-
Returns a list corresponding to the given format string,
+- where control sequences have been replaced with
+- corresponding tuples. This list can be passed to io_lib:build_text/1 to have
+- the same effect as io_lib:format(Format, Args), or to
+- io_lib:unscan_format/1
+- in order to get the corresponding pair of Format and
+- Args (with every * and corresponding argument
+- expanded to numeric values).
+-
A typical use of this function is to replace unbounded-size
+- control sequences like ~w and ~p with the
+- depth-limited variants ~W and ~P before
+- formatting to text, e.g. in a logger.
+-
+-
+-
+-
+- Revert a pre-parsed format list to a plain character list
+- and a list of arguments
+-
+-
See io_lib:scan_format/2 for
+- details.
+-
+-
+-
+-
+- Build the output text for a pre-parsed format list
+-
+-
See io_lib:scan_format/2 for
+- details.
+-
+-
+-
+
+ Indentation after printing string
+
+diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl
+index ca44d01..e9364ed 100644
+--- a/lib/stdlib/src/error_logger_file_h.erl
++++ b/lib/stdlib/src/error_logger_file_h.erl
+@@ -23,28 +23,24 @@
+
+ %%%
+ %%% A handler that can be connected to the error_logger
+-%%% event handler. Writes all events formatted to file.
++%%% event handler.
++%%% Writes all events formatted to file.
++%%% Handles events tagged error, emulator and info.
+ %%%
+ %%% It can only be started from error_logger:swap_handler({logfile, File})
+-%%% or error_logger:logfile(File).
++%%% or error_logger:logfile(File)
+ %%%
+
+ -export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+--record(st,
+- {fd,
+- filename,
+- prev_handler,
+- depth=unlimited :: 'unlimited' | non_neg_integer()}).
+-
+ %% This one is used when we takeover from the simple error_logger.
+ init({File, {error_logger, Buf}}) ->
+ case init(File, error_logger) of
+- {ok, State} ->
+- write_events(State, Buf),
+- {ok, State};
++ {ok, {Fd, File, PrevHandler}} ->
++ write_events(Fd, Buf),
++ {ok, {Fd, File, PrevHandler}};
+ Error ->
+ Error
+ end;
+@@ -56,45 +52,49 @@ init(File, PrevHandler) ->
+ process_flag(trap_exit, true),
+ case file:open(File, [write]) of
+ {ok,Fd} ->
+- Depth = get_depth(),
+- State = #st{fd=Fd,filename=File,prev_handler=PrevHandler,
+- depth=Depth},
+- {ok, State};
++ {ok, {Fd, File, PrevHandler}};
+ Error ->
+ Error
+ end.
+-
+-get_depth() ->
+- case application:get_env(kernel, error_logger_format_depth) of
+- {ok, Depth} when is_integer(Depth) ->
+- max(10, Depth);
+- undefined ->
+- unlimited
+- end.
+-
++
+ handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
+ {ok, State};
+-handle_event(Event, State) ->
+- write_event(State, Event),
++handle_event(Event, {Fd, File, PrevHandler}) ->
++ write_event(Fd, tag_event(Event)),
++ {ok, {Fd, File, PrevHandler}};
++handle_event(_, State) ->
+ {ok, State}.
+
+-handle_info({'EXIT', Fd, _Reason}, #st{fd=Fd,prev_handler=PrevHandler}) ->
++handle_info({'EXIT', Fd, _Reason}, {Fd, _File, PrevHandler}) ->
+ case PrevHandler of
+ [] ->
+ remove_handler;
+ _ ->
+ {swap_handler, install_prev, [], PrevHandler, go_back}
+ end;
++handle_info({emulator, GL, Chars}, {Fd, File, PrevHandler})
++ when node(GL) == node() ->
++ write_event(Fd, tag_event({emulator, GL, Chars})),
++ {ok, {Fd, File, PrevHandler}};
++handle_info({emulator, noproc, Chars}, {Fd, File, PrevHandler}) ->
++ write_event(Fd, tag_event({emulator, noproc, Chars})),
++ {ok, {Fd, File, PrevHandler}};
+ handle_info(_, State) ->
+ {ok, State}.
+
+-handle_call(filename, #st{filename=File}=State) ->
+- {ok, File, State};
++handle_call(filename, {Fd, File, Prev}) ->
++ {ok, File, {Fd, File, Prev}};
+ handle_call(_Query, State) ->
+ {ok, {error, bad_query}, State}.
+
+-terminate(_Reason, #st{fd=Fd}) ->
+- file:close(Fd).
++terminate(_Reason, State) ->
++ case State of
++ {Fd, _File, _Prev} ->
++ ok = file:close(Fd);
++ _ ->
++ ok
++ end,
++ [].
+
+ code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+@@ -103,71 +103,69 @@ code_change(_OldVsn, State, _Extra) ->
+ %%% Misc. functions.
+ %%% ------------------------------------------------------
+
+-write_events(State, [Ev|Es]) ->
+- %% Write the events in reversed order.
+- write_events(State, Es),
+- write_event(State, Ev);
+-write_events(_State, []) ->
+- ok.
++tag_event(Event) ->
++ {erlang:universaltime(), Event}.
+
+-write_event(#st{fd=Fd}=State, Event) ->
+- case parse_event(Event) of
+- ignore ->
+- ok;
+- {Head,Pid,FormatList} ->
+- Time = maybe_utc(erlang:universaltime()),
+- Header = write_time(Time, Head),
+- Body = format_body(State, FormatList),
+- AtNode = if
+- node(Pid) =/= node() ->
+- ["** at node ",atom_to_list(node(Pid))," **\n"];
+- true ->
+- []
+- end,
+- io:put_chars(Fd, [Header,Body,AtNode])
+- end.
++write_events(Fd, Events) -> write_events1(Fd, lists:reverse(Events)).
+
+-format_body(State, [{Format,Args}|T]) ->
+- S = try format(State, Format, Args) of
+- S0 ->
+- S0
+- catch
+- _:_ ->
+- format(State, "ERROR: ~p - ~p\n", [Format,Args])
+- end,
+- [S|format_body(State, T)];
+-format_body(_State, []) ->
+- [].
+-
+-format(#st{depth=unlimited}, Format, Args) ->
+- io_lib:format(Format, Args);
+-format(#st{depth=Depth}, Format0, Args) ->
+- Format1 = io_lib:scan_format(Format0, Args),
+- Format = limit_format(Format1, Depth),
+- io_lib:build_text(Format).
+-
+-limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p;
+- C0 =:= $w ->
+- C = C0 - ($a - $A), %To uppercase.
+- [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}||limit_format(T, Depth)];
+-limit_format([H|T], Depth) ->
+- [H|limit_format(T, Depth)];
+-limit_format([], _) ->
+- [].
++write_events1(Fd, [Event|Es]) ->
++ write_event(Fd, Event),
++ write_events1(Fd, Es);
++write_events1(_, []) ->
++ ok.
+
+-parse_event({error, _GL, {Pid, Format, Args}}) ->
+- {"ERROR REPORT",Pid,[{Format,Args}]};
+-parse_event({info_msg, _GL, {Pid, Format, Args}}) ->
+- {"INFO REPORT",Pid,[{Format, Args}]};
+-parse_event({warning_msg, _GL, {Pid, Format, Args}}) ->
+- {"WARNING REPORT",Pid,[{Format,Args}]};
+-parse_event({error_report, _GL, {Pid, std_error, Args}}) ->
+- {"ERROR REPORT",Pid,format_term(Args)};
+-parse_event({info_report, _GL, {Pid, std_info, Args}}) ->
+- {"INFO REPORT",Pid,format_term(Args)};
+-parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
+- {"WARNING REPORT",Pid,format_term(Args)};
+-parse_event(_) -> ignore.
++write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) ->
++ T = write_time(maybe_utc(Time)),
++ case catch io_lib:format(add_node(Format,Pid), Args) of
++ S when is_list(S) ->
++ io:format(Fd, T ++ S, []);
++ _ ->
++ F = add_node("ERROR: ~p - ~p~n", Pid),
++ io:format(Fd, T ++ F, [Format,Args])
++ end;
++write_event(Fd, {Time, {emulator, _GL, Chars}}) ->
++ T = write_time(maybe_utc(Time)),
++ case catch io_lib:format(Chars, []) of
++ S when is_list(S) ->
++ io:format(Fd, T ++ S, []);
++ _ ->
++ io:format(Fd, T ++ "ERROR: ~p ~n", [Chars])
++ end;
++write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) ->
++ T = write_time(maybe_utc(Time)),
++ io:format(Fd, T ++ add_node("~p~n",Pid),[Info]);
++write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) ->
++ T = write_time(maybe_utc(Time)),
++ S = format_report(Rep),
++ io:format(Fd, T ++ S ++ add_node("", Pid), []);
++write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) ->
++ T = write_time(maybe_utc(Time), "INFO REPORT"),
++ S = format_report(Rep),
++ io:format(Fd, T ++ S ++ add_node("", Pid), []);
++write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) ->
++ T = write_time(maybe_utc(Time), "INFO REPORT"),
++ case catch io_lib:format(add_node(Format,Pid), Args) of
++ S when is_list(S) ->
++ io:format(Fd, T ++ S, []);
++ _ ->
++ F = add_node("ERROR: ~p - ~p~n", Pid),
++ io:format(Fd, T ++ F, [Format,Args])
++ end;
++write_event(Fd, {Time, {warning_report, _GL, {Pid, std_warning, Rep}}}) ->
++ T = write_time(maybe_utc(Time), "WARNING REPORT"),
++ S = format_report(Rep),
++ io:format(Fd, T ++ S ++ add_node("", Pid), []);
++write_event(Fd, {Time, {warning_msg, _GL, {Pid, Format, Args}}}) ->
++ T = write_time(maybe_utc(Time), "WARNING REPORT"),
++ case catch io_lib:format(add_node(Format,Pid), Args) of
++ S when is_list(S) ->
++ io:format(Fd, T ++ S, []);
++ _ ->
++ F = add_node("ERROR: ~p - ~p~n", Pid),
++ io:format(Fd, T ++ F, [Format,Args])
++ end;
++write_event(_, _) ->
++ ok.
+
+ maybe_utc(Time) ->
+ UTC = case application:get_env(sasl, utc_log) of
+@@ -184,27 +182,30 @@ maybe_utc(Time) ->
+ maybe_utc(Time, true) -> {utc, Time};
+ maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}.
+
+-format_term(Term) when is_list(Term) ->
+- case string_p(Term) of
++format_report(Rep) when is_list(Rep) ->
++ case string_p(Rep) of
+ true ->
+- [{"~s\n",[Term]}];
+- false ->
+- format_term_list(Term)
++ io_lib:format("~s~n",[Rep]);
++ _ ->
++ format_rep(Rep)
+ end;
+-format_term(Term) ->
+- [{"~p\n",[Term]}].
+-
+-format_term_list([{Tag,Data}|T]) ->
+- [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
+-format_term_list([Data|T]) ->
+- [{" ~p\n",[Data]}|format_term_list(T)];
+-format_term_list([]) ->
+- [];
+-format_term_list(_) ->
+- %% Continue to allow non-proper lists for now.
+- %% FIXME: Remove this clause in OTP 19.
++format_report(Rep) ->
++ io_lib:format("~p~n",[Rep]).
++
++format_rep([{Tag,Data}|Rep]) ->
++ io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
++format_rep([Other|Rep]) ->
++ io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep);
++format_rep(_) ->
+ [].
+
++add_node(X, Pid) when is_atom(X) ->
++ add_node(atom_to_list(X), Pid);
++add_node(X, Pid) when node(Pid) =/= node() ->
++ lists:concat([X,"** at node ",node(Pid)," **~n"]);
++add_node(X, _) ->
++ X.
++
+ string_p([]) ->
+ false;
+ string_p(Term) ->
+@@ -220,10 +221,15 @@ string_p1([$\b|T]) -> string_p1(T);
+ string_p1([$\f|T]) -> string_p1(T);
+ string_p1([$\e|T]) -> string_p1(T);
+ string_p1([H|T]) when is_list(H) ->
+- string_p1(H) andalso string_p1(T);
++ case string_p1(H) of
++ true -> string_p1(T);
++ _ -> false
++ end;
+ string_p1([]) -> true;
+ string_p1(_) -> false.
+
++write_time(Time) -> write_time(Time, "ERROR REPORT").
++
+ write_time({utc,{{Y,Mo,D},{H,Mi,S}}}, Type) ->
+ io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
+ [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
+diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl
+index 72a052f..ad5891f 100644
+--- a/lib/stdlib/src/error_logger_tty_h.erl
++++ b/lib/stdlib/src/error_logger_tty_h.erl
+@@ -22,178 +22,144 @@
+
+ %%%
+ %%% A handler that can be connected to the error_logger
+-%%% event handler. Writes all events formatted to stdout.
++%%% event handler.
++%%% Writes all events formatted to stdout.
++%%% Handles events tagged error, emulator and info.
+ %%%
+ %%% It can only be started from error_logger:swap_handler(tty)
+-%%% or error_logger:tty(true).
++%%% or error_logger:tty(true)
+ %%%
+
+ -export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+--export([write_event/2,write_event/3]).
+-
+--record(st,
+- {user,
+- prev_handler,
+- io_mod=io,
+- depth=unlimited}).
++-export([write_event/2]).
+
+ %% This one is used when we takeover from the simple error_logger.
+ init({[], {error_logger, Buf}}) ->
+ User = set_group_leader(),
+- Depth = get_depth(),
+- State = #st{user=User,prev_handler=error_logger,depth=Depth},
+- write_events(State, Buf),
+- {ok, State};
++ write_events(Buf,io),
++ {ok, {User, error_logger}};
+ %% This one is used if someone took over from us, and now wants to
+ %% go back.
+ init({[], {error_logger_tty_h, PrevHandler}}) ->
+ User = set_group_leader(),
+- {ok, #st{user=User,prev_handler=PrevHandler}};
++ {ok, {User, PrevHandler}};
+ %% This one is used when we are started directly.
+ init([]) ->
+ User = set_group_leader(),
+- Depth = get_depth(),
+- {ok, #st{user=User,prev_handler=[],depth=Depth}}.
+-
+-get_depth() ->
+- case application:get_env(kernel, error_logger_format_depth) of
+- {ok, Depth} when is_integer(Depth) ->
+- max(10, Depth);
+- undefined ->
+- unlimited
+- end.
++ {ok, {User, []}}.
+
+ handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() ->
+ {ok, State};
+ handle_event(Event, State) ->
+- ok = do_write_event(State, tag_event(Event)),
++ write_event(tag_event(Event),io),
+ {ok, State}.
+
+-handle_info({'EXIT', User, _Reason},
+- #st{user=User,prev_handler=PrevHandler}=State) ->
++handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) ->
+ case PrevHandler of
+ [] ->
+ remove_handler;
+ _ ->
+- {swap_handler, install_prev, State,
++ {swap_handler, install_prev, {User, PrevHandler},
+ PrevHandler, go_back}
+ end;
++handle_info({emulator, GL, Chars}, State) when node(GL) == node() ->
++ write_event(tag_event({emulator, GL, Chars}),io),
++ {ok, State};
++handle_info({emulator, noproc, Chars}, State) ->
++ write_event(tag_event({emulator, noproc, Chars}),io),
++ {ok, State};
+ handle_info(_, State) ->
+ {ok, State}.
+
+ handle_call(_Query, State) -> {ok, {error, bad_query}, State}.
+
++% unfortunately, we can't unlink from User - links are not counted!
++% if pid(User) -> unlink(User); true -> ok end,
+ terminate(install_prev, _State) ->
+ [];
+-terminate(_Reason, #st{prev_handler=PrevHandler}) ->
++terminate(_Reason, {_User, PrevHandler}) ->
+ {error_logger_tty_h, PrevHandler}.
+
+ code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+-%% Exported (but unoffical) API.
+-write_event(Event, IoMod) ->
+- do_write_event(#st{io_mod=IoMod}, Event).
+-
+-write_event(Event, IoMod, Depth) ->
+- do_write_event(#st{io_mod=IoMod,depth=Depth}, Event).
+-
+-
+ %%% ------------------------------------------------------
+ %%% Misc. functions.
+ %%% ------------------------------------------------------
+
+ set_group_leader() ->
+ case whereis(user) of
+- User when is_pid(User) ->
+- link(User),
+- group_leader(User,self()),
+- User;
+- _ ->
+- false
++ User when is_pid(User) -> link(User), group_leader(User,self()), User;
++ _ -> false
+ end.
+
+ tag_event(Event) ->
+ {erlang:universaltime(), Event}.
+
+-write_events(State, [Ev|Es]) ->
+- %% Write the events in reverse order.
+- _ = write_events(State, Es),
+- _ = do_write_event(State, Ev),
+- ok;
+-write_events(_State, []) ->
++write_events(Events,IOMod) -> write_events1(lists:reverse(Events),IOMod).
++
++write_events1([Event|Es],IOMod) ->
++ write_event(Event,IOMod),
++ write_events1(Es,IOMod);
++write_events1([],_IOMod) ->
+ ok.
+
+-do_write_event(State, {Time0, Event}) ->
+- case parse_event(Event) of
+- ignore ->
+- ok;
+- {Head,Pid,FormatList} ->
+- Time = maybe_utc(Time0),
+- Header = write_time(Time, Head),
+- Body = format_body(State, FormatList),
+- AtNode = if
+- node(Pid) =/= node() ->
+- ["** at node ",atom_to_list(node(Pid))," **\n"];
+- true ->
+- []
+- end,
+- Str = [Header,Body,AtNode],
+- case State#st.io_mod of
+- io_lib ->
+- Str;
+- io ->
+- io:put_chars(user, Str)
+- end
++write_event({Time, {error, _GL, {Pid, Format, Args}}},IOMod) ->
++ T = write_time(maybe_utc(Time)),
++ case catch io_lib:format(add_node(Format,Pid), Args) of
++ S when is_list(S) ->
++ format(IOMod, T ++ S);
++ _ ->
++ F = add_node("ERROR: ~p - ~p~n", Pid),
++ format(IOMod, T ++ F, [Format,Args])
+ end;
+-do_write_event(_, _) ->
++write_event({Time, {emulator, _GL, Chars}},IOMod) ->
++ T = write_time(maybe_utc(Time)),
++ case catch io_lib:format(Chars, []) of
++ S when is_list(S) ->
++ format(IOMod, T ++ S);
++ _ ->
++ format(IOMod, T ++ "ERROR: ~p ~n", [Chars])
++ end;
++write_event({Time, {info, _GL, {Pid, Info, _}}},IOMod) ->
++ T = write_time(maybe_utc(Time)),
++ format(IOMod, T ++ add_node("~p~n",Pid),[Info]);
++write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}},IOMod) ->
++ T = write_time(maybe_utc(Time)),
++ S = format_report(Rep),
++ format(IOMod, T ++ S ++ add_node("", Pid));
++write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}},IOMod) ->
++ T = write_time(maybe_utc(Time), "INFO REPORT"),
++ S = format_report(Rep),
++ format(IOMod, T ++ S ++ add_node("", Pid));
++write_event({Time, {info_msg, _GL, {Pid, Format, Args}}},IOMod) ->
++ T = write_time(maybe_utc(Time), "INFO REPORT"),
++ case catch io_lib:format(add_node(Format,Pid), Args) of
++ S when is_list(S) ->
++ format(IOMod, T ++ S);
++ _ ->
++ F = add_node("ERROR: ~p - ~p~n", Pid),
++ format(IOMod, T ++ F, [Format,Args])
++ end;
++write_event({Time, {warning_report, _GL, {Pid, std_warning, Rep}}},IOMod) ->
++ T = write_time(maybe_utc(Time), "WARNING REPORT"),
++ S = format_report(Rep),
++ format(IOMod, T ++ S ++ add_node("", Pid));
++write_event({Time, {warning_msg, _GL, {Pid, Format, Args}}},IOMod) ->
++ T = write_time(maybe_utc(Time), "WARNING REPORT"),
++ case catch io_lib:format(add_node(Format,Pid), Args) of
++ S when is_list(S) ->
++ format(IOMod, T ++ S);
++ _ ->
++ F = add_node("ERROR: ~p - ~p~n", Pid),
++ format(IOMod, T ++ F, [Format,Args])
++ end;
++write_event({_Time, _Error},_IOMod) ->
+ ok.
+
+-format_body(State, [{Format,Args}|T]) ->
+- S = try format(State, Format, Args) of
+- S0 ->
+- S0
+- catch
+- _:_ ->
+- format(State, "ERROR: ~p - ~p\n", [Format,Args])
+- end,
+- [S|format_body(State, T)];
+-format_body(_State, []) ->
+- [].
+-
+-format(#st{depth=unlimited}, Format, Args) ->
+- io_lib:format(Format, Args);
+-format(#st{depth=Depth}, Format0, Args) ->
+- Format1 = io_lib:scan_format(Format0, Args),
+- Format = limit_format(Format1, Depth),
+- io_lib:build_text(Format).
+-
+-limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p;
+- C0 =:= $w ->
+- C = C0 - ($a - $A), %To uppercase.
+- [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}|limit_format(T, Depth)];
+-limit_format([H|T], Depth) ->
+- [H|limit_format(T, Depth)];
+-limit_format([], _) ->
+- [].
+-
+-parse_event({error, _GL, {Pid, Format, Args}}) ->
+- {"ERROR REPORT",Pid,[{Format,Args}]};
+-parse_event({info_msg, _GL, {Pid, Format, Args}}) ->
+- {"INFO REPORT",Pid,[{Format, Args}]};
+-parse_event({warning_msg, _GL, {Pid, Format, Args}}) ->
+- {"WARNING REPORT",Pid,[{Format,Args}]};
+-parse_event({error_report, _GL, {Pid, std_error, Args}}) ->
+- {"ERROR REPORT",Pid,format_term(Args)};
+-parse_event({info_report, _GL, {Pid, std_info, Args}}) ->
+- {"INFO REPORT",Pid,format_term(Args)};
+-parse_event({warning_report, _GL, {Pid, std_warning, Args}}) ->
+- {"WARNING REPORT",Pid,format_term(Args)};
+-parse_event(_) -> ignore.
+-
+ maybe_utc(Time) ->
+ UTC = case application:get_env(sasl, utc_log) of
+ {ok, Val} -> Val;
+@@ -209,27 +175,34 @@ maybe_utc(Time) ->
+ maybe_utc(Time, true) -> {utc, Time};
+ maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}.
+
+-format_term(Term) when is_list(Term) ->
+- case string_p(Term) of
++format(IOMod, String) -> format(IOMod, String, []).
++format(io_lib, String, Args) -> io_lib:format(String, Args);
++format(io, String, Args) -> io:format(user, String, Args).
++
++format_report(Rep) when is_list(Rep) ->
++ case string_p(Rep) of
+ true ->
+- [{"~s\n",[Term]}];
+- false ->
+- format_term_list(Term)
++ io_lib:format("~s~n",[Rep]);
++ _ ->
++ format_rep(Rep)
+ end;
+-format_term(Term) ->
+- [{"~p\n",[Term]}].
+-
+-format_term_list([{Tag,Data}|T]) ->
+- [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)];
+-format_term_list([Data|T]) ->
+- [{" ~p\n",[Data]}|format_term_list(T)];
+-format_term_list([]) ->
+- [];
+-format_term_list(_) ->
+- %% Continue to allow non-proper lists for now.
+- %% FIXME: Remove this clause in OTP 19.
++format_report(Rep) ->
++ io_lib:format("~p~n",[Rep]).
++
++format_rep([{Tag,Data}|Rep]) ->
++ io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep);
++format_rep([Other|Rep]) ->
++ io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep);
++format_rep(_) ->
+ [].
+
++add_node(X, Pid) when is_atom(X) ->
++ add_node(atom_to_list(X), Pid);
++add_node(X, Pid) when node(Pid) =/= node() ->
++ lists:concat([X,"** at node ",node(Pid)," **~n"]);
++add_node(X, _) ->
++ X.
++
+ string_p([]) ->
+ false;
+ string_p(Term) ->
+@@ -252,6 +225,7 @@ string_p1([H|T]) when is_list(H) ->
+ string_p1([]) -> true;
+ string_p1(_) -> false.
+
++write_time(Time) -> write_time(Time, "ERROR REPORT").
+ write_time({utc,{{Y,Mo,D},{H,Mi,S}}},Type) ->
+ io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n",
+ [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]);
+diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
+index 3c173dc..9e69601 100644
+--- a/lib/stdlib/src/io_lib.erl
++++ b/lib/stdlib/src/io_lib.erl
+@@ -2,7 +2,7 @@
+ %%
+ %% %CopyrightBegin%
+ %%
+-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
++%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ %%
+ %% The contents of this file are subject to the Erlang Public License,
+ %% Version 1.1, (the "License"); you may not use this file except in
+@@ -61,7 +61,6 @@
+ -module(io_lib).
+
+ -export([fwrite/2,fread/2,fread/3,format/2]).
+--export([scan_format/2,unscan_format/1,build_text/1]).
+ -export([print/1,print/4,indentation/2]).
+
+ -export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]).
+@@ -85,7 +84,7 @@
+ deep_unicode_char_list/1]).
+
+ -export_type([chars/0, latin1_string/0, continuation/0,
+- fread_error/0, fread_item/0, format_spec/0]).
++ fread_error/0, fread_item/0]).
+
+ %%----------------------------------------------------------------------
+
+@@ -110,18 +109,6 @@
+
+ -type fread_item() :: string() | atom() | integer() | float().
+
+--type format_spec() ::
+- {
+- ControlChar :: char(),
+- Args :: [any()],
+- Width :: 'none' | integer(),
+- Adjust :: 'left' | 'right',
+- Precision :: 'none' | integer(),
+- PadChar :: char(),
+- Encoding :: 'unicode' | 'latin1',
+- Strings :: boolean()
+- }.
+-
+ %%----------------------------------------------------------------------
+
+ %% Interface calls to sub-modules.
+@@ -170,31 +157,6 @@ format(Format, Args) ->
+ Other
+ end.
+
+--spec scan_format(Format, Data) -> FormatList when
+- Format :: io:format(),
+- Data :: [term()],
+- FormatList :: [char() | format_spec()].
+-
+-scan_format(Format, Args) ->
+- try io_lib_format:scan(Format, Args)
+- catch
+- _:_ -> erlang:error(badarg, [Format, Args])
+- end.
+-
+--spec unscan_format(FormatList) -> {Format, Data} when
+- FormatList :: [char() | format_spec()],
+- Format :: io:format(),
+- Data :: [term()].
+-
+-unscan_format(FormatList) ->
+- io_lib_format:unscan(FormatList).
+-
+--spec build_text(FormatList) -> chars() when
+- FormatList :: [char() | format_spec()].
+-
+-build_text(FormatList) ->
+- io_lib_format:build(FormatList).
+-
+ -spec print(Term) -> chars() when
+ Term :: term().
+
+diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
+index 37b47d7..56e15a1 100644
+--- a/lib/stdlib/src/io_lib_format.erl
++++ b/lib/stdlib/src/io_lib_format.erl
+@@ -1,7 +1,7 @@
+ %%
+ %% %CopyrightBegin%
+ %%
+-%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
++%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+ %%
+ %% The contents of this file are subject to the Erlang Public License,
+ %% Version 1.1, (the "License"); you may not use this file except in
+@@ -20,9 +20,10 @@
+
+ %% Formatting functions of io library.
+
+--export([fwrite/2,fwrite_g/1,indentation/2,scan/2,unscan/1,build/1]).
++-export([fwrite/2,fwrite_g/1,indentation/2]).
+
+-%% Format the arguments in Args after string Format. Just generate
++%% fwrite(Format, ArgList) -> string().
++%% Format the arguments in ArgList after string Format. Just generate
+ %% an error if there is an error in the arguments.
+ %%
+ %% To do the printing command correctly we need to calculate the
+@@ -36,83 +37,15 @@
+ %% and it also splits the handling of the control characters into two
+ %% parts.
+
+--spec fwrite(Format, Data) -> FormatList when
+- Format :: io:format(),
+- Data :: [term()],
+- FormatList :: [char() | io_lib:format_spec()].
+-
++fwrite(Format, Args) when is_atom(Format) ->
++ fwrite(atom_to_list(Format), Args);
++fwrite(Format, Args) when is_binary(Format) ->
++ fwrite(binary_to_list(Format), Args);
+ fwrite(Format, Args) ->
+- build(scan(Format, Args)).
+-
+-%% Build the output text for a pre-parsed format list.
+-
+--spec build(FormatList) -> io_lib:chars() when
+- FormatList :: [char() | io_lib:format_spec()].
+-
+-build(Cs) ->
++ Cs = collect(Format, Args),
+ Pc = pcount(Cs),
+ build(Cs, Pc, 0).
+
+-%% Parse all control sequences in the format string.
+-
+--spec scan(Format, Data) -> FormatList when
+- Format :: io:format(),
+- Data :: [term()],
+- FormatList :: [char() | io_lib:format_spec()].
+-
+-scan(Format, Args) when is_atom(Format) ->
+- scan(atom_to_list(Format), Args);
+-scan(Format, Args) when is_binary(Format) ->
+- scan(binary_to_list(Format), Args);
+-scan(Format, Args) ->
+- collect(Format, Args).
+-
+-%% Revert a pre-parsed format list to a plain character list and a
+-%% list of arguments.
+-
+--spec unscan(FormatList) -> {Format, Data} when
+- FormatList :: [char() | io_lib:format_spec()],
+- Format :: io:format(),
+- Data :: [term()].
+-
+-unscan(Cs) ->
+- {print(Cs), args(Cs)}.
+-
+-args([{_C,As,_F,_Ad,_P,_Pad,_Enc,_Str} | Cs]) ->
+- As ++ args(Cs);
+-args([_C | Cs]) ->
+- args(Cs);
+-args([]) ->
+- [].
+-
+-print([{C,_As,F,Ad,P,Pad,Enc,Str} | Cs ]) ->
+- print(C, F, Ad, P, Pad, Enc, Str) ++ print(Cs);
+-print([C | Cs]) ->
+- [C | print(Cs)];
+-print([]) ->
+- [].
+-
+-print(C, F, Ad, P, Pad, Encoding, Strings) ->
+- [$~] ++ print_field_width(F, Ad) ++ print_precision(P) ++
+- print_pad_char(Pad) ++ print_encoding(Encoding) ++
+- print_strings(Strings) ++ [C].
+-
+-print_field_width(none, _Ad) -> "";
+-print_field_width(F, left) -> integer_to_list(-F);
+-print_field_width(F, right) -> integer_to_list(F).
+-
+-print_precision(none) -> "";
+-print_precision(P) -> [$. | integer_to_list(P)].
+-
+-print_pad_char($\s) -> ""; % default, no need to make explicit
+-print_pad_char(Pad) -> [$., Pad].
+-
+-print_encoding(unicode) -> "t";
+-print_encoding(latin1) -> "".
+-
+-print_strings(false) -> "l";
+-print_strings(true) -> "".
+-
+ collect([$~|Fmt0], Args0) ->
+ {C,Fmt1,Args1} = collect_cseq(Fmt0, Args0),
+ [C|collect(Fmt1, Args1)];
+@@ -208,7 +141,7 @@ pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1);
+ pcount([_|Cs], Acc) -> pcount(Cs, Acc);
+ pcount([], Acc) -> Acc.
+
+-%% build([Control], Pc, Indentation) -> io_lib:chars().
++%% build([Control], Pc, Indentation) -> string().
+ %% Interpret the control structures. Count the number of print
+ %% remaining and only calculate indentation when necessary. Must also
+ %% be smart when calculating indentation for characters in format.
+@@ -229,14 +162,10 @@ decr_pc($p, Pc) -> Pc - 1;
+ decr_pc($P, Pc) -> Pc - 1;
+ decr_pc(_, Pc) -> Pc.
+
+-
++%% indentation(String, Indentation) -> Indentation.
+ %% Calculate the indentation of the end of a string given its start
+ %% indentation. We assume tabs at 8 cols.
+
+--spec indentation(String, StartIndent) -> integer() when
+- String :: io_lib:chars(),
+- StartIndent :: integer().
+-
+ indentation([$\n|Cs], _I) -> indentation(Cs, 0);
+ indentation([$\t|Cs], I) -> indentation(Cs, ((I + 8) div 8) * 8);
+ indentation([C|Cs], I) when is_integer(C) ->
+@@ -437,6 +366,7 @@ float_data([D|Cs], Ds) when D >= $0, D =< $9 ->
+ float_data([_|Cs], Ds) ->
+ float_data(Cs, Ds).
+
++%% fwrite_g(Float)
+ %% Writes the shortest, correctly rounded string that converts
+ %% to Float when read back with list_to_float/1.
+ %%
+@@ -444,8 +374,6 @@ float_data([_|Cs], Ds) ->
+ %% in Proceedings of the SIGPLAN '96 Conference on Programming
+ %% Language Design and Implementation.
+
+--spec fwrite_g(float()) -> string().
+-
+ fwrite_g(0.0) ->
+ "0.0";
+ fwrite_g(Float) when is_float(Float) ->
+@@ -714,7 +642,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase)
+ term([Prefix|S], F, Adj, none, Pad)
+ end.
+
+-%% char(Char, Field, Adjust, Precision, PadChar) -> chars().
++%% char(Char, Field, Adjust, Precision, PadChar) -> string().
+
+ char(C, none, _Adj, none, _Pad) -> [C];
+ char(C, F, _Adj, none, _Pad) -> chars(C, F);
diff --git a/otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch b/otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch
new file mode 100644
index 0000000..3e817d5
--- /dev/null
+++ b/otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch
@@ -0,0 +1,34 @@
+From: Peter Lemenkov
+Date: Thu, 14 Jul 2016 17:51:16 +0300
+Subject: [PATCH] Respect -proto_dist switch while connection to EPMD
+
+Signed-off-by: Peter Lemenkov
+
+diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
+index 21a3dec..0ba695c 100644
+--- a/lib/kernel/src/erl_epmd.erl
++++ b/lib/kernel/src/erl_epmd.erl
+@@ -107,6 +107,10 @@ names1(HostName) ->
+
+ register_node(Name, PortNo) ->
+ register_node(Name, PortNo, inet).
++register_node(Name, PortNo, inet_tcp) ->
++ register_node(Name, PortNo, inet);
++register_node(Name, PortNo, inet6_tcp) ->
++ register_node(Name, PortNo, inet6);
+ register_node(Name, PortNo, Family) ->
+ gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity).
+
+diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl
+index 0739cf3..c64ddc8 100644
+--- a/lib/kernel/src/inet_tcp_dist.erl
++++ b/lib/kernel/src/inet_tcp_dist.erl
+@@ -72,7 +72,7 @@ gen_listen(Driver, Name) ->
+ {ok, Socket} ->
+ TcpAddress = get_tcp_address(Driver, Socket),
+ {_,Port} = TcpAddress#net_address.address,
+- case erl_epmd:register_node(Name, Port) of
++ case erl_epmd:register_node(Name, Port, Driver) of
+ {ok, Creation} ->
+ {ok, {Socket, TcpAddress, Creation}};
+ Error ->
diff --git a/otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch b/otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch
new file mode 100644
index 0000000..9514c34
--- /dev/null
+++ b/otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch
@@ -0,0 +1,38 @@
+From: Dan Gudmundsson
+Date: Thu, 17 Mar 2016 10:23:41 +0100
+Subject: [PATCH] mnesia: Send mnesia_down messages to waiting transactions
+
+Mnesia didn't forward mnesia_down to transactions which where already
+decided to be aborted, but that could lead to hanging transactions
+still waiting for messages from the node which had stopped.
+
+diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl
+index 17af0ca..329192e 100644
+--- a/lib/mnesia/src/mnesia_tm.erl
++++ b/lib/mnesia/src/mnesia_tm.erl
+@@ -1714,13 +1714,10 @@ commit_participant(Coord, Tid, Bin, C0, DiscNs, _RamNs) ->
+ ?eval_debug_fun({?MODULE, commit_participant, undo_prepare},
+ [{tid, Tid}]);
+
+- {'EXIT', _, _} ->
++ {'EXIT', _MnesiaTM, Reason} ->
++ reply(Coord, {do_abort, Tid, self(), {bad_commit,Reason}}),
+ mnesia_recover:log_decision(D#decision{outcome = aborted}),
+- ?eval_debug_fun({?MODULE, commit_participant, exit_log_abort},
+- [{tid, Tid}]),
+- mnesia_schema:undo_prepare_commit(Tid, C0),
+- ?eval_debug_fun({?MODULE, commit_participant, exit_undo_prepare},
+- [{tid, Tid}]);
++ mnesia_schema:undo_prepare_commit(Tid, C0);
+
+ Msg ->
+ verbose("** ERROR ** commit_participant ~p, got unexpected msg: ~p~n",
+@@ -2236,8 +2233,6 @@ reconfigure_coordinators(N, [{Tid, [Store | _]} | Coordinators]) ->
+ true ->
+ send_mnesia_down(Tid, Store, N)
+ end;
+- aborted ->
+- ignore; % avoid spurious mnesia_down messages
+ _ ->
+ %% Tell the coordinator about the mnesia_down
+ send_mnesia_down(Tid, Store, N)
From 94ed2172da57885c8e1e996ba443ad4bccc14df4 Mon Sep 17 00:00:00 2001
From: Peter Lemenkov
Date: Wed, 9 Nov 2016 19:59:54 +0300
Subject: [PATCH 13/13] Fix for doc generation with OpenJDK 1.8.0
Signed-off-by: Peter Lemenkov
---
erlang.spec | 5 +
otp-0029-Fix-a-few-javadoc-errors.patch | 139 ++++++++++++++++++++++++
2 files changed, 144 insertions(+)
create mode 100644 otp-0029-Fix-a-few-javadoc-errors.patch
diff --git a/erlang.spec b/erlang.spec
index e117c20..a89b8fb 100644
--- a/erlang.spec
+++ b/erlang.spec
@@ -140,6 +140,9 @@ Patch27: otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch
# Fedora specific patch
# mnesia: Send mnesia_down messages to waiting transactions
Patch28: otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch
+# Fedora specific patch
+# Fix a few javadoc errors
+Patch29: otp-0029-Fix-a-few-javadoc-errors.patch
# end of autogenerated patch tag list
BuildRequires: lksctp-tools-devel
@@ -1035,6 +1038,7 @@ Erlang mode for XEmacs (source lisp files).
%patch26 -p1 -b .Revert_Remove_unused_code_in_error_logger_handlers
%patch27 -p1 -b .Respect_proto_dist_switch_while_connection_to_EPMD
%patch28 -p1 -b .mnesia_Send_mnesia_down_messages_to_waiting_transact
+%patch29 -p1 -b .Fix_a_few_javadoc_errors
# end of autogenerated prep patch list
# FIXME we should come up with a better solution
@@ -2370,6 +2374,7 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \
- Respect -proto_dist switch while connecting to EPMD
- Cherry-picked fix for OTP-13423. Mnesia transactions could hang while waiting
on a response from a node who had stopped.
+- Fix for doc generation with OpenJDK 1.8.0
* Thu Apr 7 2016 John Eckersberg - R16B-03.17
- Revert "Enable error_logger depth fine tuning" (rhbz#1324922)
diff --git a/otp-0029-Fix-a-few-javadoc-errors.patch b/otp-0029-Fix-a-few-javadoc-errors.patch
new file mode 100644
index 0000000..c808303
--- /dev/null
+++ b/otp-0029-Fix-a-few-javadoc-errors.patch
@@ -0,0 +1,139 @@
+From: Anthony Ramine
+Date: Sun, 8 Jun 2014 12:37:10 +0200
+Subject: [PATCH] Fix a few javadoc errors
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Reported-by: Boris Mühmer
+
+diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
+index 9ba6a4a..7aa30ee 100644
+--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
+@@ -266,7 +266,7 @@ public abstract class AbstractConnection extends Thread {
+ *
+ * @param dest
+ * the Erlang PID of the remote process.
+- * @param msg
++ * @param payload
+ * the encoded message to send.
+ *
+ * @exception java.io.IOException
+diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
+index 8e8bd47..e7a9d10 100644
+--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
+@@ -404,7 +404,7 @@ public class OtpConnection extends AbstractConnection {
+ *
+ * @param dest
+ * the Erlang PID of the remote process.
+- * @param msg
++ * @param payload
+ * the encoded message to send.
+ *
+ * @exception java.io.IOException
+diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java
+index 7e3e2a7..84b1355 100644
+--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java
++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java
+@@ -51,8 +51,8 @@ public class OtpErlangLong extends OtpErlangObject implements Serializable,
+ /**
+ * Create an Erlang integer from the given value.
+ *
+- * @param val
+- * the long value to use.
++ * @param v
++ * the big integer value to use.
+ */
+ public OtpErlangLong(final BigInteger v) {
+ if (v == null) {
+diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
+index fe81ce3..f75e435 100644
+--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
+@@ -162,7 +162,7 @@ public class OtpErlangPid extends OtpErlangObject implements Serializable,
+ * Determine if two PIDs are equal. PIDs are equal if their components are
+ * equal.
+ *
+- * @param port
++ * @param o
+ * the other PID to compare to.
+ *
+ * @return true if the PIDs are equal, false otherwise.
+diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
+index 6766b52..a5e202c 100644
+--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
+@@ -41,8 +41,6 @@ public class OtpErlangString extends OtpErlangObject implements Serializable,
+
+ /**
+ * Create an Erlang string from a list of integers.
+- *
+- * @return an Erlang string with Unicode code units.
+ *
+ * @throws OtpErlangException
+ * for non-proper and non-integer lists.
+diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
+index 0fd93b0..4a4a1e7 100644
+--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
+@@ -69,6 +69,7 @@ package com.ericsson.otp.erlang;
+ * notify other parties in a timely manner.
+ *
+ *
++ *
+ * When retrieving messages from a mailbox that has received an exit signal, an
+ * {@link OtpErlangExit OtpErlangExit} exception will be raised. Note that the
+ * exception is queued in the mailbox along with other messages, and will not be
+@@ -420,7 +421,6 @@ public class OtpMbox {
+
+ /**
+ * Equivalent to exit(new OtpErlangAtom(reason)).
+- *
+- * The header information that is available is as follows:
++ * The header information that is available is as follows:
+ *
a tag indicating the type of message
+ *
the intended recipient of the message, either as a
+ * {@link OtpErlangPid pid} or as a String, but never both.
+ *
(sometimes) the sender of the message. Due to some eccentric
+ * characteristics of the Erlang distribution protocol, not all messages have
+ * information about the sending process. In particular, only messages whose tag
+- * is {@link OtpMsg#regSendTag regSendTag} contain sender information.
+ * Message are sent using the Erlang external format (see separate
+diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+index 78f47aa..fd4eba3 100644
+--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+@@ -202,7 +202,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
+ /**
+ * Write an array of bytes to the stream.
+ *
+- * @param buf
++ * @param bytes
+ * the array of bytes to write.
+ *
+ */
+@@ -637,7 +637,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
+ * Write a positive short to the stream. The short is interpreted as a two's
+ * complement unsigned short even if it is negative.
+ *
+- * @param s
++ * @param us
+ * the short to use.
+ */
+ public void write_ushort(final short us) {