aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/doc/src/io.xml33
-rw-r--r--lib/stdlib/doc/src/notes.xml37
-rw-r--r--lib/stdlib/src/c.erl4
-rw-r--r--lib/stdlib/src/erl_lint.erl8
-rw-r--r--lib/stdlib/src/gen.erl80
-rw-r--r--lib/stdlib/src/gen_server.erl11
-rw-r--r--lib/stdlib/src/gen_statem.erl13
-rw-r--r--lib/stdlib/src/io_lib_format.erl36
-rw-r--r--lib/stdlib/test/io_SUITE.erl56
-rw-r--r--lib/stdlib/vsn.mk2
10 files changed, 162 insertions, 118 deletions
diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml
index 6a7c06188b..f1037ec76b 100644
--- a/lib/stdlib/doc/src/io.xml
+++ b/lib/stdlib/doc/src/io.xml
@@ -137,11 +137,11 @@
Hello world!
ok</pre>
<p>The general format of a control sequence is <c>~F.P.PadModC</c>.</p>
- <p>Character <c>C</c> determines the type of control sequence
- to be used, <c>F</c> and <c>P</c> are optional numeric
- arguments. If <c>F</c>, <c>P</c>, or <c>Pad</c> is <c>*</c>,
- the next argument in <c>Data</c> is used as the numeric value
- of <c>F</c> or <c>P</c>.</p>
+ <p>The character <c>C</c> determines the type of control sequence
+ to be used. It is the only required field. All of <c>F</c>,
+ <c>P</c>, <c>Pad</c>, and <c>Mod</c> are optional. For example,
+ to use a <c>#</c> for <c>Pad</c> but use the default values for
+ <c>F</c> and <c>P</c>, you can write <c>~..#C</c>.</p>
<list type="bulleted">
<item>
<p><c>F</c> is the <c>field width</c> of the printed argument. A
@@ -167,13 +167,26 @@ ok</pre>
The default padding character is <c>' '</c> (space).</p>
</item>
<item>
- <p><c>Mod</c> is the control sequence modifier. It is a
- single character that changes the interpretation of
- <c>Data</c>. This can be <c>t</c>, for Unicode translation,
- or <c>l</c>, for stopping <c>p</c> and <c>P</c> from
- detecting printable characters.</p>
+ <p><c>Mod</c> is the control sequence modifier. This is
+ one or more characters that change the interpretation of
+ <c>Data</c>. The current modifiers are <c>t</c>, for Unicode
+ translation, and <c>l</c>, for stopping <c>p</c> and <c>P</c>
+ from detecting printable characters.</p>
</item>
</list>
+ <p>If <c>F</c>, <c>P</c>, or <c>Pad</c> is a <c>*</c> character,
+ the next argument in <c>Data</c> is used as the value.
+ For example:</p>
+ <pre>
+1> <input>io:fwrite("~*.*.0f~n",[9, 5, 3.14159265]).</input>
+003.14159
+ok</pre>
+ <p>To use a literal <c>*</c> character as <c>Pad</c>, it must be
+ passed as an argument:</p>
+ <pre>
+2> <input>io:fwrite("~*.*.*f~n",[9, 5, $*, 3.14159265]).</input>
+**3.14159
+ok</pre>
<p><em>Available control sequences:</em></p>
<taglist>
<tag><c>~</c></tag>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index b61e5b9b9e..bf6b06859e 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,6 +31,43 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 3.4.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Correct <c>filelib:find_source()</c> and
+ <c>filelib:find_file()</c> to by default also search one
+ level below <c>src</c>. This is in accordance with the
+ Design Principles which states that an application can
+ have Erlang source files one level below the <c>src</c>
+ directory. </p>
+ <p>
+ Own Id: OTP-14832 Aux Id: ERL-527 </p>
+ </item>
+ <item>
+ <p> The contract of <c>erl_tar:table/2</c> is corrected.
+ </p>
+ <p>
+ Own Id: OTP-14860 Aux Id: PR 1670 </p>
+ </item>
+ <item>
+ <p> Correct a few contracts. </p>
+ <p>
+ Own Id: OTP-14889</p>
+ </item>
+ <item>
+ <p>
+ Fix string:prefix/2 to handle an empty string as second
+ argument.</p>
+ <p>
+ Own Id: OTP-14942 Aux Id: PR-1702 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.4.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index 3597e61c26..13f78841aa 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.erl
@@ -564,7 +564,7 @@ display_info(Pid) ->
Other
end,
Reds = fetch(reductions, Info),
- LM = length(fetch(messages, Info)),
+ LM = fetch(message_queue_len, Info),
HS = fetch(heap_size, Info),
SS = fetch(stack_size, Info),
iformat(w(Pid), mfa_string(Call),
@@ -886,7 +886,7 @@ portinfo(Id) ->
procline(Name, Info, Pid) ->
Call = initial_call(Info),
Reds = fetch(reductions, Info),
- LM = length(fetch(messages, Info)),
+ LM = fetch(message_queue_len, Info),
procformat(io_lib:format("~tw",[Name]),
io_lib:format("~w",[Pid]),
io_lib:format("~ts",[mfa_string(Call)]),
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 1930c462e8..9a62d21d34 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -3971,6 +3971,8 @@ extract_sequence(3, [$.,_|Fmt], Need) ->
extract_sequence(4, Fmt, Need);
extract_sequence(3, Fmt, Need) ->
extract_sequence(4, Fmt, Need);
+extract_sequence(4, [$t, $l | Fmt], Need) ->
+ extract_sequence(4, [$l, $t | Fmt], Need);
extract_sequence(4, [$t, $c | Fmt], Need) ->
extract_sequence(5, [$c|Fmt], Need);
extract_sequence(4, [$t, $s | Fmt], Need) ->
@@ -3987,8 +3989,14 @@ extract_sequence(4, [$t, C | _Fmt], _Need) ->
{error,"invalid control ~t" ++ [C]};
extract_sequence(4, [$l, $p | Fmt], Need) ->
extract_sequence(5, [$p|Fmt], Need);
+extract_sequence(4, [$l, $t, $p | Fmt], Need) ->
+ extract_sequence(5, [$p|Fmt], Need);
extract_sequence(4, [$l, $P | Fmt], Need) ->
extract_sequence(5, [$P|Fmt], Need);
+extract_sequence(4, [$l, $t, $P | Fmt], Need) ->
+ extract_sequence(5, [$P|Fmt], Need);
+extract_sequence(4, [$l, $t, C | _Fmt], _Need) ->
+ {error,"invalid control ~lt" ++ [C]};
extract_sequence(4, [$l, C | _Fmt], _Need) ->
{error,"invalid control ~l" ++ [C]};
extract_sequence(4, Fmt, Need) ->
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 0e6f49d99f..2e6223d2bb 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -157,52 +157,27 @@ call(Process, Label, Request, Timeout)
Fun = fun(Pid) -> do_call(Pid, Label, Request, Timeout) end,
do_for_proc(Process, Fun).
-do_call(Process, Label, Request, Timeout) ->
- try erlang:monitor(process, Process) of
- Mref ->
- %% If the monitor/2 call failed to set up a connection to a
- %% remote node, we don't want the '!' operator to attempt
- %% to set up the connection again. (If the monitor/2 call
- %% failed due to an expired timeout, '!' too would probably
- %% have to wait for the timeout to expire.) Therefore,
- %% use erlang:send/3 with the 'noconnect' option so that it
- %% will fail immediately if there is no connection to the
- %% remote node.
-
- catch erlang:send(Process, {Label, {self(), Mref}, Request},
- [noconnect]),
- receive
- {Mref, Reply} ->
- erlang:demonitor(Mref, [flush]),
- {ok, Reply};
- {'DOWN', Mref, _, _, noconnection} ->
- Node = get_node(Process),
- exit({nodedown, Node});
- {'DOWN', Mref, _, _, Reason} ->
- exit(Reason)
- after Timeout ->
- erlang:demonitor(Mref, [flush]),
- exit(timeout)
- end
- catch
- error:_ ->
- %% Node (C/Java?) is not supporting the monitor.
- %% The other possible case -- this node is not distributed
- %% -- should have been handled earlier.
- %% Do the best possible with monitor_node/2.
- %% This code may hang indefinitely if the Process
- %% does not exist. It is only used for featureweak remote nodes.
- Node = get_node(Process),
- monitor_node(Node, true),
- receive
- {nodedown, Node} ->
- monitor_node(Node, false),
- exit({nodedown, Node})
- after 0 ->
- Tag = make_ref(),
- Process ! {Label, {self(), Tag}, Request},
- wait_resp(Node, Tag, Timeout)
- end
+do_call(Process, Label, Request, Timeout) when is_atom(Process) =:= false ->
+ Mref = erlang:monitor(process, Process),
+
+ %% OTP-21:
+ %% Auto-connect is asynchronous. But we still use 'noconnect' to make sure
+ %% we send on the monitored connection, and not trigger a new auto-connect.
+ %%
+ erlang:send(Process, {Label, {self(), Mref}, Request}, [noconnect]),
+
+ receive
+ {Mref, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {ok, Reply};
+ {'DOWN', Mref, _, _, noconnection} ->
+ Node = get_node(Process),
+ exit({nodedown, Node});
+ {'DOWN', Mref, _, _, Reason} ->
+ exit(Reason)
+ after Timeout ->
+ erlang:demonitor(Mref, [flush]),
+ exit(timeout)
end.
get_node(Process) ->
@@ -217,19 +192,6 @@ get_node(Process) ->
node(Process)
end.
-wait_resp(Node, Tag, Timeout) ->
- receive
- {Tag, Reply} ->
- monitor_node(Node, false),
- {ok, Reply};
- {nodedown, Node} ->
- monitor_node(Node, false),
- exit({nodedown, Node})
- after Timeout ->
- monitor_node(Node, false),
- exit(timeout)
- end.
-
%%
%% Send a reply to the client.
%%
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 77a46419f6..f29314d0a2 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -437,12 +437,11 @@ decode_msg(Msg, Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug, Hi
%%% Send/receive functions
%%% ---------------------------------------------------
do_send(Dest, Msg) ->
- case catch erlang:send(Dest, Msg, [noconnect]) of
- noconnect ->
- spawn(erlang, send, [Dest,Msg]);
- Other ->
- Other
- end.
+ try erlang:send(Dest, Msg)
+ catch
+ error:_ -> ok
+ end,
+ ok.
do_multi_call(Nodes, Name, Req, infinity) ->
Tag = make_ref(),
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index f95b2ea9cd..9dc360a289 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -641,16 +641,11 @@ replies([]) ->
%% Might actually not send the message in case of caught exception
send(Proc, Msg) ->
- try erlang:send(Proc, Msg, [noconnect]) of
- noconnect ->
- _ = spawn(erlang, send, [Proc,Msg]),
- ok;
- ok ->
- ok
+ try erlang:send(Proc, Msg)
catch
- _:_ ->
- ok
- end.
+ error:_ -> ok
+ end,
+ ok.
%% Here the init_it/6 and enter_loop/5,6,7 functions converge
enter(Module, Opts, State, Data, Server, Actions, Parent) ->
diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
index 2ef4e517d8..64edbf1824 100644
--- a/lib/stdlib/src/io_lib_format.erl
+++ b/lib/stdlib/src/io_lib_format.erl
@@ -127,25 +127,23 @@ collect_cseq(Fmt0, Args0) ->
{F,Ad,Fmt1,Args1} = field_width(Fmt0, Args0),
{P,Fmt2,Args2} = precision(Fmt1, Args1),
{Pad,Fmt3,Args3} = pad_char(Fmt2, Args2),
- {Encoding,Fmt4,Args4} = encoding(Fmt3, Args3),
- {Strings,Fmt5,Args5} = strings(Fmt4, Args4),
- {C,As,Fmt6,Args6} = collect_cc(Fmt5, Args5),
- FormatSpec = #{control_char => C, args => As, width => F, adjust => Ad,
- precision => P, pad_char => Pad, encoding => Encoding,
- strings => Strings},
- {FormatSpec,Fmt6,Args6}.
-
-encoding([$t|Fmt],Args) ->
- true = hd(Fmt) =/= $l,
- {unicode,Fmt,Args};
-encoding(Fmt,Args) ->
- {latin1,Fmt,Args}.
-
-strings([$l|Fmt],Args) ->
- true = hd(Fmt) =/= $t,
- {false,Fmt,Args};
-strings(Fmt,Args) ->
- {true,Fmt,Args}.
+ Spec0 = #{width => F,
+ adjust => Ad,
+ precision => P,
+ pad_char => Pad,
+ encoding => latin1,
+ strings => true},
+ {Spec1,Fmt4} = modifiers(Fmt3, Spec0),
+ {C,As,Fmt5,Args4} = collect_cc(Fmt4, Args3),
+ Spec2 = Spec1#{control_char => C, args => As},
+ {Spec2,Fmt5,Args4}.
+
+modifiers([$t|Fmt], Spec) ->
+ modifiers(Fmt, Spec#{encoding => unicode});
+modifiers([$l|Fmt], Spec) ->
+ modifiers(Fmt, Spec#{strings => false});
+modifiers(Fmt, Spec) ->
+ {Spec, Fmt}.
field_width([$-|Fmt0], Args0) ->
{F,Fmt,Args} = field_value(Fmt0, Args0),
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index ac61e3753b..6f4e7ad7e0 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -1905,29 +1905,61 @@ otp_10836(Suite) when is_list(Suite) ->
%% OTP-10755. The 'l' modifier
otp_10755(Suite) when is_list(Suite) ->
+ %% printing plain ascii characters
S = "string",
"\"string\"" = fmt("~p", [S]),
"[115,116,114,105,110,103]" = fmt("~lp", [S]),
"\"string\"" = fmt("~P", [S, 2]),
"[115|...]" = fmt("~lP", [S, 2]),
- {'EXIT',{badarg,_}} = (catch fmt("~ltp", [S])),
- {'EXIT',{badarg,_}} = (catch fmt("~tlp", [S])),
- {'EXIT',{badarg,_}} = (catch fmt("~ltP", [S])),
- {'EXIT',{badarg,_}} = (catch fmt("~tlP", [S])),
+ %% printing latin1 chars, with and without modifiers
+ T = {[255],list_to_atom([255]),[a,b,c]},
+ "{\"ÿ\",ÿ,[a,b,c]}" = fmt("~p", [T]),
+ "{\"ÿ\",ÿ,[a,b,c]}" = fmt("~tp", [T]),
+ "{[255],ÿ,[a,b,c]}" = fmt("~lp", [T]),
+ "{[255],ÿ,[a,b,c]}" = fmt("~ltp", [T]),
+ "{[255],ÿ,[a,b,c]}" = fmt("~tlp", [T]),
+ "{\"ÿ\",ÿ,...}" = fmt("~P", [T,3]),
+ "{\"ÿ\",ÿ,...}" = fmt("~tP", [T,3]),
+ "{[255],ÿ,...}" = fmt("~lP", [T,3]),
+ "{[255],ÿ,...}" = fmt("~ltP", [T,3]),
+ "{[255],ÿ,...}" = fmt("~tlP", [T,3]),
+ %% printing unicode chars, with and without modifiers
+ U = {[666],list_to_atom([666]),[a,b,c]},
+ "{[666],'\\x{29A}',[a,b,c]}" = fmt("~p", [U]),
+ case io:printable_range() of
+ unicode ->
+ "{\"ʚ\",'ʚ',[a,b,c]}" = fmt("~tp", [U]),
+ "{\"ʚ\",'ʚ',...}" = fmt("~tP", [U,3]);
+ latin1 ->
+ "{[666],'ʚ',[a,b,c]}" = fmt("~tp", [U]),
+ "{[666],'ʚ',...}" = fmt("~tP", [U,3])
+ end,
+ "{[666],'\\x{29A}',[a,b,c]}" = fmt("~lp", [U]),
+ "{[666],'ʚ',[a,b,c]}" = fmt("~ltp", [U]),
+ "{[666],'ʚ',[a,b,c]}" = fmt("~tlp", [U]),
+ "{[666],'\\x{29A}',...}" = fmt("~P", [U,3]),
+ "{[666],'\\x{29A}',...}" = fmt("~lP", [U,3]),
+ "{[666],'ʚ',...}" = fmt("~ltP", [U,3]),
+ "{[666],'ʚ',...}" = fmt("~tlP", [U,3]),
+ %% the compiler should catch uses of ~l with other than pP
Text =
"-module(l_mod).\n"
"-export([t/0]).\n"
"t() ->\n"
" S = \"string\",\n"
- " io:format(\"~ltp\", [S]),\n"
- " io:format(\"~tlp\", [S]),\n"
- " io:format(\"~ltP\", [S, 1]),\n"
- " io:format(\"~tlP\", [S, 1]).\n",
+ " io:format(\"~lw\", [S]),\n"
+ " io:format(\"~lW\", [S, 1]),\n"
+ " io:format(\"~ltw\", [S]),\n"
+ " io:format(\"~tlw\", [S]),\n"
+ " io:format(\"~ltW\", [S, 1]),\n"
+ " io:format(\"~tlW\", [S, 1]).\n",
{ok,l_mod,[{_File,Ws}]} = compile_file("l_mod.erl", Text, Suite),
- ["format string invalid (invalid control ~lt)",
- "format string invalid (invalid control ~tl)",
- "format string invalid (invalid control ~lt)",
- "format string invalid (invalid control ~tl)"] =
+ ["format string invalid (invalid control ~lw)",
+ "format string invalid (invalid control ~lW)",
+ "format string invalid (invalid control ~ltw)",
+ "format string invalid (invalid control ~ltw)",
+ "format string invalid (invalid control ~ltW)",
+ "format string invalid (invalid control ~ltW)"] =
[lists:flatten(M:format_error(E)) || {_L,M,E} <- Ws],
ok.
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 69d258c2f0..8391389fc4 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.4.3
+STDLIB_VSN = 3.4.4